package common import ( "fmt" "regexp" "github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/template/interpolate" ) type RunConfig struct { Zone string `mapstructure:"availability_zone"` SourceImageId string `mapstructure:"source_image_id"` InstanceType string `mapstructure:"instance_type"` InstanceName string `mapstructure:"instance_name"` BootDiskType string `mapstructure:"boot_disk_type"` VPCId string `mapstructure:"vpc_id"` SubnetId string `mapstructure:"subnet_id"` SecurityGroupId string `mapstructure:"security_group_id"` // Communicator settings Comm communicator.Config `mapstructure:",squash"` UseSSHPrivateIp bool `mapstructure:"use_ssh_private_ip"` } var instanceNamePattern = regexp.MustCompile(`^[A-Za-z0-9\p{Han}-_.]{1,63}$`) func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { errs := c.Comm.Prepare(ctx) if c.Zone == "" { errs = append(errs, fmt.Errorf("%q must be set", "availability_zone")) } if c.SourceImageId == "" { errs = append(errs, fmt.Errorf("%q must be set", "source_image_id")) } if c.InstanceType == "" { errs = append(errs, fmt.Errorf("%q must be set", "instance_type")) } else if _, err := ParseInstanceType(c.InstanceType); err != nil { errs = append(errs, err) } if (c.VPCId != "" && c.SubnetId == "") || (c.VPCId == "" && c.SubnetId != "") { errs = append(errs, fmt.Errorf("expected both %q and %q to set or not set", "vpc_id", "subnet_id")) } if c.BootDiskType == "" { c.BootDiskType = "cloud_ssd" } else if err := CheckStringIn(c.BootDiskType, []string{"local_normal", "local_ssd", "cloud_ssd"}); err != nil { errs = append(errs, err) } if c.InstanceName == "" { c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()[:8]) } else if !instanceNamePattern.MatchString(c.InstanceName) { errs = append(errs, fmt.Errorf("expected %q to be 1-63 characters and only support chinese, english, numbers, '-_.', got %q", "instance_name", c.InstanceName)) } if c.UseSSHPrivateIp == true && c.VPCId == "" { 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 }