Skip to content

Commit 380774e

Browse files
authored
feat(aws-ecr): make it easy to reference image tag or digest, use everywhere (#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*
1 parent 1581af0 commit 380774e

File tree

11 files changed

+85
-25
lines changed

11 files changed

+85
-25
lines changed

packages/@aws-cdk/aws-apprunner/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ new apprunner.Service(this, 'Service', {
6565
source: apprunner.Source.fromEcr({
6666
imageConfiguration: { port: 80 },
6767
repository: ecr.Repository.fromRepositoryName(this, 'NginxRepository', 'nginx'),
68-
tag: 'latest',
68+
tagOrDigest: 'latest',
6969
}),
7070
});
7171
```

packages/@aws-cdk/aws-apprunner/lib/service.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,14 @@ export interface EcrProps {
219219
/**
220220
* Image tag.
221221
* @default - 'latest'
222+
* @deprecated use `tagOrDigest`
222223
*/
223224
readonly tag?: string;
225+
/**
226+
* Image tag or digest (digests must start with `sha256:`).
227+
* @default - 'latest'
228+
*/
229+
readonly tagOrDigest?: string;
224230
}
225231

226232
/**
@@ -313,7 +319,9 @@ export class EcrSource extends Source {
313319
return {
314320
imageRepository: {
315321
imageConfiguration: this.props.imageConfiguration,
316-
imageIdentifier: this.props.repository.repositoryUriForTag(this.props.tag || 'latest'),
322+
imageIdentifier: this.props.repository.repositoryUriForTagOrDigest(
323+
this.props.tagOrDigest || this.props.tag || 'latest',
324+
),
317325
imageRepositoryType: ImageRepositoryType.ECR,
318326
},
319327
ecrRepository: this.props.repository,

packages/@aws-cdk/aws-codebuild/lib/linux-arm-build-image.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ export class LinuxArmBuildImage implements IBuildImage {
4242
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-ecr.html
4343
*
4444
* @param repository The ECR repository
45-
* @param tag Image tag (default "latest")
45+
* @param tagOrDigest Image tag or digest (default "latest", digests must start with `sha256:`)
4646
* @returns An aarch64 Linux build image from an ECR repository.
4747
*/
48-
public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): IBuildImage {
48+
public static fromEcrRepository(repository: ecr.IRepository, tagOrDigest: string = 'latest'): IBuildImage {
4949
return new LinuxArmBuildImage({
50-
imageId: repository.repositoryUriForTag(tag),
50+
imageId: repository.repositoryUriForTagOrDigest(tagOrDigest),
5151
imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE,
5252
repository,
5353
});

packages/@aws-cdk/aws-codebuild/lib/project.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,11 +1790,11 @@ export class LinuxBuildImage implements IBuildImage {
17901790
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-ecr.html
17911791
*
17921792
* @param repository The ECR repository
1793-
* @param tag Image tag (default "latest")
1793+
* @param tagOrDigest Image tag or digest (default "latest", digests must start with `sha256:`)
17941794
*/
1795-
public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): IBuildImage {
1795+
public static fromEcrRepository(repository: ecr.IRepository, tagOrDigest: string = 'latest'): IBuildImage {
17961796
return new LinuxBuildImage({
1797-
imageId: repository.repositoryUriForTag(tag),
1797+
imageId: repository.repositoryUriForTagOrDigest(tagOrDigest),
17981798
imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE,
17991799
repository,
18001800
});
@@ -1951,15 +1951,15 @@ export class WindowsBuildImage implements IBuildImage {
19511951
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-ecr.html
19521952
*
19531953
* @param repository The ECR repository
1954-
* @param tag Image tag (default "latest")
1954+
* @param tagOrDigest Image tag or digest (default "latest", digests must start with `sha256:`)
19551955
*/
19561956
public static fromEcrRepository(
19571957
repository: ecr.IRepository,
1958-
tag: string = 'latest',
1958+
tagOrDigest: string = 'latest',
19591959
imageType: WindowsImageType = WindowsImageType.STANDARD): IBuildImage {
19601960

19611961
return new WindowsBuildImage({
1962-
imageId: repository.repositoryUriForTag(tag),
1962+
imageId: repository.repositoryUriForTagOrDigest(tagOrDigest),
19631963
imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE,
19641964
imageType,
19651965
repository,

packages/@aws-cdk/aws-ecr/lib/repository.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,24 @@ export interface IRepository extends IResource {
4242
repositoryUriForTag(tag?: string): string;
4343

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

53+
/**
54+
* 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`.
55+
*
56+
* ACCOUNT.dkr.ecr.REGION.amazonaws.com/REPOSITORY[:TAG]
57+
* ACCOUNT.dkr.ecr.REGION.amazonaws.com/REPOSITORY[@DIGEST]
58+
*
59+
* @param tagOrDigest Image tag or digest to use (tools usually default to the image with the "latest" tag if omitted)
60+
*/
61+
repositoryUriForTagOrDigest(tagOrDigest?: string): string;
62+
5363
/**
5464
* Add a policy statement to the repository's resource policy
5565
*/
@@ -162,6 +172,22 @@ export abstract class RepositoryBase extends Resource implements IRepository {
162172
return this.repositoryUriWithSuffix(digestSuffix);
163173
}
164174

175+
/**
176+
* Returns the URL of the repository. Can be used in `docker push/pull`.
177+
*
178+
* ACCOUNT.dkr.ecr.REGION.amazonaws.com/REPOSITORY[:TAG]
179+
* ACCOUNT.dkr.ecr.REGION.amazonaws.com/REPOSITORY[@DIGEST]
180+
*
181+
* @param tagOrDigest Optional image tag or digest (digests must start with `sha256:`)
182+
*/
183+
public repositoryUriForTagOrDigest(tagOrDigest?: string): string {
184+
if (tagOrDigest?.startsWith('sha256:')) {
185+
return this.repositoryUriForDigest(tagOrDigest);
186+
} else {
187+
return this.repositoryUriForTag(tagOrDigest);
188+
}
189+
}
190+
165191
/**
166192
* Returns the repository URI, with an appended suffix, if provided.
167193
* @param suffix An image tag or an image digest.

packages/@aws-cdk/aws-ecs/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,8 @@ obtained from either DockerHub or from ECR repositories, built directly from a l
393393

394394
- `ecs.ContainerImage.fromRegistry(imageName)`: use a public image.
395395
- `ecs.ContainerImage.fromRegistry(imageName, { credentials: mySecret })`: use a private image that requires credentials.
396-
- `ecs.ContainerImage.fromEcrRepository(repo, tag)`: use the given ECR repository as the image
397-
to start. If no tag is provided, "latest" is assumed.
396+
- `ecs.ContainerImage.fromEcrRepository(repo, tagOrDigest)`: use the given ECR repository as the image
397+
to start. If no tag or digest is provided, "latest" is assumed.
398398
- `ecs.ContainerImage.fromAsset('./image')`: build and upload an
399399
image directly from a `Dockerfile` in your source directory.
400400
- `ecs.ContainerImage.fromDockerImageAsset(asset)`: uses an existing

packages/@aws-cdk/aws-ecs/lib/images/ecr.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@ export class EcrImage extends ContainerImage {
2525
constructor(private readonly repository: ecr.IRepository, private readonly tagOrDigest: string) {
2626
super();
2727

28-
if (tagOrDigest?.startsWith('sha256:')) {
29-
this.imageName = this.repository.repositoryUriForDigest(this.tagOrDigest);
30-
} else {
31-
this.imageName = this.repository.repositoryUriForTag(this.tagOrDigest);
32-
}
28+
this.imageName = this.repository.repositoryUriForTagOrDigest(this.tagOrDigest);
3329
}
3430

3531
public bind(_scope: CoreConstruct, containerDefinition: ContainerDefinition): ContainerImageConfig {

packages/@aws-cdk/aws-lambda/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ new lambda.DockerImageFunction(this, 'ECRFunction', {
7979
```
8080

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

8484
## Execution Role
8585

packages/@aws-cdk/aws-lambda/lib/code.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,8 +454,15 @@ export interface EcrImageCodeProps {
454454
/**
455455
* The image tag to use when pulling the image from ECR.
456456
* @default 'latest'
457+
* @deprecated use `tagOrDigest`
457458
*/
458459
readonly tag?: string;
460+
461+
/**
462+
* The image tag or digest to use when pulling the image from ECR (digests must start with `sha256:`).
463+
* @default 'latest'
464+
*/
465+
readonly tagOrDigest?: string;
459466
}
460467

461468
/**
@@ -473,7 +480,7 @@ export class EcrImageCode extends Code {
473480

474481
return {
475482
image: {
476-
imageUri: this.repository.repositoryUriForTag(this.props?.tag ?? 'latest'),
483+
imageUri: this.repository.repositoryUriForTagOrDigest(this.props?.tagOrDigest ?? this.props?.tag ?? 'latest'),
477484
cmd: this.props.cmd,
478485
entrypoint: this.props.entrypoint,
479486
workingDirectory: this.props.workingDirectory,

packages/@aws-cdk/aws-lambda/test/code.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ describe('code', () => {
225225
code: lambda.Code.fromEcrImage(repo, {
226226
cmd: ['cmd', 'param1'],
227227
entrypoint: ['entrypoint', 'param2'],
228-
tag: 'mytag',
228+
tagOrDigest: 'mytag',
229229
workingDirectory: '/some/path',
230230
}),
231231
handler: lambda.Handler.FROM_IMAGE,
@@ -245,6 +245,29 @@ describe('code', () => {
245245
});
246246
});
247247

248+
test('digests are interpreted correctly', () => {
249+
// given
250+
const stack = new cdk.Stack();
251+
const repo = new ecr.Repository(stack, 'Repo');
252+
253+
// when
254+
new lambda.Function(stack, 'Fn', {
255+
code: lambda.Code.fromEcrImage(repo, {
256+
tagOrDigest: 'sha256:afc607424cc02c92d4d6af5184a4fef46a69548e465a320808c6ff358b6a3a8d',
257+
}),
258+
handler: lambda.Handler.FROM_IMAGE,
259+
runtime: lambda.Runtime.FROM_IMAGE,
260+
});
261+
262+
// then
263+
Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', {
264+
Code: {
265+
ImageUri: stack.resolve(repo.repositoryUriForDigest('sha256:afc607424cc02c92d4d6af5184a4fef46a69548e465a320808c6ff358b6a3a8d')),
266+
},
267+
ImageConfig: Match.absent(),
268+
});
269+
});
270+
248271
test('permission grants', () => {
249272
// given
250273
const stack = new cdk.Stack();

packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker/base-types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,10 +353,10 @@ export abstract class DockerImage {
353353
* Reference a Docker image stored in an ECR repository.
354354
*
355355
* @param repository the ECR repository where the image is hosted.
356-
* @param tag an optional `tag`
356+
* @param tagOrDigest an optional tag or digest (digests must start with `sha256:`)
357357
*/
358-
public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): DockerImage {
359-
return new StandardDockerImage({ repository, imageUri: repository.repositoryUriForTag(tag) });
358+
public static fromEcrRepository(repository: ecr.IRepository, tagOrDigest: string = 'latest'): DockerImage {
359+
return new StandardDockerImage({ repository, imageUri: repository.repositoryUriForTagOrDigest(tagOrDigest) });
360360
}
361361

362362
/**

0 commit comments

Comments
 (0)