Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Google subnetworks #5130

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions builtin/providers/google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func Provider() terraform.ResourceProvider {
"google_compute_project_metadata": resourceComputeProjectMetadata(),
"google_compute_route": resourceComputeRoute(),
"google_compute_ssl_certificate": resourceComputeSslCertificate(),
"google_compute_subnetwork": resourceComputeSubnetwork(),
"google_compute_target_http_proxy": resourceComputeTargetHttpProxy(),
"google_compute_target_https_proxy": resourceComputeTargetHttpsProxy(),
"google_compute_target_pool": resourceComputeTargetPool(),
Expand Down Expand Up @@ -144,3 +145,13 @@ func validateCredentials(v interface{}, k string) (warnings []string, errors []e

return
}

// FIXME: not sure this is the best place for this
// Given a Google zone (e.g. us-central1-f) this func returns the Region, us-central1 in this example.
func getRegionFromZone(zone string) string {
if zone != "" && len(zone) > 2 {
region := zone[:len(zone)-2]
return region
}
return ""
}
8 changes: 8 additions & 0 deletions builtin/providers/google/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,11 @@ func testAccPreCheck(t *testing.T) {
t.Fatal("GOOGLE_REGION must be set to us-central1 for acceptance tests")
}
}

func TestProvider_getRegionFromZone(t *testing.T) {
expected := "us-central1"
actual := getRegionFromZone("us-central1-f")
if expected != actual {
t.Fatalf("Region (%s) did not match expected value: %s", actual, expected)
}
}
42 changes: 34 additions & 8 deletions builtin/providers/google/resource_compute_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,13 @@ func resourceComputeInstance() *schema.Resource {
Schema: map[string]*schema.Schema{
"network": &schema.Schema{
Type: schema.TypeString,
Required: true,
Optional: true,
ForceNew: true,
},

"subnetwork": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

Expand Down Expand Up @@ -445,17 +451,36 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
prefix := fmt.Sprintf("network_interface.%d", i)
// Load up the name of this network_interfac
networkName := d.Get(prefix + ".network").(string)
network, err := config.clientCompute.Networks.Get(
config.Project, networkName).Do()
if err != nil {
return fmt.Errorf(
"Error referencing network '%s': %s",
networkName, err)
subnetworkName := d.Get(prefix + ".subnetwork").(string)
var networkLink, subnetworkLink string

if networkName != "" && subnetworkName != "" {
return fmt.Errorf("Cannot specify both network and subnetwork values.")
} else if networkName != "" {
network, err := config.clientCompute.Networks.Get(
config.Project, networkName).Do()
if err != nil {
return fmt.Errorf(
"Error referencing network '%s': %s",
networkName, err)
}
networkLink = network.SelfLink
} else {
region := getRegionFromZone(d.Get("zone").(string))
subnetwork, err := config.clientCompute.Subnetworks.Get(
config.Project, region, subnetworkName).Do()
if err != nil {
return fmt.Errorf(
"Error referencing subnetwork '%s' in region '%s': %s",
subnetworkName, region, err)
}
subnetworkLink = subnetwork.SelfLink
}

// Build the networkInterface
var iface compute.NetworkInterface
iface.Network = network.SelfLink
iface.Network = networkLink
iface.Subnetwork = subnetworkLink

// Handle access_config structs
accessConfigsCount := d.Get(prefix + ".access_config.#").(int)
Expand Down Expand Up @@ -659,6 +684,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error
"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)),
"access_config": accessConfigs,
})
}
Expand Down
59 changes: 55 additions & 4 deletions builtin/providers/google/resource_compute_instance_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,13 @@ func resourceComputeInstanceTemplate() *schema.Resource {
Schema: map[string]*schema.Schema{
"network": &schema.Schema{
Type: schema.TypeString,
Required: true,
Optional: true,
ForceNew: true,
},

"subnetwork": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

Expand Down Expand Up @@ -173,6 +179,12 @@ func resourceComputeInstanceTemplate() *schema.Resource {
Deprecated: "Please use `scheduling.on_host_maintenance` instead",
},

"region": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"scheduling": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -327,19 +339,58 @@ func buildDisks(d *schema.ResourceData, meta interface{}) ([]*compute.AttachedDi

func buildNetworks(d *schema.ResourceData, meta interface{}) (error, []*compute.NetworkInterface) {
// Build up the list of networks
config := meta.(*Config)

networksCount := d.Get("network_interface.#").(int)
networkInterfaces := make([]*compute.NetworkInterface, 0, networksCount)
for i := 0; i < networksCount; i++ {
prefix := fmt.Sprintf("network_interface.%d", i)

source := "global/networks/"
var networkName, subnetworkName string
if v, ok := d.GetOk(prefix + ".network"); ok {
source += v.(string)
networkName = v.(string)
}
if v, ok := d.GetOk(prefix + ".subnetwork"); ok {
subnetworkName = v.(string)
}

if networkName == "" && subnetworkName == "" {
return fmt.Errorf("network or subnetwork must be provided"), nil
}
if networkName != "" && subnetworkName != "" {
return fmt.Errorf("network or subnetwork must not both be provided"), nil
}

var networkLink, subnetworkLink string
if networkName != "" {
network, err := config.clientCompute.Networks.Get(
config.Project, networkName).Do()
if err != nil {
return fmt.Errorf(
"Error referencing network '%s': %s",
networkName, err), nil
}
networkLink = network.SelfLink
} else {
// lookup subnetwork link using region and subnetwork name
region := d.Get("region").(string)
if region == "" {
region = config.Region
}
subnetwork, err := config.clientCompute.Subnetworks.Get(
config.Project, region, subnetworkName).Do()
if err != nil {
return fmt.Errorf(
"Error referencing subnetwork '%s' in region '%s': %s",
subnetworkName, region, err), nil
}
subnetworkLink = subnetwork.SelfLink
}

// Build the networkInterface
var iface compute.NetworkInterface
iface.Network = source
iface.Network = networkLink
iface.Subnetwork = subnetworkLink

accessConfigsCount := d.Get(prefix + ".access_config.#").(int)
iface.AccessConfigs = make([]*compute.AccessConfig, accessConfigsCount)
Expand Down
128 changes: 128 additions & 0 deletions builtin/providers/google/resource_compute_instance_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"google.golang.org/api/compute/v1"
"strings"
)

func TestAccComputeInstanceTemplate_basic(t *testing.T) {
Expand Down Expand Up @@ -73,6 +74,47 @@ func TestAccComputeInstanceTemplate_disks(t *testing.T) {
})
}

func TestAccComputeInstanceTemplate_subnet_auto(t *testing.T) {
var instanceTemplate compute.InstanceTemplate
network := "network-" + acctest.RandString(10)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeInstanceTemplateDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeInstanceTemplate_subnet_auto(network),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceTemplateExists(
"google_compute_instance_template.foobar", &instanceTemplate),
testAccCheckComputeInstanceTemplateNetworkName(&instanceTemplate, network),
),
},
},
})
}

