ucloud packer review

This commit is contained in:
mingsheng.su 2019-06-19 21:32:33 +08:00
parent b0500f97b8
commit 7804a52dd2
16 changed files with 123 additions and 131 deletions

View File

@ -86,7 +86,6 @@ func (c *AccessConfig) Config() error {
}
func (c *AccessConfig) ValidateProjectId(projectId string) error {
supportedProjectIds, err := c.getSupportedProjectIds()
if err != nil {
return err
@ -102,7 +101,6 @@ func (c *AccessConfig) ValidateProjectId(projectId string) error {
}
func (c *AccessConfig) ValidateRegion(region string) error {
supportedRegions, err := c.getSupportedRegions()
if err != nil {
return err
@ -118,7 +116,6 @@ func (c *AccessConfig) ValidateRegion(region string) error {
}
func (c *AccessConfig) ValidateZone(region, zone string) error {
supportedZones, err := c.getSupportedZones(region)
if err != nil {
return err

View File

@ -27,7 +27,6 @@ func (c *UCloudClient) describeFirewallById(sgId string) (*unet.FirewallDataSet,
resp, err := conn.DescribeFirewall(req)
// [API-STYLE] Fire wall api has not found err code, but others don't have
if err != nil {
if uErr, ok := err.(uerr.Error); ok && uErr.Code() == 54002 {
return nil, newNotFoundError("security group", sgId)
@ -93,9 +92,6 @@ func (c *UCloudClient) DescribeImageById(imageId string) (*uhost.UHostImageSet,
resp, err := c.uhostconn.DescribeImage(req)
if err != nil {
//if uErr, ok := err.(uerr.Error); ok && uErr.Code() == 8889 {
// return nil, newNotFoundError("image", imageId)
//}
return nil, err
}
@ -132,9 +128,6 @@ func (c *UCloudClient) describeImageByInfo(projectId, regionId, imageId string)
resp, err := c.uhostconn.DescribeImage(req)
if err != nil {
//if uErr, ok := err.(uerr.Error); ok && uErr.Code() == 8889 {
// return nil, newNotFoundError("image", imageId)
//}
return nil, err
}

View File

@ -8,13 +8,13 @@ const (
)
const (
osTypeWindows = "Windows"
securityGroupNonWeb = "recommend non web"
instanceStateRunning = "Running"
instanceStateStopped = "Stopped"
bootDiskStateInitializing = "Initializing"
bootDiskStateNormal = "Normal"
imageStateAvailable = "Available"
osTypeWindows = "Windows"
securityGroupNonWeb = "recommend non web"
instanceStateRunning = "Running"
instanceStateStopped = "Stopped"
bootDiskStateNormal = "Normal"
imageStateAvailable = "Available"
ipTypePrivate = "Private"
)
var bootDiskTypeMap = map[string]string{

View File

@ -35,6 +35,7 @@ func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error {
if imageDestination.Name == "" {
imageDestination.Name = imageName
}
errs = append(errs, imageDestination.validate()...)
}
}

View File

@ -49,7 +49,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
if c.BootDiskType == "" {
c.BootDiskType = "cloud_ssd"
} else if err := checkStringIn(c.BootDiskType,
[]string{"local_normal", "local_ssd", "cloud_normal", "cloud_ssd"}); err != nil {
[]string{"local_normal", "local_ssd", "cloud_ssd"}); err != nil {
errs = append(errs, err)
}
@ -63,9 +63,50 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
errs = append(errs, fmt.Errorf("%q must be set when use_ssh_private_ip is true", "vpc_id"))
}
if c.Comm.SSHPassword != "" && len(validateInstancePassword(c.Comm.SSHPassword)) != 0 {
for _, v := range validateInstancePassword(c.Comm.SSHPassword) {
errs = append(errs, v)
}
}
if len(errs) > 0 {
return errs
}
return nil
}
var instancePasswordUpperPattern = regexp.MustCompile(`[A-Z]`)
var instancePasswordLowerPattern = regexp.MustCompile(`[a-z]`)
var instancePasswordNumPattern = regexp.MustCompile(`[0-9]`)
var instancePasswordSpecialPattern = regexp.MustCompile(`[` + "`" + `()~!@#$%^&*-+=_|{}\[\]:;'<>,.?/]`)
var instancePasswordPattern = regexp.MustCompile(`^[A-Za-z0-9` + "`" + `()~!@#$%^&*-+=_|{}\[\]:;'<>,.?/]{8,30}$`)
func validateInstancePassword(password string) (errors []error) {
if !instancePasswordPattern.MatchString(password) {
errors = append(errors, fmt.Errorf("%q is invalid, should have between 8-30 characters and any characters must be legal, got %q", "ssh_password", password))
}
categoryCount := 0
if instancePasswordUpperPattern.MatchString(password) {
categoryCount++
}
if instancePasswordLowerPattern.MatchString(password) {
categoryCount++
}
if instancePasswordNumPattern.MatchString(password) {
categoryCount++
}
if instancePasswordSpecialPattern.MatchString(password) {
categoryCount++
}
if categoryCount < 2 {
errors = append(errors, fmt.Errorf("%q is invalid, should have least 2 items of capital letters, lower case letters, numbers and special characters, got %q", "ssh_password", password))
}
return
}

View File

@ -2,6 +2,7 @@ package uhost
import (
"context"
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -21,7 +22,7 @@ func (s *stepCheckSourceImageId) Run(ctx context.Context, state multistep.StateB
if isNotFoundError(err) {
return halt(state, err, "")
}
return halt(state, err, "Error on querying source_image_id")
return halt(state, err, fmt.Sprintf("Error on querying specified source_image_id %q", s.SourceUHostImageId))
}
if imageSet.OsType == osTypeWindows {

View File

@ -25,7 +25,7 @@ func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.State
err = fmt.Errorf("the specified security group %q not exist", s.SecurityGroupId)
return halt(state, err, "")
}
return halt(state, err, "Error on querying security group")
return halt(state, err, fmt.Sprintf("Error on querying specified security group %q", s.SecurityGroupId))
}
state.Put("security_group_id", securityGroupSet.FWId)
@ -44,7 +44,7 @@ func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.State
resp, err := conn.DescribeFirewall(req)
if err != nil {
return halt(state, err, "Error on querying security group")
return halt(state, err, "Error on querying default security group")
}
if resp == nil || len(resp.DataSet) < 1 {
@ -65,10 +65,11 @@ func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.State
offset = offset + limit
}
if securityGroupId != "" {
state.Put("security_group_id", securityGroupId)
return multistep.ActionContinue
if securityGroupId == "" {
return halt(state, fmt.Errorf("the default security group not exist"), "")
}
state.Put("security_group_id", securityGroupId)
return multistep.ActionContinue
}

View File

@ -23,7 +23,7 @@ func (s *stepConfigSubnet) Run(ctx context.Context, state multistep.StateBag) mu
err = fmt.Errorf("the specified subnet %q not exist", s.SubnetId)
return halt(state, err, "")
}
return halt(state, err, "Error on querying subnet")
return halt(state, err, fmt.Sprintf("Error on querying specified subnet %q", s.SubnetId))
}
state.Put("subnet_id", subnetSet.SubnetId)

View File

@ -25,7 +25,7 @@ func (s *stepConfigVPC) Run(ctx context.Context, state multistep.StateBag) multi
err = fmt.Errorf("the specified vpc %q not exist", s.VPCId)
return halt(state, err, "")
}
return halt(state, err, "Error on querying vpc")
return halt(state, err, fmt.Sprintf("Error on querying specified vpc %q", s.VPCId))
}
state.Put("vpc_id", vpcSet.VPCId)

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/hashicorp/packer/common/retry"
"strings"
"time"
"github.com/hashicorp/packer/helper/multistep"
@ -29,34 +30,34 @@ func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag)
srcImageId := state.Get("image_id").(string)
artifactImages := state.Get("ucloud_images").(*imageInfoSet)
expectedImages := newImageInfoSet(nil)
ui.Say(fmt.Sprintf("Copying image with %q...", srcImageId))
for _, imageDestination := range s.ImageDestinations {
if imageDestination.ProjectId == s.ProjectId && imageDestination.Region == s.RegionId {
for _, v := range s.ImageDestinations {
if v.ProjectId == s.ProjectId && v.Region == s.RegionId {
continue
}
req := conn.NewCopyCustomImageRequest()
req.TargetProjectId = ucloud.String(imageDestination.ProjectId)
req.TargetRegion = ucloud.String(imageDestination.Region)
req.TargetProjectId = ucloud.String(v.ProjectId)
req.TargetRegion = ucloud.String(v.Region)
req.SourceImageId = ucloud.String(srcImageId)
req.TargetImageName = ucloud.String(imageDestination.Name)
req.TargetImageName = ucloud.String(v.Name)
req.TargetImageDescription = ucloud.String(v.Description)
resp, err := conn.CopyCustomImage(req)
if err != nil {
return halt(state, err, "Error on copying images")
return halt(state, err, fmt.Sprintf("Error on copying image %q to %s:%s", srcImageId, v.ProjectId, v.Region))
}
image := imageInfo{
Region: imageDestination.Region,
ProjectId: imageDestination.ProjectId,
Region: v.Region,
ProjectId: v.ProjectId,
ImageId: resp.TargetImageId,
}
expectedImages.Set(image)
artifactImages.Set(image)
ui.Message(fmt.Sprintf("Copying image from %s:%s:%s to %s:%s:%s)",
s.ProjectId, s.RegionId, srcImageId, imageDestination.ProjectId, imageDestination.Region, resp.TargetImageId))
s.ProjectId, s.RegionId, srcImageId, v.ProjectId, v.Region, resp.TargetImageId))
}
err := retry.Config{
@ -67,7 +68,7 @@ func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag)
for _, v := range expectedImages.GetAll() {
imageSet, err := client.describeImageByInfo(v.ProjectId, v.Region, v.ImageId)
if err != nil {
return err
return fmt.Errorf("reading %s:%s:%s failed, %s", v.ProjectId, v.Region, v.ImageId, err)
}
if imageSet.State == imageStateAvailable {
@ -84,10 +85,15 @@ func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag)
})
if err != nil {
return halt(state, err, fmt.Sprintf("Error on waiting for copying image finished"))
var s []string
for _, v := range expectedImages.GetAll() {
s = append(s, fmt.Sprintf("%s:%s:%s", v.ProjectId, v.Region, v.ImageId))
}
return halt(state, err, fmt.Sprintf("Error on waiting for copying images %q available", strings.Join(s, ",")))
}
ui.Message(fmt.Sprintf("Copy image complete"))
ui.Message(fmt.Sprintf("Copying image complete"))
return multistep.ActionContinue
}
@ -128,5 +134,5 @@ func (s *stepCopyUCloudImage) Cleanup(state multistep.StateBag) {
}
}
ui.Message("Delete copied image complete")
ui.Message("Deleting copied image complete")
}

