Give addresses beta support, add internal addresses (#594)

This commit is contained in:
Nic Cope 2017-11-07 13:38:26 -08:00 committed by Vincent Roseberry
parent 06356c2fc7
commit 20fc7cf077
4 changed files with 274 additions and 25 deletions

View File

@ -3,6 +3,7 @@ package google
import (
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
@ -17,7 +18,7 @@ func TestAccComputeAddress_importBasic(t *testing.T) {
CheckDestroy: testAccCheckComputeAddressDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeAddress_basic,
Config: testAccComputeAddress_basic(acctest.RandString(10)),
},
resource.TestStep{
@ -28,3 +29,41 @@ func TestAccComputeAddress_importBasic(t *testing.T) {
},
})
}
/* Disabled pending support for importing beta resources. See:
https://github.com/terraform-providers/terraform-provider-google/issues/694
func TestAccComputeAddress_importInternal(t *testing.T) {
t.Parallel()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeAddressDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeAddress_internal(acctest.RandString(10)),
},
resource.TestStep{
ResourceName: "google_compute_address.internal",
ImportState: true,
ImportStateVerify: true,
},
resource.TestStep{
ResourceName: "google_compute_address.internal_with_subnet",
ImportState: true,
ImportStateVerify: true,
},
resource.TestStep{
ResourceName: "google_compute_address.internal_with_subnet_and_address",
ImportState: true,
ImportStateVerify: true,
},
},
})
}
*/

View File

