AlexaスキルでGo


前回(Alexa-hostedスキルを使用してAlexaスキルを作ってみる)、Go言語でAlexaスキルで作ろうと思って始めたものの、途中から脱線してAlexa-hostedスキルの方に流れてしまいました。
改めてGo言語で。前回作ったやつのバックエンド部分は捨てる想定です。
なので作るスキルは一緒。

Go言語をLambdaにデプロイする

しかし、やはりというかなんというか、すでにやっている方おられますね。
AlexaスキルをGoとServerlessで書いてみた

このままやったらだいたいできるんじゃないかしら。
進めていて詰まる所があったら記します。なければこの記事には公開の意義ないかもしれん。

まずはこちらを参考に進める

$ serverless create -u https://github.com/serverless/serverless-golang/ -p alexa-kamefood-go
$ cd alexa-kamefood-go
$ vi serverless.yml
$ diff serverless.yml serverless.yml.orig 
25,26c25,26
<   stage: dev
<   region: ap-northeast-1
---
> #  stage: dev
> #  region: us-east-1
57,59d56
<     memorySize: 128
<     events:
<       - alexaSkill
$ go get github.com/aws/aws-lambda-go/lambda

で、おおよそこちらのレポジトリの内容でdeployしたらうまくいきました。

$ sls deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Warning! You are using an old syntax for alexaSkill which doesn't restrict the invocation solely to your skill. Please refer to the documentation for additional information.
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service alexa-kamefood-go.zip file to S3 (10.04 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
...................
Serverless: Stack update finished...
Service Information
service: alexa-kamefood-go
stage: dev
region: ap-northeast-1
stack: alexa-kamefood-go-dev
resources: 6
api keys:
  None
endpoints:
  None
functions:
  hello: alexa-kamefood-go-dev-hello
layers:
  None

で、alexa-kamefood-go-dev-hello というfunctionができております。(functionの名称どうでも良かったのでhelloのままです。。)

テストしても普通に使える。
結局、すんなりといきました。

コードを書く

その後、こちらをインスパイアしつつ、コードを仕上げていきます。
なお、json読みこむようにしたのでserverless.yml 少し変えました

serverless.yml
package:
 exclude:
   - ./**
 include:
   - ./bin/**
   - ./jsons/**

jsonのあたりのコードはこんな感じです。

main.go
var (
        // ErrInvalidIntent is error-object
        ErrInvalidIntent = errors.New("Invalid intent")
        FoodJsonFile string = "./jsons/foods.json"
)

.....

        // JSONファイル読み込み
        foods := make(map[string]string)
        bytes, err := ioutil.ReadFile(FoodJsonFile)
        if err != nil {
            log.Fatal(err)
        }
        // JSONデコード
        if err := json.Unmarshal(bytes, &foods); err != nil {
            log.Fatal(err)
        }

        if food, ok := intent.Slots["foodSlot"]; ok {
            if speech, ok := foods[food.Value]; ok {
                speechOutput = speech
            }else{
                speechOutput = "すみません。" + food.Value + "についてはわかりませんでした。他の食べ物の名前を聞いてみてくださいね。ではまた。"
            }
        } else {
            speechOutput = "すみません。わかりませんでした。他の食べ物の名前を聞いてみてくださいね。ではまた。"
        }

encoding/json を使っています。
なお、コードについては参照元のライセンス表記がなく、ちょっと公開するのがグレーなので公開は控えておきます。

申請

実はAlexa-hostedのままで申請をしていたのですが、取り下げつつ、Goのlambda functionのものと差し替えてみました。

で、切り替え後のテスト

うん、大丈夫そう。
Go言語の方のLambda functionで申請してみます。

おまけ

で、これで終わったらちょっとつまんないので少し遊ぶ。
git hubに上げたら勝手にdeployするようにしたいです。
aws codestarを使ってみます。

開いてロール作成

Go言語を選択し、GitHubアカウントに連携します

buildspec.ymlとtemplate.yml編集

けっこう適当なので余計な部分とかあるかもですが。

buildspec.yml
version: 0.2

phases:

  install:
    commands:

      # AWS Codebuild Go images use /go for the $GOPATH so let's symlink our
      # application source code into that directory structure.
      #- ln -s "${CODEBUILD_SRC_DIR}" "/go/src/handler"

  pre_build:
    commands:

      # Make sure we're in the project directory within our GOPATH
      #- cd "/go/src/handler"

      # Fetch all dependencies
      - go get github.com/aws/aws-lambda-go/lambda
      - go get encoding/json
      - go get io/ioutil

  build:
    commands:
      # Build our go application
      - go build -o bin/main main.go
      #- serverless deploy -v
      # Copy static assets to S3, and package application with AWS CloudFormation/SAM
      - aws cloudformation package --template template.yml --s3-bucket $S3_BUCKET --output-template template-export.yml

  post_build:
    commands:
      # Do not remove this statement. This command is required for AWS CodeStar projects.
      # Update the AWS Partition, AWS Region, account ID and project ID in the project ARN on template-configuration.json file so AWS CloudFormation can tag project resources.
      - sed -i.bak 's/\$PARTITION\$/'${PARTITION}'/g;s/\$AWS_REGION\$/'${AWS_REGION}'/g;s/\$ACCOUNT_ID\$/'${ACCOUNT_ID}'/g;s/\$PROJECT_ID\$/'${PROJECT_ID}'/g' template-configuration.json

artifacts:
  type: zip
  files:
    - template-export.yml
    - template-configuration.json
template.yml
AWSTemplateFormatVersion: 2010-09-09
Transform:
- AWS::Serverless-2016-10-31
- AWS::CodeStar

Parameters:
  ProjectId:
    Type: String
    Description: AWS CodeStar projectID used to associate new resources to team members
  CodeDeployRole:
    Type: String
    Description: IAM role to allow AWS CodeDeploy to manage deployment of AWS Lambda functions
  Stage:
    Type: String
    Description: The name for a project pipeline stage, such as Staging or Prod, for which resources are provisioned and deployed.
    Default: ''

Globals:
  Function:
    AutoPublishAlias: live
    DeploymentPreference:
      Enabled: true
      Type: Canary10Percent5Minutes
      Role: !Ref CodeDeployRole

Resources:
  hello:
    Type: AWS::Serverless::Function
    Properties:
      Handler: bin/main
      Runtime: go1.x
      Role:
        Fn::GetAtt:
        - LambdaExecutionRole
        - Arn
      Events:
        AlexaSkillEvent:
          Type: AlexaSkill
  LambdaExecutionRole:
    Description: Creating service role in IAM for AWS Lambda
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'CodeStar-${ProjectId}-Execution${Stage}'
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service: [lambda.amazonaws.com]
          Action: sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        -  arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      PermissionsBoundary: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/CodeStar_${ProjectId}_PermissionsBoundary'

いらないファイルを削ったり、ソースを置いたり

publicとか、main-test.goとか消しました。
あとはソースを上書き。

githubにpush

で勝手にlambda関数が作られます。
ちゃんと動きました。

CodeStar使って、Codepipelineの一式(CodeCommit or GitHub, CodeBuild, CodeDeploy)がまるっと作られた感じです。
あら、便利。

というか、CodeBuild内でSeverless FrameWorkのコマンド書いちゃえばCode Buildだけでもできそう。