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:
parent
e851757e5f
commit
e0cfb404fb
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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},
|
||||||
|
|
|
@ -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",
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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}}`.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue