接著就是要來設定AWS Lambda+API Gateway了~
以下程式碼之後正式開發會不太一樣,所以先不詳細解釋,這邊就簡單開一個main.go複製貼上去做測試就好,目標是確認昨天的存在SSM的key們能被正常讀取,同時部署一個Gin框架做的簡易Linebot上去,確認AWS到Line這段是沒問題的~
package main
import (
"context"
"fmt"
"log"
"net/http"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
ginadapter "github.com/awslabs/aws-lambda-go-api-proxy/gin"
"github.com/gin-gonic/gin"
"github.com/line/line-bot-sdk-go/v7/linebot"
)
var (
ginLambda *ginadapter.GinLambda
ssmsvc *SSM
)
func init() {
ssmsvc = NewSSMClient()
lineSecret, err := ssmsvc.Param("CHANNEL_SECRET", true).GetValue()
if err != nil {
log.Println(err)
}
lineAccessToken, err := ssmsvc.Param("CHANNEL_ACCESS_TOKEN", true).GetValue()
if err != nil {
log.Println(err)
}
bot, err := linebot.New(
lineSecret,
lineAccessToken,
)
if err != nil {
log.Fatal(err)
}
// stdout and stderr are sent to AWS CloudWatch Logs
log.Printf("Gin cold start")
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.POST("/callback", func(c *gin.Context) {
// ctx := c.Request.Context()
events, err := bot.ParseRequest(c.Request)
if err != nil {
if err == linebot.ErrInvalidSignature {
log.Println(err)
c.JSON(http.StatusBadRequest, err)
} else {
log.Println(err)
c.JSON(http.StatusInternalServerError, err)
}
return
}
for _, event := range events {
if event.Type == linebot.EventTypeMessage {
switch message := event.Message.(type) {
case *linebot.TextMessage:
if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(message.Text)).Do(); err != nil {
log.Print(err)
}
case *linebot.StickerMessage:
replyMessage := fmt.Sprintf(
"sticker id is %s, stickerResourceType is %s", message.StickerID, message.StickerResourceType)
if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(replyMessage)).Do(); err != nil {
log.Print(err)
}
}
}
}
})
ginLambda = ginadapter.New(r)
}
func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// If no name is provided in the HTTP request body, throw an error
return ginLambda.ProxyWithContext(ctx, req)
}
func main() {
lambda.Start(Handler)
}
type SSM struct {
client ssmiface.SSMAPI
}
func Sessions() (*session.Session, error) {
sess, err := session.NewSession()
svc := session.Must(sess, err)
return svc, err
}
func NewSSMClient() *SSM {
// Create AWS Session
sess, err := Sessions()
if err != nil {
log.Println(err)
return nil
}
ssmsvc := &SSM{ssm.New(sess)}
// Return SSM client
return ssmsvc
}
type Param struct {
Name string
WithDecryption bool
ssmsvc *SSM
}
func (s *SSM) Param(name string, decryption bool) *Param {
return &Param{
Name: name,
WithDecryption: decryption,
ssmsvc: s,
}
}
func (p *Param) GetValue() (string, error) {
ssmsvc := p.ssmsvc.client
parameter, err := ssmsvc.GetParameter(&ssm.GetParameterInput{
Name: &p.Name,
WithDecryption: &p.WithDecryption,
})
if err != nil {
return "", err
}
value := *parameter.Parameter.Value
return value, nil
}
記得把"CHANNEL_SECRET","CHANNEL_ACCESS_TOKEN",換成你的SSM紀錄的KEY名稱
LINUX 比較簡單如下:
arm64:
GOOS=linux GOARCH=arm64 go build -tags lambda.norpc -o bootstrap main.go
x86_64:
GOOS=linux GOARCH=amd64 go build -tags lambda.norpc -o bootstrap main.go
然後把編譯後的二進制檔壓縮成zip
zip myFunction.zip bootstrap
而window比較麻煩,要先裝
go install github.com/aws/aws-lambda-go/cmd/build-lambda-zip@latest
x86_64: (記得CMD要開powershell)
$env:GOOS = "linux"
$env:GOARCH = "amd64"
$env:CGO_ENABLED = "0"
go build -tags lambda.norpc -o bootstrap main.go
build-lambda-zip -o myFunction.zip bootstrap
arm64:
$env:GOOS = "linux"
$env:GOARCH = "arm64"
$env:CGO_ENABLED = "0"
go build -tags lambda.norpc -o bootstrap main.go
build-lambda-zip -o myFunction.zip bootstrap
詳細可以參考下面網址
Deploy Go Lambda functions with .zip file archives - AWS Lambda
按到Lambda建立一個新的layer,上傳剛剛弄好的zip檔
Runtime選provided.al2(Amazon Linux 2),不要選Go 1.X (2023/12/31就不能用了)
然後去建立函式,一樣的設定
到裡面按新增層,選擇剛剛的layer
給角色增加SSM的許可
點進組態-許可,點進他幫我們創好的角色名稱
連接政策
搜尋SSM,打勾FullAccess,往下滑 ⇒新增許可
測試一下,選個APIGATEWAY的範本
有打到就可以來去設定API Gateway了~
到API GateWay的頁面,先建立一個REST API
然後,創建一個資源,設定代理資源,CORS打勾
然後綁定自己的Lambda函數
記得最後部屬API,階段名稱可以是production
回到Lambda就能成功看到綁定好了
如果上面都設定好,可以在上一步最後找到API Gateway的API endpoint。
接著用拿到的API endpoint來更新Line的webhook
因為我們寫的gin後端用來處理line的路由是/callback,所以我們把url最後面換掉~
像下面這樣:
如果更新成功,顯示success,就代表沒問題拉~ 那我們明天見:)