Quantcast
Channel: kakakakakku blog
Viewing all 903 articles
Browse latest View live

AWS CLI で Amazon Inspector「抑制ルール」を設定する

$
0
0

Amazon Inspector の「抑制ルール (suppression rules)」を使うと,条件に一致する検出結果を除外できる.また Amazon Inspector では FindingStatus: SUPPRESSEDとして AWS Security Hub に検出結果を統合するため,過剰に増えてしまう AWS Security Hub 側の通知を減らすこともできる👌

docs.aws.amazon.com

今回は Amazon Inspector の「抑制ルール」を AWS CLI で設定してみた❗️

aws inspector2 create-filterコマンド

aws inspector2 create-filterコマンドを使って設定できる.

awscli.amazonaws.com

今回はサンプルとして Amazon Linux 2023 Security Advisoriesに載っている CVE を適当にピックアップして抑制する.注意点としては vulnerabilityIdに設定できる CVE は API (FilterCriteria) 側で「最大10個まで」という仕様になっているところ.多くの CVE を抑制する場合は複数の抑制ルールを設定する必要がある📝(それはもう根本的な対応が必要な状況にも感じるけど...😇)

$ aws inspector2 create-filter \--name cve-suppression \--action SUPPRESS \--filter-criteria'{"vulnerabilityId": [      {"comparison": "EQUALS", "value": "CVE-2022-41723"},      {"comparison": "EQUALS", "value": "CVE-2023-39326"},      {"comparison": "EQUALS", "value": "CVE-2023-6597"}    ]  }'{"arn": "arn:aws:inspector2:ap-northeast-1:111111111111:owner/111111111111/filter/f1e2c67b2501b7b1"}

抑制ルール(設定後)

aws inspector2 update-filterコマンド

aws inspector2 update-filterコマンドを使って更新できる.更新するときは --filter-arnオプションに ARN を指定することを忘れずに👍

$ aws inspector2 update-filter \--filter-arn arn:aws:inspector2:ap-northeast-1:111111111111:owner/111111111111/filter/f1e2c67b2501b7b1 \--filter-criteria'{"vulnerabilityId": [      {"comparison": "EQUALS", "value": "CVE-2022-41723"},      {"comparison": "EQUALS", "value": "CVE-2023-39326"},      {"comparison": "EQUALS", "value": "CVE-2023-6597"},      {"comparison": "EQUALS", "value": "CVE-2024-20290"},      {"comparison": "EQUALS", "value": "CVE-2024-21506"}    ]  }'{"arn": "arn:aws:inspector2:ap-northeast-1:111111111111:owner/111111111111/filter/f1e2c67b2501b7b1"}

抑制ルール(更新後)

Terraform AWS Provider サポート外

現時点(2024年5月)だと Terraform AWS Provider では Amazon Inspector の抑制ルールを設定できないという制約がある💦

github.com


Terraform の AWS Cloud Control Provider (awscc) で Amazon Inspector「抑制ルール」を設定しよう

$
0
0

現時点(2024年5月)だと Terraform AWS Provider では Amazon Inspector の「抑制ルール」を設定できないという制約がある💦

github.com

Terraform AWS Provider 以外だと

を使って Amazon Inspector の「抑制ルール」を設定することもできるけど,AWS Cloud Control Provider (awscc)inspectorv2_filterリソースを使って設定することもできる❗️今回はサンプルとして Amazon Linux 2023 Security Advisoriesに載っている CVE を適当にピックアップして抑制してみた.

resource"awscc_inspectorv2_filter""suppression"{name          = "cve-suppression-awscc"filter_action = "SUPPRESS"filter_criteria = {vulnerability_id = [{comparison = "EQUALS"value      = "CVE-2022-41723"},
      {comparison = "EQUALS"value      = "CVE-2023-39326"},
      {comparison = "EQUALS"value      = "CVE-2023-6597"},
    ]}}

簡単👌

抑制ルール(設定後)

関連記事

前に AWS Chatbot を AWS Cloud Control Provider (awscc) で構築するのも試した🤖

kakakakakku.hatenablog.com

テスタビリティ(テスト容易性)に関してチームで考えるエクササイズがたくさん!Team Guide to Software Testability を読んだ

$
0
0

ソフトウェアのテスタビリティ(テスト容易性)に関してまとまった書籍「Team Guide to Software Testability」を読んだ📕

本書の特徴は「テスタビリティ」を重要視してソフトウェアの品質・デリバリーの予測可能性を高めていくために「どのようにチームで取り組んでいくか」というチーム目線で技術的な施策と組織的な施策が紹介されている点かなと💡後述するエクササイズも充実していて,読んで良かったな〜と思える一冊だった.テスタビリティに含まれる品質特性に関しては 優れたテスト容易性を実現するためのポイント | PrAhaENGINEERLABに詳しくまとまっていて,あわせて読むと相乗効果があると思う📝

本書は Team Guideというシリーズで計4種類出版されていて,他のテーマも気になるぞー \( 'ω')/ ちなみに Leanpub だと4冊セットで安く買えたりもする👌

  • Team Guide to Software Operability
  • Team Guide to Metrics for Business Decisions
  • Team Guide to Software Testability
  • Team Guide to Software Releasability

leanpub.com

最近ソフトウェアのテスト全般に課題のあるプロダクトを支援していて,参考になる情報をインプットする中で Team Guide to Software Testabilityが良さそうだったので読むことにしたという経緯がある💡ちなみに Team Guide to Software Testability は「Web API テスト技法」を読んでいたら第7章で紹介されていて知った.

目次

  • Exercises
  • Introduction
  • 1: Set a pragmatic direction for improving testability using trade-off sliders
  • 2: Create testability targets to improve interactions with dependencies
  • 3: Adopt testability mapping to expose hard-to-test architectures
  • 4: Apply the CODS model to increase architectural testability for faster feedback
  • 5: Adopt ephemeral development environments for fast feedback
  • 6: Use production data to enhance your testing strategy
  • 7: Use team testing reviews to enable sustainable delivery
  • Appendix - Notes on 10 P’s of Testability

テスタビリティの重要さ

Introduction ではテスタビリティの重要さがコンパクトにまとまっていた.テスタビリティの価値を再確認できて良かった👌

  • Team dynamics(チームのダイナミクス)
  • Predictability(予測可能性)
  • Supporting your business and customers(ビジネスと顧客のサポート)
  • Fast feedback(迅速なフィードバック)
  • Data, not opinion(意見ではなくデータ)

本書は TDD (Test-Driven Development) と直接的な関係があるわけではないけど,TDD はテスタビリティを意識する機会になると思うし,Fast feedbackに関しては fukabori.fm #114 で話題に出ていたフィードバックの話にもつながるな〜と思いながら読んでいた📻

fukabori.fm

エクササイズ

本書の良いところは,テスタビリティに関する解説だけでなく,各章に「エクササイズ」というチームで取り組むイベントが紹介されていること❗️エクササイズの内容以外に「準備すること・ファシリテーションのコツ・FAQ・ゴール・アウトプット」などもまとまっていて,取り組みやすいようになっていた.

特にエクササイズに取り組むことを考えると「ファシリテーションの良し悪し」で効果が全然変わってきそうで重要だと感じた.

  • Chapter 1 exercises
    • Team Test for Testability
      • 1.2 Exercise: do the Team Test for Testability for a quick testability health check
    • Trade Off Sliders
      • 1.3 Exercise: use Trade-Off Sliders to guide your testability focus
  • Chapter 2 exercises
    • Testability Dependency Targets
      • 2.2 Exercise: employ Testability Dependency Targets to improve interactions with dependent teams and systems
  • Chapter 3 exercises
    • Testing Smells
      • 3.3 Exercise: Use testing smells to diagnose poor architectural testability
    • Testability Mapping
      • 3.4 Exercise: adopt testability mapping to measure testing feedback and waste
  • Chapter 4 exercises
    • CODS model
      • 4.3 Exercise: Use ‘CODS’ to increase architectural testability
  • Chapter 5 exercises
    • Agile Test Quadrants
      • 5.3 Exercise: Use the Agile Test Quadrants to extend testing in your development environment
  • Chapter 6 exercises
    • Data from Production
      • 6.2 Exercise: employ data from production to keep your test strategy relevant
  • Chapter 7 exercises
    • 10 P’s of Testability
      • 7.3 Exercise: use the 10 P’s of Testability to track team testing culture
    • Incident Reviews
      • 7.4 Exercise: adopt incident reviews to target testability improvement actions

どのエクササイズも良かったけど,特に良くてすぐに取り組んでみようと思った2つを紹介する👏

Testing Smells

このエクササイズでは以下の「15項目の課題」に対して,どれがテスタビリティの非効率さに影響を与えているのかを洗い出す.

  • Too many production issues(運用上の問題が多すぎる)
  • Pre-release regression cycles(リリース前に行う回帰テストのサイクル)
  • Lack of automation & exploratory testing(自動化と探索的テストが不足している)
  • Hesitance to change code(コード変更に対する躊躇)
  • Testing not considered during architectural design(アーキテクチャ設計時にテストが考慮されていない)
  • Team constantly seeking more testers(チームが常にテスト担当者を探している)
  • Too many slow user interface tests(UI テストが遅すぎる)
  • Important scenarios not tested(重要なシナリオがテストされていない)
  • Ineffective unit and integration tests(非効率な単体テストと統合テスト)
  • Cluttered, ineffective logging(適当で非効率なログ)
  • Flaky nondeterministic automation(不安定な自動化)
  • Tests that contain duplication & irrelevant detail(重複しているテストと不要なテスト)
  • Issues are difficult to reproduce(再現できない問題)
  • Issues are difficult to isolate & debug(問題を切り分けてデバッグできない問題)
  • Too much effort spent writing, maintaining and debugging automation(自動化のメンテナンスとデバッグが大変すぎる)

そして,それぞれの項目に対して以下の 1-5のスコアを付ける.最終的にチームで優先的に取り組むべき(取り除くべき)課題のトップ3を洗い出して,ディスカッションをする❗️

  • 1: No impact on team effectiveness(チームの効率さに影響なし)
  • 2: Small impact on team effectiveness(チームの効率さに少し影響がある)
  • 3: Moderate impact but rarely impacts team effectiveness(ある程度の影響はあるがチームの効率さにはほとんど影響なし)
  • 4: Moderate impact but often impacts team effectiveness(ある程度の影響がありチームの効率さにも頻繁に影響がある)
  • 5: Large impact, almost always impacts team effectiveness(影響が大きく常にチームの効率さに影響がある)

10 P’s of Testability

このエクササイズでは以下の「10項目の観点」に対して,どれがテスタビリティに悪い影響を与えているのかを洗い出す.

  • People(人々)
  • Philosophy(行動指針)
  • Product(プロダクト)
  • Process(プロセス)
  • Problem(問題)
  • Project(プロジェクト)
  • Pipeline(パイプライン)
  • Productivity(生産性)
  • Production Issues(本番環境の課題)
  • Proactivity(積極性)

