CDK 管道堆栈是否可以避免引用特定的回购协议和 Github 连接?
Can a CDK pipeline stack avoid referring to a specific repo and Github connection?
我的 CDK 管道栈有这段代码:
const pipeline = new CodePipeline(this, id, {
pipelineName: id,
synth: new CodeBuildStep("Synth", {
input: CodePipelineSource.connection("user/example4-be", "main", {
connectionArn: "arn:aws:codestar-connections:us-east-1:111...1111:connection/1111-1111.....1111",
}),
installCommands: [],
commands: []
}
),
})
这使得代码与它所在的存储库 (user/example4-be
) 以及用于访问它的 Github 连接 (arn:aws:codestar-connections:...
) 紧密耦合。如果有人分叉 repo 并想要一个并行管道,这将会中断。我觉得这两个值应该是配置而不是代码的一部分。
有没有办法使用 CDK 和 CodePipeline 将其作为外部变量?我想如果可能的话,变量应该是每个管道的?我不太确定。
Subclass Stack
并接受源配置输入作为自定义道具类型。1
// SourceConfigPipelineStack.ts
interface SourceConfigPipelineStackProps extends cdk.StackProps {
source: pipelines.CodePipelineSource;
}
export class SourceConfigPipelineStack extends cdk.Stack {
constructor(
scope: Construct,
id: string,
props: SourceConfigPipelineStackProps
) {
super(scope, id);
const pipeline = new pipelines.CodePipeline(this, id, {
pipelineName: id,
synth: new pipelines.CodeBuildStep('Synth', {
input: props.source,
installCommands: [],
commands: [],
}),
});
}
}
管道消费者然后将自己的源作为配置传递:
// app.ts
new SourceConfigPipelineStack(app, 'MyPipelineStack', {
env,
source: pipelines.CodePipelineSource.connection('user/example4-be', 'main', {
connectionArn:
'arn:aws:codestar-connections:us-east-1:111...1111:connection/1111-1111.....1111',
}),
});
编辑:将 ARN 配置放入代码中是否“不好”?
不是根据 AWS。 CDK“最佳实践”文档说它是 reasonable to hardcode cross-stack ARNs:
When the two stacks are in different AWS CDK apps, use a static from
method to import an externally-defined resource based on its ARN ... (for example, Table.fromArn()
for a DynamoDB table). Use the CfnOutput
construct to print the ARN or other required value in the output of cdk deploy
, or look in the AWS console. Or the second app can parse the CloudFormation template generated by the first app and retrieve that value from the Outputs
section.
代码中的硬编码 ARN 有时比 Parameter
、Secret
或 CfnOutput
.
等替代方案更差,有时更好
编辑:使用配置工厂处理 multi-environment 配置
所有应用程序都有 app-level 个配置项(例如 defaultInstanceSize
),这些项通常因环境而异。生产账户需要 full-powered 资源,开发账户不需要。考虑在配置工厂中封装 (non-secret) 配置。构造函数接收一个帐户和区域以及 returns 明文配置对象。堆栈接收配置作为道具。
// app.ts
const { env, isProd, retainOnDelete, enableDynamoCache, defaultInstanceSize, repoName, branchName, githubConnectionArn } =
// the config factory is using the account and region from the --profile flag
new EnvConfigurator('SuperApp', process.env.CDK_DEFAULT_ACCOUNT, process.env.CDK_DEFAULT_REGION).config;
new SourceConfigPipelineStack(app, 'MyPipelineStack', {
env,
source: pipelines.CodePipelineSource.connection(repoName, branchName, {
connectionArn: githubConnectionArn
}),
stackTerminationProtection: isProd,
});
本地配置模式有几个优点:
- 配置值很容易被发现并集中在一个地方
- 可以允许调用者提供type-constrained覆盖
- 轻松断言配置值
- 配置值受版本控制
- Pipeline-friendly:避免cross-account权限问题
本地配置可以与 Parameter
、CfnOutput
和 Secret
一起使用,它们具有互补的优势。应用程序通常会使用每一个。理智的人可以不同意究竟在哪里划定界限。
(1) 基本的 CDK 模式是 Construct composition:“组合是通过构造定义 higher-level 抽象的关键模式......一般来说,组合优于组合开发 AWS CDK 构造时继承。”在这种情况下,subclass Stack
而不是 Construct
base class 是有意义的,因为 OP用例是一个克隆的 repo,大概是部署阶段 non-optionally 封装在堆栈中。
如果您想将此信息保留在回购之外,您可以在单独的堆栈中创建 SSM 参数,部署它并填充参数,然后在管道中执行 synth-time 查找。
这是 python 中的样子:
class ParametersStack(cdk.Stack):
def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs):
super().__init__(scope, construct_id, **kwargs)
codestar_connection = csc.CfnConnection(
self, "my_connection", connection_name="my_connection", provider_type="GitHub"
)
ssm.StringParameter(
self,
"codestar_arn",
string_value=codestar_connection.ref,
parameter_name="/codestar/connection_arn",
)
ssm.StringParameter(
self,
"repo_owner",
string_value="REPO_OWNER",
parameter_name="/github/repo_owner",
)
ssm.StringParameter(
self,
"main_repo_name",
string_value="MAIN_REPO_NAME",
parameter_name="/github/repo_name",
)
然后您将部署此堆栈、设置连接并填充存储库所有者和名称参数。
管道堆栈中:
github_repo_owner = ssm.StringParameter.value_from_lookup(
self, "/github/repo_owner"
)
github_repo_name = ssm.StringParameter.value_from_lookup(
self, "/github/repo_name"
)
# The following is needed because during the first synth, the values will be # filled with dummy values that are incompatible, so just replace them with # dummy values that will synth
# See https://github.com/aws/aws-cdk/issues/8699
if "dummy" in github_repo_owner:
github_repo_owner = "dummy"
if "dummy" in github_repo_name:
github_repo_name = "dummy"
repo_string = f"{github_repo_owner}/{github_repo_name}"
codestar_connection_arn = ssm.StringParameter.value_from_lookup(
self, "/codestar/connection_arn"
)
source = pipelines.CodePipelineSource.connection(
repo_string=repo_string,
branch=branch_name,
connection_arn=codestar_connection_arn,
)
您还需要授予管道在合成期间执行查找的权利。您可以通过允许 synth 操作的角色承担查找角色来执行此操作
synth_step = pipelines.CodeBuildStep(
"synth",
install_commands=[
"npm install -g aws-cdk",
"pip install -r requirements.txt",
],
commands=[
"cdk synth",
],
input=source,
role_policy_statements=[
iam.PolicyStatement(
effect=iam.Effect.ALLOW,
actions=["sts:AssumeRole"],
resources=["*"],
conditions={
"StringEquals": {
"iam:ResourceTag/aws-cdk:bootstrap-role": "lookup"
}
},
),
],
)
查找到的值将保存在cdk.context.json
中。如果您不将其提交给您的 VCS,管道将进行查找并每次获取实际值。
我的 CDK 管道栈有这段代码:
const pipeline = new CodePipeline(this, id, {
pipelineName: id,
synth: new CodeBuildStep("Synth", {
input: CodePipelineSource.connection("user/example4-be", "main", {
connectionArn: "arn:aws:codestar-connections:us-east-1:111...1111:connection/1111-1111.....1111",
}),
installCommands: [],
commands: []
}
),
})
这使得代码与它所在的存储库 (user/example4-be
) 以及用于访问它的 Github 连接 (arn:aws:codestar-connections:...
) 紧密耦合。如果有人分叉 repo 并想要一个并行管道,这将会中断。我觉得这两个值应该是配置而不是代码的一部分。
有没有办法使用 CDK 和 CodePipeline 将其作为外部变量?我想如果可能的话,变量应该是每个管道的?我不太确定。
Subclass Stack
并接受源配置输入作为自定义道具类型。1
// SourceConfigPipelineStack.ts
interface SourceConfigPipelineStackProps extends cdk.StackProps {
source: pipelines.CodePipelineSource;
}
export class SourceConfigPipelineStack extends cdk.Stack {
constructor(
scope: Construct,
id: string,
props: SourceConfigPipelineStackProps
) {
super(scope, id);
const pipeline = new pipelines.CodePipeline(this, id, {
pipelineName: id,
synth: new pipelines.CodeBuildStep('Synth', {
input: props.source,
installCommands: [],
commands: [],
}),
});
}
}
管道消费者然后将自己的源作为配置传递:
// app.ts
new SourceConfigPipelineStack(app, 'MyPipelineStack', {
env,
source: pipelines.CodePipelineSource.connection('user/example4-be', 'main', {
connectionArn:
'arn:aws:codestar-connections:us-east-1:111...1111:connection/1111-1111.....1111',
}),
});
编辑:将 ARN 配置放入代码中是否“不好”?
不是根据 AWS。 CDK“最佳实践”文档说它是 reasonable to hardcode cross-stack ARNs:
When the two stacks are in different AWS CDK apps, use a static
from
method to import an externally-defined resource based on its ARN ... (for example,Table.fromArn()
for a DynamoDB table). Use theCfnOutput
construct to print the ARN or other required value in the output ofcdk deploy
, or look in the AWS console. Or the second app can parse the CloudFormation template generated by the first app and retrieve that value from theOutputs
section.
代码中的硬编码 ARN 有时比 Parameter
、Secret
或 CfnOutput
.
编辑:使用配置工厂处理 multi-environment 配置
所有应用程序都有 app-level 个配置项(例如 defaultInstanceSize
),这些项通常因环境而异。生产账户需要 full-powered 资源,开发账户不需要。考虑在配置工厂中封装 (non-secret) 配置。构造函数接收一个帐户和区域以及 returns 明文配置对象。堆栈接收配置作为道具。
// app.ts
const { env, isProd, retainOnDelete, enableDynamoCache, defaultInstanceSize, repoName, branchName, githubConnectionArn } =
// the config factory is using the account and region from the --profile flag
new EnvConfigurator('SuperApp', process.env.CDK_DEFAULT_ACCOUNT, process.env.CDK_DEFAULT_REGION).config;
new SourceConfigPipelineStack(app, 'MyPipelineStack', {
env,
source: pipelines.CodePipelineSource.connection(repoName, branchName, {
connectionArn: githubConnectionArn
}),
stackTerminationProtection: isProd,
});
本地配置模式有几个优点:
- 配置值很容易被发现并集中在一个地方
- 可以允许调用者提供type-constrained覆盖
- 轻松断言配置值
- 配置值受版本控制
- Pipeline-friendly:避免cross-account权限问题
本地配置可以与 Parameter
、CfnOutput
和 Secret
一起使用,它们具有互补的优势。应用程序通常会使用每一个。理智的人可以不同意究竟在哪里划定界限。
(1) 基本的 CDK 模式是 Construct composition:“组合是通过构造定义 higher-level 抽象的关键模式......一般来说,组合优于组合开发 AWS CDK 构造时继承。”在这种情况下,subclass Stack
而不是 Construct
base class 是有意义的,因为 OP用例是一个克隆的 repo,大概是部署阶段 non-optionally 封装在堆栈中。
如果您想将此信息保留在回购之外,您可以在单独的堆栈中创建 SSM 参数,部署它并填充参数,然后在管道中执行 synth-time 查找。
这是 python 中的样子:
class ParametersStack(cdk.Stack):
def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs):
super().__init__(scope, construct_id, **kwargs)
codestar_connection = csc.CfnConnection(
self, "my_connection", connection_name="my_connection", provider_type="GitHub"
)
ssm.StringParameter(
self,
"codestar_arn",
string_value=codestar_connection.ref,
parameter_name="/codestar/connection_arn",
)
ssm.StringParameter(
self,
"repo_owner",
string_value="REPO_OWNER",
parameter_name="/github/repo_owner",
)
ssm.StringParameter(
self,
"main_repo_name",
string_value="MAIN_REPO_NAME",
parameter_name="/github/repo_name",
)
然后您将部署此堆栈、设置连接并填充存储库所有者和名称参数。
管道堆栈中:
github_repo_owner = ssm.StringParameter.value_from_lookup(
self, "/github/repo_owner"
)
github_repo_name = ssm.StringParameter.value_from_lookup(
self, "/github/repo_name"
)
# The following is needed because during the first synth, the values will be # filled with dummy values that are incompatible, so just replace them with # dummy values that will synth
# See https://github.com/aws/aws-cdk/issues/8699
if "dummy" in github_repo_owner:
github_repo_owner = "dummy"
if "dummy" in github_repo_name:
github_repo_name = "dummy"
repo_string = f"{github_repo_owner}/{github_repo_name}"
codestar_connection_arn = ssm.StringParameter.value_from_lookup(
self, "/codestar/connection_arn"
)
source = pipelines.CodePipelineSource.connection(
repo_string=repo_string,
branch=branch_name,
connection_arn=codestar_connection_arn,
)
您还需要授予管道在合成期间执行查找的权利。您可以通过允许 synth 操作的角色承担查找角色来执行此操作
synth_step = pipelines.CodeBuildStep(
"synth",
install_commands=[
"npm install -g aws-cdk",
"pip install -r requirements.txt",
],
commands=[
"cdk synth",
],
input=source,
role_policy_statements=[
iam.PolicyStatement(
effect=iam.Effect.ALLOW,
actions=["sts:AssumeRole"],
resources=["*"],
conditions={
"StringEquals": {
"iam:ResourceTag/aws-cdk:bootstrap-role": "lookup"
}
},
),
],
)
查找到的值将保存在cdk.context.json
中。如果您不将其提交给您的 VCS,管道将进行查找并每次获取实际值。