From 8f237b7b94d32380af4f906ad3897157c242c29c Mon Sep 17 00:00:00 2001 From: Matt Page Date: Wed, 20 Aug 2014 10:20:28 -0700 Subject: [PATCH] Allow specifying project for source images in GCE Within GCE, images may be shared across projects. Prior to this commit, there was no way to inform the GCE builder that a source image belonged to a specific project. This adds an optional 'source_image_project_id' key to the GCE builder config. --- builder/googlecompute/config.go | 70 ++++++++++--------- builder/googlecompute/driver.go | 7 +- builder/googlecompute/driver_gce.go | 10 +-- builder/googlecompute/step_create_instance.go | 10 ++- 4 files changed, 56 insertions(+), 41 deletions(-) diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index 803963eb3..52df02f3a 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -16,25 +16,26 @@ import ( type Config struct { common.PackerConfig `mapstructure:",squash"` - BucketName string `mapstructure:"bucket_name"` - ClientSecretsFile string `mapstructure:"client_secrets_file"` - DiskSizeGb int64 `mapstructure:"disk_size"` - ImageName string `mapstructure:"image_name"` - ImageDescription string `mapstructure:"image_description"` - InstanceName string `mapstructure:"instance_name"` - MachineType string `mapstructure:"machine_type"` - Metadata map[string]string `mapstructure:"metadata"` - Network string `mapstructure:"network"` - Passphrase string `mapstructure:"passphrase"` - PrivateKeyFile string `mapstructure:"private_key_file"` - ProjectId string `mapstructure:"project_id"` - SourceImage string `mapstructure:"source_image"` - SSHUsername string `mapstructure:"ssh_username"` - SSHPort uint `mapstructure:"ssh_port"` - RawSSHTimeout string `mapstructure:"ssh_timeout"` - RawStateTimeout string `mapstructure:"state_timeout"` - Tags []string `mapstructure:"tags"` - Zone string `mapstructure:"zone"` + BucketName string `mapstructure:"bucket_name"` + ClientSecretsFile string `mapstructure:"client_secrets_file"` + DiskSizeGb int64 `mapstructure:"disk_size"` + ImageName string `mapstructure:"image_name"` + ImageDescription string `mapstructure:"image_description"` + InstanceName string `mapstructure:"instance_name"` + MachineType string `mapstructure:"machine_type"` + Metadata map[string]string `mapstructure:"metadata"` + Network string `mapstructure:"network"` + Passphrase string `mapstructure:"passphrase"` + PrivateKeyFile string `mapstructure:"private_key_file"` + ProjectId string `mapstructure:"project_id"` + SourceImage string `mapstructure:"source_image"` + SourceImageProjectId string `mapstructure:"source_image_project_id"` + SSHUsername string `mapstructure:"ssh_username"` + SSHPort uint `mapstructure:"ssh_port"` + RawSSHTimeout string `mapstructure:"ssh_timeout"` + RawStateTimeout string `mapstructure:"state_timeout"` + Tags []string `mapstructure:"tags"` + Zone string `mapstructure:"zone"` clientSecrets *clientSecrets instanceName string @@ -103,21 +104,22 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Process Templates templates := map[string]*string{ - "bucket_name": &c.BucketName, - "client_secrets_file": &c.ClientSecretsFile, - "image_name": &c.ImageName, - "image_description": &c.ImageDescription, - "instance_name": &c.InstanceName, - "machine_type": &c.MachineType, - "network": &c.Network, - "passphrase": &c.Passphrase, - "private_key_file": &c.PrivateKeyFile, - "project_id": &c.ProjectId, - "source_image": &c.SourceImage, - "ssh_username": &c.SSHUsername, - "ssh_timeout": &c.RawSSHTimeout, - "state_timeout": &c.RawStateTimeout, - "zone": &c.Zone, + "bucket_name": &c.BucketName, + "client_secrets_file": &c.ClientSecretsFile, + "image_name": &c.ImageName, + "image_description": &c.ImageDescription, + "instance_name": &c.InstanceName, + "machine_type": &c.MachineType, + "network": &c.Network, + "passphrase": &c.Passphrase, + "private_key_file": &c.PrivateKeyFile, + "project_id": &c.ProjectId, + "source_image": &c.SourceImage, + "source_image_project_id": &c.SourceImageProjectId, + "ssh_username": &c.SSHUsername, + "ssh_timeout": &c.RawSSHTimeout, + "state_timeout": &c.RawStateTimeout, + "zone": &c.Zone, } for n, ptr := range templates { diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index f4b6006a9..da1b52088 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -23,10 +23,15 @@ type Driver interface { WaitForInstance(state, zone, name string) <-chan error } +type Image struct { + Name string + ProjectId string +} + type InstanceConfig struct { Description string DiskSizeGb int64 - Image string + Image Image MachineType string Metadata map[string]string Name string diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index 65d7c7bcd..1439b7e3a 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -134,7 +134,7 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) { } // Get the image - d.ui.Message(fmt.Sprintf("Loading image: %s", c.Image)) + d.ui.Message(fmt.Sprintf("Loading image: %s in project %s", c.Image.Name, c.Image.ProjectId)) image, err := d.getImage(c.Image) if err != nil { return nil, err @@ -229,17 +229,17 @@ func (d *driverGCE) WaitForInstance(state, zone, name string) <-chan error { return errCh } -func (d *driverGCE) getImage(name string) (image *compute.Image, err error) { - projects := []string{d.projectId, "centos-cloud", "coreos-cloud", "debian-cloud", "google-containers", "opensuse-cloud", "rhel-cloud", "suse-cloud", "windows-cloud"} +func (d *driverGCE) getImage(img Image) (image *compute.Image, err error) { + projects := []string{img.ProjectId, "centos-cloud", "coreos-cloud", "debian-cloud", "google-containers", "opensuse-cloud", "rhel-cloud", "suse-cloud", "windows-cloud"} for _, project := range projects { - image, err = d.service.Images.Get(project, name).Do() + image, err = d.service.Images.Get(project, img.Name).Do() if err == nil && image != nil && image.SelfLink != "" { return } image = nil } - err = fmt.Errorf("Image %s could not be found in any of these projects: %s", name, projects) + err = fmt.Errorf("Image %s could not be found in any of these projects: %s", img.Name, projects) return } diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index 3eab82021..d35dc4157 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -16,6 +16,14 @@ type StepCreateInstance struct { instanceName string } +func (config *Config) getImage() (Image) { + project := config.ProjectId + if config.SourceImageProjectId != "" { + project = config.SourceImageProjectId + } + return Image{Name: config.SourceImage, ProjectId: project} +} + // Run executes the Packer build step that creates a GCE instance. func (s *StepCreateInstance) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) @@ -29,7 +37,7 @@ func (s *StepCreateInstance) Run(state multistep.StateBag) multistep.StepAction errCh, err := driver.RunInstance(&InstanceConfig{ Description: "New instance created by Packer", DiskSizeGb: config.DiskSizeGb, - Image: config.SourceImage, + Image: config.getImage(), MachineType: config.MachineType, Metadata: map[string]string{ "sshKeys": fmt.Sprintf("%s:%s", config.SSHUsername, sshPublicKey),