From 58db4a9e10fad209e88c221f93e93fbe8b0c73dd 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()