Skip to content

Commit

Permalink
fix(ecr): Allow referencing an EcrImage by digest instead of tag (#13299
Browse files Browse the repository at this point in the history
)

Fixes #5082


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
otaviomacedo authored Feb 26, 2021
1 parent f511639 commit 266a621
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 3 deletions.
32 changes: 31 additions & 1 deletion packages/@aws-cdk/aws-ecr/lib/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ export interface IRepository extends IResource {
*/
repositoryUriForTag(tag?: string): string;

/**
* Returns the URI of the repository for a certain tag. Can be used in `docker push/pull`.
*
* ACCOUNT.dkr.ecr.REGION.amazonaws.com/REPOSITORY[@DIGEST]
*
* @param digest Image digest to use (tools usually default to the image with the "latest" tag if omitted)
*/
repositoryUriForDigest(digest?: string): string;

/**
* Add a policy statement to the repository's resource policy
*/
Expand Down Expand Up @@ -136,8 +145,29 @@ export abstract class RepositoryBase extends Resource implements IRepository {
*/
public repositoryUriForTag(tag?: string): string {
const tagSuffix = tag ? `:${tag}` : '';
return this.repositoryUriWithSuffix(tagSuffix);
}

/**
* Returns the URL of the repository. Can be used in `docker push/pull`.
*
* ACCOUNT.dkr.ecr.REGION.amazonaws.com/REPOSITORY[@DIGEST]
*
* @param digest Optional image digest
*/
public repositoryUriForDigest(digest?: string): string {
const digestSuffix = digest ? `@${digest}` : '';
return this.repositoryUriWithSuffix(digestSuffix);
}

/**
* Returns the repository URI, with an appended suffix, if provided.
* @param suffix An image tag or an image digest.
* @private
*/
private repositoryUriWithSuffix(suffix?: string): string {
const parts = this.stack.parseArn(this.repositoryArn);
return `${parts.account}.dkr.ecr.${parts.region}.${this.stack.urlSuffix}/${this.repositoryName}${tagSuffix}`;
return `${parts.account}.dkr.ecr.${parts.region}.${this.stack.urlSuffix}/${this.repositoryName}${suffix}`;
}

/**
Expand Down
8 changes: 6 additions & 2 deletions packages/@aws-cdk/aws-ecs/lib/images/ecr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ export class EcrImage extends ContainerImage {
/**
* Constructs a new instance of the EcrImage class.
*/
constructor(private readonly repository: ecr.IRepository, private readonly tag: string) {
constructor(private readonly repository: ecr.IRepository, private readonly tagOrDigest: string) {
super();

this.imageName = this.repository.repositoryUriForTag(this.tag);
if (tagOrDigest?.startsWith('sha256:')) {
this.imageName = this.repository.repositoryUriForDigest(this.tagOrDigest);
} else {
this.imageName = this.repository.repositoryUriForTag(this.tagOrDigest);
}
}

public bind(_scope: CoreConstruct, containerDefinition: ContainerDefinition): ContainerImageConfig {
Expand Down
140 changes: 140 additions & 0 deletions packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,146 @@ describe('ec2 task definition', () => {

});

test('correctly sets containers from ECR repository using an image tag', () => {
// GIVEN
const stack = new cdk.Stack();

const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef');

taskDefinition.addContainer('web', {
image: ecs.ContainerImage.fromEcrRepository(new Repository(stack, 'myECRImage'), 'myTag'),
memoryLimitMiB: 512,
});

// THEN
expect(stack).toHaveResource('AWS::ECS::TaskDefinition', {
ContainerDefinitions: [{
Essential: true,
Memory: 512,
Image: {
'Fn::Join': [
'',
[
{
'Fn::Select': [
4,
{
'Fn::Split': [
':',
{
'Fn::GetAtt': [
'myECRImage7DEAE474',
'Arn',
],
},
],
},
],
},
'.dkr.ecr.',
{
'Fn::Select': [
3,
{
'Fn::Split': [
':',
{
'Fn::GetAtt': [
'myECRImage7DEAE474',
'Arn',
],
},
],
},
],
},
'.',
{
Ref: 'AWS::URLSuffix',
},
'/',
{
Ref: 'myECRImage7DEAE474',
},
':myTag',
],
],
},
Name: 'web',
}],
});
});

test('correctly sets containers from ECR repository using an image digest', () => {
// GIVEN
const stack = new cdk.Stack();

const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef');

taskDefinition.addContainer('web', {
image: ecs.ContainerImage.fromEcrRepository(new Repository(stack, 'myECRImage'), 'sha256:94afd1f2e64d908bc90dbca0035a5b567EXAMPLE'),
memoryLimitMiB: 512,
});

// THEN
expect(stack).toHaveResource('AWS::ECS::TaskDefinition', {
ContainerDefinitions: [{
Essential: true,
Memory: 512,
Image: {
'Fn::Join': [
'',
[
{
'Fn::Select': [
4,
{
'Fn::Split': [
':',
{
'Fn::GetAtt': [
'myECRImage7DEAE474',
'Arn',
],
},
],
},
],
},
'.dkr.ecr.',
{
'Fn::Select': [
3,
{
'Fn::Split': [
':',
{
'Fn::GetAtt': [
'myECRImage7DEAE474',
'Arn',
],
},
],
},
],
},
'.',
{
Ref: 'AWS::URLSuffix',
},
'/',
{
Ref: 'myECRImage7DEAE474',
},
'@sha256:94afd1f2e64d908bc90dbca0035a5b567EXAMPLE',
],
],
},
Name: 'web',
}],
});
});

test('correctly sets containers from ECR repository using default props', () => {
// GIVEN
const stack = new cdk.Stack();
Expand Down

0 comments on commit 266a621

Please sign in to comment.