[インデックス 10502] ファイルの概要
このコミットは、Go言語プロジェクトのビルドダッシュボードにおける、ビルダ(自動ビルドシステム)が利用するバックエンド機能の実装と、それに関連するテストの追加を目的としています。具体的には、コミット情報、ビルド結果、タグ、ビルドログなどをApp EngineのDatastoreに保存・管理し、ビルダからのリクエストに応じて適切な情報を提供するAPIエンドポイント群が実装されています。
コミット
commit 49dfaad870208969ff3129ed1e1bda8f12302c03
Author: Andrew Gerrand <adg@golang.org>
Date: Fri Nov 25 12:53:05 2011 +1100
dashboard: builder-facing implementation and tests
R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/5431048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/49dfaad870208969ff3129ed1e1bda8f12302c03
元コミット内容
dashboard: builder-facing implementation and tests
このコミットは、Goプロジェクトのダッシュボードにおいて、ビルダ(自動ビルドを実行するマシンやプロセス)が利用する機能の実装と、そのテストコードを追加するものです。
変更の背景
Go言語プロジェクトのような大規模なオープンソースプロジェクトでは、様々なプラットフォームやアーキテクチャでのビルドとテストの継続的な実行が不可欠です。これを実現するためには、各ビルダが自身の作業状況(どのコミットをビルドしたか、結果はどうだったか、ログは何かなど)を中央のシステムに報告し、その情報を集約・可視化する「ビルドダッシュボード」が必要です。
このコミットは、そのビルドダッシュボードのバックエンド部分、特にビルダからのデータ受信と管理を行うAPIとデータモデルをGoogle App Engine上に構築することを目的としています。これにより、ビルダはHTTPリクエストを通じてコミット情報、ビルド結果、ログなどをダッシュボードに送信できるようになり、ダッシュボードはこれらの情報を永続化し、後続の処理や表示に利用できるようになります。
前提知識の解説
Google App Engine (GAE)
Google App Engineは、Googleが提供するPlatform as a Service (PaaS) です。開発者はアプリケーションコードを記述し、GAEにデプロイするだけで、インフラの管理をGoogleに任せることができます。スケーラビリティ、信頼性、セキュリティが組み込まれており、Go言語を含む複数の言語をサポートしています。このコミットでは、Go言語で書かれたApp Engineアプリケーションとしてダッシュボードのバックエンドが構築されています。
Google Cloud Datastore (旧称: App Engine Datastore)
Google Cloud Datastoreは、Google Cloud Platformが提供するNoSQLドキュメントデータベースです。GAEアプリケーションの永続化層としてよく利用されます。Datastoreは、エンティティ(データレコード)、キー(エンティティを一意に識別するID)、エンティティグループ(関連するエンティティの集合で、トランザクションの単位となる)といった概念を持ちます。このコミットでは、Package
、Commit
、Result
、Log
、Tag
といったGoの構造体がDatastoreのエンティティとしてマッピングされ、データの保存と取得が行われています。
Go言語
GoはGoogleによって開発された静的型付け、コンパイル型のプログラミング言語です。シンプルさ、効率性、並行処理のサポートが特徴です。このコミットのコードは全てGo言語で書かれており、net/http
パッケージを使ったHTTPハンドラの定義や、appengine
、appengine/datastore
パッケージを使ったGAE固有の機能利用が見られます。
継続的インテグレーション (CI) とビルドダッシュボード
継続的インテグレーション (CI) は、開発者がコード変更を頻繁にメインブランチにマージし、自動化されたビルドとテストを実行するソフトウェア開発プラクティスです。ビルドダッシュボードは、CIシステムの一部として、様々なビルドジョブのステータス、テスト結果、コードカバレッジなどを一元的に表示するウェブインターフェースです。これにより、開発チームはプロジェクトの健全性を常に把握し、問題があれば早期に発見・修正できます。このコミットは、このビルドダッシュボードのデータ収集部分を担っています。
技術的詳細
このコミットは、Go App Engineアプリケーションとして動作するビルドダッシュボードのバックエンドAPIを実装しています。
app.yaml
の変更
app.yaml
はApp Engineアプリケーションの設定ファイルです。このコミットでは、以下の新しいURLハンドラが追加されています。
/log/.+
: ビルドログの取得エンドポイント。/buildtest
: テスト用のエンドポイントで、管理者ログインが必要。- 既存の
/commit
,/tag
,/todo
,/result
エンドポイントも_go_app
スクリプトにルーティングされています。
misc/dashboard/app/build/build.go
の変更
このファイルは、ダッシュボードの主要なデータモデルとAPIハンドラを含んでいます。
Datastoreエンティティの定義と変更
Package
: ビルド対象のパッケージ(例: Go本体、その他のGoリポジトリ)を表す。NextNum
フィールドが追加され、次のコミットに割り当てる連番を管理するようになりました。Commit
: 特定のパッケージにおけるコミットを表す。Num
フィールドが追加され、パッケージ内で単調増加するコミット番号が割り当てられるようになりました。Key
メソッドの変更:Package
エンティティを親とするエンティティグループ内にCommit
エンティティが作成されるようになり、トランザクションの整合性が向上しました。キーの形式もPackagePath|Hash
に変更されています。Valid()
: コミットハッシュの基本的な検証を行う。AddResult()
: コミットエンティティにビルド結果(Result
)を追加する。Datastoreトランザクション内で呼び出されることを想定。HasResult()
: 特定のビルダのビルド結果が既に存在するかどうかをチェックする。
Result
: 特定のコミット、ビルダ、OS/アーキテクチャの組み合わせに対するビルド結果を表す。Log
フィールドがstring
から[]byte
に変更され、ログデータを直接保持するのではなく、LogHash
を介してLog
エンティティを参照するようになりました。Key()
:Package
を親とするキーを生成。Valid()
: ハッシュの基本的な検証を行う。
Log
: 圧縮されたビルドログを保持するエンティティ。PutLog()
: 生のログテキストを受け取り、gzip圧縮してDatastoreに保存し、そのSHA1ハッシュを返す。
Tag
:weekly
,release
,tip
などの特定のタグを表す。Key()
:Package
を親とするキーを生成。Valid()
: タグの種類とハッシュの基本的な検証を行う。
HTTPハンドラの実装
commitHandler(w http.ResponseWriter, r *http.Request)
:- JSON形式でエンコードされた
Commit
オブジェクトをリクエストボディから受け取る。 Commit
のValid()
メソッドで基本的な検証を行う。- Datastoreトランザクション (
datastore.RunInTransaction
) 内でaddCommit
関数を呼び出し、コミットをDatastoreに保存する。
- JSON形式でエンコードされた
addCommit(c appengine.Context, com *Commit)
:- トランザクション内で実行される。
- 既に同じコミットがDatastoreに存在する場合は何もしない。
Package
エンティティから次のコミット番号 (NextNum
) を取得し、Commit
に割り当てる。- 親コミットが存在するかどうかを検証する(最初のコミットでない場合)。
- Goリポジトリのコミットであれば、
tip
タグを更新する。 Commit
エンティティをDatastoreに保存する。
tagHandler(w http.ResponseWriter, r *http.Request)
:- JSON形式でエンコードされた
Tag
オブジェクトをリクエストボディから受け取り、Datastoreに保存する。
- JSON形式でエンコードされた
todoHandler(w http.ResponseWriter, r *http.Request)
:builder
とpackagePath
クエリパラメータを受け取る。- 指定されたパッケージの最新のコミットから遡り、特定のビルダがまだ結果を報告していない最初のコミットのハッシュを返す。これにより、ビルダは次にどのコミットをビルドすべきかを知ることができる。
resultHandler(w http.ResponseWriter, r *http.Request)
:- JSON形式でエンコードされた
Result
オブジェクトをリクエストボディから受け取る。 Result
のValid()
メソッドで基本的な検証を行う。- ログデータが含まれていれば、
PutLog
関数を使ってログを保存し、そのハッシュをResult
に設定する。 - Datastoreトランザクション内で
Result
を保存し、関連するCommit
エンティティのResult
フィールドを更新する。
- JSON形式でエンコードされた
logHandler(w http.ResponseWriter, r *http.Request)
:- URLパスからログのハッシュを取得し、そのハッシュに対応する圧縮されたログデータをDatastoreから取得する。
- 取得したログデータをgzip展開し、HTTPレスポンスとしてクライアントに返す。
AuthHandler(h http.HandlerFunc) http.HandlerFunc
:- HTTPハンドラをラップし、
key
クエリパラメータによる認証を行うミドルウェア関数。 secretKey
とビルダ名から生成されるハッシュ値と、リクエストで提供されたkey
を比較して認証を行う。
- HTTPハンドラをラップし、
init()
関数:- アプリケーションの起動時に、各URLパスと対応するハンドラ関数を登録する。
AuthHandler
を使用して認証が必要なエンドポイントを保護している。
- アプリケーションの起動時に、各URLパスと対応するハンドラ関数を登録する。
validHash(hash string) bool
:- ハッシュが空でないことを確認するヘルパー関数(TODOコメントでより厳密な検証が必要とされている)。
logErr(w http.ResponseWriter, r *http.Request, err os.Error)
:- エラーをApp Engineのログに出力し、HTTPレスポンスとしてエラーメッセージを返すヘルパー関数。
misc/dashboard/app/build/key.go
の新規追加
このファイルは、認証に使用される secretKey
を定義しています。本番環境にデプロイする前に、このキーを秘密の値に設定する必要があることを警告するコメントと、開発サーバでのみ動作するよう panic
を含む init
関数が含まれています。
misc/dashboard/app/build/test.go
の新規追加
このファイルは、ビルドダッシュボードのバックエンドAPIのテストスイートを含んでいます。
init()
関数:/buildtest
パスにtestHandler
を登録する。testEntityKinds
: テスト中にクリアするDatastoreエンティティの種類を定義。testRequests
: 様々なテストケースを定義した構造体のスライス。/commit
エンドポイントへのリクエストでコミットを登録。/todo
エンドポイントへのリクエストで次にビルドすべきコミットを取得。/result
エンドポイントへのリクエストでビルド結果を登録。/log
エンドポイントへのリクエストでログを取得。
testPackages
: テスト用のPackage
エンティティを定義。testHandler(w http.ResponseWriter, r *http.Request)
:- 開発サーバでのみ実行されることを確認。
- テスト開始前に
nukeEntities
を呼び出してDatastoreのテストデータをクリアする。 testPackages
をDatastoreに保存。testRequests
の各テストケースを順に実行し、HTTPリクエストをシミュレートし、レスポンスを検証する。
nukeEntities(c appengine.Context, kinds []string) os.Error
:- 指定された種類のDatastoreエンティティを全て削除する関数。テスト環境のクリーンアップに使用される。本番環境での誤操作を防ぐため、開発サーバでのみ実行可能。
コアとなるコードの変更箇所
misc/dashboard/app/app.yaml
:/log/.+
と/buildtest
の新しいURLハンドラが追加されました。
misc/dashboard/app/build/build.go
:Package
構造体にNextNum
フィールドが追加。Commit
構造体のNum
フィールドの順序が変更され、Key
メソッドが親エンティティ(Package
)を持つように変更されました。Commit
にValid()
,AddResult()
,HasResult()
メソッドが追加。Result
構造体のLog
フィールドが[]byte
に変更され、Key()
,Valid()
メソッドが追加。Log
構造体とPutLog()
関数が追加され、ログの保存と取得ロジックが実装されました。Tag
構造体にValid()
メソッドが追加。commitHandler
,tagHandler
,todoHandler
,resultHandler
の具体的な実装が追加されました(以前は関数シグネチャのみ)。logHandler
,AuthHandler
,validHash
,logErr
ヘルパー関数が新規追加。init()
関数内で、新しいハンドラの登録とAuthHandler
の適用が行われました。
misc/dashboard/app/build/key.go
:secretKey
定義を含む新規ファイル。
misc/dashboard/app/build/test.go
:- ビルドダッシュボードのAPIをテストするためのテストスイートを含む新規ファイル。
コアとなるコードの解説
このコミットの核心は、Go App Engine上でビルドダッシュボードのバックエンドAPIを構築することにあります。
build.go
では、まず Package
, Commit
, Result
, Log
, Tag
といったGoの構造体がDatastoreのエンティティとして定義されています。これらの構造体には、Datastoreのキーを生成するための Key()
メソッドや、データの整合性を検証するための Valid()
メソッドが実装されています。特に Commit
と Result
は、それぞれ Package
を親とするエンティティグループ内に配置されることで、関連するデータのトランザクション整合性が保証されます。
HTTPハンドラは、ビルダからのリクエストを処理する主要なコンポーネントです。
commitHandler
は、ビルダが新しいコミット情報をダッシュボードに報告する際に使用されます。コミットはDatastoreに保存され、Goリポジトリの最新コミットであればtip
タグも更新されます。resultHandler
は、ビルダがビルド結果を報告する際に使用されます。ビルドログがあれば、それはgzip圧縮されてLog
エンティティとして別途保存され、そのハッシュがResult
に関連付けられます。その後、Result
はDatastoreに保存され、関連するCommit
エンティティも更新されます。todoHandler
は、ビルダが次にどのコミットをビルドすべきかを問い合わせるために使用されます。これは、特定のビルダがまだビルドしていない最新のコミットをDatastoreから検索して返します。logHandler
は、ビルドログのハッシュに基づいて、保存された圧縮ログデータを取得し、展開して提供します。
セキュリティ面では、AuthHandler
が重要な役割を果たします。これは、secretKey
を用いたシンプルな認証メカニズムを提供し、許可されたビルダのみが commit
, result
, tag
, todo
などの機密性の高いAPIエンドポイントにアクセスできるようにします。
key.go
は、この認証メカニズムの secretKey
を定義するプレースホルダであり、本番デプロイ時には適切な秘密鍵を設定する必要があることを開発者に促しています。
test.go
は、これらのバックエンドAPIが正しく機能するかを検証するための包括的なテストスイートを提供します。nukeEntities
関数を使ってテスト前にDatastoreをクリーンアップし、様々なリクエストをシミュレートしてAPIの動作とレスポンスを検証することで、実装の信頼性を確保しています。
全体として、このコミットは、Goプロジェクトのビルドダッシュボードがビルダからの情報を効率的かつ安全に収集・管理するための堅牢なバックエンドシステムを構築しています。
関連リンク
参考にした情報源リンク
- コミットメッセージと変更されたソースコード
- Google App Engineのドキュメント(Go言語、Datastoreに関する一般的な知識)
- Go言語の標準ライブラリ(
net/http
,encoding/json
,compress/gzip
,crypto/sha1
など)の一般的な知識 - 継続的インテグレーションとビルドダッシュボードに関する一般的な概念