From 551d225a34ce8d9a67716cfb718f1e5d1384fcd7 Mon Sep 17 00:00:00 2001 From: Michael Nguyen Date: Tue, 10 Sep 2024 15:45:21 -0400 Subject: [PATCH] aliyun: make image replication idempotent There is a history of failure copying images to other regions on aliyun. Upstream code that calls the CopyImage code more than once will get a DuplicateImage error when an image with the same name already exists. Let's check to see if the image name exists in the region before attempting to copy the image to the region and return early if it already exists. --- mantle/platform/api/aliyun/api.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/mantle/platform/api/aliyun/api.go b/mantle/platform/api/aliyun/api.go index 87bd15d7c8..bb5a9ad907 100644 --- a/mantle/platform/api/aliyun/api.go +++ b/mantle/platform/api/aliyun/api.go @@ -134,6 +134,20 @@ func (a *API) CopyImage(source_id, dest_name, dest_region, dest_description, kms Value: "mantle", }, } + + // Check if an image with the name has already been uploaded. This can + // happen when replication to a region fails which happens often on aliyun + images, err := a.GetImagesInRegion(dest_name, dest_region) + if err != nil { + return "", fmt.Errorf("getting image: %v", err) + } + + // return early if there is already an image with that tag + if len(images.Images.Image) > 0 { + plog.Infof("image with name %v in %v region already exists--skipping copy", dest_name, dest_region) + return images.Images.Image[0].ImageId, nil + } + response, err := a.ecs.CopyImage(request) if err != nil { return "", fmt.Errorf("copying image: %v", err) @@ -296,6 +310,16 @@ func (a *API) finishImportImageTask(importImageResponse *ecs.ImportImageResponse return importImageResponse.ImageId, nil } +// GetImagesInRegion retrieves a list of images by ImageName in a specified region +func (a *API) GetImagesInRegion(name string, region string) (*ecs.DescribeImagesResponse, error) { + request := ecs.CreateDescribeImagesRequest() + request.SetConnectTimeout(defaultConnectTimeout) + request.SetReadTimeout(defaultReadTimeout) + request.ImageName = name + request.RegionId = region + return a.ecs.DescribeImages(request) +} + // GetImages retrieves a list of images by ImageName func (a *API) GetImages(name string) (*ecs.DescribeImagesResponse, error) { request := ecs.CreateDescribeImagesRequest()