AWS Lambda Function と Go でシュッとやってバーンってしたい

前提

  • AWS Lambda Function でWEBリクエストをうけとってなんかしたい
  • Go 使う。ふつーのWEBアプリケーションはかけるものとする
  • 設定少なくさっさと立ち上げたい

最終形

Lambda Function + Function URL w/CORS を立てる。バイナリは↓で作っておく。

なお以下のソースコードは既存のものから不要なものを載せないよう切り貼りしたものであり、これ単体での動作確認は行っていないので雰囲気で読み取ってください。

// cmd/lambda/main.go
package main

import (
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/awslabs/aws-lambda-go-api-proxy/httpadapter"

    "github.com/hkdnet/tekitou/handler"
)

func main() {
    mux := handler.NewHandler()
    lambda.Start(httpadapter.NewV2(mux).ProxyWithContext)
}
// handler/handler.go
package handler

import "github.com/gorilla/mux"

func NewHandler() http.Handler {
    // なんでもいいけどとりあえず gorilla にしておく。
    r := mux.NewRouter()
    // てけとーにルーティング足す
    return r
}

ローカル確認はこんな感じにしておくとよい。

// cmd/local/main.go
package main

import (
    "fmt"
    "net/http"
    "os"
    "strings"

    "log"

    "github.com/hkdnet/tekitou/handler"
)

var corsHeaders map[string]string

func init() {
    corsHeaders = make(map[string]string)
    corsHeaders["Access-Control-Allow-Origin"] = "*"
    corsHeaders["Access-Control-Allow-Methods"] = "GET,PUT"
    corsHeaders["Access-Control-Allow-Headers"] = "content-type"
}

// CORS header. This is set by Lambda Function URL in production. Our lambda handler doesn't need to care CORS.
// But in local we do need it.
func setCorsHeaders(h *http.Header) {
    for k, v := range corsHeaders {
        h.Set(k, v)
    }
}

type respWriterWithCors struct {
    w http.ResponseWriter

    wroteHeader bool
}

func (w *respWriterWithCors) Header() http.Header {
    return w.w.Header()
}
func (w *respWriterWithCors) Write(b []byte) (int, error) {
    if !w.wroteHeader {
        w.WriteHeader(http.StatusOK)
    }
    return w.w.Write(b)
}
func (w *respWriterWithCors) WriteHeader(statusCode int) {
    h := w.Header()
    setCorsHeaders(&h)
    w.w.WriteHeader(statusCode)
    w.wroteHeader = true
}

func main() {
    h := handler.NewHander()

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if strings.EqualFold(r.Method, "options") {
            h := w.Header()
            setCorsHeaders(&h)
            fmt.Fprintf(w, "")
            return
        }
        w = &respWriterWithCors{w: w, wroteHeader: false}
        h.ServeHTTP(w, r)
    })

    log.Println("start listening")

    err := http.ListenAndServe(":3001", nil)
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s", err)
        os.Exit(1)
    }
}

紆余曲折部分

普通にやってると aws-lambda-go にいきつくと思うんだけど、これはそもそもリフレクションバリバリつかっていてちょっと追いにくい。

github.com

そして Function URL を使えば API Gateway なくてもいいらしいという風のうわさを聞いたものとしては API Gateway をはさみたくない。ついでにいうとルーティングも自前で解析しないで Go の既存のWEBフレームワークとかにお願いしたい。

そのへんを解決してくれるのがこれ。

github.com

Function URL 対応とは一言も書いてないが API Gateway V2にすれば普通に動く(ということはおそらく Function URL + aws-lambda-go の APIGWv2でも動くはずだが未検証)。でもとりあえず http.Handler なものを返せば動いてくれるので考えることが減って楽

次の問題はどうやってローカル確認するか。これも http.Handler があるので上述のように cmd を2つに分けてローカル確認用と実際にLambdaに置く用でそれぞれエントリポイントを用意すればよい。

ここまできて最後の問題がCORS設定。Function URL は CORS の設定があるのでこれをそのまま使えたほうが楽。だけどローカルでは当然そんな便利なやつはないので書く必要がある。それが respWriterWithCors 部分。ローカルでサーバを立てる前に ResponseWriter をラップしてあげればよい。

これでだいたい動くものができるので、あとは中身をどうにかするだけ。

謝辞

本記事の構成にいたるまで、takeshinoda さんにアドバイスをたくさんいただきました。ありがとうございました!

GitHub + Terraform Cloud で不幸にも workspace が大量にあるときに変更がある workspace だけピックアップするやつ

workspace が大量すぎて困っている人は通常あんまりいないと思うが弊社では非常に役に立っている。

以下のブックマークレットを作って叩けばよい。

javascript:(function(){ [...document.querySelectorAll('.merge-status-item')].filter(e => e.textContent.includes("Terraform plan has no changes") || !e.textContent.includes("Details") ).forEach(e => {e.className = '';  e.style.height = 0; e.style.padding = '0'; e.innerHTML = ''}) })()

2021年12月の近況

この記事は

この記事は Rubyist近況[1] Advent Calendar 2021 - Adventar の5日目の記事ですが、現在2021年12月23日18時くらいです。これは、5日目の投稿をブッチしたわけではなく、空いているので埋めているのであり、私が怠惰であることを示すわけではないということを念の為書いておきます*1

4日目は、zunda さんの記事でした。

zenn.dev

近況

近況は99日前のこの記事でだいたい書きましたが、重複することも含めてつらつら書きます。

hkdnet.hatenablog.com

仕事

