From 3ac351637112820f75538ee30f3ee9b73ac7f4bd Mon Sep 17 00:00:00 2001 From: Evan Brown Date: Sun, 7 Aug 2016 18:01:31 -0700 Subject: [PATCH] provider/google: Support static private IP addresses (#6310) * provider/google: Support static private IP addresses The private address of an instance's network interface may now be specified. If no value is provided, an address will be chosen by Google Compute Engine and that value will be read into Terraform state. * docs: GCE private static IP address information --- .../google/resource_compute_instance.go | 6 +- .../google/resource_compute_instance_test.go | 122 ++++++++++++++++++ .../google/r/compute_instance.html.markdown | 5 +- 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/builtin/providers/google/resource_compute_instance.go b/builtin/providers/google/resource_compute_instance.go index cb06822fa6..9a4387b520 100644 --- a/builtin/providers/google/resource_compute_instance.go +++ b/builtin/providers/google/resource_compute_instance.go @@ -153,6 +153,8 @@ func resourceComputeInstance() *schema.Resource { "address": &schema.Schema{ Type: schema.TypeString, + Optional: true, + ForceNew: true, Computed: true, }, @@ -467,9 +469,10 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err networkInterfaces = make([]*compute.NetworkInterface, 0, networkInterfacesCount) for i := 0; i < networkInterfacesCount; i++ { prefix := fmt.Sprintf("network_interface.%d", i) - // Load up the name of this network_interfac + // Load up the name of this network_interface networkName := d.Get(prefix + ".network").(string) subnetworkName := d.Get(prefix + ".subnetwork").(string) + address := d.Get(prefix + ".address").(string) var networkLink, subnetworkLink string if networkName != "" && subnetworkName != "" { @@ -499,6 +502,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err var iface compute.NetworkInterface iface.Network = networkLink iface.Subnetwork = subnetworkLink + iface.NetworkIP = address // Handle access_config structs accessConfigsCount := d.Get(prefix + ".access_config.#").(int) diff --git a/builtin/providers/google/resource_compute_instance_test.go b/builtin/providers/google/resource_compute_instance_test.go index c133b97e74..a20e127e1c 100644 --- a/builtin/providers/google/resource_compute_instance_test.go +++ b/builtin/providers/google/resource_compute_instance_test.go @@ -371,6 +371,47 @@ func TestAccComputeInstance_subnet_custom(t *testing.T) { }) } +func TestAccComputeInstance_address_auto(t *testing.T) { + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_address_auto(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasAnyAddress(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_address_custom(t *testing.T) { + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var address = "10.0.200.200" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_address_custom(instanceName, address), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasAddress(&instance, address), + ), + }, + }, + }) +} func testAccCheckComputeInstanceDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) @@ -528,6 +569,30 @@ func testAccCheckComputeInstanceHasSubnet(instance *compute.Instance) resource.T } } +func testAccCheckComputeInstanceHasAnyAddress(instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, i := range instance.NetworkInterfaces { + if i.NetworkIP == "" { + return fmt.Errorf("no address") + } + } + + return nil + } +} + +func testAccCheckComputeInstanceHasAddress(instance *compute.Instance, address string) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, i := range instance.NetworkInterfaces { + if i.NetworkIP != address { + return fmt.Errorf("Wrong address found: expected %v, got %v", address, i.NetworkIP) + } + } + + return nil + } +} + func testAccComputeInstance_basic_deprecated_network(instance string) string { return fmt.Sprintf(` resource "google_compute_instance" "foobar" { @@ -880,3 +945,60 @@ func testAccComputeInstance_subnet_custom(instance string) string { }`, acctest.RandString(10), acctest.RandString(10), instance) } + +func testAccComputeInstance_address_auto(instance string) string { + return fmt.Sprintf(` + resource "google_compute_network" "inst-test-network" { + name = "inst-test-network-%s" + } + resource "google_compute_subnetwork" "inst-test-subnetwork" { + name = "inst-test-subnetwork-%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + network = "${google_compute_network.inst-test-network.self_link}" + } + resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + disk { + image = "debian-7-wheezy-v20160301" + } + + network_interface { + subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.name}" + access_config { } + } + + }`, acctest.RandString(10), acctest.RandString(10), instance) +} + +func testAccComputeInstance_address_custom(instance, address string) string { + return fmt.Sprintf(` + resource "google_compute_network" "inst-test-network" { + name = "inst-test-network-%s" + } + resource "google_compute_subnetwork" "inst-test-subnetwork" { + name = "inst-test-subnetwork-%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + network = "${google_compute_network.inst-test-network.self_link}" + } + resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + disk { + image = "debian-7-wheezy-v20160301" + } + + network_interface { + subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.name}" + address = "%s" + access_config { } + } + + }`, acctest.RandString(10), acctest.RandString(10), instance, address) +} diff --git a/website/source/docs/providers/google/r/compute_instance.html.markdown b/website/source/docs/providers/google/r/compute_instance.html.markdown index 77f89f360a..3164f10610 100644 --- a/website/source/docs/providers/google/r/compute_instance.html.markdown +++ b/website/source/docs/providers/google/r/compute_instance.html.markdown @@ -139,6 +139,9 @@ The `network_interface` block supports: to. The subnetwork must exist in the same region this instance will be created in. Either `network` or `subnetwork` must be provided. +* `address` - (Optional) The private IP address to assign to the instance. If + empty, the address will be automatically assigned. + * `access_config` - (Optional) Access configurations, i.e. IPs via which this instance can be accessed via the Internet. Omit to ensure that the instance is not accessible from the Internet (this means that ssh provisioners will @@ -189,7 +192,7 @@ exported: * `tags_fingerprint` - The unique fingerprint of the tags. -* `network_interface.0.address` - The internal ip address of the instance (usually on the 10.x.x.x range). +* `network_interface.0.address` - The internal ip address of the instance, either manually or dynamically assigned. * `network_interface.0.access_config.0.assigned_nat_ip` - If the instance has an access config, either the given external ip (in the `nat_ip` field) or the ephemeral (generated) ip (if you didn't provide one).