hogehogeプログラマ

PHPer。趣味ではServerless Frameworkをメインで触ってます

Serverless のテンプレートでCloudFormationを学ぶ

指定したURLのスクリーンショットへの保存を
API Gateway + Lambda + S3で実現する為のテンプレートを利用して
CloudFormationのテンプレートについて学習した際の記録になります。

スタックを作成してみる

https://serverless.com/blog/building-a-serverless-screenshot-service-with-lambda/
から「Launch Stack」を押下するとアイルランドリージョン(eu-west-1)で
CloudFormationのスタック作成画面が表示されます。

各画面で以下を実施し

  • パラメータのScreenshotBucketNameにS3のバケット名を入力
  • AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。」にチェックを付与

作成ボタンを押下すると暫く(20分程)してアイルランドリージョンでAPI Gateway等が作成されます。

テンプレートを読む

https://s3-eu-west-1.amazonaws.com/serverless-screenshots-service/2016-09-23T12:50:03/template.yml が該当のYAML形式で記述されたテンプレートになります。

これにAWSのドキュメントとにらめっこしてコメントを追記してみました。

※ コメントがYAML形式に沿ってないのでこれをコピペしてもエラーになります

東京リージョン(ap-northeast-1)でスタックを作成する

最初に作成した際はアイルランドリージョンのまま作成しましたが
次は東京リージョンに変更して実施してみます。

f:id:hogehoge0604:20180118213844p:plain

CREATE_FAILED AWS::CloudFront::Distribution CloudFrontEndpoint Resource creation cancelled
CREATE_FAILED AWS::IAM::Policy IamPolicyLambdaExecution Resource creation cancelled
CREATE_FAILED AWS::Lambda::Function ListScreenshotsLambdaFunction Resource creation cancelled
CREATE_FAILED AWS::Lambda::Function TakeScreenshotLambdaFunction Error occurred while GetObject. S3 Error Code: PermanentRedirect. S3 Error Message: The bucket is in this region: eu-west-1. Please use this region to retry the request
CREATE_FAILED AWS::Lambda::Function CreateThumbnailsLambdaFunction Error occurred while GetObject. S3 Error Code: PermanentRedirect. S3 Error Message: The bucket is in this region: eu-west-1. Please use this region to retry the request

東京リージョンに切り替えて作成したところエラーとなってしまいました。

エラーの原因と解決

Lambdaに配置するzipファイルをS3から取得していますがS3のリージョンを見たところ
アイルランドリージョンになっています。
https://s3-eu-west-1.amazonaws.com/serverless-screenshots-service/2016-09-23T12:50:03/lambda-screenshots.zip

AWS Lambda 関数コード - AWS CloudFormation

このバケットは、Lambda 関数を作成するのと同じ AWS リージョン内に存在する必要があります。
Lambda 関数とバケットが同じリージョンにある限り、別の AWS アカウントからバケットを指定できます。

上記の通り、別リージョンのS3に取得に行ったことによるエラーになります。

AWS Lambda 関数コード - AWS CloudFormationを見た感じだとZipFileは関数のソースコードを埋め込むプロパティのようなのでS3にzipを配置する以外に手はなさそうです。そのため以下の対応を行いました。

  1. S3バケットを東京リージョンに立ててLambdaに設置するzipファイルを配置
  2. CloudFormationのテンプレートをダウンロードし以下のS3バケット名とパスの記述を全て1で作成した内容に変更する
S3Bucket: serverless-screenshots-service
S3Key: 2016-09-23T12:50:03/lambda-screenshots.zip
  1. CloudFormationにアクセスし「テンプレートをAmazon S3にアップロード」の方から修正したテンプレートを設定する

で再度スタックの作成を行ったところ無事に作成できました。

実際に実行してみる

curl -X POST https://djln6al5ml.execute-api.ap-northeast-1.amazonaws.com/dev/screenshots?url=http://google.com/ -H "x-api-key: {APIキー}"
{"message":"Forbidden"}

アクセス制限に掛かってしまいました……
調べたところAPI Gatewayの使用量プランを設定が無かったので
使用量プランを設定しステージとAPIキーを紐付けたら改めて実行します。

curl -X POST https://djln6al5ml.execute-api.ap-northeast-1.amazonaws.com/dev/screenshots?url=http://google.com/ -H "x-api-key: {APIキー}"
{
  "hash":"6ab016b2dad7ba49a992ba0213a91cf8",
  "key":"6ab016b2dad7ba49a992ba0213a91cf8/original.png",
  "bucket":"hogehoge-serverless-screenshots-service",
  "url":"https://d2iiuopwdwbiap.cloudfront.net/6ab016b2dad7ba49a992ba0213a91cf8/original.png"
}

スタックを削除する

一度でもAPIを動作しスクリーンショットを作成するとS3のバケット削除でエラーとなります。

f:id:hogehoge0604:20180118213841p:plain

DELETE_FAILED AWS::S3::Bucket S3BucketScreenshots The bucket you tried to delete is not empty

