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