diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index bb189b19d..13446a092 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -31,6 +31,7 @@ type Config struct { MachineType string `mapstructure:"machine_type"` Metadata map[string]string `mapstructure:"metadata"` Network string `mapstructure:"network"` + Subnetwork string `mapstructure:"subnetwork"` Address string `mapstructure:"address"` Preemptible bool `mapstructure:"preemptible"` SourceImage string `mapstructure:"source_image"` @@ -38,6 +39,7 @@ type Config struct { RawStateTimeout string `mapstructure:"state_timeout"` Tags []string `mapstructure:"tags"` UseInternalIP bool `mapstructure:"use_internal_ip"` + Region string `mapstructure:"region"` Zone string `mapstructure:"zone"` account accountFile @@ -125,6 +127,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend( errs, errors.New("a zone must be specified")) } + if c.Region == "" && len(c.Zone) > 2 { + // get region from Zone + region := c.Zone[:len(c.Zone)-2] + c.Region = region + } stateTimeout, err := time.ParseDuration(c.RawStateTimeout) if err != nil { diff --git a/builder/googlecompute/config_test.go b/builder/googlecompute/config_test.go index 2b450ed9b..5c6ea6334 100644 --- a/builder/googlecompute/config_test.go +++ b/builder/googlecompute/config_test.go @@ -168,6 +168,13 @@ func TestImageName(t *testing.T) { } } +func TestRegion(t *testing.T) { + c, _, _ := NewConfig(testConfig(t)) + if c.Region != "us-east1" { + t.Fatalf("Region should be 'us-east1' given Zone of 'us-east1-a', but is %s", c.Region) + } +} + // Helper stuff below func testConfig(t *testing.T) map[string]interface{} { @@ -175,7 +182,7 @@ func testConfig(t *testing.T) map[string]interface{} { "account_file": testAccountFile(t), "project_id": "hashicorp", "source_image": "foo", - "zone": "us-east-1a", + "zone": "us-east1-a", } } diff --git a/builder/googlecompute/driver.go b/builder/googlecompute/driver.go index 495c1e092..1789a988a 100644 --- a/builder/googlecompute/driver.go +++ b/builder/googlecompute/driver.go @@ -47,8 +47,10 @@ type InstanceConfig struct { Metadata map[string]string Name string Network string + Subnetwork string Address string Preemptible bool Tags []string + Region string Zone string } diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index 91d04255f..f163b744f 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -215,6 +215,25 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) { return nil, err } + // Subnetwork + // Validate Subnetwork config now that we have some info about the network + if !network.AutoCreateSubnetworks && len(network.Subnetworks) > 0 { + // Network appears to be in "custom" mode, so a subnetwork is required + if c.Subnetwork == "" { + return nil, fmt.Errorf("a subnetwork must be specified") + } + } + // Get the subnetwork + subnetworkSelfLink := "" + if c.Subnetwork != "" { + d.ui.Message(fmt.Sprintf("Loading subnetwork: %s for region: %s", c.Subnetwork, c.Region)) + subnetwork, err := d.service.Subnetworks.Get(d.projectId, c.Region, c.Subnetwork).Do() + if err != nil { + return nil, err + } + subnetworkSelfLink = subnetwork.SelfLink + } + // If given a regional ip, get it accessconfig := compute.AccessConfig{ Name: "AccessConfig created by Packer", @@ -235,9 +254,10 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) { // Build up the metadata metadata := make([]*compute.MetadataItems, len(c.Metadata)) for k, v := range c.Metadata { + vCopy := v metadata = append(metadata, &compute.MetadataItems{ Key: k, - Value: &v, + Value: &vCopy, }) } @@ -268,6 +288,7 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) { &accessconfig, }, Network: network.SelfLink, + Subnetwork: subnetworkSelfLink, }, }, Scheduling: &compute.Scheduling{ diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index f3f16a09f..3f6874f6a 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -59,9 +59,11 @@ func (s *StepCreateInstance) Run(state multistep.StateBag) multistep.StepAction Metadata: config.getInstanceMetadata(sshPublicKey), Name: name, Network: config.Network, + Subnetwork: config.Subnetwork, Address: config.Address, Preemptible: config.Preemptible, Tags: config.Tags, + Region: config.Region, Zone: config.Zone, }) diff --git a/website/source/docs/builders/googlecompute.html.markdown b/website/source/docs/builders/googlecompute.html.markdown index cebb1d484..a3171680e 100644 --- a/website/source/docs/builders/googlecompute.html.markdown +++ b/website/source/docs/builders/googlecompute.html.markdown @@ -139,9 +139,18 @@ builder. - `network` (string) - The Google Compute network to use for the launched instance. Defaults to `"default"`. +- `region` (string) - The region in which to launch the instance. Defaults to + to the region hosting the specified `zone`. + - `state_timeout` (string) - The time to wait for instance state changes. Defaults to `"5m"`. +- `subnetwork` (string) - The Google Compute subnetwork to use for the launced + instance. Only required if the `network` has been created with custom + subnetting. + Note, the region of the subnetwork must match the `region` or `zone` in + which the VM is launched. + - `tags` (array of strings) - `use_internal_ip` (boolean) - If true, use the instance's internal IP