github.com/rahart/packer@v0.12.2-0.20161229105310-282bb6ad370f/builder/googlecompute/config.go (about) 1 package googlecompute 2 3 import ( 4 "errors" 5 "fmt" 6 "regexp" 7 "time" 8 9 "github.com/mitchellh/packer/common" 10 "github.com/mitchellh/packer/common/uuid" 11 "github.com/mitchellh/packer/helper/communicator" 12 "github.com/mitchellh/packer/helper/config" 13 "github.com/mitchellh/packer/packer" 14 "github.com/mitchellh/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 Address string `mapstructure:"address"` 30 DiskName string `mapstructure:"disk_name"` 31 DiskSizeGb int64 `mapstructure:"disk_size"` 32 DiskType string `mapstructure:"disk_type"` 33 ImageName string `mapstructure:"image_name"` 34 ImageDescription string `mapstructure:"image_description"` 35 ImageFamily string `mapstructure:"image_family"` 36 InstanceName string `mapstructure:"instance_name"` 37 MachineType string `mapstructure:"machine_type"` 38 Metadata map[string]string `mapstructure:"metadata"` 39 Network string `mapstructure:"network"` 40 NetworkProjectId string `mapstructure:"network_project_id"` 41 OmitExternalIP bool `mapstructure:"omit_external_ip"` 42 Preemptible bool `mapstructure:"preemptible"` 43 RawStateTimeout string `mapstructure:"state_timeout"` 44 Region string `mapstructure:"region"` 45 Scopes []string `mapstructure:"scopes"` 46 SourceImage string `mapstructure:"source_image"` 47 SourceImageFamily string `mapstructure:"source_image_family"` 48 SourceImageProjectId string `mapstructure:"source_image_project_id"` 49 StartupScriptFile string `mapstructure:"startup_script_file"` 50 Subnetwork string `mapstructure:"subnetwork"` 51 Tags []string `mapstructure:"tags"` 52 UseInternalIP bool `mapstructure:"use_internal_ip"` 53 Zone string `mapstructure:"zone"` 54 55 Account AccountFile 56 privateKeyBytes []byte 57 stateTimeout time.Duration 58 imageAlreadyExists bool 59 ctx interpolate.Context 60 } 61 62 func NewConfig(raws ...interface{}) (*Config, []string, error) { 63 c := new(Config) 64 err := config.Decode(c, &config.DecodeOpts{ 65 Interpolate: true, 66 InterpolateContext: &c.ctx, 67 InterpolateFilter: &interpolate.RenderFilter{ 68 Exclude: []string{ 69 "run_command", 70 }, 71 }, 72 }, raws...) 73 if err != nil { 74 return nil, nil, err 75 } 76 77 var errs *packer.MultiError 78 79 // Set defaults. 80 if c.Network == "" { 81 c.Network = "default" 82 } 83 84 if c.DiskSizeGb == 0 { 85 c.DiskSizeGb = 10 86 } 87 88 if c.DiskType == "" { 89 c.DiskType = "pd-standard" 90 } 91 92 if c.ImageDescription == "" { 93 c.ImageDescription = "Created by Packer" 94 } 95 96 if c.ImageName == "" { 97 img, err := interpolate.Render("packer-{{timestamp}}", nil) 98 if err != nil { 99 errs = packer.MultiErrorAppend(errs, 100 fmt.Errorf("Unable to parse image name: %s ", err)) 101 } else { 102 c.ImageName = img 103 } 104 } 105 106 if len(c.ImageFamily) > 63 { 107 errs = packer.MultiErrorAppend(errs, 108 errors.New("Invalid image family: Must not be longer than 63 characters")) 109 } 110 111 if c.ImageFamily != "" { 112 if !reImageFamily.MatchString(c.ImageFamily) { 113 errs = packer.MultiErrorAppend(errs, 114 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")) 115 } 116 117 } 118 119 if c.InstanceName == "" { 120 c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) 121 } 122 123 if c.DiskName == "" { 124 c.DiskName = c.InstanceName 125 } 126 127 if c.MachineType == "" { 128 c.MachineType = "n1-standard-1" 129 } 130 131 if c.RawStateTimeout == "" { 132 c.RawStateTimeout = "5m" 133 } 134 135 if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { 136 errs = packer.MultiErrorAppend(errs, es...) 137 } 138 139 // Process required parameters. 140 if c.ProjectId == "" { 141 errs = packer.MultiErrorAppend( 142 errs, errors.New("a project_id must be specified")) 143 } 144 145 if c.Scopes == nil { 146 c.Scopes = []string{ 147 "https://www.googleapis.com/auth/userinfo.email", 148 "https://www.googleapis.com/auth/compute", 149 "https://www.googleapis.com/auth/devstorage.full_control", 150 } 151 } 152 153 if c.SourceImage == "" && c.SourceImageFamily == "" { 154 errs = packer.MultiErrorAppend( 155 errs, errors.New("a source_image or source_image_family must be specified")) 156 } 157 158 if c.Zone == "" { 159 errs = packer.MultiErrorAppend( 160 errs, errors.New("a zone must be specified")) 161 } 162 if c.Region == "" && len(c.Zone) > 2 { 163 // get region from Zone 164 region := c.Zone[:len(c.Zone)-2] 165 c.Region = region 166 } 167 168 err = c.CalcTimeout() 169 if err != nil { 170 errs = packer.MultiErrorAppend(errs, err) 171 } 172 173 if c.AccountFile != "" { 174 if err := ProcessAccountFile(&c.Account, c.AccountFile); err != nil { 175 errs = packer.MultiErrorAppend(errs, err) 176 } 177 } 178 179 if c.OmitExternalIP && c.Address != "" { 180 errs = packer.MultiErrorAppend(fmt.Errorf("you can not specify an external address when 'omit_external_ip' is true")) 181 } 182 183 if c.OmitExternalIP && !c.UseInternalIP { 184 errs = packer.MultiErrorAppend(fmt.Errorf("'use_internal_ip' must be true if 'omit_external_ip' is true")) 185 } 186 187 // Check for any errors. 188 if errs != nil && len(errs.Errors) > 0 { 189 return nil, nil, errs 190 } 191 192 return c, nil, nil 193 } 194 195 func (c *Config) CalcTimeout() error { 196 stateTimeout, err := time.ParseDuration(c.RawStateTimeout) 197 if err != nil { 198 return fmt.Errorf("Failed parsing state_timeout: %s", err) 199 } 200 c.stateTimeout = stateTimeout 201 return nil 202 }