Skip to content

Commit

Permalink
feat(aws-ecr): make it easy to reference image tag or digest, use eve…
Browse files Browse the repository at this point in the history
…rywhere (#19799)

This generalises the fix of #13299 by creating a `IRepository.repositoryUriForTagOrDigest` function that detects whether something looks like a digest (starts with `sha256:`) or is a tag, and formats the URI with `@` or `:` as appropriate. This function is then used in most places that previously called `repositoryUriForTag`, meaning they can use image digests in addition to tags. The one remain real call is in aws-ecs's `TagParameterContainerImage`.

This includes aws-lambda's `EcrImageCode`, and thus closes #15333.

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)?
	* [ ] Did you use `cdk-integ` to deploy the infrastructure and generate the snapshot (i.e. `cdk-integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
huonw committed Apr 13, 2022
1 parent 1581af0 commit 380774e
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 25 deletions.
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-apprunner/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ new apprunner.Service(this, 'Service', {
source: apprunner.Source.fromEcr({
imageConfiguration: { port: 80 },
repository: ecr.Repository.fromRepositoryName(this, 'NginxRepository', 'nginx'),
tag: 'latest',
tagOrDigest: 'latest',
}),
});
```
Expand Down
10 changes: 9 additions & 1 deletion packages/@aws-cdk/aws-apprunner/lib/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,14 @@ export interface EcrProps {
/**
* Image tag.
* @default - 'latest'
* @deprecated use `tagOrDigest`
*/
readonly tag?: string;
/**
* Image tag or digest (digests must start with `sha256:`).
* @default - 'latest'
*/
readonly tagOrDigest?: string;
}

/**
Expand Down Expand Up @@ -313,7 +319,9 @@ export class EcrSource extends Source {
return {
imageRepository: {
imageConfiguration: this.props.imageConfiguration,
imageIdentifier: this.props.repository.repositoryUriForTag(this.props.tag || 'latest'),
imageIdentifier: this.props.repository.repositoryUriForTagOrDigest(
this.props.tagOrDigest || this.props.tag || 'latest',
),
imageRepositoryType: ImageRepositoryType.ECR,
},
ecrRepository: this.props.repository,
Expand Down
6 changes: 3 additions & 3 deletions packages/@aws-cdk/aws-codebuild/lib/linux-arm-build-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ export class LinuxArmBuildImage implements IBuildImage {
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-ecr.html
*
* @param repository The ECR repository
* @param tag Image tag (default "latest")
* @param tagOrDigest Image tag or digest (default "latest", digests must start with `sha256:`)
* @returns An aarch64 Linux build image from an ECR repository.
*/
public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): IBuildImage {
public static fromEcrRepository(repository: ecr.IRepository, tagOrDigest: string = 'latest'): IBuildImage {
return new LinuxArmBuildImage({
imageId: repository.repositoryUriForTag(tag),
imageId: repository.repositoryUriForTagOrDigest(tagOrDigest),
imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE,
repository,
});
Expand Down
12 changes: 6 additions & 6 deletions packages/@aws-cdk/aws-codebuild/lib/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1790,11 +1790,11 @@ export class LinuxBuildImage implements IBuildImage {
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-ecr.html
*
* @param repository The ECR repository
* @param tag Image tag (default "latest")
* @param tagOrDigest Image tag or digest (default "latest", digests must start with `sha256:`)
*/
public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): IBuildImage {
public static fromEcrRepository(repository: ecr.IRepository, tagOrDigest: string = 'latest'): IBuildImage {
return new LinuxBuildImage({
imageId: repository.repositoryUriForTag(tag),
imageId: repository.repositoryUriForTagOrDigest(tagOrDigest),
imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE,
repository,
});
Expand Down Expand Up @@ -1951,15 +1951,15 @@ export class WindowsBuildImage implements IBuildImage {
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-ecr.html
*
* @param repository The ECR repository
* @param tag Image tag (default "latest")
* @param tagOrDigest Image tag or digest (default "latest", digests must start with `sha256:`)
*/
public static fromEcrRepository(
repository: ecr.IRepository,
tag: string = 'latest',
tagOrDigest: string = 'latest',
imageType: WindowsImageType = WindowsImageType.STANDARD): IBuildImage {

return new WindowsBuildImage({
imageId: repository.repositoryUriForTag(tag),
imageId: repository.repositoryUriForTagOrDigest(tagOrDigest),
imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE,
imageType,
repository,
Expand Down
28 changes: 27 additions & 1 deletion packages/@aws-cdk/aws-ecr/lib/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,24 @@ 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`.
* Returns the URI of the repository for a certain digest. 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;

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

/**
* Add a policy statement to the repository's resource policy
*/
Expand Down Expand Up @@ -162,6 +172,22 @@ export abstract class RepositoryBase extends Resource implements IRepository {
return this.repositoryUriWithSuffix(digestSuffix);
}

/**
* Returns the URL of the repository. Can be used in `docker push/pull`.
*
* ACCOUNT.dkr.ecr.REGION.amazonaws.com/REPOSITORY[:TAG]
* ACCOUNT.dkr.ecr.REGION.amazonaws.com/REPOSITORY[@DIGEST]
*
* @param tagOrDigest Optional image tag or digest (digests must start with `sha256:`)
*/
public repositoryUriForTagOrDigest(tagOrDigest?: string): string {
if (tagOrDigest?.startsWith('sha256:')) {
return this.repositoryUriForDigest(tagOrDigest);
} else {
return this.repositoryUriForTag(tagOrDigest);
}
}

/**
* Returns the repository URI, with an appended suffix, if provided.
* @param suffix An image tag or an image digest.
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,8 @@ obtained from either DockerHub or from ECR repositories, built directly from a l

- `ecs.ContainerImage.fromRegistry(imageName)`: use a public image.
- `ecs.ContainerImage.fromRegistry(imageName, { credentials: mySecret })`: use a private image that requires credentials.
- `ecs.ContainerImage.fromEcrRepository(repo, tag)`: use the given ECR repository as the image
to start. If no tag is provided, "latest" is assumed.
- `ecs.ContainerImage.fromEcrRepository(repo, tagOrDigest)`: use the given ECR repository as the image
to start. If no tag or digest is provided, "latest" is assumed.
- `ecs.ContainerImage.fromAsset('./image')`: build and upload an
image directly from a `Dockerfile` in your source directory.
- `ecs.ContainerImage.fromDockerImageAsset(asset)`: uses an existing
Expand Down
6 changes: 1 addition & 5 deletions packages/@aws-cdk/aws-ecs/lib/images/ecr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ export class EcrImage extends ContainerImage {
constructor(private readonly repository: ecr.IRepository, private readonly tagOrDigest: string) {
super();

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

public bind(_scope: CoreConstruct, containerDefinition: ContainerDefinition): ContainerImageConfig {
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-lambda/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ new lambda.DockerImageFunction(this, 'ECRFunction', {
```

The props for these docker image resources allow overriding the image's `CMD`, `ENTRYPOINT`, and `WORKDIR`
configurations. See their docs for more information.
configurations as well as choosing a specific tag or digest. See their docs for more information.

## Execution Role

Expand Down
9 changes: 8 additions & 1 deletion packages/@aws-cdk/aws-lambda/lib/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,15 @@ export interface EcrImageCodeProps {
/**
* The image tag to use when pulling the image from ECR.
* @default 'latest'
* @deprecated use `tagOrDigest`
*/
readonly tag?: string;

/**
* The image tag or digest to use when pulling the image from ECR (digests must start with `sha256:`).
* @default 'latest'
*/
readonly tagOrDigest?: string;
}

/**
Expand All @@ -473,7 +480,7 @@ export class EcrImageCode extends Code {

return {
image: {
imageUri: this.repository.repositoryUriForTag(this.props?.tag ?? 'latest'),
imageUri: this.repository.repositoryUriForTagOrDigest(this.props?.tagOrDigest ?? this.props?.tag ?? 'latest'),
cmd: this.props.cmd,
entrypoint: this.props.entrypoint,
workingDirectory: this.props.workingDirectory,
Expand Down
25 changes: 24 additions & 1 deletion packages/@aws-cdk/aws-lambda/test/code.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ describe('code', () => {
code: lambda.Code.fromEcrImage(repo, {
cmd: ['cmd', 'param1'],
entrypoint: ['entrypoint', 'param2'],
tag: 'mytag',
tagOrDigest: 'mytag',
workingDirectory: '/some/path',
}),
handler: lambda.Handler.FROM_IMAGE,
Expand All @@ -245,6 +245,29 @@ describe('code', () => {
});
});

test('digests are interpreted correctly', () => {
// given
const stack = new cdk.Stack();
const repo = new ecr.Repository(stack, 'Repo');

// when
new lambda.Function(stack, 'Fn', {
code: lambda.Code.fromEcrImage(repo, {
tagOrDigest: 'sha256:afc607424cc02c92d4d6af5184a4fef46a69548e465a320808c6ff358b6a3a8d',
}),
handler: lambda.Handler.FROM_IMAGE,
runtime: lambda.Runtime.FROM_IMAGE,
});

// then
Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', {
Code: {
ImageUri: stack.resolve(repo.repositoryUriForDigest('sha256:afc607424cc02c92d4d6af5184a4fef46a69548e465a320808c6ff358b6a3a8d')),
},
ImageConfig: Match.absent(),
});
});

test('permission grants', () => {
// given
const stack = new cdk.Stack();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,10 +353,10 @@ export abstract class DockerImage {
* Reference a Docker image stored in an ECR repository.
*
* @param repository the ECR repository where the image is hosted.
* @param tag an optional `tag`
* @param tagOrDigest an optional tag or digest (digests must start with `sha256:`)
*/
public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): DockerImage {
return new StandardDockerImage({ repository, imageUri: repository.repositoryUriForTag(tag) });
public static fromEcrRepository(repository: ecr.IRepository, tagOrDigest: string = 'latest'): DockerImage {
return new StandardDockerImage({ repository, imageUri: repository.repositoryUriForTagOrDigest(tagOrDigest) });
}

/**
Expand Down

0 comments on commit 380774e

Please sign in to comment.