🐍
[Python×Lambda] CloudFrontのキャッシュ削除のソースコードとテストコード
概要
Lambda(Python)で、CloudFrontのキャッシュ削除するための、ソースコードとそのテストコード(覚え書き)
関数のパフォーマンスを向上させるため、Lambdaのベストプラクティスに沿って、実行環境の再利用を活用して関数ハンドラー外で SDK クライアントを宣言しています
コード
- 言語: Python3
- テストライブラリ: Pytest, moto
ソースコード
lambda_function.py
import boto3
import os
from datetime import datetime
# CloudFrontクライアントとディストリビューションIDの初期化
cloudfront = boto3.client('cloudfront')
distribution_id = os.environ.get('DISTRIBUTION_ID')
def lambda_handler(event, context):
# 無効化バッチの設定
invalidation_batch = {
'CallerReference': f'invalidate-{context.aws_request_id}',
'Paths': {
'Quantity': 1, # 無効化するパスの数
'Items': ['/*'] # 無効化するパスのリスト
}
}
# 無効化リクエストの送信
response = cloudfront.create_invalidation(
DistributionId=distribution_id,
InvalidationBatch=invalidation_batch
)
# `Invalidation`オブジェクトから必要な情報を抽出
invalidation_id = response['Invalidation'].get('Id')
invalidation_status = response['Invalidation'].get('Status')
create_time = response['Invalidation'].get('CreateTime')
create_time_iso = create_time.isoformat() if create_time else None
# レスポンスを返却
return {
'StatusCode': response['ResponseMetadata']['HTTPStatusCode'],
'InvalidationId': invalidation_id,
'InvalidationStatus': invalidation_status,
'CreateTime': create_time_iso
}
テストコード
test_lambda_function.py
import os
import pytest
from moto import mock_aws
import boto3
from datetime import datetime
@pytest.fixture(scope="function")
def aws_credentials():
"""Mocked AWS credentials for moto."""
os.environ["AWS_ACCESS_KEY_ID"] = "testing"
os.environ["AWS_SECRET_ACCESS_KEY"] = "testing"
os.environ["AWS_SECURITY_TOKEN"] = "testing"
os.environ["AWS_SESSION_TOKEN"] = "testing"
os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
@pytest.fixture(scope="function")
def s3_bucket():
"""Mocked S3 bucket using moto."""
with mock_aws():
s3 = boto3.client('s3', region_name='us-east-1')
bucket_name = 'test-bucket'
s3.create_bucket(Bucket=bucket_name)
yield bucket_name
@pytest.fixture(scope="function")
def cloudfront_client(aws_credentials, s3_bucket):
"""Mocked CloudFront client using moto."""
with mock_aws():
client = boto3.client('cloudfront', region_name='us-east-1')
# CloudFrontディストリビューションの作成(モック化したS3バケットを使用)
distribution_config = {
'CallerReference': 'unique-caller-reference',
'Origins': {
'Quantity': 1,
'Items': [
{
'Id': 'origin-1',
'DomainName': f'{s3_bucket}.s3.amazonaws.com',
'OriginPath': '',
'CustomHeaders': {'Quantity': 0},
'S3OriginConfig': {'OriginAccessIdentity': ''}
}
]
},
'DefaultCacheBehavior': {
'TargetOriginId': 'origin-1',
'ViewerProtocolPolicy': 'allow-all',
'AllowedMethods': {
'Quantity': 2,
'Items': ['GET', 'HEAD'],
'CachedMethods': {'Quantity': 2, 'Items': ['GET', 'HEAD']}
},
'ForwardedValues': {
'QueryString': False,
'Cookies': {'Forward': 'none'},
'Headers': {'Quantity': 0},
'QueryStringCacheKeys': {'Quantity': 0}
},
'MinTTL': 0,
'DefaultTTL': 86400,
'MaxTTL': 31536000,
'Compress': False,
'LambdaFunctionAssociations': {'Quantity': 0}
},
'Comment': '',
'Enabled': True
}
# モック化ディストリビューションの作成
response =client.create_distribution(DistributionConfig=distribution_config)
# 取得したディストリビューションIDを環境変数に設定
os.environ['DISTRIBUTION_ID'] = response['Distribution']['Id']
yield client
def test_lambda_handler(cloudfront_client):
"""Test the lambda_handler function."""
# Lambdaハンドラーのimport
from lambda_function import lambda_handler
# Lambdaハンドラー用のイベントとコンテキスト
event = {}
context = type('Context', (object,), {'aws_request_id': '12345'})()
# create_invalidation レスポンスのモック化
mock_response = {
'ResponseMetadata': {
'HTTPStatusCode': 201
},
'Invalidation': {
'Id': 'I0123456789',
'Status': 'InProgress',
'CreateTime': datetime.now()
}
}
# モックされたcreate_invalidationメソッド
cloudfront_client.create_invalidation = lambda DistributionId,InvalidationBatch: mock_response
# 実際のcloudfrontクライアントをモックされたクライアントに置き換え
original_cloudfront_client = lambda_handler.__globals__['cloudfront']
lambda_handler.__globals__['cloudfront'] = cloudfront_client
try:
# lambda_handlerの呼び出し
response = lambda_handler(event, context)
# レスポンスの検証
assert response['StatusCode'] == 201
assert response['InvalidationId'] == 'I0123456789'
assert response['InvalidationStatus'] == 'InProgress'
assert 'CreateTime' in response
assert isinstance(response['CreateTime'], str)
finally:
# テスト後に元のclientに戻す
lambda_handler.__globals__['cloudfront'] = original_cloudfront_client
ポイント
- import(
from lambda_function import lambda_handler
)をテスト関数の中で実行すること - 実際(lambda_function.py)のcloudfrontクライアントをモックされたクライアントに置き換えること
参考
- boto3 のインスタンス生成をグローバルで行っても moto を利用したユニットテストを成功させるには? | かんがるーさんの日記
- 【pytest】モックの使い方まとめ | Zenn
- moto | GitHub
- Moto: Mock AWS Services
※ChatGPT(4o)のお力をかなりお借りしました。
Discussion