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