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 <calvinpohwc@gmail.com>
This commit is contained in:
Wei Cheng 2020-06-02 10:42:33 -07:00 committed by GitHub
parent e851757e5f
commit e0cfb404fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 70 additions and 17 deletions

View File

@ -101,6 +101,25 @@ type Config struct {
ImageLabels map[string]string `mapstructure:"image_labels" required:"false"` ImageLabels map[string]string `mapstructure:"image_labels" required:"false"`
// Licenses to apply to the created image. // Licenses to apply to the created image.
ImageLicenses []string `mapstructure:"image_licenses" required:"false"` 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. // A name to give the launched instance. Beware that this must be unique.
// Defaults to `packer-{{uuid}}`. // Defaults to `packer-{{uuid}}`.
InstanceName string `mapstructure:"instance_name" required:"false"` 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 == "" { if c.InstanceName == "" {
c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
} }

View File

@ -80,6 +80,7 @@ type FlatConfig struct {
ImageFamily *string `mapstructure:"image_family" required:"false" cty:"image_family" hcl:"image_family"` 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"` 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"` 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"` 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"` 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"` 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_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_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_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}, "instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false},
"labels": &hcldec.AttrSpec{Name: "labels", Type: cty.Map(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}, "machine_type": &hcldec.AttrSpec{Name: "machine_type", Type: cty.String, Required: false},

View File

@ -518,6 +518,9 @@ func testConfig(t *testing.T) (config map[string]interface{}, tempAccountFile st
"image_licenses": []string{ "image_licenses": []string{
"test-license", "test-license",
}, },
"image_storage_locations": []string{
"us-east1",
},
"metadata_files": map[string]string{}, "metadata_files": map[string]string{},
"zone": "us-east1-a", "zone": "us-east1-a",
} }

View File

@ -13,7 +13,7 @@ import (
type Driver interface { type Driver interface {
// CreateImage creates an image from the given disk in Google Compute // CreateImage creates an image from the given disk in Google Compute
// Engine. // 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 deletes the image with the given name.
DeleteImage(name string) <-chan error DeleteImage(name string) <-chan error

View File

@ -137,7 +137,7 @@ func NewDriverGCE(ui packer.Ui, p string, conf *jwt.Config, vaultOauth string) (
}, nil }, 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{ gce_image := &compute.Image{
Description: description, Description: description,
Name: name, Name: name,
@ -147,6 +147,7 @@ func (d *driverGCE) CreateImage(name, description, family, zone, disk string, im
ImageEncryptionKey: image_encryption_key, ImageEncryptionKey: image_encryption_key,
SourceDisk: fmt.Sprintf("%s%s/zones/%s/disks/%s", d.service.BasePath, d.projectId, zone, disk), SourceDisk: fmt.Sprintf("%s%s/zones/%s/disks/%s", d.service.BasePath, d.projectId, zone, disk),
SourceType: "RAW", SourceType: "RAW",
StorageLocations: imageStorageLocations,
} }
imageCh := make(chan *Image, 1) imageCh := make(chan *Image, 1)

View File

@ -9,19 +9,20 @@ import (
// DriverMock is a Driver implementation that is a mocked out so that // DriverMock is a Driver implementation that is a mocked out so that
// it can be used for tests. // it can be used for tests.
type DriverMock struct { type DriverMock struct {
CreateImageName string CreateImageName string
CreateImageDesc string CreateImageDesc string
CreateImageFamily string CreateImageFamily string
CreateImageEncryptionKey *compute.CustomerEncryptionKey CreateImageEncryptionKey *compute.CustomerEncryptionKey
CreateImageLabels map[string]string CreateImageLabels map[string]string
CreateImageLicenses []string CreateImageLicenses []string
CreateImageZone string CreateImageStorageLocations []string
CreateImageDisk string CreateImageZone string
CreateImageResultProjectId string CreateImageDisk string
CreateImageResultSelfLink string CreateImageResultProjectId string
CreateImageResultSizeGb int64 CreateImageResultSelfLink string
CreateImageErrCh <-chan error CreateImageResultSizeGb int64
CreateImageResultCh <-chan *Image CreateImageErrCh <-chan error
CreateImageResultCh <-chan *Image
DeleteImageName string DeleteImageName string
DeleteImageErrCh <-chan error DeleteImageErrCh <-chan error
@ -88,12 +89,13 @@ type DriverMock struct {
WaitForInstanceErrCh <-chan error 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.CreateImageName = name
d.CreateImageDesc = description d.CreateImageDesc = description
d.CreateImageFamily = family d.CreateImageFamily = family
d.CreateImageLabels = image_labels d.CreateImageLabels = image_labels
d.CreateImageLicenses = image_licenses d.CreateImageLicenses = image_licenses
d.CreateImageStorageLocations = imageStorageLocations
d.CreateImageZone = zone d.CreateImageZone = zone
d.CreateImageDisk = disk d.CreateImageDisk = disk
d.CreateImageEncryptionKey = image_encryption_key d.CreateImageEncryptionKey = image_encryption_key

View File

@ -40,7 +40,8 @@ func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
imageCh, errCh := driver.CreateImage( imageCh, errCh := driver.CreateImage(
config.ImageName, config.ImageDescription, config.ImageFamily, config.Zone, 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 var err error
select { select {
case err = <-errCh: case err = <-errCh:

View File

@ -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.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.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.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) { func TestStepCreateImage_errorOnChannel(t *testing.T) {

View File

@ -66,6 +66,25 @@
- `image_licenses` ([]string) - Licenses to apply to the created image. - `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. - `instance_name` (string) - A name to give the launched instance. Beware that this must be unique.
Defaults to `packer-{{uuid}}`. Defaults to `packer-{{uuid}}`.