-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(codepipeline): add ECS deploy Action. (#2050)
Fixes #1386
- Loading branch information
Showing
7 changed files
with
1,155 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
packages/@aws-cdk/aws-codepipeline-actions/lib/ecs/deploy-action.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import codepipeline = require('@aws-cdk/aws-codepipeline'); | ||
import ecs = require('@aws-cdk/aws-ecs'); | ||
import iam = require('@aws-cdk/aws-iam'); | ||
|
||
/** | ||
* Construction properties of {@link EcsDeployAction}. | ||
*/ | ||
export interface EcsDeployActionProps extends codepipeline.CommonActionProps { | ||
/** | ||
* The input artifact that contains the JSON image definitions file to use for deployments. | ||
* The JSON file is a list of objects, | ||
* each with 2 keys: `name` is the name of the container in the Task Definition, | ||
* and `imageUri` is the Docker image URI you want to update your service with. | ||
* If you use this property, it's assumed the file is called 'imagedefinitions.json'. | ||
* If your build uses a different file, leave this property empty, | ||
* and use the `imageFile` property instead. | ||
* | ||
* @default - one of this property, or `imageFile`, is required | ||
* @see https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-create.html#pipelines-create-image-definitions | ||
*/ | ||
readonly inputArtifact?: codepipeline.Artifact; | ||
|
||
/** | ||
* The name of the JSON image definitions file to use for deployments. | ||
* The JSON file is a list of objects, | ||
* each with 2 keys: `name` is the name of the container in the Task Definition, | ||
* and `imageUri` is the Docker image URI you want to update your service with. | ||
* Use this property if you want to use a different name for this file than the default 'imagedefinitions.json'. | ||
* If you use this property, you don't need to specify the `inputArtifact` property. | ||
* | ||
* @default - one of this property, or `inputArtifact`, is required | ||
* @see https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-create.html#pipelines-create-image-definitions | ||
*/ | ||
readonly imageFile?: codepipeline.ArtifactPath; | ||
|
||
/** | ||
* The ECS Service to deploy. | ||
*/ | ||
readonly service: ecs.BaseService; | ||
} | ||
|
||
/** | ||
* CodePipeline Action to deploy an ECS Service. | ||
*/ | ||
export class EcsDeployAction extends codepipeline.DeployAction { | ||
constructor(props: EcsDeployActionProps) { | ||
super({ | ||
...props, | ||
inputArtifact: determineInputArtifact(props), | ||
provider: 'ECS', | ||
artifactBounds: { | ||
minInputs: 1, | ||
maxInputs: 1, | ||
minOutputs: 0, | ||
maxOutputs: 0, | ||
}, | ||
configuration: { | ||
ClusterName: props.service.clusterName, | ||
ServiceName: props.service.serviceName, | ||
FileName: props.imageFile && props.imageFile.fileName, | ||
}, | ||
}); | ||
} | ||
|
||
protected bind(info: codepipeline.ActionBind): void { | ||
// permissions based on CodePipeline documentation: | ||
// https://docs.aws.amazon.com/codepipeline/latest/userguide/how-to-custom-role.html#how-to-update-role-new-services | ||
info.role.addToPolicy(new iam.PolicyStatement() | ||
.addActions( | ||
'ecs:DescribeServices', | ||
'ecs:DescribeTaskDefinition', | ||
'ecs:DescribeTasks', | ||
'ecs:ListTasks', | ||
'ecs:RegisterTaskDefinition', | ||
'ecs:UpdateService', | ||
) | ||
.addAllResources()); | ||
|
||
info.role.addToPolicy(new iam.PolicyStatement() | ||
.addActions( | ||
'iam:PassRole', | ||
) | ||
.addAllResources() | ||
.addCondition('StringEqualsIfExists', { | ||
'iam:PassedToService': [ | ||
'ec2.amazonaws.com', | ||
'ecs-tasks.amazonaws.com', | ||
], | ||
})); | ||
} | ||
} | ||
|
||
function determineInputArtifact(props: EcsDeployActionProps): codepipeline.Artifact { | ||
if (props.imageFile && props.inputArtifact) { | ||
throw new Error("Exactly one of 'inputArtifact' or 'imageFile' can be provided in the ECS deploy Action"); | ||
} | ||
if (props.imageFile) { | ||
return props.imageFile.artifact; | ||
} | ||
if (props.inputArtifact) { | ||
return props.inputArtifact; | ||
} | ||
throw new Error("Specifying one of 'inputArtifact' or 'imageFile' is required for the ECS deploy Action"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
packages/@aws-cdk/aws-codepipeline-actions/test/ecs/test.ecs-deploy-action.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import codepipeline = require('@aws-cdk/aws-codepipeline'); | ||
import ec2 = require('@aws-cdk/aws-ec2'); | ||
import ecs = require('@aws-cdk/aws-ecs'); | ||
import cdk = require('@aws-cdk/cdk'); | ||
import { Test } from 'nodeunit'; | ||
import cpactions = require('../../lib'); | ||
|
||
export = { | ||
'ECS deploy Action': { | ||
'throws an exception if neither inputArtifact nor imageFile were provided'(test: Test) { | ||
const service = anyEcsService(); | ||
|
||
test.throws(() => { | ||
new cpactions.EcsDeployAction({ | ||
actionName: 'ECS', | ||
service, | ||
}); | ||
}, /one of 'inputArtifact' or 'imageFile' is required/); | ||
|
||
test.done(); | ||
}, | ||
|
||
'can be created just by specifying the inputArtifact'(test: Test) { | ||
const service = anyEcsService(); | ||
const artifact = new codepipeline.Artifact('Artifact'); | ||
|
||
const action = new cpactions.EcsDeployAction({ | ||
actionName: 'ECS', | ||
service, | ||
inputArtifact: artifact, | ||
}); | ||
|
||
test.equal(action.configuration.FileName, undefined); | ||
|
||
test.done(); | ||
}, | ||
|
||
'can be created just by specifying the imageFile'(test: Test) { | ||
const service = anyEcsService(); | ||
const artifact = new codepipeline.Artifact('Artifact'); | ||
|
||
const action = new cpactions.EcsDeployAction({ | ||
actionName: 'ECS', | ||
service, | ||
imageFile: artifact.atPath('imageFile.json'), | ||
}); | ||
|
||
test.equal(action.configuration.FileName, 'imageFile.json'); | ||
|
||
test.done(); | ||
}, | ||
|
||
'throws an exception if both inputArtifact and imageFile were provided'(test: Test) { | ||
const service = anyEcsService(); | ||
const artifact = new codepipeline.Artifact('Artifact'); | ||
|
||
test.throws(() => { | ||
new cpactions.EcsDeployAction({ | ||
actionName: 'ECS', | ||
service, | ||
inputArtifact: artifact, | ||
imageFile: artifact.atPath('file.json'), | ||
}); | ||
}, /one of 'inputArtifact' or 'imageFile' can be provided/); | ||
|
||
test.done(); | ||
}, | ||
}, | ||
}; | ||
|
||
function anyEcsService(): ecs.FargateService { | ||
const stack = new cdk.Stack(); | ||
const taskDefinition = new ecs.FargateTaskDefinition(stack, 'TaskDefinition'); | ||
taskDefinition.addContainer('MainContainer', { | ||
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), | ||
}); | ||
const vpc = new ec2.VpcNetwork(stack, 'VPC'); | ||
const cluster = new ecs.Cluster(stack, 'Cluster', { | ||
vpc, | ||
}); | ||
return new ecs.FargateService(stack, 'FargateService', { | ||
cluster, | ||
taskDefinition, | ||
}); | ||
} |
Oops, something went wrong.