そして,それぞれの項目に対して以下の3種類から投票する.最終的にテスタビリティに悪い影響を与えている項目を洗い出して,改善策をディスカッションする❗️

  • happy(満足)
  • unhappy(不満)
  • no opinion(意見なし)

「テスタビリティに課題がある」と言っても,チームによって原因は違っていて,さらにメンバー間の価値観の違いなどもあるため,テスタビリティに対する認識合わせができることは重要だと思う.よって,このエクササイズはすぐにでも取り組みたいと思った💪例えば四半期ごとに実施すればテスタビリティの変化を計測できるというメリットもあると思う.

まとめ

「Team Guide to Software Testability」を読んだ📕読んで終わりではなく,実際にチームでエクササイズに取り組むことでさらに効果が得られる一冊だった.ソフトウェアのテスタビリティ(テスト容易性)に課題を感じていたら読んでみると良いのではないでしょうか〜 \( 'ω')/

関連ポスト

関連書籍

ちょうど今読んでいる「ソフトウェアアーキテクチャメトリクス」の3章にもソフトウェアを進化させるアーキテクチャ特性として「テスト容易性」が重要と書かれていた👀

AWS CDK でプレフィックス集計のために Amazon S3 Storage Lens を設定する

$
0
0

Amazon S3 でオブジェクトの使用状況(合計サイズ・平均サイズなど)を「プレフィックス別(フォルダ別)」で可視化する場合,Amazon S3 Storage Lens の「高度なメトリクスとレコメンデーション機能」「プレフィックス集計」を設定する❗️

docs.aws.amazon.com

今回は AWS CDK で aws_s3.CfnStorageLensを使って「プレフィックス集計」を設定してみた.設定の詳細な仕様は AWS CloudFormation ドキュメント参照📝

そして,Amazon S3 Storage Lens の設定をシンプルにするため

  • 集計するプレフィックスの階層は 1
  • 集計対象にするプレフィックスの最小サイズ閾値は 1%(設定できる最低値)
  • 集計対象は新しく作った Amazon S3 バケットに限定

とした👌

docs.aws.amazon.com

docs.aws.amazon.com

サンプルコードを載せておく.ちなみに検証で使った kakakakakku-sandbox-storage-lensバケットは既に削除してある🗑

import{
  Stack,
  StackProps,
  aws_s3,}from'aws-cdk-lib'import{ Construct }from'constructs'exportclass SandboxCdkS3Stack extends Stack {constructor(scope: Construct, id: string, props?: StackProps){super(scope, id, props)const bucket =new aws_s3.Bucket(this,'Bucket',{
      bucketName: 'kakakakakku-sandbox-storage-lens',})new aws_s3.CfnStorageLens(this,'StorageLens',{
      storageLensConfiguration: {
        id: 'storage-lens',
        isEnabled: true,
        accountLevel: {
          bucketLevel: {
            prefixLevel: {
              storageMetrics: {
                selectionCriteria: {
                  delimiter: '/',
                  maxDepth: 1,
                  minStorageBytesPercentage: 1,}}}}},
        include: {
          buckets: [
            bucket.bucketArn
          ]}}})}}

そして prefix-1から prefix-5までのフォルダにサンプルファイルをアップロードして,数日待ったらプレフィックス別の使用状況を確認できるようになっていた👏

prefix-1/
prefix-2/
prefix-3/
prefix-4/
prefix-5/

以下にプレフィックス集計結果の一部を載せておく👀

Amazon S3 Storage Lens プレフィックス集計結果 1

Amazon S3 Storage Lens プレフィックス集計結果 2

アプリケーションの認可ソリューションを体験できる「Amazon Verified Permissions workshop」

$
0
0

Amazon Verified Permissions を学ぶため「Amazon Verified Permissions workshop」を実施してみた💡3時間ほどで Amazon Verified Permissions の基本的な概念・マネジメントコンソール操作から,実際にサーバーレスアプリケーションに組み込んだときの実装例も学べてとても良かった❗️

boto3 の操作だと,通常の is_authorized()以外に is_authorized_with_token()batch_is_authorized()も出てくる💡もし Amazon Verified Permissions をまだ試したことがないけど気になってる人がいたらおすすめ.個人的な実施メモをまとめておこうと思う \( 'ω')/

catalog.workshops.aws

Lab 1 - Amazon Verified Permissions Console Demo

まず,マネジメントコンソールで Amazon Verified Permissions に入門する.ポリシーストア・ポリシーを設定して,テストベンチを使って認可リクエストの動作確認をする👌

alice は LIST#000001 を ReadList できる🙆‍♂

alice は LIST#000002 を ReadList できない🙅‍♂

さらに認可仕様のスキーマを設定する.今回は TinyTodoという TODO リストを管理するアプリケーションを題材にしているため「タスク」「タスクリスト」を操作するアクションをスキーマに設定している✅ スキーマによってポリシーを厳格に設定できることを確認し,またポリシーテンプレートも活用することでポリシーを再利用できるようにした.Lab 1で Amazon Verified Permissions の基本的な流れを掴むことができた.

スキーマ(アクション図)

Lab 2 - Application Built on Amazon Verified Permissions

Lab 2 からは Amazon Verified Permissions を TinyTodo アプリケーションに組み込んでいく❗️Amazon Verified Permissions 以外は AWS CloudFormation で簡単に構築できる.サーバーレスアーキテクチャに理解があれば良いと思うけど,もし Amazon API Gateway / AWS Lambda / Amazon Cognito / Amazon DynamoDB に慣れていなければ,ワークショップを進める前にデプロイされたサービスの設定などをザッと確認しておくと進めやすくなると思う👌

TinyTodo アーキテクチャ図は Lab 2 から引用

あとは API を実行してタスク一覧の取得・新しいタスクの登録を試す.boto3 で is_authorized()を実行する実装の流れも理解できた.

boto3.amazonaws.com

$ curl -X POST "$WS_API_ENDPOINT/task/create"\>-H"Content-Type: application/json"\>-H"Authorization: Bearer $WS_ACCESS_TOKEN_ALICE"\>-d'{quote>         "listId": 1,quote>         "name": "Task 7 - Execute The Project Plan",quote>         "description": "Once you have got your project plan in place, it is time to execute it—and bring your project to life!"quote>       }' | jq
{"taskId": 7}

$ curl -X POST "$WS_API_ENDPOINT/task/create"\>-H"Content-Type: application/json"\>-H"Authorization: Bearer $WS_ACCESS_TOKEN_ALICE"\>-d'{quote>         "listId": 2,quote>         "name": "Task 7 - Execute The Project Plan",quote>         "description": "Once you have got your project plan in place, it is time to execute it—and bring your project to life!"quote>       }' | jq
{"message": "Access denied -- permissions check failed"}

さらに Lab 2 の最後は boto3 で create_policy()を実行して,アプリケーションからポリシーを追加する実装もあって参考になった❗️へぇ〜

boto3.amazonaws.com

Lab 3 - Using Identity Sources

Amazon Cognito User Pool の「カスタム属性」として project: Project-Alphaを追加して,認証時の ID Token (JWT) に含まれる custom:projectをそのまま認可リクエストに使う流れを体験した.そして,boto3 で is_authorized_with_token()を実行すると ID Token (JWT) をそのまま渡せることも学べた❗️

ID Token (JWT) を渡せるとは言え,boto3 のドキュメントに At this time, Verified Permissions accepts tokens from only Amazon Cognito.と書いてあるのは注意点かなと💡

boto3.amazonaws.com

Lab 4 - Authorization Improvements

最後の Lab 4 ではもともと意図的に組み込まれていた実装上の誤りを修正しつつ,タスクリストの共有機能の動作確認を進めていく.そして,Amazon Verified Permissions への認可リクエストが過剰に増えてしまうことを AWS X-Ray で確認しつつ,boto3 の batch_is_authorized()を使って改善する.

boto3.amazonaws.com

batch_is_authorized() 活用前

batch_is_authorized() 活用後

認可リクエストをまとめて実行できる機能が提供されているのも知らなくて勉強になった❗️2023年11月にリリースされた機能だったのか〜 \( 'ω')/

aws.amazon.com

まとめ

Amazon Verified Permissions に実践入門できるワークショップ「Amazon Verified Permissions workshop」を試した👌 Amazon Verified Permissions の基本的な概念・マネジメントコンソール操作から,実際にサーバーレスアプリケーションに組み込んだときの実装例も学べてとても良かった❗️

今回は TinyTodo アプリケーションに組み込んだけど,実際に仕事で Amazon Verified Permissions を活用する場合は認可モデルの設計が良し悪しを決めるのは間違いなく,ドキュメントにモデリングの指針がまとまっているため,あわせて読んでおくと良さそう📝

docs.aws.amazon.com

LocalStack を使って Amazon Transcribe をローカル環境で操作する

$
0
0

Amazon Transcribe で Speech-to-Textを実現するときに,LocalStack を使えば Amazon Transcribe を「AWS アカウントを使わずにローカル環境で」動かせる👌LocalStack 自体は仕事でもプライベートでも使ってるけど,Amazon Transcribe は今まで試したことがなくて,今回試してみた❗️

Amazon Transcribe API のサポート状況

もちろん LocalStack がすべての Amazon Transcribe API をサポートしているわけではないけど,カバレッジは以下で確認できる.StartTranscriptionJob / ListTranscriptionJobs / GetTranscriptionJobはサポートされてて基本的な操作はできそう.

docs.localstack.cloud

Vosk

そもそも Amazon Transcribe の Speech-to-Text の仕組みは公開されていないのでは?と疑問に感じるけど,LocalStack の内部では Voskが使われているとドキュメントに載っていた💡英語・日本語・ドイツ語など,多くの言語をサポートしている.もちろん Vosk の精度に依存せず,LocalStack をインタフェース確認として使うのが良いと思う👌

alphacephei.com

注意点

最初に Amazon Transcribe を試したところ,TranscriptionJob(文字起こしジョブ)を実行すると以下のエラーが出てしまって困ったけど,何やら関連する issueもあって,プラットフォームが関係するようだった.今回は Apple M3 を使っているため localstack:latest-amd64イメージで LocalStack を起動したら問題なく動くようになった👌試すときに注意が必要かなと🚨

cannot load library '/var/lib/localstack/lib/python-packages/lib/python3.11/site-packages/vosk/libvosk.so': libatomic.so.1: cannot open shared object file: No such file or directory

試す

まずは AWS CloudFormation で Amazon S3 バケットを作る.LocalStack は AWS CloudFormation / Amazon S3 もサポートしている❗️

AWSTemplateFormatVersion:'2010-09-09'Resources:S3Bucket:Type: AWS::S3::Bucket
    Properties:BucketName: transcribe-sandbox

awslocalコマンドで AWS CloudFormation スタックをデプロイして,transcribe-sandboxバケットを確認した👌

$ awslocal cloudformation deploy --stack-name transcribe-sandbox --template-file templates/template.yaml

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - transcribe-sandbox

$ awslocal s3api list-buckets | jq -r'.Buckets[].Name'
transcribe-sandbox

次に AWS CLI で Speech-to-Text をする mp3 を transcribe-sandboxバケットにアップロードする.今回はサンプルとして効果音ラボ「よろしくお願いします(冷静な魔術師)」を使う.準備完了〜 \( 'ω')/

$ awslocal s3 cp ./mp3/wizard-greeting1.mp3 s3://transcribe-sandbox/wizard-greeting1.mp3
upload: mp3/wizard-greeting1.mp3 to s3://transcribe-sandbox/wizard-greeting1.mp3

そして今度は Python (boto3) スクリプトで Amazon Transcribe の TranscriptionJob(文字起こしジョブ)を実行する❗️

import uuid

import boto3

transcribe = boto3.client('transcribe', endpoint_url='http://localhost:4566')

response = transcribe.start_transcription_job(
    TranscriptionJobName=f'job-{str(uuid.uuid4())}',
    Media={
        'MediaFileUri': 's3://transcribe-sandbox/wizard-greeting1.mp3',
    },
    LanguageCode='ja-JP',
)

print('TranscriptionJobName: ' + response['TranscriptionJob']['TranscriptionJobName'])
print('TranscriptionJobStatus: ' + response['TranscriptionJob']['TranscriptionJobStatus'])

実行すると TranscriptionJobNameなどのレスポンスを確認できる.

TranscriptionJobName: job-b9b2d46a-6621-4890-b09f-08fb3d5f059e
TranscriptionJobStatus: IN_PROGRESS

少し待つと IN_PROGRESSから COMPLETEDになる.LocalStack の Resource Browser(マネジメントコンソール)で TranscriptionJob(文字起こしジョブ)の詳細を確認できる.

もちろん AWS CLI の transcribe get-transcription-jobコマンドでも確認できる👌

$ awslocal transcribe get-transcription-job --transcription-job job-b9b2d46a-6621-4890-b09f-08fb3d5f059e | jq .
{"TranscriptionJob": {"TranscriptionJobName": "job-b9b2d46a-6621-4890-b09f-08fb3d5f059e",
    "TranscriptionJobStatus": "COMPLETED",
    "LanguageCode": "ja-JP",
    "MediaSampleRateHertz": 44100,
    "MediaFormat": "wav",
    "Media": {"MediaFileUri": "s3://transcribe-sandbox/wizard-greeting1.mp3"},
    "Transcript": {"TranscriptFileUri": "http://s3.localhost.localstack.cloud:4566/transcribe-sandbox/job-b9b2d46a-6621-4890-b09f-08fb3d5f059e.json?AWSAccessKeyId=__internal_call__&Signature=pihlxHmUVQOeBuoHSjUCy1Yz0Gs%3D&Expires=1718111742"},
    "StartTime": "2024-06-11T21:38:34.119979+09:00",
    "CreationTime": "2024-06-11T21:38:34.119496+09:00",
    "CompletionTime": "2024-06-11T21:40:32.940129+09:00"}}

最終的に transcribe-sandboxバケットにアップロードされた job-b9b2d46a-6621-4890-b09f-08fb3d5f059e.jsonを確認すると,期待通りに「よろしくお願いします」を Speech-to-Text できていた👏

{"jobName": "job-b9b2d46a-6621-4890-b09f-08fb3d5f059e",
  "status": "COMPLETED",
  "results": {"transcripts": [{"transcript": "よろしく お 願い し ます"
      }],
    "items": [{"start_time": 0.0,
        "end_time": 0.48,
        "type": "pronunciation",
        "alternatives": [{"confidence": 0.816937,
            "content": "よろしく"
          }]},
      {"start_time": 0.48,
        "end_time": 0.57,
        "type": "pronunciation",
        "alternatives": [{"confidence": 0.984702,
            "content": ""
          }]},
      {"start_time": 0.57,
        "end_time": 0.87,
        "type": "pronunciation",
        "alternatives": [{"confidence": 0.984702,
            "content": "願い"
          }]},
      {"start_time": 0.87,
        "end_time": 0.99,
        "type": "pronunciation",
        "alternatives": [{"confidence": 1.0,
            "content": ""
          }]},
      {"start_time": 0.99,
        "end_time": 1.23,
        "type": "pronunciation",
        "alternatives": [{"confidence": 1.0,
            "content": "ます"
          }]}]}}

関連記事

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

Apache Ivy で Guava を取得するときに javadoc.jar と sources.jar を除外する

$
0
0

Apache Ant と Apache Ivy を使って Maven Repository から Guavaを取得すると不要な javadoc.jarsources.jarも一緒に取得されてしまうときの解決策を2つメモしておく〜📝

再現確認

まず,Apache Ant の build.xmlにデフォルト設定の ivy:retrieveタスクを書いておく.

<target name="resolve"><ivy:retrieve/></target>

そして,Apache Ivy の ivy.xmlには Maven Repository に載っている dependencyタグを書く.ちなみに今回は取得するファイル数を減らすために意図的に transitive="false"を付けておく👌

<ivy-module version="2.0"><info organisation="sandbox"module="module"/><dependencies><dependency org="com.google.guava"name="guava"rev="33.2.1-jre"transitive="false"/></dependencies></ivy-module>

ant resolveコマンドを実行すると,以下の3ファイルが取得されてしまう😇

guava-33.2.1-jre-javadoc.jar
guava-33.2.1-jre-sources.jar
guava-33.2.1-jre.jar

解決策1

解決策の一つは build.xmlivy:retrieveタスクに type="jar, bundle"を設定しておくこと.そうすると guava-33.2.1-jre.jarのみを取得できる❗️

<ivy:retrieve type="jar, bundle"/>

ant.apache.org

解決策2

もう一つの解決策は ivy.xmldependencyタグの中に artifactタグを設定しておくこと.同じく guava-33.2.1-jre.jarのみを取得できる❗️

<ivy-module version="2.0"><info organisation="sandbox"module="module"/><dependencies><dependency org="com.google.guava"name="guava"rev="33.2.1-jre"transitive="false"><artifact name="guava"/></dependency></dependencies></ivy-module>

ant.apache.org

localstack-utils: 単体テスト実行時に使い捨て可能な LocalStack を起動しよう

$
0
0

LocalStack から公式に提供されている localstack-utilsを使うと,pytest など Python で単体テストを実行するときに一時的な(使い捨て可能な)LocalStack 環境を起動できる🌍

docs.localstack.cloud

ちなみに僕は普段仕事で testcontainers-pythonLocalStackContainerを使ってて最高に便利なんだけど,結果的に localstack-utils もほとんど同じように使うことができた👌

kakakakakku.hatenablog.com

現在最新は localstack-utils 1.0.1 だった.

github.com

pypi.org

サンプルコード

👾 app.py

まずは Amazon DynamoDB の Forumテーブルからアイテムを取得する search_forum()関数を今回のテスト対象とする.Forumテーブルというのは Amazon DynamoDB のドキュメントに載っているサンプルでそのまま使うことにした.

そして,boto3 client は環境変数 ENVによって3種類作れるようにしてある👌

  • local: ローカル開発用 (LocalStack)
  • test: テスト用 (localstack-utils / LocalStack)
  • その他: 実際の AWS アカウント

ちなみに localstack-utils のデフォルト設定では 4566ポートで LocalStack を起動する.それだとローカル開発用の LocalStack とポートが競合してしまうため,今回は 14566ポートで起動することにした.

import os

import boto3

TABLE_NAME = 'Forum'if os.environ['ENV'] == 'local':
    dynamodb = boto3.client('dynamodb', endpoint_url='http://localhost:4566')
elif os.environ['ENV'] == 'test':
    dynamodb = boto3.client('dynamodb', endpoint_url='http://localhost:14566')
else:
    dynamodb = boto3.client('dynamodb')


defsearch_forum(name):
    return dynamodb.get_item(
        TableName=TABLE_NAME,
        Key={
            'Name': {'S': name},
        },
    )

👾 test_app.py

pytest 実行時に呼び出すフィクスチャとして @pytest.fixtureデコレータで _setup()関数を実装した.localstack-utils には LocalStack を起動する startup_localstack()関数と LocalStack を停止する stop_localstack()関数が実装されているため,それを _setup()関数内で実行している.14566ポートで起動する設定もしてある👌

そして,Amazon DynamoDB テーブル Forumを作って,サンプルデータ(アイテム)を1つ登録している.最後にテストケースとしては search_forum()関数を呼び出して「アイテムを取得できる場合」「アイテムを取得できない場合」を確認している✔️

import boto3
import pytest
from app import search_forum
from localstack_utils.localstack import startup_localstack, stop_localstack

TABLE_NAME = 'Forum'@pytest.fixture(scope='module', autouse=True)
def_setup():
    startup_localstack(gateway_listen='0.0.0.0:14566')

    dynamodb = boto3.client('dynamodb', endpoint_url='http://localhost:14566')

    dynamodb.create_table(
        TableName=TABLE_NAME,
        KeySchema=[
            {
                'AttributeName': 'Name',
                'KeyType': 'HASH',
            }
        ],
        AttributeDefinitions=[
            {
                'AttributeName': 'Name',
                'AttributeType': 'S',
            }
        ],
        BillingMode='PAY_PER_REQUEST',
    )

    item = {
        'Name': {'S': 'Amazon DynamoDB'},
        'Category': {'S': 'Amazon Web Services'},
        'Threads': {'N': '2'},
        'Messages': {'N': '4'},
        'Views': {'N': '1000'},
    }

    dynamodb.put_item(TableName=TABLE_NAME, Item=item)

    yield

    stop_localstack()


deftest_search_forum():
    item = search_forum('Amazon DynamoDB')
    assert item['Item']['Category']['S'] == 'Amazon Web Services'assert item['Item']['Views']['N'] == '1000'

    item = search_forum('Amazon S3')
    assert'Item'notin item

動作確認

期待通り実行できた👏

$ ENV=test pytest -p no:warnings
===========================================================================================test session starts ============================================================================================(中略)
configfile: pyproject.toml
collected 1 item

tests/test_app.py . [100%]============================================================================================1 passed in4.97s =============================================================================================

詳しくはコード参照

localstack-utils にはドキュメントというドキュメントはなく,オプションなど詳しくは GitHub のコードを確認する必要がある.例えば今回は startup_localstack()関数に gateway_listenを設定したけど,他にも image_name / tag / proなども設定できる👌

github.com

まとめ

localstack-utils を使って Python で単体テストを実行するときに一時的な(使い捨て可能な)LocalStack 環境を活用しよう❗️


LocalStack を使って CloudWatch Logs サブスクリプションフィルタをローカル環境で試す

$
0
0

Amazon CloudWatch Logs サブスクリプションフィルタを使ってログを AWS Lambda に流す構成を AWS アカウントにデプロイする前に LocalStack にデプロイして確認してみた❗

docs.aws.amazon.com

LocalStack は Amazon CloudWatch Logs サブスクリプションフィルタもサポートしている👌しかし注意点はあって,サブスクリプションフィルタに設定するフィルタパターン(JSON フィルタ・正規表現フィルタなど)は LocalStack Proでサポートされている💡よって,今回はフィルタなし(すべてのログを流す)で試す.

