github.com/angdraug/packer@v1.3.2/builder/amazon/common/run_config.go (about) 1 package common 2 3 import ( 4 "fmt" 5 "net" 6 "os" 7 "regexp" 8 "strings" 9 "time" 10 11 "github.com/hashicorp/packer/common/uuid" 12 "github.com/hashicorp/packer/helper/communicator" 13 "github.com/hashicorp/packer/template/interpolate" 14 ) 15 16 var reShutdownBehavior = regexp.MustCompile("^(stop|terminate)$") 17 18 type AmiFilterOptions struct { 19 Filters map[*string]*string 20 Owners []*string 21 MostRecent bool `mapstructure:"most_recent"` 22 } 23 24 func (d *AmiFilterOptions) Empty() bool { 25 return len(d.Owners) == 0 && len(d.Filters) == 0 26 } 27 28 func (d *AmiFilterOptions) NoOwner() bool { 29 return len(d.Owners) == 0 30 } 31 32 type SubnetFilterOptions struct { 33 Filters map[*string]*string 34 MostFree bool `mapstructure:"most_free"` 35 Random bool `mapstructure:"random"` 36 } 37 38 func (d *SubnetFilterOptions) Empty() bool { 39 return len(d.Filters) == 0 40 } 41 42 type VpcFilterOptions struct { 43 Filters map[*string]*string 44 } 45 46 func (d *VpcFilterOptions) Empty() bool { 47 return len(d.Filters) == 0 48 } 49 50 type SecurityGroupFilterOptions struct { 51 Filters map[*string]*string 52 } 53 54 func (d *SecurityGroupFilterOptions) Empty() bool { 55 return len(d.Filters) == 0 56 } 57 58 // RunConfig contains configuration for running an instance from a source 59 // AMI and details on how to access that launched image. 60 type RunConfig struct { 61 AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"` 62 AvailabilityZone string `mapstructure:"availability_zone"` 63 BlockDurationMinutes int64 `mapstructure:"block_duration_minutes"` 64 DisableStopInstance bool `mapstructure:"disable_stop_instance"` 65 EbsOptimized bool `mapstructure:"ebs_optimized"` 66 EnableT2Unlimited bool `mapstructure:"enable_t2_unlimited"` 67 IamInstanceProfile string `mapstructure:"iam_instance_profile"` 68 InstanceInitiatedShutdownBehavior string `mapstructure:"shutdown_behavior"` 69 InstanceType string `mapstructure:"instance_type"` 70 SecurityGroupFilter SecurityGroupFilterOptions `mapstructure:"security_group_filter"` 71 RunTags map[string]string `mapstructure:"run_tags"` 72 SecurityGroupId string `mapstructure:"security_group_id"` 73 SecurityGroupIds []string `mapstructure:"security_group_ids"` 74 SourceAmi string `mapstructure:"source_ami"` 75 SourceAmiFilter AmiFilterOptions `mapstructure:"source_ami_filter"` 76 SpotPrice string `mapstructure:"spot_price"` 77 SpotPriceAutoProduct string `mapstructure:"spot_price_auto_product"` 78 SpotTags map[string]string `mapstructure:"spot_tags"` 79 SubnetFilter SubnetFilterOptions `mapstructure:"subnet_filter"` 80 SubnetId string `mapstructure:"subnet_id"` 81 TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"` 82 TemporarySGSourceCidr string `mapstructure:"temporary_security_group_source_cidr"` 83 UserData string `mapstructure:"user_data"` 84 UserDataFile string `mapstructure:"user_data_file"` 85 VpcFilter VpcFilterOptions `mapstructure:"vpc_filter"` 86 VpcId string `mapstructure:"vpc_id"` 87 WindowsPasswordTimeout time.Duration `mapstructure:"windows_password_timeout"` 88 89 // Communicator settings 90 Comm communicator.Config `mapstructure:",squash"` 91 } 92 93 func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { 94 // If we are not given an explicit ssh_keypair_name or 95 // ssh_private_key_file, then create a temporary one, but only if the 96 // temporary_key_pair_name has not been provided and we are not using 97 // ssh_password. 98 if c.Comm.SSHKeyPairName == "" && c.Comm.SSHTemporaryKeyPairName == "" && 99 c.Comm.SSHPrivateKeyFile == "" && c.Comm.SSHPassword == "" { 100 101 c.Comm.SSHTemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()) 102 } 103 104 if c.WindowsPasswordTimeout == 0 { 105 c.WindowsPasswordTimeout = 20 * time.Minute 106 } 107 108 if c.RunTags == nil { 109 c.RunTags = make(map[string]string) 110 } 111 112 // Validation 113 errs := c.Comm.Prepare(ctx) 114 115 // Validating ssh_interface 116 if c.Comm.SSHInterface != "public_ip" && 117 c.Comm.SSHInterface != "private_ip" && 118 c.Comm.SSHInterface != "public_dns" && 119 c.Comm.SSHInterface != "private_dns" && 120 c.Comm.SSHInterface != "" { 121 errs = append(errs, fmt.Errorf("Unknown interface type: %s", c.Comm.SSHInterface)) 122 } 123 124 if c.Comm.SSHKeyPairName != "" { 125 if c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" && c.Comm.SSHPrivateKeyFile == "" { 126 errs = append(errs, fmt.Errorf("ssh_private_key_file must be provided to retrieve the winrm password when using ssh_keypair_name.")) 127 } else if c.Comm.SSHPrivateKeyFile == "" && !c.Comm.SSHAgentAuth { 128 errs = append(errs, fmt.Errorf("ssh_private_key_file must be provided or ssh_agent_auth enabled when ssh_keypair_name is specified.")) 129 } 130 } 131 132 if c.SourceAmi == "" && c.SourceAmiFilter.Empty() { 133 errs = append(errs, fmt.Errorf("A source_ami or source_ami_filter must be specified")) 134 } 135 136 if c.SourceAmi == "" && c.SourceAmiFilter.NoOwner() { 137 errs = append(errs, fmt.Errorf("For security reasons, your source AMI filter must declare an owner.")) 138 } 139 140 if c.InstanceType == "" { 141 errs = append(errs, fmt.Errorf("An instance_type must be specified")) 142 } 143 144 if c.BlockDurationMinutes%60 != 0 { 145 errs = append(errs, fmt.Errorf( 146 "block_duration_minutes must be multiple of 60")) 147 } 148 149 if c.SpotPrice == "auto" { 150 if c.SpotPriceAutoProduct == "" { 151 errs = append(errs, fmt.Errorf( 152 "spot_price_auto_product must be specified when spot_price is auto")) 153 } 154 } 155 156 if c.SpotPriceAutoProduct != "" { 157 if c.SpotPrice != "auto" { 158 errs = append(errs, fmt.Errorf( 159 "spot_price should be set to auto when spot_price_auto_product is specified")) 160 } 161 } 162 163 if c.SpotTags != nil { 164 if c.SpotPrice == "" || c.SpotPrice == "0" { 165 errs = append(errs, fmt.Errorf( 166 "spot_tags should not be set when not requesting a spot instance")) 167 } 168 } 169 170 if c.UserData != "" && c.UserDataFile != "" { 171 errs = append(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified.")) 172 } else if c.UserDataFile != "" { 173 if _, err := os.Stat(c.UserDataFile); err != nil { 174 errs = append(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile)) 175 } 176 } 177 178 if c.SecurityGroupId != "" { 179 if len(c.SecurityGroupIds) > 0 { 180 errs = append(errs, fmt.Errorf("Only one of security_group_id or security_group_ids can be specified.")) 181 } else { 182 c.SecurityGroupIds = []string{c.SecurityGroupId} 183 c.SecurityGroupId = "" 184 } 185 } 186 187 if c.TemporarySGSourceCidr == "" { 188 c.TemporarySGSourceCidr = "0.0.0.0/0" 189 } else { 190 if _, _, err := net.ParseCIDR(c.TemporarySGSourceCidr); err != nil { 191 errs = append(errs, fmt.Errorf("Error parsing temporary_security_group_source_cidr: %s", err.Error())) 192 } 193 } 194 195 if c.InstanceInitiatedShutdownBehavior == "" { 196 c.InstanceInitiatedShutdownBehavior = "stop" 197 } else if !reShutdownBehavior.MatchString(c.InstanceInitiatedShutdownBehavior) { 198 errs = append(errs, fmt.Errorf("shutdown_behavior only accepts 'stop' or 'terminate' values.")) 199 } 200 201 if c.EnableT2Unlimited { 202 if c.SpotPrice != "" { 203 errs = append(errs, fmt.Errorf("Error: T2 Unlimited cannot be used in conjuction with Spot Instances")) 204 } 205 firstDotIndex := strings.Index(c.InstanceType, ".") 206 if firstDotIndex == -1 { 207 errs = append(errs, fmt.Errorf("Error determining main Instance Type from: %s", c.InstanceType)) 208 } else if c.InstanceType[0:firstDotIndex] != "t2" { 209 errs = append(errs, fmt.Errorf("Error: T2 Unlimited enabled with a non-T2 Instance Type: %s", c.InstanceType)) 210 } 211 } 212 213 return errs 214 } 215 216 func (c *RunConfig) IsSpotInstance() bool { 217 return c.SpotPrice != "" && c.SpotPrice != "0" 218 }