Merge pull request #3531 from mitchellh/f-gce-image-family

Add support for Google image family
This commit is contained in:
Chris Bednarski 2016-05-13 15:40:03 -07:00
commit a5b763418f
11 changed files with 2227 additions and 1574 deletions

2
Godeps/Godeps.json generated
View File

@ -562,7 +562,7 @@
},
{
"ImportPath": "google.golang.org/api/compute/v1",
"Rev": "ddff2aff599105a55549cf173852507dfa094b7f"
"Rev": "ff0a1ff302946b997eb1832381419d1f95143483"
},
{
"ImportPath": "google.golang.org/api/gensupport",

View File

@ -3,6 +3,7 @@ package googlecompute
import (
"errors"
"fmt"
"regexp"
"time"
"github.com/mitchellh/packer/common"
@ -13,6 +14,8 @@ import (
"github.com/mitchellh/packer/template/interpolate"
)
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.
@ -28,6 +31,7 @@ type Config struct {
DiskType string `mapstructure:"disk_type"`
ImageName string `mapstructure:"image_name"`
ImageDescription string `mapstructure:"image_description"`
ImageFamily string `mapstructure:"image_family"`
InstanceName string `mapstructure:"instance_name"`
MachineType string `mapstructure:"machine_type"`
Metadata map[string]string `mapstructure:"metadata"`
@ -93,6 +97,19 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
}
}
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())
}

View File

@ -108,6 +108,26 @@ func TestConfigPrepare(t *testing.T) {
"SO VERY BAD",
true,
},
{
"image_family",
nil,
false,
},
{
"image_family",
"",
false,
},
{
"image_family",
"foo-bar",
false,
},
{
"image_family",
"foo bar",
true,
},
}
for _, tc := range cases {
@ -182,6 +202,7 @@ func testConfig(t *testing.T) map[string]interface{} {
"account_file": testAccountFile(t),
"project_id": "hashicorp",
"source_image": "foo",
"image_family": "bar",
"zone": "us-east1-a",
}
}

View File

@ -10,7 +10,7 @@ type Driver interface {
// CreateImage creates an image from the given disk in Google Compute
// Engine.
CreateImage(name, description, zone, disk string) <-chan error
CreateImage(name, description, family, zone, disk string) <-chan error
// DeleteImage deletes the image with the given name.
DeleteImage(name string) <-chan error

View File

@ -91,10 +91,11 @@ func (d *driverGCE) ImageExists(name string) bool {
return err == nil
}
func (d *driverGCE) CreateImage(name, description, zone, disk string) <-chan error {
func (d *driverGCE) CreateImage(name, description, family, zone, disk string) <-chan error {
image := &compute.Image{
Description: description,
Name: name,
Family: family,
SourceDisk: fmt.Sprintf("%s%s/zones/%s/disks/%s", d.service.BasePath, d.projectId, zone, disk),
SourceType: "RAW",
}

View File

@ -6,11 +6,12 @@ type DriverMock struct {
ImageExistsName string
ImageExistsResult bool
CreateImageName string
CreateImageDesc string
CreateImageZone string
CreateImageDisk string
CreateImageErrCh <-chan error
CreateImageName string
CreateImageDesc string
CreateImageFamily string
CreateImageZone string
CreateImageDisk string
CreateImageErrCh <-chan error
DeleteImageName string
DeleteImageErrCh <-chan error
@ -50,9 +51,10 @@ func (d *DriverMock) ImageExists(name string) bool {
return d.ImageExistsResult
}
func (d *DriverMock) CreateImage(name, description, zone, disk string) <-chan error {
func (d *DriverMock) CreateImage(name, description, family, zone, disk string) <-chan error {
d.CreateImageName = name
d.CreateImageDesc = description
d.CreateImageFamily = family
d.CreateImageZone = zone
d.CreateImageDisk = disk

View File

@ -23,7 +23,7 @@ func (s *StepCreateImage) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating image...")
errCh := driver.CreateImage(config.ImageName, config.ImageDescription, config.Zone, config.DiskName)
errCh := driver.CreateImage(config.ImageName, config.ImageDescription, config.ImageFamily, config.Zone, config.DiskName)
var err error
select {
case err = <-errCh:

View File

@ -31,6 +31,9 @@ func TestStepCreateImage(t *testing.T) {
if driver.CreateImageDesc != config.ImageDescription {
t.Fatalf("bad: %#v", driver.CreateImageDesc)
}
if driver.CreateImageFamily != config.ImageFamily {
t.Fatalf("bad: %#v", driver.CreateImageFamily)
}
if driver.CreateImageZone != config.Zone {
t.Fatalf("bad: %#v", driver.CreateImageZone)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -126,11 +126,13 @@ builder.
- `disk_type` (string) - Type of disk used to back your instance, like `pd-ssd` or `pd-standard`. Defaults to `pd-standard`.
- `image_description` (string) - The description of the resulting image.
- `image_family` (string) - The name of the image family to which the resulting image belongs. You can create disks by specifying an image family instead of a specific image name. The image family always returns its latest image that is not deprecated.
- `image_name` (string) - The unique name of the resulting image. Defaults to
`"packer-{{timestamp}}"`.
- `image_description` (string) - The description of the resulting image.
- `instance_name` (string) - A name to give the launched instance. Beware that
this must be unique. Defaults to `"packer-{{uuid}}"`.