diff --git a/API.md b/API.md index 397cdbd0..abb565b9 100644 --- a/API.md +++ b/API.md @@ -148,6 +148,7 @@ const eCRDeploymentProps: ECRDeploymentProps = { ... } | src | IImageName | The source of the docker image. | | buildImage | string | Image to use to build Golang lambda for custom resource, if download fails or is not wanted. | | environment | {[ key: string ]: string} | The environment variable to set. | +| imageArch | string | The image architecture to be copied. | | lambdaHandler | string | The name of the lambda handler. | | lambdaRuntime | aws-cdk-lib.aws_lambda.Runtime | The lambda function runtime environment. | | memoryLimit | number | The amount of memory (in MiB) to allocate to the AWS Lambda function which replicates the files from the CDK bucket to the destination bucket. | @@ -211,6 +212,19 @@ The environment variable to set. --- +##### `imageArch`Optional + +```typescript +public readonly imageArch: string; +``` + +- *Type:* string +- *Default:* runtime.GOARCH if it's not explicitly specified, according to https://github.com/containers/image/blob/main/internal/pkg/platform/platform_matcher.go#L161 + +The image architecture to be copied. + +--- + ##### `lambdaHandler`Optional ```typescript diff --git a/lambda/main.go b/lambda/main.go index 639d52c6..803ff736 100644 --- a/lambda/main.go +++ b/lambda/main.go @@ -53,6 +53,10 @@ func handler(ctx context.Context, event cfn.Event) (physicalResourceID string, d if err != nil { return physicalResourceID, data, err } + imageArch, err := getStrPropsDefault(event.ResourceProperties, IMAGE_ARCH, "") + if err != nil { + return physicalResourceID, data, err + } srcCreds, err := getStrPropsDefault(event.ResourceProperties, SRC_CREDS, "") if err != nil { return physicalResourceID, data, err @@ -71,7 +75,7 @@ func handler(ctx context.Context, event cfn.Event) (physicalResourceID string, d return physicalResourceID, data, err } - log.Printf("SrcImage: %v DestImage: %v", srcImage, destImage) + log.Printf("SrcImage: %v DestImage: %v ImageArch: %v", srcImage, destImage, imageArch) srcRef, err := alltransports.ParseImageName(srcImage) if err != nil { @@ -82,13 +86,13 @@ func handler(ctx context.Context, event cfn.Event) (physicalResourceID string, d return physicalResourceID, data, err } - srcOpts := NewImageOpts(srcImage) + srcOpts := NewImageOpts(srcImage, imageArch) srcOpts.SetCreds(srcCreds) srcCtx, err := srcOpts.NewSystemContext() if err != nil { return physicalResourceID, data, err } - destOpts := NewImageOpts(destImage) + destOpts := NewImageOpts(destImage, imageArch) destOpts.SetCreds(destCreds) destCtx, err := destOpts.NewSystemContext() if err != nil { diff --git a/lambda/main_test.go b/lambda/main_test.go index a28b323f..46dd0345 100644 --- a/lambda/main_test.go +++ b/lambda/main_test.go @@ -33,10 +33,10 @@ func TestMain(t *testing.T) { destRef, err := alltransports.ParseImageName(destImage) assert.NoError(t, err) - srcOpts := NewImageOpts(srcImage) + srcOpts := NewImageOpts(srcImage, "") srcCtx, err := srcOpts.NewSystemContext() assert.NoError(t, err) - destOpts := NewImageOpts(destImage) + destOpts := NewImageOpts(destImage, "") destCtx, err := destOpts.NewSystemContext() assert.NoError(t, err) @@ -53,3 +53,12 @@ func TestMain(t *testing.T) { }) assert.NoError(t, err) } + +func TestNewImageOpts(t *testing.T) { + srcOpts := NewImageOpts("s3://cdk-ecr-deployment/nginx.tar:nginx:latest", "arm64") + _, err := srcOpts.NewSystemContext() + assert.NoError(t, err) + destOpts := NewImageOpts("dir:/tmp/nginx.dir", "arm64") + _, err = destOpts.NewSystemContext() + assert.NoError(t, err) +} diff --git a/lambda/utils.go b/lambda/utils.go index 461c3807..9d0311d8 100644 --- a/lambda/utils.go +++ b/lambda/utils.go @@ -23,6 +23,7 @@ import ( const ( SRC_IMAGE string = "SrcImage" DEST_IMAGE string = "DestImage" + IMAGE_ARCH string = "ImageArch" SRC_CREDS string = "SrcCreds" DEST_CREDS string = "DestCreds" ) @@ -86,14 +87,15 @@ type ImageOpts struct { requireECRLogin bool region string creds string + arch string } -func NewImageOpts(uri string) *ImageOpts { +func NewImageOpts(uri string, arch string) *ImageOpts { requireECRLogin := strings.Contains(uri, "dkr.ecr") if requireECRLogin { - return &ImageOpts{uri, requireECRLogin, GetECRRegion(uri), ""} + return &ImageOpts{uri, requireECRLogin, GetECRRegion(uri), "", arch} } else { - return &ImageOpts{uri, requireECRLogin, "", ""} + return &ImageOpts{uri, requireECRLogin, "", "", arch} } } @@ -109,6 +111,7 @@ func (s *ImageOpts) NewSystemContext() (*types.SystemContext, error) { ctx := &types.SystemContext{ DockerRegistryUserAgent: "ecr-deployment", DockerAuthConfig: &types.DockerAuthConfig{}, + ArchitectureChoice: s.arch, } if s.creds != "" { diff --git a/src/index.ts b/src/index.ts index 9ab65116..cb801f66 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,14 @@ export interface ECRDeploymentProps { */ readonly dest: IImageName; + /** + * The image architecture to be copied. + * + * @default - the underlying lambda auto-detects the relevant architecture (e.g., amd64, arm64) + * https://github.com/containers/image/blob/main/internal/pkg/platform/platform_matcher.go#L161 + */ + readonly imageArch?: string; + /** * The amount of memory (in MiB) to allocate to the AWS Lambda function which * replicates the files from the CDK bucket to the destination bucket. @@ -207,6 +215,7 @@ export class ECRDeployment extends Construct { SrcCreds: props.src.creds, DestImage: props.dest.uri, DestCreds: props.dest.creds, + ImageArch: props.imageArch ?? '', }, }); } diff --git a/test/example.ecr-deployment.ts b/test/example.ecr-deployment.ts index 2d5bd7c8..60cd229d 100644 --- a/test/example.ecr-deployment.ts +++ b/test/example.ecr-deployment.ts @@ -34,9 +34,27 @@ class TestECRDeployment extends Stack { dest: new ecrDeploy.DockerImageName(`${repo.repositoryUri}:latest`), }); + new ecrDeploy.ECRDeployment(this, 'DeployECRImage', { + src: new ecrDeploy.DockerImageName(image.imageUri), + dest: new ecrDeploy.DockerImageName(`${repo.repositoryUri}:latest`), + imageArch: 'arm64', + }); + + new ecrDeploy.ECRDeployment(this, 'DeployDockerImage', { + src: new ecrDeploy.DockerImageName('javacs3/javacs3:latest', 'dockerhub'), + dest: new ecrDeploy.DockerImageName(`${repo.repositoryUri}:dockerhub`), + }).addToPrincipalPolicy(new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: [ + 'secretsmanager:GetSecretValue', + ], + resources: ['*'], + })); + new ecrDeploy.ECRDeployment(this, 'DeployDockerImage', { src: new ecrDeploy.DockerImageName('javacs3/javacs3:latest', 'dockerhub'), dest: new ecrDeploy.DockerImageName(`${repo.repositoryUri}:dockerhub`), + imageArch: 'amd64', }).addToPrincipalPolicy(new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: [