diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index e97f68984..9a6072a25 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -149,8 +149,9 @@ type Config struct { // family always returns its latest image that is not deprecated. Example: // "debian-8". SourceImageFamily string `mapstructure:"source_image_family" required:"true"` - // The project ID of the project containing the source image. - SourceImageProjectId string `mapstructure:"source_image_project_id" required:"false"` + // A list of project IDs to search for the source image. Packer will search the first + // project ID in the list first, and fall back to the next in the list, until it finds the source image. + SourceImageProjectId []string `mapstructure:"source_image_project_id" required:"false"` // The path to a startup script to run on the VM from which the image will // be made. StartupScriptFile string `mapstructure:"startup_script_file" required:"false"` diff --git a/builder/googlecompute/config.hcl2spec.go b/builder/googlecompute/config.hcl2spec.go index 956c8e587..69f4a1b3c 100644 --- a/builder/googlecompute/config.hcl2spec.go +++ b/builder/googlecompute/config.hcl2spec.go @@ -89,7 +89,7 @@ type FlatConfig struct { ServiceAccountEmail *string `mapstructure:"service_account_email" required:"false" cty:"service_account_email"` SourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image"` SourceImageFamily *string `mapstructure:"source_image_family" required:"true" cty:"source_image_family"` - SourceImageProjectId *string `mapstructure:"source_image_project_id" required:"false" cty:"source_image_project_id"` + SourceImageProjectId []string `mapstructure:"source_image_project_id" required:"false" cty:"source_image_project_id"` StartupScriptFile *string `mapstructure:"startup_script_file" required:"false" cty:"startup_script_file"` Subnetwork *string `mapstructure:"subnetwork" required:"false" cty:"subnetwork"` Tags []string `mapstructure:"tags" required:"false" cty:"tags"` @@ -190,7 +190,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "service_account_email": &hcldec.AttrSpec{Name: "service_account_email", Type: cty.String, Required: false}, "source_image": &hcldec.AttrSpec{Name: "source_image", Type: cty.String, Required: false}, "source_image_family": &hcldec.AttrSpec{Name: "source_image_family", Type: cty.String, Required: false}, - "source_image_project_id": &hcldec.AttrSpec{Name: "source_image_project_id", Type: cty.String, Required: false}, + "source_image_project_id": &hcldec.AttrSpec{Name: "source_image_project_id", Type: cty.List(cty.String), Required: false}, "startup_script_file": &hcldec.AttrSpec{Name: "startup_script_file", Type: cty.String, Required: false}, "subnetwork": &hcldec.AttrSpec{Name: "subnetwork", Type: cty.String, Required: false}, "tags": &hcldec.AttrSpec{Name: "tags", Type: cty.List(cty.String), Required: false}, diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index 7ede0ae2b..e7d34e10d 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -29,6 +29,11 @@ type Driver interface { // particular image. GetImage(name string, fromFamily bool) (*Image, error) + // GetImageFromProject gets an image from a specific projects. + // Returns the image from the first project in slice it can find one + // If fromFamily is true, name designates an image family instead of a particular image. + GetImageFromProjects(project []string, name string, fromFamily bool) (*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) diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index eea002d3a..ea68b6d65 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -210,8 +210,8 @@ func (d *driverGCE) DeleteDisk(zone, name string) (<-chan error, error) { go waitForState(errCh, "DONE", d.refreshZoneOp(zone, op)) return errCh, nil } - func (d *driverGCE) GetImage(name string, fromFamily bool) (*Image, error) { + projects := []string{ d.projectId, // Public projects, drawn from @@ -234,6 +234,9 @@ func (d *driverGCE) GetImage(name string, fromFamily bool) (*Image, error) { "google-containers", "opensuse-cloud", } + return d.GetImageFromProjects(projects, name, fromFamily) +} +func (d *driverGCE) GetImageFromProjects(projects []string, name string, fromFamily bool) (*Image, error) { var errs error for _, project := range projects { image, err := d.GetImageFromProject(project, name, fromFamily) diff --git a/builder/googlecompute/driver_mock.go b/builder/googlecompute/driver_mock.go index fed2cccab..080772e5b 100644 --- a/builder/googlecompute/driver_mock.go +++ b/builder/googlecompute/driver_mock.go @@ -36,10 +36,11 @@ type DriverMock struct { DeleteDiskErrCh <-chan error DeleteDiskErr error - GetImageName string - GetImageFromFamily bool - GetImageResult *Image - GetImageErr error + GetImageName string + GetImageSourceProjects []string + GetImageFromFamily bool + GetImageResult *Image + GetImageErr error GetImageFromProjectProject string GetImageFromProjectName string @@ -179,6 +180,12 @@ func (d *DriverMock) GetImage(name string, fromFamily bool) (*Image, error) { d.GetImageFromFamily = fromFamily return d.GetImageResult, d.GetImageErr } +func (d *DriverMock) GetImageFromProjects(projects []string, name string, fromFamily bool) (*Image, error) { + d.GetImageSourceProjects = projects + d.GetImageFromProjectName = name + d.GetImageFromProjectFromFamily = fromFamily + return d.GetImageFromProjectResult, d.GetImageFromProjectErr +} func (d *DriverMock) GetImageFromProject(project, name string, fromFamily bool) (*Image, error) { d.GetImageFromProjectProject = project diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index b2a15a389..9421a2420 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -85,10 +85,10 @@ func getImage(c *Config, d Driver) (*Image, error) { name = c.SourceImage fromFamily = false } - if c.SourceImageProjectId == "" { + if len(c.SourceImageProjectId) == 0 { return d.GetImage(name, fromFamily) } else { - return d.GetImageFromProject(c.SourceImageProjectId, name, fromFamily) + return d.GetImageFromProjects(c.SourceImageProjectId, name, fromFamily) } } diff --git a/post-processor/googlecompute-export/post-processor.go b/post-processor/googlecompute-export/post-processor.go index 16453742a..4f21e4fde 100644 --- a/post-processor/googlecompute-export/post-processor.go +++ b/post-processor/googlecompute-export/post-processor.go @@ -145,7 +145,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact NetworkProjectId: builderProjectId, StateTimeout: 5 * time.Minute, SourceImageFamily: "debian-9-worker", - SourceImageProjectId: "compute-image-tools", + SourceImageProjectId: []string{"compute-image-tools"}, Subnetwork: p.config.Subnetwork, Zone: p.config.Zone, Scopes: []string{ diff --git a/website/source/partials/builder/googlecompute/_Config-not-required.html.md b/website/source/partials/builder/googlecompute/_Config-not-required.html.md index 956d03014..3314b1b57 100644 --- a/website/source/partials/builder/googlecompute/_Config-not-required.html.md +++ b/website/source/partials/builder/googlecompute/_Config-not-required.html.md @@ -108,7 +108,8 @@ project's default service account unless disable_default_service_account is true. -- `source_image_project_id` (string) - The project ID of the project containing the source image. +- `source_image_project_id` ([]string) - A list of project IDs to search for the source image. Packer will search the first + project ID in the list first, and fall back to the next in the list, until it finds the source image. - `startup_script_file` (string) - The path to a startup script to run on the VM from which the image will be made.