技術情報棚卸し(平日限定)

todoa2cの技術情報棚卸しです。平日限定ってことはアレだ。言わせんな恥ずかしい。

GoでGoogle App Engineを触るテスト

はじめに

Google App Engineを初めて触ることにしました。 しかもみんな大好きプログラミング言語Goで。 ここ最近Gopher Tシャツを着てPythonコードを書くことが多かったので、 リハビリも兼ねつつGoogle App Engineでのスピンアップ最速と言われるGoを 採用することにしました。 以下は作業メモです。

準備

開発環境はThe Development Environmentに書かれている。 Goの環境はSDKに含まれているけれど、別途Python 2.7が必要。 ローカルでサーバーを起動するなどで必要らしい。 既にPython 2.7は入れていたので、そのまま次に進む。

GAE/g のチュートリアル(英語)を読む。噂に違わず、Goの標準APIで大体の事が書けそうな雰囲気を掴む。

Using the Datastoreの部分は正直よく分かっていない。

あとはざっとGo Service APIsにも目を通す。

書いてみる

今回作ろうと思ったものは、GitHubに多少絡むものなので、 GitHub APIも使ってみることにする。

幸いGoogleがgo-githubを公開しているので、 下記のコマンドで使えるようにする。 go-githubには書かれていなかったが、 go-githubがgo-querystringに依存しているようなので、 合わせて入れる。

1
2
go get github.com/google/go-github
go get github.com/google/go-querystring

上記で取得できたライブラリを、プロジェクトフォルダと同じ場所に置く。 環境変数GOPATHの参照先に置く、といういつものルールではないことに注意が必要。

実際に、GitHub APIのSearch APIを叩いてみる。 とりあえずgo-githubのサンプル通りに…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package front

import (
  "github.com/google/go-github/github"
  "html/template"
  "net/http"
)

func init() {
  http.HandleFunc("/", index)
  http.HandleFunc("/find", find)
}

func index(w http.ResponseWriter, r *http.Request) {
  if err := indexTemplate.Execute(w, nil); err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}

var indexTemplate = template.Must(template.New("index").Parse(indexTemplateHTML))

const indexTemplateHTML = `
<html>
  <body>
    <form action="/find" method="get">
      <div><input type="text" name="q"></div>
      <div><input type="submit" value="Search"></div>
    </form>
  </body>
</html>
`

func find(w http.ResponseWriter, r *http.Request) {
  client := github.NewClient(nil)
  query := r.FormValue("q")

  opts := &github.SearchOptions{Sort: "forks", Order: "desc", ListOptions: github.ListOptions{Page: 1, PerPage: 20}}
  res, _, err := client.Search.Repositories(query, opts)

  if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      return
  }

  if err := findTemplate.Execute(w, res); err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}

var findTemplate = template.Must(template.New("find").Parse(findTemplateHTML))

const findTemplateHTML = `
<html>
  <body>
      <ul>
    
      <li><a href=""></a></li>
    
  </ul>
  </body>
</html>
`

で、goapp serveによりアプリを起動して、http://localhost:8080/にアクセスして、 いざ検索実行!

1
2
3
Get https://api.github.com/search/repositories?order=desc&page=1&per_page=20&q=abc&sort=forks:
http.DefaultTransport and http.DefaultClient are not available in App Engine.
See https://developers.google.com/appengine/docs/go/urlfetch/overview

ちーん。エラーが返ってきた。 そう言えばURL Fetch Go API Overviewに、 GAE環境用のHTTP clientを取得する必要があると書かれていたのを思い出す。

で、import文とfind関数を以下のように変更する。 appengine, appengine/urlfetchの2つをimportすることと、 appengine.NewContext(r)でContextなるものを取得し、それを使ってurlfetch.Client(c)で GAE環境用HTTP clientを取得するように変更した。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import (
  "appengine"
  "appengine/urlfetch"
  "github.com/google/go-github/github"
  "html/template"
  "net/http"
)

func find(w http.ResponseWriter, r *http.Request) {
  // 下記の2行でApp Engine上でURL Fetchができる
  c := appengine.NewContext(r)  
  client := github.NewClient(urlfetch.Client(c))

  query := r.FormValue("q")

  opts := &github.SearchOptions{Sort: "forks", Order: "desc", ListOptions: github.ListOptions{Page: 1, PerPage: 20}}
  res, _, err := client.Search.Repositories(query, opts)

  if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      return
  }

  if err := findTemplate.Execute(w, res); err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}

これで何とか動いた。 このくらいの内容であれば、あまり困らずにGoで書けることに驚き。

デプロイ

デプロイは事前にApplications Overviewで アプリケーションを登録しておき、app.yamlのアプリケーション名と名前を合わせて(よく分かってない)、 goapp deployコマンドでデプロイできる。

私は2段階認証を使っているので、事前にデプロイ用の固有パスワードを取得しておいた。

デプロイは割とあっさり完了し、普通にGAE/Go環境が本番環境でも動いている。凄い… 次はDatastoreまわりを学んでいこうっと。

分からない点

  • appengine.NewContextとは?
  • go-githubのSearchOptionsの詳細
  • Datastore
  • スケジュール実行
  • GAE/gに適したフレームワークってあるのかしら?
  • 関係ないけどそろそろシンタックスハイライトをこのブログでも使いたい
  • これまた関係ないけどF-SecureがひたすらWebアプリの実行を邪魔してツラい。

Comments