in src/ec2-deployer.ts [99:194]
constructor(scope: cdk.Construct, id: string, props: Ec2DeployerProps) {
// Validate that props.deploymentTimeout is less than 2 hours, per maximum value accepted by downstream customresources.Provider.totalTimeout
if (props.deploymentTimeout && props.deploymentTimeout.toMilliseconds() > Ec2Deployer.MAX_DEPLOYMENT_TIMEOUT.toMilliseconds()) { // have to convert to milliseconds in case the cdk.Duration is passed in milliseconds
throw new Error(`Invalid prop: deploymentTimeout must be less than ${Ec2Deployer.MAX_DEPLOYMENT_TIMEOUT.toHumanString()}.`);
}
// Validate that at least one instanceRole is supplied if we cannot get them from deploymentGroup.autoScalingGroups
if (!props.deploymentGroup.autoScalingGroups && (!props.instanceRoles || props.instanceRoles.length === 0)) {
throw new Error('If deploymentGroup is of type IServerDeploymentGroup, you must supply at least one role in instanceRoles.');
}
super(scope, id);
// Set defaults for any missing props
this.code = props.code.bind(this);
this.deploymentGroup = props.deploymentGroup;
this.waitToComplete = props.waitToComplete !== undefined ? props.waitToComplete : true;
this.deploymentTimeout = this.waitToComplete ? props.deploymentTimeout || cdk.Duration.minutes(5) : undefined; // can only be defined if waitToComplete=true because of downstream customresources.Provider.totalTimeout
// Create OnEventHandler Lambda function for custom resource
// Can't use SingletonFunction because permissions are dependent on props passed into each Ec2Deployer instance
const onEvent = new lambda.Function(this, 'OnEventHandler', {
// const onEvent = new lambda.SingletonFunction(this, 'OnEventHandler', {
// uuid: '3a9c56a9-1dd5-42dc-af2f-10b76edde830',
code: lambda.Code.fromAsset(path.join(__dirname, '../custom-resource-runtime/ec2-deployer')),
runtime: lambda.Runtime.PYTHON_3_8,
handler: 'index.on_event',
initialPolicy: [
new iam.PolicyStatement({
actions: ['codedeploy:GetDeploymentConfig'],
resources: [codedeploy.ServerDeploymentConfig.ONE_AT_A_TIME.deploymentConfigArn],
}),
new iam.PolicyStatement({
actions: ['codedeploy:CreateDeployment'],
resources: [this.deploymentGroup.deploymentGroupArn],
}),
new iam.PolicyStatement({
actions: ['codedeploy:GetApplicationRevision', 'codedeploy:RegisterApplicationRevision'],
resources: [this.deploymentGroup.application.applicationArn],
}),
],
});
// Create IsCompleteHandler Lambda function for custom resource, only if waitToComplete=true
// Can't use SingletonFunction because permissions are dependent on props passed into each Ec2Deployer instance
let isComplete = undefined;
if (this.waitToComplete) {
// isComplete = new lambda.SingletonFunction(this, 'IsCompleteHandler', {
// uuid: 'f58e4e2e-8b7e-4bd0-b33b-c5c9f19f5546',
isComplete = new lambda.Function(this, 'IsCompleteHandler', {
code: lambda.Code.fromAsset(path.join(__dirname, '../custom-resource-runtime/ec2-deployer')),
runtime: lambda.Runtime.PYTHON_3_8,
handler: 'index.is_complete',
initialPolicy: [
new iam.PolicyStatement({
resources: [this.deploymentGroup.deploymentGroupArn],
actions: ['codedeploy:GetDeployment'],
}),
],
});
}
// Create provider for custom resource
const deployerProvider = new customresources.Provider(this, 'Provider', {
onEventHandler: onEvent,
totalTimeout: this.deploymentTimeout,
isCompleteHandler: isComplete,
});
// Ensure ASGs have read access to code S3 object for deployment
const policyStatement = new iam.PolicyStatement({
actions: ['s3:GetObject*'],
resources: [`arn:${cdk.Stack.of(this).partition}:s3:::${this.code.s3Location.bucketName}/${this.code.s3Location.objectKey}`],
});
if (props.instanceRoles) {
for (let role of props.instanceRoles) {
role.addToPrincipalPolicy(policyStatement);
}
} else {
for (let asg of this.deploymentGroup.autoScalingGroups!) {
(asg as autoscaling.AutoScalingGroup).role.addToPrincipalPolicy(policyStatement);
}
}
// Create custom resource that triggers a deployment
new cdk.CustomResource(this, 'CustomResource', {
serviceToken: deployerProvider.serviceToken,
properties: {
applicationName: this.deploymentGroup.application.applicationName,
deploymentGroupName: this.deploymentGroup.deploymentGroupName,
codeS3BucketName: this.code.s3Location.bucketName,
codeS3ObjectKey: this.code.s3Location.objectKey,
codeS3ObjectVersion: this.code.s3Location.objectVersion,
},
});
}