@ -2,17 +2,29 @@ package google
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1"
"regexp"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
)
const (
addressTypeExternal = "EXTERNAL"
addressTypeInternal = "INTERNAL"
)
var (
computeAddressIdTemplate = "projects/%s/regions/%s/addresses/%s"
computeAddressLinkRegex = regexp.MustCompile("projects/(.+)/regions/(.+)/addresses/(.+)$")
AddressBaseApiVersion = v1
AddressVersionedFeatures = []Feature{
{Version: v0beta, Item: "address_type", DefaultValue: addressTypeExternal},
{Version: v0beta, Item: "subnetwork"},
}
)
func resourceComputeAddress() *schema.Resource {
@ -27,6 +39,8 @@ func resourceComputeAddress() *schema.Resource {
SchemaVersion: 1,
MigrateState: resourceComputeAddressMigrateState,
// These fields mostly correlate to the fields in the beta Address
// resource. See https://cloud.google.com/compute/docs/reference/beta/addresses#resource
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
@ -34,8 +48,29 @@ func resourceComputeAddress() *schema.Resource {
ForceNew: true,
},
"address_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: addressTypeExternal,
ValidateFunc: validation.StringInSlice(
[]string{addressTypeInternal, addressTypeExternal}, false),
},
"subnetwork": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
DiffSuppressFunc: linkDiffSuppress,
},
// address will be computed unless it is specified explicitly.
// address may only be specified for the INTERNAL address_type.
"address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
@ -61,6 +96,7 @@ func resourceComputeAddress() *schema.Resource {
}
func resourceComputeAddressCreate(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, AddressBaseApiVersion, AddressVersionedFeatures)
config := meta.(*Config)
region, err := getRegion(d, config)
@ -74,9 +110,30 @@ func resourceComputeAddressCreate(d *schema.ResourceData, meta interface{}) erro
}
// Build the address parameter
addr := &compute.Address{Name: d.Get("name").(string)}
op, err := config.clientCompute.Addresses.Insert(
project, region, addr).Do()
v0BetaAddress := &computeBeta.Address{
Name: d.Get("name").(string),
AddressType: d.Get("address_type").(string),
Subnetwork: d.Get("subnetwork").(string),
}
if desired, ok := d.GetOk("address"); ok {
v0BetaAddress.Address = desired.(string)
}
var op interface{}
switch computeApiVersion {
case v1:
v1Address := &compute.Address{}
err = Convert(v0BetaAddress, v1Address)
if err != nil {
return err
}
op, err = config.clientCompute.Addresses.Insert(
project, region, v1Address).Do()
case v0beta:
op, err = config.clientComputeBeta.Addresses.Insert(
project, region, v0BetaAddress).Do()
}
if err != nil {
return fmt.Errorf("Error creating address: %s", err)
}
@ -85,10 +142,10 @@ func resourceComputeAddressCreate(d *schema.ResourceData, meta interface{}) erro
d.SetId(computeAddressId{
Project: project,
Region: region,
Name: addr.Name,
Name: v0BetaAddress.Name,
}.canonicalId())
err = computeOperationWait(config.clientCompute, op, project, "Creating Address")
err = computeSharedOperationWait(config.clientCompute, op, project, "Creating Address")
if err != nil {
return err
}
@ -97,6 +154,7 @@ func resourceComputeAddressCreate(d *schema.ResourceData, meta interface{}) erro
}
func resourceComputeAddressRead(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, AddressBaseApiVersion, AddressVersionedFeatures)
config := meta.(*Config)
addressId, err := parseComputeAddressId(d.Id(), config)
@ -104,14 +162,40 @@ func resourceComputeAddressRead(d *schema.ResourceData, meta interface{}) error
return err
}
addr, err := config.clientCompute.Addresses.Get(
addressId.Project, addressId.Region, addressId.Name).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Address %q", d.Get("name").(string)))
addr := &computeBeta.Address{}
switch computeApiVersion {
case v1:
v1Address, err := config.clientCompute.Addresses.Get(
addressId.Project, addressId.Region, addressId.Name).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Address %q", d.Get("name").(string)))
}
err = Convert(v1Address, addr)
if err != nil {
return err
}
case v0beta:
var err error
addr, err = config.clientComputeBeta.Addresses.Get(
addressId.Project, addressId.Region, addressId.Name).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Address %q", d.Get("name").(string)))
}
}
// The v1 API does not include an AddressType field, as it only supports
// external addresses. An empty AddressType implies a v1 address that has
// been converted to v0beta, and thus an EXTERNAL address.
d.Set("address_type", addr.AddressType)
if addr.AddressType == "" {
d.Set("address_type", addressTypeExternal)
}
d.Set("subnetwork", ConvertSelfLinkToV1(addr.Subnetwork))
d.Set("address", addr.Address)
d.Set("self_link", addr.SelfLink)
d.Set("self_link", ConvertSelfLinkToV1(addr.SelfLink))
d.Set("name", addr.Name)
d.Set("region", GetResourceNameFromSelfLink(addr.Region))
@ -119,6 +203,7 @@ func resourceComputeAddressRead(d *schema.ResourceData, meta interface{}) error
}
func resourceComputeAddressDelete(d *schema.ResourceData, meta interface{}) error {
computeApiVersion := getComputeApiVersion(d, AddressBaseApiVersion, AddressVersionedFeatures)
config := meta.(*Config)
addressId, err := parseComputeAddressId(d.Id(), config)
@ -127,14 +212,23 @@ func resourceComputeAddressDelete(d *schema.ResourceData, meta interface{}) erro
}
// Delete the address
log.Printf("[DEBUG] address delete request")
op, err := config.clientCompute.Addresses.Delete(
addressId.Project, addressId.Region, addressId.Name).Do()
if err != nil {
return fmt.Errorf("Error deleting address: %s", err)
var op interface{}
switch computeApiVersion {
case v1:
op, err = config.clientCompute.Addresses.Delete(
addressId.Project, addressId.Region, addressId.Name).Do()
if err != nil {
return fmt.Errorf("Error deleting address: %s", err)
}
case v0beta:
op, err = config.clientComputeBeta.Addresses.Delete(
addressId.Project, addressId.Region, addressId.Name).Do()
if err != nil {
return fmt.Errorf("Error deleting address: %s", err)
}
}
err = computeOperationWait(config.clientCompute, op, addressId.Project, "Deleting Address")
err = computeSharedOperationWait(config.clientCompute, op, addressId.Project, "Deleting Address")
if err != nil {
return err
}

View File

@ -7,6 +7,7 @@ import (
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
)
@ -85,7 +86,7 @@ func TestAccComputeAddress_basic(t *testing.T) {
CheckDestroy: testAccCheckComputeAddressDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeAddress_basic,
Config: testAccComputeAddress_basic(acctest.RandString(10)),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeAddressExists(
"google_compute_address.foobar", &addr),
@ -95,6 +96,30 @@ func TestAccComputeAddress_basic(t *testing.T) {
})
}
func TestAccComputeAddress_internal(t *testing.T) {
var addr computeBeta.Address
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeAddressDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeAddress_internal(acctest.RandString(10)),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeBetaAddressExists("google_compute_address.internal", &addr),
testAccCheckComputeBetaAddressExists("google_compute_address.internal_with_subnet", &addr),
testAccCheckComputeBetaAddressExists("google_compute_address.internal_with_subnet_and_address", &addr),
resource.TestCheckResourceAttr("google_compute_address.internal", "address_type", "INTERNAL"),
resource.TestCheckResourceAttr("google_compute_address.internal_with_subnet", "address_type", "INTERNAL"),
resource.TestCheckResourceAttr("google_compute_address.internal_with_subnet_and_address", "address_type", "INTERNAL"),
resource.TestCheckResourceAttr("google_compute_address.internal_with_subnet_and_address", "address", "10.0.42.42"),
),
},
},
})
}
func testAccCheckComputeAddressDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
@ -146,7 +171,83 @@ func testAccCheckComputeAddressExists(n string, addr *compute.Address) resource.
}
}
var testAccComputeAddress_basic = fmt.Sprintf(`
func testAccCheckComputeBetaAddressExists(n string, addr *computeBeta.Address) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
config := testAccProvider.Meta().(*Config)
addressId, err := parseComputeAddressId(rs.Primary.ID, nil)
found, err := config.clientComputeBeta.Addresses.Get(
config.Project, addressId.Region, addressId.Name).Do()
if err != nil {
return err
}
if found.Name != addressId.Name {
return fmt.Errorf("Addr not found")
}
*addr = *found
return nil
}
}
func testAccComputeAddress_basic(i string) string {
return fmt.Sprintf(`
resource "google_compute_address" "foobar" {
name = "address-test-%s"
}`, acctest.RandString(10))
}`, i)
}
func testAccComputeAddress_internal(i string) string {
return fmt.Sprintf(`
resource "google_compute_address" "internal" {
name = "address-test-internal-%s"
address_type = "INTERNAL"
region = "us-east1"
}
resource "google_compute_network" "default" {
name = "network-test-%s"
}
resource "google_compute_subnetwork" "foo" {
name = "subnetwork-test-%s"
ip_cidr_range = "10.0.0.0/16"
region = "us-east1"
network = "${google_compute_network.default.self_link}"
}
resource "google_compute_address" "internal_with_subnet" {
name = "address-test-internal-with-subnet-%s"
subnetwork = "${google_compute_subnetwork.foo.self_link}"
address_type = "INTERNAL"
region = "us-east1"
}
// We can't test the address alone, because we don't know what IP range the
// default subnetwork uses.
resource "google_compute_address" "internal_with_subnet_and_address" {
name = "address-test-internal-with-subnet-and-address-%s"
subnetwork = "${google_compute_subnetwork.foo.self_link}"
address_type = "INTERNAL"
address = "10.0.42.42"
region = "us-east1"
}`,
i, // google_compute_address.internal name
i, // google_compute_network.default name
i, // google_compute_subnetwork.foo name
i, // google_compute_address.internal_with_subnet_name
i, // google_compute_address.internal_with_subnet_and_address name
)
}

View File

@ -9,8 +9,11 @@ description: |-
# google\_compute\_address
Creates a static IP address resource for Google Compute Engine. For more information see
[the official documentation](https://cloud.google.com/compute/docs/instances-and-network) and
[API](https://cloud.google.com/compute/docs/reference/latest/addresses).
the official documentation for
[external](https://cloud.google.com/compute/docs/instances-and-network) and
[internal](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-internal-ip-address)
static IP reservations, as well as the
[API](https://cloud.google.com/compute/docs/reference/beta/addresses/insert).
## Example Usage
@ -36,6 +39,18 @@ The following arguments are supported:
* `region` - (Optional) The Region in which the created address should reside.
If it is not provided, the provider region is used.
* `address_type` - (Optional) The Address Type that should be configured.
Specify INTERNAL to reserve an internal static IP address EXTERNAL to
specify an external static IP address. Defaults to EXTERNAL if omitted.
* `subnetwork` - (Optional) The self link URI of the subnetwork in which to
create the address. A subnetwork may only be specified for INTERNAL
address types.
* `address` - (Optional) The IP address to reserve. An address may only be
specified for INTERNAL address types. The IP address must be inside the
specified subnetwork, if any.
## Attributes Reference
In addition to the arguments listed above, the following computed attributes are