mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-04 17:51:11 +00:00
google_compute_instance can specified the subnetwork using a self_link (#290)
This commit is contained in:
parent
b95fb1b6f9
commit
3fde8cf24e
@ -13,6 +13,7 @@ import (
|
|||||||
computeBeta "google.golang.org/api/compute/v0.beta"
|
computeBeta "google.golang.org/api/compute/v0.beta"
|
||||||
"google.golang.org/api/compute/v1"
|
"google.golang.org/api/compute/v1"
|
||||||
"google.golang.org/api/googleapi"
|
"google.golang.org/api/googleapi"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global MutexKV
|
// 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:
|
// 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 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
|
// - is the network name only (i.e not prefixed with http://www.googleapis.com/compute/...), is returned unchanged
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/hashicorp/terraform/helper/validation"
|
"github.com/hashicorp/terraform/helper/validation"
|
||||||
"google.golang.org/api/compute/v1"
|
"google.golang.org/api/compute/v1"
|
||||||
"google.golang.org/api/googleapi"
|
"google.golang.org/api/googleapi"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func stringScopeHashcode(v interface{}) int {
|
func stringScopeHashcode(v interface{}) int {
|
||||||
@ -278,20 +279,26 @@ func resourceComputeInstance() *schema.Resource {
|
|||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"network": &schema.Schema{
|
"network": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
DiffSuppressFunc: linkDiffSuppress,
|
||||||
|
ConflictsWith: []string{"network_interface.0.subnetwork", "network_interface.0.subnetwork_project"},
|
||||||
},
|
},
|
||||||
|
|
||||||
"subnetwork": &schema.Schema{
|
"subnetwork": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
DiffSuppressFunc: linkDiffSuppress,
|
||||||
},
|
},
|
||||||
|
|
||||||
"subnetwork_project": &schema.Schema{
|
"subnetwork_project": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -704,34 +711,21 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
|
|||||||
prefix := fmt.Sprintf("network_interface.%d", i)
|
prefix := fmt.Sprintf("network_interface.%d", i)
|
||||||
// Load up the name of this network_interface
|
// Load up the name of this network_interface
|
||||||
networkName := d.Get(prefix + ".network").(string)
|
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)
|
address := d.Get(prefix + ".address").(string)
|
||||||
var networkLink, subnetworkLink string
|
var networkLink, subnetworkLink string
|
||||||
|
|
||||||
if networkName != "" && subnetworkName != "" {
|
if networkName != "" {
|
||||||
return fmt.Errorf("Cannot specify both network and subnetwork values.")
|
|
||||||
} else if networkName != "" {
|
|
||||||
networkLink, err = getNetworkLink(d, config, prefix+".network")
|
networkLink, err = getNetworkLink(d, config, prefix+".network")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Error referencing network '%s': %s",
|
"Error referencing network '%s': %s",
|
||||||
networkName, err)
|
networkName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
region := getRegionFromZone(d.Get("zone").(string))
|
subnetworkLink, err = getSubnetworkLink(d, config, prefix+".subnetwork", prefix+".subnetwork_project", "zone")
|
||||||
if subnetworkProject == "" {
|
|
||||||
subnetworkProject = project
|
|
||||||
}
|
|
||||||
subnetwork, err := config.clientCompute.Subnetworks.Get(
|
|
||||||
subnetworkProject, region, subnetworkName).Do()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(
|
return err
|
||||||
"Error referencing subnetwork '%s' in region '%s': %s",
|
|
||||||
subnetworkName, region, err)
|
|
||||||
}
|
}
|
||||||
subnetworkLink = subnetwork.SelfLink
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the networkInterface
|
// Build the networkInterface
|
||||||
@ -961,9 +955,9 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
|
|||||||
networkInterfaces = append(networkInterfaces, map[string]interface{}{
|
networkInterfaces = append(networkInterfaces, map[string]interface{}{
|
||||||
"name": iface.Name,
|
"name": iface.Name,
|
||||||
"address": iface.NetworkIP,
|
"address": iface.NetworkIP,
|
||||||
"network": d.Get(fmt.Sprintf("network_interface.%d.network", i)),
|
"network": iface.Network,
|
||||||
"subnetwork": d.Get(fmt.Sprintf("network_interface.%d.subnetwork", i)),
|
"subnetwork": iface.Subnetwork,
|
||||||
"subnetwork_project": d.Get(fmt.Sprintf("network_interface.%d.subnetwork_project", i)),
|
"subnetwork_project": getProjectFromSubnetworkLink(iface.Subnetwork),
|
||||||
"access_config": accessConfigs,
|
"access_config": accessConfigs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1452,3 +1446,12 @@ func flattenScratchDisk(disk *compute.AttachedDisk) map[string]interface{} {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getProjectFromSubnetworkLink(subnetwork string) string {
|
||||||
|
r := regexp.MustCompile(SubnetworkLinkRegex)
|
||||||
|
if !r.MatchString(subnetwork) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.FindStringSubmatch(subnetwork)[1]
|
||||||
|
}
|
||||||
|
@ -1609,7 +1609,7 @@ resource "google_compute_instance" "foobar" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
network_interface {
|
network_interface {
|
||||||
subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.name}"
|
subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.self_link}"
|
||||||
access_config { }
|
access_config { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,14 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Copied from the official Google Cloud auto-generated client.
|
const (
|
||||||
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])?))"
|
// 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) {
|
func validateGCPName(v interface{}, k string) (ws []string, errors []error) {
|
||||||
re := `^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$`
|
re := `^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$`
|
||||||
|
@ -210,12 +210,14 @@ The `network_interface` block supports:
|
|||||||
* `network` - (Optional) The name or self_link of the network to attach this interface to.
|
* `network` - (Optional) The name or self_link of the network to attach this interface to.
|
||||||
Either `network` or `subnetwork` must be provided.
|
Either `network` or `subnetwork` must be provided.
|
||||||
|
|
||||||
* `subnetwork` - (Optional) The name of the subnetwork to attach this interface
|
* `subnetwork` - (Optional) The name or self_link of the subnetwork to attach this
|
||||||
to. The subnetwork must exist in the same region this instance will be
|
interface to. The subnetwork must exist in the same region this instance will be
|
||||||
created in. Either `network` or `subnetwork` must be provided.
|
created in. Either `network` or `subnetwork` must be provided.
|
||||||
|
|
||||||
* `subnetwork_project` - (Optional) The project in which the subnetwork belongs.
|
* `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
|
* `address` - (Optional) The private IP address to assign to the instance. If
|
||||||
empty, the address will be automatically assigned.
|
empty, the address will be automatically assigned.
|
||||||
|
Loading…
Reference in New Issue
Block a user