CloudFormationで作成したバケットの中身を空にしてから再度削除することで
綺麗に削除できました。
ちなみにAPI Gatewayの使用量プランは手動で作成したため
CloudFormationの管理外なので別途手動で削除する必要があります。

紆余曲折ありましたがこれでようやくServerlessなScreenshot APIが出来ました。
使用量プランの設定をCloudFormationのテンプレートに含めたいところですが
気が向いたらやってみて追記します。

Laravel PHPUnit レスポンスコードのアサーション遍歴

Laravel5.3 未満

レスポンスが200 OKかチェックしたい場合はassertResponseOkを利用
レスポンスコードを指定したい場合はassertResponseStatusを利用する

Laravel5.4

Illuminate\Foundation\Testing\TestResponse にて定義されているassertStatusを利用

https://laravel.com/api/5.4/Illuminate/Foundation/Testing/TestResponse.html
を見るとレスポンスが200 OKかチェックしたい場合はassertSuccessfulが利用できそうだけど
公式(https://laravel.com/docs/5.4/http-tests) も含め利用可能なアサーションには記述が無い
(実際に試してはないです)

Laravel5.5

レスポンスが200 OKかチェックしたい場合はassertSuccessfulを利用
レスポンスコードを指定したい場合はassertStatusを利用する
※ 5.5で利用可能なアサーションにassertSuccessfulが追記されているようです

Eloquent 登録日時と更新日時の小技

最後以外は普通にドキュメントに書いてあることですがたまに触ると忘れるのでメモがてら。

カラム名を変更したい

デフォルトは以下の通り

  • 登録日時: created_at
  • 更新日時: updated_at

変更する場合はCREATED_AT/UPDATED_ATを設定する

登録日時、更新日時を持たない

timestampsをfalseにする

登録日時のみ持ちたい

ちなみにIlluminate\Database\Eloquent\Concerns\HasTimestampsで定義されているメソッドを上書きしてもOKかと思ったけどgetUpdatedAtColumnでカラム名を取得し更新日時を設定している箇所が多々あり現実的ではなかったです。

Laravel5.3 APIのルーティング

久し振りにLaravelを入れたところルーティング周りが
変更されていたので軽く触ってみました。

APIのルーティングはapi.phpにて管理します。
試しに以下のような設定を追加します。

/api/echoにアクセスするとHello Worldが表示されます。
api.phpはApp\Providers\RouteServiceProvider#mapApiRoutesで
prefixとして/apiが設定されているのでURLは/api/echoになります。

prefixを変更することで/api以外にも変更可能です。

個人的にはルーティングを管理する1ファイルでURLが明確に判るようにしたいので
RouteServiceProviderでprefixが差し込まれるのはあまり好きではないですが
変更は容易ですし以前よりも管理もしやすくなってるように思います。

MariaDB 10.2.1からレコードのデフォルト値の設定が柔軟に

https://mariadb.com/kb/en/library/create-table/#default

From MariaDB 10.2.1 you can use most functions in DEFAULT. Expressions should have parentheses around them. If you use a non deterministic function in DEFAULT then all inserts to the table will be replicated in row mode. You can even refer to earlier columns in the DEFAULT expression:

試しに以下の様なSQL文を発行してみます。

CREATE TABLE t1 (
  seq int PRIMARY KEY,
  code char(6) DEFAULT ( CONCAT("A", LPAD(seq, 5, 0)) )
)

正常終了しました。
ちなみにMariaDB 5.5.56で実行すると以下のエラーとなります。

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '( CONCAT("A", LPAD(seq, 5, 0)) )
)' at line 3

次は以下のSQLを実行してみます。

CREATE TABLE t2 (
  seq int PRIMARY KEY AUTO_INCREMENT,
  code char(6) DEFAULT ( CONCAT("A", LPAD(seq, 5, 0)) )
)

すると以下のエラーが発生してしまいました。
どうやらAUTO_INCREMENTと組み合わせて利用することは出来無いようです。

ERROR 1901 (HY000): Function or expression 'AUTO_INCREMENT' cannot be used in the DEFAULT clause of `seq`

続いては以下SQLでBLOBでもできるのか試してみます。

CREATE TABLE t3 (
  seq int PRIMARY KEY AUTO_INCREMENT,
  data blob DEFAULT ( COLUMN_CREATE("id", "1") )
)

正常に完了しました。
Dynamic Columnsも利用できるようです。(まず使わないと思いますが)

AUTO_INCREMENTと併用するとエラーとなったのは残念ですが
デフォルト値を柔軟に設定できるようになったのは便利なので利用できる場面では
ドンドン使っていきたいところです。

最近お仕事でよく利用するAmazon RDSはMariaDB 10.1なので
まだ当面利用はできませんけどね笑

http://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/CHAP_MariaDB.html

2018/1/11追記:
RDSでMariaDB 10.2がサポートされたようです。
Amazon RDS で MariaDB 10.2 がサポート可能になりました