func TestAccComputeInstanceTemplate_subnet_custom(t *testing.T) {
var instanceTemplate compute.InstanceTemplate

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeInstanceTemplateDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeInstanceTemplate_subnet_custom,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceTemplateExists(
"google_compute_instance_template.foobar", &instanceTemplate),
testAccCheckComputeInstanceTemplateSubnetwork(&instanceTemplate),
),
},
},
})
}

func testAccCheckComputeInstanceTemplateDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)

Expand Down Expand Up @@ -158,6 +200,18 @@ func testAccCheckComputeInstanceTemplateNetwork(instanceTemplate *compute.Instan
}
}

func testAccCheckComputeInstanceTemplateNetworkName(instanceTemplate *compute.InstanceTemplate, network string) resource.TestCheckFunc {
return func(s *terraform.State) error {
for _, i := range instanceTemplate.Properties.NetworkInterfaces {
if !strings.Contains(i.Network, network) {
return fmt.Errorf("Network doesn't match expected value, Expected: %s Actual: %s", network, i.Network[strings.LastIndex("/", i.Network)+1:])
}
}

return nil
}
}

func testAccCheckComputeInstanceTemplateDisk(instanceTemplate *compute.InstanceTemplate, source string, delete bool, boot bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
if instanceTemplate.Properties.Disks == nil {
Expand Down Expand Up @@ -186,6 +240,18 @@ func testAccCheckComputeInstanceTemplateDisk(instanceTemplate *compute.InstanceT
}
}

func testAccCheckComputeInstanceTemplateSubnetwork(instanceTemplate *compute.InstanceTemplate) resource.TestCheckFunc {
return func(s *terraform.State) error {
for _, i := range instanceTemplate.Properties.NetworkInterfaces {
if i.Subnetwork == "" {
return fmt.Errorf("no subnet")
}
}

return nil
}
}

func testAccCheckComputeInstanceTemplateTag(instanceTemplate *compute.InstanceTemplate, n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if instanceTemplate.Properties.Tags == nil {
Expand Down Expand Up @@ -293,3 +359,65 @@ resource "google_compute_instance_template" "foobar" {
foo = "bar"
}
}`, acctest.RandString(10), acctest.RandString(10))

func testAccComputeInstanceTemplate_subnet_auto(network string) string {
return fmt.Sprintf(`
resource "google_compute_network" "auto-network" {
name = "%s"
auto_create_subnetworks = true
}

resource "google_compute_instance_template" "foobar" {
name = "instance-tpl-%s"
machine_type = "n1-standard-1"

disk {
source_image = "debian-7-wheezy-v20160211"
auto_delete = true
disk_size_gb = 10
boot = true
}

network_interface {
network = "${google_compute_network.auto-network.name}"
}

metadata {
foo = "bar"
}
}`, network, acctest.RandString(10))
}

var testAccComputeInstanceTemplate_subnet_custom = fmt.Sprintf(`
resource "google_compute_network" "network" {
name = "network-%s"
auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "subnetwork" {
name = "subnetwork-%s"
ip_cidr_range = "10.0.0.0/24"
region = "us-central1"
network = "${google_compute_network.network.self_link}"
}

resource "google_compute_instance_template" "foobar" {
name = "instance-test-%s"
machine_type = "n1-standard-1"
region = "us-central1"

disk {
source_image = "debian-7-wheezy-v20160211"
auto_delete = true
disk_size_gb = 10
boot = true
}

network_interface {
subnetwork = "${google_compute_subnetwork.subnetwork.name}"
}

metadata {
foo = "bar"
}
}`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10))
Loading