diff --git a/builder/alicloud/ecs/access_config.go b/builder/alicloud/ecs/access_config.go index 8bb06393f..e0ace80ea 100644 --- a/builder/alicloud/ecs/access_config.go +++ b/builder/alicloud/ecs/access_config.go @@ -2,11 +2,12 @@ package ecs import ( "fmt" + "os" + "time" + "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/version" - "os" - "time" ) // Config of alicloud diff --git a/builder/alicloud/ecs/artifact.go b/builder/alicloud/ecs/artifact.go index 67ee3a72c..4be42ea5b 100644 --- a/builder/alicloud/ecs/artifact.go +++ b/builder/alicloud/ecs/artifact.go @@ -82,7 +82,7 @@ func (a *Artifact) Destroy() error { continue } - if images[0].IsCopied { + if images[0].IsCopied && images[0].Status != ImageStatusAvailable { copyingImages[regionId] = imageId } else { sourceImage[regionId] = &images[0] diff --git a/builder/alicloud/ecs/builder_acc_test.go b/builder/alicloud/ecs/builder_acc_test.go index a0c22dff0..8d93db640 100644 --- a/builder/alicloud/ecs/builder_acc_test.go +++ b/builder/alicloud/ecs/builder_acc_test.go @@ -3,12 +3,13 @@ package ecs import ( "encoding/json" "fmt" - "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" - builderT "github.com/hashicorp/packer/helper/builder/testing" - "github.com/hashicorp/packer/packer" "os" "strings" "testing" + + "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" + builderT "github.com/hashicorp/packer/helper/builder/testing" + "github.com/hashicorp/packer/packer" ) const defaultTestRegion = "cn-beijing" @@ -609,6 +610,200 @@ func checkImageTags() builderT.TestCheckFunc { } } +func TestBuilderAcc_dataDiskEncrypted(t *testing.T) { + t.Parallel() + builderT.Test(t, builderT.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Builder: &Builder{}, + Template: testBuilderAccDataDiskEncrypted, + Check: checkDataDiskEncrypted(), + }) +} + +const testBuilderAccDataDiskEncrypted = ` +{ "builders": [{ + "type": "test", + "region": "cn-beijing", + "instance_type": "ecs.n1.tiny", + "source_image":"ubuntu_18_04_64_20G_alibase_20190223.vhd", + "io_optimized":"true", + "ssh_username":"root", + "image_name": "packer-test-dataDiskEncrypted_{{timestamp}}", + "image_disk_mappings": [ + { + "disk_name": "data_disk1", + "disk_size": 25, + "disk_encrypted": true, + "disk_delete_with_instance": true + }, + { + "disk_name": "data_disk2", + "disk_size": 35, + "disk_encrypted": false, + "disk_delete_with_instance": true + }, + { + "disk_name": "data_disk3", + "disk_size": 45, + "disk_delete_with_instance": true + } + ] + }] +}` + +func checkDataDiskEncrypted() builderT.TestCheckFunc { + return func(artifacts []packer.Artifact) error { + if len(artifacts) > 1 { + return fmt.Errorf("more than 1 artifact") + } + + // Get the actual *Artifact pointer so we can access the AMIs directly + artifactRaw := artifacts[0] + artifact, ok := artifactRaw.(*Artifact) + if !ok { + return fmt.Errorf("unknown artifact: %#v", artifactRaw) + } + imageId := artifact.AlicloudImages[defaultTestRegion] + + // describe the image, get block devices with a snapshot + client, _ := testAliyunClient() + + describeImagesRequest := ecs.CreateDescribeImagesRequest() + describeImagesRequest.RegionId = defaultTestRegion + describeImagesRequest.ImageId = imageId + imagesResponse, err := client.DescribeImages(describeImagesRequest) + if err != nil { + return fmt.Errorf("describe images failed due to %s", err) + } + + if len(imagesResponse.Images.Image) == 0 { + return fmt.Errorf("image %s generated can not be found", imageId) + } + image := imagesResponse.Images.Image[0] + + var snapshotIds []string + for _, mapping := range image.DiskDeviceMappings.DiskDeviceMapping { + snapshotIds = append(snapshotIds, mapping.SnapshotId) + } + + data, _ := json.Marshal(snapshotIds) + + describeSnapshotRequest := ecs.CreateDescribeSnapshotsRequest() + describeSnapshotRequest.RegionId = defaultTestRegion + describeSnapshotRequest.SnapshotIds = string(data) + describeSnapshotsResponse, err := client.DescribeSnapshots(describeSnapshotRequest) + if err != nil { + return fmt.Errorf("describe data snapshots failed due to %s", err) + } + if len(describeSnapshotsResponse.Snapshots.Snapshot) != 4 { + return fmt.Errorf("expect %d data snapshots but got %d", len(snapshotIds), len(describeSnapshotsResponse.Snapshots.Snapshot)) + } + snapshots := describeSnapshotsResponse.Snapshots.Snapshot + for _, snapshot := range snapshots { + if snapshot.SourceDiskType == DiskTypeSystem { + if snapshot.Encrypted != false { + return fmt.Errorf("the system snapshot expected to be non-encrypted but got true") + } + + continue + } + + if snapshot.SourceDiskSize == "25" && snapshot.Encrypted != true { + return fmt.Errorf("the first snapshot expected to be encrypted but got false") + } + + if snapshot.SourceDiskSize == "35" && snapshot.Encrypted != false { + return fmt.Errorf("the second snapshot expected to be non-encrypted but got true") + } + + if snapshot.SourceDiskSize == "45" && snapshot.Encrypted != false { + return fmt.Errorf("the third snapshot expected to be non-encrypted but got true") + } + } + return nil + } +} + +func TestBuilderAcc_systemDiskEncrypted(t *testing.T) { + t.Parallel() + builderT.Test(t, builderT.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Builder: &Builder{}, + Template: testBuilderAccSystemDiskEncrypted, + Check: checkSystemDiskEncrypted(), + }) +} + +const testBuilderAccSystemDiskEncrypted = ` +{ + "builders": [{ + "type": "test", + "region": "cn-beijing", + "instance_type": "ecs.n1.tiny", + "source_image":"ubuntu_18_04_64_20G_alibase_20190223.vhd", + "io_optimized":"true", + "ssh_username":"root", + "image_name": "packer-test_{{timestamp}}", + "image_encrypted": "true" + }] +}` + +func checkSystemDiskEncrypted() builderT.TestCheckFunc { + return func(artifacts []packer.Artifact) error { + if len(artifacts) > 1 { + return fmt.Errorf("more than 1 artifact") + } + + // Get the actual *Artifact pointer so we can access the AMIs directly + artifactRaw := artifacts[0] + artifact, ok := artifactRaw.(*Artifact) + if !ok { + return fmt.Errorf("unknown artifact: %#v", artifactRaw) + } + + // describe the image, get block devices with a snapshot + client, _ := testAliyunClient() + imageId := artifact.AlicloudImages[defaultTestRegion] + + describeImagesRequest := ecs.CreateDescribeImagesRequest() + describeImagesRequest.RegionId = defaultTestRegion + describeImagesRequest.ImageId = imageId + describeImagesRequest.Status = ImageStatusQueried + imagesResponse, err := client.DescribeImages(describeImagesRequest) + if err != nil { + return fmt.Errorf("describe images failed due to %s", err) + } + + if len(imagesResponse.Images.Image) == 0 { + return fmt.Errorf("image %s generated can not be found", imageId) + } + + image := imagesResponse.Images.Image[0] + if image.IsCopied == false { + return fmt.Errorf("image %s generated expexted to be copied but false", image.ImageId) + } + + describeSnapshotRequest := ecs.CreateDescribeSnapshotsRequest() + describeSnapshotRequest.RegionId = defaultTestRegion + describeSnapshotRequest.SnapshotIds = fmt.Sprintf("[\"%s\"]", image.DiskDeviceMappings.DiskDeviceMapping[0].SnapshotId) + describeSnapshotsResponse, err := client.DescribeSnapshots(describeSnapshotRequest) + if err != nil { + return fmt.Errorf("describe system snapshots failed due to %s", err) + } + snapshots := describeSnapshotsResponse.Snapshots.Snapshot[0] + + if snapshots.Encrypted != true { + return fmt.Errorf("system snapshot of image %s expected to be encrypted but got false", imageId) + } + + return nil + } +} + func testAccPreCheck(t *testing.T) { if v := os.Getenv("ALICLOUD_ACCESS_KEY"); v == "" { t.Fatal("ALICLOUD_ACCESS_KEY must be set for acceptance tests") diff --git a/builder/alicloud/ecs/client.go b/builder/alicloud/ecs/client.go index ef9613757..5a8d436a9 100644 --- a/builder/alicloud/ecs/client.go +++ b/builder/alicloud/ecs/client.go @@ -2,10 +2,11 @@ package ecs import ( "fmt" + "time" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" - "time" ) type ClientWrapper struct { diff --git a/builder/alicloud/ecs/client_test.go b/builder/alicloud/ecs/client_test.go index 9ea8f25c2..16bd3dbaf 100644 --- a/builder/alicloud/ecs/client_test.go +++ b/builder/alicloud/ecs/client_test.go @@ -2,9 +2,10 @@ package ecs import ( "fmt" - "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" "testing" "time" + + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" ) func TestWaitForExpectedExceedRetryTimes(t *testing.T) { diff --git a/builder/alicloud/ecs/image_config.go b/builder/alicloud/ecs/image_config.go index 06cc43d38..80193ff56 100644 --- a/builder/alicloud/ecs/image_config.go +++ b/builder/alicloud/ecs/image_config.go @@ -16,6 +16,7 @@ type AlicloudDiskDevice struct { Description string `mapstructure:"disk_description"` DeleteWithInstance bool `mapstructure:"disk_delete_with_instance"` Device string `mapstructure:"disk_device"` + Encrypted *bool `mapstructure:"disk_encrypted"` } type AlicloudDiskDevices struct { @@ -31,6 +32,7 @@ type AlicloudImageConfig struct { AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account"` AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions"` AlicloudImageDestinationNames []string `mapstructure:"image_copy_names"` + ImageEncrypted *bool `mapstructure:"image_encrypted"` AlicloudImageForceDelete bool `mapstructure:"image_force_delete"` AlicloudImageForceDeleteSnapshots bool `mapstructure:"image_force_delete_snapshots"` AlicloudImageForceDeleteInstances bool `mapstructure:"image_force_delete_instances"` diff --git a/builder/alicloud/ecs/step_attach_keypair.go b/builder/alicloud/ecs/step_attach_keypair.go index 6ded785fd..d2a2a9c02 100644 --- a/builder/alicloud/ecs/step_attach_keypair.go +++ b/builder/alicloud/ecs/step_attach_keypair.go @@ -3,6 +3,7 @@ package ecs import ( "context" "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/helper/multistep" diff --git a/builder/alicloud/ecs/step_check_source_image.go b/builder/alicloud/ecs/step_check_source_image.go index 5e89c4d32..073d84b12 100644 --- a/builder/alicloud/ecs/step_check_source_image.go +++ b/builder/alicloud/ecs/step_check_source_image.go @@ -3,6 +3,7 @@ package ecs import ( "context" "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/alicloud/ecs/step_config_eip.go b/builder/alicloud/ecs/step_config_eip.go index 5efe1273c..468e1ecab 100644 --- a/builder/alicloud/ecs/step_config_eip.go +++ b/builder/alicloud/ecs/step_config_eip.go @@ -3,6 +3,7 @@ package ecs import ( "context" "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" "github.com/hashicorp/packer/common/uuid" diff --git a/builder/alicloud/ecs/step_config_security_group.go b/builder/alicloud/ecs/step_config_security_group.go index 29abe8087..b147f0a30 100644 --- a/builder/alicloud/ecs/step_config_security_group.go +++ b/builder/alicloud/ecs/step_config_security_group.go @@ -3,6 +3,7 @@ package ecs import ( "context" "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/common/uuid" diff --git a/builder/alicloud/ecs/step_config_vpc.go b/builder/alicloud/ecs/step_config_vpc.go index f8fc26a19..3b8f9574b 100644 --- a/builder/alicloud/ecs/step_config_vpc.go +++ b/builder/alicloud/ecs/step_config_vpc.go @@ -4,6 +4,7 @@ import ( "context" errorsNew "errors" "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/common/uuid" diff --git a/builder/alicloud/ecs/step_config_vswitch.go b/builder/alicloud/ecs/step_config_vswitch.go index d14759d5f..4f2541b89 100644 --- a/builder/alicloud/ecs/step_config_vswitch.go +++ b/builder/alicloud/ecs/step_config_vswitch.go @@ -3,6 +3,7 @@ package ecs import ( "context" "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/common/uuid" diff --git a/builder/alicloud/ecs/step_create_image.go b/builder/alicloud/ecs/step_create_image.go index 4c41bb6f1..6b460d35b 100644 --- a/builder/alicloud/ecs/step_create_image.go +++ b/builder/alicloud/ecs/step_create_image.go @@ -3,12 +3,15 @@ package ecs import ( "context" "fmt" + "time" + + "github.com/hashicorp/packer/common/random" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "time" ) type stepCreateAlicloudImage struct { @@ -26,10 +29,15 @@ func (s *stepCreateAlicloudImage) Run(ctx context.Context, state multistep.State client := state.Get("client").(*ClientWrapper) ui := state.Get("ui").(packer.Ui) - // Create the alicloud image - ui.Say(fmt.Sprintf("Creating image: %s", config.AlicloudImageName)) + tempImageName := config.AlicloudImageName + if config.ImageEncrypted != nil && *config.ImageEncrypted { + tempImageName = fmt.Sprintf("packer_%s", random.AlphaNum(7)) + ui.Say(fmt.Sprintf("Creating temporary image for encryption: %s", tempImageName)) + } else { + ui.Say(fmt.Sprintf("Creating image: %s", tempImageName)) + } - createImageRequest := s.buildCreateImageRequest(state) + createImageRequest := s.buildCreateImageRequest(state, tempImageName) createImageResponse, err := client.WaitForExpected(&WaitForExpectArgs{ RequestFunc: func() (responses.AcsResponse, error) { return client.CreateImage(createImageRequest) @@ -43,20 +51,12 @@ func (s *stepCreateAlicloudImage) Run(ctx context.Context, state multistep.State imageId := createImageResponse.(*ecs.CreateImageResponse).ImageId - _, err = client.WaitForImageStatus(config.AlicloudRegion, imageId, ImageStatusAvailable, time.Duration(s.WaitSnapshotReadyTimeout)*time.Second) + imagesResponse, err := client.WaitForImageStatus(config.AlicloudRegion, imageId, ImageStatusAvailable, time.Duration(s.WaitSnapshotReadyTimeout)*time.Second) if err != nil { return halt(state, err, "Timeout waiting for image to be created") } - describeImagesRequest := ecs.CreateDescribeImagesRequest() - describeImagesRequest.ImageId = imageId - describeImagesRequest.RegionId = config.AlicloudRegion - imagesResponse, err := client.DescribeImages(describeImagesRequest) - if err != nil { - return halt(state, err, "") - } - - images := imagesResponse.Images.Image + images := imagesResponse.(*ecs.DescribeImagesResponse).Images.Image if len(images) == 0 { return halt(state, err, "Unable to find created image") } @@ -83,17 +83,24 @@ func (s *stepCreateAlicloudImage) Cleanup(state multistep.StateBag) { return } + config := state.Get("config").(*Config) + encryptedSet := config.ImageEncrypted != nil && *config.ImageEncrypted + _, cancelled := state.GetOk(multistep.StateCancelled) _, halted := state.GetOk(multistep.StateHalted) - if !cancelled && !halted { + + if !cancelled && !halted && !encryptedSet { return } client := state.Get("client").(*ClientWrapper) ui := state.Get("ui").(packer.Ui) - config := state.Get("config").(*Config) - ui.Say("Deleting the image because of cancellation or error...") + if !cancelled && !halted && encryptedSet { + ui.Say(fmt.Sprintf("Deleting temporary image %s(%s) and related snapshots after finishing encryption...", s.image.ImageId, s.image.ImageName)) + } else { + ui.Say("Deleting the image and related snapshots because of cancellation or error...") + } deleteImageRequest := ecs.CreateDeleteImageRequest() deleteImageRequest.RegionId = config.AlicloudRegion @@ -102,15 +109,25 @@ func (s *stepCreateAlicloudImage) Cleanup(state multistep.StateBag) { ui.Error(fmt.Sprintf("Error deleting image, it may still be around: %s", err)) return } + + //Delete the snapshot of this image + for _, diskDevices := range s.image.DiskDeviceMappings.DiskDeviceMapping { + deleteSnapshotRequest := ecs.CreateDeleteSnapshotRequest() + deleteSnapshotRequest.SnapshotId = diskDevices.SnapshotId + if _, err := client.DeleteSnapshot(deleteSnapshotRequest); err != nil { + ui.Error(fmt.Sprintf("Error deleting snapshot, it may still be around: %s", err)) + return + } + } } -func (s *stepCreateAlicloudImage) buildCreateImageRequest(state multistep.StateBag) *ecs.CreateImageRequest { +func (s *stepCreateAlicloudImage) buildCreateImageRequest(state multistep.StateBag, imageName string) *ecs.CreateImageRequest { config := state.Get("config").(*Config) request := ecs.CreateCreateImageRequest() request.ClientToken = uuid.TimeOrderedUUID() request.RegionId = config.AlicloudRegion - request.ImageName = config.AlicloudImageName + request.ImageName = imageName request.ImageVersion = config.AlicloudImageVersion request.Description = config.AlicloudImageDescription diff --git a/builder/alicloud/ecs/step_create_instance.go b/builder/alicloud/ecs/step_create_instance.go index 2260922ba..512ea5c98 100644 --- a/builder/alicloud/ecs/step_create_instance.go +++ b/builder/alicloud/ecs/step_create_instance.go @@ -172,6 +172,9 @@ func (s *stepCreateAlicloudInstance) buildCreateInstanceRequest(state multistep. dataDisk.Description = imageDisk.Description dataDisk.DeleteWithInstance = strconv.FormatBool(imageDisk.DeleteWithInstance) dataDisk.Device = imageDisk.Device + if imageDisk.Encrypted != nil { + dataDisk.Encrypted = strconv.FormatBool(*imageDisk.Encrypted) + } dataDisks = append(dataDisks, dataDisk) } diff --git a/builder/alicloud/ecs/step_create_snapshot.go b/builder/alicloud/ecs/step_create_snapshot.go index f99ea0d39..339fbef83 100644 --- a/builder/alicloud/ecs/step_create_snapshot.go +++ b/builder/alicloud/ecs/step_create_snapshot.go @@ -3,11 +3,12 @@ package ecs import ( "context" "fmt" + "time" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "time" ) type stepCreateAlicloudSnapshot struct { diff --git a/builder/alicloud/ecs/step_region_copy_image.go b/builder/alicloud/ecs/step_region_copy_image.go index 34319ee00..8fb30551b 100644 --- a/builder/alicloud/ecs/step_region_copy_image.go +++ b/builder/alicloud/ecs/step_region_copy_image.go @@ -3,7 +3,9 @@ package ecs import ( "context" "fmt" + "time" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -16,17 +18,27 @@ type stepRegionCopyAlicloudImage struct { } func (s *stepRegionCopyAlicloudImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + config := state.Get("config").(*Config) + + if config.ImageEncrypted != nil { + s.AlicloudImageDestinationRegions = append(s.AlicloudImageDestinationRegions, s.RegionId) + s.AlicloudImageDestinationNames = append(s.AlicloudImageDestinationNames, config.AlicloudImageName) + } + if len(s.AlicloudImageDestinationRegions) == 0 { return multistep.ActionContinue } client := state.Get("client").(*ClientWrapper) - imageId := state.Get("alicloudimage").(string) - alicloudImages := state.Get("alicloudimages").(map[string]string) + ui := state.Get("ui").(packer.Ui) + srcImageId := state.Get("alicloudimage").(string) + alicloudImages := state.Get("alicloudimages").(map[string]string) numberOfName := len(s.AlicloudImageDestinationNames) + + ui.Say(fmt.Sprintf("Coping image %s from %s...", srcImageId, s.RegionId)) for index, destinationRegion := range s.AlicloudImageDestinationRegions { - if destinationRegion == s.RegionId { + if destinationRegion == s.RegionId && config.ImageEncrypted == nil { continue } @@ -37,39 +49,57 @@ func (s *stepRegionCopyAlicloudImage) Run(ctx context.Context, state multistep.S copyImageRequest := ecs.CreateCopyImageRequest() copyImageRequest.RegionId = s.RegionId - copyImageRequest.ImageId = imageId + copyImageRequest.ImageId = srcImageId copyImageRequest.DestinationRegionId = destinationRegion copyImageRequest.DestinationImageName = ecsImageName + if config.ImageEncrypted != nil { + copyImageRequest.Encrypted = requests.NewBoolean(*config.ImageEncrypted) + } - image, err := client.CopyImage(copyImageRequest) + imageResponse, err := client.CopyImage(copyImageRequest) if err != nil { return halt(state, err, "Error copying images") } - alicloudImages[destinationRegion] = image.ImageId + alicloudImages[destinationRegion] = imageResponse.ImageId + ui.Message(fmt.Sprintf("Copy image from %s(%s) to %s(%s)", s.RegionId, srcImageId, destinationRegion, imageResponse.ImageId)) } + + if config.ImageEncrypted != nil { + if _, err := client.WaitForImageStatus(s.RegionId, alicloudImages[s.RegionId], ImageStatusAvailable, time.Duration(ALICLOUD_DEFAULT_LONG_TIMEOUT)*time.Second); err != nil { + return halt(state, err, fmt.Sprintf("Timeout waiting image %s finish copying", alicloudImages[s.RegionId])) + } + } + return multistep.ActionContinue } func (s *stepRegionCopyAlicloudImage) Cleanup(state multistep.StateBag) { _, cancelled := state.GetOk(multistep.StateCancelled) _, halted := state.GetOk(multistep.StateHalted) - if cancelled || halted { - ui := state.Get("ui").(packer.Ui) - client := state.Get("client").(*ClientWrapper) - alicloudImages := state.Get("alicloudimages").(map[string]string) - ui.Say(fmt.Sprintf("Stopping copy image because cancellation or error...")) - for copiedRegionId, copiedImageId := range alicloudImages { - if copiedRegionId == s.RegionId { - continue - } - cancelCopyImageRequest := ecs.CreateCancelCopyImageRequest() - cancelCopyImageRequest.RegionId = copiedRegionId - cancelCopyImageRequest.ImageId = copiedImageId - if _, err := client.CancelCopyImage(cancelCopyImageRequest); err != nil { - ui.Say(fmt.Sprintf("Error cancelling copy image: %v", err)) - } + if !cancelled && !halted { + return + } + + ui := state.Get("ui").(packer.Ui) + ui.Say(fmt.Sprintf("Stopping copy image because cancellation or error...")) + + client := state.Get("client").(*ClientWrapper) + alicloudImages := state.Get("alicloudimages").(map[string]string) + srcImageId := state.Get("alicloudimage").(string) + + for copiedRegionId, copiedImageId := range alicloudImages { + if copiedImageId == srcImageId { + continue + } + + cancelCopyImageRequest := ecs.CreateCancelCopyImageRequest() + cancelCopyImageRequest.RegionId = copiedRegionId + cancelCopyImageRequest.ImageId = copiedImageId + if _, err := client.CancelCopyImage(cancelCopyImageRequest); err != nil { + + ui.Error(fmt.Sprintf("Error cancelling copy image: %v", err)) } } } diff --git a/website/source/docs/builders/alicloud-ecs.html.md b/website/source/docs/builders/alicloud-ecs.html.md index 945b95090..22839d032 100644 --- a/website/source/docs/builders/alicloud-ecs.html.md +++ b/website/source/docs/builders/alicloud-ecs.html.md @@ -73,6 +73,12 @@ builder. - `image_copy_regions` (array of string) - Copy to the destination regionIds. +- `image_encrypted` (boolean) - Whether or not to encrypt the target images, including those copied if `image_copy_regions` is specified. If this option + is set to true, a temporary image will be created from the provisioned + instance in the main region and an encrypted copy will be generated in the + same region. By default, Packer will keep the encryption setting to what + it was in the source image. + - `image_description` (string) - The description of the image, with a length limit of 0 to 256 characters. Leaving it blank means null, which is the default value. It cannot begin with `http://` or `https://`. @@ -148,6 +154,13 @@ builder. Snapshots from on or before July 15, 2013 cannot be used to create a disk. + - `disk_encrypted` (boolean) - Whether or not to encrypt the data disk. + If this option is set to true, the data disk will be encryped and corresponding snapshot in the target image will also be encrypted. By + default, if this is an extra data disk, Packer will not encrypt the + data disk. Otherwise, Packer will keep the encryption setting to what + it was in the source image. Please refer to Introduction of [ECS disk encryption](https://www.alibabacloud.com/help/doc-detail/59643.htm) + for more details. + - `image_ignore_data_disks`(boolean) - If this value is true, the image created will not include any snapshot of data disks. This option would be useful for any circumstance that default data disks with instance types are