github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/builder/googlecompute/config.go (about) 1 package googlecompute 2 3 import ( 4 "errors" 5 "fmt" 6 "regexp" 7 "time" 8 9 "github.com/hashicorp/packer/common" 10 "github.com/hashicorp/packer/common/uuid" 11 "github.com/hashicorp/packer/helper/communicator" 12 "github.com/hashicorp/packer/helper/config" 13 "github.com/hashicorp/packer/packer" 14 "github.com/hashicorp/packer/template/interpolate" 15 ) 16 17 var reImageFamily = regexp.MustCompile(`^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$`) 18 19 // Config is the configuration structure for the GCE builder. It stores 20 // both the publicly settable state as well as the privately generated 21 // state of the config object. 22 type Config struct { 23 common.PackerConfig `mapstructure:",squash"` 24 Comm communicator.Config `mapstructure:",squash"` 25 26 AccountFile string `mapstructure:"account_file"` 27 ProjectId string `mapstructure:"project_id"` 28 29 AcceleratorType string `mapstructure:"accelerator_type"` 30 AcceleratorCount int64 `mapstructure:"accelerator_count"` 31 Address string `mapstructure:"address"` 32 DisableDefaultServiceAccount bool `mapstructure:"disable_default_service_account"` 33 DiskName string `mapstructure:"disk_name"` 34 DiskSizeGb int64 `mapstructure:"disk_size"` 35 DiskType string `mapstructure:"disk_type"` 36 ImageName string `mapstructure:"image_name"` 37 ImageDescription string `mapstructure:"image_description"` 38 ImageFamily string `mapstructure:"image_family"` 39 ImageLabels map[string]string `mapstructure:"image_labels"` 40 ImageLicenses []string `mapstructure:"image_licenses"` 41 InstanceName string `mapstructure:"instance_name"` 42 Labels map[string]string `mapstructure:"labels"` 43 MachineType string `mapstructure:"machine_type"` 44 Metadata map[string]string `mapstructure:"metadata"` 45 Network string `mapstructure:"network"` 46 NetworkProjectId string `mapstructure:"network_project_id"` 47 OmitExternalIP bool `mapstructure:"omit_external_ip"` 48 OnHostMaintenance string `mapstructure:"on_host_maintenance"` 49 Preemptible bool `mapstructure:"preemptible"` 50 RawStateTimeout string `mapstructure:"state_timeout"` 51 Region string `mapstructure:"region"` 52 Scopes []string `mapstructure:"scopes"` 53 ServiceAccountEmail string `mapstructure:"service_account_email"` 54 SourceImage string `mapstructure:"source_image"` 55 SourceImageFamily string `mapstructure:"source_image_family"` 56 SourceImageProjectId string `mapstructure:"source_image_project_id"` 57 StartupScriptFile string `mapstructure:"startup_script_file"` 58 Subnetwork string `mapstructure:"subnetwork"` 59 Tags []string `mapstructure:"tags"` 60 UseInternalIP bool `mapstructure:"use_internal_ip"` 61 Zone string `mapstructure:"zone"` 62 63 Account AccountFile 64 stateTimeout time.Duration 65 imageAlreadyExists bool 66 ctx interpolate.Context 67 } 68 69 func NewConfig(raws ...interface{}) (*Config, []string, error) { 70 c := new(Config) 71 c.ctx.Funcs = TemplateFuncs 72 err := config.Decode(c, &config.DecodeOpts{ 73 Interpolate: true, 74 InterpolateContext: &c.ctx, 75 InterpolateFilter: &interpolate.RenderFilter{ 76 Exclude: []string{ 77 "run_command", 78 }, 79 }, 80 }, raws...) 81 if err != nil { 82 return nil, nil, err 83 } 84 85 var errs *packer.MultiError 86 87 // Set defaults. 88 if c.Network == "" && c.Subnetwork == "" { 89 c.Network = "default" 90 } 91 92 if c.NetworkProjectId == "" { 93 c.NetworkProjectId = c.ProjectId 94 } 95 96 if c.DiskSizeGb == 0 { 97 c.DiskSizeGb = 10 98 } 99 100 if c.DiskType == "" { 101 c.DiskType = "pd-standard" 102 } 103 104 if c.ImageDescription == "" { 105 c.ImageDescription = "Created by Packer" 106 } 107 108 if c.OnHostMaintenance == "MIGRATE" && c.Preemptible { 109 errs = packer.MultiErrorAppend(errs, 110 errors.New("on_host_maintenance must be TERMINATE when using preemptible instances.")) 111 } 112 // Setting OnHostMaintenance Correct Defaults 113 // "MIGRATE" : Possible and default if Preemptible is false 114 // "TERMINATE": Required if Preemptible is true 115 if c.Preemptible { 116 c.OnHostMaintenance = "TERMINATE" 117 } else { 118 if c.OnHostMaintenance == "" { 119 c.OnHostMaintenance = "MIGRATE" 120 } 121 } 122 123 // Make sure user sets a valid value for on_host_maintenance option 124 if !(c.OnHostMaintenance == "MIGRATE" || c.OnHostMaintenance == "TERMINATE") { 125 errs = packer.MultiErrorAppend(errs, 126 errors.New("on_host_maintenance must be one of MIGRATE or TERMINATE.")) 127 } 128 129 if c.ImageName == "" { 130 img, err := interpolate.Render("packer-{{timestamp}}", nil) 131 if err != nil { 132 errs = packer.MultiErrorAppend(errs, 133 fmt.Errorf("Unable to parse image name: %s ", err)) 134 } else { 135 c.ImageName = img 136 } 137 } 138 139 if len(c.ImageFamily) > 63 { 140 errs = packer.MultiErrorAppend(errs, 141 errors.New("Invalid image family: Must not be longer than 63 characters")) 142 } 143 144 if c.ImageFamily != "" { 145 if !reImageFamily.MatchString(c.ImageFamily) { 146 errs = packer.MultiErrorAppend(errs, 147 errors.New("Invalid image family: The first character must be a lowercase letter, and all following characters must be a dash, lowercase letter, or digit, except the last character, which cannot be a dash")) 148 } 149 150 } 151 152 if c.InstanceName == "" { 153 c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) 154 } 155 156 if c.DiskName == "" { 157 c.DiskName = c.InstanceName 158 } 159 160 if c.MachineType == "" { 161 c.MachineType = "n1-standard-1" 162 } 163 164 if c.RawStateTimeout == "" { 165 c.RawStateTimeout = "5m" 166 } 167 168 if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { 169 errs = packer.MultiErrorAppend(errs, es...) 170 } 171 172 // Process required parameters. 173 if c.ProjectId == "" { 174 errs = packer.MultiErrorAppend( 175 errs, errors.New("a project_id must be specified")) 176 } 177 178 if c.Scopes == nil { 179 c.Scopes = []string{ 180 "https://www.googleapis.com/auth/userinfo.email", 181 "https://www.googleapis.com/auth/compute", 182 "https://www.googleapis.com/auth/devstorage.full_control", 183 } 184 } 185 186 if c.SourceImage == "" && c.SourceImageFamily == "" { 187 errs = packer.MultiErrorAppend( 188 errs, errors.New("a source_image or source_image_family must be specified")) 189 } 190 191 if c.Zone == "" { 192 errs = packer.MultiErrorAppend( 193 errs, errors.New("a zone must be specified")) 194 } 195 if c.Region == "" && len(c.Zone) > 2 { 196 // get region from Zone 197 region := c.Zone[:len(c.Zone)-2] 198 c.Region = region 199 } 200 201 err = c.CalcTimeout() 202 if err != nil { 203 errs = packer.MultiErrorAppend(errs, err) 204 } 205 206 if c.AccountFile != "" { 207 if err := ProcessAccountFile(&c.Account, c.AccountFile); err != nil { 208 errs = packer.MultiErrorAppend(errs, err) 209 } 210 } 211 212 if c.OmitExternalIP && c.Address != "" { 213 errs = packer.MultiErrorAppend(fmt.Errorf("you can not specify an external address when 'omit_external_ip' is true")) 214 } 215 216 if c.OmitExternalIP && !c.UseInternalIP { 217 errs = packer.MultiErrorAppend(fmt.Errorf("'use_internal_ip' must be true if 'omit_external_ip' is true")) 218 } 219 220 if c.AcceleratorCount > 0 && len(c.AcceleratorType) == 0 { 221 errs = packer.MultiErrorAppend(fmt.Errorf("'accelerator_type' must be set when 'accelerator_count' is more than 0")) 222 } 223 224 if c.AcceleratorCount > 0 && c.OnHostMaintenance != "TERMINATE" { 225 errs = packer.MultiErrorAppend(fmt.Errorf("'on_host_maintenance' must be set to 'TERMINATE' when 'accelerator_count' is more than 0")) 226 } 227 228 // If DisableDefaultServiceAccount is provided, don't allow a value for ServiceAccountEmail 229 if c.DisableDefaultServiceAccount && c.ServiceAccountEmail != "" { 230 errs = packer.MultiErrorAppend(fmt.Errorf("you may not specify a 'service_account_email' when 'disable_default_service_account' is true")) 231 } 232 233 // Check for any errors. 234 if errs != nil && len(errs.Errors) > 0 { 235 return nil, nil, errs 236 } 237 238 return c, nil, nil 239 } 240 241 func (c *Config) CalcTimeout() error { 242 stateTimeout, err := time.ParseDuration(c.RawStateTimeout) 243 if err != nil { 244 return fmt.Errorf("Failed parsing state_timeout: %s", err) 245 } 246 c.stateTimeout = stateTimeout 247 return nil 248 }