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.
This commit is contained in:
Matt Page 2014-08-20 10:20:28 -07:00
parent 70cc555133
commit 8f237b7b94
4 changed files with 56 additions and 41 deletions

View File

@ -16,25 +16,26 @@ import (
type Config struct { type Config struct {
common.PackerConfig `mapstructure:",squash"` common.PackerConfig `mapstructure:",squash"`
BucketName string `mapstructure:"bucket_name"` BucketName string `mapstructure:"bucket_name"`
ClientSecretsFile string `mapstructure:"client_secrets_file"` ClientSecretsFile string `mapstructure:"client_secrets_file"`
DiskSizeGb int64 `mapstructure:"disk_size"` DiskSizeGb int64 `mapstructure:"disk_size"`
ImageName string `mapstructure:"image_name"` ImageName string `mapstructure:"image_name"`
ImageDescription string `mapstructure:"image_description"` ImageDescription string `mapstructure:"image_description"`
InstanceName string `mapstructure:"instance_name"` InstanceName string `mapstructure:"instance_name"`
MachineType string `mapstructure:"machine_type"` MachineType string `mapstructure:"machine_type"`
Metadata map[string]string `mapstructure:"metadata"` Metadata map[string]string `mapstructure:"metadata"`
Network string `mapstructure:"network"` Network string `mapstructure:"network"`
Passphrase string `mapstructure:"passphrase"` Passphrase string `mapstructure:"passphrase"`
PrivateKeyFile string `mapstructure:"private_key_file"` PrivateKeyFile string `mapstructure:"private_key_file"`
ProjectId string `mapstructure:"project_id"` ProjectId string `mapstructure:"project_id"`
SourceImage string `mapstructure:"source_image"` SourceImage string `mapstructure:"source_image"`
SSHUsername string `mapstructure:"ssh_username"` SourceImageProjectId string `mapstructure:"source_image_project_id"`
SSHPort uint `mapstructure:"ssh_port"` SSHUsername string `mapstructure:"ssh_username"`
RawSSHTimeout string `mapstructure:"ssh_timeout"` SSHPort uint `mapstructure:"ssh_port"`
RawStateTimeout string `mapstructure:"state_timeout"` RawSSHTimeout string `mapstructure:"ssh_timeout"`
Tags []string `mapstructure:"tags"` RawStateTimeout string `mapstructure:"state_timeout"`
Zone string `mapstructure:"zone"` Tags []string `mapstructure:"tags"`
Zone string `mapstructure:"zone"`
clientSecrets *clientSecrets clientSecrets *clientSecrets
instanceName string instanceName string
@ -103,21 +104,22 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
// Process Templates // Process Templates
templates := map[string]*string{ templates := map[string]*string{
"bucket_name": &c.BucketName, "bucket_name": &c.BucketName,
"client_secrets_file": &c.ClientSecretsFile, "client_secrets_file": &c.ClientSecretsFile,
"image_name": &c.ImageName, "image_name": &c.ImageName,
"image_description": &c.ImageDescription, "image_description": &c.ImageDescription,
"instance_name": &c.InstanceName, "instance_name": &c.InstanceName,
"machine_type": &c.MachineType, "machine_type": &c.MachineType,
"network": &c.Network, "network": &c.Network,
"passphrase": &c.Passphrase, "passphrase": &c.Passphrase,
"private_key_file": &c.PrivateKeyFile, "private_key_file": &c.PrivateKeyFile,
"project_id": &c.ProjectId, "project_id": &c.ProjectId,
"source_image": &c.SourceImage, "source_image": &c.SourceImage,
"ssh_username": &c.SSHUsername, "source_image_project_id": &c.SourceImageProjectId,
"ssh_timeout": &c.RawSSHTimeout, "ssh_username": &c.SSHUsername,
"state_timeout": &c.RawStateTimeout, "ssh_timeout": &c.RawSSHTimeout,
"zone": &c.Zone, "state_timeout": &c.RawStateTimeout,
"zone": &c.Zone,
} }
for n, ptr := range templates { for n, ptr := range templates {

View File

@ -23,10 +23,15 @@ type Driver interface {
WaitForInstance(state, zone, name string) <-chan error WaitForInstance(state, zone, name string) <-chan error
} }
type Image struct {
Name string
ProjectId string
}
type InstanceConfig struct { type InstanceConfig struct {
Description string Description string
DiskSizeGb int64 DiskSizeGb int64
Image string Image Image
MachineType string MachineType string
Metadata map[string]string Metadata map[string]string
Name string Name string

View File

@ -134,7 +134,7 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
} }
// Get the image // 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) image, err := d.getImage(c.Image)
if err != nil { if err != nil {
return nil, err return nil, err
@ -229,17 +229,17 @@ func (d *driverGCE) WaitForInstance(state, zone, name string) <-chan error {
return errCh return errCh
} }
func (d *driverGCE) getImage(name string) (image *compute.Image, err error) { func (d *driverGCE) getImage(img Image) (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"} projects := []string{img.ProjectId, "centos-cloud", "coreos-cloud", "debian-cloud", "google-containers", "opensuse-cloud", "rhel-cloud", "suse-cloud", "windows-cloud"}
for _, project := range projects { 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 != "" { if err == nil && image != nil && image.SelfLink != "" {
return return
} }
image = nil 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 return
} }

View File

@ -16,6 +16,14 @@ type StepCreateInstance struct {
instanceName string 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. // Run executes the Packer build step that creates a GCE instance.
func (s *StepCreateInstance) Run(state multistep.StateBag) multistep.StepAction { func (s *StepCreateInstance) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
@ -29,7 +37,7 @@ func (s *StepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
errCh, err := driver.RunInstance(&InstanceConfig{ errCh, err := driver.RunInstance(&InstanceConfig{
Description: "New instance created by Packer", Description: "New instance created by Packer",
DiskSizeGb: config.DiskSizeGb, DiskSizeGb: config.DiskSizeGb,
Image: config.SourceImage, Image: config.getImage(),
MachineType: config.MachineType, MachineType: config.MachineType,
Metadata: map[string]string{ Metadata: map[string]string{
"sshKeys": fmt.Sprintf("%s:%s", config.SSHUsername, sshPublicKey), "sshKeys": fmt.Sprintf("%s:%s", config.SSHUsername, sshPublicKey),