diff --git a/google/resource_compute_global_address.go b/google/resource_compute_global_address.go index c89667ee101..bbb8015cf23 100644 --- a/google/resource_compute_global_address.go +++ b/google/resource_compute_global_address.go @@ -18,6 +18,7 @@ import ( "fmt" "log" "reflect" + "strconv" "time" "github.com/hashicorp/terraform/helper/schema" @@ -72,6 +73,23 @@ func resourceComputeGlobalAddress() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{"IPV4", "IPV6", ""}, false), DiffSuppressFunc: emptyOrDefaultStringSuppress("IPV4"), }, + "network": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, + "prefix_length": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "purpose": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"GCE_ENDPOINT", "DNS_RESOLVER", "VPC_PEERING", "NAT_AUTO", ""}, false), + }, "creation_timestamp": { Type: schema.TypeString, Computed: true, @@ -118,12 +136,30 @@ func resourceComputeGlobalAddressCreate(d *schema.ResourceData, meta interface{} } else if v, ok := d.GetOkExists("ip_version"); !isEmptyValue(reflect.ValueOf(ipVersionProp)) && (ok || !reflect.DeepEqual(v, ipVersionProp)) { obj["ipVersion"] = ipVersionProp } + prefixLengthProp, err := expandComputeGlobalAddressPrefixLength(d.Get("prefix_length"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("prefix_length"); !isEmptyValue(reflect.ValueOf(prefixLengthProp)) && (ok || !reflect.DeepEqual(v, prefixLengthProp)) { + obj["prefixLength"] = prefixLengthProp + } addressTypeProp, err := expandComputeGlobalAddressAddressType(d.Get("address_type"), d, config) if err != nil { return err } else if v, ok := d.GetOkExists("address_type"); !isEmptyValue(reflect.ValueOf(addressTypeProp)) && (ok || !reflect.DeepEqual(v, addressTypeProp)) { obj["addressType"] = addressTypeProp } + purposeProp, err := expandComputeGlobalAddressPurpose(d.Get("purpose"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("purpose"); !isEmptyValue(reflect.ValueOf(purposeProp)) && (ok || !reflect.DeepEqual(v, purposeProp)) { + obj["purpose"] = purposeProp + } + networkProp, err := expandComputeGlobalAddressNetwork(d.Get("network"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("network"); !isEmptyValue(reflect.ValueOf(networkProp)) && (ok || !reflect.DeepEqual(v, networkProp)) { + obj["network"] = networkProp + } url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/global/addresses") if err != nil { @@ -204,9 +240,18 @@ func resourceComputeGlobalAddressRead(d *schema.ResourceData, meta interface{}) if err := d.Set("ip_version", flattenComputeGlobalAddressIpVersion(res["ipVersion"], d)); err != nil { return fmt.Errorf("Error reading GlobalAddress: %s", err) } + if err := d.Set("prefix_length", flattenComputeGlobalAddressPrefixLength(res["prefixLength"], d)); err != nil { + return fmt.Errorf("Error reading GlobalAddress: %s", err) + } if err := d.Set("address_type", flattenComputeGlobalAddressAddressType(res["addressType"], d)); err != nil { return fmt.Errorf("Error reading GlobalAddress: %s", err) } + if err := d.Set("purpose", flattenComputeGlobalAddressPurpose(res["purpose"], d)); err != nil { + return fmt.Errorf("Error reading GlobalAddress: %s", err) + } + if err := d.Set("network", flattenComputeGlobalAddressNetwork(res["network"], d)); err != nil { + return fmt.Errorf("Error reading GlobalAddress: %s", err) + } if err := d.Set("self_link", ConvertSelfLinkToV1(res["selfLink"].(string))); err != nil { return fmt.Errorf("Error reading GlobalAddress: %s", err) } @@ -287,10 +332,31 @@ func flattenComputeGlobalAddressIpVersion(v interface{}, d *schema.ResourceData) return v } +func flattenComputeGlobalAddressPrefixLength(v interface{}, d *schema.ResourceData) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } // let terraform core handle it if we can't convert the string to an int. + } + return v +} + func flattenComputeGlobalAddressAddressType(v interface{}, d *schema.ResourceData) interface{} { return v } +func flattenComputeGlobalAddressPurpose(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeGlobalAddressNetwork(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + func expandComputeGlobalAddressAddress(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -307,6 +373,22 @@ func expandComputeGlobalAddressIpVersion(v interface{}, d TerraformResourceData, return v, nil } +func expandComputeGlobalAddressPrefixLength(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandComputeGlobalAddressAddressType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } + +func expandComputeGlobalAddressPurpose(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeGlobalAddressNetwork(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseGlobalFieldValue("networks", v.(string), "project", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for network: %s", err) + } + return f.RelativeLink(), nil +} diff --git a/google/resource_compute_global_address_test.go b/google/resource_compute_global_address_test.go index 3222cb5abba..00d6f5fafb9 100644 --- a/google/resource_compute_global_address_test.go +++ b/google/resource_compute_global_address_test.go @@ -38,6 +38,26 @@ func TestAccComputeGlobalAddress_ipv6(t *testing.T) { }) } +func TestAccComputeGlobalAddress_internal(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeGlobalAddressDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeGlobalAddress_internal(), + }, + { + ResourceName: "google_compute_global_address.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckComputeGlobalAddressExists(n string, addr *compute.Address) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -101,3 +121,20 @@ resource "google_compute_global_address" "foobar" { ip_version = "IPV6" }`, acctest.RandString(10)) } + +func testAccComputeGlobalAddress_internal() string { + return fmt.Sprintf(` +resource "google_compute_network" "foobar" { + name = "address-test-%s" +} + + +resource "google_compute_global_address" "foobar" { + name = "address-test-%s" + address_type = "INTERNAL" + purpose = "VPC_PEERING" + prefix_length = 24 + address = "172.20.181.0" + network = "${google_compute_network.foobar.self_link}" +}`, acctest.RandString(10), acctest.RandString(10)) +} diff --git a/website/docs/r/compute_global_address.html.markdown b/website/docs/r/compute_global_address.html.markdown index fd1e013cefd..da716358bb1 100644 --- a/website/docs/r/compute_global_address.html.markdown +++ b/website/docs/r/compute_global_address.html.markdown @@ -79,12 +79,33 @@ The following arguments are supported: The IP Version that will be used by this address. Valid options are `IPV4` or `IPV6`. The default value is `IPV4`. +* `prefix_length` - + (Optional) + The prefix length of the IP range. If not present, it means the + address field is a single IP address. + This field is not applicable to addresses with addressType=EXTERNAL. + * `address_type` - (Optional) The type of the address to reserve, default is EXTERNAL. * EXTERNAL indicates public/external single IP address. * INTERNAL indicates internal IP ranges belonging to some network. +* `purpose` - + (Optional) + The purpose of the resource. For global internal addresses it can be + * GCE_ENDPOINT for addresses that are used by VM instances, alias IP ranges, internal load balancers, and similar resources. + * DNS_RESOLVER for a DNS resolver address in a subnetwork + * VPC_PEERING for addresses that are reserved for VPC peer networks. + * NAT_AUTO for addresses that are external IP addresses automatically reserved for Cloud NAT. + +* `network` - + (Optional) + The URL of the network in which to reserve the IP range. The IP range + must be in RFC1918 space. The network cannot be deleted if there are + any reserved IP ranges referring to it. + This should only be set when using an Internal address. + * `project` - (Optional) The ID of the project in which the resource belongs. If it is not provided, the provider project is used.