google_compute_instance can specified the subnetwork using a self_link (#290)

This commit is contained in:
Vincent Roseberry 2017-08-04 11:00:45 -07:00 committed by GitHub
parent b95fb1b6f9
commit 3fde8cf24e
5 changed files with 83 additions and 31 deletions

View File

@ -13,6 +13,7 @@ import (
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
"regexp"
)
// Global MutexKV
@ -273,6 +274,46 @@ func getNetworkLink(d *schema.ResourceData, config *Config, field string) (strin
}
}
// Reads the "subnetwork" fields from the given resource data and if the value is:
// - a resource URL, returns the string unchanged
// - a subnetwork name, looks up the resource URL using the google client.
//
// If `subnetworkField` is a resource url, `subnetworkProjectField` cannot be set.
// If `subnetworkField` is a subnetwork name, `subnetworkProjectField` will be used
// as the project if set. If not, we fallback on the default project.
func getSubnetworkLink(d *schema.ResourceData, config *Config, subnetworkField, subnetworkProjectField, zoneField string) (string, error) {
if v, ok := d.GetOk(subnetworkField); ok {
subnetwork := v.(string)
r := regexp.MustCompile(SubnetworkLinkRegex)
if r.MatchString(subnetwork) {
return subnetwork, nil
}
var project string
if subnetworkProject, ok := d.GetOk(subnetworkProjectField); ok {
project = subnetworkProject.(string)
} else {
var err error
project, err = getProject(d, config)
if err != nil {
return "", err
}
}
region := getRegionFromZone(d.Get(zoneField).(string))
subnet, err := config.clientCompute.Subnetworks.Get(project, region, subnetwork).Do()
if err != nil {
return "", fmt.Errorf(
"Error referencing subnetwork '%s' in region '%s': %s",
subnetwork, region, err)
}
return subnet.SelfLink, nil
}
return "", nil
}
// getNetworkName reads the "network" field from the given resource data and if the value:
// - is a resource URL, extracts the network name from the URL and returns it
// - is the network name only (i.e not prefixed with http://www.googleapis.com/compute/...), is returned unchanged

View File

@ -9,6 +9,7 @@ import (
"github.com/hashicorp/terraform/helper/validation"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
"regexp"
)
func stringScopeHashcode(v interface{}) int {
@ -278,20 +279,26 @@ func resourceComputeInstance() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"network": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
DiffSuppressFunc: linkDiffSuppress,
ConflictsWith: []string{"network_interface.0.subnetwork", "network_interface.0.subnetwork_project"},
},
"subnetwork": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
DiffSuppressFunc: linkDiffSuppress,
},
"subnetwork_project": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
@ -704,34 +711,21 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
prefix := fmt.Sprintf("network_interface.%d", i)
// Load up the name of this network_interface
networkName := d.Get(prefix + ".network").(string)
subnetworkName := d.Get(prefix + ".subnetwork").(string)
subnetworkProject := d.Get(prefix + ".subnetwork_project").(string)
address := d.Get(prefix + ".address").(string)
var networkLink, subnetworkLink string
if networkName != "" && subnetworkName != "" {
return fmt.Errorf("Cannot specify both network and subnetwork values.")
} else if networkName != "" {
if networkName != "" {
networkLink, err = getNetworkLink(d, config, prefix+".network")
if err != nil {
return fmt.Errorf(
"Error referencing network '%s': %s",
networkName, err)
}
} else {
region := getRegionFromZone(d.Get("zone").(string))
if subnetworkProject == "" {
subnetworkProject = project
}
subnetwork, err := config.clientCompute.Subnetworks.Get(
subnetworkProject, region, subnetworkName).Do()
subnetworkLink, err = getSubnetworkLink(d, config, prefix+".subnetwork", prefix+".subnetwork_project", "zone")
if err != nil {
return fmt.Errorf(
"Error referencing subnetwork '%s' in region '%s': %s",
subnetworkName, region, err)
return err
}
subnetworkLink = subnetwork.SelfLink
}
// Build the networkInterface
@ -961,9 +955,9 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
networkInterfaces = append(networkInterfaces, map[string]interface{}{
"name": iface.Name,
"address": iface.NetworkIP,
"network": d.Get(fmt.Sprintf("network_interface.%d.network", i)),
"subnetwork": d.Get(fmt.Sprintf("network_interface.%d.subnetwork", i)),
"subnetwork_project": d.Get(fmt.Sprintf("network_interface.%d.subnetwork_project", i)),
"network": iface.Network,
"subnetwork": iface.Subnetwork,
"subnetwork_project": getProjectFromSubnetworkLink(iface.Subnetwork),
"access_config": accessConfigs,
})
}
@ -1452,3 +1446,12 @@ func flattenScratchDisk(disk *compute.AttachedDisk) map[string]interface{} {
}
return result
}
func getProjectFromSubnetworkLink(subnetwork string) string {
r := regexp.MustCompile(SubnetworkLinkRegex)
if !r.MatchString(subnetwork) {
return ""
}
return r.FindStringSubmatch(subnetwork)[1]
}

View File

@ -1609,7 +1609,7 @@ resource "google_compute_instance" "foobar" {
}
network_interface {
subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.name}"
subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.self_link}"
access_config { }
}

View File

@ -6,8 +6,14 @@ import (
"regexp"
)
// Copied from the official Google Cloud auto-generated client.
const ProjectRegex = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))"
const (
// Copied from the official Google Cloud auto-generated client.
ProjectRegex = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))"
RegionRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?"
SubnetworkRegex = "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?"
SubnetworkLinkRegex = "projects/(" + ProjectRegex + ")/regions/(" + RegionRegex + ")/subnetworks/(" + SubnetworkRegex + ")$"
)
func validateGCPName(v interface{}, k string) (ws []string, errors []error) {
re := `^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$`

View File

@ -210,12 +210,14 @@ The `network_interface` block supports:
* `network` - (Optional) The name or self_link of the network to attach this interface to.
Either `network` or `subnetwork` must be provided.
* `subnetwork` - (Optional) The name of the subnetwork to attach this interface
to. The subnetwork must exist in the same region this instance will be
* `subnetwork` - (Optional) The name or self_link of the subnetwork to attach this
interface to. The subnetwork must exist in the same region this instance will be
created in. Either `network` or `subnetwork` must be provided.
* `subnetwork_project` - (Optional) The project in which the subnetwork belongs.
If it is not provided, the provider project is used.
If the `subnetwork` is a self_link, this field is ignored in favor of the project
defined in the subnetwork self_link. If the `subnetwork` is a name and this
field is not provided, the provider project is used.
* `address` - (Optional) The private IP address to assign to the instance. If
empty, the address will be automatically assigned.