packer-cn/builder/tencentcloud/cvm/step_create_image.go
ZhiQiang Fan 7235a4a775 feature: support data disks for tencentcloud builder
Data disk is an important feature for instances and images, this
patch implements the basic functionality of it. More work needs
to be done to make it friendly to users. Docs has pointed out
current limitation.

Also update dependencies because this feature requires new code in
tencentcloud go sdk.
2019-06-30 00:05:38 +08:00

148 lines
3.7 KiB
Go

package cvm
import (
"context"
"fmt"
"time"
"github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepCreateImage struct {
imageId string
}
func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
client := state.Get("cvm_client").(*cvm.Client)
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*cvm.Instance)
ui.Say(fmt.Sprintf("Creating image %s", config.ImageName))
req := cvm.NewCreateImageRequest()
req.ImageName = &config.ImageName
req.ImageDescription = &config.ImageDescription
req.InstanceId = instance.InstanceId
// TODO: We should allow user to specify which data disk should be
// included into created image.
var dataDiskIds []*string
for _, disk := range instance.DataDisks {
dataDiskIds = append(dataDiskIds, disk.DiskId)
}
if len(dataDiskIds) > 0 {
req.DataDiskIds = dataDiskIds
}
True := "True"
False := "False"
if config.ForcePoweroff {
req.ForcePoweroff = &True
} else {
req.ForcePoweroff = &False
}
if config.Reboot {
req.Reboot = &True
} else {
req.Reboot = &False
}
if config.Sysprep {
req.Sysprep = &True
} else {
req.Sysprep = &False
}
err := retry.Config{
Tries: 60,
RetryDelay: (&retry.Backoff{
InitialBackoff: 5 * time.Second,
MaxBackoff: 5 * time.Second,
Multiplier: 2,
}).Linear,
ShouldRetry: func(err error) bool {
if e, ok := err.(*errors.TencentCloudSDKError); ok {
if e.Code == "InvalidImageName.Duplicate" {
return false
}
}
return true
},
}.Run(ctx, func(ctx context.Context) error {
_, err := client.CreateImage(req)
return err
})
if err != nil {
err := fmt.Errorf("create image failed: %s", err.Error())
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
err = WaitForImageReady(client, config.ImageName, "NORMAL", 3600)
if err != nil {
err := fmt.Errorf("create image failed: %s", err.Error())
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
describeReq := cvm.NewDescribeImagesRequest()
FILTER_IMAGE_NAME := "image-name"
describeReq.Filters = []*cvm.Filter{
{
Name: &FILTER_IMAGE_NAME,
Values: []*string{&config.ImageName},
},
}
describeResp, err := client.DescribeImages(describeReq)
if err != nil {
err := fmt.Errorf("wait image ready failed: %s", err.Error())
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if *describeResp.Response.TotalCount == 0 {
err := fmt.Errorf("create image(%s) failed", config.ImageName)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
s.imageId = *describeResp.Response.ImageSet[0].ImageId
state.Put("image", describeResp.Response.ImageSet[0])
tencentCloudImages := make(map[string]string)
tencentCloudImages[config.Region] = s.imageId
state.Put("tencentcloudimages", tencentCloudImages)
return multistep.ActionContinue
}
func (s *stepCreateImage) Cleanup(state multistep.StateBag) {
if s.imageId == "" {
return
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
client := state.Get("cvm_client").(*cvm.Client)
ui := state.Get("ui").(packer.Ui)
ui.Say("Delete image because of cancellation or error...")
req := cvm.NewDeleteImagesRequest()
req.ImageIds = []*string{&s.imageId}
_, err := client.DeleteImages(req)
if err != nil {
ui.Error(fmt.Sprintf("delete image(%s) failed", s.imageId))
}
}