相変わらずトレジャーデータというところに勤めています。Ruby も書いています。ここ2週ほど手元でゴニョゴニョしてるのは Go と JavaScript ですが、チームのメインリポジトリRails ですし、主な仕事もそっちです。Ruby のお仕事は弊社でもまだたくさんありますので、ご興味がある方は僕にご一報のうえ、こちらからどうぞ。無事採用となった場合にはリファラルボーナスがあった気がするのでとてもよいご飯を還元できると思います。

仕事内容としては、エンタープライズ〜という感じの機能を作ったり、英語を読んだり英語を書いたりしています。せっかくなのでビッグデータ〜という感じに近い機能についてもガリガリやりたい気持ちはありますが、めぐり合わせとかの関係もありやってませんね。あんまり自分のキャリアや好みの領域に明確な指針がなく、会社としてこれやりたいんですね、じゃあ空いてるんで僕入りますか、みたいな感じになりがちです。

英語は相変わらずそんなにできないわけですが、英語がどうこうというより多数の言語、タイムゾーンに属するメンバーがいる中でプロジェクトを前にすすめるというところをちゃんとやっていく必要があり、それには胆力が必要となることが多く、やっていき〜という感じです。ちゃんとやっていけてないからこんなことを書いているわけですが。規模的には当然自分でなにかやったら全部終わるみたいになることが少なく、開発側だけでもチーム横断になるし、プロダクト側ともいい感じにやっていく必要があり、それをちゃんと終わらせるというのはやはり労力がいるものだなあと思います。

仕事2

副業のほうも続けているわけですが、こっちもこっちでありがたいことに人が増えてきており、ただの開発者としてではなく、どうやったら全体としてのアウトプットがいい感じになるかということを素人ながら考えたりしています。

人が増えているとは言いましたが、フロントエンドの人がまだまだ足りてないので募集中です。副業とか業務委託からお願いすることになると思うのでこのへんを読んで興味がありましたらぜひ。なおこちらについても応募前に僕にご一報いただけるとリファラルボーナスがありますのでなんらかの還元ができると思います。

生活

相変わらず料理をずっとしています。まだ飽きないですね。仕事でヘロヘロだったりすると作る気にならなかったりしますが。最近はあまりに日常になりすぎたせいで特に写真撮ったりもしなくなりました。趣味であり、生活の一部であるという。

家ではビールをやたらと飲んでいたのですが、特に飲むぞうおおおという気持ちでないときにはご飯と一緒ならビアリーでよい気がしてきました。もちろん酒を味わうぞというときには物足りないのですが。ノンアル系はどうにも苦手なのですが、ビアリーには慣れたのでこのまま進めばいけるようになるかもしれません。というか別に常備されてればジンジャーエールとかでもいいという説はあります(未検証)。健康のためにはアルコールが少ないほうがいいのはそのとおりなのですが、特に財布への負荷が軽くなってないのが気になるところではあります。別にお金のかかる趣味があるわけでもないので食と酒には好きにお金を使っていいとは思っているのですが、低アルの割には高いですよねという気持ちになってしまう*2。ビール以外にも日本酒をよく飲んでおり、近くに好きな酒屋も出来たのですが、いかんせん酒自体を記憶するのが非常に苦手で、ブログに書くほどの何かはありません。

そういえば家がほしい気持ちが高まり、いろいろなんやかんやしています。2人暮らしが1年経ちだいたい必要な家の感じとかがわかってきたこと、今の家の家賃を考えると買ってしまってもだいたいなんとかなること、結婚式も終わり新しいことを検討する余裕ができたことなどからです。家、何もかもがわからず、ひたすらブログやら情報サイトを眺めたりしてなるほど〜という気持ちになったりしています。

娯楽系でいうと、一時期SEKIROブログになっていたようにせきろにかなりの時間を費やしていました。トータル120時間だそうです。ゲームはじゃあしばらくいいかな、と思っていたところハロウィンセールかなにかで兄がダクソ3を始めたらしく、じゃあやるかと思っていまはダクソ3をやっています。こっちはまだ43時間程度なので安心ですね。1周目を脳筋ビルドでラスボス倒してDLCがまだ、といったところですが、歯ごたえが足りなく感じてきたので新プロフで始めているところです。難易度については、SEKIROよりかんたんな気がしますね。SEKIRO は基本的にボスに勝つためにはボスを理解する必要がありますが、ダクソ3のほうではそもそも探索真面目にやってたらレベル上がってゴリゴリ殴ってたら勝てるみたいになりがちでした。完成がはやい脳筋ビルドなせいもあるとは思います。ちゃんと理解していかないと勝てないくらいのレベルに抑えたい。

終わりに

本記事の序文は敬愛する id:takeshinoda パイセン*3の記事を参考にさせていただきました。

また、こうした近況を書いて投稿する機会を作ってくれた、id:takkan_m さんもありがとうございました。といっても忘年会に参加したことあったかなかったかあやしいですが……。

*1:なお当然ながら私が怠惰でないことを示すわけでもありません

*2:それを突き詰めると工業用アルコール飲んでろ、になるのもわかっています

*3:このフレーズも https://twitter.com/highwide/status/1470691534681313281 を参考にさせていただいています

SEKIROを1周クリアした

もともと兄者のプレイ動画を一回見ていて、なんとなくのストーリーとボスは把握していた。

おもしろそうだなーと思いつつ未プレイだったが、2broのダクソ3だったり、今年のRTA In Japanの走りを見ていたりしたら気持ちが高まりやることにした。

フロムゲー面白そうに見えますよね、ボスとか地形に対して慣れていってだんだん最適化されていく感じとか。僕別にゲームそんなにうまくないんですが、自分がだんだんとボスに慣れていくのがわかってすごくよかったです。

以下ネタバレ込み

続きを読む