From 9b39e3f92859cdc115999a2d1299f91334bbdacb Mon Sep 17 00:00:00 2001 From: krisko Date: Tue, 23 Apr 2019 19:21:52 +0200 Subject: [PATCH 1/4] GCP builder add image encryption support --- builder/googlecompute/artifact.go | 2 +- builder/googlecompute/builder.go | 2 +- builder/googlecompute/config.go | 271 ++-------------- builder/googlecompute/config_gce.go | 291 ++++++++++++++++++ builder/googlecompute/config_test.go | 8 +- builder/googlecompute/driver.go | 2 +- builder/googlecompute/driver_gce.go | 20 +- .../step_check_existing_image.go | 2 +- .../step_check_existing_image_test.go | 2 +- builder/googlecompute/step_create_image.go | 6 +- .../googlecompute/step_create_image_test.go | 2 +- builder/googlecompute/step_create_instance.go | 8 +- .../step_create_instance_test.go | 18 +- builder/googlecompute/step_create_ssh_key.go | 2 +- .../googlecompute/step_create_ssh_key_test.go | 6 +- .../step_create_windows_password.go | 2 +- .../step_create_windows_password_test.go | 2 +- builder/googlecompute/step_instance_info.go | 2 +- .../googlecompute/step_instance_info_test.go | 6 +- .../googlecompute/step_teardown_instance.go | 4 +- .../step_teardown_instance_test.go | 2 +- .../googlecompute/step_wait_startup_script.go | 2 +- .../step_wait_startup_script_test.go | 2 +- builder/googlecompute/winrm.go | 2 +- .../googlecompute-export/post-processor.go | 2 +- 25 files changed, 366 insertions(+), 302 deletions(-) create mode 100644 builder/googlecompute/config_gce.go diff --git a/builder/googlecompute/artifact.go b/builder/googlecompute/artifact.go index 9d98d6566..d4f7814b1 100644 --- a/builder/googlecompute/artifact.go +++ b/builder/googlecompute/artifact.go @@ -9,7 +9,7 @@ import ( type Artifact struct { image *Image driver Driver - config *Config + config *ConfigGCE } // BuilderId returns the builder Id. diff --git a/builder/googlecompute/builder.go b/builder/googlecompute/builder.go index 9a2b4fecc..cf4b0cb4a 100644 --- a/builder/googlecompute/builder.go +++ b/builder/googlecompute/builder.go @@ -18,7 +18,7 @@ const BuilderId = "packer.googlecompute" // Builder represents a Packer Builder. type Builder struct { - config *Config + config *ConfigGCE runner multistep.Runner } diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index 40d0094c8..f4e473cd8 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -1,257 +1,32 @@ package googlecompute -import ( - "errors" - "fmt" - "os" - "regexp" - "time" +import compute "google.golang.org/api/compute/v1" - "github.com/hashicorp/packer/common" - "github.com/hashicorp/packer/common/uuid" - "github.com/hashicorp/packer/helper/communicator" - "github.com/hashicorp/packer/helper/config" - "github.com/hashicorp/packer/packer" - "github.com/hashicorp/packer/template/interpolate" -) +// Configis the interface that has to be implemented to pass +//configuration for GCE. The Config interface exists mostly to allow a mock implementation +// to be used to test the steps. +type Config interface { + //GetImageDescription gets image desription for the image. + GetImageDescription() string -var reImageFamily = regexp.MustCompile(`^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$`) + //GetImageName gets image name for the image. + GetImageName() string -// 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"` - Comm communicator.Config `mapstructure:",squash"` + //GGetImageFamily gets image family for the image. + GetImageFamily() string - AccountFile string `mapstructure:"account_file"` - ProjectId string `mapstructure:"project_id"` + //GetImageLabels gets list of labels for the image. + GetImageLabels() map[string]string - AcceleratorType string `mapstructure:"accelerator_type"` - AcceleratorCount int64 `mapstructure:"accelerator_count"` - Address string `mapstructure:"address"` - DisableDefaultServiceAccount bool `mapstructure:"disable_default_service_account"` - DiskName string `mapstructure:"disk_name"` - DiskSizeGb int64 `mapstructure:"disk_size"` - DiskType string `mapstructure:"disk_type"` - ImageName string `mapstructure:"image_name"` - ImageDescription string `mapstructure:"image_description"` - ImageFamily string `mapstructure:"image_family"` - ImageLabels map[string]string `mapstructure:"image_labels"` - ImageLicenses []string `mapstructure:"image_licenses"` - InstanceName string `mapstructure:"instance_name"` - Labels map[string]string `mapstructure:"labels"` - MachineType string `mapstructure:"machine_type"` - Metadata map[string]string `mapstructure:"metadata"` - MinCpuPlatform string `mapstructure:"min_cpu_platform"` - Network string `mapstructure:"network"` - NetworkProjectId string `mapstructure:"network_project_id"` - OmitExternalIP bool `mapstructure:"omit_external_ip"` - OnHostMaintenance string `mapstructure:"on_host_maintenance"` - Preemptible bool `mapstructure:"preemptible"` - RawStateTimeout string `mapstructure:"state_timeout"` - Region string `mapstructure:"region"` - Scopes []string `mapstructure:"scopes"` - ServiceAccountEmail string `mapstructure:"service_account_email"` - SourceImage string `mapstructure:"source_image"` - SourceImageFamily string `mapstructure:"source_image_family"` - SourceImageProjectId string `mapstructure:"source_image_project_id"` - StartupScriptFile string `mapstructure:"startup_script_file"` - Subnetwork string `mapstructure:"subnetwork"` - Tags []string `mapstructure:"tags"` - UseInternalIP bool `mapstructure:"use_internal_ip"` - Zone string `mapstructure:"zone"` + //GetImageDescription gets image desription for the image. + GetImageLicenses() []string - Account AccountFile - stateTimeout time.Duration - imageAlreadyExists bool - ctx interpolate.Context -} - -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) - c.ctx.Funcs = TemplateFuncs - err := config.Decode(c, &config.DecodeOpts{ - Interpolate: true, - InterpolateContext: &c.ctx, - InterpolateFilter: &interpolate.RenderFilter{ - Exclude: []string{ - "run_command", - }, - }, - }, raws...) - if err != nil { - return nil, nil, err - } - - var errs *packer.MultiError - - // Set defaults. - if c.Network == "" && c.Subnetwork == "" { - c.Network = "default" - } - - if c.NetworkProjectId == "" { - c.NetworkProjectId = c.ProjectId - } - - if c.DiskSizeGb == 0 { - c.DiskSizeGb = 10 - } - - if c.DiskType == "" { - c.DiskType = "pd-standard" - } - - if c.ImageDescription == "" { - c.ImageDescription = "Created by Packer" - } - - if c.OnHostMaintenance == "MIGRATE" && c.Preemptible { - errs = packer.MultiErrorAppend(errs, - errors.New("on_host_maintenance must be TERMINATE when using preemptible instances.")) - } - // Setting OnHostMaintenance Correct Defaults - // "MIGRATE" : Possible and default if Preemptible is false - // "TERMINATE": Required if Preemptible is true - if c.Preemptible { - c.OnHostMaintenance = "TERMINATE" - } else { - if c.OnHostMaintenance == "" { - c.OnHostMaintenance = "MIGRATE" - } - } - - // Make sure user sets a valid value for on_host_maintenance option - if !(c.OnHostMaintenance == "MIGRATE" || c.OnHostMaintenance == "TERMINATE") { - errs = packer.MultiErrorAppend(errs, - errors.New("on_host_maintenance must be one of MIGRATE or TERMINATE.")) - } - - if c.ImageName == "" { - img, err := interpolate.Render("packer-{{timestamp}}", nil) - if err != nil { - errs = packer.MultiErrorAppend(errs, - fmt.Errorf("Unable to parse image name: %s ", err)) - } else { - c.ImageName = img - } - } - - if len(c.ImageFamily) > 63 { - errs = packer.MultiErrorAppend(errs, - errors.New("Invalid image family: Must not be longer than 63 characters")) - } - - if c.ImageFamily != "" { - if !reImageFamily.MatchString(c.ImageFamily) { - 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")) - } - - } - - if c.InstanceName == "" { - c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) - } - - if c.DiskName == "" { - c.DiskName = c.InstanceName - } - - if c.MachineType == "" { - c.MachineType = "n1-standard-1" - } - - if c.RawStateTimeout == "" { - c.RawStateTimeout = "5m" - } - - if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { - errs = packer.MultiErrorAppend(errs, es...) - } - - // Process required parameters. - if c.ProjectId == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("a project_id must be specified")) - } - - if c.Scopes == nil { - c.Scopes = []string{ - "https://www.googleapis.com/auth/userinfo.email", - "https://www.googleapis.com/auth/compute", - "https://www.googleapis.com/auth/devstorage.full_control", - } - } - - if c.SourceImage == "" && c.SourceImageFamily == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("a source_image or source_image_family must be specified")) - } - - if c.Zone == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("a zone must be specified")) - } - if c.Region == "" && len(c.Zone) > 2 { - // get region from Zone - region := c.Zone[:len(c.Zone)-2] - c.Region = region - } - - err = c.CalcTimeout() - if err != nil { - errs = packer.MultiErrorAppend(errs, err) - } - - if c.AccountFile != "" { - if err := ProcessAccountFile(&c.Account, c.AccountFile); err != nil { - errs = packer.MultiErrorAppend(errs, err) - } - } - - 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")) - } - - if c.AcceleratorCount > 0 && len(c.AcceleratorType) == 0 { - errs = packer.MultiErrorAppend(fmt.Errorf("'accelerator_type' must be set when 'accelerator_count' is more than 0")) - } - - if c.AcceleratorCount > 0 && c.OnHostMaintenance != "TERMINATE" { - errs = packer.MultiErrorAppend(fmt.Errorf("'on_host_maintenance' must be set to 'TERMINATE' when 'accelerator_count' is more than 0")) - } - - // If DisableDefaultServiceAccount is provided, don't allow a value for ServiceAccountEmail - if c.DisableDefaultServiceAccount && c.ServiceAccountEmail != "" { - errs = packer.MultiErrorAppend(fmt.Errorf("you may not specify a 'service_account_email' when 'disable_default_service_account' is true")) - } - - if c.StartupScriptFile != "" { - if _, err := os.Stat(c.StartupScriptFile); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("startup_script_file: %v", err)) - } - } - - // Check for any errors. - if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs - } - - return c, nil, nil -} - -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 + //GetZone gets image zone for the image. + GetZone() string + + //GetDiskName gets disk name for the image. + GetDiskName() string + + //GetImageEncryptionKey gets image encryption key for the image. + GetImageEncryptionKey() *compute.CustomerEncryptionKey } diff --git a/builder/googlecompute/config_gce.go b/builder/googlecompute/config_gce.go new file mode 100644 index 000000000..c285784ec --- /dev/null +++ b/builder/googlecompute/config_gce.go @@ -0,0 +1,291 @@ +package googlecompute + +import ( + "errors" + "fmt" + "os" + "regexp" + "time" + + "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/common/uuid" + "github.com/hashicorp/packer/helper/communicator" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/template/interpolate" + compute "google.golang.org/api/compute/v1" +) + +var reImageFamily = regexp.MustCompile(`^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$`) + +// 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 ConfigGCE struct { + common.PackerConfig `mapstructure:",squash"` + Comm communicator.Config `mapstructure:",squash"` + + AccountFile string `mapstructure:"account_file"` + ProjectId string `mapstructure:"project_id"` + + AcceleratorType string `mapstructure:"accelerator_type"` + AcceleratorCount int64 `mapstructure:"accelerator_count"` + Address string `mapstructure:"address"` + DisableDefaultServiceAccount bool `mapstructure:"disable_default_service_account"` + DiskName string `mapstructure:"disk_name"` + DiskSizeGb int64 `mapstructure:"disk_size"` + DiskType string `mapstructure:"disk_type"` + ImageName string `mapstructure:"image_name"` + ImageDescription string `mapstructure:"image_description"` + ImageFamily string `mapstructure:"image_family"` + ImageLabels map[string]string `mapstructure:"image_labels"` + ImageEncryptionKey *compute.CustomerEncryptionKey `mapstructure:"image_encryption_key"` + ImageLicenses []string `mapstructure:"image_licenses"` + InstanceName string `mapstructure:"instance_name"` + Labels map[string]string `mapstructure:"labels"` + MachineType string `mapstructure:"machine_type"` + Metadata map[string]string `mapstructure:"metadata"` + MinCpuPlatform string `mapstructure:"min_cpu_platform"` + Network string `mapstructure:"network"` + NetworkProjectId string `mapstructure:"network_project_id"` + OmitExternalIP bool `mapstructure:"omit_external_ip"` + OnHostMaintenance string `mapstructure:"on_host_maintenance"` + Preemptible bool `mapstructure:"preemptible"` + RawStateTimeout string `mapstructure:"state_timeout"` + Region string `mapstructure:"region"` + Scopes []string `mapstructure:"scopes"` + ServiceAccountEmail string `mapstructure:"service_account_email"` + SourceImage string `mapstructure:"source_image"` + SourceImageFamily string `mapstructure:"source_image_family"` + SourceImageProjectId string `mapstructure:"source_image_project_id"` + StartupScriptFile string `mapstructure:"startup_script_file"` + Subnetwork string `mapstructure:"subnetwork"` + Tags []string `mapstructure:"tags"` + UseInternalIP bool `mapstructure:"use_internal_ip"` + Zone string `mapstructure:"zone"` + + Account AccountFile + stateTimeout time.Duration + imageAlreadyExists bool + ctx interpolate.Context +} + +func NewConfig(raws ...interface{}) (*ConfigGCE, []string, error) { + c := new(ConfigGCE) + c.ctx.Funcs = TemplateFuncs + err := config.Decode(c, &config.DecodeOpts{ + Interpolate: true, + InterpolateContext: &c.ctx, + InterpolateFilter: &interpolate.RenderFilter{ + Exclude: []string{ + "run_command", + }, + }, + }, raws...) + if err != nil { + return nil, nil, err + } + + var errs *packer.MultiError + + // Set defaults. + if c.Network == "" && c.Subnetwork == "" { + c.Network = "default" + } + + if c.NetworkProjectId == "" { + c.NetworkProjectId = c.ProjectId + } + + if c.DiskSizeGb == 0 { + c.DiskSizeGb = 10 + } + + if c.DiskType == "" { + c.DiskType = "pd-standard" + } + + if c.ImageDescription == "" { + c.ImageDescription = "Created by Packer" + } + + if c.OnHostMaintenance == "MIGRATE" && c.Preemptible { + errs = packer.MultiErrorAppend(errs, + errors.New("on_host_maintenance must be TERMINATE when using preemptible instances.")) + } + // Setting OnHostMaintenance Correct Defaults + // "MIGRATE" : Possible and default if Preemptible is false + // "TERMINATE": Required if Preemptible is true + if c.Preemptible { + c.OnHostMaintenance = "TERMINATE" + } else { + if c.OnHostMaintenance == "" { + c.OnHostMaintenance = "MIGRATE" + } + } + + // Make sure user sets a valid value for on_host_maintenance option + if !(c.OnHostMaintenance == "MIGRATE" || c.OnHostMaintenance == "TERMINATE") { + errs = packer.MultiErrorAppend(errs, + errors.New("on_host_maintenance must be one of MIGRATE or TERMINATE.")) + } + + if c.ImageName == "" { + img, err := interpolate.Render("packer-{{timestamp}}", nil) + if err != nil { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Unable to parse image name: %s ", err)) + } else { + c.ImageName = img + } + } + + if len(c.ImageFamily) > 63 { + errs = packer.MultiErrorAppend(errs, + errors.New("Invalid image family: Must not be longer than 63 characters")) + } + + if c.ImageFamily != "" { + if !reImageFamily.MatchString(c.ImageFamily) { + 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")) + } + + } + + if c.InstanceName == "" { + c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) + } + + if c.DiskName == "" { + c.DiskName = c.InstanceName + } + + if c.MachineType == "" { + c.MachineType = "n1-standard-1" + } + + if c.RawStateTimeout == "" { + c.RawStateTimeout = "5m" + } + + if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { + errs = packer.MultiErrorAppend(errs, es...) + } + + // Process required parameters. + if c.ProjectId == "" { + errs = packer.MultiErrorAppend( + errs, errors.New("a project_id must be specified")) + } + + if c.Scopes == nil { + c.Scopes = []string{ + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/compute", + "https://www.googleapis.com/auth/devstorage.full_control", + } + } + + if c.SourceImage == "" && c.SourceImageFamily == "" { + errs = packer.MultiErrorAppend( + errs, errors.New("a source_image or source_image_family must be specified")) + } + + if c.Zone == "" { + errs = packer.MultiErrorAppend( + errs, errors.New("a zone must be specified")) + } + if c.Region == "" && len(c.Zone) > 2 { + // get region from Zone + region := c.Zone[:len(c.Zone)-2] + c.Region = region + } + + err = c.CalcTimeout() + if err != nil { + errs = packer.MultiErrorAppend(errs, err) + } + + if c.AccountFile != "" { + if err := ProcessAccountFile(&c.Account, c.AccountFile); err != nil { + errs = packer.MultiErrorAppend(errs, err) + } + } + + 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")) + } + + if c.AcceleratorCount > 0 && len(c.AcceleratorType) == 0 { + errs = packer.MultiErrorAppend(fmt.Errorf("'accelerator_type' must be set when 'accelerator_count' is more than 0")) + } + + if c.AcceleratorCount > 0 && c.OnHostMaintenance != "TERMINATE" { + errs = packer.MultiErrorAppend(fmt.Errorf("'on_host_maintenance' must be set to 'TERMINATE' when 'accelerator_count' is more than 0")) + } + + // If DisableDefaultServiceAccount is provided, don't allow a value for ServiceAccountEmail + if c.DisableDefaultServiceAccount && c.ServiceAccountEmail != "" { + errs = packer.MultiErrorAppend(fmt.Errorf("you may not specify a 'service_account_email' when 'disable_default_service_account' is true")) + } + + if c.StartupScriptFile != "" { + if _, err := os.Stat(c.StartupScriptFile); err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("startup_script_file: %v", err)) + } + } + + // Check for any errors. + if errs != nil && len(errs.Errors) > 0 { + return nil, nil, errs + } + + return c, nil, nil +} + +func (c *ConfigGCE) 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 +} + +func (c *ConfigGCE) GetImageDescription() string { + return c.ImageDescription +} + +func (c *ConfigGCE) GetImageName() string { + return c.ImageName +} + +func (c *ConfigGCE) GetImageFamily() string { + return c.ImageFamily +} + +func (c *ConfigGCE) GetImageLabels() map[string]string { + return c.ImageLabels +} + +func (c *ConfigGCE) GetImageLicenses() []string { + return c.ImageLicenses +} + +func (c *ConfigGCE) GetDiskName() string { + return c.DiskName +} + +func (c *ConfigGCE) GetZone() string { + return c.Zone +} + +func (c *ConfigGCE) GetImageEncryptionKey() *compute.CustomerEncryptionKey { + return c.ImageEncryptionKey +} diff --git a/builder/googlecompute/config_test.go b/builder/googlecompute/config_test.go index 0d3422a6e..77086a413 100644 --- a/builder/googlecompute/config_test.go +++ b/builder/googlecompute/config_test.go @@ -347,16 +347,16 @@ func TestConfigPrepareStartupScriptFile(t *testing.T) { func TestConfigDefaults(t *testing.T) { cases := []struct { - Read func(c *Config) interface{} + Read func(c *ConfigGCE) interface{} Value interface{} }{ { - func(c *Config) interface{} { return c.Comm.Type }, + func(c *ConfigGCE) interface{} { return c.Comm.Type }, "ssh", }, { - func(c *Config) interface{} { return c.Comm.SSHPort }, + func(c *ConfigGCE) interface{} { return c.Comm.SSHPort }, 22, }, } @@ -422,7 +422,7 @@ func testConfig(t *testing.T) (config map[string]interface{}, tempAccountFile st return config, tempAccountFile } -func testConfigStruct(t *testing.T) *Config { +func testConfigStruct(t *testing.T) *ConfigGCE { raw, tempfile := testConfig(t) defer os.Remove(tempfile) diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index af3daf4ee..f4bdcd20d 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -11,7 +11,7 @@ import ( type Driver interface { // CreateImage creates an image from the given disk in Google Compute // Engine. - CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string) (<-chan *Image, <-chan error) + CreateImage(config Config) (<-chan *Image, <-chan error) // DeleteImage deletes the image with the given name. DeleteImage(name string) <-chan error diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index 153e7ac20..30bb16d6a 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -104,17 +104,17 @@ func NewDriverGCE(ui packer.Ui, p string, a *AccountFile) (Driver, error) { }, nil } -func (d *driverGCE) CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string) (<-chan *Image, <-chan error) { +func (d *driverGCE) CreateImage(config Config) (<-chan *Image, <-chan error) { gce_image := &compute.Image{ - Description: description, - Name: name, - Family: family, - Labels: image_labels, - Licenses: image_licenses, - SourceDisk: fmt.Sprintf("%s%s/zones/%s/disks/%s", d.service.BasePath, d.projectId, zone, disk), - SourceType: "RAW", + Description: config.GetImageDescription(), + ImageEncryptionKey: config.GetImageEncryptionKey(), + Name: config.GetImageName(), + Family: config.GetImageFamily(), + Labels: config.GetImageLabels(), + Licenses: config.GetImageLicenses(), + SourceDisk: fmt.Sprintf("%s%s/zones/%s/disks/%s", d.service.BasePath, d.projectId, config.GetZone(), config.GetDiskName()), + SourceType: "RAW", } - imageCh := make(chan *Image, 1) errCh := make(chan error, 1) op, err := d.service.Images.Insert(d.projectId, gce_image).Do() @@ -129,7 +129,7 @@ func (d *driverGCE) CreateImage(name, description, family, zone, disk string, im return } var image *Image - image, err = d.GetImageFromProject(d.projectId, name, false) + image, err = d.GetImageFromProject(d.projectId, config.GetImageName(), false) if err != nil { close(imageCh) errCh <- err diff --git a/builder/googlecompute/step_check_existing_image.go b/builder/googlecompute/step_check_existing_image.go index 0574a0234..d9e4d5593 100644 --- a/builder/googlecompute/step_check_existing_image.go +++ b/builder/googlecompute/step_check_existing_image.go @@ -14,7 +14,7 @@ type StepCheckExistingImage int // Run executes the Packer build step that checks if the image already exists. func (s *StepCheckExistingImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) d := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) diff --git a/builder/googlecompute/step_check_existing_image_test.go b/builder/googlecompute/step_check_existing_image_test.go index d4b9a9541..64f063191 100644 --- a/builder/googlecompute/step_check_existing_image_test.go +++ b/builder/googlecompute/step_check_existing_image_test.go @@ -18,7 +18,7 @@ func TestStepCheckExistingImage(t *testing.T) { state.Put("instance_name", "foo") - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) driver := state.Get("driver").(*DriverMock) driver.ImageExistsResult = true diff --git a/builder/googlecompute/step_create_image.go b/builder/googlecompute/step_create_image.go index dd3770014..824be6946 100644 --- a/builder/googlecompute/step_create_image.go +++ b/builder/googlecompute/step_create_image.go @@ -19,7 +19,7 @@ type StepCreateImage int // The image is created from the persistent disk used by the instance. The // instance must be deleted and the disk retained before doing this step. func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) @@ -38,9 +38,7 @@ func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul ui.Say("Creating image...") - imageCh, errCh := driver.CreateImage( - config.ImageName, config.ImageDescription, config.ImageFamily, config.Zone, - config.DiskName, config.ImageLabels, config.ImageLicenses) + imageCh, errCh := driver.CreateImage(config) var err error select { case err = <-errCh: diff --git a/builder/googlecompute/step_create_image_test.go b/builder/googlecompute/step_create_image_test.go index 65ccca313..e870eba42 100644 --- a/builder/googlecompute/step_create_image_test.go +++ b/builder/googlecompute/step_create_image_test.go @@ -18,7 +18,7 @@ func TestStepCreateImage(t *testing.T) { step := new(StepCreateImage) defer step.Cleanup(state) - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) d := state.Get("driver").(*DriverMock) // These are the values of the image the driver will return. diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index 5ed854670..faa4ade02 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -16,7 +16,7 @@ type StepCreateInstance struct { Debug bool } -func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string) (map[string]string, error) { +func (c *ConfigGCE) createInstanceMetadata(sourceImage *Image, sshPublicKey string) (map[string]string, error) { instanceMetadata := make(map[string]string) var err error @@ -58,7 +58,7 @@ func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string) return instanceMetadata, err } -func getImage(c *Config, d Driver) (*Image, error) { +func getImage(c *ConfigGCE, d Driver) (*Image, error) { name := c.SourceImageFamily fromFamily := true if c.SourceImage != "" { @@ -74,7 +74,7 @@ func getImage(c *Config, d Driver) (*Image, error) { // Run executes the Packer build step that creates a GCE instance. func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) d := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) @@ -167,7 +167,7 @@ func (s *StepCreateInstance) Cleanup(state multistep.StateBag) { return } - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) diff --git a/builder/googlecompute/step_create_instance_test.go b/builder/googlecompute/step_create_instance_test.go index e56159138..cf00c372e 100644 --- a/builder/googlecompute/step_create_instance_test.go +++ b/builder/googlecompute/step_create_instance_test.go @@ -22,7 +22,7 @@ func TestStepCreateInstance(t *testing.T) { state.Put("ssh_public_key", "key") - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) d := state.Get("driver").(*DriverMock) d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100) @@ -61,7 +61,7 @@ func TestStepCreateInstance_fromFamily(t *testing.T) { state.Put("ssh_public_key", "key") - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) c.SourceImage = tc.Name c.SourceImageFamily = tc.Family d := state.Get("driver").(*DriverMock) @@ -89,7 +89,7 @@ func TestStepCreateInstance_windowsNeedsPassword(t *testing.T) { defer step.Cleanup(state) state.Put("ssh_public_key", "key") - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) d := state.Get("driver").(*DriverMock) d.GetImageResult = StubImage("test-image", "test-project", []string{"windows"}, 100) c.Comm.Type = "winrm" @@ -136,7 +136,7 @@ func TestStepCreateInstance_windowsPasswordSet(t *testing.T) { state.Put("ssh_public_key", "key") - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) driver := state.Get("driver").(*DriverMock) driver.GetImageResult = StubImage("test-image", "test-project", []string{"windows"}, 100) config.Comm.Type = "winrm" @@ -231,7 +231,7 @@ func TestStepCreateInstance_errorTimeout(t *testing.T) { errCh := make(chan error, 1) - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) config.stateTimeout = 1 * time.Microsecond d := state.Get("driver").(*DriverMock) @@ -255,7 +255,7 @@ func TestStepCreateInstance_noServiceAccount(t *testing.T) { state.Put("ssh_public_key", "key") - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) c.DisableDefaultServiceAccount = true c.ServiceAccountEmail = "" d := state.Get("driver").(*DriverMock) @@ -279,7 +279,7 @@ func TestStepCreateInstance_customServiceAccount(t *testing.T) { state.Put("ssh_public_key", "key") - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) c.DisableDefaultServiceAccount = true c.ServiceAccountEmail = "custom-service-account" d := state.Get("driver").(*DriverMock) @@ -298,7 +298,7 @@ func TestStepCreateInstance_customServiceAccount(t *testing.T) { func TestCreateInstanceMetadata(t *testing.T) { state := testState(t) - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) image := StubImage("test-image", "test-project", []string{}, 100) key := "abcdefgh12345678" @@ -313,7 +313,7 @@ func TestCreateInstanceMetadata(t *testing.T) { func TestCreateInstanceMetadata_noPublicKey(t *testing.T) { state := testState(t) - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) image := StubImage("test-image", "test-project", []string{}, 100) sshKeys := c.Metadata["sshKeys"] diff --git a/builder/googlecompute/step_create_ssh_key.go b/builder/googlecompute/step_create_ssh_key.go index 416657b7d..1555d61dc 100644 --- a/builder/googlecompute/step_create_ssh_key.go +++ b/builder/googlecompute/step_create_ssh_key.go @@ -24,7 +24,7 @@ type StepCreateSSHKey struct { // The key pairs are added to the ssh config func (s *StepCreateSSHKey) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) if config.Comm.SSHPrivateKeyFile != "" { ui.Say("Using existing SSH private key") diff --git a/builder/googlecompute/step_create_ssh_key_test.go b/builder/googlecompute/step_create_ssh_key_test.go index 88ba7530c..3f9e9cb57 100644 --- a/builder/googlecompute/step_create_ssh_key_test.go +++ b/builder/googlecompute/step_create_ssh_key_test.go @@ -17,7 +17,7 @@ func TestStepCreateSSHKey_impl(t *testing.T) { func TestStepCreateSSHKey_privateKey(t *testing.T) { state := testState(t) step := new(StepCreateSSHKey) - cfg := state.Get("config").(*Config) + cfg := state.Get("config").(*ConfigGCE) cfg.Comm.SSHPrivateKeyFile = "test-fixtures/fake-key" defer step.Cleanup(state) @@ -35,7 +35,7 @@ func TestStepCreateSSHKey_privateKey(t *testing.T) { func TestStepCreateSSHKey(t *testing.T) { state := testState(t) step := new(StepCreateSSHKey) - cfg := state.Get("config").(*Config) + cfg := state.Get("config").(*ConfigGCE) defer step.Cleanup(state) // run the step @@ -62,7 +62,7 @@ func TestStepCreateSSHKey_debug(t *testing.T) { state := testState(t) step := new(StepCreateSSHKey) - cfg := state.Get("config").(*Config) + cfg := state.Get("config").(*ConfigGCE) step.Debug = true step.DebugKeyPath = tf.Name() diff --git a/builder/googlecompute/step_create_windows_password.go b/builder/googlecompute/step_create_windows_password.go index fda606245..522bec8fd 100644 --- a/builder/googlecompute/step_create_windows_password.go +++ b/builder/googlecompute/step_create_windows_password.go @@ -28,7 +28,7 @@ type StepCreateWindowsPassword struct { func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) d := state.Get("driver").(Driver) - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) name := state.Get("instance_name").(string) if c.Comm.WinRMPassword != "" { diff --git a/builder/googlecompute/step_create_windows_password_test.go b/builder/googlecompute/step_create_windows_password_test.go index 6abb6243d..634b304c7 100644 --- a/builder/googlecompute/step_create_windows_password_test.go +++ b/builder/googlecompute/step_create_windows_password_test.go @@ -37,7 +37,7 @@ func TestStepCreateOrResetWindowsPassword_passwordSet(t *testing.T) { // Step is run after the instance is created so we will have an instance name set state.Put("instance_name", "mock_instance") - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) c.Comm.WinRMPassword = "password" diff --git a/builder/googlecompute/step_instance_info.go b/builder/googlecompute/step_instance_info.go index 1809fbb84..54daf3a47 100644 --- a/builder/googlecompute/step_instance_info.go +++ b/builder/googlecompute/step_instance_info.go @@ -18,7 +18,7 @@ type StepInstanceInfo struct { // Run executes the Packer build step that gathers GCE instance info. // This adds "instance_ip" to the multistep state. func (s *StepInstanceInfo) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) diff --git a/builder/googlecompute/step_instance_info_test.go b/builder/googlecompute/step_instance_info_test.go index 86f718369..fc70f8bf2 100644 --- a/builder/googlecompute/step_instance_info_test.go +++ b/builder/googlecompute/step_instance_info_test.go @@ -20,7 +20,7 @@ func TestStepInstanceInfo(t *testing.T) { state.Put("instance_name", "foo") - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) driver := state.Get("driver").(*DriverMock) driver.GetNatIPResult = "1.2.3.4" @@ -58,7 +58,7 @@ func TestStepInstanceInfo_InternalIP(t *testing.T) { state.Put("instance_name", "foo") - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) config.UseInternalIP = true driver := state.Get("driver").(*DriverMock) driver.GetNatIPResult = "1.2.3.4" @@ -155,7 +155,7 @@ func TestStepInstanceInfo_errorTimeout(t *testing.T) { state.Put("instance_name", "foo") - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) config.stateTimeout = 1 * time.Microsecond driver := state.Get("driver").(*DriverMock) diff --git a/builder/googlecompute/step_teardown_instance.go b/builder/googlecompute/step_teardown_instance.go index c992dead3..2a61d5a5a 100644 --- a/builder/googlecompute/step_teardown_instance.go +++ b/builder/googlecompute/step_teardown_instance.go @@ -18,7 +18,7 @@ type StepTeardownInstance struct { // Run executes the Packer build step that tears down a GCE instance. func (s *StepTeardownInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) @@ -55,7 +55,7 @@ func (s *StepTeardownInstance) Run(ctx context.Context, state multistep.StateBag // Deleting the instance does not remove the boot disk. This cleanup removes // the disk. func (s *StepTeardownInstance) Cleanup(state multistep.StateBag) { - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) diff --git a/builder/googlecompute/step_teardown_instance_test.go b/builder/googlecompute/step_teardown_instance_test.go index 129c4e596..aee5b4c5f 100644 --- a/builder/googlecompute/step_teardown_instance_test.go +++ b/builder/googlecompute/step_teardown_instance_test.go @@ -16,7 +16,7 @@ func TestStepTeardownInstance(t *testing.T) { step := new(StepTeardownInstance) defer step.Cleanup(state) - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) driver := state.Get("driver").(*DriverMock) // run the step diff --git a/builder/googlecompute/step_wait_startup_script.go b/builder/googlecompute/step_wait_startup_script.go index ede82ba8c..23d9807d7 100644 --- a/builder/googlecompute/step_wait_startup_script.go +++ b/builder/googlecompute/step_wait_startup_script.go @@ -16,7 +16,7 @@ type StepWaitStartupScript int // Run reads the instance metadata and looks for the log entry // indicating the startup script finished. func (s *StepWaitStartupScript) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) instanceName := state.Get("instance_name").(string) diff --git a/builder/googlecompute/step_wait_startup_script_test.go b/builder/googlecompute/step_wait_startup_script_test.go index a970dc48d..099de158c 100644 --- a/builder/googlecompute/step_wait_startup_script_test.go +++ b/builder/googlecompute/step_wait_startup_script_test.go @@ -11,7 +11,7 @@ import ( func TestStepWaitStartupScript(t *testing.T) { state := testState(t) step := new(StepWaitStartupScript) - c := state.Get("config").(*Config) + c := state.Get("config").(*ConfigGCE) d := state.Get("driver").(*DriverMock) testZone := "test-zone" diff --git a/builder/googlecompute/winrm.go b/builder/googlecompute/winrm.go index 767787401..f2cfd659b 100644 --- a/builder/googlecompute/winrm.go +++ b/builder/googlecompute/winrm.go @@ -7,7 +7,7 @@ import ( // winrmConfig returns the WinRM configuration. func winrmConfig(state multistep.StateBag) (*communicator.WinRMConfig, error) { - config := state.Get("config").(*Config) + config := state.Get("config").(*ConfigGCE) password := state.Get("winrm_password").(string) return &communicator.WinRMConfig{ diff --git a/post-processor/googlecompute-export/post-processor.go b/post-processor/googlecompute-export/post-processor.go index b0e15e296..0ac5e1ea7 100644 --- a/post-processor/googlecompute-export/post-processor.go +++ b/post-processor/googlecompute-export/post-processor.go @@ -117,7 +117,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact "startup-script": StartupScript, "zone": p.config.Zone, } - exporterConfig := googlecompute.Config{ + exporterConfig := googlecompute.ConfigGCE{ DiskName: exporterName, DiskSizeGb: p.config.DiskSizeGb, DiskType: p.config.DiskType, From f1e966401272b0594695d9e7bd3d5978dcfcfefc Mon Sep 17 00:00:00 2001 From: krisko Date: Tue, 23 Apr 2019 20:49:41 +0200 Subject: [PATCH 2/4] Googlebuilder: Add image key encryption --- builder/googlecompute/config_test.go | 15 +++++++++++ builder/googlecompute/driver_mock.go | 27 ++++++++++++------- .../googlecompute/step_create_image_test.go | 1 + 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/builder/googlecompute/config_test.go b/builder/googlecompute/config_test.go index 77086a413..755050b93 100644 --- a/builder/googlecompute/config_test.go +++ b/builder/googlecompute/config_test.go @@ -156,6 +156,21 @@ func TestConfigPrepare(t *testing.T) { "foo bar", true, }, + { + "image_encryption_key", + map[string]string{"kmsKeyName": "foo"}, + false, + }, + { + "image_encryption_key", + map[string]string{"No such key": "foo"}, + true, + }, + { + "image_encryption_key", + map[string]string{"kmsKeyName": "foo", "RawKey": "foo"}, + false, + }, { "scopes", []string{}, diff --git a/builder/googlecompute/driver_mock.go b/builder/googlecompute/driver_mock.go index 720f9d60b..103a7ed47 100644 --- a/builder/googlecompute/driver_mock.go +++ b/builder/googlecompute/driver_mock.go @@ -1,6 +1,10 @@ package googlecompute -import "fmt" +import ( + "fmt" + + compute "google.golang.org/api/compute/v1" +) // DriverMock is a Driver implementation that is a mocked out so that // it can be used for tests. @@ -9,6 +13,7 @@ type DriverMock struct { CreateImageDesc string CreateImageFamily string CreateImageLabels map[string]string + CreateImageEncryptionKey *compute.CustomerEncryptionKey CreateImageLicenses []string CreateImageZone string CreateImageDisk string @@ -82,14 +87,16 @@ type DriverMock struct { WaitForInstanceErrCh <-chan error } -func (d *DriverMock) CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string) (<-chan *Image, <-chan error) { - d.CreateImageName = name - d.CreateImageDesc = description - d.CreateImageFamily = family - d.CreateImageLabels = image_labels - d.CreateImageLicenses = image_licenses - d.CreateImageZone = zone - d.CreateImageDisk = disk +func (d *DriverMock) CreateImage(config Config) (<-chan *Image, <-chan error) { + d.CreateImageName = config.GetImageName() + d.CreateImageDesc = config.GetImageDescription() + d.CreateImageFamily = config.GetImageFamily() + d.CreateImageLabels = config.GetImageLabels() + d.CreateImageLicenses = config.GetImageLicenses() + d.CreateImageZone = config.GetZone() + d.CreateImageDisk = config.GetDiskName() + d.CreateImageEncryptionKey = config.GetImageEncryptionKey() + if d.CreateImageResultProjectId == "" { d.CreateImageResultProjectId = "test" } @@ -108,7 +115,7 @@ func (d *DriverMock) CreateImage(name, description, family, zone, disk string, i ch <- &Image{ Labels: d.CreateImageLabels, Licenses: d.CreateImageLicenses, - Name: name, + Name: d.CreateImageName, ProjectId: d.CreateImageResultProjectId, SelfLink: d.CreateImageResultSelfLink, SizeGb: d.CreateImageResultSizeGb, diff --git a/builder/googlecompute/step_create_image_test.go b/builder/googlecompute/step_create_image_test.go index e870eba42..0f1458124 100644 --- a/builder/googlecompute/step_create_image_test.go +++ b/builder/googlecompute/step_create_image_test.go @@ -47,6 +47,7 @@ func TestStepCreateImage(t *testing.T) { assert.Equal(t, d.CreateImageDisk, c.DiskName, "Incorrect disk passed to driver.") assert.Equal(t, d.CreateImageLabels, c.ImageLabels, "Incorrect image_labels passed to driver.") assert.Equal(t, d.CreateImageLicenses, c.ImageLicenses, "Incorrect image_licenses passed to driver.") + assert.Equal(t, d.CreateImageEncryptionKey, c.ImageEncryptionKey, "Incorrect image_encryption_key passed to driver.") } func TestStepCreateImage_errorOnChannel(t *testing.T) { From f2f6772aee487e487f5a805395aa58df732d0169 Mon Sep 17 00:00:00 2001 From: krisko Date: Thu, 2 May 2019 17:33:47 +0200 Subject: [PATCH 3/4] Document image_encryption_key --- website/source/docs/builders/googlecompute.html.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/website/source/docs/builders/googlecompute.html.md b/website/source/docs/builders/googlecompute.html.md index dfbdd514e..1248b556f 100644 --- a/website/source/docs/builders/googlecompute.html.md +++ b/website/source/docs/builders/googlecompute.html.md @@ -270,6 +270,17 @@ builder. - `image_name` (string) - The unique name of the resulting image. Defaults to `"packer-{{timestamp}}"`. +- `image_encryption_key` (object of encryption key) - Image encryption key to apply to the created image. Possible values: + * kmsKeyName - The name of the encryption key that is stored in Google Cloud KMS. + * RawKey: - A 256-bit customer-supplied encryption key, encodes in RFC 4648 base64. + + example: + ``` json + { + "kmsKeyName": "projects/${project}/locations/${region}/keyRings/computeEngine/cryptoKeys/computeEngine/cryptoKeyVersions/4" + } + ``` + - `instance_name` (string) - A name to give the launched instance. Beware that this must be unique. Defaults to `"packer-{{uuid}}"`. From e96bda6466c834c2e3e1ebbe68d4e5a91b30f620 Mon Sep 17 00:00:00 2001 From: krisko Date: Sun, 5 May 2019 17:12:30 +0200 Subject: [PATCH 4/4] Remove config interface. Make CreateImage function to accept config as list of config fields --- builder/googlecompute/artifact.go | 2 +- builder/googlecompute/builder.go | 2 +- builder/googlecompute/config.go | 273 ++++++++++++++-- builder/googlecompute/config_gce.go | 291 ------------------ builder/googlecompute/config_test.go | 8 +- builder/googlecompute/driver.go | 4 +- builder/googlecompute/driver_gce.go | 19 +- builder/googlecompute/driver_mock.go | 23 +- .../step_check_existing_image.go | 2 +- .../step_check_existing_image_test.go | 2 +- builder/googlecompute/step_create_image.go | 6 +- .../googlecompute/step_create_image_test.go | 2 +- builder/googlecompute/step_create_instance.go | 8 +- .../step_create_instance_test.go | 18 +- builder/googlecompute/step_create_ssh_key.go | 2 +- .../googlecompute/step_create_ssh_key_test.go | 6 +- .../step_create_windows_password.go | 2 +- .../step_create_windows_password_test.go | 2 +- builder/googlecompute/step_instance_info.go | 2 +- .../googlecompute/step_instance_info_test.go | 6 +- .../googlecompute/step_teardown_instance.go | 4 +- .../step_teardown_instance_test.go | 2 +- .../googlecompute/step_wait_startup_script.go | 2 +- .../step_wait_startup_script_test.go | 2 +- builder/googlecompute/winrm.go | 2 +- .../googlecompute-export/post-processor.go | 2 +- 26 files changed, 317 insertions(+), 377 deletions(-) delete mode 100644 builder/googlecompute/config_gce.go diff --git a/builder/googlecompute/artifact.go b/builder/googlecompute/artifact.go index d4f7814b1..9d98d6566 100644 --- a/builder/googlecompute/artifact.go +++ b/builder/googlecompute/artifact.go @@ -9,7 +9,7 @@ import ( type Artifact struct { image *Image driver Driver - config *ConfigGCE + config *Config } // BuilderId returns the builder Id. diff --git a/builder/googlecompute/builder.go b/builder/googlecompute/builder.go index cf4b0cb4a..9a2b4fecc 100644 --- a/builder/googlecompute/builder.go +++ b/builder/googlecompute/builder.go @@ -18,7 +18,7 @@ const BuilderId = "packer.googlecompute" // Builder represents a Packer Builder. type Builder struct { - config *ConfigGCE + config *Config runner multistep.Runner } diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index f4e473cd8..df55d99d2 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -1,32 +1,259 @@ package googlecompute -import compute "google.golang.org/api/compute/v1" +import ( + "errors" + "fmt" + "os" + "regexp" + "time" -// Configis the interface that has to be implemented to pass -//configuration for GCE. The Config interface exists mostly to allow a mock implementation -// to be used to test the steps. -type Config interface { - //GetImageDescription gets image desription for the image. - GetImageDescription() string + "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/common/uuid" + "github.com/hashicorp/packer/helper/communicator" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/template/interpolate" + compute "google.golang.org/api/compute/v1" +) - //GetImageName gets image name for the image. - GetImageName() string +var reImageFamily = regexp.MustCompile(`^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$`) - //GGetImageFamily gets image family for the image. - GetImageFamily() string +// 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"` + Comm communicator.Config `mapstructure:",squash"` - //GetImageLabels gets list of labels for the image. - GetImageLabels() map[string]string + AccountFile string `mapstructure:"account_file"` + ProjectId string `mapstructure:"project_id"` - //GetImageDescription gets image desription for the image. - GetImageLicenses() []string + AcceleratorType string `mapstructure:"accelerator_type"` + AcceleratorCount int64 `mapstructure:"accelerator_count"` + Address string `mapstructure:"address"` + DisableDefaultServiceAccount bool `mapstructure:"disable_default_service_account"` + DiskName string `mapstructure:"disk_name"` + DiskSizeGb int64 `mapstructure:"disk_size"` + DiskType string `mapstructure:"disk_type"` + ImageName string `mapstructure:"image_name"` + ImageDescription string `mapstructure:"image_description"` + ImageEncryptionKey *compute.CustomerEncryptionKey `mapstructure:"image_encryption_key"` + ImageFamily string `mapstructure:"image_family"` + ImageLabels map[string]string `mapstructure:"image_labels"` + ImageLicenses []string `mapstructure:"image_licenses"` + InstanceName string `mapstructure:"instance_name"` + Labels map[string]string `mapstructure:"labels"` + MachineType string `mapstructure:"machine_type"` + Metadata map[string]string `mapstructure:"metadata"` + MinCpuPlatform string `mapstructure:"min_cpu_platform"` + Network string `mapstructure:"network"` + NetworkProjectId string `mapstructure:"network_project_id"` + OmitExternalIP bool `mapstructure:"omit_external_ip"` + OnHostMaintenance string `mapstructure:"on_host_maintenance"` + Preemptible bool `mapstructure:"preemptible"` + RawStateTimeout string `mapstructure:"state_timeout"` + Region string `mapstructure:"region"` + Scopes []string `mapstructure:"scopes"` + ServiceAccountEmail string `mapstructure:"service_account_email"` + SourceImage string `mapstructure:"source_image"` + SourceImageFamily string `mapstructure:"source_image_family"` + SourceImageProjectId string `mapstructure:"source_image_project_id"` + StartupScriptFile string `mapstructure:"startup_script_file"` + Subnetwork string `mapstructure:"subnetwork"` + Tags []string `mapstructure:"tags"` + UseInternalIP bool `mapstructure:"use_internal_ip"` + Zone string `mapstructure:"zone"` - //GetZone gets image zone for the image. - GetZone() string - - //GetDiskName gets disk name for the image. - GetDiskName() string - - //GetImageEncryptionKey gets image encryption key for the image. - GetImageEncryptionKey() *compute.CustomerEncryptionKey + Account AccountFile + stateTimeout time.Duration + imageAlreadyExists bool + ctx interpolate.Context +} + +func NewConfig(raws ...interface{}) (*Config, []string, error) { + c := new(Config) + c.ctx.Funcs = TemplateFuncs + err := config.Decode(c, &config.DecodeOpts{ + Interpolate: true, + InterpolateContext: &c.ctx, + InterpolateFilter: &interpolate.RenderFilter{ + Exclude: []string{ + "run_command", + }, + }, + }, raws...) + if err != nil { + return nil, nil, err + } + + var errs *packer.MultiError + + // Set defaults. + if c.Network == "" && c.Subnetwork == "" { + c.Network = "default" + } + + if c.NetworkProjectId == "" { + c.NetworkProjectId = c.ProjectId + } + + if c.DiskSizeGb == 0 { + c.DiskSizeGb = 10 + } + + if c.DiskType == "" { + c.DiskType = "pd-standard" + } + + if c.ImageDescription == "" { + c.ImageDescription = "Created by Packer" + } + + if c.OnHostMaintenance == "MIGRATE" && c.Preemptible { + errs = packer.MultiErrorAppend(errs, + errors.New("on_host_maintenance must be TERMINATE when using preemptible instances.")) + } + // Setting OnHostMaintenance Correct Defaults + // "MIGRATE" : Possible and default if Preemptible is false + // "TERMINATE": Required if Preemptible is true + if c.Preemptible { + c.OnHostMaintenance = "TERMINATE" + } else { + if c.OnHostMaintenance == "" { + c.OnHostMaintenance = "MIGRATE" + } + } + + // Make sure user sets a valid value for on_host_maintenance option + if !(c.OnHostMaintenance == "MIGRATE" || c.OnHostMaintenance == "TERMINATE") { + errs = packer.MultiErrorAppend(errs, + errors.New("on_host_maintenance must be one of MIGRATE or TERMINATE.")) + } + + if c.ImageName == "" { + img, err := interpolate.Render("packer-{{timestamp}}", nil) + if err != nil { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Unable to parse image name: %s ", err)) + } else { + c.ImageName = img + } + } + + if len(c.ImageFamily) > 63 { + errs = packer.MultiErrorAppend(errs, + errors.New("Invalid image family: Must not be longer than 63 characters")) + } + + if c.ImageFamily != "" { + if !reImageFamily.MatchString(c.ImageFamily) { + 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")) + } + + } + + if c.InstanceName == "" { + c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) + } + + if c.DiskName == "" { + c.DiskName = c.InstanceName + } + + if c.MachineType == "" { + c.MachineType = "n1-standard-1" + } + + if c.RawStateTimeout == "" { + c.RawStateTimeout = "5m" + } + + if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { + errs = packer.MultiErrorAppend(errs, es...) + } + + // Process required parameters. + if c.ProjectId == "" { + errs = packer.MultiErrorAppend( + errs, errors.New("a project_id must be specified")) + } + + if c.Scopes == nil { + c.Scopes = []string{ + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/compute", + "https://www.googleapis.com/auth/devstorage.full_control", + } + } + + if c.SourceImage == "" && c.SourceImageFamily == "" { + errs = packer.MultiErrorAppend( + errs, errors.New("a source_image or source_image_family must be specified")) + } + + if c.Zone == "" { + errs = packer.MultiErrorAppend( + errs, errors.New("a zone must be specified")) + } + if c.Region == "" && len(c.Zone) > 2 { + // get region from Zone + region := c.Zone[:len(c.Zone)-2] + c.Region = region + } + + err = c.CalcTimeout() + if err != nil { + errs = packer.MultiErrorAppend(errs, err) + } + + if c.AccountFile != "" { + if err := ProcessAccountFile(&c.Account, c.AccountFile); err != nil { + errs = packer.MultiErrorAppend(errs, err) + } + } + + 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")) + } + + if c.AcceleratorCount > 0 && len(c.AcceleratorType) == 0 { + errs = packer.MultiErrorAppend(fmt.Errorf("'accelerator_type' must be set when 'accelerator_count' is more than 0")) + } + + if c.AcceleratorCount > 0 && c.OnHostMaintenance != "TERMINATE" { + errs = packer.MultiErrorAppend(fmt.Errorf("'on_host_maintenance' must be set to 'TERMINATE' when 'accelerator_count' is more than 0")) + } + + // If DisableDefaultServiceAccount is provided, don't allow a value for ServiceAccountEmail + if c.DisableDefaultServiceAccount && c.ServiceAccountEmail != "" { + errs = packer.MultiErrorAppend(fmt.Errorf("you may not specify a 'service_account_email' when 'disable_default_service_account' is true")) + } + + if c.StartupScriptFile != "" { + if _, err := os.Stat(c.StartupScriptFile); err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("startup_script_file: %v", err)) + } + } + + // Check for any errors. + if errs != nil && len(errs.Errors) > 0 { + return nil, nil, errs + } + + return c, nil, nil +} + +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 } diff --git a/builder/googlecompute/config_gce.go b/builder/googlecompute/config_gce.go deleted file mode 100644 index c285784ec..000000000 --- a/builder/googlecompute/config_gce.go +++ /dev/null @@ -1,291 +0,0 @@ -package googlecompute - -import ( - "errors" - "fmt" - "os" - "regexp" - "time" - - "github.com/hashicorp/packer/common" - "github.com/hashicorp/packer/common/uuid" - "github.com/hashicorp/packer/helper/communicator" - "github.com/hashicorp/packer/helper/config" - "github.com/hashicorp/packer/packer" - "github.com/hashicorp/packer/template/interpolate" - compute "google.golang.org/api/compute/v1" -) - -var reImageFamily = regexp.MustCompile(`^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$`) - -// 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 ConfigGCE struct { - common.PackerConfig `mapstructure:",squash"` - Comm communicator.Config `mapstructure:",squash"` - - AccountFile string `mapstructure:"account_file"` - ProjectId string `mapstructure:"project_id"` - - AcceleratorType string `mapstructure:"accelerator_type"` - AcceleratorCount int64 `mapstructure:"accelerator_count"` - Address string `mapstructure:"address"` - DisableDefaultServiceAccount bool `mapstructure:"disable_default_service_account"` - DiskName string `mapstructure:"disk_name"` - DiskSizeGb int64 `mapstructure:"disk_size"` - DiskType string `mapstructure:"disk_type"` - ImageName string `mapstructure:"image_name"` - ImageDescription string `mapstructure:"image_description"` - ImageFamily string `mapstructure:"image_family"` - ImageLabels map[string]string `mapstructure:"image_labels"` - ImageEncryptionKey *compute.CustomerEncryptionKey `mapstructure:"image_encryption_key"` - ImageLicenses []string `mapstructure:"image_licenses"` - InstanceName string `mapstructure:"instance_name"` - Labels map[string]string `mapstructure:"labels"` - MachineType string `mapstructure:"machine_type"` - Metadata map[string]string `mapstructure:"metadata"` - MinCpuPlatform string `mapstructure:"min_cpu_platform"` - Network string `mapstructure:"network"` - NetworkProjectId string `mapstructure:"network_project_id"` - OmitExternalIP bool `mapstructure:"omit_external_ip"` - OnHostMaintenance string `mapstructure:"on_host_maintenance"` - Preemptible bool `mapstructure:"preemptible"` - RawStateTimeout string `mapstructure:"state_timeout"` - Region string `mapstructure:"region"` - Scopes []string `mapstructure:"scopes"` - ServiceAccountEmail string `mapstructure:"service_account_email"` - SourceImage string `mapstructure:"source_image"` - SourceImageFamily string `mapstructure:"source_image_family"` - SourceImageProjectId string `mapstructure:"source_image_project_id"` - StartupScriptFile string `mapstructure:"startup_script_file"` - Subnetwork string `mapstructure:"subnetwork"` - Tags []string `mapstructure:"tags"` - UseInternalIP bool `mapstructure:"use_internal_ip"` - Zone string `mapstructure:"zone"` - - Account AccountFile - stateTimeout time.Duration - imageAlreadyExists bool - ctx interpolate.Context -} - -func NewConfig(raws ...interface{}) (*ConfigGCE, []string, error) { - c := new(ConfigGCE) - c.ctx.Funcs = TemplateFuncs - err := config.Decode(c, &config.DecodeOpts{ - Interpolate: true, - InterpolateContext: &c.ctx, - InterpolateFilter: &interpolate.RenderFilter{ - Exclude: []string{ - "run_command", - }, - }, - }, raws...) - if err != nil { - return nil, nil, err - } - - var errs *packer.MultiError - - // Set defaults. - if c.Network == "" && c.Subnetwork == "" { - c.Network = "default" - } - - if c.NetworkProjectId == "" { - c.NetworkProjectId = c.ProjectId - } - - if c.DiskSizeGb == 0 { - c.DiskSizeGb = 10 - } - - if c.DiskType == "" { - c.DiskType = "pd-standard" - } - - if c.ImageDescription == "" { - c.ImageDescription = "Created by Packer" - } - - if c.OnHostMaintenance == "MIGRATE" && c.Preemptible { - errs = packer.MultiErrorAppend(errs, - errors.New("on_host_maintenance must be TERMINATE when using preemptible instances.")) - } - // Setting OnHostMaintenance Correct Defaults - // "MIGRATE" : Possible and default if Preemptible is false - // "TERMINATE": Required if Preemptible is true - if c.Preemptible { - c.OnHostMaintenance = "TERMINATE" - } else { - if c.OnHostMaintenance == "" { - c.OnHostMaintenance = "MIGRATE" - } - } - - // Make sure user sets a valid value for on_host_maintenance option - if !(c.OnHostMaintenance == "MIGRATE" || c.OnHostMaintenance == "TERMINATE") { - errs = packer.MultiErrorAppend(errs, - errors.New("on_host_maintenance must be one of MIGRATE or TERMINATE.")) - } - - if c.ImageName == "" { - img, err := interpolate.Render("packer-{{timestamp}}", nil) - if err != nil { - errs = packer.MultiErrorAppend(errs, - fmt.Errorf("Unable to parse image name: %s ", err)) - } else { - c.ImageName = img - } - } - - if len(c.ImageFamily) > 63 { - errs = packer.MultiErrorAppend(errs, - errors.New("Invalid image family: Must not be longer than 63 characters")) - } - - if c.ImageFamily != "" { - if !reImageFamily.MatchString(c.ImageFamily) { - 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")) - } - - } - - if c.InstanceName == "" { - c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) - } - - if c.DiskName == "" { - c.DiskName = c.InstanceName - } - - if c.MachineType == "" { - c.MachineType = "n1-standard-1" - } - - if c.RawStateTimeout == "" { - c.RawStateTimeout = "5m" - } - - if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { - errs = packer.MultiErrorAppend(errs, es...) - } - - // Process required parameters. - if c.ProjectId == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("a project_id must be specified")) - } - - if c.Scopes == nil { - c.Scopes = []string{ - "https://www.googleapis.com/auth/userinfo.email", - "https://www.googleapis.com/auth/compute", - "https://www.googleapis.com/auth/devstorage.full_control", - } - } - - if c.SourceImage == "" && c.SourceImageFamily == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("a source_image or source_image_family must be specified")) - } - - if c.Zone == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("a zone must be specified")) - } - if c.Region == "" && len(c.Zone) > 2 { - // get region from Zone - region := c.Zone[:len(c.Zone)-2] - c.Region = region - } - - err = c.CalcTimeout() - if err != nil { - errs = packer.MultiErrorAppend(errs, err) - } - - if c.AccountFile != "" { - if err := ProcessAccountFile(&c.Account, c.AccountFile); err != nil { - errs = packer.MultiErrorAppend(errs, err) - } - } - - 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")) - } - - if c.AcceleratorCount > 0 && len(c.AcceleratorType) == 0 { - errs = packer.MultiErrorAppend(fmt.Errorf("'accelerator_type' must be set when 'accelerator_count' is more than 0")) - } - - if c.AcceleratorCount > 0 && c.OnHostMaintenance != "TERMINATE" { - errs = packer.MultiErrorAppend(fmt.Errorf("'on_host_maintenance' must be set to 'TERMINATE' when 'accelerator_count' is more than 0")) - } - - // If DisableDefaultServiceAccount is provided, don't allow a value for ServiceAccountEmail - if c.DisableDefaultServiceAccount && c.ServiceAccountEmail != "" { - errs = packer.MultiErrorAppend(fmt.Errorf("you may not specify a 'service_account_email' when 'disable_default_service_account' is true")) - } - - if c.StartupScriptFile != "" { - if _, err := os.Stat(c.StartupScriptFile); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("startup_script_file: %v", err)) - } - } - - // Check for any errors. - if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs - } - - return c, nil, nil -} - -func (c *ConfigGCE) 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 -} - -func (c *ConfigGCE) GetImageDescription() string { - return c.ImageDescription -} - -func (c *ConfigGCE) GetImageName() string { - return c.ImageName -} - -func (c *ConfigGCE) GetImageFamily() string { - return c.ImageFamily -} - -func (c *ConfigGCE) GetImageLabels() map[string]string { - return c.ImageLabels -} - -func (c *ConfigGCE) GetImageLicenses() []string { - return c.ImageLicenses -} - -func (c *ConfigGCE) GetDiskName() string { - return c.DiskName -} - -func (c *ConfigGCE) GetZone() string { - return c.Zone -} - -func (c *ConfigGCE) GetImageEncryptionKey() *compute.CustomerEncryptionKey { - return c.ImageEncryptionKey -} diff --git a/builder/googlecompute/config_test.go b/builder/googlecompute/config_test.go index 755050b93..4db82040b 100644 --- a/builder/googlecompute/config_test.go +++ b/builder/googlecompute/config_test.go @@ -362,16 +362,16 @@ func TestConfigPrepareStartupScriptFile(t *testing.T) { func TestConfigDefaults(t *testing.T) { cases := []struct { - Read func(c *ConfigGCE) interface{} + Read func(c *Config) interface{} Value interface{} }{ { - func(c *ConfigGCE) interface{} { return c.Comm.Type }, + func(c *Config) interface{} { return c.Comm.Type }, "ssh", }, { - func(c *ConfigGCE) interface{} { return c.Comm.SSHPort }, + func(c *Config) interface{} { return c.Comm.SSHPort }, 22, }, } @@ -437,7 +437,7 @@ func testConfig(t *testing.T) (config map[string]interface{}, tempAccountFile st return config, tempAccountFile } -func testConfigStruct(t *testing.T) *ConfigGCE { +func testConfigStruct(t *testing.T) *Config { raw, tempfile := testConfig(t) defer os.Remove(tempfile) diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index f4bdcd20d..7ede0ae2b 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -3,6 +3,8 @@ package googlecompute import ( "crypto/rsa" "time" + + compute "google.golang.org/api/compute/v1" ) // Driver is the interface that has to be implemented to communicate @@ -11,7 +13,7 @@ import ( type Driver interface { // CreateImage creates an image from the given disk in Google Compute // Engine. - CreateImage(config Config) (<-chan *Image, <-chan error) + CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string, image_encryption_key *compute.CustomerEncryptionKey) (<-chan *Image, <-chan error) // DeleteImage deletes the image with the given name. DeleteImage(name string) <-chan error diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index 30bb16d6a..400ec9c02 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -104,17 +104,18 @@ func NewDriverGCE(ui packer.Ui, p string, a *AccountFile) (Driver, error) { }, nil } -func (d *driverGCE) CreateImage(config Config) (<-chan *Image, <-chan error) { +func (d *driverGCE) CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string, image_encryption_key *compute.CustomerEncryptionKey) (<-chan *Image, <-chan error) { gce_image := &compute.Image{ - Description: config.GetImageDescription(), - ImageEncryptionKey: config.GetImageEncryptionKey(), - Name: config.GetImageName(), - Family: config.GetImageFamily(), - Labels: config.GetImageLabels(), - Licenses: config.GetImageLicenses(), - SourceDisk: fmt.Sprintf("%s%s/zones/%s/disks/%s", d.service.BasePath, d.projectId, config.GetZone(), config.GetDiskName()), + Description: description, + Name: name, + Family: family, + Labels: image_labels, + Licenses: image_licenses, + ImageEncryptionKey: image_encryption_key, + SourceDisk: fmt.Sprintf("%s%s/zones/%s/disks/%s", d.service.BasePath, d.projectId, zone, disk), SourceType: "RAW", } + imageCh := make(chan *Image, 1) errCh := make(chan error, 1) op, err := d.service.Images.Insert(d.projectId, gce_image).Do() @@ -129,7 +130,7 @@ func (d *driverGCE) CreateImage(config Config) (<-chan *Image, <-chan error) { return } var image *Image - image, err = d.GetImageFromProject(d.projectId, config.GetImageName(), false) + image, err = d.GetImageFromProject(d.projectId, name, false) if err != nil { close(imageCh) errCh <- err diff --git a/builder/googlecompute/driver_mock.go b/builder/googlecompute/driver_mock.go index 103a7ed47..fed2cccab 100644 --- a/builder/googlecompute/driver_mock.go +++ b/builder/googlecompute/driver_mock.go @@ -12,8 +12,8 @@ type DriverMock struct { CreateImageName string CreateImageDesc string CreateImageFamily string - CreateImageLabels map[string]string CreateImageEncryptionKey *compute.CustomerEncryptionKey + CreateImageLabels map[string]string CreateImageLicenses []string CreateImageZone string CreateImageDisk string @@ -87,16 +87,15 @@ type DriverMock struct { WaitForInstanceErrCh <-chan error } -func (d *DriverMock) CreateImage(config Config) (<-chan *Image, <-chan error) { - d.CreateImageName = config.GetImageName() - d.CreateImageDesc = config.GetImageDescription() - d.CreateImageFamily = config.GetImageFamily() - d.CreateImageLabels = config.GetImageLabels() - d.CreateImageLicenses = config.GetImageLicenses() - d.CreateImageZone = config.GetZone() - d.CreateImageDisk = config.GetDiskName() - d.CreateImageEncryptionKey = config.GetImageEncryptionKey() - +func (d *DriverMock) CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string, image_encryption_key *compute.CustomerEncryptionKey) (<-chan *Image, <-chan error) { + d.CreateImageName = name + d.CreateImageDesc = description + d.CreateImageFamily = family + d.CreateImageLabels = image_labels + d.CreateImageLicenses = image_licenses + d.CreateImageZone = zone + d.CreateImageDisk = disk + d.CreateImageEncryptionKey = image_encryption_key if d.CreateImageResultProjectId == "" { d.CreateImageResultProjectId = "test" } @@ -115,7 +114,7 @@ func (d *DriverMock) CreateImage(config Config) (<-chan *Image, <-chan error) { ch <- &Image{ Labels: d.CreateImageLabels, Licenses: d.CreateImageLicenses, - Name: d.CreateImageName, + Name: name, ProjectId: d.CreateImageResultProjectId, SelfLink: d.CreateImageResultSelfLink, SizeGb: d.CreateImageResultSizeGb, diff --git a/builder/googlecompute/step_check_existing_image.go b/builder/googlecompute/step_check_existing_image.go index d9e4d5593..0574a0234 100644 --- a/builder/googlecompute/step_check_existing_image.go +++ b/builder/googlecompute/step_check_existing_image.go @@ -14,7 +14,7 @@ type StepCheckExistingImage int // Run executes the Packer build step that checks if the image already exists. func (s *StepCheckExistingImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) d := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) diff --git a/builder/googlecompute/step_check_existing_image_test.go b/builder/googlecompute/step_check_existing_image_test.go index 64f063191..d4b9a9541 100644 --- a/builder/googlecompute/step_check_existing_image_test.go +++ b/builder/googlecompute/step_check_existing_image_test.go @@ -18,7 +18,7 @@ func TestStepCheckExistingImage(t *testing.T) { state.Put("instance_name", "foo") - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) driver := state.Get("driver").(*DriverMock) driver.ImageExistsResult = true diff --git a/builder/googlecompute/step_create_image.go b/builder/googlecompute/step_create_image.go index 824be6946..536cde6f8 100644 --- a/builder/googlecompute/step_create_image.go +++ b/builder/googlecompute/step_create_image.go @@ -19,7 +19,7 @@ type StepCreateImage int // The image is created from the persistent disk used by the instance. The // instance must be deleted and the disk retained before doing this step. func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) @@ -38,7 +38,9 @@ func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul ui.Say("Creating image...") - imageCh, errCh := driver.CreateImage(config) + imageCh, errCh := driver.CreateImage( + config.ImageName, config.ImageDescription, config.ImageFamily, config.Zone, + config.DiskName, config.ImageLabels, config.ImageLicenses, config.ImageEncryptionKey) var err error select { case err = <-errCh: diff --git a/builder/googlecompute/step_create_image_test.go b/builder/googlecompute/step_create_image_test.go index 0f1458124..2e84af218 100644 --- a/builder/googlecompute/step_create_image_test.go +++ b/builder/googlecompute/step_create_image_test.go @@ -18,7 +18,7 @@ func TestStepCreateImage(t *testing.T) { step := new(StepCreateImage) defer step.Cleanup(state) - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) d := state.Get("driver").(*DriverMock) // These are the values of the image the driver will return. diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index faa4ade02..5ed854670 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -16,7 +16,7 @@ type StepCreateInstance struct { Debug bool } -func (c *ConfigGCE) createInstanceMetadata(sourceImage *Image, sshPublicKey string) (map[string]string, error) { +func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string) (map[string]string, error) { instanceMetadata := make(map[string]string) var err error @@ -58,7 +58,7 @@ func (c *ConfigGCE) createInstanceMetadata(sourceImage *Image, sshPublicKey stri return instanceMetadata, err } -func getImage(c *ConfigGCE, d Driver) (*Image, error) { +func getImage(c *Config, d Driver) (*Image, error) { name := c.SourceImageFamily fromFamily := true if c.SourceImage != "" { @@ -74,7 +74,7 @@ func getImage(c *ConfigGCE, d Driver) (*Image, error) { // Run executes the Packer build step that creates a GCE instance. func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) d := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) @@ -167,7 +167,7 @@ func (s *StepCreateInstance) Cleanup(state multistep.StateBag) { return } - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) diff --git a/builder/googlecompute/step_create_instance_test.go b/builder/googlecompute/step_create_instance_test.go index cf00c372e..e56159138 100644 --- a/builder/googlecompute/step_create_instance_test.go +++ b/builder/googlecompute/step_create_instance_test.go @@ -22,7 +22,7 @@ func TestStepCreateInstance(t *testing.T) { state.Put("ssh_public_key", "key") - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) d := state.Get("driver").(*DriverMock) d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100) @@ -61,7 +61,7 @@ func TestStepCreateInstance_fromFamily(t *testing.T) { state.Put("ssh_public_key", "key") - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) c.SourceImage = tc.Name c.SourceImageFamily = tc.Family d := state.Get("driver").(*DriverMock) @@ -89,7 +89,7 @@ func TestStepCreateInstance_windowsNeedsPassword(t *testing.T) { defer step.Cleanup(state) state.Put("ssh_public_key", "key") - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) d := state.Get("driver").(*DriverMock) d.GetImageResult = StubImage("test-image", "test-project", []string{"windows"}, 100) c.Comm.Type = "winrm" @@ -136,7 +136,7 @@ func TestStepCreateInstance_windowsPasswordSet(t *testing.T) { state.Put("ssh_public_key", "key") - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) driver := state.Get("driver").(*DriverMock) driver.GetImageResult = StubImage("test-image", "test-project", []string{"windows"}, 100) config.Comm.Type = "winrm" @@ -231,7 +231,7 @@ func TestStepCreateInstance_errorTimeout(t *testing.T) { errCh := make(chan error, 1) - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) config.stateTimeout = 1 * time.Microsecond d := state.Get("driver").(*DriverMock) @@ -255,7 +255,7 @@ func TestStepCreateInstance_noServiceAccount(t *testing.T) { state.Put("ssh_public_key", "key") - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) c.DisableDefaultServiceAccount = true c.ServiceAccountEmail = "" d := state.Get("driver").(*DriverMock) @@ -279,7 +279,7 @@ func TestStepCreateInstance_customServiceAccount(t *testing.T) { state.Put("ssh_public_key", "key") - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) c.DisableDefaultServiceAccount = true c.ServiceAccountEmail = "custom-service-account" d := state.Get("driver").(*DriverMock) @@ -298,7 +298,7 @@ func TestStepCreateInstance_customServiceAccount(t *testing.T) { func TestCreateInstanceMetadata(t *testing.T) { state := testState(t) - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) image := StubImage("test-image", "test-project", []string{}, 100) key := "abcdefgh12345678" @@ -313,7 +313,7 @@ func TestCreateInstanceMetadata(t *testing.T) { func TestCreateInstanceMetadata_noPublicKey(t *testing.T) { state := testState(t) - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) image := StubImage("test-image", "test-project", []string{}, 100) sshKeys := c.Metadata["sshKeys"] diff --git a/builder/googlecompute/step_create_ssh_key.go b/builder/googlecompute/step_create_ssh_key.go index 1555d61dc..416657b7d 100644 --- a/builder/googlecompute/step_create_ssh_key.go +++ b/builder/googlecompute/step_create_ssh_key.go @@ -24,7 +24,7 @@ type StepCreateSSHKey struct { // The key pairs are added to the ssh config func (s *StepCreateSSHKey) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) if config.Comm.SSHPrivateKeyFile != "" { ui.Say("Using existing SSH private key") diff --git a/builder/googlecompute/step_create_ssh_key_test.go b/builder/googlecompute/step_create_ssh_key_test.go index 3f9e9cb57..88ba7530c 100644 --- a/builder/googlecompute/step_create_ssh_key_test.go +++ b/builder/googlecompute/step_create_ssh_key_test.go @@ -17,7 +17,7 @@ func TestStepCreateSSHKey_impl(t *testing.T) { func TestStepCreateSSHKey_privateKey(t *testing.T) { state := testState(t) step := new(StepCreateSSHKey) - cfg := state.Get("config").(*ConfigGCE) + cfg := state.Get("config").(*Config) cfg.Comm.SSHPrivateKeyFile = "test-fixtures/fake-key" defer step.Cleanup(state) @@ -35,7 +35,7 @@ func TestStepCreateSSHKey_privateKey(t *testing.T) { func TestStepCreateSSHKey(t *testing.T) { state := testState(t) step := new(StepCreateSSHKey) - cfg := state.Get("config").(*ConfigGCE) + cfg := state.Get("config").(*Config) defer step.Cleanup(state) // run the step @@ -62,7 +62,7 @@ func TestStepCreateSSHKey_debug(t *testing.T) { state := testState(t) step := new(StepCreateSSHKey) - cfg := state.Get("config").(*ConfigGCE) + cfg := state.Get("config").(*Config) step.Debug = true step.DebugKeyPath = tf.Name() diff --git a/builder/googlecompute/step_create_windows_password.go b/builder/googlecompute/step_create_windows_password.go index 522bec8fd..fda606245 100644 --- a/builder/googlecompute/step_create_windows_password.go +++ b/builder/googlecompute/step_create_windows_password.go @@ -28,7 +28,7 @@ type StepCreateWindowsPassword struct { func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) d := state.Get("driver").(Driver) - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) name := state.Get("instance_name").(string) if c.Comm.WinRMPassword != "" { diff --git a/builder/googlecompute/step_create_windows_password_test.go b/builder/googlecompute/step_create_windows_password_test.go index 634b304c7..6abb6243d 100644 --- a/builder/googlecompute/step_create_windows_password_test.go +++ b/builder/googlecompute/step_create_windows_password_test.go @@ -37,7 +37,7 @@ func TestStepCreateOrResetWindowsPassword_passwordSet(t *testing.T) { // Step is run after the instance is created so we will have an instance name set state.Put("instance_name", "mock_instance") - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) c.Comm.WinRMPassword = "password" diff --git a/builder/googlecompute/step_instance_info.go b/builder/googlecompute/step_instance_info.go index 54daf3a47..1809fbb84 100644 --- a/builder/googlecompute/step_instance_info.go +++ b/builder/googlecompute/step_instance_info.go @@ -18,7 +18,7 @@ type StepInstanceInfo struct { // Run executes the Packer build step that gathers GCE instance info. // This adds "instance_ip" to the multistep state. func (s *StepInstanceInfo) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) diff --git a/builder/googlecompute/step_instance_info_test.go b/builder/googlecompute/step_instance_info_test.go index fc70f8bf2..86f718369 100644 --- a/builder/googlecompute/step_instance_info_test.go +++ b/builder/googlecompute/step_instance_info_test.go @@ -20,7 +20,7 @@ func TestStepInstanceInfo(t *testing.T) { state.Put("instance_name", "foo") - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) driver := state.Get("driver").(*DriverMock) driver.GetNatIPResult = "1.2.3.4" @@ -58,7 +58,7 @@ func TestStepInstanceInfo_InternalIP(t *testing.T) { state.Put("instance_name", "foo") - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) config.UseInternalIP = true driver := state.Get("driver").(*DriverMock) driver.GetNatIPResult = "1.2.3.4" @@ -155,7 +155,7 @@ func TestStepInstanceInfo_errorTimeout(t *testing.T) { state.Put("instance_name", "foo") - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) config.stateTimeout = 1 * time.Microsecond driver := state.Get("driver").(*DriverMock) diff --git a/builder/googlecompute/step_teardown_instance.go b/builder/googlecompute/step_teardown_instance.go index 2a61d5a5a..c992dead3 100644 --- a/builder/googlecompute/step_teardown_instance.go +++ b/builder/googlecompute/step_teardown_instance.go @@ -18,7 +18,7 @@ type StepTeardownInstance struct { // Run executes the Packer build step that tears down a GCE instance. func (s *StepTeardownInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) @@ -55,7 +55,7 @@ func (s *StepTeardownInstance) Run(ctx context.Context, state multistep.StateBag // Deleting the instance does not remove the boot disk. This cleanup removes // the disk. func (s *StepTeardownInstance) Cleanup(state multistep.StateBag) { - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) diff --git a/builder/googlecompute/step_teardown_instance_test.go b/builder/googlecompute/step_teardown_instance_test.go index aee5b4c5f..129c4e596 100644 --- a/builder/googlecompute/step_teardown_instance_test.go +++ b/builder/googlecompute/step_teardown_instance_test.go @@ -16,7 +16,7 @@ func TestStepTeardownInstance(t *testing.T) { step := new(StepTeardownInstance) defer step.Cleanup(state) - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) driver := state.Get("driver").(*DriverMock) // run the step diff --git a/builder/googlecompute/step_wait_startup_script.go b/builder/googlecompute/step_wait_startup_script.go index 23d9807d7..ede82ba8c 100644 --- a/builder/googlecompute/step_wait_startup_script.go +++ b/builder/googlecompute/step_wait_startup_script.go @@ -16,7 +16,7 @@ type StepWaitStartupScript int // Run reads the instance metadata and looks for the log entry // indicating the startup script finished. func (s *StepWaitStartupScript) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) instanceName := state.Get("instance_name").(string) diff --git a/builder/googlecompute/step_wait_startup_script_test.go b/builder/googlecompute/step_wait_startup_script_test.go index 099de158c..a970dc48d 100644 --- a/builder/googlecompute/step_wait_startup_script_test.go +++ b/builder/googlecompute/step_wait_startup_script_test.go @@ -11,7 +11,7 @@ import ( func TestStepWaitStartupScript(t *testing.T) { state := testState(t) step := new(StepWaitStartupScript) - c := state.Get("config").(*ConfigGCE) + c := state.Get("config").(*Config) d := state.Get("driver").(*DriverMock) testZone := "test-zone" diff --git a/builder/googlecompute/winrm.go b/builder/googlecompute/winrm.go index f2cfd659b..767787401 100644 --- a/builder/googlecompute/winrm.go +++ b/builder/googlecompute/winrm.go @@ -7,7 +7,7 @@ import ( // winrmConfig returns the WinRM configuration. func winrmConfig(state multistep.StateBag) (*communicator.WinRMConfig, error) { - config := state.Get("config").(*ConfigGCE) + config := state.Get("config").(*Config) password := state.Get("winrm_password").(string) return &communicator.WinRMConfig{ diff --git a/post-processor/googlecompute-export/post-processor.go b/post-processor/googlecompute-export/post-processor.go index 0ac5e1ea7..b0e15e296 100644 --- a/post-processor/googlecompute-export/post-processor.go +++ b/post-processor/googlecompute-export/post-processor.go @@ -117,7 +117,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact "startup-script": StartupScript, "zone": p.config.Zone, } - exporterConfig := googlecompute.ConfigGCE{ + exporterConfig := googlecompute.Config{ DiskName: exporterName, DiskSizeGb: p.config.DiskSizeGb, DiskType: p.config.DiskType,