From e0cfb404fb60ab8950be51fedee1794148d41d4a Mon Sep 17 00:00:00 2001 From: Wei Cheng Date: Tue, 2 Jun 2020 10:42:33 -0700 Subject: [PATCH] builder/googlecompute: add support to specify storage location for GCP images (#9326) * feat: add support to specify storage location for GCP images Signed-off-by: Wei Cheng --- builder/googlecompute/config.go | 24 +++++++++++++++ builder/googlecompute/config.hcl2spec.go | 2 ++ builder/googlecompute/config_test.go | 3 ++ builder/googlecompute/driver.go | 2 +- builder/googlecompute/driver_gce.go | 3 +- builder/googlecompute/driver_mock.go | 30 ++++++++++--------- builder/googlecompute/step_create_image.go | 3 +- .../googlecompute/step_create_image_test.go | 1 + .../googlecompute/Config-not-required.mdx | 19 ++++++++++++ 9 files changed, 70 insertions(+), 17 deletions(-) diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index e2e7724c0..e80f224bd 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -101,6 +101,25 @@ type Config struct { ImageLabels map[string]string `mapstructure:"image_labels" required:"false"` // Licenses to apply to the created image. ImageLicenses []string `mapstructure:"image_licenses" required:"false"` + // Storage location, either regional or multi-regional, where snapshot + // content is to be stored and only accepts 1 value. Always defaults to a nearby regional or multi-regional + // location. + // + // multi-regional example: + // + // ```json + // { + // "image_storage_locations": ["us"] + // } + // ``` + // regional example: + // + // ```json + // { + // "image_storage_locations": ["us-east1"] + // } + // ``` + ImageStorageLocations []string `mapstructure:"image_storage_locations" required:"false"` // A name to give the launched instance. Beware that this must be unique. // Defaults to `packer-{{uuid}}`. InstanceName string `mapstructure:"instance_name" required:"false"` @@ -307,6 +326,11 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { } } + if len(c.ImageStorageLocations) > 1 { + errs = packer.MultiErrorAppend(errs, + errors.New("Invalid image storage locations: Must not have more than 1 region")) + } + if c.InstanceName == "" { c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) } diff --git a/builder/googlecompute/config.hcl2spec.go b/builder/googlecompute/config.hcl2spec.go index 5a3700a7b..7317e9175 100644 --- a/builder/googlecompute/config.hcl2spec.go +++ b/builder/googlecompute/config.hcl2spec.go @@ -80,6 +80,7 @@ type FlatConfig struct { ImageFamily *string `mapstructure:"image_family" required:"false" cty:"image_family" hcl:"image_family"` ImageLabels map[string]string `mapstructure:"image_labels" required:"false" cty:"image_labels" hcl:"image_labels"` ImageLicenses []string `mapstructure:"image_licenses" required:"false" cty:"image_licenses" hcl:"image_licenses"` + ImageStorageLocations []string `mapstructure:"image_storage_locations" required:"false" cty:"image_storage_locations" hcl:"image_storage_locations"` InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name" hcl:"instance_name"` Labels map[string]string `mapstructure:"labels" required:"false" cty:"labels" hcl:"labels"` MachineType *string `mapstructure:"machine_type" required:"false" cty:"machine_type" hcl:"machine_type"` @@ -189,6 +190,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_family": &hcldec.AttrSpec{Name: "image_family", Type: cty.String, Required: false}, "image_labels": &hcldec.AttrSpec{Name: "image_labels", Type: cty.Map(cty.String), Required: false}, "image_licenses": &hcldec.AttrSpec{Name: "image_licenses", Type: cty.List(cty.String), Required: false}, + "image_storage_locations": &hcldec.AttrSpec{Name: "image_storage_locations", Type: cty.List(cty.String), Required: false}, "instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false}, "labels": &hcldec.AttrSpec{Name: "labels", Type: cty.Map(cty.String), Required: false}, "machine_type": &hcldec.AttrSpec{Name: "machine_type", Type: cty.String, Required: false}, diff --git a/builder/googlecompute/config_test.go b/builder/googlecompute/config_test.go index b7c46ea56..051c3797b 100644 --- a/builder/googlecompute/config_test.go +++ b/builder/googlecompute/config_test.go @@ -518,6 +518,9 @@ func testConfig(t *testing.T) (config map[string]interface{}, tempAccountFile st "image_licenses": []string{ "test-license", }, + "image_storage_locations": []string{ + "us-east1", + }, "metadata_files": map[string]string{}, "zone": "us-east1-a", } diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index 0de581bb0..2e0224230 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -13,7 +13,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, image_encryption_key *compute.CustomerEncryptionKey) (<-chan *Image, <-chan error) + CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string, image_encryption_key *compute.CustomerEncryptionKey, imageStorageLocation []string) (<-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 f98f964c4..3110081c5 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -137,7 +137,7 @@ func NewDriverGCE(ui packer.Ui, p string, conf *jwt.Config, vaultOauth string) ( }, nil } -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) { +func (d *driverGCE) CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string, image_encryption_key *compute.CustomerEncryptionKey, imageStorageLocations []string) (<-chan *Image, <-chan error) { gce_image := &compute.Image{ Description: description, Name: name, @@ -147,6 +147,7 @@ func (d *driverGCE) CreateImage(name, description, family, zone, disk string, im ImageEncryptionKey: image_encryption_key, SourceDisk: fmt.Sprintf("%s%s/zones/%s/disks/%s", d.service.BasePath, d.projectId, zone, disk), SourceType: "RAW", + StorageLocations: imageStorageLocations, } imageCh := make(chan *Image, 1) diff --git a/builder/googlecompute/driver_mock.go b/builder/googlecompute/driver_mock.go index 080772e5b..e01ffe058 100644 --- a/builder/googlecompute/driver_mock.go +++ b/builder/googlecompute/driver_mock.go @@ -9,19 +9,20 @@ import ( // DriverMock is a Driver implementation that is a mocked out so that // it can be used for tests. type DriverMock struct { - CreateImageName string - CreateImageDesc string - CreateImageFamily string - CreateImageEncryptionKey *compute.CustomerEncryptionKey - CreateImageLabels map[string]string - CreateImageLicenses []string - CreateImageZone string - CreateImageDisk string - CreateImageResultProjectId string - CreateImageResultSelfLink string - CreateImageResultSizeGb int64 - CreateImageErrCh <-chan error - CreateImageResultCh <-chan *Image + CreateImageName string + CreateImageDesc string + CreateImageFamily string + CreateImageEncryptionKey *compute.CustomerEncryptionKey + CreateImageLabels map[string]string + CreateImageLicenses []string + CreateImageStorageLocations []string + CreateImageZone string + CreateImageDisk string + CreateImageResultProjectId string + CreateImageResultSelfLink string + CreateImageResultSizeGb int64 + CreateImageErrCh <-chan error + CreateImageResultCh <-chan *Image DeleteImageName string DeleteImageErrCh <-chan error @@ -88,12 +89,13 @@ type DriverMock struct { WaitForInstanceErrCh <-chan error } -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) { +func (d *DriverMock) CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string, image_encryption_key *compute.CustomerEncryptionKey, imageStorageLocations []string) (<-chan *Image, <-chan error) { d.CreateImageName = name d.CreateImageDesc = description d.CreateImageFamily = family d.CreateImageLabels = image_labels d.CreateImageLicenses = image_licenses + d.CreateImageStorageLocations = imageStorageLocations d.CreateImageZone = zone d.CreateImageDisk = disk d.CreateImageEncryptionKey = image_encryption_key diff --git a/builder/googlecompute/step_create_image.go b/builder/googlecompute/step_create_image.go index 62106d60b..a7779e6f6 100644 --- a/builder/googlecompute/step_create_image.go +++ b/builder/googlecompute/step_create_image.go @@ -40,7 +40,8 @@ func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul imageCh, errCh := driver.CreateImage( config.ImageName, config.ImageDescription, config.ImageFamily, config.Zone, - config.DiskName, config.ImageLabels, config.ImageLicenses, config.ImageEncryptionKey.ComputeType()) + config.DiskName, config.ImageLabels, config.ImageLicenses, config.ImageEncryptionKey.ComputeType(), + config.ImageStorageLocations) 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 956b13896..2a3017f0a 100644 --- a/builder/googlecompute/step_create_image_test.go +++ b/builder/googlecompute/step_create_image_test.go @@ -48,6 +48,7 @@ func TestStepCreateImage(t *testing.T) { 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.ComputeType(), "Incorrect image_encryption_key passed to driver.") + assert.Equal(t, d.CreateImageStorageLocations, c.ImageStorageLocations, "Incorrect image_storage_locations passed to driver.") } func TestStepCreateImage_errorOnChannel(t *testing.T) { diff --git a/website/pages/partials/builder/googlecompute/Config-not-required.mdx b/website/pages/partials/builder/googlecompute/Config-not-required.mdx index 6a7204c1d..bc9f889e3 100644 --- a/website/pages/partials/builder/googlecompute/Config-not-required.mdx +++ b/website/pages/partials/builder/googlecompute/Config-not-required.mdx @@ -66,6 +66,25 @@ - `image_licenses` ([]string) - Licenses to apply to the created image. +- `image_storage_locations` ([]string) - Storage location, either regional or multi-regional, where snapshot + content is to be stored and only accepts 1 value. Always defaults to a nearby regional or multi-regional + location. + + multi-regional example: + + ```json + { + "image_storage_locations": ["us"] + } + ``` + regional example: + + ```json + { + "image_storage_locations": ["us-east1"] + } + ``` + - `instance_name` (string) - A name to give the launched instance. Beware that this must be unique. Defaults to `packer-{{uuid}}`.