AWS SAM CLIをWindowsで動作させたとき、$sam buildでエラーが出た問題を解決した

AWS Lambdaをローカルで実行するには、AWS SAM CLIというものを使えばよいと聞いたので、 AWS SAM CLIを使ったLambdaのローカル実行と簡単デプロイを試してみた。

困ったこと(エラー内容)

$ sam buildを実行時、次のエラーが表示される

Error: PythonPipBuilder:Validation - Binary validation failed for python, searched for python in following locations  : ['C:\\Python38\\python.EXE', 'C:\\Users\\Owner\\AppData\\Local\\Programs\\Python\\Python36\\python.EXE', 'C:\\Users\\Owner\\AppData\\Local\\Microsoft\\WindowsApps\\python.EXE', 'C:\\Users\\Owner\\AppData\\Local\\Microsoft\\WindowsApps\\python3.EXE'] which did not satisfy constraints for runtime: python3.9. Do you have python for runtime: python3.9 on your PATH?

実行環境

詳細など

3年前の記事を参考にしているためか、OSの差異のためか(参考記事はMac、本記事はWindows10で実行している)、 $ sam init時に尋ねられる内容は参考記事とはやや異なっていた。 具体的には、「Which runtime would you like to use?」という質問事項はなかった。

また、AWS SAM, AWS CLIのインストールは次の公式リファレンスを参考にした。

AWS SAM CLIのインストール AWS CLI の最新バージョンを使用してインストールまたは更新を行う

ここで$ sam buildを実行したとき、次のエラーが表示された。

Error: PythonPipBuilder:Validation - Binary validation failed for python, searched for python in following locations  : ['C:\\Python38\\python.EXE', 'C:\\Users\\Owner\\AppData\\Local\\Programs\\Python\\Python36\\python.EXE', 'C:\\Users\\Owner\\AppData\\Local\\Microsoft\\WindowsApps\\python.EXE', 'C:\\Users\\Owner\\AppData\\Local\\Microsoft\\WindowsApps\\python3.EXE'] which did not satisfy constraints for runtime: python3.9. Do you have python for runtime: python3.9 on your PATH?

Python3.9入れてないからエラーが出ているだけだが、Pythonのインストーラを探しにいくと、どうやらもうインストーラが配布されていないようだった。

プログラムが3.9を探しにいってるのでエラーになっているとも考えられ、これを3.8を探すように修正すれば動作するのではないかと感じたので template.yamlを次のように修正したところ、問題なく実行できた。 (後続の$ sam local invokeについても、問題なく実行できた)

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  test_sam

  Sample SAM Template for test_sam

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3
    MemorySize: 128

    Tracing: Active
  Api:
    TracingEnabled: true
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.8  # ここを3.9から3.8へ修正
      Architectures:
      - x86_64
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

  ApplicationResourceGroup:
    Type: AWS::ResourceGroups::Group
    Properties:
      Name:
        Fn::Sub: ApplicationInsights-SAM-${AWS::StackName}
      ResourceQuery:
        Type: CLOUDFORMATION_STACK_1_0
  ApplicationInsightsMonitoring:
    Type: AWS::ApplicationInsights::Application
    Properties:
      ResourceGroupName:
        Ref: ApplicationResourceGroup
      AutoConfigurationEnabled: 'true'
Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: API Gateway endpoint URL for Prod stage for Hello World function
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: Hello World Lambda Function ARN
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: Implicit IAM Role created for Hello World function
    Value: !GetAtt HelloWorldFunctionRole.Arn

参考

devblog.thebase.in

GoogleChromeで画面全体を1枚でキャプチャする方法

Google Chromeで、拡張機能なしに画面全体をキャプチャする方法を見つけたので書き残しておく

pdfで出力した帳票をWebブラウザで表示し、その出力内容を検証する(エビデンスとしてExcelスクリーンショットを残し、アレコレと吹き出しをつける) みたいなことをするときに、 何度もキャプチャを撮る必要がなくなる

アコーディオンメニューを展開した結果をキャプチャするときにも使えそう

1.Developer tool上でCtrl+Shift+P

2.コマンドに"full"とか"capture"を打って"Caputure full size screenshot"を予測表示させる

3.Enterを押して保存

参考

Express.jsでAccess-Control-Allow-Originを設定しているのにCORSエラーが発生した

Vue.jsからExpressへPOSTリクエストを送信するとき、Access-Control-Allow-Originを設定しているにも関わらずCORSエラーが発生したので、その解決策をメモしておく。

WebセキュリティやHTTPにそもそも疎いため、特にpreflight requestについては別の機会に学び直したい。

ソースコード

app.post('/db_insert', async (req: Request, res: Response) => {

    // ブラウザを叩いても"test"が出力されないので、そもそもリクエストを受け取れていない?
    console.log("test");
    // 項目名
    const subject_name = req.body.subject_name;
    // 学習時間
    const used_time = req.body.used_time;

    const query = "INSERT INTO learning_list(subject_name, used_time) VALUES(?, ?)"
    const params = [subject_name, used_time] as any[];
    let con_res: any[] = []
    try {
        con_res = (await db_query(query, params, db_con)) as any[]
    } catch (err) {
        console.log(err)
    }
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5173')
    res.send(con_res)
})
<script setup>
import axios from "axios";
import { reactive } from "vue";

const url2 = "http://localhost:3000/db_insert";

const insert_form = reactive({
    subject_name: "",
    used_time: ""
})

const insertData = async () => {
    let result = await axios.post(url2, {
        subject_name: insert_form.subject_name,
        used_time: insert_form.used_time
    });
};
</script>

<template>
    <p>データ登録</p>
    <form>
        項目名<input type="text" v-model="insert_form.subject_name" /><br>
        学習時間<input type="text" v-model="insert_form.used_time" />
        <input type="button" value="INSERT!" @click="insertData" />
    </form>
</template>

原因

Preflight Requestというリクエストが送信されているためにCORSエラーが発生していた。(画像からも読み取れる)

対策

ミドルウェアの「cors」を導入した。

app.use(cors({
    origin: 'http://localhost:5173',
    credentials: true,
    optionsSuccessStatus: 200
}))

app.post('/db_insert', async (req: Request, res: Response) => {

    // 項目名
    const subject_name = req.body.subject_name;
    // 学習時間
    const used_time = req.body.used_time

    const query = "INSERT INTO learning_list(subject_name, used_time) VALUES(?, ?)"
    const params = [subject_name, used_time] as any[];
    let con_res: any[] = []
    try {
        con_res = (await db_query(query, params, db_con)) as any[]
    } catch (err) {
        console.log(err)
    }
    res.send(con_res)
})

参考

preflight requestやミドルウェアcorsについては次のサイトを参考にした。