Merge pull request #4162 from classmarkets/google-source-image-family
builder/googlecompute: support source image family
This commit is contained in:
commit
6947a74151
|
@ -43,6 +43,7 @@ type Config struct {
|
|||
Region string `mapstructure:"region"`
|
||||
Scopes []string `mapstructure:"scopes"`
|
||||
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"`
|
||||
|
@ -148,9 +149,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if c.SourceImage == "" {
|
||||
if c.SourceImage == "" && c.SourceImageFamily == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("a source_image must be specified"))
|
||||
errs, errors.New("a source_image or source_image_family must be specified"))
|
||||
}
|
||||
|
||||
if c.Zone == "" {
|
||||
|
|
|
@ -46,6 +46,17 @@ func TestConfigPrepare(t *testing.T) {
|
|||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"source_image_family",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"source_image_family",
|
||||
"foo",
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"zone",
|
||||
nil,
|
||||
|
|
|
@ -22,11 +22,14 @@ type Driver interface {
|
|||
// DeleteDisk deletes the disk with the given name.
|
||||
DeleteDisk(zone, name string) (<-chan error, error)
|
||||
|
||||
// GetImage gets an image; tries the default and public projects.
|
||||
GetImage(name string) (*Image, error)
|
||||
// GetImage gets an image; tries the default and public projects. If
|
||||
// fromFamily is true, name designates an image family instead of a
|
||||
// particular image.
|
||||
GetImage(name string, fromFamily bool) (*Image, error)
|
||||
|
||||
// GetImageFromProject gets an image from a specific project.
|
||||
GetImageFromProject(project, name string) (*Image, error)
|
||||
// GetImageFromProject gets an image from a specific project. If fromFamily
|
||||
// is true, name designates an image family instead of a particular image.
|
||||
GetImageFromProject(project, name string, fromFamily bool) (*Image, error)
|
||||
|
||||
// GetInstanceMetadata gets a metadata variable for the instance, name.
|
||||
GetInstanceMetadata(zone, name, key string) (string, error)
|
||||
|
|
|
@ -119,7 +119,7 @@ func (d *driverGCE) CreateImage(name, description, family, zone, disk string) (<
|
|||
return
|
||||
}
|
||||
var image *Image
|
||||
image, err = d.GetImageFromProject(d.projectId, name)
|
||||
image, err = d.GetImageFromProject(d.projectId, name, false)
|
||||
if err != nil {
|
||||
close(imageCh)
|
||||
errCh <- err
|
||||
|
@ -167,11 +167,11 @@ func (d *driverGCE) DeleteDisk(zone, name string) (<-chan error, error) {
|
|||
return errCh, nil
|
||||
}
|
||||
|
||||
func (d *driverGCE) GetImage(name string) (*Image, error) {
|
||||
func (d *driverGCE) GetImage(name string, fromFamily bool) (*Image, error) {
|
||||
projects := []string{d.projectId, "centos-cloud", "coreos-cloud", "debian-cloud", "google-containers", "opensuse-cloud", "rhel-cloud", "suse-cloud", "ubuntu-os-cloud", "windows-cloud", "gce-nvme"}
|
||||
var errs error
|
||||
for _, project := range projects {
|
||||
image, err := d.GetImageFromProject(project, name)
|
||||
image, err := d.GetImageFromProject(project, name, fromFamily)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
@ -185,8 +185,17 @@ func (d *driverGCE) GetImage(name string) (*Image, error) {
|
|||
projects, errs)
|
||||
}
|
||||
|
||||
func (d *driverGCE) GetImageFromProject(project, name string) (*Image, error) {
|
||||
image, err := d.service.Images.Get(project, name).Do()
|
||||
func (d *driverGCE) GetImageFromProject(project, name string, fromFamily bool) (*Image, error) {
|
||||
var (
|
||||
image *compute.Image
|
||||
err error
|
||||
)
|
||||
|
||||
if fromFamily {
|
||||
image, err = d.service.Images.GetFromFamily(project, name).Do()
|
||||
} else {
|
||||
image, err = d.service.Images.Get(project, name).Do()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -264,7 +273,7 @@ func (d *driverGCE) GetSerialPortOutput(zone, name string) (string, error) {
|
|||
}
|
||||
|
||||
func (d *driverGCE) ImageExists(name string) bool {
|
||||
_, err := d.GetImageFromProject(d.projectId, name)
|
||||
_, err := d.GetImageFromProject(d.projectId, name, false)
|
||||
// The API may return an error for reasons other than the image not
|
||||
// existing, but this heuristic is sufficient for now.
|
||||
return err == nil
|
||||
|
|
|
@ -30,14 +30,16 @@ type DriverMock struct {
|
|||
DeleteDiskErrCh <-chan error
|
||||
DeleteDiskErr error
|
||||
|
||||
GetImageName string
|
||||
GetImageResult *Image
|
||||
GetImageErr error
|
||||
GetImageName string
|
||||
GetImageFromFamily bool
|
||||
GetImageResult *Image
|
||||
GetImageErr error
|
||||
|
||||
GetImageFromProjectProject string
|
||||
GetImageFromProjectName string
|
||||
GetImageFromProjectResult *Image
|
||||
GetImageFromProjectErr error
|
||||
GetImageFromProjectProject string
|
||||
GetImageFromProjectName string
|
||||
GetImageFromProjectFromFamily bool
|
||||
GetImageFromProjectResult *Image
|
||||
GetImageFromProjectErr error
|
||||
|
||||
GetInstanceMetadataZone string
|
||||
GetInstanceMetadataName string
|
||||
|
@ -162,14 +164,16 @@ func (d *DriverMock) DeleteDisk(zone, name string) (<-chan error, error) {
|
|||
return resultCh, d.DeleteDiskErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) GetImage(name string) (*Image, error) {
|
||||
func (d *DriverMock) GetImage(name string, fromFamily bool) (*Image, error) {
|
||||
d.GetImageName = name
|
||||
d.GetImageFromFamily = fromFamily
|
||||
return d.GetImageResult, d.GetImageErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) GetImageFromProject(project, name string) (*Image, error) {
|
||||
func (d *DriverMock) GetImageFromProject(project, name string, fromFamily bool) (*Image, error) {
|
||||
d.GetImageFromProjectProject = project
|
||||
d.GetImageFromProjectName = name
|
||||
d.GetImageFromProjectFromFamily = fromFamily
|
||||
return d.GetImageFromProjectResult, d.GetImageFromProjectErr
|
||||
}
|
||||
|
||||
|
|
|
@ -58,10 +58,16 @@ func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string)
|
|||
}
|
||||
|
||||
func getImage(c *Config, d Driver) (*Image, error) {
|
||||
name := c.SourceImageFamily
|
||||
fromFamily := true
|
||||
if c.SourceImage != "" {
|
||||
name = c.SourceImage
|
||||
fromFamily = false
|
||||
}
|
||||
if c.SourceImageProjectId == "" {
|
||||
return d.GetImage(c.SourceImage)
|
||||
return d.GetImage(name, fromFamily)
|
||||
} else {
|
||||
return d.GetImageFromProject(c.SourceImageProjectId, c.SourceImage)
|
||||
return d.GetImageFromProject(c.SourceImageProjectId, c.SourceImage, fromFamily)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,6 +86,8 @@ func (s *StepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Using image: %s", sourceImage.Name))
|
||||
|
||||
if sourceImage.IsWindows() && c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" {
|
||||
state.Put("create_windows_password", true)
|
||||
}
|
||||
|
|
|
@ -42,6 +42,45 @@ func TestStepCreateInstance(t *testing.T) {
|
|||
assert.Equal(t, d.DeleteDiskZone, c.Zone, "Incorrect disk zone passed to driver.")
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_fromFamily(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
Family string
|
||||
Expect bool
|
||||
}{
|
||||
{"test-image", "", false},
|
||||
{"test-image", "test-family", false}, // name trumps family
|
||||
{"", "test-family", true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
c.SourceImage = tc.Name
|
||||
c.SourceImageFamily = tc.Family
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(state), multistep.ActionContinue, "Step should have passed and continued.")
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
|
||||
// Check args passed to the driver.
|
||||
if tc.Expect {
|
||||
assert.True(t, d.GetImageFromFamily, "Driver wasn't instructed to use an image family")
|
||||
} else {
|
||||
assert.False(t, d.GetImageFromFamily, "Driver was unexpectedly instructed to use an image family")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_windowsNeedsPassword(t *testing.T) {
|
||||
|
||||
state := testState(t)
|
||||
|
|
|
@ -125,8 +125,14 @@ builder.
|
|||
- `project_id` (string) - The project ID that will be used to launch instances
|
||||
and store images.
|
||||
|
||||
- `source_image` (string) - The source image to use to create the new
|
||||
image from. Example: `"debian-7-wheezy-v20150127"`
|
||||
- `source_image` (string) - The source image to use to create the new image
|
||||
from. You can also specify `source_image_family` instead. If both
|
||||
`source_image` and `source_image_family` are specified, `source_image`
|
||||
takes precedence. Example: `"debian-8-jessie-v20161027"`
|
||||
|
||||
- `source_image_family` (string) - The source image family to use to create
|
||||
the new image from. The image family always returns its latest image that
|
||||
is not deprecated. Example: `"debian-8"`.
|
||||
|
||||
- `zone` (string) - The zone in which to launch the instance used to create
|
||||
the image. Example: `"us-central1-a"`
|
||||
|
@ -147,7 +153,10 @@ builder.
|
|||
|
||||
- `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_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}}"`.
|
||||
|
|
Loading…
Reference in New Issue