docs.localstack.cloud

あくまでサンプルとして以下の構成を LocalStack を使ってローカル環境にデプロイする.

サンプルコード

👾 template.yaml(AWS SAM テンプレート)

今回は以下のように AWS SAM テンプレートを実装した📝AWS Lambda 関数は log-senderlog-receiverの2つを準備して,log-senderの Amazon CloudWatch Logs ロググループにサブスクリプションフィルタを設定してある👌

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:LogSenderFunction:Type: AWS::Serverless::Function
    Properties:FunctionName: log-sender
      CodeUri: src/
      Handler: log-sender.lambda_handler
      Runtime: python3.12
      Architectures:- x86_64
  LogSenderLogGroup:Type: AWS::Logs::LogGroup
    Properties:LogGroupName: /aws/lambda/log-sender
  LogSenderSubscriptionFilter:Type: AWS::Logs::SubscriptionFilter
    Properties:LogGroupName:!Ref LogSenderLogGroup
      FilterPattern:""DestinationArn:!GetAtt LogReceiverFunction.Arn
  LogReceiverFunction:Type: AWS::Serverless::Function
    Properties:FunctionName: log-receiver
      CodeUri: src/
      Handler: log-receiver.lambda_handler
      Runtime: python3.12
      Architectures:- x86_64

👾 log-sender.py

log-sender ではシンプルに {"id": "27a1cc30-b4c4-4192-9db9-d19962fe8f33", "message": "sample message"}のように UUID と固定メッセージを含んだ構造化ログ (JSON) を出力する👌

import json
import uuid


defmain():
    print(
        json.dumps(
            {
                'id': str(uuid.uuid4()),
                'message': 'sample message',
            }
        )
    )


deflambda_handler(event, context):
    main()


if __name__ == '__main__':
    main()

👾 log-receiver.py

log-receiver はサブスクリプションフィルタから流れてきたログをそのままログに出力する.サブスクリプションフィルタ経由だとログは GZIP 圧縮と Base64 エンコードで変換された状態になるけど,今回は Powertools for AWS Lambda (Python)の Event Source Data Classes で CloudWatchLogsEventCloudWatchLogsDecodedDataを使ってお手軽に実装した👏 便利〜 \( 'ω')/

docs.powertools.aws.dev

import json

from aws_lambda_powertools.utilities.data_classes import CloudWatchLogsEvent
from aws_lambda_powertools.utilities.data_classes.cloud_watch_logs_event import CloudWatchLogsDecodedData


defmain(event):
    event = CloudWatchLogsEvent(event)
    decompressed: CloudWatchLogsDecodedData = event.parse_logs_data()
    logs = decompressed.log_events
    for log in logs:
        print(log.message)


deflambda_handler(event, context):
    main(event)


if __name__ == '__main__':
    withopen('./events/event.json', 'r') as f:
        event = json.load(f)

    main(event)

ちなみに @event_source(data_class=CloudWatchLogsEvent)のようにデコレータを使えば event = CloudWatchLogsEvent(event)という値の詰め直しは不要になるけど,Lambda コンテキストに依存していてローカル開発がしにくく採用しなかった.前に紹介した Powertools for AWS Lambda (Python) の Validationでも Lambda コンテキスト依存を避ける実装を紹介していたりする💡

kakakakakku.hatenablog.com

動作確認

samlocalコマンドでビルド・デプロイをして,awslocalコマンドで AWS Lambda 関数を実行した.

$ samlocal build
$ samlocal deploy

$ awslocal lambda invoke --function-name log-sender outfile

LocalStack の Amazon CloudWatch Logs を確認すると,期待通りに log-senderlog-receiverどちらにも同じログが出ていた👌

log-sender

{"id": "27a1cc30-b4c4-4192-9db9-d19962fe8f33", "message": "sample message"}

log-receiver

{"id": "27a1cc30-b4c4-4192-9db9-d19962fe8f33", "message": "sample message"}

boto3 Config で Amazon S3 Transfer Acceleration エンドポイントを使えるようにする

$
0
0

Amazon S3 で Transfer Accelerationを有効化すると,エッジロケーションを活用してオブジェクトを高速にアップロード・ダウンロードできるようになる.そして bucketname.s3-accelerate.amazonaws.comというエンドポイントが追加される👀

aws.amazon.com

docs.aws.amazon.com

boto3 x Amazon S3 Transfer Acceleration

Python (boto3) で Amazon S3 Transfer Acceleration エンドポイントを使う場合は Boto3 Config の use_accelerate_endpointで設定できる👌今回は検証も兼ねて Config 設定の動作確認をしてみた.

boto3.amazonaws.com

準備

自宅(東京)から動作確認をするため,今回は us-east-1(バージニア)に Amazon S3 バケットを作った.そして aws s3api put-bucket-accelerate-configurationコマンドで Amazon S3 Transfer Acceleration を有効化した.あと mkfile 50m 50mbコマンドで 50MB のダミーファイルを作っておく📁

$ aws s3 mb s3://transfer-acceleration-sandbox --region us-east-1
$ aws s3api put-bucket-accelerate-configuration --bucket transfer-acceleration-sandbox --accelerate-configurationStatus=Enabled --region us-east-1

1. Config 設定なし

今回はエンドポイントを確認するため botocore のデバッグログを出力できるようにしておく👌

import time
import uuid

import boto3
import botocore

botocore.session.Session().set_debug_logger()

session = boto3.Session(profile_name='xxxxx', region_name='us-east-1')

s3_client = session.client(
    's3',
)

start = time.time()
s3_client.upload_file('50mb', 'transfer-acceleration-sandbox', f'50mb-{uuid.uuid4().hex}')
end = time.time()

print(f'{end - start} seconds')

実行すると s3.amazonaws.comエンドポイントにリクエストを送信していた🛜

Sending http request:
(中略)
url=https://transfer-acceleration-sandbox.s3.amazonaws.com/50mb-a3637f080158476fa0333dc838d700d7?uploadId=xxx
(中略)

計3回実行したアップロード時間は以下だった💡

  • 35.75226593017578 seconds
  • 36.11813402175903 seconds
  • 42.113038063049316 seconds

2. Config 設定あり

今度は Config で use_accelerate_endpointを設定する❗️

import time
import uuid

import boto3
import botocore

botocore.session.Session().set_debug_logger()

session = boto3.Session(profile_name='xxxxx', region_name='us-east-1')

s3_client = session.client(
    's3',
    config=boto3.session.Config(
        s3={
            'use_accelerate_endpoint': True,
        },
    ),
)

start = time.time()
s3_client.upload_file('50mb', 'transfer-acceleration-sandbox', f'50mb-{uuid.uuid4().hex}')
end = time.time()

print(f'{end - start} seconds')

実行すると今度は s3-accelerate.amazonaws.comエンドポイントにリクエストを送信していた🛜

Sending http request:
(中略)
url=https://transfer-acceleration-sandbox.s3-accelerate.amazonaws.com/50mb-8b5a312964c94d5a8e803655e1aa6792?uploadId=xxx
(中略)

計3回実行したアップロード時間は以下だった💡速くなってる〜 \( 'ω')/

  • 14.759350061416626 seconds
  • 12.844246625900269 seconds
  • 13.640271663665771 seconds

Powertools for AWS Lambda (Python) の Validation で UUID フォーマットをバリデーションする

$
0
0

Powertools for AWS Lambda (Python) の Validationでプロパティの「UUID フォーマット」をチェックする場合は以下のようにスキーマを実装すると良さそう👌

{
    '$schema': 'http://json-schema.org/draft-07/schema',
    'type': 'object',
    'required': ['id'],
    'properties': {
        'id': {
            'type': 'string',
            'pattern': '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
        },
    },
}

便利な Powertools for AWS Lambda (Python) の Validation に関しては前に紹介記事を書いた📝

kakakakakku.hatenablog.com

公式ドキュメントも参照📝

docs.powertools.aws.dev

検証環境

今回は AWS SAM を使って Amazon API Gateway (REST API) と AWS Lambda 関数を構築する.Amazon API Gateway に POST リクエストを送ってバリデーションロジックの確認をした❗️

動作確認

👾 template.yaml

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:Function:Type: AWS::Serverless::Function
    Properties:FunctionName: powertools-validation-uuid
      CodeUri: src/
      Handler: app.lambda_handler
      Runtime: python3.12
      Architectures:- x86_64
      Layers:- arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:73
      Events:Api:Type: Api
          Properties:Path: /
            Method: POST

👾 app.py

今回はサンプルとしてパラメータが正常であれば 200 OKを返して,異常であれば 400 BAD_REQUESTを返すようにした.

import json
from http import HTTPStatus

from aws_lambda_powertools.utilities.validation import SchemaValidationError, envelopes, validate

SCHEMA = {
    '$schema': 'http://json-schema.org/draft-07/schema',
    'type': 'object',
    'required': ['id'],
    'properties': {
        'id': {
            'type': 'string',
            'pattern': '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
        },
    },
}


defmain(event):
    try:
        validate(event=event, schema=SCHEMA, envelope=envelopes.API_GATEWAY_REST)
    except SchemaValidationError as e:
        return {
            'statusCode': HTTPStatus.BAD_REQUEST,
            'body': json.dumps({'message': e.validation_message}),
        }

    return {
        'statusCode': HTTPStatus.OK,
        'body': json.dumps({'message': 'ok'}),
    }


deflambda_handler(event, context):
    return main(event)


if __name__ == '__main__':
    withopen('./events/event.json', 'r') as f:
        event = json.load(f)

    response = main(event)
    print(response['body'])

動作確認

ok

event.json

{"id": "92380de4-cfe1-4b9c-9a67-2928dcba10e0"
}

UUID Generatorで取得した UUID を idプロパティに指定すると ok になった🙆‍♂

$ curl -s-X POST --data @event.json ${ENDPOINT}{"message": "ok"}

data.id must match pattern ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$

event.json

{"id": "92380de4-cfe1-4b9c-9a67-2928dcba10e00"
}

UUID 以外のフォーマット(1文字余分に追加した)で idプロパティを指定するとバリデーションエラーになった🙅‍♂️

$ curl -s-X POST --data @event.json ${ENDPOINT}{"message": "data.id must match pattern ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"}

JSON Schema 2019-09

ちなみに JSON Schema 自体はビルトインフォーマットとして uuidをサポートしているけど,Powertools for AWS Lambda (Python) の Validation で 'format': 'uuid'とスキーマを実装すると Unknown format: uuidというエラーが出てしまう🔥

json-schema.org

実は uuidフォーマットは JSON Schema 2019-09(1つ前は JSON Schema draft-07)でサポートされていて,Powertools for AWS Lambda (Python) の Validation が依存している fastjsonschema (Fast JSON schema for Python)draft-04draft-06draft-07のみをサポートしているという背景があって現状では使えなかった📝

horejsek.github.io

Casbin 認可ポリシーを DynamoDB に保存できるアダプター「python-dycasbin」を試した

$
0
0

Casbin で認可ポリシーを保存する1番簡単な選択肢は CSV ファイルだけど,アダプターを使うと認可ポリシーをデータベースで管理できる👌今回は PyCasbinで,ドキュメントに載っている DynamoDB Adapter (python-dycasbin) を使って,Casbin 認可ポリシーを Amazon DynamoDB に保存してみた.試したログをまとめておく❗️

casbin.org

また LoadPolicy()SavePolicy()など,アダプターインタフェースを実装すればカスタムアダプターも実装できる👀

casbin.org

👾 requirements.txt

今回は PyCasbin と python-dycasbin の最新バージョンを使う👌

casbin==1.36.2
python-dycasbin==0.4.1

pypi.org

pypi.org

👾 model.conf

そして,Casbin Model はドキュメントにも載っている ACL (Access Control List)をそのまま使う👌

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

casbin.org

👾 add-policy.py

まず,python-dycasbin を使って Amazon DynamoDB テーブルに Casbin 認可ポリシーを登録する.今回はサンプルとして Amazon S3 を参考に s3:PutObjectアクションを制御することにした.

  • sub (kakakakakku) は obj1 (arn:aws:s3:::sample-bucket-1/*) に対して act (s3:PutObject) できる
  • sub (kakakakakku) は obj2 (arn:aws:s3:::sample-bucket-2/*) に対して act (s3:PutObject) できる

また Amazon DynamoDB は LocalStackを使ってローカル環境でアクセスできるようにしておく✅

import casbin
from python_dycasbin import adapter

adapter = adapter.Adapter(endpoint_url='http://localhost:4566')

e = casbin.Enforcer('model.conf', adapter, True)

sub = 'kakakakakku'
obj1 = 'arn:aws:s3:::sample-bucket-1/*'
obj2 = 'arn:aws:s3:::sample-bucket-2/*'
act = 's3:PutObject'

e.add_policy(sub, obj1, act)
e.add_policy(sub, obj2, act)

add-policy.pyを実行すると,Adapter 経由で自動的に Amazon DynamoDB テーブル(デフォルトだと casbin_rule)が構築される.Adapter クラスの初期化 (__init__) で毎回テーブルを構築しようと試みるところは微妙に感じるし,テーブル設定として WCU / RCU が 10で固定されているのも厳しい気がした😇オンデマンドキャパシティを選択するオプションがあっても良さそう.

github.com

登録した Casbin 認可ポリシーは Amazon DynamoDB テーブルでは以下のように登録されていた.

$ awslocaldynamodbscan --table-name casbin_rule
{"Items": [{"ptype": {"S": "p"
            },
            "v0": {"S": "kakakakakku"
            },
            "v1": {"S": "arn:aws:s3:::sample-bucket-1/*"
            },
            "id": {"S": "bf571b8e16cef131aeac79c15a800421"
            },
            "v2": {"S": "s3:PutObject"
            }},
        {"ptype": {"S": "p"
            },
            "v0": {"S": "kakakakakku"
            },
            "v1": {"S": "arn:aws:s3:::sample-bucket-2/*"
            },
            "id": {"S": "46808bf495552ff34293bdb9f8f0a9b2"
            },
            "v2": {"S": "s3:PutObject"
            }}],
    "Count": 2,
    "ScannedCount": 2,
    "ConsumedCapacity": null}

登録されたデータ(アイテム)は LocalStack Resource Browser でも確認できた👌

👾 enforce.py

今度は arn:aws:s3:::sample-bucket-1/*arn:aws:s3:::sample-bucket-2/*arn:aws:s3:::sample-bucket-3/*に対する s3:PutObjectアクションの許可を確認する.

import casbin
from python_dycasbin import adapter

adapter = adapter.Adapter(endpoint_url='http://localhost:4566')

e = casbin.Enforcer('model.conf', adapter, True)

sub = 'kakakakakku'
obj1 = 'arn:aws:s3:::sample-bucket-1/*'
obj2 = 'arn:aws:s3:::sample-bucket-2/*'
obj3 = 'arn:aws:s3:::sample-bucket-3/*'
act = 's3:PutObject'

e.enforce(sub, obj1, act)
e.enforce(sub, obj2, act)
e.enforce(sub, obj3, act)

期待通りに True, True, Falseと認可判断ができていた❗️

2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-1/*, s3:PutObject ---> True
2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-2/*, s3:PutObject ---> True
2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-3/*, s3:PutObject ---> False

ちなみに python-dycasbin の実装を読むと,Amazon DynamoDB テーブルに対して Scanを実行していて気になる💨Amazon DynamoDB テーブルに GSI (Global Secondary Index) を追加して,Scan ではなく Queryを使うように変更したプルリクエストも出ているようだった.

👾 remove-policy.py

最後は arn:aws:s3:::sample-bucket-2/*に対する Casbin 認可ポリシーを削除してみる.

import casbin
from python_dycasbin import adapter

adapter = adapter.Adapter(endpoint_url='http://localhost:4566')

e = casbin.Enforcer('model.conf', adapter, True)

sub = 'kakakakakku'
obj2 = 'arn:aws:s3:::sample-bucket-2/*'
act = 's3:PutObject'

e.remove_policy(sub, obj2, act)

もう一度 s3:PutObjectアクションの許可を確認したところ,期待通りに True, False, Falseと認可判断ができていた❗️

2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-1/*, s3:PutObject ---> True
2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-2/*, s3:PutObject ---> False
2024-06-21 19:00:00,000 Request: kakakakakku, arn:aws:s3:::sample-bucket-3/*, s3:PutObject ---> False

Amazon DynamoDB テーブルのデータ(アイテム)も削除されていた👌

$ awslocaldynamodbscan --table-name casbin_rule
{"Items": [{"ptype": {"S": "p"
            },
            "v0": {"S": "kakakakakku"
            },
            "v1": {"S": "arn:aws:s3:::sample-bucket-1/*"
            },
            "id": {"S": "bf571b8e16cef131aeac79c15a800421"
            },
            "v2": {"S": "s3:PutObject"
            }}],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null}

Apache Ivy の configurations でテストに必要な依存関係を取得する

$
0
0

Apache Ivy を使って Maven Repository からライブラリを取得するときに,例えば CI 環境では JUnit や Mockito などの「テストに必要な依存関係」も取得したいという場面がある.Apache Ivy では configurations(コンフィグレーション)を使って柔軟に依存関係を管理できる👌

サンプルコード

Apache Ivy の ivy.xmlを以下のように書いた.ポイントは configurationsタグで,今回は compile(コンパイルに必要な依存関係)と test(テストに必要な依存関係)という2種類のコンフィグレーションを設定した.Apache Ivy のドキュメントを見ると,アプリケーション単位の依存関係などさらに柔軟に設定している例もあった💡そして dependencyタグを書くときに conf="compile->default"conf="test->default"のようにコンフィグレーションを紐付ければ OK ✅

<ivy-module version="2.0"><info organisation="sandbox"module="module"/><configurations><conf name="compile" /><conf name="test" /></configurations><dependencies><!-- compile --><dependency org="com.google.code.gson"name="gson"rev="2.11.0"conf="compile->default"/><!-- test --><dependency org="org.junit.jupiter"name="junit-jupiter-api"rev="5.10.3"conf="test->default"/><dependency org="org.mockito"name="mockito-core"rev="5.12.0"conf="test->default"/><dependency org="org.mockito"name="mockito-junit-jupiter"rev="5.12.0"conf="test->default"/></dependencies></ivy-module>

そして Apache Ant の build.xmlには resolveターゲットと resolve-testターゲットを書いておく.

<project xmlns:ivy="antlib:org.apache.ivy.ant"><target name="resolve"><ivy:retrieve type="jar"conf="compile"/></target><target name="resolve-test"depends="resolve"><ivy:retrieve type="jar"conf="test"/></target></project>

動作確認

ant resolveコマンドを実行すると,コンパイルに必要な Gson とその依存関係を取得できる👌

error_prone_annotations-2.27.0.jar
gson-2.11.0.jar

ant resolve-testコマンドを実行すると,テストに必要な JUnit / Mockito とその依存関係を取得できる👌 そして resolve-testターゲットには resolveターゲットへの依存も設定しているため,コンパイルに必要な依存関係も取得できている \( 'ω')/

apiguardian-api-1.1.2.jar
byte-buddy-1.14.15.jar
byte-buddy-agent-1.14.15.jar
error_prone_annotations-2.27.0.jar
gson-2.11.0.jar
junit-jupiter-api-5.10.3.jar
junit-platform-commons-1.10.3.jar
mockito-core-5.12.0.jar
mockito-junit-jupiter-5.12.0.jar
objenesis-3.3.jar
opentest4j-1.3.0.jar

ドキュメント

以下に Apache Ivy の configurations に関連するドキュメントを載せておく🔗

ant.apache.org

ant.apache.org

ant.apache.org

関連記事

kakakakakku.hatenablog.com

カックマイクラ実況 YouTube 振り返り(開設1年🎉)

$
0
0

2023年7月1日に開設した YouTube チャンネル「カックマイクラ実況」を1年間続けてみた❗️簡単に振り返ろうと思う.

www.youtube.com

ちなみに「6ヶ月間」の振り返りは以下の記事にまとめてある📝機材などは特に変化なし.

kakakakakku.hatenablog.com

Stats (~2024/06/30) 👾

  • 動画: 71本(2023年12月から+46本)
  • チャンネル登録者数: 61人(2023年12月から+45人)
    • チャンネル登録者数「50人」達成 🎉
  • 視聴回数: 22,306回(2023年12月から+21,089回)
    • YouTube ショート「AI に家を建築してもらおう」「5000回再生」達成 🎉

www.youtube.com

YouTube ショート 👾

2024年3月頃から YouTube ショートを作り始めた.計31本も作った❗️最初は今まで作った動画のダイジェストとして作っていて,2024年5月頃からは完全に YouTube ショートのみ作ることにして,マインクラフトのアップデート紹介・Tips 紹介・AI 建築シリーズなどの企画を考えてコツコツと作った.

YouTube ショートを作って驚いたのは「ショートフィード」経由の新規流入が多いことだった.今まで YouTube 動画を作っても誰にも見られず,動画自体に流入してもらうことへのハードルが高いという課題があった.YouTube ショートだとアルゴリズムはわからないけど,チャンネル登録者以外にリーチできて,数名でも興味を持ってもらえる人に見てもらうことができた💡視聴回数の増加・チャンネル登録者数の増加はまさに YouTube ショートの結果と言える.

テクノロジーネタ 👾

2024年1月の振り返り記事に「2024年は "マインクラフト x テクノロジー"な動画も作りたい」という目標を書いた通り,コマンド紹介・Mod 紹介・スナップショットアップデート紹介・AI 建築などの動画を多く作った.Wiki を見ながらコマンドのオプションを調べたり,GitHub リポジトリを見ながら Mod の実装を調べたりする作業は普段の技術調査と同じ流れで特に苦労することなく楽しめた💡僕自身の強みを活かせていると思う \( 'ω')/

例えば rideコマンドを使った以下の動画は個人的におすすめ❗️(全然見られてないけどーw)

www.youtube.com

www.youtube.com

AI 建築シリーズ 👾

2024年6月から個人的に「AI 建築シリーズ」と呼んでいるけど,ChatGPT などの LLM に生成してもらったコードを使ってマインクラフトの建築をするというネタで YouTube ショートを作り始めた💡

www.youtube.com

www.youtube.com

Raspberry Jam Mod (raspberryjammod) を使うと,マインクラフトのバージョンは古めだけど Python から直接マインクラフトを操作できる.プロンプトを工夫すれば,LLM で Raspberry Jam Mod のコードを生成できる👌技術的な勘所がないと環境構築が大変そうではあるけど,特にハマるところはなかった.

github.com

今後 👾

仕事(本業)も増えてきてて YouTube に使える自由時間は減っていく傾向にあるけど,今後も趣味としてコツコツ YouTube 活動は続けていこうと思う❗️チャンネル登録をしてもらえると本当に嬉しいのでもし良かったらポチッとお願いしまーす🙏

www.youtube.com

Amazon S3 署名付き URL 経由のアップロードでオブジェクトメタデータを設定する

$
0
0

Amazon S3 署名付き URL を発行してオブジェクトをアップロードするときに「オブジェクトメタデータ」も設定できる👌できるのかな〜と気になって試してみたらできた.署名付き URL 経由でアップロードされたオブジェクトに対して付加情報を設定しておくという活用ができそう \( 'ω')/

👾 main.py

今回は Python (boto3) で試した.generate_presigned_url()を実行するときに ParamsMetadataを設定すれば OK👌ようするに署名付き URL を "発行するときに"オブジェクトメタデータを指定しておくことになる.

以下の main.pyを実行して署名付き URL を発行すると,署名付き URL 自体に x-amz-meta-key1=value1&x-amz-meta-key2=value2&x-amz-meta-key3=value3というクエリパラメータが追加された.

import os

import boto3

s3 = boto3.client('s3')

url = s3.generate_presigned_url(
    'put_object',
    Params={
        'Bucket': os.environ['BUCKET'],
        'Key': 'sample.png',
        'Metadata': {
            'key1': 'value1',
            'key2': 'value2',
            'key3': 'value3',
        },
    },
    ExpiresIn=600,
)

print(url)

動作確認

次に適当なファイルとして sample.pngを署名付き URL 経由でアップロードする👌

$ PRESIGNED_URL=xxx
$ curl -X PUT --upload-file sample.png ${PRESIGNED_URL}

AWS CLI でオブジェクトメタデータを確認すると,期待通りに設定できていた❗️

$ aws s3api head-object --bucket${BUCKET}--key sample.png --query'Metadata' | jq .
{"key1": "value1",
  "key2": "value2",
  "key3": "value3"}

Grand: Apache Ant の依存関係を可視化する

$
0
0

Grand を使うと Apache Ant ターゲットの依存関係を dot ファイルとして可視化できる.そもそも可視化したくなるほど依存関係が複雑なことが課題ではあると思うけど,既存の build.xmlを解析したり,新しく build.xmlを読むメンバーのために README.mdに図を貼っておいたりという用途はありそう👌

特に新しいツールではなく,また GitHub を見ると2020年頃からメンテナンスは止まってそうだけど,試す機会があってメモとして残しておく❗️

ant-grand.github.io

セットアップ

Grand は Grand 自身を Ant ターゲットとして実行できる.まずは GitHub の Releases から grand-1.9.7.jarをダウンロードしておく.今回は lib/ディレクトリに置いた.

github.com

Grand を実行するターゲットは可視化対象となる build.xmlに直接追加することもできるし,Grand 専用の build.xmlを作ることもできる.今回は後者のアイデアを採用して grand.xmlを以下のように設定した.

<project name="Grand"><target name="grand"><typedef resource="net/ggtools/grand/antlib.xml"classpath="lib/grand-1.9.7.jar"/><grand output="build.dot"buildfile="build.xml" /></target></project>

最終的に以下のようなディレクトリ構成になった👀

.
├── README.md
├── build.xml
├── grand.xml
└── lib
    └── grand-1.9.7.jar

お試し1

まずは Apache Ant のドキュメント Writing a Simple Buildfileに載っている build.xmlを使う.

ant.apache.org

<project name="MyProject"default="dist"basedir="."><description>
    simple example build file
  </description><!-- set global properties for this build --><property name="src"location="src"/><property name="build"location="build"/><property name="dist"location="dist"/><target name="init"><!-- Create the time stamp --><tstamp/><!-- Create the build directory structure used by compile --><mkdir dir="${build}"/></target><target name="compile"depends="init"description="compile the source"><!-- Compile the Java code from ${src} into ${build} --><javac srcdir="${src}"destdir="${build}"/></target><target name="dist"depends="compile"description="generate the distribution"><!-- Create the distribution directory --><mkdir dir="${dist}/lib"/><!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file --><jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar"basedir="${build}"/></target><target name="clean"description="clean up"><!-- Delete the ${build} and ${dist} directory trees --><delete dir="${build}"/><delete dir="${dist}"/></target></project>

そして ant -f grand.xml grandコマンドを実行すると依存関係を可視化できた❗️

お試し2

次に Apache Ant のドキュメント Tutorial: Hello World with Apache Antに載っている build.xmlを使う.

ant.apache.org

<project name="HelloWorld"basedir="."default="main"><property name="src.dir"value="src"/><property name="build.dir"value="build"/><property name="classes.dir"value="${build.dir}/classes"/><property name="jar.dir"value="${build.dir}/jar"/><property name="main-class"value="oata.HelloWorld"/><target name="clean"><delete dir="${build.dir}"/></target><target name="compile"><mkdir dir="${classes.dir}"/><javac srcdir="${src.dir}"destdir="${classes.dir}"/></target><target name="jar"depends="compile"><mkdir dir="${jar.dir}"/><jar destfile="${jar.dir}/${ant.project.name}.jar"basedir="${classes.dir}"><manifest><attribute name="Main-Class"value="${main-class}"/></manifest></jar></target><target name="run"depends="jar"><java jar="${jar.dir}/${ant.project.name}.jar"fork="true"/></target><target name="clean-build"depends="clean,jar"/><target name="main"depends="clean,run"/></project>

そして ant -f grand.xml grandコマンドを実行すると依存関係を可視化できた❗️

カスタマイズ用プロパティファイル

可視化した依存関係の色は以下のように決まっている🎨

  • 黄色: デフォルトターゲット
  • 緑色: descriptionありターゲット
  • 白色: descriptionなしターゲット

カスタマイズ用プロパティファイルを使うと一部の設定を変更できる.今回は以下の設定で grand.propertiesを作った.各ノードのフォントサイズを大きくして,descriptionありターゲットも 白色(楕円形)にしてみた.正直 descriptionは書かないこともあって,デフォルトの 緑色白色は同じで良いかな〜と思った.

dot.node.attributes=fontsize="20"
dot.mainnode.attributes=fillcolor="white"

そして grand.xmlpropertyタグの設定と grandタグに outputconfigfileオプションを追加すれば OK👌

<project name="Grand"><target name="grand"><property file="grand.properties"/><typedef resource="net/ggtools/grand/antlib.xml"classpath="lib/grand-1.9.7.jar"/><grand output="build.dot"buildfile="build.xml"outputconfigfile="grand.properties"/></target></project>

お試し1で使った build.xmlに対して ant -f grand.xml grandコマンドを実行すると,ちゃんとフォントサイズが大きくなって,緑色(長方形)もなくなっていた❗️

習慣化 振り返り(2024年1-6月)

$
0
0

2024年から Habitifyを使って個人的な習慣化管理を楽しんでいる❗️

2024年前半(1-6月)の習慣化を振り返ろうと思う \( 'ω')/

www.habitify.me

朝活🌅

2023年2月から1年半ほど「朝活」を続けていて,Google Sheets に記録していた朝活メモを2024年から Habitify に移行した.2023年の朝活に関しては以下の記事にまとめてある☕

kakakakakku.hatenablog.com

2024年前半(1-6月)で朝活を「176日」継続できたー👏

以下の Habitify ログは 7/9 に取得したため,9日間は差し引いてある.

朝活メトリクス

ちなみに朝活を skip して OK という条件として「飲み会の翌朝」「体調不良の日」の2つを決めていて,半年間で6日は skip した.言い換えると「今日は朝活したくないな〜」という気持ちの弱さが出た場合に限って不達成となる運用にしている😇とは言えそういう日はなくて僕自身にとって朝活はとにかく貴重な時間になっている🕐

朝活で取り組んだこと

Habitify API を使って Habitify メモを集計してみた.176日間コツコツとインプットできた❗️

  25 LocalStack
  22 Python 実践レシピ
  17 Start building with Next.js
  16 位置情報エンジニア養成講座
  16 dbt
  14 AWS コンピュータービジョン開発の教科書
  10 runn
   9 Web API テスト技法
   8 Terraform の教科書
   8 Microsoft Learn GitHub Actions
   8 Bedrock ワークショップ
   4 SageMaker Examples
   4 React-Admin Tutorial
   4 Amazon Verified Permissions workshop
   2 amazon-s3-multipart-upload-transfer-acceleration
   2 Terraform イベント準備
   2 PyTorch
   1 ソフトウェアアーキテクチャメトリクス
   1 Hasura Tutorial PostgreSQL
   1 CDK x EventBridge Pipes
   1 Bedrock デジタルトレーニング
   1 AWS What's New

そして朝活でインプットしたことをテックブログにアウトプットする流れも強く意識している.以下に一部の記事を貼っておく📝 他にもたくさんアウトプットできた〜

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

10分間読書📕

2024年2月12日から「10分間読書」という習慣化を追加した.今まで本を読みたくてもまとまった時間が取れたら読もうというマインドセット(優先順位)になっていて積読になってしまうという課題があった.毎日ガッツリ読む必要はなくてコツコツで良いじゃん📕と考えて「10分間」に決めた.

結果的に積読を減らせていて最高だし,書籍からインプットする機会も増えた.そして,時間に関しては忙しい日は10分間で終わればよく,もっと読みたいな〜と思う日はキリが良いところまで読めばよく,ハードルをできる限り下げた「10分間」という軽さが背中を押してくれている感じがする😀

2024年前半(2-6月)で10分間読書を「140日」継続できたー👏

以下の Habitify ログは 7/9 に取得したため,9日間は差し引いてある.

10分間読書メトリクス

10分間読書で読んだ本

Habitify API を使って Habitify メモを集計してみた.本を読むのは遅いけど毎日楽しくコツコツ読めて本当に良かった \( 'ω')/

  18 データエンジニアリングの基礎
  17 認証と認可 Keycloak 入門
  17 Team Guide to Software Testability
  17 Good Code, Bad Code
  16 AWS コスト最適化ガイドブック
  14 組織を変える5つの対話
  14 テスト駆動 Python 第2版
  12 ソフトウェアアーキテクチャメトリクス
   8 スモール・リーダーシップ
   7 Terraform の教科書

その他

他に3つ習慣化を管理している💪

  • ビタミンを摂る(毎日)
  • プランク(毎日)
  • 1週間を振り返る(毎週)

習慣化に悩む人におすすめの一冊

僕自身は興味のあることを習慣化するのが得意だとは思うけど,もし習慣化に悩む人がいたら「小さな習慣」をおすすめしたい📕

kakakakakku.hatenablog.com

まとめ

2024年後半(7-12月)も Habitifyを使って習慣化を楽しむぞー❗️

関連記事

Habitify API に関しては以下にまとめてある.

kakakakakku.hatenablog.com

Powertools for AWS Lambda (Python) の Parameters で DynamoDB GSI からパラメータを取得するカスタムプロバイダ

$
0
0

Powertools for AWS Lambda (Python) の Parametersを使うと AWS Systems Manager Parameter Store / AWS Secrets Manager / AWS AppConfig / Amazon DynamoDB から AWS Lambda 関数で使うパラメータ(何かしらの値)を簡単に取得できる❗️また取得したパラメータを内部的にキャッシュして,パラメータの過剰な取得を抑制する仕組みもある.個人的には AWS Systems Manager Parameter Store をバックエンドによく使っている👌

docs.powertools.aws.dev

Amazon DynamoDB からパラメータを取得する場合は Powertools for AWS Lambda (Python) の DynamoDBProviderを使う.しかし DynamoDBProviderは現状 Amazon DynamoDB テーブルからしかパラメータを取得できず,データ構造的に Amazon DynamoDB テーブルの GSI (Global Secondary Index)からパラメータを取得したいという場面があったりする.今回は「カスタムプロバイダ」を実装して GSI からパラメータを取得してみた \( 'ω')/

1. Amazon DynamoDB テーブルからパラメータを取得する

まずは Amazon DynamoDB テーブルからパラメータを取得する.今回は複数値を取得する get_multiple()を前提にする.

👾 template.yaml(一部)

Amazon DynamoDB テーブルは Powertools ドキュメントの例を参考に AWS SAM (AWS CloudFormation) で以下のように構築した.パーティションキーは idで,ソートキーは skとなる.

Resources:Table:Type: AWS::DynamoDB::Table
    Properties:TableName: parameters
      AttributeDefinitions:- AttributeName: id
          AttributeType: S
        - AttributeName: sk
          AttributeType: S
      KeySchema:- AttributeName: id
          KeyType: HASH
        - AttributeName: sk
          KeyType: RANGE
      BillingMode: PAY_PER_REQUEST

そして,同じく Powertools ドキュメントに載っているサンプルデータを AWS CLI で登録しておく.

$ aws dynamodb put-item --table-name parameters \--item'{ "id": { "S": "config" }, "sk": { "S": "endpoint_comments" }, "value": { "S": "https://jsonplaceholder.typicode.com/comments/" } }'
$ aws dynamodb put-item --table-name parameters \--item'{ "id": { "S": "config" }, "sk": { "S": "limit" }, "value": { "S": "10" } }'

最終的に以下のようになる👌

id sk value
config endpoint_comments https://jsonplaceholder.typicode.com/comments/
config limit 10

👾 app.py

AWS Lambda 関数は簡単に実装できる❗️Amazon DynamoDB テーブル parametersを参照するように DynamoDBProviderを初期化して,get_multiple()を使って configという値をキーにパラメータを取得している.デフォルトではパラメータを 5秒間キャッシュするけど,今回は動作確認も兼ねて max_ageを設定して 30秒間にした.

from aws_lambda_powertools.utilities import parameters

dynamodb_provider = parameters.DynamoDBProvider(table_name='parameters')


deflambda_handler(event, context):
    configs = dynamodb_provider.get_multiple('config', max_age=30)
    print(configs)

動作確認

AWS Lambda 関数を定期的に実行しつつ,途中で以下のコマンドを実行して3つ目の config を追加する.値は適当💨

$ aws dynamodb put-item --table-name parameters \--item'{ "id": { "S": "config" }, "sk": { "S": "sort" }, "value": { "S": "ASC" } }'

結果的にプロパティのキャッシュが切れてから3つ目の config も取得された👌

{'endpoint_comments': 'https://jsonplaceholder.typicode.com/comments/', 'limit': '10'}
{'endpoint_comments': 'https://jsonplaceholder.typicode.com/comments/', 'limit': '10'}
{'endpoint_comments': 'https://jsonplaceholder.typicode.com/comments/', 'limit': '10'}
{'endpoint_comments': 'https://jsonplaceholder.typicode.com/comments/', 'limit': '10', 'sort': 'ASC'}

2. Amazon DynamoDB GSI (Global Secondary Index) パラメータを取得する

今度は Amazon DynamoDB GSI (Global Secondary Index) からパラメータを取得する.しかし Powertools for AWS Lambda (Python) の DynamoDBProviderは GSI をサポートしていないためカスタムプロバイダを実装する.同じく今回は複数値を取得する get_multiple()を前提にする.GSI サポートは issue にもなかった💨(特に需要なさそう?)

👾 template.yaml(一部)

Amazon DynamoDB テーブルは少し構造を変えて uuidをパーティションキーにした.そして,id-index GSI では idをパーティションキーにして,最初の例と同じデータを取得できるようにした.

Resources:Table:Type: AWS::DynamoDB::Table
    Properties:TableName: parameters
      AttributeDefinitions:- AttributeName: uuid
          AttributeType: S
        - AttributeName: id
          AttributeType: S
      KeySchema:- AttributeName: uuid
          KeyType: HASH
      BillingMode: PAY_PER_REQUEST
      GlobalSecondaryIndexes:- IndexName: id-index
          KeySchema:- AttributeName: id
              KeyType: HASH
          Projection:ProjectionType: ALL

同じようにサンプルデータを AWS CLI で登録しておく.

$ aws dynamodb put-item --table-name parameters \--item'{ "uuid": { "S": "d33b9dca-952c-4218-b05e-cbd2222ef766" }, "id": { "S": "config" }, "sk": { "S": "endpoint_comments" }, "value": { "S": "https://jsonplaceholder.typicode.com/comments/" } }'
$ aws dynamodb put-item --table-name parameters \--item'{ "uuid": { "S": "c2be3412-1107-48d0-b992-750b8fcd4d42" }, "id": { "S": "config" }, "sk": { "S": "limit" }, "value": { "S": "10" } }'

最終的に以下のようになる👌

uuid id sk value
d33b9dca-952c-4218-b05e-cbd2222ef766 config endpoint_comments https://jsonplaceholder.typicode.com/comments/
c2be3412-1107-48d0-b992-750b8fcd4d42 config limit 10

👾 dynamodb.py

次にカスタムプロバイダ DynamoDBIndexProviderを実装する.サンプルとして実装量をできる限り減らしているけど,汎用的に実装するのであれば DynamoDBProviderの実装を参考にして,指定できる値を増やしたり,LastEvaluatedKeyを評価したりすると良いと思う👀

github.com

カスタムプロバイダの実装方法は Powertools ドキュメントにも載っているけど,BaseProviderを継承して _get()_get_multiple()を実装すれば OK👌今回は複数値を前提にしているため,_get()NotImplementedErrorを返して,_get_multiple()では GSI に Query を実行している.

import boto3
from aws_lambda_powertools.utilities.parameters import BaseProvider


classDynamoDBIndexProvider(BaseProvider):
    def__init__(self, table_name, index_name):
        self.index_name = index_name
        self.table = boto3.resource('dynamodb').Table(table_name)
        super().__init__()

    def_get(self):
        raiseNotImplementedErrordef_get_multiple(self, id):
        response = self.table.query(
            IndexName=self.index_name,
            KeyConditionExpression='id = :id',
            ExpressionAttributeValues={':id': id},
        )

        return {item['sk']: item['value'] for item in response['Items']}

👾 app.py

こっちはほぼ同じで,Powertools for AWS Lambda (Python) の DynamoDBProviderDynamoDBIndexProviderに置き換えた程度👌 GSI 名を index_nameで指定している〜

from dynamodb import DynamoDBIndexProvider

dynamodb_provider = DynamoDBIndexProvider(table_name='parameters', index_name='id-index')


deflambda_handler(event, context):
    configs = dynamodb_provider.get_multiple('config', max_age=30)
    print(configs)

動作確認

同じく AWS Lambda 関数を定期的に実行しつつ,途中で3つ目の config を追加する.

$ aws dynamodb put-item --table-name parameters \--item'{ "uuid": { "S": "571a981c-9002-4e2f-b3e5-207f8a0ce1fd" }, "id": { "S": "config" }, "sk": { "S": "sort" }, "value": { "S": "ASC" } }'

結果的にプロパティのキャッシュが切れてから3つ目の config も取得された👌

{'limit': '10', 'endpoint_comments': 'https://jsonplaceholder.typicode.com/comments/'}
{'limit': '10', 'endpoint_comments': 'https://jsonplaceholder.typicode.com/comments/'}
{'limit': '10', 'endpoint_comments': 'https://jsonplaceholder.typicode.com/comments/'}
{'limit': '10', 'endpoint_comments': 'https://jsonplaceholder.typicode.com/comments/', 'sort': 'ASC'}

まとめ

Powertools for AWS Lambda (Python) の Parametersで Amazon DynamoDB テーブルの GSI (Global Secondary Index)からパラメータを取得する場合はカスタムプロバイダを実装しよう❗️

Powertools for AWS Lambda (Python) の Feature flags で「時間ベースフィーチャーフラグ」を試す

$
0
0

Powertools for AWS Lambda (Python) の Feature flagsを使って「時間ベースフィーチャーフラグ (Time based feature flags)」を試してみた.Powertools for AWS Lambda (Python) の Feature flags はデフォルトだと AWS AppConfig をバックエンドにしていて,時間ベース以外に文字列部分一致・数値範囲など柔軟に条件をしてフラグ制御 (true or false) ができる👌

docs.powertools.aws.dev

検証環境

アプリケーション (AWS Lambda) とフィーチャーフラグ (AWS AppConfig) は運用上ライフサイクルが異なるため別のスタックにした.

├── application
│   ├── src
│   │   └── app.py
│   └── template.yaml
└── features
    └── template.yaml

1. features

まず AWS AppConfig をデプロイする.Powertools ドキュメントを参考に AWS CloudFormation テンプレートを書いた.ちなみに Powertools ドキュメントに載っているサンプルだと AWS AppConfig のデプロイ戦略にビルトインの AppConfig.AllAtOnceが指定されていた📝これは即時デプロイをしたら10分間待つ戦略で (BakeTime 10 minutes) 今回はサクサクと検証をしたく,BakeTime なしのカスタムデプロイ戦略を追加した💡

そして HostedConfigurationVersionに時間ベースフィーチャーフラグ (Time based feature flags) を設定した.今回はサンプルとして開始時間と終了時間が決まっているキャンペーン(campaign-acampaign-b)の制御をする❗️

  • campaign-a: 2024-07-06T12:00:00 ~ 2024-07-06T12:29:59
  • campaign-b: 2024-07-06T12:30:00 ~ 2024-07-06T12:59:59

ちなみに時間ベースだと,他には「毎日◯時〜◯時」「毎週◯曜日」という設定もできる📅

AWSTemplateFormatVersion: 2010-09-09

Resources:Application:Type: AWS::AppConfig::Application
    Properties:Name: sandbox
  Environment:Type: AWS::AppConfig::Environment
    Properties:Name: prd
      ApplicationId:!Ref Application
  ConfigurationProfile:Type: AWS::AppConfig::ConfigurationProfile
    Properties:Name: profile
      ApplicationId:!Ref Application
      LocationUri: hosted
  HostedConfigurationVersion:Type: AWS::AppConfig::HostedConfigurationVersion
    Properties:ApplicationId:!Ref Application
      ConfigurationProfileId:!Ref ConfigurationProfile
      ContentType: application/json
      Content: |
        {"campaign-a":{"default":false,
            "rules":{"Enable Campaign A":{"when_match":true,
                "conditions":[{"action":"SCHEDULE_BETWEEN_DATETIME_RANGE",
                    "key":"CURRENT_DATETIME",
                    "value":{"START":"2024-07-06T12:00:00",
                      "END":"2024-07-06T12:29:59",
                      "TIMEZONE":"Asia/Tokyo"}}]}}},
          "campaign-b":{"default":false,
            "rules":{"Enable Campaign B":{"when_match":true,
                "conditions":[{"action":"SCHEDULE_BETWEEN_DATETIME_RANGE",
                    "key":"CURRENT_DATETIME",
                    "value":{"START":"2024-07-06T12:30:00",
                      "END":"2024-07-06T12:59:59",
                      "TIMEZONE":"Asia/Tokyo"}}]}}}}DeploymentStrategy:Type: AWS::AppConfig::DeploymentStrategy
    Properties:Name: Custom.AllAtOnce
      DeploymentDurationInMinutes:0GrowthFactor:100FinalBakeTimeInMinutes:0ReplicateTo: NONE
  Deployment:Type: AWS::AppConfig::Deployment
    Properties:ApplicationId:!Ref Application
      EnvironmentId:!Ref Environment
      ConfigurationProfileId:!Ref ConfigurationProfile
      ConfigurationVersion:!Ref HostedConfigurationVersion
      DeploymentStrategyId:!Ref DeploymentStrategy

ちなみに AWS AppConfig 自体のフィーチャーフラグとは関係なく,AWS AppConfig をバックエンドにした Powertools for AWS Lambda (Python) の仕組みになる.コンソールだと 自由形式の設定プロファイルとなる.少しややこしい😇

2. application

次は AWS Lambda をデプロイする.AWS SAM テンプレートで Powertools Layer を設定しておく.あと IAM Role に AWS AppConfig を操作する権限を付けておく.Powertools ドキュメントには appconfig:GetLatestConfigurationappconfig:StartConfigurationSessionで十分と書いてあった✅

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:Function:Type: AWS::Serverless::Function
    Properties:FunctionName: powertools-feature-flags-time-based
      CodeUri: src/
      Handler: app.lambda_handler
      Runtime: python3.12
      Architectures:- x86_64
      Layers:- arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:75
      Role: arn:aws:iam::000000000000:role/xxxxx

コードでは Powertools for AWS Lambda (Python) の Feature flags で evaluate()を実行して,結果を表示するシンプルな実装にした👌

from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags

app_config = AppConfigStore(application='sandbox', environment='prd', name='profile')
feature_flags = FeatureFlags(store=app_config)


deflambda_handler(event, context):
    campaign_a = feature_flags.evaluate(name='campaign-a', default=False)
    campaign_b = feature_flags.evaluate(name='campaign-b', default=False)

    features = {
        'campaign-a': campaign_a,
        'campaign-b': campaign_b,
    }

    print(features)

動作確認

計4回実行してみた🕐

期待通りの結果になったー \( 'ω')/

👇 11:50
{'campaign-a': False, 'campaign-b': False}
👇 12:10
{'campaign-a': True, 'campaign-b': False}
👇 12:40
{'campaign-a': False, 'campaign-b': True}
👇 13:10
{'campaign-a': False, 'campaign-b': False}

フィーチャーフラグのキャッシュ機能

Powertools for AWS Lambda (Python) の Feature flags は毎回 AWS AppConfig から取得するのではなく,デフォルトでは「5秒間」キャッシュされる📂AWS Lambda 関数のライフサイクルとして,初期化後にもフィーチャーフラグを更新できる👌

docs.aws.amazon.com

AWS AppConfig の呼び出し回数を減らすこととフィーチャーフラグの鮮度を高めることのトレードオフにはなるけど,もしキャッシュ時間を変更する場合は AppConfigStoreで初期化するときに max_ageを設定して変更できる.

app_config = AppConfigStore(application='sandbox', environment='prd', name='profile', max_age=30)

単体テスト

Powertools ドキュメントには pytest-mock を使って AWS AppConfig に依存せずテストをするサンプル実装が載っていた.LocalStack も AWS AppConfig をサポートしているけど,LocalStack Proで使えるため,契約が必要になってしまう💨

docs.localstack.cloud

関連記事

kakakakakku.hatenablog.com

人と向かい合おう!エンジニアリングマネジャーに限らずおすすめできる「エンジニアリングマネジャー入門」を読んだ

$
0
0

2024年7月14日に出版された新著「エンジニアリングマネジャー入門」を読んだ📕

本書は「人と向かい合う」ことにフォーカスしていて,エンジニアリングマネジャーがどんなことを日々考えて,どんなことに日々対処しているのかという実践的なノウハウがまとまっていた.著者が Google をはじめとした多くの組織で実践してきた体験談がベースになっているからこその説得力も感じられる一冊だった💡

本書は翻訳を担当された @iwashi86さんに送っていただいた❗️活動量の多さと影響力の広さに驚きです🎉

出版おめでとうございます〜 \( 'ω')/

目次

本書は「計24種類」のトピックから構成されていて,一つ一つコンパクトにまとまってて読みやすかった📕

もし気になるトピックがあったら読んでみてもらえればと.

  • Part 1: 自分のチーム
    • Chapter 1: 自分のチームを大切にする
    • Chapter 2: 価値観の価値
    • Chapter 3: 信頼と弱さ
    • Chapter 4: 自分のチームは「彼ら」ではなく「私たち」
    • Chapter 5: 幸せとやる気の原動力
    • Chapter 6: 長期的な従業員のケア
    • Chapter 7: キャリアラダー
    • Chapter 8: 重要な1on1
  • Part 2: コラボレーション
    • Chapter 9: マネジャーとしてのコミュニケーション
    • Chapter 10: チェンジマネジメント
    • Chapter 11: フィードバックの与え方
    • Chapter 12: フィードバックを受け取る
    • Chapter 13: 良いミーティング
    • Chapter 14: 対立のマネジメント
    • Chapter 15: クロスチームとオープンソースのコラボレーション
  • Part 3: チームが最高の仕事をできるように支援する
    • Chapter 16: チームの仕事の優先度付け
    • Chapter 17: プルリクエストのスコープを絞る方法
    • Chapter 18: 実行の速度
    • Chapter 19: プロダクトとエンジニアリングの時間配分
  • Part 4: 自分の仕事
    • Chapter 20: ハイレベルでの優先度付け
    • Chapter 21: 日々の優先度付け
    • Chapter 22: 境界線を設定する
    • Chapter 23: まず自分を大切にすること
    • Chapter 24: 自分を信じること

pub.jmam.co.jp

エンジニアリングマネジャーに限らず読もう

本書を読んでまず感じたのは「書籍タイトルを見て自分には関係ないな〜と思わず一度読んでみるべきでは!?」ということだった😀

僕自身が過去にエンジニアリングマネジャー的なポジションを兼務していた時期があったり,そもそもポジションに関係なく,たとえ IC (Individual Contributor) だとしても組織や人に目を向けることが当たり前だと思っていることも関係するけど(全員リーダー❗️),エンジニアリングマネジャーに限らず読んで欲しいな〜と思った \( 'ω')/

目指すキャリアパスにエンジニアリングマネジャーという選択肢がなかったとしても,エンジニアリングマネジャーが日々こういうことを考えているんだな〜と知っておくことは重要だし,今こういう行動をしたらきっとエンジニアリングマネジャーは喜んでくれるだろうな〜💕というある意味 "打算的な"振る舞いもできたりすると思う.

そして比較的ジュニアなエンジニアにも本書をおすすめしたく,キャリアを進めていく人たちはこういうことを考えているんだよ〜という気付きになると思う.視野が広がってもっと働きやすくなりそう💪

訳者あとがきが良かった

本書を読み終えて,どの Chapter も良かったけど,特に好きな Chapter はどこかな〜と考えたら以下の3つになった💡自分自身も意識しているし,改めて勉強になった Chapter だった.

  • Chapter 3: 信頼と弱さ
  • Chapter 8: 重要な1on1
  • Chapter 14: 対立のマネジメント

Chapter 以外だと,本書の最後にある「訳者あとがき」がたった3ページながらとても良かった👏本書の特徴を紹介しつつ,同じエンジニアリングマネジャーというテーマながら方向性の異なる「エレガントパズル」との比較も紹介されていて,併読したくなった.また本書を読んで今すぐピンと来なくてもまた読み直してくれれば良いよ〜という実践と読書の融合を推奨している流れも良かった❗️

エレガントパズルまだ読んでなくてゴメンナサイ🙏

フィードバックは僕自身の改善ポイント

Chapter 11 と Chapter 12 は「フィードバック」がトピックになっていて,僕自身はフィードバックを与えることに少し苦手意識があるため,特に Chapter 11 は数回読み直した.

本書に載っている価値観ワークのサンプルとして「優しいフィードバックを受けたい価値観」と「率直なフィードバックを好む価値観」という例が載っていた.僕自身は率直なフィードバックを求めていて,回りくどいフィードバックを嫌う傾向にあるため,相手にも「良かれと思って」フィードバックをするけど,受ける側の心の状態が整ってなく受け入れてもらえなかったという経験もあり,過去を反省しながら読んだ.フィードバックをしたりされたりすることは誰しもあるし,やはりエンジニアリングマネジャーに限らず読むべきポイントがまとまってるな〜と感じた.

引用されていた書籍「あなたを成長させるフィードバックの授業」は読んでみよう📕

誤植など

  • P.91 車用社用(文脈的に間違ってるかな?と感じた)
  • P.116 あなた今からあなたは今から
  • P.126 できるたけできるだけ
  • P.201 不適切かしれませんが不適切かもしれませんが

まとめ

またイイ一冊に出会えた❗️

「エンジニアリングマネジャー入門」おすすめです〜 \( 'ω')/

Viewing all 903 articles
Browse latest View live