2024/01/14

ビルドエラー "ERROR: failed to initialize analyzer: getting previous image" の対処 [GAE]

事象

久しぶりにGAEのプロジェクトをビルドすると、mavenで以下のエラーに遭遇しました。
このプロジェクトはappengine-maven-pluginを利用してビルドしています。

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:27 min
[INFO] Finished at: 2024-01-xxTxx:xx:xx+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal com.google.cloud.tools:appengine-maven-plugin:2.5.0:deploy (default-cli) on project プロジェクトID.サービスID: App Engine application deployment failed: com.google.cloud.tools.appengine.operations.cloudsdk.process.ProcessHandlerException: com.google.cloud.tools.appengine.AppEngineException: Non zero exit: 1 -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException


Cloud Buildを確認すると以下のエラーが出てしました。

ERROR: failed to initialize analyzer: getting previous image: getting config file for image "asia.gcr.io/プロジェクトID/app-engine-tmp/app/サービスID/xxxxxx:latest": GET https://storage.googleapis.com/asia.artifacts.プロジェクトID.appspot.com/containers/images/sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx?access_token=REDACTED: unexpected status code 404 Not Found: <error><code>NoSuchKey</code><message>The specified key does not exist.</message><details>No such object: asia.artifacts.プロジェクトID.appspot.com/containers/images/sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</details></error>


調査

「差分ビルドしているけど前回のビルドイメージが見つからない」と言っているようです。だいぶ前ですが、Cloud Storageのビルドイメージを保存しているバケットにライフサイクルを適用して、生成から14日を過ぎたものは削除するように設定しています。その当時は14日以上空けてビルドしても問題なく再ビルド可能でした。また手作業でCloud Storageのビルドイメージを消してからのビルドも可能でした。それに差分ビルドによるビルド時間短縮の効果もわずかでした。

その後ビルド方法が変更されて差分ビルドしかしてくれなくなったようです。

ネットで探してみると「GAEのデプロイが失敗する: ERROR: (gcloud.app.deploy) Error Response: [9] Cloud build xxxxx status: FAILURE」が見つかりました。まったく同じ状況です。


解決方法

gcloud app deploy のケース

Goで作成しているサービスは上記の記事と同じくgcloud app deploy --no-cacheで回避できました。


maven appengine:deploy のケース

Javaで作成しているサービスはデプロイまでmvn appengine:deployで行っています。appengine-maven-pluginに--no-cacheと同様のパラメータがないか探してみたのですが、見当たりません。serviceAccountKeyFileという怪しいタグがありますがweb上に全く説明が見つからず、またタグ名から推測される動作も異なります。

なぜかJavaで書いたサービスはgcloud app deployだとビルド・デプロイは成功するものの、生成されるwarに依存ライブラリが含まれないので使っていませんでした。

しかし一旦gcloud app deploy --no-cacheでデプロイすればビルドイメージができるので、その後はmvn appengine:deployでデプロイできるようになりました。


2023/12/02

vsReversiアップデート

 Google Cloudのデモとして用意していたvsReversiをアップデートしました。今回はJava8→17の更新だけです。内容は特に変わっていません。Google App EngineのJava8サポート終了が予定されているので、それに備えての更新です。

Java11を飛ばして17にした理由は、単にライフサイクルがJava17のほうが長いからです。App Engineのランタイムサポートスケジュールを参照してください。Java11だと残り1年もないですね。すでにJavaのLTSバージョンは今年秋に21がリリースされていますが、これもそのうちApp Engineで使えるようになることでしょう。


2023/10/18

CORSに対応する [Go]

背景

 HTML/JavaScript/TypeScriptのダウンロード元と Ajaxでのアクセス先が異なる場合、Cross Resource Origin Sharing(以下CORSと略)ポリシーに従う必要があります。AjaxやCORSについての詳細はいろいろなところで説明されているので、ここでは割愛します。

 Go+GinでのCORS対応の実装についてです。


前提

 使用するソフトウェアのバージョンは以下のとおりです。

  • Go : 1.20
  • Gin : 1.9.0


 また、すべてのアクセス先に対してCORSアクセスを許可する設定であるAccess-Control-Allow-Originヘッダへの"*"の設定ではなく、特定のアクセス先に対してのみ許可するものとします。


gin-contrib/cors

 最初に見つけたのが「gin-contrib/cors」です。Ginの公式なCORSのパッケージのようです。ドキュメントはこちら。スニペットを見る限り使い方も簡単で、Ginのミドルウェアとして設定するだけです。
 しかし「Canonical example」のスニペットを参考に実装してみたのですが、まったくCORSポリシーを解決できませんでした。OPTIONSリクエストに対して、レスポンスヘッダにはAccess-Control-Allow-Originヘッダが付いていません。しばらく設定を変えたりして試してみたのですが、全く解決できませんでした。

 この状況をググってみて見つかったのがStackOverflowの記事「Go gin framework CORS」です。質問者はGoの標準ライブラリのみを使用していますが、OPTIONSに続くアクセスが発生せず、期待動作しないと言っています。
 回答を見ると複数パターンあるようです。
  • Goの標準ライブラリのみで実装の修正。質問者はこれで解決したもよう。
  • gin-contrib/corsの使用を勧めるもの。これで動いたといっているのは1名のみ。
  • rs/corsを勧めるもの。
  • 独自ライブラリを勧めるもの。

 自分の使っているGo/Ginのバージョンに比べて、gin-contrib/corsの最新リリースは2022/07/05なので幾分か古く、そのためGo/Ginの更新について行っていない可能性があります。Go/Ginのバージョンダウンまでして確認するほどgin-contrib/corsにこだわることもないので、このパッケージは諦めました。

標準ライブラリでの実装

 変なライブラリを入れたくないので、結局標準ライブラリのみで実装することにしました。実際にCORSを解決できた最小限の実装が以下です。Ginのミドルウェアにしています。


func main() {
    router := gin.Default()
    router.Use(corsSetting)
    // リクエストハンドラを設定 router.GET(...) etc.
}

func corsSetting(ginctx *gin.Context) {
    // リクエスト元の確認
    requestheader := ginctx.Request.Header
    requestorigin := requestheader.Get("origin")
    if リクエストの確認 {
        // レスポンスヘッダの設定
        responseheader := ginctx.Writer.Header()
        responseheader.Set("Access-Control-Allow-Origin", requestorigin)
        responseheader.Set("Access-Control-Allow-Headers", "Content-Type")
    }
}


 OPTIONSに続くアクセスのために、Access-Control-Allow-Headersレスポンスヘッダに"Content-Type"を設定しています。