View File

@ -32,7 +32,7 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
resp, err := conn.CreateCustomImage(req)
if err != nil {
return halt(state, err, "")
return halt(state, err, "Error on creating image")
}
err = retry.Config{
@ -54,12 +54,12 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
})
if err != nil {
return halt(state, err, "Error on waiting for image to available")
return halt(state, err, fmt.Sprintf("Error on waiting for image %q available", resp.ImageId))
}
imageSet, err := client.DescribeImageById(resp.ImageId)
if err != nil {
return halt(state, err, "Error on reading image")
return halt(state, err, fmt.Sprintf("Error on reading image when creating %q", resp.ImageId))
}
s.image = imageSet
@ -74,7 +74,7 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
}
state.Put("ucloud_images", newImageInfoSet(images))
ui.Message(fmt.Sprintf("Create image %q complete", imageSet.ImageId))
ui.Message(fmt.Sprintf("Creating image %q complete", imageSet.ImageId))
return multistep.ActionContinue
}
@ -99,5 +99,5 @@ func (s *stepCreateImage) Cleanup(state multistep.StateBag) {
if err != nil {
ui.Error(fmt.Sprintf("Error on deleting image %q", s.image.ImageId))
}
ui.Message(fmt.Sprintf("Delete image %q complete", s.image.ImageId))
ui.Message(fmt.Sprintf("Deleting image %q complete", s.image.ImageId))
}

