Upgrade to Pro — share decks privately, control downloads, hide ads and more …

🐭 Lambda関数をGoで実装してみた話

🐭 Lambda関数をGoで実装してみた話

社内LTにて、Goを布教しようと試みましたʕ◔ϖ◔ʔ

長谷川 広樹

March 26, 2021
Tweet

More Decks by 長谷川 広樹

Other Decks in Programming

Transcript

  1. ディレクトリ構造(2) notify-slack-of-amplify-events # srcに相当 │ ├── build │  └── Dockerfile ├──

    cmd │  ├── main.go │  ├── controllers │  ├── entities │  └── usecases │ ├── config ├── test ├── .air.toml ├── docker-compose.yml ├── go.mod └── go.sum https://github.com/hiroki-it/notify-slack-of-amplify-events ディレクトリ構造ベストプラクティス:https://github.com/golang-standards/project-layout
  2. ディレクトリ構造(3) ・build  Dockerfileを配置. ・cmd  main.goファイルや,サブmainパッケージを配置. src # ソースコードを配置 ├── build

    └── cmd    ├── main.go    ├── controllers    ├── entities    └── usecases
  3. Goファイルの要素(1) package <パッケージ名> // 名前空間の宣言 import "<パッケージ名>" // パッケージの読み込み func

    Xxx(){ // 関数 } ・一つのディレクトリ内では一つのパッケージ名のみ. ・関数名は,頭文字が大文字だとパブリック,小文字だとプライベートになる.
  4. Goファイルの要素(3) func Division(x int, y int) (int, int) { //

    商を計算する. quotient := x / y // 余りを計算する. remainder := x % y // 商と余りを返却する. return quotient, remainder } ・引数と返却値の型に厳格. ・複数の値を返却可能.
  5. Goの環境構築(1) FROM golang:1.15 ENV CGO_ENABLED=0 # C言語製のライブラリの有効化 ENV GOOS=linux #

    Goが稼働するOS(※GoはOSに縛られない) ENV GOARCH=amd64 # CPUアーキテクチャ ENV GO111MODULE=on # go.modの有効化 WORKDIR ${GOPATH}/src 最近,『go.mod(≒ composer.json)』『go.sum(≒ composer.lock)』が導入
  6. Goの環境構築(2) module github.com/hiroki-it/notify-slack-of-amplify-events go 1.15 require ( # プロトコルを除いたURLとバージョンでパッケージを必ず指定 github.com/aws/aws-lambda-go

    v1.23.0 github.com/stretchr/testify v1.7.0 ) replace ( # mainパッケージの共通部品も,インターネット上に存在することを強制 github.com/hiroki-it/notify-slack-of-amplify-events/cmd/entities/xxx => /cmd/entities/xxx ) あらゆるパッケージはインターネット上にリリースされるべきという思想 go.modファイル
  7. Goの環境構築(3) # インストールのキャッシュを活用するためにコピーしておく. COPY go.mod go.sum ./ # パッケージをインストールする. RUN

    go get -u github.com/cosmtrek/air \ && go mod download -x COPY . . ・mod downloadコマンド  go.modファイルに実装したパッケージをインストール
  8. Goの環境構築(5) # ビルドのアーティファクトを/go/binに配置する. RUN go build -x -o go/bin ./cmd

    CMD ["/go/bin/cmd"] ・cmdディレクトリ  ベストプラクティスに則り,cmdディレクトリにソースコードを配置. ・buildコマンド  ソースコードをビルドし,binディレクトリにアーティファクトを配置.
  9. Lambdaを使用した通知処理(2) func HandleRequest(request Request) error { var event eventbridge.Event //

    EventBridgeから転送されたJSONを受信し,構造体にマッピングします. err := json.Unmarshal([]byte(request.Records[0].EventBridge.Event), &event) slackClient := slack.NewSlackClient() message := slackClient.BuildMessage(event) return slackClient.PostMessage(message) } 構造体?? 構造体とJSONのマッピング?? handler.goファイル
  10. Lambdaを使用した通知処理(3) type Person struct { Name string // 構造体が持つデータの型 }

    func main() { person := Person{"Hiroki"} // 構造体にデータを設定する. fmt.Printf("%#v\n", person.Name) // "Hiroki" } ・構造体とは,メソッドは持たず,データのみを持つもの. ・関数に構造体を関連付けることで,オブジェクトを擬似的に表現可能.
  11. Lambdaを使用した通知処理(4) type Person struct { // 構造体 Name string }

    func (person Person) SetName(name string) { // セッター関数を構造体に関連付ける person.Name = name } func (person Person) GetName() string { // ゲッター関数を構造体に関連付ける return person.Name } func main() { person := Person{Name: "Gopher"} person.SetName("Hiroki") // セッター関数で値を上書きする fmt.Printf("%#v\n", person.GetName()) // "Gopher" }
  12. Lambdaを使用した通知処理(5) type Event struct { Version string `json:"version"` ... Detail

    struct { AppId string `json:"appId"` BranchName string `json:"branchName"` JobId string `json:"jobId"` JobStatus string `json:"jobStatus"` } `json:"detail"` } JSONと構造体で相互変換するため,構造体の定義時にJSONマッピングが必要 { "event": { "version": "0", ... "detail": { "appId": "dd31ugx1agx51", "branchName": "feature/293_deploy_to_amplify", "jobId": "2", "jobStatus": "SUCCEED" } } }
  13. Lambdaを使用した通知処理(6) func Handler(request Request) error { var event eventbridge.Event err

    := json.Unmarshal([]byte(request.Records[0].EventBridge.Event), &event) slackClient := slack.NewSlackClient() // 通知メッセージのJSONを構成します. message := slackClient.BuildMessage(event) return slackClient.PostMessage(message) } https://github.com/hiroki-it/notify-slack-of-amplify-events/blob/develop/cmd/ buildMessageメソッドで構成される構造体は,slackのtype.goの閲覧を推奨󰢛
  14. Lambdaを使用した通知処理(7) func postMessage(message Message) error { json, err := json.Marshal(message)

    // マッピングを元に,構造体をJSONに変換する. request, err := http.NewRequest( // リクエストメッセージを定義する. "POST", os.Getenv("SLACK_API_URL"), bytes.NewBuffer(json), ) ... ・JSONをMessage構造体にマッピング. ・POSTリクエストの必要なパラメータを設定. post.goファイル
  15. Lambdaを使用した通知処理(8) … request.Header.Set("Content-Type", "application/json") // ヘッダーを定義する. request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("SLACK_API_TOKEN")))

    client := &http.Client{} response, err := client.Do(request) // HTTPリクエストを送信する. defer response.Body.Close() // deferで宣言しておき,HTTP通信を必ず終了できるようにする. return nil } ・クライアントを起動し,リクエストを送信. ・defer宣言された関数は,たとえ処理が停止しても最後に必ず実行される.
  16. おまけ:例外処理 json, err := json.Marshal(...) if err != nil {

    return err } request, err := http.NewRequest(...) if err != nil { return err } ・Goの思想では例外処理は無駄なものとされ,try-catchが無い. ・多くの関数は二つ目の返却値にエラーを返すため,毎回検証するしかない.