parent
671f15572d
commit
bcb319640e
|
@ -43,6 +43,7 @@ type Config struct {
|
||||||
Region string `mapstructure:"region"`
|
Region string `mapstructure:"region"`
|
||||||
Scopes []string `mapstructure:"scopes"`
|
Scopes []string `mapstructure:"scopes"`
|
||||||
SourceImage string `mapstructure:"source_image"`
|
SourceImage string `mapstructure:"source_image"`
|
||||||
|
SourceImageFamily string `mapstructure:"source_image_family"`
|
||||||
SourceImageProjectId string `mapstructure:"source_image_project_id"`
|
SourceImageProjectId string `mapstructure:"source_image_project_id"`
|
||||||
StartupScriptFile string `mapstructure:"startup_script_file"`
|
StartupScriptFile string `mapstructure:"startup_script_file"`
|
||||||
Subnetwork string `mapstructure:"subnetwork"`
|
Subnetwork string `mapstructure:"subnetwork"`
|
||||||
|
@ -152,9 +153,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.SourceImage == "" {
|
if c.SourceImage == "" && c.SourceImageFamily == "" {
|
||||||
errs = packer.MultiErrorAppend(
|
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 == "" {
|
if c.Zone == "" {
|
||||||
|
|
|
@ -46,6 +46,17 @@ func TestConfigPrepare(t *testing.T) {
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"source_image_family",
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source_image_family",
|
||||||
|
"foo",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"zone",
|
"zone",
|
||||||
nil,
|
nil,
|
||||||
|
|
|
@ -22,11 +22,14 @@ type Driver interface {
|
||||||
// DeleteDisk deletes the disk with the given name.
|
// DeleteDisk deletes the disk with the given name.
|
||||||
DeleteDisk(zone, name string) (<-chan error, error)
|
DeleteDisk(zone, name string) (<-chan error, error)
|
||||||
|
|
||||||
// GetImage gets an image; tries the default and public projects.
|
// GetImage gets an image; tries the default and public projects. If
|
||||||
GetImage(name string) (*Image, error)
|
// 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 gets an image from a specific project. If fromFamily
|
||||||
GetImageFromProject(project, name string) (*Image, error)
|
// 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 gets a metadata variable for the instance, name.
|
||||||
GetInstanceMetadata(zone, name, key string) (string, error)
|
GetInstanceMetadata(zone, name, key string) (string, error)
|
||||||
|
|
|
@ -119,7 +119,7 @@ func (d *driverGCE) CreateImage(name, description, family, zone, disk string) (<
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var image *Image
|
var image *Image
|
||||||
image, err = d.GetImageFromProject(d.projectId, name)
|
image, err = d.GetImageFromProject(d.projectId, name, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
close(imageCh)
|
close(imageCh)
|
||||||
errCh <- err
|
errCh <- err
|
||||||
|
@ -167,11 +167,11 @@ func (d *driverGCE) DeleteDisk(zone, name string) (<-chan error, error) {
|
||||||
return errCh, nil
|
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"}
|
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
|
var errs error
|
||||||
for _, project := range projects {
|
for _, project := range projects {
|
||||||
image, err := d.GetImageFromProject(project, name)
|
image, err := d.GetImageFromProject(project, name, fromFamily)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = packer.MultiErrorAppend(errs, err)
|
errs = packer.MultiErrorAppend(errs, err)
|
||||||
}
|
}
|
||||||
|
@ -185,8 +185,17 @@ func (d *driverGCE) GetImage(name string) (*Image, error) {
|
||||||
projects, errs)
|
projects, errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driverGCE) GetImageFromProject(project, name string) (*Image, error) {
|
func (d *driverGCE) GetImageFromProject(project, name string, fromFamily bool) (*Image, error) {
|
||||||
image, err := d.service.Images.Get(project, name).Do()
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -264,7 +273,7 @@ func (d *driverGCE) GetSerialPortOutput(zone, name string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *driverGCE) ImageExists(name string) bool {
|
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
|
// The API may return an error for reasons other than the image not
|
||||||
// existing, but this heuristic is sufficient for now.
|
// existing, but this heuristic is sufficient for now.
|
||||||
return err == nil
|
return err == nil
|
||||||
|
|
|
@ -31,11 +31,13 @@ type DriverMock struct {
|
||||||
DeleteDiskErr error
|
DeleteDiskErr error
|
||||||
|
|
||||||
GetImageName string
|
GetImageName string
|
||||||
|
GetImageFromFamily bool
|
||||||
GetImageResult *Image
|
GetImageResult *Image
|
||||||
GetImageErr error
|
GetImageErr error
|
||||||
|
|
||||||
GetImageFromProjectProject string
|
GetImageFromProjectProject string
|
||||||
GetImageFromProjectName string
|
GetImageFromProjectName string
|
||||||
|
GetImageFromProjectFromFamily bool
|
||||||
GetImageFromProjectResult *Image
|
GetImageFromProjectResult *Image
|
||||||
GetImageFromProjectErr error
|
GetImageFromProjectErr error
|
||||||
|
|
||||||
|
@ -162,14 +164,16 @@ func (d *DriverMock) DeleteDisk(zone, name string) (<-chan error, error) {
|
||||||
return resultCh, d.DeleteDiskErr
|
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.GetImageName = name
|
||||||
|
d.GetImageFromFamily = fromFamily
|
||||||
return d.GetImageResult, d.GetImageErr
|
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.GetImageFromProjectProject = project
|
||||||
d.GetImageFromProjectName = name
|
d.GetImageFromProjectName = name
|
||||||
|
d.GetImageFromProjectFromFamily = fromFamily
|
||||||
return d.GetImageFromProjectResult, d.GetImageFromProjectErr
|
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) {
|
func getImage(c *Config, d Driver) (*Image, error) {
|
||||||
|
name := c.SourceImageFamily
|
||||||
|
fromFamily := true
|
||||||
|
if c.SourceImage != "" {
|
||||||
|
name = c.SourceImage
|
||||||
|
fromFamily = false
|
||||||
|
}
|
||||||
if c.SourceImageProjectId == "" {
|
if c.SourceImageProjectId == "" {
|
||||||
return d.GetImage(c.SourceImage)
|
return d.GetImage(name, fromFamily)
|
||||||
} else {
|
} else {
|
||||||
return d.GetImageFromProject(c.SourceImageProjectId, c.SourceImage)
|
return d.GetImageFromProject(c.SourceImageProjectId, c.SourceImage, fromFamily)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,45 @@ func TestStepCreateInstance(t *testing.T) {
|
||||||
assert.Equal(t, d.DeleteDiskZone, c.Zone, "Incorrect disk zone passed to driver.")
|
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) {
|
func TestStepCreateInstance_windowsNeedsPassword(t *testing.T) {
|
||||||
|
|
||||||
state := testState(t)
|
state := testState(t)
|
||||||
|
|
|
@ -125,8 +125,11 @@ builder.
|
||||||
- `project_id` (string) - The project ID that will be used to launch instances
|
- `project_id` (string) - The project ID that will be used to launch instances
|
||||||
and store images.
|
and store images.
|
||||||
|
|
||||||
- `source_image` (string) - The source image to use to create the new
|
- `source_image` or `source_image_family` (string) - The source image or
|
||||||
image from. Example: `"debian-7-wheezy-v20150127"`
|
family to use to create the new image from. The image family always returns
|
||||||
|
its latest image that is not deprecated. If both `source_image` and
|
||||||
|
`source_image_family` are specified, `source_image` takes precedence.
|
||||||
|
Example: `"debian-7-wheezy-v20150127"`, `"debian-7"`.
|
||||||
|
|
||||||
- `zone` (string) - The zone in which to launch the instance used to create
|
- `zone` (string) - The zone in which to launch the instance used to create
|
||||||
the image. Example: `"us-central1-a"`
|
the image. Example: `"us-central1-a"`
|
||||||
|
@ -147,7 +150,10 @@ builder.
|
||||||
|
|
||||||
- `image_description` (string) - The description of the resulting image.
|
- `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
|
- `image_name` (string) - The unique name of the resulting image. Defaults to
|
||||||
`"packer-{{timestamp}}"`.
|
`"packer-{{timestamp}}"`.
|
||||||
|
|
Loading…
Reference in New Issue