View File

@ -31,12 +31,7 @@ func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag)
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating Instance...")
req, err := s.buildCreateInstanceRequest(state)
if err != nil {
return halt(state, err, "")
}
resp, err := conn.CreateUHostInstance(req)
resp, err := conn.CreateUHostInstance(s.buildCreateInstanceRequest(state))
if err != nil {
return halt(state, err, "Error on creating instance")
}
@ -53,6 +48,15 @@ func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag)
if err != nil {
return err
}
if inst.State == "ResizeFail" {
return fmt.Errorf("resizing instance failed")
}
if inst.State == "Install Fail" {
return fmt.Errorf("install failed")
}
if inst == nil || inst.State != instanceStateRunning {
return newExpectedStateError("instance", instanceId)
}
@ -61,20 +65,23 @@ func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag)
})
if err != nil {
return halt(state, err, "Error on waiting for instance to available")
return halt(state, err, fmt.Sprintf("Error on waiting for instance %q available", instanceId))
}
ui.Message(fmt.Sprintf("Create instance %q complete", instanceId))
ui.Message(fmt.Sprintf("Creating instance %q complete", instanceId))
instance, err := client.describeUHostById(instanceId)
if err != nil {
return halt(state, err, "")
return halt(state, err, fmt.Sprintf("Error on reading instance when creating %q", instanceId))
}
s.instanceId = instanceId
state.Put("instance", instance)
if instance.BootDiskState == bootDiskStateInitializing {
ui.Say(fmt.Sprintf("Waiting for boot disk of instance initialized when boot_disk_type is %q", s.BootDiskType))
if instance.BootDiskState != bootDiskStateNormal {
ui.Say("Waiting for boot disk of instance initialized")
if s.BootDiskType == "local_normal" || s.BootDiskType == "local_ssd" {
ui.Message(fmt.Sprintf("Warning: It takes around 10 mins for boot disk initialization when `boot_disk_type` is %q", s.BootDiskType))
}
err = retry.Config{
Tries: 200,
@ -95,10 +102,10 @@ func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag)
})
if err != nil {
return halt(state, err, "Error on waiting for boot disk of instance initialized")
return halt(state, err, fmt.Sprintf("Error on waiting for boot disk of instance %q initialized", instanceId))
}
ui.Message(fmt.Sprintf("Waite for boot disk of instance %q initialized complete", instanceId))
ui.Message(fmt.Sprintf("Waiting for boot disk of instance %q initialized complete", instanceId))
}
return multistep.ActionContinue
@ -123,35 +130,21 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) {
client := state.Get("client").(*UCloudClient)
conn := client.uhostconn
stopReq := conn.NewPoweroffUHostInstanceRequest()
stopReq.UHostId = ucloud.String(s.instanceId)
instance, err := client.describeUHostById(s.instanceId)
if err != nil {
if isNotFoundError(err) {
return
}
ui.Error(fmt.Sprintf("Error on reading instance when delete %q, %s",
ui.Error(fmt.Sprintf("Error on reading instance when deleting %q, %s",
s.instanceId, err.Error()))
return
}
if instance.State != instanceStateStopped {
err = retry.Config{
Tries: 5,
ShouldRetry: func(err error) bool {
return err != nil
},
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 6 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
if _, err = conn.PoweroffUHostInstance(stopReq); err != nil {
return err
}
return nil
})
if err != nil {
ui.Error(fmt.Sprintf("Error on stopping instance when delete %q, %s",
stopReq := conn.NewPoweroffUHostInstanceRequest()
stopReq.UHostId = ucloud.String(s.instanceId)
if _, err = conn.PoweroffUHostInstance(stopReq); err != nil {
ui.Error(fmt.Sprintf("Error on stopping instance when deleting %q, %s",
s.instanceId, err.Error()))
return
}
@ -176,7 +169,7 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) {
})
if err != nil {
ui.Error(fmt.Sprintf("Error on waiting for instance %q to stopped, %s",
ui.Error(fmt.Sprintf("Error on waiting for stopping instance when deleting %q, %s",
s.instanceId, err.Error()))
return
}
@ -187,20 +180,7 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) {
deleteReq.ReleaseUDisk = ucloud.Bool(true)
deleteReq.ReleaseEIP = ucloud.Bool(true)
err = retry.Config{
Tries: 5,
ShouldRetry: func(err error) bool {
return err != nil
},
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 6 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
if _, err = conn.TerminateUHostInstance(deleteReq); err != nil {
return err
}
return nil
})
if err != nil {
if _, err = conn.TerminateUHostInstance(deleteReq); err != nil {
ui.Error(fmt.Sprintf("Error on deleting instance %q, %s",
s.instanceId, err.Error()))
return
@ -221,10 +201,10 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) {
return
}
ui.Message(fmt.Sprintf("Delete instance %q complete", s.instanceId))
ui.Message(fmt.Sprintf("Deleting instance %q complete", s.instanceId))
}
func (s *stepCreateInstance) buildCreateInstanceRequest(state multistep.StateBag) (*uhost.CreateUHostInstanceRequest, error) {
func (s *stepCreateInstance) buildCreateInstanceRequest(state multistep.StateBag) *uhost.CreateUHostInstanceRequest {
client := state.Get("client").(*UCloudClient)
conn := client.uhostconn
srcImage := state.Get("source_image").(*uhost.UHostImageSet)
@ -292,7 +272,7 @@ func (s *stepCreateInstance) buildCreateInstanceRequest(state multistep.StateBag
req.NetworkInterface = append(req.NetworkInterface, networkInterface)
}
return req, nil
return req
}
func (s *stepCreateInstance) randStringFromCharSet(strlen int, charSet string) string {

View File

@ -23,7 +23,7 @@ func (s *stepStopInstance) Run(ctx context.Context, state multistep.StateBag) mu
instance, err := client.describeUHostById(instance.UHostId)
if err != nil {
return halt(state, err, fmt.Sprintf("Error on reading instance when stop %q", instance.UHostId))
return halt(state, err, fmt.Sprintf("Error on reading instance when stopping %q", instance.UHostId))
}
if instance.State != instanceStateStopped {
@ -67,10 +67,10 @@ func (s *stepStopInstance) Run(ctx context.Context, state multistep.StateBag) mu
})
if err != nil {
return halt(state, err, fmt.Sprintf("Error on waiting for instance %q to stopped", instance.UHostId))
return halt(state, err, fmt.Sprintf("Error on waiting for stopping instance when stopping %q", instance.UHostId))
}
ui.Message(fmt.Sprintf("Stop instance %q complete", instance.UHostId))
ui.Message(fmt.Sprintf("Stopping instance %q complete", instance.UHostId))
}
return multistep.ActionContinue

View File

@ -46,17 +46,18 @@ func SSHHost(usePrivateIp bool) func(multistep.StateBag) (string, error) {
var privateIp, publicIp string
for _, v := range instance.IPSet {
if v.Type == "Private" {
if v.Type == ipTypePrivate {
privateIp = v.IP
} else {
publicIp = v.IP
}
}
if usePrivateIp {
return privateIp, nil
} else {
return publicIp, nil
}
return publicIp, nil
}
}

View File

@ -1,29 +0,0 @@
{
"variables": {
"ucloud_public_key": "{{env `UCLOUD_PUBLIC_KEY`}}",
"ucloud_private_key": "{{env `UCLOUD_PRIVATE_KEY`}}",
"ucloud_project_id": "{{env `UCLOUD_PROJECT_ID`}}"
},
"builders": [{
"type": "ucloud-uhost",
"public_key":"{{user `ucloud_public_key`}}",
"private_key":"{{user `ucloud_private_key`}}",
"project_id": "{{user `ucloud_project_id`}}",
"region": "cn-bj2",
"availability_zone": "cn-bj2-02",
"instance_type": "n-basic-2",
"source_image_id":"uimage-irofn4",
"ssh_username":"ubuntu",
"image_name": "packer-test-ubuntu-bj"
}],
"provisioners": [{
"type": "shell",
"inline": [
"sudo visudo",
"ubuntu ALL = NOPASSWD: /usr/local/sbin/apt-get",
"sudo apt-get update",
"sudo apt-get install -y mysql"
]
}]
}

View File

@ -26,7 +26,7 @@ In addition to the options listed here, a
[communicator](../templates/communicator.html) can be configured for this
builder.
\~&gt; **Note:** This builder only support ssh authenticating with username and given password.
\~&gt; **Note:** This builder not support build Windows image yet and only support ssh authenticating with `ssh_user_name` (Required) and `ssh_password` (Optional). There into, `ssh_user_name` can be set `root` for CentoOS image, `ubuntu` for Ubuntu image. `ssh_password` contains 8-30 characters, and at least 2 items of capital letters, lower case letters, numbers and special characters. The special characters include <code>`()~!@#$%^&*-+=_|{}\[]:;'<>,.?/</code>.
### Required: