262118 bytes
年に1回ぐらい試すのにいつも忘れるのでメモ
仕事でタグの棚卸しをしていてこのsemver記法的に正しいんだっけみたいなのぱぱっと見る必要があったので備忘録がてら残しとく
オンラインでぱぱっと見れたらいいんだけど、
なんか探し方が悪かったのかsemverとは何かみたいなサイトばっかりひっかかったので
大人しくnpmのsemverパッケージ使う方が早そうだった
www.npmjs.com
package.jsonとか書いてるならnpm semantic version calculatorとかのほうが良い
# 適当に入れる(node.js入ってる前提) $ npm install semver $ node
> const semver = require('semver') // ただ正しいかどうかならvalidでnullが返ってこなければOK > semver.valid('1.0.0') '1.0.0' > semver.valid('1.2.3.4') null // どこがどれだっけ的なのを確認するならparseで見るのが手っ取り早かった > semver.parse('1.0.0-beta') SemVer { options: {}, loose: false, includePrerelease: false, raw: '1.0.0-beta', major: 1, minor: 0, patch: 0, prerelease: [ 'beta' ], build: [], version: '1.0.0-beta' }
pythonのreduceたまにどこにあるかよくわかんなくて迷う
from functools import reduce ld = [{'Key': 'Key1', 'Value': 'Value1'}, {'Key': 'Key2', 'Value': 'Value2'}, {'Key': 'Key3', 'Value': 'Value3'}] reduce(lambda acc, x: dict(acc, **{x['Key']: x['Value']}) , ld, {}) # {'Key1': 'Value1', 'Key2': 'Value2', 'Key3': 'Value3'} ld2 = [{'Key1': 'Value1'}, {'Key2': 'Value2'}, {'Key3': 'Value3'}] reduce(lambda acc, x: dict(acc, **x), ld2, {}) # {'Key1': 'Value1', 'Key2': 'Value2', 'Key3': 'Value3'}
あんまりケースとしてはなさそうだけどCDKの実行基盤でFargateを採用してみた
なんでそんなことしてたかの詳細は割愛するとして、
自分同様DockerImageにCDKの諸々を固めて
cdk --profile なんたら --role-arn なんたら2 deploy
みたいなことをFargateでやっていて且つ他アカウントの別リージョンにデプロイする人の助けにでもなれば報われる
他アカウントの別リージョンにデプロイするはずが、
他アカウントのデプロイ元のFargateが実行されているリージョンでデプロイがされる
例えば、Aアカウントの東京リージョンでFargateを立ち上げて
そこからBアカウントのオレゴンリージョンにデプロイとかしようとするとBアカウントの東京リージョンにデプロイされるという話
上でdeploy時にprofileを指定している通り
~/.aws/config
に↓のようなconfigを入れています
~/.aws/credentials
は空ファイルにしてます
[profile xxx] region = us-west-2 role_arn = arn:aws:iam::xxxxxxxxxx:role/DeployRole role_session_name = deploy credential_source = EcsContainer
環境変数にも似たようなものを入れてます
(というか↑のconfigを動的に生成する為に環境変数経由でデプロイ先のアカウントとリージョンをコンテナに伝えてます)
ACCOUNT_ID=xxxxxxxxxx ACCOUNT_REGION=us-west-2
Fargate起動時にはプロセスにはこんな感じの環境変数が自動的に設定されています
HOSTNAME=ip-x-y-z-zz.ap-northeast-1.compute.internal AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/xxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_DEFAULT_REGION=ap-northeast-1 AWS_EXECUTION_ENV=AWS_ECS_FARGATE AWS_REGION=ap-northeast-1 ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ECS_CONTAINER_METADATA_URI_V4=http://169.254.170.2/v4/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
cdkのdoctorで見ると↓みたいなのが出てきます
cdk --profile xxx --role-arn arn:aws:iam::xxxxxxxxxx:role/DeployRoleForCloudFormation doctor :information_source: CDK Version: 1.110.1 (build 0028d09) :information_source: AWS environment variables: - AWS_EXECUTION_ENV = AWS_ECS_FARGATE - AWS_CONTAINER_CREDENTIALS_RELATIVE_URI = /v2/credentials/xxxxxxxxxxxxxxxxxxxxxx - AWS_DEFAULT_REGION = ap-northeast-1 - AWS_REGION = ap-northeast-1 - AWS_STS_REGIONAL_ENDPOINTS = regional - AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1 - AWS_SDK_LOAD_CONFIG = 1 :information_source: No CDK environment variables END
勘の良い人であればなんか怪しいやつらがいることに気がつきますね?
こいつらです
- AWS_DEFAULT_REGION = ap-northeast-1 - AWS_REGION = ap-northeast-1
もしこいつらが原因ならリージョンとアカウントをCDKの bin/stack.ts
に環境変数の値で直接渡せば解決するんじゃね?と考えました
↓は公式のサンプルでとりあえずこの通りに渡すことにしました
Environments - AWS Cloud Development Kit (CDK)
new MyDevStack(app, 'dev', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }});
そして一番上の方で設定していた環境変数とは別に↓のような感じでCDK向けに新たに環境変数を追加しました
CDK_DEFAULT_ACCOUNT="${ACCOUNT_ID}" CDK_DEFAULT_REGION="${ACCOUNT_REGION}"
そしてサンプルのように渡した上でdoctorでも確認をし問題なく設定されていることを確認しました
cdk --profile xxx --role-arn arn:aws:iam::xxxxxxxxxx:role/DeployRoleForCloudFormation doctor ... :information_source: CDK environment variables: - CDK_DEFAULT_ACCOUNT = xxxxxxxxxx - CDK_DEFAULT_REGION = us-west-2
この状態でbootstrapを叩くと...
cdk --profile xxx --role-arn arn:aws:iam::xxxxxxxxxx:role/DeployRoleForCloudFormation bootstrap CDK_DEFAULT_ACCOUNT:xxxxxxxxxx //これはconsole.logを仕込んだ CDK_DEFAULT_REGION:ap-northeast-1 // これはconsole.logを仕込んだ
CDK_DEFAULT_REGION:ap-northeast-1😇
なんか勝手に上書きされました
先程の勘が良い人であれば、なんか上書きしそうなやつがいたことに心当たりがありますね?
こいつらです
- AWS_DEFAULT_REGION = ap-northeast-1 - AWS_REGION = ap-northeast-1
ここまでくるとCDKの内部の挙動について読む必要が出てくるので読んでいきます
(下記の推測が間違っている可能性もなくはないので、最終的には最新のコードを読んでください)
CDK_DEFAULT_REGION
は ここで定義されています
/** * Environment variable set by the CDK CLI with the default AWS region. */ export const DEFAULT_REGION_ENV = 'CDK_DEFAULT_REGION';
この変数で値を設定しているところを探します
async function populateDefaultEnvironmentIfNeeded(aws: SdkProvider, env: { [key: string]: string | undefined}) { env[cxapi.DEFAULT_REGION_ENV] = aws.defaultRegion; ... }
aws.defaultRegionを探しますが、そもそもawsは引数で渡されているのでそちらから探します
関数自体の呼び出しはexecProgramの中で呼ばれています
export async function execProgram(aws: SdkProvider, config: Configuration): Promise<cxapi.CloudAssembly> { const env: { [key: string]: string } = { }; const context = config.context.all; await populateDefaultEnvironmentIfNeeded(aws, env); ... }
execProgramはCloudExecutableの内部で呼ばれていることが以下の二箇所からわかります
async function initCommandLine() { ... const sdkProvider = await SdkProvider.withAwsCliCompatibleDefaults({ profile: configuration.settings.get(['profile']), ec2creds: argv.ec2creds, httpOptions: { proxyAddress: argv.proxy, caBundlePath: argv['ca-bundle-path'], }, }); const cloudFormation = new CloudFormationDeployments({ sdkProvider }); const cloudExecutable = new CloudExecutable({ configuration, sdkProvider, synthesizer: execProgram, }); ... }
と
export class CloudExecutable { ... private async doSynthesize(): Promise<CloudAssembly> { ... while (true) { const assembly = await this.props.synthesizer(this.props.sdkProvider, this.props.configuration); ... } ... } ... }
CloudExecutableのsdkProviderはinitCommandLine側で作られたものであることも同時にわかるので
SdkProvider.withAwsCliCompatibleDefaultsを探します
export class SdkProvider { ... public static async withAwsCliCompatibleDefaults(options: SdkProviderOptions = {}) { ... const region = await AwsCliCompatible.region({ profile: options.profile, ec2instance: options.ec2creds, }); return new SdkProvider(chain, region, sdkOptions); } ... public constructor( private readonly defaultChain: AWS.CredentialProviderChain, /** * Default region */ public readonly defaultRegion: string, private readonly sdkOptions: ConfigurationOptions = {}) { } ... }
defaultRegionはAwsCliCompatible.regionで取得した値を使用していることがわかったので更に探します
export class AwsCliCompatible { ... public static async region(options: RegionOptions = {}): Promise<string> { ... let region = process.env.AWS_REGION || process.env.AMAZON_REGION || process.env.AWS_DEFAULT_REGION || process.env.AMAZON_DEFAULT_REGION; while (!region && toCheck.length > 0) { const opts = toCheck.shift()!; if (await fs.pathExists(opts.filename)) { const configFile = new SharedIniFile(opts); const section = await configFile.getProfile(opts.profile); region = section?.region; } } ... return region; } }
ようやく終わりが見えました
ここまで来ると設定されるリージョンの優先順位は
process.env.AWS_REGION > process.env.AMAZON_REGION > process.env.AWS_DEFAULT_REGION > process.env.AMAZON_DEFAULT_REGION > プロファイルのセクションにあるリージョン > その他の設定(ここはコードを見てください)
であることがわかりました
ここで元々の設定を振り返ると明らかに2つの環境変数が悪さをしていることがわかります
- AWS_DEFAULT_REGION = ap-northeast-1 - AWS_REGION = ap-northeast-1
しかしながらこれらの環境変数をunsetして実行するのが良いとは言いづらいので、
(Fargate側で何かに利用している可能性も否めない)
最終的に下記2つの環境変数を利用することをやめて、
元々自分で設定していた環境変数をbin/stack.ts
で利用することで上書きを回避し問題なく動かす事ができました
CDK_DEFAULT_ACCOUNT CDK_DEFAULT_REGION
最近CDK使ってて出会した
端的に言えばCFnが悪いやつ(下はGithubのIssue)
ただまあ公式にも書いてあるこれをどうにかしてデプロイしたい
CreationPolicy、DeletionPolicy または UpdatePolicy 属性単独では更新できません。
更新できるのは、リソースを追加、変更、または削除する変更を含める場合だけです。
たとえば、リソースのメタデータ属性を追加または変更することはできます。
しかしながら一部変更を適用するのに、
いちいちリソースを変更まではしたくないなあと思って適当に回避策を思いついて試したらうまく動いたので残しとく
まずは適当にCDKのアプリケーションを作る(今回はTypeScriptだけど他の言語でも出来るはず)
cdk init app --language typescript # 作ると勝手にディレクトリ移動されるので今回はS3を使う npm install @aws-cdk/aws-s3
作ったらまずは適当にBucketを作る
import * as cdk from '@aws-cdk/core'; import * as s3 from '@aws-cdk/aws-s3'; export class CdkTestStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); new s3.Bucket(this, 'TestBucket', { bucketName: 'ex-test-bucket-000000' }); } }
デプロイする
npm run cdk -- deploy
で、作ったあとにやっぱRemovalPolicyにDESTROYをつけたくなったとする
new s3.Bucket(this, 'TestBucket', { bucketName: 'ex-test-bucket-000000', removalPolicy: cdk.RemovalPolicy.DESTROY, });
diffが出る(元々デフォルトでRetainがついてるので)
npm run cdk -- diff > cdk-test@0.1.0 cdk > cdk "diff" Stack CdkTestStack Resources [~] AWS::S3::Bucket TestBucket TestBucket560B80BC ├─ [~] DeletionPolicy │ ├─ [-] Retain │ └─ [+] Delete └─ [~] UpdateReplacePolicy ├─ [-] Retain └─ [+] Delete
デプロイするとNo changes!!!!😇
npm run cdk -- deploy > cdk-test@0.1.0 cdk > cdk "deploy" CdkTestStack: deploying... CdkTestStack: creating CloudFormation changeset... ✅ CdkTestStack (no changes)
というわけで↓を新たに入れておく
new cdk.CfnOutput(this, 'UpdateTime', { description: 'UpdateTime', value: new Date().toLocaleString(), })
diffが増える
npm run cdk -- diff > cdk-test@0.1.0 cdk > cdk "diff" Stack CdkTestStack Resources [~] AWS::S3::Bucket TestBucket TestBucket560B80BC ├─ [~] DeletionPolicy │ ├─ [-] Retain │ └─ [+] Delete └─ [~] UpdateReplacePolicy ├─ [-] Retain └─ [+] Delete Outputs [+] Output UpdateTime UpdateTime: {"Description":"UpdateTime","Value":"2021/7/22 16:51:32"}
デプロイをする
今回はリソースを追加したので当たり前だが成功する
npm run cdk -- deploy > cdk-test@0.1.0 cdk > cdk "deploy" CdkTestStack: deploying... CdkTestStack: creating CloudFormation changeset... ✅ CdkTestStack Outputs: CdkTestStack.UpdateTime = 2021/7/22 16:52:02
今度はRemovalPolicyをRETAINに戻したくなったとする
new s3.Bucket(this, 'TestBucket', { bucketName: 'ex-test-bucket-000000', // removalPolicy: cdk.RemovalPolicy.DESTROY, これを削除する });
diffを見る
(ぱっと見、上で失敗したときのパターンに見える)
npm run cdk -- diff > cdk-test@0.1.0 cdk > cdk "diff" Stack CdkTestStack Resources [~] AWS::S3::Bucket TestBucket TestBucket560B80BC ├─ [~] DeletionPolicy │ ├─ [-] Delete │ └─ [+] Retain └─ [~] UpdateReplacePolicy ├─ [-] Delete └─ [+] Retain
これをdeployするとOutputsのUpdateTimeが変わる為、CDK(CFn)はちゃんとデプロイしてくれる
npm run cdk -- deploy > cdk-test@0.1.0 cdk > cdk "deploy" CdkTestStack: deploying... CdkTestStack: creating CloudFormation changeset... ✅ CdkTestStack Outputs: CdkTestStack.UpdateTime = 2021/7/22 16:56:21
再度diffを叩くと何も変更なしになる
(リソースへの変更は適用された為)
npm run cdk -- diff > cdk-test@0.1.0 cdk > cdk "diff" Stack CdkTestStack There were no differences
この状態でdeployするとUpdateTimeに対しての変更がかかる
npm run cdk -- deploy > cdk-test@0.1.0 cdk > cdk "deploy" CdkTestStack: deploying... CdkTestStack: creating CloudFormation changeset... ✅ CdkTestStack Outputs: CdkTestStack.UpdateTime = 2021/7/22 16:58:23
とまあこんな感じでOutputsにUpdateTimeとかを設定しておけば、
管理したいリソースに対して変更を加えずともOutputsに変更を加えて半強制的にデプロイをすることが出来て便利という小技