From 687d64be8537e99564a319950632aa60d0704873 Mon Sep 17 00:00:00 2001 From: Dhruv Patel Date: Fri, 15 Mar 2024 16:48:27 -0400 Subject: [PATCH] feat: support different architectures when copying images --- API.md | 1 + lambda/main.go | 13 ++++++-- lambda/main_test.go | 4 +-- lambda/utils.go | 54 +++++++++++++++++++++++++++++----- src/index.ts | 6 ++++ test/example.ecr-deployment.ts | 2 ++ 6 files changed, 68 insertions(+), 12 deletions(-) diff --git a/API.md b/API.md index b5d1470e..33331197 100644 --- a/API.md +++ b/API.md @@ -75,6 +75,7 @@ new ECRDeployment(scope: Construct, id: string, props: ECRDeploymentProps) * **props** ([ECRDeploymentProps](#cdk-ecr-deployment-ecrdeploymentprops)) *No description* * **dest** ([IImageName](#cdk-ecr-deployment-iimagename)) The destination of the docker image. * **src** ([IImageName](#cdk-ecr-deployment-iimagename)) The source of the docker image. + * **architecture** (string) The architecture of the images to copy. Necessary if the image architecture differs from the lambda architecture. * **buildImage** (string) Image to use to build Golang lambda for custom resource, if download fails or is not wanted. __*Default*__: public.ecr.aws/sam/build-go1.x:latest * **environment** (Map) The environment variable to set. __*Optional*__ * **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. __*Default*__: 512 diff --git a/lambda/main.go b/lambda/main.go index 639d52c6..223cafce 100644 --- a/lambda/main.go +++ b/lambda/main.go @@ -53,6 +53,13 @@ func handler(ctx context.Context, event cfn.Event) (physicalResourceID string, d if err != nil { return physicalResourceID, data, err } + arch, err := getStrProps(event.ResourceProperties, ARCHITECTURE) + if !IsValidGOARCH(arch) { + return physicalResourceID, data, fmt.Errorf("invalid GOARCH %s", arch) + } + if err != nil { + return physicalResourceID, data, err + } srcCreds, err := getStrPropsDefault(event.ResourceProperties, SRC_CREDS, "") if err != nil { return physicalResourceID, data, err @@ -71,7 +78,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 Architecture: %v", srcImage, destImage, arch) srcRef, err := alltransports.ParseImageName(srcImage) if err != nil { @@ -82,13 +89,13 @@ func handler(ctx context.Context, event cfn.Event) (physicalResourceID string, d return physicalResourceID, data, err } - srcOpts := NewImageOpts(srcImage) + srcOpts := NewImageOpts(srcImage, arch) srcOpts.SetCreds(srcCreds) srcCtx, err := srcOpts.NewSystemContext() if err != nil { return physicalResourceID, data, err } - destOpts := NewImageOpts(destImage) + destOpts := NewImageOpts(destImage, arch) destOpts.SetCreds(destCreds) destCtx, err := destOpts.NewSystemContext() if err != nil { diff --git a/lambda/main_test.go b/lambda/main_test.go index a28b323f..036f4bc6 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, "arm64") srcCtx, err := srcOpts.NewSystemContext() assert.NoError(t, err) - destOpts := NewImageOpts(destImage) + destOpts := NewImageOpts(destImage, "arm64") destCtx, err := destOpts.NewSystemContext() assert.NoError(t, err) diff --git a/lambda/utils.go b/lambda/utils.go index 461c3807..6cc51f27 100644 --- a/lambda/utils.go +++ b/lambda/utils.go @@ -21,10 +21,11 @@ import ( ) const ( - SRC_IMAGE string = "SrcImage" - DEST_IMAGE string = "DestImage" - SRC_CREDS string = "SrcCreds" - DEST_CREDS string = "DestCreds" + SRC_IMAGE string = "SrcImage" + DEST_IMAGE string = "DestImage" + SRC_CREDS string = "SrcCreds" + DEST_CREDS string = "DestCreds" + ARCHITECTURE string = "Architecture" ) type ECRAuth struct { @@ -35,6 +36,33 @@ type ECRAuth struct { ExpiresAt time.Time } +var validArchs = []string{ + "386", + "amd64", + "amd64p32", + "arm", + "arm64", + "arm64be", + "armbe", + "loong64", + "mips", + "mips64", + "mips64le", + "mips64p32", + "mips64p32le", + "mipsle", + "ppc", + "ppc64", + "ppc64le", + "riscv", + "riscv64", + "s390", + "s390x", + "sparc", + "sparc64", + "wasm", +} + func GetECRRegion(uri string) string { re := regexp.MustCompile(`dkr\.ecr\.(.+?)\.`) m := re.FindStringSubmatch(uri) @@ -86,14 +114,15 @@ type ImageOpts struct { requireECRLogin bool region string creds string + architecture 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 +138,7 @@ func (s *ImageOpts) NewSystemContext() (*types.SystemContext, error) { ctx := &types.SystemContext{ DockerRegistryUserAgent: "ecr-deployment", DockerAuthConfig: &types.DockerAuthConfig{}, + ArchitectureChoice: s.architecture, } if s.creds != "" { @@ -184,3 +214,13 @@ func GetSecret(secretId string) (secret string, err error) { } return *resp.SecretString, nil } + +func IsValidGOARCH(arch string) bool { + for _, validArch := range validArchs { + if arch == validArch { + return true + } + } + + return false +} diff --git a/src/index.ts b/src/index.ts index 388ed888..1942aec6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,6 +31,11 @@ export interface ECRDeploymentProps { */ readonly dest: IImageName; + /** + * The architecture of the docker image. + */ + readonly architecture?: 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. @@ -192,6 +197,7 @@ export class ECRDeployment extends Construct { SrcCreds: props.src.creds, DestImage: props.dest.uri, DestCreds: props.dest.creds, + Architecture: props.architecture, }, }); } diff --git a/test/example.ecr-deployment.ts b/test/example.ecr-deployment.ts index 2d5bd7c8..d7ea87aa 100644 --- a/test/example.ecr-deployment.ts +++ b/test/example.ecr-deployment.ts @@ -32,11 +32,13 @@ class TestECRDeployment extends Stack { new ecrDeploy.ECRDeployment(this, 'DeployECRImage', { src: new ecrDeploy.DockerImageName(image.imageUri), dest: new ecrDeploy.DockerImageName(`${repo.repositoryUri}:latest`), + architecture: 'arm64', }); new ecrDeploy.ECRDeployment(this, 'DeployDockerImage', { src: new ecrDeploy.DockerImageName('javacs3/javacs3:latest', 'dockerhub'), dest: new ecrDeploy.DockerImageName(`${repo.repositoryUri}:dockerhub`), + architecture: 'arm64', }).addToPrincipalPolicy(new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: [