Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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)、エンティティグループ(関連するエンティティの集合で、トランザクションの単位となる)といった概念を持ちます。このコミットでは、PackageCommitResultLogTagといったGoの構造体がDatastoreのエンティティとしてマッピングされ、データの保存と取得が行われています。

Go言語

GoはGoogleによって開発された静的型付け、コンパイル型のプログラミング言語です。シンプルさ、効率性、並行処理のサポートが特徴です。このコミットのコードは全てGo言語で書かれており、net/httpパッケージを使ったHTTPハンドラの定義や、appengineappengine/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 オブジェクトをリクエストボディから受け取る。
    • CommitValid() メソッドで基本的な検証を行う。
    • Datastoreトランザクション (datastore.RunInTransaction) 内で addCommit 関数を呼び出し、コミットをDatastoreに保存する。
  • addCommit(c appengine.Context, com *Commit):
    • トランザクション内で実行される。
    • 既に同じコミットがDatastoreに存在する場合は何もしない。
    • Package エンティティから次のコミット番号 (NextNum) を取得し、Commit に割り当てる。
    • 親コミットが存在するかどうかを検証する(最初のコミットでない場合)。
    • Goリポジトリのコミットであれば、tip タグを更新する。
    • Commit エンティティをDatastoreに保存する。
  • tagHandler(w http.ResponseWriter, r *http.Request):
    • JSON形式でエンコードされた Tag オブジェクトをリクエストボディから受け取り、Datastoreに保存する。
  • todoHandler(w http.ResponseWriter, r *http.Request):
    • builderpackagePath クエリパラメータを受け取る。
    • 指定されたパッケージの最新のコミットから遡り、特定のビルダがまだ結果を報告していない最初のコミットのハッシュを返す。これにより、ビルダは次にどのコミットをビルドすべきかを知ることができる。
  • resultHandler(w http.ResponseWriter, r *http.Request):
    • JSON形式でエンコードされた Result オブジェクトをリクエストボディから受け取る。
    • ResultValid() メソッドで基本的な検証を行う。
    • ログデータが含まれていれば、PutLog 関数を使ってログを保存し、そのハッシュを Result に設定する。
    • Datastoreトランザクション内で Result を保存し、関連する Commit エンティティの Result フィールドを更新する。
  • logHandler(w http.ResponseWriter, r *http.Request):
    • URLパスからログのハッシュを取得し、そのハッシュに対応する圧縮されたログデータをDatastoreから取得する。
    • 取得したログデータをgzip展開し、HTTPレスポンスとしてクライアントに返す。
  • AuthHandler(h http.HandlerFunc) http.HandlerFunc:
    • HTTPハンドラをラップし、key クエリパラメータによる認証を行うミドルウェア関数。
    • secretKey とビルダ名から生成されるハッシュ値と、リクエストで提供された key を比較して認証を行う。
  • init() 関数:
    • アプリケーションの起動時に、各URLパスと対応するハンドラ関数を登録する。AuthHandler を使用して認証が必要なエンドポイントを保護している。
  • 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)を持つように変更されました。
    • CommitValid(), 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() メソッドが実装されています。特に CommitResult は、それぞれ 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 など)の一般的な知識
  • 継続的インテグレーションとビルドダッシュボードに関する一般的な概念