2013-12-12 16:41:44 -08:00
package googlecompute
import (
"errors"
"fmt"
2016-05-06 14:39:55 +02:00
"regexp"
2013-12-12 16:41:44 -08:00
"time"
"github.com/mitchellh/packer/common"
2014-04-26 11:12:43 -07:00
"github.com/mitchellh/packer/common/uuid"
2015-06-13 18:30:16 -04:00
"github.com/mitchellh/packer/helper/communicator"
2015-05-27 12:57:48 -07:00
"github.com/mitchellh/packer/helper/config"
2013-12-12 16:41:44 -08:00
"github.com/mitchellh/packer/packer"
2015-05-27 12:57:48 -07:00
"github.com/mitchellh/packer/template/interpolate"
2013-12-12 16:41:44 -08:00
)
2016-05-13 15:37:25 -07:00
var reImageFamily = regexp . MustCompile ( `^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$` )
2013-12-12 16:41:44 -08:00
// Config is the configuration structure for the GCE builder. It stores
// both the publicly settable state as well as the privately generated
// state of the config object.
type Config struct {
common . PackerConfig `mapstructure:",squash"`
2015-06-13 18:30:16 -04:00
Comm communicator . Config `mapstructure:",squash"`
2013-12-12 16:41:44 -08:00
2014-11-24 08:36:14 -08:00
AccountFile string `mapstructure:"account_file"`
ProjectId string `mapstructure:"project_id"`
2014-09-05 09:47:20 -07:00
2016-05-24 17:13:36 -07:00
Address string `mapstructure:"address"`
2014-11-24 08:36:14 -08:00
DiskName string `mapstructure:"disk_name"`
2014-08-20 10:20:28 -07:00
DiskSizeGb int64 `mapstructure:"disk_size"`
2015-10-13 20:18:26 -04:00
DiskType string `mapstructure:"disk_type"`
2014-08-20 10:20:28 -07:00
ImageName string `mapstructure:"image_name"`
ImageDescription string `mapstructure:"image_description"`
2016-05-06 13:43:39 +02:00
ImageFamily string `mapstructure:"image_family"`
2014-08-20 10:20:28 -07:00
InstanceName string `mapstructure:"instance_name"`
MachineType string `mapstructure:"machine_type"`
Metadata map [ string ] string `mapstructure:"metadata"`
Network string `mapstructure:"network"`
2016-08-02 13:43:04 -07:00
OmitExternalIP bool `mapstructure:"omit_external_ip"`
2015-12-05 05:13:35 +09:00
Preemptible bool `mapstructure:"preemptible"`
2016-05-24 17:13:36 -07:00
RawStateTimeout string `mapstructure:"state_timeout"`
Region string `mapstructure:"region"`
2014-08-20 10:20:28 -07:00
SourceImage string `mapstructure:"source_image"`
SourceImageProjectId string `mapstructure:"source_image_project_id"`
2016-05-24 17:13:36 -07:00
StartupScriptFile string `mapstructure:"startup_script_file"`
Subnetwork string `mapstructure:"subnetwork"`
2014-08-20 10:20:28 -07:00
Tags [] string `mapstructure:"tags"`
2015-05-29 14:50:11 -07:00
UseInternalIP bool `mapstructure:"use_internal_ip"`
2014-08-20 10:20:28 -07:00
Zone string `mapstructure:"zone"`
2013-12-12 16:41:44 -08:00
2016-09-23 14:21:43 +02:00
Account AccountFile
privateKeyBytes [] byte
stateTimeout time . Duration
imageAlreadyExists bool
ctx interpolate . Context
2013-12-12 16:41:44 -08:00
}
func NewConfig ( raws ... interface {}) ( * Config , [] string , error ) {
c := new ( Config )
2015-06-12 14:00:59 -07:00
err := config . Decode ( c , & config . DecodeOpts {
2015-06-22 09:22:42 -07:00
Interpolate : true ,
InterpolateContext : & c . ctx ,
2015-05-27 12:57:48 -07:00
InterpolateFilter : & interpolate . RenderFilter {
Exclude : [] string {
"run_command" ,
},
},
}, raws ... )
2013-12-12 16:41:44 -08:00
if err != nil {
return nil , nil , err
}
2015-07-07 17:12:21 -06:00
var errs * packer . MultiError
2013-12-12 16:41:44 -08:00
// Set defaults.
if c . Network == "" {
c . Network = "default"
}
2014-08-07 15:34:08 -04:00
if c . DiskSizeGb == 0 {
c . DiskSizeGb = 10
}
2015-10-13 20:18:26 -04:00
if c . DiskType == "" {
c . DiskType = "pd-standard"
}
2013-12-12 16:41:44 -08:00
if c . ImageDescription == "" {
c . ImageDescription = "Created by Packer"
}
if c . ImageName == "" {
2015-07-06 09:32:08 +00:00
img , err := interpolate . Render ( "packer-{{timestamp}}" , nil )
if err != nil {
2015-07-07 17:12:21 -06:00
errs = packer . MultiErrorAppend ( errs ,
fmt . Errorf ( "Unable to parse image name: %s " , err ))
2016-01-19 11:12:19 -08:00
} else {
2015-07-07 17:12:21 -06:00
c . ImageName = img
2015-07-06 09:32:08 +00:00
}
2013-12-12 16:41:44 -08:00
}
2016-05-06 14:39:55 +02:00
if len ( c . ImageFamily ) > 63 {
errs = packer . MultiErrorAppend ( errs ,
errors . New ( "Invalid image family: Must not be longer than 63 characters" ))
}
if c . ImageFamily != "" {
2016-05-13 15:37:25 -07:00
if ! reImageFamily . MatchString ( c . ImageFamily ) {
2016-05-06 14:39:55 +02:00
errs = packer . MultiErrorAppend ( errs ,
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" ))
}
}
2014-04-26 12:14:53 +10:00
if c . InstanceName == "" {
c . InstanceName = fmt . Sprintf ( "packer-%s" , uuid . TimeOrderedUUID ())
}
2014-11-24 08:36:14 -08:00
if c . DiskName == "" {
c . DiskName = c . InstanceName
}
2013-12-12 16:41:44 -08:00
if c . MachineType == "" {
c . MachineType = "n1-standard-1"
}
if c . RawStateTimeout == "" {
c . RawStateTimeout = "5m"
}
2015-06-13 18:30:16 -04:00
if c . Comm . SSHUsername == "" {
c . Comm . SSHUsername = "root"
2013-12-12 16:41:44 -08:00
}
2015-06-29 09:56:33 -07:00
if es := c . Comm . Prepare ( & c . ctx ); len ( es ) > 0 {
errs = packer . MultiErrorAppend ( errs , es ... )
}
2013-12-12 16:41:44 -08:00
// Process required parameters.
if c . ProjectId == "" {
errs = packer . MultiErrorAppend (
errs , errors . New ( "a project_id must be specified" ))
}
if c . SourceImage == "" {
errs = packer . MultiErrorAppend (
errs , errors . New ( "a source_image must be specified" ))
}
if c . Zone == "" {
errs = packer . MultiErrorAppend (
errs , errors . New ( "a zone must be specified" ))
}
2016-02-11 17:31:46 +13:00
if c . Region == "" && len ( c . Zone ) > 2 {
// get region from Zone
region := c . Zone [: len ( c . Zone ) - 2 ]
c . Region = region
}
2013-12-12 16:41:44 -08:00
2016-07-07 17:50:46 -04:00
err = c . CalcTimeout ()
2013-12-12 16:41:44 -08:00
if err != nil {
2016-07-07 17:50:46 -04:00
errs = packer . MultiErrorAppend ( errs , err )
2013-12-12 16:41:44 -08:00
}
2014-09-05 09:47:20 -07:00
if c . AccountFile != "" {
2016-07-07 17:50:46 -04:00
if err := ProcessAccountFile ( & c . Account , c . AccountFile ); err != nil {
2015-10-08 00:36:31 -04:00
errs = packer . MultiErrorAppend ( errs , err )
2013-12-13 19:32:01 -08:00
}
2013-12-12 16:41:44 -08:00
}
2016-08-02 13:43:04 -07:00
if c . OmitExternalIP && c . Address != "" {
errs = packer . MultiErrorAppend ( fmt . Errorf ( "you can not specify an external address when 'omit_external_ip' is true" ))
}
if c . OmitExternalIP && ! c . UseInternalIP {
errs = packer . MultiErrorAppend ( fmt . Errorf ( "'use_internal_ip' must be true if 'omit_external_ip' is true" ))
}
2013-12-12 16:41:44 -08:00
// Check for any errors.
if errs != nil && len ( errs . Errors ) > 0 {
return nil , nil , errs
}
return c , nil , nil
}
2016-07-07 17:50:46 -04:00
func ( c * Config ) CalcTimeout () error {
stateTimeout , err := time . ParseDuration ( c . RawStateTimeout )
if err != nil {
return fmt . Errorf ( "Failed parsing state_timeout: %s" , err )
}
c . stateTimeout = stateTimeout
return nil
2016-09-23 14:21:43 +02:00
}