diff --git a/provider/terraform/tests/data_source_compute_lb_ip_ranges_test.go b/provider/terraform/tests/data_source_compute_lb_ip_ranges_test.go new file mode 100644 index 000000000000..d70580bc9ba1 --- /dev/null +++ b/provider/terraform/tests/data_source_compute_lb_ip_ranges_test.go @@ -0,0 +1,34 @@ +package google + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataSourceComputeLbIpRanges_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeLbIpRangesConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr("data.google_compute_lb_ip_ranges.some", + "network.#", regexp.MustCompile("^[1-9]+[0-9]*$")), + resource.TestMatchResourceAttr("data.google_compute_lb_ip_ranges.some", + "network.0", regexp.MustCompile("^[0-9./]+$")), + resource.TestMatchResourceAttr("data.google_compute_lb_ip_ranges.some", + "http_ssl_tcp_internal.#", regexp.MustCompile("^[1-9]+[0-9]*$")), + resource.TestMatchResourceAttr("data.google_compute_lb_ip_ranges.some", + "http_ssl_tcp_internal.0", regexp.MustCompile("^[0-9./]+$")), + ), + }, + }, + }) +} + +const testAccComputeLbIpRangesConfig = ` +data "google_compute_lb_ip_ranges" "some" {} +` diff --git a/provider/terraform/tests/data_source_google_compute_address_test.go b/provider/terraform/tests/data_source_google_compute_address_test.go new file mode 100644 index 000000000000..9f1e97989471 --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_address_test.go @@ -0,0 +1,165 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestComputeAddressIdParsing(t *testing.T) { + cases := map[string]struct { + ImportId string + ExpectedError bool + ExpectedCanonicalId string + Config *Config + }{ + "id is a full self link": { + ImportId: "https://www.googleapis.com/compute/v1/projects/test-project/regions/us-central1/addresses/test-address", + ExpectedError: false, + ExpectedCanonicalId: "projects/test-project/regions/us-central1/addresses/test-address", + }, + "id is a partial self link": { + ImportId: "projects/test-project/regions/us-central1/addresses/test-address", + ExpectedError: false, + ExpectedCanonicalId: "projects/test-project/regions/us-central1/addresses/test-address", + }, + "id is project/region/address": { + ImportId: "test-project/us-central1/test-address", + ExpectedError: false, + ExpectedCanonicalId: "projects/test-project/regions/us-central1/addresses/test-address", + }, + "id is region/address": { + ImportId: "us-central1/test-address", + ExpectedError: false, + ExpectedCanonicalId: "projects/default-project/regions/us-central1/addresses/test-address", + Config: &Config{Project: "default-project"}, + }, + "id is address": { + ImportId: "test-address", + ExpectedError: false, + ExpectedCanonicalId: "projects/default-project/regions/us-east1/addresses/test-address", + Config: &Config{Project: "default-project", Region: "us-east1"}, + }, + "id has invalid format": { + ImportId: "i/n/v/a/l/i/d", + ExpectedError: true, + }, + } + + for tn, tc := range cases { + addressId, err := parseComputeAddressId(tc.ImportId, tc.Config) + + if tc.ExpectedError && err == nil { + t.Fatalf("bad: %s, expected an error", tn) + } + + if err != nil { + if tc.ExpectedError { + continue + } + t.Fatalf("bad: %s, err: %#v", tn, err) + } + + if addressId.canonicalId() != tc.ExpectedCanonicalId { + t.Fatalf("bad: %s, expected canonical id to be `%s` but is `%s`", tn, tc.ExpectedCanonicalId, addressId.canonicalId()) + } + } +} + +func TestAccDataSourceComputeAddress(t *testing.T) { + t.Parallel() + + rsName := "foobar" + rsFullName := fmt.Sprintf("google_compute_address.%s", rsName) + dsName := "my_address" + dsFullName := fmt.Sprintf("data.google_compute_address.%s", dsName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDataSourceComputeAddressDestroy(rsFullName), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceComputeAddressConfig(rsName, dsName), + Check: resource.ComposeTestCheckFunc( + testAccDataSourceComputeAddressCheck(dsFullName, rsFullName), + ), + }, + }, + }) +} + +func testAccDataSourceComputeAddressCheck(data_source_name string, resource_name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[data_source_name] + if !ok { + return fmt.Errorf("root module has no resource called %s", data_source_name) + } + + rs, ok := s.RootModule().Resources[resource_name] + if !ok { + return fmt.Errorf("can't find %s in state", resource_name) + } + + ds_attr := ds.Primary.Attributes + rs_attr := rs.Primary.Attributes + + address_attrs_to_test := []string{ + "self_link", + "name", + "address", + } + + for _, attr_to_check := range address_attrs_to_test { + if ds_attr[attr_to_check] != rs_attr[attr_to_check] { + return fmt.Errorf( + "%s is %s; want %s", + attr_to_check, + ds_attr[attr_to_check], + rs_attr[attr_to_check], + ) + } + } + + if ds_attr["status"] != "RESERVED" { + return fmt.Errorf("status is %s; want RESERVED", ds_attr["status"]) + } + + return nil + } +} + +func testAccCheckDataSourceComputeAddressDestroy(resource_name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + rs, ok := s.RootModule().Resources[resource_name] + if !ok { + return fmt.Errorf("can't find %s in state", resource_name) + } + + addressId, err := parseComputeAddressId(rs.Primary.ID, nil) + + _, err = config.clientCompute.Addresses.Get( + config.Project, addressId.Region, addressId.Name).Do() + if err == nil { + return fmt.Errorf("Address still exists") + } + + return nil + } +} + +func testAccDataSourceComputeAddressConfig(rsName, dsName string) string { + return fmt.Sprintf(` +resource "google_compute_address" "%s" { + name = "address-test" +} + +data "google_compute_address" "%s" { + name = "${google_compute_address.%s.name}" +} +`, rsName, dsName, rsName) +} diff --git a/provider/terraform/tests/data_source_google_compute_backend_service_test.go b/provider/terraform/tests/data_source_google_compute_backend_service_test.go new file mode 100644 index 000000000000..c6b4212ef6e6 --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_backend_service_test.go @@ -0,0 +1,85 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceComputeBackendService_basic(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceComputeBackendService_basic(serviceName, checkName), + Check: testAccDataSourceComputeBackendServiceCheck("data.google_compute_backend_service.baz", "google_compute_backend_service.foobar"), + }, + }, + }) +} + +func testAccDataSourceComputeBackendServiceCheck(dsName, rsName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[rsName] + if !ok { + return fmt.Errorf("can't find resource called %s in state", rsName) + } + + ds, ok := s.RootModule().Resources[dsName] + if !ok { + return fmt.Errorf("can't find data source called %s in state", dsName) + } + + dsAttr := ds.Primary.Attributes + rsAttr := rs.Primary.Attributes + + attrsToTest := []string{ + "id", + "name", + "description", + "self_link", + "fingerprint", + "port_name", + "protocol", + } + + for _, attrToTest := range attrsToTest { + if dsAttr[attrToTest] != rsAttr[attrToTest] { + return fmt.Errorf("%s is %s; want %s", attrToTest, dsAttr[attrToTest], rsAttr[attrToTest]) + } + } + + return nil + } +} + +func testAccDataSourceComputeBackendService_basic(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + description = "foobar backend service" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +data "google_compute_backend_service" "baz" { + name = "${google_compute_backend_service.foobar.name}" +} +`, serviceName, checkName) +} diff --git a/provider/terraform/tests/data_source_google_compute_default_service_account_test.go b/provider/terraform/tests/data_source_google_compute_default_service_account_test.go new file mode 100644 index 000000000000..83e8929cb08f --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_default_service_account_test.go @@ -0,0 +1,31 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataSourceGoogleComputeDefaultServiceAccount_basic(t *testing.T) { + t.Parallel() + + resourceName := "data.google_compute_default_service_account.default" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleComputeDefaultServiceAccount_basic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "email"), + ), + }, + }, + }) +} + +const testAccCheckGoogleComputeDefaultServiceAccount_basic = ` +data "google_compute_default_service_account" "default" { } +` diff --git a/provider/terraform/tests/data_source_google_compute_forwarding_rule_test.go b/provider/terraform/tests/data_source_google_compute_forwarding_rule_test.go new file mode 100644 index 000000000000..abb321a68c7d --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_forwarding_rule_test.go @@ -0,0 +1,95 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceGoogleForwardingRule(t *testing.T) { + t.Parallel() + + poolName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + ruleName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceGoogleForwardingRuleConfig(poolName, ruleName), + Check: resource.ComposeTestCheckFunc( + testAccDataSourceGoogleForwardingRuleCheck("data.google_compute_forwarding_rule.my_forwarding_rule", "google_compute_forwarding_rule.foobar-fr"), + ), + }, + }, + }) +} + +func testAccDataSourceGoogleForwardingRuleCheck(data_source_name string, resource_name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[data_source_name] + if !ok { + return fmt.Errorf("root module has no resource called %s", data_source_name) + } + + rs, ok := s.RootModule().Resources[resource_name] + if !ok { + return fmt.Errorf("can't find %s in state", resource_name) + } + + ds_attr := ds.Primary.Attributes + rs_attr := rs.Primary.Attributes + forwarding_rule_attrs_to_test := []string{ + "id", + "self_link", + "name", + "description", + "region", + "port_range", + "ports", + "target", + "ip_address", + "ip_protocol", + "load_balancing_scheme", + "backend_service", + "network", + "subnetwork", + } + + for _, attr_to_check := range forwarding_rule_attrs_to_test { + if ds_attr[attr_to_check] != rs_attr[attr_to_check] { + return fmt.Errorf( + "%s is %s; want %s", + attr_to_check, + ds_attr[attr_to_check], + rs_attr[attr_to_check], + ) + } + } + return nil + } +} + +func testAccDataSourceGoogleForwardingRuleConfig(poolName, ruleName string) string { + return fmt.Sprintf(` + resource "google_compute_target_pool" "foobar-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "%s" + } + resource "google_compute_forwarding_rule" "foobar-fr" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "UDP" + name = "%s" + port_range = "80-81" + target = "${google_compute_target_pool.foobar-tp.self_link}" + } + data "google_compute_forwarding_rule" "my_forwarding_rule" { + name = "${google_compute_forwarding_rule.foobar-fr.name}" + } +`, poolName, ruleName) +} diff --git a/provider/terraform/tests/data_source_google_compute_global_address_test.go b/provider/terraform/tests/data_source_google_compute_global_address_test.go new file mode 100644 index 000000000000..9cf0ef5233a2 --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_global_address_test.go @@ -0,0 +1,84 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceComputeGlobalAddress(t *testing.T) { + t.Parallel() + + rsName := "foobar" + rsFullName := fmt.Sprintf("google_compute_global_address.%s", rsName) + dsName := "my_address" + dsFullName := fmt.Sprintf("data.google_compute_global_address.%s", dsName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeGlobalAddressDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceComputeGlobalAddressConfig(rsName, dsName), + Check: resource.ComposeTestCheckFunc( + testAccDataSourceComputeGlobalAddressCheck(dsFullName, rsFullName), + ), + }, + }, + }) +} + +func testAccDataSourceComputeGlobalAddressCheck(data_source_name string, resource_name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[data_source_name] + if !ok { + return fmt.Errorf("root module has no resource called %s", data_source_name) + } + + rs, ok := s.RootModule().Resources[resource_name] + if !ok { + return fmt.Errorf("can't find %s in state", resource_name) + } + + ds_attr := ds.Primary.Attributes + rs_attr := rs.Primary.Attributes + + address_attrs_to_test := []string{ + "self_link", + "name", + "address", + } + + for _, attr_to_check := range address_attrs_to_test { + if ds_attr[attr_to_check] != rs_attr[attr_to_check] { + return fmt.Errorf( + "%s is %s; want %s", + attr_to_check, + ds_attr[attr_to_check], + rs_attr[attr_to_check], + ) + } + } + + if ds_attr["status"] != "RESERVED" { + return fmt.Errorf("status is %s; want RESERVED", ds_attr["status"]) + } + + return nil + } +} + +func testAccDataSourceComputeGlobalAddressConfig(rsName, dsName string) string { + return fmt.Sprintf(` +resource "google_compute_global_address" "%s" { + name = "address-test" +} + +data "google_compute_global_address" "%s" { + name = "${google_compute_global_address.%s.name}" +} +`, rsName, dsName, rsName) +} diff --git a/provider/terraform/tests/data_source_google_compute_image_test.go b/provider/terraform/tests/data_source_google_compute_image_test.go new file mode 100644 index 000000000000..83a866fce655 --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_image_test.go @@ -0,0 +1,100 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceComputeImage(t *testing.T) { + t.Parallel() + + family := acctest.RandomWithPrefix("tf-test") + name := acctest.RandomWithPrefix("tf-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeImageDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDataSourcePublicImageConfig, + Check: resource.ComposeTestCheckFunc( + testAccDataSourceCheckPublicImage(), + ), + }, + { + Config: testAccDataSourceCustomImageConfig(family, name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_compute_image.from_name", + "name", name), + resource.TestCheckResourceAttr("data.google_compute_image.from_name", + "family", family), + resource.TestCheckResourceAttrSet("data.google_compute_image.from_name", + "self_link"), + ), + }, + }, + }) +} + +func testAccDataSourceCheckPublicImage() resource.TestCheckFunc { + return func(s *terraform.State) error { + data_source_name := "data.google_compute_image.debian" + ds, ok := s.RootModule().Resources[data_source_name] + if !ok { + return fmt.Errorf("root module has no resource called %s", data_source_name) + } + + ds_attr := ds.Primary.Attributes + attrs_to_test := map[string]string{ + "self_link": "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-9-stretch-v20171129", + "family": "debian-9", + } + + for attr, expect_value := range attrs_to_test { + if ds_attr[attr] != expect_value { + return fmt.Errorf( + "%s is %s; want %s", + attr, + ds_attr[attr], + expect_value, + ) + } + } + + return nil + } +} + +var testAccDataSourcePublicImageConfig = ` +data "google_compute_image" "debian" { + project = "debian-cloud" + name = "debian-9-stretch-v20171129" +} +` + +func testAccDataSourceCustomImageConfig(family, name string) string { + return fmt.Sprintf(` +resource "google_compute_image" "image" { + family = "%s" + name = "%s" + source_disk = "${google_compute_disk.disk.self_link}" +} +resource "google_compute_disk" "disk" { + name = "%s-disk" + zone = "us-central1-b" +} +data "google_compute_image" "from_name" { + project = "${google_compute_image.image.project}" + name = "${google_compute_image.image.name}" +} +data "google_compute_image" "from_family" { + project = "${google_compute_image.image.project}" + family = "${google_compute_image.image.family}" +} +`, family, name, name) +} diff --git a/provider/terraform/tests/data_source_google_compute_instance_group_test.go b/provider/terraform/tests/data_source_google_compute_instance_group_test.go new file mode 100644 index 000000000000..bc42d7805093 --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_instance_group_test.go @@ -0,0 +1,326 @@ +package google + +import ( + "errors" + "fmt" + "reflect" + "sort" + "strconv" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceGoogleComputeInstanceGroup_basic(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckDataSourceGoogleComputeInstanceGroupConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataSourceGoogleComputeInstanceGroup("data.google_compute_instance_group.test"), + ), + }, + }, + }) +} + +func TestAccDataSourceGoogleComputeInstanceGroup_withNamedPort(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckDataSourceGoogleComputeInstanceGroupConfigWithNamedPort(), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataSourceGoogleComputeInstanceGroup("data.google_compute_instance_group.test"), + ), + }, + }, + }) +} + +func TestAccDataSourceGoogleComputeInstanceGroup_fromIGM(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckDataSourceGoogleComputeInstanceGroup_fromIGM(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_compute_instance_group.test", "instances.#", "10"), + ), + }, + }, + }) +} + +func testAccCheckDataSourceGoogleComputeInstanceGroup(dataSourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + dsFullName := "data.google_compute_instance_group.test" + rsFullName := "google_compute_instance_group.test" + ds, ok := s.RootModule().Resources[dsFullName] + if !ok { + return fmt.Errorf("cant' find data source called %s in state", dsFullName) + } + + rs, ok := s.RootModule().Resources[rsFullName] + if !ok { + return fmt.Errorf("can't find resource called %s in state", rsFullName) + } + + dsAttrs := ds.Primary.Attributes + rsAttrs := rs.Primary.Attributes + + attrsToTest := []string{ + "id", + "name", + "zone", + "project", + "description", + "network", + "self_link", + "size", + } + + for _, attrToTest := range attrsToTest { + if dsAttrs[attrToTest] != rsAttrs[attrToTest] { + return fmt.Errorf("%s is %s; want %s", attrToTest, dsAttrs[attrToTest], rsAttrs[attrToTest]) + } + } + + dsNamedPortsCount, ok := dsAttrs["named_port.#"] + if !ok { + return errors.New("can't find 'named_port' attribute in data source") + } + + dsNoOfNamedPorts, err := strconv.Atoi(dsNamedPortsCount) + if err != nil { + return errors.New("failed to read number of named ports in data source") + } + + rsNamedPortsCount, ok := rsAttrs["named_port.#"] + if !ok { + return errors.New("can't find 'named_port' attribute in resource") + } + + rsNoOfNamedPorts, err := strconv.Atoi(rsNamedPortsCount) + if err != nil { + return errors.New("failed to read number of named ports in resource") + } + + if dsNoOfNamedPorts != rsNoOfNamedPorts { + return fmt.Errorf( + "expected %d number of named port, received %d, this is most likely a bug", + rsNoOfNamedPorts, + dsNoOfNamedPorts, + ) + } + + namedPortItemKeys := []string{"name", "value"} + for i := 0; i < dsNoOfNamedPorts; i++ { + for _, key := range namedPortItemKeys { + idx := fmt.Sprintf("named_port.%d.%s", i, key) + if dsAttrs[idx] != rsAttrs[idx] { + return fmt.Errorf("%s is %s; want %s", idx, dsAttrs[idx], rsAttrs[idx]) + } + } + } + + dsInstancesCount, ok := dsAttrs["instances.#"] + if !ok { + return errors.New("can't find 'instances' attribute in data source") + } + + dsNoOfInstances, err := strconv.Atoi(dsInstancesCount) + if err != nil { + return errors.New("failed to read number of named ports in data source") + } + + rsInstancesCount, ok := rsAttrs["instances.#"] + if !ok { + return errors.New("can't find 'instances' attribute in resource") + } + + rsNoOfInstances, err := strconv.Atoi(rsInstancesCount) + if err != nil { + return errors.New("failed to read number of instances in resource") + } + + if dsNoOfInstances != rsNoOfInstances { + return fmt.Errorf( + "expected %d number of instances, received %d, this is most likely a bug", + rsNoOfInstances, + dsNoOfInstances, + ) + } + + // We don't know the exact keys of the elements, so go through the whole list looking for matching ones + dsInstancesValues := []string{} + for k, v := range dsAttrs { + if strings.HasPrefix(k, "instances") && !strings.HasSuffix(k, "#") { + dsInstancesValues = append(dsInstancesValues, v) + } + } + + rsInstancesValues := []string{} + for k, v := range rsAttrs { + if strings.HasPrefix(k, "instances") && !strings.HasSuffix(k, "#") { + rsInstancesValues = append(rsInstancesValues, v) + } + } + + sort.Strings(dsInstancesValues) + sort.Strings(rsInstancesValues) + + if !reflect.DeepEqual(dsInstancesValues, rsInstancesValues) { + return fmt.Errorf("expected %v list of instances, received %v", rsInstancesValues, dsInstancesValues) + } + + return nil + } +} + +func testAccCheckDataSourceGoogleComputeInstanceGroupConfig() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "test" { + name = "tf-test-%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + + access_config { + // Ephemeral IP + } + } +} + +resource "google_compute_instance_group" "test" { + name = "tf-test-%s" + zone = "${google_compute_instance.test.zone}" + + instances = [ + "${google_compute_instance.test.self_link}", + ] +} + +data "google_compute_instance_group" "test" { + name = "${google_compute_instance_group.test.name}" + zone = "${google_compute_instance_group.test.zone}" +} +`, acctest.RandString(10), acctest.RandString(10)) +} + +func testAccCheckDataSourceGoogleComputeInstanceGroupConfigWithNamedPort() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "test" { + name = "tf-test-%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + + access_config { + // Ephemeral IP + } + } +} + +resource "google_compute_instance_group" "test" { + name = "tf-test-%s" + zone = "${google_compute_instance.test.zone}" + + named_port { + name = "http" + port = "8080" + } + + named_port { + name = "https" + port = "8443" + } + + instances = [ + "${google_compute_instance.test.self_link}", + ] +} + +data "google_compute_instance_group" "test" { + name = "${google_compute_instance_group.test.name}" + zone = "${google_compute_instance_group.test.zone}" +} +`, acctest.RandString(10), acctest.RandString(10)) +} + +func testAccCheckDataSourceGoogleComputeInstanceGroup_fromIGM() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } +} + +resource "google_compute_instance_group_manager" "igm" { + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm" + zone = "us-central1-a" + target_size = 10 + + wait_for_instances = true +} + +data "google_compute_instance_group" "test" { + self_link = "${google_compute_instance_group_manager.igm.instance_group}" +} +`, acctest.RandomWithPrefix("test-igm"), acctest.RandomWithPrefix("test-igm")) +} diff --git a/provider/terraform/tests/data_source_google_compute_instance_test.go b/provider/terraform/tests/data_source_google_compute_instance_test.go new file mode 100644 index 000000000000..af8fb2eebfdd --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_instance_test.go @@ -0,0 +1,133 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceComputeInstance_basic(t *testing.T) { + t.Parallel() + + instanceName := fmt.Sprintf("data-instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceComputeInstanceConfig(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccDataSourceComputeInstanceCheck("data.google_compute_instance.bar", "google_compute_instance.foo"), + resource.TestCheckResourceAttr("data.google_compute_instance.bar", "network_interface.#", "1"), + resource.TestCheckResourceAttr("data.google_compute_instance.bar", "boot_disk.0.initialize_params.0.size", "10"), + resource.TestCheckResourceAttr("data.google_compute_instance.bar", "boot_disk.0.initialize_params.0.type", "pd-standard"), + resource.TestCheckResourceAttr("data.google_compute_instance.bar", "scratch_disk.0.interface", "SCSI"), + resource.TestCheckResourceAttr("data.google_compute_instance.bar", "network_interface.0.access_config.0.network_tier", "PREMIUM"), + ), + }, + }, + }) +} + +func testAccDataSourceComputeInstanceCheck(datasourceName string, resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[datasourceName] + if !ok { + return fmt.Errorf("root module has no resource called %s", datasourceName) + } + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("can't find %s in state", resourceName) + } + + datasourceAttributes := ds.Primary.Attributes + resourceAttributes := rs.Primary.Attributes + + instanceAttrsToTest := []string{ + "name", + "machine_type", + "can_ip_forward", + "description", + "deletion_protection", + "labels", + "metadata", + "min_cpu_platform", + "project", + "tags", + "zone", + "cpu_platform", + "instance_id", + "label_fingerprint", + "metadata_fingerprint", + "self_link", + "tags_fingerprint", + } + + for _, attrToCheck := range instanceAttrsToTest { + if datasourceAttributes[attrToCheck] != resourceAttributes[attrToCheck] { + return fmt.Errorf( + "%s is %s; want %s", + attrToCheck, + datasourceAttributes[attrToCheck], + resourceAttributes[attrToCheck], + ) + } + } + + return nil + } +} + +func testAccDataSourceComputeInstanceConfig(instanceName string) string { + return fmt.Sprintf(` +resource "google_compute_instance" "foo" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "debian-8-jessie-v20160803" + } + } + + scratch_disk { + } + + network_interface { + network = "default" + + access_config { + // Ephemeral IP + } + } + + metadata { + foo = "bar" + baz = "qux" + } + + metadata { + startup-script = "echo Hello" + } + + labels { + my_key = "my_value" + my_other_key = "my_other_value" + } +} + +data "google_compute_instance" "bar" { + name = "${google_compute_instance.foo.name}" + zone = "us-central1-a" +} +`, instanceName) +} diff --git a/provider/terraform/tests/data_source_google_compute_network_test.go b/provider/terraform/tests/data_source_google_compute_network_test.go new file mode 100644 index 000000000000..cd80228fc369 --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_network_test.go @@ -0,0 +1,75 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceGoogleNetwork(t *testing.T) { + t.Parallel() + + networkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceGoogleNetworkConfig(networkName), + Check: resource.ComposeTestCheckFunc( + testAccDataSourceGoogleNetworkCheck("data.google_compute_network.my_network", "google_compute_network.foobar"), + ), + }, + }, + }) +} + +func testAccDataSourceGoogleNetworkCheck(data_source_name string, resource_name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[data_source_name] + if !ok { + return fmt.Errorf("root module has no resource called %s", data_source_name) + } + + rs, ok := s.RootModule().Resources[resource_name] + if !ok { + return fmt.Errorf("can't find %s in state", resource_name) + } + + ds_attr := ds.Primary.Attributes + rs_attr := rs.Primary.Attributes + network_attrs_to_test := []string{ + "id", + "self_link", + "name", + "description", + } + + for _, attr_to_check := range network_attrs_to_test { + if ds_attr[attr_to_check] != rs_attr[attr_to_check] { + return fmt.Errorf( + "%s is %s; want %s", + attr_to_check, + ds_attr[attr_to_check], + rs_attr[attr_to_check], + ) + } + } + return nil + } +} + +func testAccDataSourceGoogleNetworkConfig(name string) string { + return fmt.Sprintf(` +resource "google_compute_network" "foobar" { + name = "%s" + description = "my-description" +} + +data "google_compute_network" "my_network" { + name = "${google_compute_network.foobar.name}" +}`, name) +} diff --git a/provider/terraform/tests/data_source_google_compute_region_instance_group_test.go b/provider/terraform/tests/data_source_google_compute_region_instance_group_test.go new file mode 100644 index 000000000000..e72824673347 --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_region_instance_group_test.go @@ -0,0 +1,89 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataSourceRegionInstanceGroup(t *testing.T) { + t.Parallel() + name := "acctest-" + acctest.RandString(6) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceRegionInstanceGroup_basic(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_compute_region_instance_group.data_source", "name", name), + resource.TestCheckResourceAttr("data.google_compute_region_instance_group.data_source", "project", getTestProjectFromEnv()), + resource.TestCheckResourceAttr("data.google_compute_region_instance_group.data_source", "instances.#", "10")), + }, + }, + }) +} + +func testAccDataSourceRegionInstanceGroup_basic(instanceManagerName string) string { + return fmt.Sprintf(` +resource "google_compute_health_check" "autohealing" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + healthy_threshold = 2 + unhealthy_threshold = 10 + + http_health_check { + request_path = "/" + port = "80" + } +} + +resource "google_compute_target_pool" "foo" { + name = "%s" +} + +data "google_compute_image" "debian" { + project = "debian-cloud" + name = "debian-9-stretch-v20171129" +} + +resource "google_compute_instance_template" "foo" { + machine_type = "n1-standard-1" + disk { + source_image = "${data.google_compute_image.debian.self_link}" + } + network_interface { + access_config { + } + network = "default" + } +} + +resource "google_compute_region_instance_group_manager" "foo" { + name = "%s" + base_instance_name = "foo" + instance_template = "${google_compute_instance_template.foo.self_link}" + region = "us-central1" + target_pools = ["${google_compute_target_pool.foo.self_link}"] + target_size = 1 + + named_port { + name = "web" + port = 80 + } + wait_for_instances = true + + auto_healing_policies { + health_check = "${google_compute_health_check.autohealing.self_link}" + initial_delay_sec = 10 + } +} + +data "google_compute_region_instance_group" "data_source" { + self_link = "${google_compute_region_instance_group_manager.foo.instance_group}" +} +`, acctest.RandomWithPrefix("test-rigm-"), acctest.RandomWithPrefix("test-rigm-"), instanceManagerName) +} diff --git a/provider/terraform/tests/data_source_google_compute_regions_test.go b/provider/terraform/tests/data_source_google_compute_regions_test.go new file mode 100644 index 000000000000..99a2c923f28f --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_regions_test.go @@ -0,0 +1,72 @@ +package google + +import ( + "errors" + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeRegions_basic(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleComputeRegionsConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleComputeRegionsMeta("data.google_compute_regions.available"), + ), + }, + }, + }) +} + +func testAccCheckGoogleComputeRegionsMeta(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Can't find regions data source: %s", n) + } + + if rs.Primary.ID == "" { + return errors.New("regions data source ID not set.") + } + + count, ok := rs.Primary.Attributes["names.#"] + if !ok { + return errors.New("can't find 'names' attribute") + } + + noOfNames, err := strconv.Atoi(count) + if err != nil { + return errors.New("failed to read number of regions") + } + if noOfNames < 2 { + return fmt.Errorf("expected at least 2 regions, received %d, this is most likely a bug", + noOfNames) + } + + for i := 0; i < noOfNames; i++ { + idx := "names." + strconv.Itoa(i) + v, ok := rs.Primary.Attributes[idx] + if !ok { + return fmt.Errorf("region list is corrupt (%q not found), this is definitely a bug", idx) + } + if len(v) < 1 { + return fmt.Errorf("Empty region name (%q), this is definitely a bug", idx) + } + } + + return nil + } +} + +var testAccCheckGoogleComputeRegionsConfig = ` +data "google_compute_regions" "available" {} +` diff --git a/provider/terraform/tests/data_source_google_compute_ssl_policy_test.go b/provider/terraform/tests/data_source_google_compute_ssl_policy_test.go new file mode 100644 index 000000000000..8b7462dba3ed --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_ssl_policy_test.go @@ -0,0 +1,83 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceGoogleSslPolicy(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceGoogleSslPolicy(), + Check: resource.ComposeTestCheckFunc( + testAccDataSourceGoogleSslPolicyCheck("data.google_compute_ssl_policy.ssl_policy", "google_compute_ssl_policy.foobar"), + ), + }, + }, + }) +} + +func testAccDataSourceGoogleSslPolicyCheck(data_source_name string, resource_name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[data_source_name] + if !ok { + return fmt.Errorf("root module has no resource called %s", data_source_name) + } + + rs, ok := s.RootModule().Resources[resource_name] + if !ok { + return fmt.Errorf("can't find %s in state", resource_name) + } + + ds_attr := ds.Primary.Attributes + rs_attr := rs.Primary.Attributes + + ssl_policy_attrs_to_test := []string{ + "id", + "self_link", + "name", + "description", + "min_tls_version", + "profile", + "custom_features", + } + + for _, attr_to_check := range ssl_policy_attrs_to_test { + if ds_attr[attr_to_check] != rs_attr[attr_to_check] { + return fmt.Errorf( + "%s is %s; want %s", + attr_to_check, + ds_attr[attr_to_check], + rs_attr[attr_to_check], + ) + } + } + + return nil + } +} + +func testAccDataSourceGoogleSslPolicy() string { + return fmt.Sprintf(` + +resource "google_compute_ssl_policy" "foobar" { + name = "%s" + description = "my-description" + min_tls_version = "TLS_1_2" + profile = "MODERN" +} + +data "google_compute_ssl_policy" "ssl_policy" { + name = "${google_compute_ssl_policy.foobar.name}" +} +`, acctest.RandomWithPrefix("test-ssl-policy")) +} diff --git a/provider/terraform/tests/data_source_google_compute_subnetwork_test.go b/provider/terraform/tests/data_source_google_compute_subnetwork_test.go new file mode 100644 index 000000000000..7c44f89c4585 --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_subnetwork_test.go @@ -0,0 +1,96 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceGoogleSubnetwork(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceGoogleSubnetwork(), + Check: resource.ComposeTestCheckFunc( + testAccDataSourceGoogleSubnetworkCheck("data.google_compute_subnetwork.my_subnetwork", "google_compute_subnetwork.foobar"), + ), + }, + }, + }) +} + +func testAccDataSourceGoogleSubnetworkCheck(data_source_name string, resource_name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[data_source_name] + if !ok { + return fmt.Errorf("root module has no resource called %s", data_source_name) + } + + rs, ok := s.RootModule().Resources[resource_name] + if !ok { + return fmt.Errorf("can't find %s in state", resource_name) + } + + ds_attr := ds.Primary.Attributes + rs_attr := rs.Primary.Attributes + + subnetwork_attrs_to_test := []string{ + "id", + "self_link", + "name", + "description", + "ip_cidr_range", + "private_ip_google_access", + "secondary_ip_range", + } + + for _, attr_to_check := range subnetwork_attrs_to_test { + if ds_attr[attr_to_check] != rs_attr[attr_to_check] { + return fmt.Errorf( + "%s is %s; want %s", + attr_to_check, + ds_attr[attr_to_check], + rs_attr[attr_to_check], + ) + } + } + + if v1RsNetwork := ConvertSelfLinkToV1(rs_attr["network"]); ds_attr["network"] != v1RsNetwork { + return fmt.Errorf("network is %s; want %s", ds_attr["network"], v1RsNetwork) + } + + return nil + } +} + +func testAccDataSourceGoogleSubnetwork() string { + return fmt.Sprintf(` + +resource "google_compute_network" "foobar" { + name = "%s" + description = "my-description" +} +resource "google_compute_subnetwork" "foobar" { + name = "subnetwork-test" + description = "my-description" + ip_cidr_range = "10.0.0.0/24" + network = "${google_compute_network.foobar.self_link}" + private_ip_google_access = true + secondary_ip_range { + range_name = "tf-test-secondary-range" + ip_cidr_range = "192.168.1.0/24" + } +} + +data "google_compute_subnetwork" "my_subnetwork" { + name = "${google_compute_subnetwork.foobar.name}" +} +`, acctest.RandomWithPrefix("network-test")) +} diff --git a/provider/terraform/tests/data_source_google_compute_vpn_gateway_test.go b/provider/terraform/tests/data_source_google_compute_vpn_gateway_test.go new file mode 100644 index 000000000000..cec78918e0a4 --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_vpn_gateway_test.go @@ -0,0 +1,77 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceGoogleVpnGateway(t *testing.T) { + t.Parallel() + + vpnGatewayName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDataSourceGoogleVpnGatewayConfig(vpnGatewayName), + Check: resource.ComposeTestCheckFunc( + testAccDataSourceGoogleVpnGatewayCheck("data.google_compute_vpn_gateway.my_vpn_gateway", "google_compute_vpn_gateway.foobar"), + ), + }, + }, + }) +} + +func testAccDataSourceGoogleVpnGatewayCheck(data_source_name string, resource_name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[data_source_name] + if !ok { + return fmt.Errorf("root module has no resource called %s", data_source_name) + } + + rs, ok := s.RootModule().Resources[resource_name] + if !ok { + return fmt.Errorf("can't find %s in state", resource_name) + } + + ds_attr := ds.Primary.Attributes + rs_attr := rs.Primary.Attributes + vpn_gateway_attrs_to_test := []string{ + "id", + "self_link", + "name", + "description", + "network", + } + + for _, attr_to_check := range vpn_gateway_attrs_to_test { + if ds_attr[attr_to_check] != rs_attr[attr_to_check] { + return fmt.Errorf( + "%s is %s; want %s", + attr_to_check, + ds_attr[attr_to_check], + rs_attr[attr_to_check], + ) + } + } + return nil + } +} + +func testAccDataSourceGoogleVpnGatewayConfig(name string) string { + return fmt.Sprintf(` +resource "google_compute_vpn_gateway" "foobar" { + name = "%s" + description = "my-description" + network = "default" +} + +data "google_compute_vpn_gateway" "my_vpn_gateway" { + name = "${google_compute_vpn_gateway.foobar.name}" +}`, name) +} diff --git a/provider/terraform/tests/data_source_google_compute_zones_test.go b/provider/terraform/tests/data_source_google_compute_zones_test.go new file mode 100644 index 000000000000..9e47bdb353d0 --- /dev/null +++ b/provider/terraform/tests/data_source_google_compute_zones_test.go @@ -0,0 +1,72 @@ +package google + +import ( + "errors" + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeZones_basic(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleComputeZonesConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleComputeZonesMeta("data.google_compute_zones.available"), + ), + }, + }, + }) +} + +func testAccCheckGoogleComputeZonesMeta(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Can't find zones data source: %s", n) + } + + if rs.Primary.ID == "" { + return errors.New("zones data source ID not set.") + } + + count, ok := rs.Primary.Attributes["names.#"] + if !ok { + return errors.New("can't find 'names' attribute") + } + + noOfNames, err := strconv.Atoi(count) + if err != nil { + return errors.New("failed to read number of zones") + } + if noOfNames < 2 { + return fmt.Errorf("expected at least 2 zones, received %d, this is most likely a bug", + noOfNames) + } + + for i := 0; i < noOfNames; i++ { + idx := "names." + strconv.Itoa(i) + v, ok := rs.Primary.Attributes[idx] + if !ok { + return fmt.Errorf("zone list is corrupt (%q not found), this is definitely a bug", idx) + } + if len(v) < 1 { + return fmt.Errorf("Empty zone name (%q), this is definitely a bug", idx) + } + } + + return nil + } +} + +var testAccCheckGoogleComputeZonesConfig = ` +data "google_compute_zones" "available" {} +` diff --git a/provider/terraform/tests/resource_compute_attached_disk_test.go b/provider/terraform/tests/resource_compute_attached_disk_test.go new file mode 100644 index 000000000000..9d9cef7087f9 --- /dev/null +++ b/provider/terraform/tests/resource_compute_attached_disk_test.go @@ -0,0 +1,222 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeAttachedDisk_basic(t *testing.T) { + t.Parallel() + + diskName := acctest.RandomWithPrefix("tf-test-disk") + instanceName := acctest.RandomWithPrefix("tf-test-inst") + importID := fmt.Sprintf("%s/us-central1-a/%s:%s", getTestProjectFromEnv(), instanceName, diskName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + // Check destroy isn't a good test here, see comment on testCheckAttachedDiskIsNowDetached + CheckDestroy: nil, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAttachedDiskResource(diskName, instanceName) + testAttachedDiskResourceAttachment(), + }, + resource.TestStep{ + ResourceName: "google_compute_attached_disk.test", + ImportStateId: importID, + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAttachedDiskResource(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testCheckAttachedDiskIsNowDetached(instanceName, diskName), + ), + }, + }, + }) +} + +func TestAccComputeAttachedDisk_full(t *testing.T) { + t.Parallel() + + diskName := acctest.RandomWithPrefix("tf-test") + instanceName := acctest.RandomWithPrefix("tf-test") + importID := fmt.Sprintf("%s/us-central1-a/%s:%s", getTestProjectFromEnv(), instanceName, diskName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + // Check destroy isn't a good test here, see comment on testCheckAttachedDiskIsNowDetached + CheckDestroy: nil, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAttachedDiskResource(diskName, instanceName) + testAttachedDiskResourceAttachmentFull(), + }, + resource.TestStep{ + ResourceName: "google_compute_attached_disk.test", + ImportStateId: importID, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + +} + +func TestAccComputeAttachedDisk_count(t *testing.T) { + t.Parallel() + + diskPrefix := acctest.RandomWithPrefix("tf-test") + instanceName := acctest.RandomWithPrefix("tf-test") + count := 2 + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAttachedDiskResourceCount(diskPrefix, instanceName, count), + Check: resource.ComposeTestCheckFunc( + testCheckAttachedDiskContainsManyDisks(instanceName, count), + ), + }, + }, + }) + +} + +// testCheckAttachedDiskIsNowDetached queries a compute instance and iterates through the attached +// disks to confirm that a specific disk is no longer attached to the instance +// +// This is being used instead of a CheckDestory method because destory will delete both the compute +// instance and the disk, whereas destroying just the attached disk should only detach the disk but +// leave the instance and disk around. So just using a normal check destroy could end up with a +// situation where the detach fails but since the instance/disk get destroyed we wouldn't notice. +func testCheckAttachedDiskIsNowDetached(instanceName, diskName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + instance, err := config.clientCompute.Instances.Get(getTestProjectFromEnv(), "us-central1-a", instanceName).Do() + if err != nil { + return err + } + + ad := findDiskByName(instance.Disks, diskName) + if ad != nil { + return fmt.Errorf("compute disk is still attached to compute instance") + } + + return nil + } +} + +func testCheckAttachedDiskContainsManyDisks(instanceName string, count int) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + instance, err := config.clientCompute.Instances.Get(getTestProjectFromEnv(), "us-central1-a", instanceName).Do() + if err != nil { + return err + } + + // There will be 1 extra disk because of the compute instance's boot disk + if (count + 1) != len(instance.Disks) { + return fmt.Errorf("expected %d disks to be attached, found %d", count+1, len(instance.Disks)) + } + + return nil + } +} + +func testAttachedDiskResourceAttachment() string { + return fmt.Sprintf(` +resource "google_compute_attached_disk" "test" { + disk = "${google_compute_disk.test1.self_link}" + instance = "${google_compute_instance.test.self_link}" +}`) +} + +func testAttachedDiskResourceAttachmentFull() string { + return fmt.Sprintf(` +resource "google_compute_attached_disk" "test" { + disk = "${google_compute_disk.test1.self_link}" + instance = "${google_compute_instance.test.self_link}" + mode = "READ_ONLY" + device_name = "test-device-name" +}`) +} + +func testAttachedDiskResource(diskName, instanceName string) string { + return fmt.Sprintf(` +resource "google_compute_disk" "test1" { + name = "%s" + zone = "us-central1-a" + size = 10 +} + +resource "google_compute_instance" "test" { + name = "%s" + machine_type = "f1-micro" + zone = "us-central1-a" + + lifecycle { + ignore_changes = [ + "attached_disk", + ] + } + + boot_disk { + initialize_params { + image = "debian-cloud/debian-9" + } + } + + network_interface { + network = "default" + } +}`, diskName, instanceName) +} + +func testAttachedDiskResourceCount(diskPrefix, instanceName string, count int) string { + return fmt.Sprintf(` +resource "google_compute_disk" "many" { + name = "%s-${count.index}" + zone = "us-central1-a" + size = 10 + count = %d +} + +resource "google_compute_instance" "test" { + name = "%s" + machine_type = "f1-micro" + zone = "us-central1-a" + + lifecycle { + ignore_changes = [ + "attached_disk", + ] + } + + boot_disk { + initialize_params { + image = "debian-cloud/debian-9" + } + } + + network_interface { + network = "default" + } +} + +resource "google_compute_attached_disk" "test" { + count = "${google_compute_disk.many.count}" + disk = "${google_compute_disk.many.*.self_link[count.index]}" + instance = "${google_compute_instance.test.self_link}" +}`, diskPrefix, count, instanceName) +} diff --git a/provider/terraform/tests/resource_compute_backend_bucket_test.go b/provider/terraform/tests/resource_compute_backend_bucket_test.go new file mode 100644 index 000000000000..fbfc3a95cb49 --- /dev/null +++ b/provider/terraform/tests/resource_compute_backend_bucket_test.go @@ -0,0 +1,202 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeBackendBucket_basic(t *testing.T) { + t.Parallel() + + backendName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + storageName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendBucket + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendBucket_basic(backendName, storageName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendBucketExists( + "google_compute_backend_bucket.foobar", &svc), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_bucket.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + + if svc.BucketName != storageName { + t.Errorf("Expected BucketName to be %q, got %q", storageName, svc.BucketName) + } +} + +func TestAccComputeBackendBucket_basicModified(t *testing.T) { + t.Parallel() + + backendName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + storageName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + secondStorageName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendBucket + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendBucket_basic(backendName, storageName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendBucketExists( + "google_compute_backend_bucket.foobar", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeBackendBucket_basicModified( + backendName, storageName, secondStorageName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendBucketExists( + "google_compute_backend_bucket.foobar", &svc), + ), + }, + }, + }) + + if svc.BucketName != secondStorageName { + t.Errorf("Expected BucketName to be %q, got %q", secondStorageName, svc.BucketName) + } +} + +func testAccCheckComputeBackendBucketDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_backend_bucket" { + continue + } + + _, err := config.clientCompute.BackendBuckets.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Backend bucket %s still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckComputeBackendBucketExists(n string, svc *compute.BackendBucket) 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) + + found, err := config.clientCompute.BackendBuckets.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Backend bucket %s not found", rs.Primary.ID) + } + + *svc = *found + + return nil + } +} + +func TestAccComputeBackendBucket_withCdnEnabled(t *testing.T) { + t.Parallel() + + backendName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + storageName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendBucket + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendBucket_withCdnEnabled( + backendName, storageName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendBucketExists( + "google_compute_backend_bucket.foobar", &svc), + ), + }, + }, + }) + + if svc.EnableCdn != true { + t.Errorf("Expected EnableCdn == true, got %t", svc.EnableCdn) + } +} + +func testAccComputeBackendBucket_basic(backendName, storageName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_bucket" "foobar" { + name = "%s" + bucket_name = "${google_storage_bucket.bucket_one.name}" +} + +resource "google_storage_bucket" "bucket_one" { + name = "%s" + location = "EU" +} +`, backendName, storageName) +} + +func testAccComputeBackendBucket_basicModified(backendName, bucketOne, bucketTwo string) string { + return fmt.Sprintf(` +resource "google_compute_backend_bucket" "foobar" { + name = "%s" + bucket_name = "${google_storage_bucket.bucket_two.name}" +} + +resource "google_storage_bucket" "bucket_one" { + name = "%s" + location = "EU" +} + +resource "google_storage_bucket" "bucket_two" { + name = "%s" + location = "EU" +} +`, backendName, bucketOne, bucketTwo) +} + +func testAccComputeBackendBucket_withCdnEnabled(backendName, storageName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_bucket" "foobar" { + name = "%s" + bucket_name = "${google_storage_bucket.bucket.name}" + enable_cdn = true +} + +resource "google_storage_bucket" "bucket" { + name = "%s" + location = "EU" +} +`, backendName, storageName) +} diff --git a/provider/terraform/tests/resource_compute_backend_service_migrate_test.go b/provider/terraform/tests/resource_compute_backend_service_migrate_test.go new file mode 100644 index 000000000000..1d162f5207c2 --- /dev/null +++ b/provider/terraform/tests/resource_compute_backend_service_migrate_test.go @@ -0,0 +1,95 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform/terraform" +) + +func TestComputeBackendServiceMigrateState(t *testing.T) { + cases := map[string]struct { + StateVersion int + Attributes map[string]string + ExpectedAttributes map[string]string + Meta interface{} + }{ + "v0 to v1": { + StateVersion: 0, + Attributes: map[string]string{ + "backend.#": "1", + "backend.242332812.group": "https://www.googleapis.com/compute/v1/projects/project_name/zones/zone_name/instances/instanceGroups/igName", + "backend.242332812.balancing_mode": "UTILIZATION", + "backend.242332812.max_utilization": "0.8", + }, + ExpectedAttributes: map[string]string{ + "backend.#": "1", + "backend.2573491210.group": "https://www.googleapis.com/compute/v1/projects/project_name/zones/zone_name/instances/instanceGroups/igName", + "backend.2573491210.balancing_mode": "UTILIZATION", + "backend.2573491210.max_utilization": "0.8", + }, + Meta: &Config{}, + }, + } + + for tn, tc := range cases { + is := &terraform.InstanceState{ + ID: "i-abc123", + Attributes: tc.Attributes, + } + is, err := resourceComputeBackendServiceMigrateState( + tc.StateVersion, is, tc.Meta) + + if err != nil { + t.Fatalf("bad: %s, err: %#v", tn, err) + } + + for k, v := range tc.ExpectedAttributes { + if is.Attributes[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + tn, k, v, k, is.Attributes[k], is.Attributes) + } + } + + for k, v := range is.Attributes { + if tc.ExpectedAttributes[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + tn, k, tc.ExpectedAttributes[k], k, v, is.Attributes) + } + } + } +} + +func TestComputeBackendServiceMigrateState_empty(t *testing.T) { + cases := map[string]struct { + StateVersion int + }{ + "v0": { + StateVersion: 0, + }, + } + + for tn, tc := range cases { + var is *terraform.InstanceState + var meta *Config + + // should handle nil + is, err := resourceComputeBackendServiceMigrateState(tc.StateVersion, is, meta) + + if err != nil { + t.Fatalf("bad %s, err: %#v", tn, err) + } + if is != nil { + t.Fatalf("bad %s, expected nil instancestate, got: %#v", tn, is) + } + + // should handle non-nil but empty + is = &terraform.InstanceState{} + is, err = resourceComputeBackendServiceMigrateState(tc.StateVersion, is, meta) + + if err != nil { + t.Fatalf("bad %s, err: %#v", tn, err) + } + } +} diff --git a/provider/terraform/tests/resource_compute_backend_service_test.go b/provider/terraform/tests/resource_compute_backend_service_test.go new file mode 100644 index 000000000000..19d8116a6ad5 --- /dev/null +++ b/provider/terraform/tests/resource_compute_backend_service_test.go @@ -0,0 +1,1026 @@ +package google + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeBackendService_basic(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + extraCheckName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_basic(serviceName, checkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeBackendService_basicModified( + serviceName, checkName, extraCheckName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeBackendService_withBackend(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + igName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + itName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withBackend( + serviceName, igName, itName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.lipsum", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeBackendService_withBackend( + serviceName, igName, itName, checkName, 20), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.lipsum", &svc), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.lipsum", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + + if svc.TimeoutSec != 20 { + t.Errorf("Expected TimeoutSec == 20, got %d", svc.TimeoutSec) + } + if svc.Protocol != "HTTP" { + t.Errorf("Expected Protocol to be HTTP, got %q", svc.Protocol) + } + if len(svc.Backends) != 1 { + t.Errorf("Expected 1 backend, got %d", len(svc.Backends)) + } +} + +func TestAccComputeBackendService_withBackendAndIAP(t *testing.T) { + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + igName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + itName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withBackendAndIAP( + serviceName, igName, itName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExistsWithIAP( + "google_compute_backend_service.lipsum", &svc), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.lipsum", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAccComputeBackendService_withBackend( + serviceName, igName, itName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExistsWithoutIAP( + "google_compute_backend_service.lipsum", &svc), + ), + }, + }, + }) + + if svc.TimeoutSec != 10 { + t.Errorf("Expected TimeoutSec == 10, got %d", svc.TimeoutSec) + } + if svc.Protocol != "HTTP" { + t.Errorf("Expected Protocol to be HTTP, got %q", svc.Protocol) + } + if len(svc.Backends) != 1 { + t.Errorf("Expected 1 backend, got %d", len(svc.Backends)) + } + +} + +func TestAccComputeBackendService_updatePreservesOptionalParameters(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withSessionAffinity( + serviceName, checkName, "initial-description", "GENERATED_COOKIE"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeBackendService_withSessionAffinity( + serviceName, checkName, "updated-description", "GENERATED_COOKIE"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + }, + }) + + if svc.SessionAffinity != "GENERATED_COOKIE" { + t.Errorf("Expected SessionAffinity == \"GENERATED_COOKIE\", got %s", svc.SessionAffinity) + } +} + +func TestAccComputeBackendService_withConnectionDraining(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withConnectionDraining(serviceName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + + if svc.ConnectionDraining.DrainingTimeoutSec != 10 { + t.Errorf("Expected ConnectionDraining.DrainingTimeoutSec == 10, got %d", svc.ConnectionDraining.DrainingTimeoutSec) + } +} + +func TestAccComputeBackendService_withConnectionDrainingAndUpdate(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withConnectionDraining(serviceName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeBackendService_basic(serviceName, checkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + }, + }) + + if svc.ConnectionDraining.DrainingTimeoutSec != 300 { + t.Errorf("Expected ConnectionDraining.DrainingTimeoutSec == 300, got %d", svc.ConnectionDraining.DrainingTimeoutSec) + } +} + +func TestAccComputeBackendService_withHttpsHealthCheck(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withHttpsHealthCheck(serviceName, checkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeBackendService_withCdnPolicy(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withCdnPolicy(serviceName, checkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeBackendService_withSecurityPolicy(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + polName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withSecurityPolicy(serviceName, checkName, polName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + resource.TestMatchResourceAttr("google_compute_backend_service.foobar", "security_policy", regexp.MustCompile(polName)), + ), + }, + }, + }) +} + +func testAccCheckComputeBackendServiceDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_backend_service" { + continue + } + + _, err := config.clientCompute.BackendServices.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Backend service %s still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckComputeBackendServiceExists(n string, svc *compute.BackendService) 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) + + found, err := config.clientCompute.BackendServices.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Backend service %s not found", rs.Primary.ID) + } + + *svc = *found + + return nil + } +} + +func testAccCheckComputeBackendServiceExistsWithIAP(n string, svc *compute.BackendService) 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) + + found, err := config.clientCompute.BackendServices.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Backend service %s not found", rs.Primary.ID) + } + + if found.Iap == nil || found.Iap.Enabled == false { + return fmt.Errorf("IAP not found or not enabled.") + } + + *svc = *found + + return nil + } +} + +func testAccCheckComputeBackendServiceExistsWithoutIAP(n string, svc *compute.BackendService) 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) + + found, err := config.clientCompute.BackendServices.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Backend service %s not found", rs.Primary.ID) + } + + if found.Iap != nil && found.Iap.Enabled == true { + return fmt.Errorf("IAP enabled when it should be disabled") + } + + *svc = *found + + return nil + } +} +func TestAccComputeBackendService_withCDNEnabled(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withCDNEnabled( + serviceName, checkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + }, + }) + + if svc.EnableCDN != true { + t.Errorf("Expected EnableCDN == true, got %t", svc.EnableCDN) + } +} + +func TestAccComputeBackendService_withSessionAffinity(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withSessionAffinity( + serviceName, checkName, "description", "CLIENT_IP"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeBackendService_withSessionAffinity( + serviceName, checkName, "description", "GENERATED_COOKIE"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.foobar", &svc), + ), + }, + }, + }) + + if svc.SessionAffinity != "GENERATED_COOKIE" { + t.Errorf("Expected SessionAffinity == \"GENERATED_COOKIE\", got %s", svc.SessionAffinity) + } +} + +func TestAccComputeBackendService_withMaxConnections(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + igName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + itName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + var svc compute.BackendService + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withMaxConnections( + serviceName, igName, itName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.lipsum", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeBackendService_withMaxConnections( + serviceName, igName, itName, checkName, 20), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.lipsum", &svc), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.lipsum", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + + if svc.Backends[0].MaxConnections != 20 { + t.Errorf("Expected MaxConnections == 20, got %d", svc.Backends[0].MaxConnections) + } +} + +func TestAccComputeBackendService_withMaxConnectionsPerInstance(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + igName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + itName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + var svc compute.BackendService + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withMaxConnectionsPerInstance( + serviceName, igName, itName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.lipsum", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeBackendService_withMaxConnectionsPerInstance( + serviceName, igName, itName, checkName, 20), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBackendServiceExists( + "google_compute_backend_service.lipsum", &svc), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.lipsum", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + + if svc.Backends[0].MaxConnectionsPerInstance != 20 { + t.Errorf("Expected MaxConnectionsPerInstance == 20, got %d", svc.Backends[0].MaxConnectionsPerInstance) + } +} + +func TestAccComputeBackendService_withCustomHeaders(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeBackendService_withCustomHeaders(serviceName, checkName), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAccComputeBackendService_basic(serviceName, checkName), + }, + resource.TestStep{ + ResourceName: "google_compute_backend_service.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeBackendService_basic(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, checkName) +} + +func testAccComputeBackendService_withCDNEnabled(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + enable_cdn = true +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, checkName) +} + +func testAccComputeBackendService_basicModified(serviceName, checkOne, checkTwo string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.one.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_http_health_check" "one" { + name = "%s" + request_path = "/one" + check_interval_sec = 30 + timeout_sec = 30 +} +`, serviceName, checkOne, checkTwo) +} + +func testAccComputeBackendService_withBackend( + serviceName, igName, itName, checkName string, timeout int64) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_backend_service" "lipsum" { + name = "%s" + description = "Hello World 1234" + port_name = "http" + protocol = "HTTP" + timeout_sec = %v + + backend { + group = "${google_compute_instance_group_manager.foobar.instance_group}" + } + + health_checks = ["${google_compute_http_health_check.default.self_link}"] +} + +resource "google_compute_instance_group_manager" "foobar" { + name = "%s" + instance_template = "${google_compute_instance_template.foobar.self_link}" + base_instance_name = "foobar" + zone = "us-central1-f" + target_size = 1 + auto_healing_policies { + health_check = "${google_compute_http_health_check.default.self_link}" + initial_delay_sec = "10" + } +} + +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + + network_interface { + network = "default" + } + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } +} + +resource "google_compute_http_health_check" "default" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, timeout, igName, itName, checkName) +} + +func testAccComputeBackendService_withBackendAndIAP( + serviceName, igName, itName, checkName string, timeout int64) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_backend_service" "lipsum" { + name = "%s" + description = "Hello World 1234" + port_name = "http" + protocol = "HTTP" + timeout_sec = %v + + backend { + group = "${google_compute_instance_group_manager.foobar.instance_group}" + } + + iap { + oauth2_client_id = "test" + oauth2_client_secret = "test" + } + + health_checks = ["${google_compute_http_health_check.default.self_link}"] +} + +resource "google_compute_instance_group_manager" "foobar" { + name = "%s" + instance_template = "${google_compute_instance_template.foobar.self_link}" + base_instance_name = "foobar" + zone = "us-central1-f" + target_size = 1 +} + +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + + network_interface { + network = "default" + } + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } +} + +resource "google_compute_http_health_check" "default" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, timeout, igName, itName, checkName) +} + +func testAccComputeBackendService_withSessionAffinity(serviceName, checkName, description, affinityName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + description = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + session_affinity = "%s" +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, description, affinityName, checkName) +} + +func testAccComputeBackendService_withConnectionDraining(serviceName, checkName string, drainingTimeout int64) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + connection_draining_timeout_sec = %v +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, drainingTimeout, checkName) +} + +func testAccComputeBackendService_withHttpsHealthCheck(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_https_health_check.zero.self_link}"] + protocol = "HTTPS" +} + +resource "google_compute_https_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, checkName) +} + +func testAccComputeBackendService_withCdnPolicy(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + + cdn_policy { + cache_key_policy { + include_protocol = true + include_host = true + include_query_string = true + query_string_whitelist = ["foo", "bar"] + } + } +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, checkName) +} + +func testAccComputeBackendService_withSecurityPolicy(serviceName, checkName, polName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + security_policy = "${google_compute_security_policy.policy.self_link}" +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_security_policy" "policy" { + name = "%s" + description = "basic security policy" +} +`, serviceName, checkName, polName) +} + +func testAccComputeBackendService_withMaxConnections( + serviceName, igName, itName, checkName string, maxConnections int64) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_backend_service" "lipsum" { + name = "%s" + description = "Hello World 1234" + port_name = "http" + protocol = "TCP" + + backend { + group = "${google_compute_instance_group_manager.foobar.instance_group}" + max_connections = %v + } + + health_checks = ["${google_compute_health_check.default.self_link}"] +} + +resource "google_compute_instance_group_manager" "foobar" { + name = "%s" + instance_template = "${google_compute_instance_template.foobar.self_link}" + base_instance_name = "foobar" + zone = "us-central1-f" + target_size = 1 + auto_healing_policies { + health_check = "${google_compute_health_check.default.self_link}" + initial_delay_sec = "10" + } +} + +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + + network_interface { + network = "default" + } + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } +} + +resource "google_compute_health_check" "default" { + name = "%s" + tcp_health_check { + port = "110" + } +} +`, serviceName, maxConnections, igName, itName, checkName) +} + +func testAccComputeBackendService_withMaxConnectionsPerInstance( + serviceName, igName, itName, checkName string, maxConnectionsPerInstance int64) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_backend_service" "lipsum" { + name = "%s" + description = "Hello World 1234" + port_name = "http" + protocol = "TCP" + + backend { + group = "${google_compute_instance_group_manager.foobar.instance_group}" + max_connections_per_instance = %v + } + + health_checks = ["${google_compute_health_check.default.self_link}"] +} + +resource "google_compute_instance_group_manager" "foobar" { + name = "%s" + instance_template = "${google_compute_instance_template.foobar.self_link}" + base_instance_name = "foobar" + zone = "us-central1-f" + target_size = 1 + auto_healing_policies { + health_check = "${google_compute_health_check.default.self_link}" + initial_delay_sec = "10" + } +} + +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + + network_interface { + network = "default" + } + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } +} + +resource "google_compute_health_check" "default" { + name = "%s" + tcp_health_check { + port = "110" + } +} +`, serviceName, maxConnectionsPerInstance, igName, itName, checkName) +} + +func testAccComputeBackendService_withCustomHeaders(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + + custom_request_headers = ["Client-Region: {client_region}", "Client-Rtt: {client_rtt_msec}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +`, serviceName, checkName) +} diff --git a/provider/terraform/tests/resource_compute_disk_test.go b/provider/terraform/tests/resource_compute_disk_test.go new file mode 100644 index 000000000000..74dd3abb487f --- /dev/null +++ b/provider/terraform/tests/resource_compute_disk_test.go @@ -0,0 +1,772 @@ +package google + +import ( + "fmt" + "os" + "regexp" + "strconv" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestDiskImageDiffSuppress(t *testing.T) { + cases := map[string]struct { + Old, New string + ExpectDiffSuppress bool + }{ + // Full & partial links + "matching self_link with different api version": { + Old: "https://www.googleapis.com/compute/beta/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + ExpectDiffSuppress: true, + }, + "matching image partial self_link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "projects/debian-cloud/global/images/debian-8-jessie-v20171213", + ExpectDiffSuppress: true, + }, + "matching image partial no project self_link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "global/images/debian-8-jessie-v20171213", + ExpectDiffSuppress: true, + }, + "different image self_link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-jessie-v20171213", + ExpectDiffSuppress: false, + }, + "different image partial self_link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "projects/debian-cloud/global/images/debian-7-jessie-v20171213", + ExpectDiffSuppress: false, + }, + "different image partial no project self_link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "global/images/debian-7-jessie-v20171213", + ExpectDiffSuppress: false, + }, + // Image name + "matching image name": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "debian-8-jessie-v20171213", + ExpectDiffSuppress: true, + }, + "different image name": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "debian-7-jessie-v20171213", + ExpectDiffSuppress: false, + }, + // Image short hand + "matching image short hand": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "debian-cloud/debian-8-jessie-v20171213", + ExpectDiffSuppress: true, + }, + "matching image short hand but different project": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "different-cloud/debian-8-jessie-v20171213", + ExpectDiffSuppress: false, + }, + "different image short hand": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "debian-cloud/debian-7-jessie-v20171213", + ExpectDiffSuppress: false, + }, + // Image Family + "matching image family": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "family/debian-8", + ExpectDiffSuppress: true, + }, + "matching image family self link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/family/debian-8", + ExpectDiffSuppress: true, + }, + "matching unconventional image family self link": { + Old: "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1404-trusty-v20180122", + New: "https://www.googleapis.com/compute/v1/projects/projects/ubuntu-os-cloud/global/images/family/ubuntu-1404-lts", + ExpectDiffSuppress: true, + }, + "matching image family partial self link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "projects/debian-cloud/global/images/family/debian-8", + ExpectDiffSuppress: true, + }, + "matching unconventional image family partial self link": { + Old: "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1404-trusty-v20180122", + New: "projects/ubuntu-os-cloud/global/images/family/ubuntu-1404-lts", + ExpectDiffSuppress: true, + }, + "matching image family partial no project self link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "global/images/family/debian-8", + ExpectDiffSuppress: true, + }, + "matching image family short hand": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "debian-cloud/debian-8", + ExpectDiffSuppress: true, + }, + "matching image family short hand with project short name": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "debian/debian-8", + ExpectDiffSuppress: true, + }, + "matching unconventional image family short hand": { + Old: "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1404-trusty-v20180122", + New: "ubuntu-os-cloud/ubuntu-1404-lts", + ExpectDiffSuppress: true, + }, + "matching unconventional image family - minimal": { + Old: "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-minimal-1804-bionic-v20180705", + New: "ubuntu-minimal-1804-lts", + ExpectDiffSuppress: true, + }, + "different image family": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "family/debian-7", + ExpectDiffSuppress: false, + }, + "different image family self link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/family/debian-7", + ExpectDiffSuppress: false, + }, + "different image family partial self link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "projects/debian-cloud/global/images/family/debian-7", + ExpectDiffSuppress: false, + }, + "different image family partial no project self link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "global/images/family/debian-7", + ExpectDiffSuppress: false, + }, + "matching image family but different project in self link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "https://www.googleapis.com/compute/v1/projects/other-cloud/global/images/family/debian-8", + ExpectDiffSuppress: false, + }, + "different image family but different project in partial self link": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "projects/other-cloud/global/images/family/debian-8", + ExpectDiffSuppress: false, + }, + "different image family short hand": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "debian-cloud/debian-7", + ExpectDiffSuppress: false, + }, + "matching image family shorthand but different project": { + Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", + New: "different-cloud/debian-8", + ExpectDiffSuppress: false, + }, + } + + for tn, tc := range cases { + if diskImageDiffSuppress("image", tc.Old, tc.New, nil) != tc.ExpectDiffSuppress { + t.Errorf("bad: %s, %q => %q expect DiffSuppress to return %t", tn, tc.Old, tc.New, tc.ExpectDiffSuppress) + } + } +} + +// Test that all the naming pattern for public images are supported. +func TestAccComputeDisk_imageDiffSuppressPublicVendorsFamilyNames(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + + config := getInitializedConfig(t) + + for _, publicImageProject := range imageMap { + token := "" + for paginate := true; paginate; { + resp, err := config.clientCompute.Images.List(publicImageProject).Filter("deprecated.replacement ne .*images.*").PageToken(token).Do() + if err != nil { + t.Fatalf("Can't list public images for project %q", publicImageProject) + } + + for _, image := range resp.Items { + if !diskImageDiffSuppress("image", image.SelfLink, "family/"+image.Family, nil) { + t.Errorf("should suppress diff for image %q and family %q", image.SelfLink, image.Family) + } + } + token := resp.NextPageToken + paginate = token != "" + } + } +} + +func TestAccComputeDisk_basic(t *testing.T) { + t.Parallel() + + diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var disk compute.Disk + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeDiskDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeDisk_basic(diskName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foobar", &disk), + testAccCheckComputeDiskHasLabel(&disk, "my-label", "my-label-value"), + testAccCheckComputeDiskHasLabelFingerprint(&disk, "google_compute_disk.foobar"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_disk.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeDisk_timeout(t *testing.T) { + t.Parallel() + + diskName := acctest.RandomWithPrefix("tf-test-disk") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeDisk_timeout(diskName), + ExpectError: regexp.MustCompile("timeout"), + }, + }, + }) +} + +func TestAccComputeDisk_update(t *testing.T) { + t.Parallel() + + diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var disk compute.Disk + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccComputeDisk_basic(diskName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foobar", &disk), + resource.TestCheckResourceAttr("google_compute_disk.foobar", "size", "50"), + testAccCheckComputeDiskHasLabel(&disk, "my-label", "my-label-value"), + testAccCheckComputeDiskHasLabelFingerprint(&disk, "google_compute_disk.foobar"), + ), + }, + { + Config: testAccComputeDisk_updated(diskName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foobar", &disk), + resource.TestCheckResourceAttr("google_compute_disk.foobar", "size", "100"), + testAccCheckComputeDiskHasLabel(&disk, "my-label", "my-updated-label-value"), + testAccCheckComputeDiskHasLabel(&disk, "a-new-label", "a-new-label-value"), + testAccCheckComputeDiskHasLabelFingerprint(&disk, "google_compute_disk.foobar"), + ), + }, + }, + }) +} + +func TestAccComputeDisk_fromSnapshot(t *testing.T) { + t.Parallel() + + diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + firstDiskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + snapshotName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + projectName := getTestProjectFromEnv() + + var disk compute.Disk + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeDiskDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeDisk_fromSnapshot(projectName, firstDiskName, snapshotName, diskName, "self_link"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.seconddisk", &disk), + ), + }, + resource.TestStep{ + Config: testAccComputeDisk_fromSnapshot(projectName, firstDiskName, snapshotName, diskName, "name"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.seconddisk", &disk), + ), + }, + }, + }) +} + +func TestAccComputeDisk_encryption(t *testing.T) { + t.Parallel() + + diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var disk compute.Disk + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeDiskDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeDisk_encryption(diskName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foobar", &disk), + testAccCheckEncryptionKey( + "google_compute_disk.foobar", &disk), + ), + }, + }, + }) +} + +func TestAccComputeDisk_deleteDetach(t *testing.T) { + t.Parallel() + + diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var disk compute.Disk + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeDiskDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeDisk_deleteDetach(instanceName, diskName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foo", &disk), + ), + }, + // this needs to be a second step so we refresh and see the instance + // listed as attached to the disk; the instance is created after the + // disk. and the disk's properties aren't refreshed unless there's + // another step + resource.TestStep{ + Config: testAccComputeDisk_deleteDetach(instanceName, diskName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foo", &disk), + testAccCheckComputeDiskInstances( + "google_compute_disk.foo", &disk), + ), + }, + }, + }) +} + +func TestAccComputeDisk_deleteDetachIGM(t *testing.T) { + t.Parallel() + + diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + diskName2 := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + mgrName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var disk compute.Disk + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeDiskDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeDisk_deleteDetachIGM(diskName, mgrName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foo", &disk), + ), + }, + // this needs to be a second step so we refresh and see the instance + // listed as attached to the disk; the instance is created after the + // disk. and the disk's properties aren't refreshed unless there's + // another step + resource.TestStep{ + Config: testAccComputeDisk_deleteDetachIGM(diskName, mgrName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foo", &disk), + testAccCheckComputeDiskInstances( + "google_compute_disk.foo", &disk), + ), + }, + // Change the disk name to recreate the instances + resource.TestStep{ + Config: testAccComputeDisk_deleteDetachIGM(diskName2, mgrName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foo", &disk), + ), + }, + // Add the extra step like before + resource.TestStep{ + Config: testAccComputeDisk_deleteDetachIGM(diskName2, mgrName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeDiskExists( + "google_compute_disk.foo", &disk), + testAccCheckComputeDiskInstances( + "google_compute_disk.foo", &disk), + ), + }, + }, + }) +} + +func TestAccComputeDisk_computeDiskUserRegex(t *testing.T) { + + shouldPass := []string{ + + "https://www.googleapis.com/compute/v1/projects/project-id/zones/us-central1/instances/123", + "https://www.googleapis.com/compute/v1/projects/123123/zones/us-central1/instances/123", + "https://www.googleapis.com/compute/v1/projects/hashicorptest.net:project-123/zones/us-central1/instances/123", + "https://www.googleapis.com/compute/v1/projects/123/zones/456/instances/789", + } + + shouldFail := []string{ + "https://www.googleapis.com/compute/v1/projects/project#/zones/us-central1/instances/123", + "https://www.googleapis.com/compute/v1/projects/project/zones/us-central#/instances/123", + "https://www.googleapis.com/compute/v1/projects/project/zones/us-central1/instances/?", + "https://www.googleapis.com/compute/v1/projects/foo.com:bar:baz/zones/us-central1/instances/?", + "https://www.googleapis.com/compute/v1/projects/foo.com:/zones/us-central1/instances/?", + } + + for _, element := range shouldPass { + if !computeDiskUserRegex.MatchString(element) { + t.Error("computeDiskUserRegex should match on '" + element + "' but doesn't") + } + } + + for _, element := range shouldFail { + if computeDiskUserRegex.MatchString(element) { + t.Error("computeDiskUserRegex shouldn't match on '" + element + "' but does") + } + } + +} + +func testAccCheckComputeDiskDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_disk" { + continue + } + + _, err := config.clientCompute.Disks.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Disk still exists") + } + } + + return nil +} + +func testAccCheckComputeDiskExists(n string, disk *compute.Disk) resource.TestCheckFunc { + return func(s *terraform.State) error { + p := getTestProjectFromEnv() + 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) + + found, err := config.clientCompute.Disks.Get( + p, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Disk not found") + } + + *disk = *found + + return nil + } +} + +func testAccCheckComputeDiskHasLabel(disk *compute.Disk, key, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + val, ok := disk.Labels[key] + if !ok { + return fmt.Errorf("Label with key %s not found", key) + } + + if val != value { + return fmt.Errorf("Label value did not match for key %s: expected %s but found %s", key, value, val) + } + return nil + } +} + +func testAccCheckComputeDiskHasLabelFingerprint(disk *compute.Disk, resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + state := s.RootModule().Resources[resourceName] + if state == nil { + return fmt.Errorf("Unable to find resource named %s", resourceName) + } + + labelFingerprint := state.Primary.Attributes["label_fingerprint"] + if labelFingerprint != disk.LabelFingerprint { + return fmt.Errorf("Label fingerprints do not match: api returned %s but state has %s", + disk.LabelFingerprint, labelFingerprint) + } + + return nil + } +} + +func testAccCheckEncryptionKey(n string, disk *compute.Disk) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + attr := rs.Primary.Attributes["disk_encryption_key.0.sha256"] + if disk.DiskEncryptionKey == nil { + return fmt.Errorf("Disk %s has mismatched encryption key.\nTF State: %+v\nGCP State: ", n, attr) + } else if attr != disk.DiskEncryptionKey.Sha256 { + return fmt.Errorf("Disk %s has mismatched encryption key.\nTF State: %+v.\nGCP State: %+v", + n, attr, disk.DiskEncryptionKey.Sha256) + } + return nil + } +} + +func testAccCheckComputeDiskInstances(n string, disk *compute.Disk) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + attr := rs.Primary.Attributes["users.#"] + if strconv.Itoa(len(disk.Users)) != attr { + return fmt.Errorf("Disk %s has mismatched users.\nTF State: %+v\nGCP State: %+v", n, rs.Primary.Attributes["users"], disk.Users) + } + + for pos, user := range disk.Users { + if rs.Primary.Attributes["users."+strconv.Itoa(pos)] != user { + return fmt.Errorf("Disk %s has mismatched users.\nTF State: %+v.\nGCP State: %+v", + n, rs.Primary.Attributes["users"], disk.Users) + } + } + return nil + } +} + +func testAccComputeDisk_basic(diskName string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + image = "${data.google_compute_image.my_image.self_link}" + size = 50 + type = "pd-ssd" + zone = "us-central1-a" + labels { + my-label = "my-label-value" + } +}`, diskName) +} + +func testAccComputeDisk_timeout(diskName string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + image = "${data.google_compute_image.my_image.self_link}" + type = "pd-ssd" + zone = "us-central1-a" + + timeouts { + create = "1s" + } +}`, diskName) +} + +func testAccComputeDisk_updated(diskName string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + image = "${data.google_compute_image.my_image.self_link}" + size = 100 + type = "pd-ssd" + zone = "us-central1-a" + labels { + my-label = "my-updated-label-value" + a-new-label = "a-new-label-value" + } +}`, diskName) +} + +func testAccComputeDisk_fromSnapshot(projectName, firstDiskName, snapshotName, diskName, ref_selector string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "d1-%s" + image = "${data.google_compute_image.my_image.self_link}" + size = 50 + type = "pd-ssd" + zone = "us-central1-a" + project = "%s" +} + +resource "google_compute_snapshot" "snapdisk" { + name = "%s" + source_disk = "${google_compute_disk.foobar.name}" + zone = "us-central1-a" + project = "%s" +} + +resource "google_compute_disk" "seconddisk" { + name = "d2-%s" + snapshot = "${google_compute_snapshot.snapdisk.%s}" + type = "pd-ssd" + zone = "us-central1-a" + project = "%s" +}`, firstDiskName, projectName, snapshotName, projectName, diskName, ref_selector, projectName) +} + +func testAccComputeDisk_encryption(diskName string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + image = "${data.google_compute_image.my_image.self_link}" + size = 50 + type = "pd-ssd" + zone = "us-central1-a" + disk_encryption_key { + raw_key = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=" + } +}`, diskName) +} + +func testAccComputeDisk_deleteDetach(instanceName, diskName string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foo" { + name = "%s" + image = "${data.google_compute_image.my_image.self_link}" + size = 50 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance" "bar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + attached_disk { + source = "${google_compute_disk.foo.self_link}" + } + + network_interface { + network = "default" + } +}`, diskName, instanceName) +} + +func testAccComputeDisk_deleteDetachIGM(diskName, mgrName string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foo" { + name = "%s" + image = "${data.google_compute_image.my_image.self_link}" + size = 50 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance_template" "template" { + machine_type = "g1-small" + + disk { + boot = true + source = "${google_compute_disk.foo.name}" + auto_delete = false + } + + network_interface { + network = "default" + } + + lifecycle { + create_before_destroy = true + } +} + +resource "google_compute_instance_group_manager" "manager" { + name = "%s" + base_instance_name = "disk-igm" + instance_template = "${google_compute_instance_template.template.self_link}" + zone = "us-central1-a" + target_size = 1 +}`, diskName, mgrName) +} diff --git a/provider/terraform/tests/resource_compute_firewall_migrate_test.go b/provider/terraform/tests/resource_compute_firewall_migrate_test.go new file mode 100644 index 000000000000..e28d607f358c --- /dev/null +++ b/provider/terraform/tests/resource_compute_firewall_migrate_test.go @@ -0,0 +1,81 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform/terraform" +) + +func TestComputeFirewallMigrateState(t *testing.T) { + cases := map[string]struct { + StateVersion int + Attributes map[string]string + Expected map[string]string + Meta interface{} + }{ + "change scope from list to set": { + StateVersion: 0, + Attributes: map[string]string{ + "allow.#": "1", + "allow.0.protocol": "udp", + "allow.0.ports.#": "4", + "allow.0.ports.1693978638": "8080", + "allow.0.ports.172152165": "8081", + "allow.0.ports.299962681": "7072", + "allow.0.ports.3435931483": "4044", + }, + Expected: map[string]string{ + "allow.#": "1", + "allow.0.protocol": "udp", + "allow.0.ports.#": "4", + "allow.0.ports.0": "8080", + "allow.0.ports.1": "8081", + "allow.0.ports.2": "7072", + "allow.0.ports.3": "4044", + }, + }, + } + for tn, tc := range cases { + is := &terraform.InstanceState{ + ID: "i-abc123", + Attributes: tc.Attributes, + } + is, err := resourceComputeFirewallMigrateState( + tc.StateVersion, is, tc.Meta) + + if err != nil { + t.Fatalf("bad: %s, err: %#v", tn, err) + } + + for k, v := range tc.Expected { + if is.Attributes[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + tn, k, v, k, is.Attributes[k], is.Attributes) + } + } + } +} + +func TestComputeFirewallMigrateState_empty(t *testing.T) { + var is *terraform.InstanceState + var meta interface{} + + // should handle nil + is, err := resourceComputeFirewallMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } + if is != nil { + t.Fatalf("expected nil instancestate, got: %#v", is) + } + + // should handle non-nil but empty + is = &terraform.InstanceState{} + is, err = resourceComputeFirewallMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } +} diff --git a/provider/terraform/tests/resource_compute_forwarding_rule_test.go b/provider/terraform/tests/resource_compute_forwarding_rule_test.go new file mode 100644 index 000000000000..bc936e080b7e --- /dev/null +++ b/provider/terraform/tests/resource_compute_forwarding_rule_test.go @@ -0,0 +1,301 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeForwardingRule_update(t *testing.T) { + t.Parallel() + + poolName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + ruleName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeForwardingRule_basic(poolName, ruleName), + }, + resource.TestStep{ + ResourceName: "google_compute_forwarding_rule.foobar", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAccComputeForwardingRule_update(poolName, ruleName), + }, + resource.TestStep{ + ResourceName: "google_compute_forwarding_rule.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeForwardingRule_singlePort(t *testing.T) { + t.Parallel() + + poolName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + ruleName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeForwardingRule_singlePort(poolName, ruleName), + }, + resource.TestStep{ + ResourceName: "google_compute_forwarding_rule.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeForwardingRule_ip(t *testing.T) { + t.Parallel() + + addrName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + poolName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + ruleName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeForwardingRule_ip(addrName, poolName, ruleName), + }, + resource.TestStep{ + ResourceName: "google_compute_forwarding_rule.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeForwardingRule_internalLoadBalancing(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + networkName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + ruleName1 := fmt.Sprintf("tf-%s", acctest.RandString(10)) + ruleName2 := fmt.Sprintf("tf-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeForwardingRule_internalLoadBalancing(serviceName, checkName, networkName, ruleName1, ruleName2), + }, + resource.TestStep{ + ResourceName: "google_compute_forwarding_rule.foobar", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + ResourceName: "google_compute_forwarding_rule.foobar2", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeForwardingRule_networkTier(t *testing.T) { + t.Parallel() + + poolName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + ruleName := fmt.Sprintf("tf-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeForwardingRule_networkTier(poolName, ruleName), + }, + + resource.TestStep{ + ResourceName: "google_compute_forwarding_rule.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckComputeForwardingRuleDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_forwarding_rule" { + continue + } + + _, err := config.clientCompute.ForwardingRules.Get( + config.Project, config.Region, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("ForwardingRule still exists") + } + } + + return nil +} + +func testAccComputeForwardingRule_basic(poolName, ruleName string) string { + return fmt.Sprintf(` +resource "google_compute_target_pool" "foo-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "foo-%s" +} +resource "google_compute_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "UDP" + name = "%s" + port_range = "80-81" + target = "${google_compute_target_pool.foo-tp.self_link}" + labels = {"foo" = "bar"} +} +`, poolName, ruleName) +} + +func testAccComputeForwardingRule_update(poolName, ruleName string) string { + return fmt.Sprintf(` +resource "google_compute_target_pool" "foo-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "foo-%s" +} +resource "google_compute_target_pool" "bar-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "bar-%s" +} +resource "google_compute_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "UDP" + name = "%s" + port_range = "80-81" + target = "${google_compute_target_pool.bar-tp.self_link}" + labels = {"baz" = "qux"} +} +`, poolName, poolName, ruleName) +} + +func testAccComputeForwardingRule_singlePort(poolName, ruleName string) string { + return fmt.Sprintf(` +resource "google_compute_target_pool" "foobar-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "%s" +} +resource "google_compute_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "UDP" + name = "%s" + port_range = "80" + target = "${google_compute_target_pool.foobar-tp.self_link}" +} +`, poolName, ruleName) +} + +func testAccComputeForwardingRule_ip(addrName, poolName, ruleName string) string { + return fmt.Sprintf(` +resource "google_compute_address" "foo" { + name = "%s" +} +resource "google_compute_target_pool" "foobar-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "%s" +} +resource "google_compute_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_address = "${google_compute_address.foo.address}" + ip_protocol = "TCP" + name = "%s" + port_range = "80-81" + target = "${google_compute_target_pool.foobar-tp.self_link}" +} +`, addrName, poolName, ruleName) +} + +func testAccComputeForwardingRule_internalLoadBalancing(serviceName, checkName, networkName, ruleName1, ruleName2 string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar-bs" { + name = "%s" + description = "Resource created for Terraform acceptance testing" + health_checks = ["${google_compute_health_check.zero.self_link}"] + region = "us-central1" +} +resource "google_compute_health_check" "zero" { + name = "%s" + description = "Resource created for Terraform acceptance testing" + check_interval_sec = 1 + timeout_sec = 1 + + tcp_health_check { + port = "80" + } +} +resource "google_compute_network" "foobar" { + name = "%s" + auto_create_subnetworks = true +} +resource "google_compute_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + load_balancing_scheme = "INTERNAL" + backend_service = "${google_compute_region_backend_service.foobar-bs.self_link}" + ports = ["80"] + network = "${google_compute_network.foobar.name}" + subnetwork = "%s" +} +resource "google_compute_forwarding_rule" "foobar2" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + load_balancing_scheme = "INTERNAL" + backend_service = "${google_compute_region_backend_service.foobar-bs.self_link}" + ports = ["80"] + network = "${google_compute_network.foobar.self_link}" +} +`, serviceName, checkName, networkName, ruleName1, networkName, ruleName2) +} + +func testAccComputeForwardingRule_networkTier(poolName, ruleName string) string { + return fmt.Sprintf(` +resource "google_compute_target_pool" "foobar-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "%s" +} +resource "google_compute_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "UDP" + name = "%s" + port_range = "80-81" + target = "${google_compute_target_pool.foobar-tp.self_link}" + + network_tier = "STANDARD" +} +`, poolName, ruleName) +} diff --git a/provider/terraform/tests/resource_compute_global_forwarding_rule_test.go b/provider/terraform/tests/resource_compute_global_forwarding_rule_test.go new file mode 100644 index 000000000000..d6dc28934045 --- /dev/null +++ b/provider/terraform/tests/resource_compute_global_forwarding_rule_test.go @@ -0,0 +1,579 @@ +package google + +import ( + "fmt" + "testing" + + "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" +) + +func TestAccComputeGlobalForwardingRule_basic(t *testing.T) { + t.Parallel() + + fr := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + proxy1 := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + proxy2 := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + backend := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + urlmap := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeGlobalForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeGlobalForwardingRule_basic1(fr, proxy1, proxy2, backend, hc, urlmap), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeGlobalForwardingRuleExists( + "google_compute_global_forwarding_rule.foobar"), + + // Unlike GlobalAddress, IpVersion is "" instead of "IPV4" when this is made with a v1 API. + testAccCheckComputeBetaGlobalForwardingRuleIpVersion("google_compute_global_forwarding_rule.foobar", ""), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_global_forwarding_rule.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeGlobalForwardingRule_update(t *testing.T) { + t.Parallel() + + fr := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + proxy1 := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + proxy2 := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + backend := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + urlmap := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeGlobalForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeGlobalForwardingRule_basic1(fr, proxy1, proxy2, backend, hc, urlmap), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeGlobalForwardingRuleExists( + "google_compute_global_forwarding_rule.foobar"), + ), + }, + + resource.TestStep{ + Config: testAccComputeGlobalForwardingRule_basic2(fr, proxy1, proxy2, backend, hc, urlmap), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeGlobalForwardingRuleExists( + "google_compute_global_forwarding_rule.foobar"), + ), + }, + }, + }) +} + +func TestAccComputeGlobalForwardingRule_ipv6(t *testing.T) { + t.Parallel() + + var frule computeBeta.ForwardingRule + + fr := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + proxy1 := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + proxy2 := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + backend := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + urlmap := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeGlobalForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeGlobalForwardingRule_ipv6(fr, proxy1, proxy2, backend, hc, urlmap), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBetaGlobalForwardingRuleExists( + "google_compute_global_forwarding_rule.foobar", &frule), + testAccCheckComputeBetaGlobalForwardingRuleIpVersion("google_compute_global_forwarding_rule.foobar", "IPV6"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_global_forwarding_rule.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeGlobalForwardingRule_labels(t *testing.T) { + t.Parallel() + + var frule computeBeta.ForwardingRule + + fr := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + proxy1 := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + proxy2 := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + backend := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + urlmap := fmt.Sprintf("forwardrule-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeGlobalForwardingRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeGlobalForwardingRule_labels(fr, proxy1, proxy2, backend, hc, urlmap), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBetaGlobalForwardingRuleExists( + "google_compute_global_forwarding_rule.foobar", &frule), + testAccCheckComputeBetaGlobalForwardingRuleHasLabel(&frule, "my-label", "my-label-value"), + testAccCheckComputeBetaGlobalForwardingRuleHasLabel(&frule, "my-second-label", "my-second-label-value"), + testAccCheckComputeBetaGlobalForwardingRuleHasCorrectLabelFingerprint(&frule, "google_compute_global_forwarding_rule.foobar"), + ), + }, + resource.TestStep{ + Config: testAccComputeGlobalForwardingRule_labelsUpdated(fr, proxy1, proxy2, backend, hc, urlmap), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeBetaGlobalForwardingRuleExists( + "google_compute_global_forwarding_rule.foobar", &frule), + testAccCheckComputeBetaGlobalForwardingRuleHasLabel(&frule, "my-label", "my-label-value"), + testAccCheckComputeBetaGlobalForwardingRuleHasLabel(&frule, "my-third-label", "my-third-label-value"), + testAccCheckComputeBetaGlobalForwardingRuleHasCorrectLabelFingerprint(&frule, "google_compute_global_forwarding_rule.foobar"), + ), + }, + }, + }) +} + +func testAccCheckComputeGlobalForwardingRuleDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_global_forwarding_rule" { + continue + } + + _, err := config.clientCompute.GlobalForwardingRules.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Global Forwarding Rule still exists") + } + } + + return nil +} + +func testAccCheckComputeGlobalForwardingRuleExists(n string) 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) + + found, err := config.clientCompute.GlobalForwardingRules.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Global Forwarding Rule not found") + } + + return nil + } +} + +func testAccCheckComputeBetaGlobalForwardingRuleExists(n string, frule *computeBeta.ForwardingRule) 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) + + found, err := config.clientComputeBeta.GlobalForwardingRules.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Global Forwarding Rule not found") + } + + *frule = *found + + return nil + } +} + +func testAccCheckComputeBetaGlobalForwardingRuleIpVersion(n, version string) 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) + + frule, err := config.clientComputeBeta.GlobalForwardingRules.Get(config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if frule.IpVersion != version { + return fmt.Errorf("Expected IP version to be %s, got %s", version, frule.IpVersion) + } + + return nil + } +} + +func testAccCheckComputeBetaGlobalForwardingRuleHasLabel(frule *computeBeta.ForwardingRule, key, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + val, ok := frule.Labels[key] + if !ok { + return fmt.Errorf("label with key %s not found", key) + } + + if val != value { + return fmt.Errorf("label value did not match for key %s: expected %s but found %s", key, value, val) + } + return nil + } +} + +func testAccCheckComputeBetaGlobalForwardingRuleHasCorrectLabelFingerprint( + frule *computeBeta.ForwardingRule, resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + tfLabelFingerprint := s.RootModule().Resources[resourceName].Primary.Attributes["label_fingerprint"] + remoteLabelFingerprint := frule.LabelFingerprint + + if tfLabelFingerprint != remoteLabelFingerprint { + return fmt.Errorf("Label fingerprint mismatch: remote has %#v but terraform has %#v", + remoteLabelFingerprint, tfLabelFingerprint) + } + return nil + } +} + +func testAccComputeGlobalForwardingRule_basic1(fr, proxy1, proxy2, backend, hc, urlmap string) string { + return fmt.Sprintf(` + resource "google_compute_global_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "TCP" + name = "%s" + port_range = "80" + target = "${google_compute_target_http_proxy.foobar1.self_link}" + } + + resource "google_compute_target_http_proxy" "foobar1" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar.self_link}" + } + + resource "google_compute_target_http_proxy" "foobar2" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar.self_link}" + } + + resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + } + + resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 + } + + resource "google_compute_url_map" "foobar" { + name = "%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + path_rule { + paths = ["/*"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } + }`, fr, proxy1, proxy2, backend, hc, urlmap) +} + +func testAccComputeGlobalForwardingRule_labels(fr, proxy1, proxy2, backend, hc, urlmap string) string { + return fmt.Sprintf(` + resource "google_compute_global_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "TCP" + name = "%s" + port_range = "80" + target = "${google_compute_target_http_proxy.foobar1.self_link}" + + labels { + my-label = "my-label-value" + my-second-label = "my-second-label-value" + } + } + + resource "google_compute_target_http_proxy" "foobar1" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar.self_link}" + } + + resource "google_compute_target_http_proxy" "foobar2" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar.self_link}" + } + + resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + } + + resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 + } + + resource "google_compute_url_map" "foobar" { + name = "%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + path_rule { + paths = ["/*"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } + }`, fr, proxy1, proxy2, backend, hc, urlmap) +} + +func testAccComputeGlobalForwardingRule_labelsUpdated(fr, proxy1, proxy2, backend, hc, urlmap string) string { + return fmt.Sprintf(` + resource "google_compute_global_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "TCP" + name = "%s" + port_range = "80" + target = "${google_compute_target_http_proxy.foobar1.self_link}" + + labels { + my-label = "my-label-value" + my-third-label = "my-third-label-value" + } + } + + resource "google_compute_target_http_proxy" "foobar1" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar.self_link}" + } + + resource "google_compute_target_http_proxy" "foobar2" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar.self_link}" + } + + resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + } + + resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 + } + + resource "google_compute_url_map" "foobar" { + name = "%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + path_rule { + paths = ["/*"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } + }`, fr, proxy1, proxy2, backend, hc, urlmap) +} + +func testAccComputeGlobalForwardingRule_basic2(fr, proxy1, proxy2, backend, hc, urlmap string) string { + return fmt.Sprintf(` + resource "google_compute_global_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "TCP" + name = "%s" + port_range = "80" + target = "${google_compute_target_http_proxy.foobar2.self_link}" + } + + resource "google_compute_target_http_proxy" "foobar1" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar.self_link}" + } + + resource "google_compute_target_http_proxy" "foobar2" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar.self_link}" + } + + resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + } + + resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 + } + + resource "google_compute_url_map" "foobar" { + name = "%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + path_rule { + paths = ["/*"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } + }`, fr, proxy1, proxy2, backend, hc, urlmap) +} + +func testAccComputeGlobalForwardingRule_ipv6(fr, proxy1, proxy2, backend, hc, urlmap string) string { + return fmt.Sprintf(` + resource "google_compute_global_forwarding_rule" "foobar" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "TCP" + name = "%s" + port_range = "80" + target = "${google_compute_target_http_proxy.foobar1.self_link}" + ip_version = "IPV6" + } + + resource "google_compute_target_http_proxy" "foobar1" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar.self_link}" + } + + resource "google_compute_target_http_proxy" "foobar2" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar.self_link}" + } + + resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + } + + resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 + } + + resource "google_compute_url_map" "foobar" { + name = "%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + path_rule { + paths = ["/*"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } + }`, fr, proxy1, proxy2, backend, hc, urlmap) +} diff --git a/provider/terraform/tests/resource_compute_http_health_check_test.go b/provider/terraform/tests/resource_compute_http_health_check_test.go new file mode 100644 index 000000000000..cf12e72b9642 --- /dev/null +++ b/provider/terraform/tests/resource_compute_http_health_check_test.go @@ -0,0 +1,189 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeHttpHealthCheck_basic(t *testing.T) { + t.Parallel() + + var healthCheck compute.HttpHealthCheck + + hhckName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeHttpHealthCheckDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeHttpHealthCheck_basic(hhckName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeHttpHealthCheckExists( + "google_compute_http_health_check.foobar", &healthCheck), + testAccCheckComputeHttpHealthCheckRequestPath( + "/health_check", &healthCheck), + testAccCheckComputeHttpHealthCheckThresholds( + 3, 3, &healthCheck), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_http_health_check.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeHttpHealthCheck_update(t *testing.T) { + t.Parallel() + + var healthCheck compute.HttpHealthCheck + + hhckName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeHttpHealthCheckDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeHttpHealthCheck_update1(hhckName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeHttpHealthCheckExists( + "google_compute_http_health_check.foobar", &healthCheck), + testAccCheckComputeHttpHealthCheckRequestPath( + "/not_default", &healthCheck), + testAccCheckComputeHttpHealthCheckThresholds( + 2, 2, &healthCheck), + ), + }, + resource.TestStep{ + Config: testAccComputeHttpHealthCheck_update2(hhckName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeHttpHealthCheckExists( + "google_compute_http_health_check.foobar", &healthCheck), + testAccCheckComputeHttpHealthCheckRequestPath( + "/", &healthCheck), + testAccCheckComputeHttpHealthCheckThresholds( + 10, 10, &healthCheck), + ), + }, + }, + }) +} + +func testAccCheckComputeHttpHealthCheckDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_http_health_check" { + continue + } + + _, err := config.clientCompute.HttpHealthChecks.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("HttpHealthCheck still exists") + } + } + + return nil +} + +func testAccCheckComputeHttpHealthCheckExists(n string, healthCheck *compute.HttpHealthCheck) 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) + + found, err := config.clientCompute.HttpHealthChecks.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("HttpHealthCheck not found") + } + + *healthCheck = *found + + return nil + } +} + +func testAccCheckComputeHttpHealthCheckRequestPath(path string, healthCheck *compute.HttpHealthCheck) resource.TestCheckFunc { + return func(s *terraform.State) error { + if healthCheck.RequestPath != path { + return fmt.Errorf("RequestPath doesn't match: expected %s, got %s", path, healthCheck.RequestPath) + } + + return nil + } +} + +func testAccCheckComputeHttpHealthCheckThresholds(healthy, unhealthy int64, healthCheck *compute.HttpHealthCheck) resource.TestCheckFunc { + return func(s *terraform.State) error { + if healthCheck.HealthyThreshold != healthy { + return fmt.Errorf("HealthyThreshold doesn't match: expected %d, got %d", healthy, healthCheck.HealthyThreshold) + } + + if healthCheck.UnhealthyThreshold != unhealthy { + return fmt.Errorf("UnhealthyThreshold doesn't match: expected %d, got %d", unhealthy, healthCheck.UnhealthyThreshold) + } + + return nil + } +} + +func testAccComputeHttpHealthCheck_basic(hhckName string) string { + return fmt.Sprintf(` +resource "google_compute_http_health_check" "foobar" { + name = "%s" + check_interval_sec = 3 + description = "Resource created for Terraform acceptance testing" + healthy_threshold = 3 + host = "foobar" + port = "80" + request_path = "/health_check" + timeout_sec = 2 + unhealthy_threshold = 3 +} +`, hhckName) +} + +func testAccComputeHttpHealthCheck_update1(hhckName string) string { + return fmt.Sprintf(` +resource "google_compute_http_health_check" "foobar" { + name = "%s" + description = "Resource created for Terraform acceptance testing" + request_path = "/not_default" +} +`, hhckName) +} + +func testAccComputeHttpHealthCheck_update2(hhckName string) string { + return fmt.Sprintf(` +resource "google_compute_http_health_check" "foobar" { + name = "%s" + description = "Resource updated for Terraform acceptance testing" + healthy_threshold = 10 + unhealthy_threshold = 10 +} +`, hhckName) +} diff --git a/provider/terraform/tests/resource_compute_https_health_check_test.go b/provider/terraform/tests/resource_compute_https_health_check_test.go new file mode 100644 index 000000000000..ee32427ff9cf --- /dev/null +++ b/provider/terraform/tests/resource_compute_https_health_check_test.go @@ -0,0 +1,189 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeHttpsHealthCheck_basic(t *testing.T) { + t.Parallel() + + var healthCheck compute.HttpsHealthCheck + + hhckName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeHttpsHealthCheckDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeHttpsHealthCheck_basic(hhckName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeHttpsHealthCheckExists( + "google_compute_https_health_check.foobar", &healthCheck), + testAccCheckComputeHttpsHealthCheckRequestPath( + "/health_check", &healthCheck), + testAccCheckComputeHttpsHealthCheckThresholds( + 3, 3, &healthCheck), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_https_health_check.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeHttpsHealthCheck_update(t *testing.T) { + t.Parallel() + + var healthCheck compute.HttpsHealthCheck + + hhckName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeHttpsHealthCheckDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeHttpsHealthCheck_update1(hhckName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeHttpsHealthCheckExists( + "google_compute_https_health_check.foobar", &healthCheck), + testAccCheckComputeHttpsHealthCheckRequestPath( + "/not_default", &healthCheck), + testAccCheckComputeHttpsHealthCheckThresholds( + 2, 2, &healthCheck), + ), + }, + resource.TestStep{ + Config: testAccComputeHttpsHealthCheck_update2(hhckName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeHttpsHealthCheckExists( + "google_compute_https_health_check.foobar", &healthCheck), + testAccCheckComputeHttpsHealthCheckRequestPath( + "/", &healthCheck), + testAccCheckComputeHttpsHealthCheckThresholds( + 10, 10, &healthCheck), + ), + }, + }, + }) +} + +func testAccCheckComputeHttpsHealthCheckDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_https_health_check" { + continue + } + + _, err := config.clientCompute.HttpsHealthChecks.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("HttpsHealthCheck still exists") + } + } + + return nil +} + +func testAccCheckComputeHttpsHealthCheckExists(n string, healthCheck *compute.HttpsHealthCheck) 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) + + found, err := config.clientCompute.HttpsHealthChecks.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("HttpsHealthCheck not found") + } + + *healthCheck = *found + + return nil + } +} + +func testAccCheckComputeHttpsHealthCheckRequestPath(path string, healthCheck *compute.HttpsHealthCheck) resource.TestCheckFunc { + return func(s *terraform.State) error { + if healthCheck.RequestPath != path { + return fmt.Errorf("RequestPath doesn't match: expected %s, got %s", path, healthCheck.RequestPath) + } + + return nil + } +} + +func testAccCheckComputeHttpsHealthCheckThresholds(healthy, unhealthy int64, healthCheck *compute.HttpsHealthCheck) resource.TestCheckFunc { + return func(s *terraform.State) error { + if healthCheck.HealthyThreshold != healthy { + return fmt.Errorf("HealthyThreshold doesn't match: expected %d, got %d", healthy, healthCheck.HealthyThreshold) + } + + if healthCheck.UnhealthyThreshold != unhealthy { + return fmt.Errorf("UnhealthyThreshold doesn't match: expected %d, got %d", unhealthy, healthCheck.UnhealthyThreshold) + } + + return nil + } +} + +func testAccComputeHttpsHealthCheck_basic(hhckName string) string { + return fmt.Sprintf(` +resource "google_compute_https_health_check" "foobar" { + check_interval_sec = 3 + description = "Resource created for Terraform acceptance testing" + healthy_threshold = 3 + host = "foobar" + name = "%s" + port = "80" + request_path = "/health_check" + timeout_sec = 2 + unhealthy_threshold = 3 +} +`, hhckName) +} + +func testAccComputeHttpsHealthCheck_update1(hhckName string) string { + return fmt.Sprintf(` +resource "google_compute_https_health_check" "foobar" { + name = "%s" + description = "Resource created for Terraform acceptance testing" + request_path = "/not_default" +} +`, hhckName) +} + +func testAccComputeHttpsHealthCheck_update2(hhckName string) string { + return fmt.Sprintf(` +resource "google_compute_https_health_check" "foobar" { + name = "%s" + description = "Resource updated for Terraform acceptance testing" + healthy_threshold = 10 + unhealthy_threshold = 10 +} +`, hhckName) +} diff --git a/provider/terraform/tests/resource_compute_image_test.go b/provider/terraform/tests/resource_compute_image_test.go new file mode 100644 index 000000000000..66fe34c165e6 --- /dev/null +++ b/provider/terraform/tests/resource_compute_image_test.go @@ -0,0 +1,335 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeImage_basic(t *testing.T) { + t.Parallel() + + var image compute.Image + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeImageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeImage_basic("image-test-" + acctest.RandString(10)), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeImageExists( + "google_compute_image.foobar", &image), + testAccCheckComputeImageDescription(&image, "description-test"), + testAccCheckComputeImageFamily(&image, "family-test"), + testAccCheckComputeImageContainsLabel(&image, "my-label", "my-label-value"), + testAccCheckComputeImageContainsLabel(&image, "empty-label", ""), + testAccCheckComputeImageHasComputedFingerprint(&image, "google_compute_image.foobar"), + ), + }, + }, + }) +} + +func TestAccComputeImage_withLicense(t *testing.T) { + t.Parallel() + + var image compute.Image + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeImageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeImage_license("image-test-" + acctest.RandString(10)), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeImageExists( + "google_compute_image.foobar", &image), + testAccCheckComputeImageDescription(&image, "description-test"), + testAccCheckComputeImageFamily(&image, "family-test"), + testAccCheckComputeImageContainsLabel(&image, "my-label", "my-label-value"), + testAccCheckComputeImageContainsLabel(&image, "empty-label", ""), + testAccCheckComputeImageContainsLicense(&image, "https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx"), + testAccCheckComputeImageHasComputedFingerprint(&image, "google_compute_image.foobar"), + ), + }, + }, + }) +} + +func TestAccComputeImage_update(t *testing.T) { + t.Parallel() + + var image compute.Image + + name := "image-test-" + acctest.RandString(10) + // Only labels supports an update + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeImageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeImage_basic(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeImageExists( + "google_compute_image.foobar", &image), + testAccCheckComputeImageContainsLabel(&image, "my-label", "my-label-value"), + testAccCheckComputeImageContainsLabel(&image, "empty-label", ""), + testAccCheckComputeImageHasComputedFingerprint(&image, "google_compute_image.foobar"), + ), + }, + resource.TestStep{ + Config: testAccComputeImage_update(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeImageExists( + "google_compute_image.foobar", &image), + testAccCheckComputeImageDoesNotContainLabel(&image, "my-label"), + testAccCheckComputeImageContainsLabel(&image, "empty-label", "oh-look-theres-a-label-now"), + testAccCheckComputeImageContainsLabel(&image, "new-field", "only-shows-up-when-updated"), + testAccCheckComputeImageHasComputedFingerprint(&image, "google_compute_image.foobar"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_image.foobar", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"raw_disk"}, + }, + }, + }) +} + +func TestAccComputeImage_basedondisk(t *testing.T) { + t.Parallel() + + var image compute.Image + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeImageDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeImage_basedondisk(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeImageExists( + "google_compute_image.foobar", &image), + testAccCheckComputeImageHasSourceDisk(&image), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_image.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckComputeImageDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_image" { + continue + } + + _, err := config.clientCompute.Images.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Image still exists") + } + } + + return nil +} + +func testAccCheckComputeImageExists(n string, image *compute.Image) 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) + + found, err := config.clientCompute.Images.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Image not found") + } + + *image = *found + + return nil + } +} + +func testAccCheckComputeImageDescription(image *compute.Image, description string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if image.Description != description { + return fmt.Errorf("Wrong image description: expected '%s' got '%s'", description, image.Description) + } + return nil + } +} + +func testAccCheckComputeImageFamily(image *compute.Image, family string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if image.Family != family { + return fmt.Errorf("Wrong image family: expected '%s' got '%s'", family, image.Family) + } + return nil + } +} + +func testAccCheckComputeImageContainsLabel(image *compute.Image, key string, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + v, ok := image.Labels[key] + if !ok { + return fmt.Errorf("Expected label with key '%s' not found", key) + } + if v != value { + return fmt.Errorf("Incorrect label value for key '%s': expected '%s' but found '%s'", key, value, v) + } + return nil + } +} + +func testAccCheckComputeImageContainsLicense(image *compute.Image, expectedLicense string) resource.TestCheckFunc { + return func(s *terraform.State) error { + + for _, thisLicense := range image.Licenses { + if thisLicense == expectedLicense { + return nil + } + } + + return fmt.Errorf("Expected license '%s' was not found", expectedLicense) + } +} + +func testAccCheckComputeImageDoesNotContainLabel(image *compute.Image, key string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if v, ok := image.Labels[key]; ok { + return fmt.Errorf("Expected no label for key '%s' but found one with value '%s'", key, v) + } + + return nil + } +} + +func testAccCheckComputeImageHasComputedFingerprint(image *compute.Image, resource string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // First ensure we actually have a fingerprint + if image.LabelFingerprint == "" { + return fmt.Errorf("No fingerprint set in API read result") + } + + state := s.RootModule().Resources[resource] + if state == nil { + return fmt.Errorf("Unable to find resource named %s in resources", resource) + } + + storedFingerprint := state.Primary.Attributes["label_fingerprint"] + if storedFingerprint != image.LabelFingerprint { + return fmt.Errorf("Stored fingerprint doesn't match fingerprint found on server; stored '%s', server '%s'", + storedFingerprint, image.LabelFingerprint) + } + + return nil + } +} + +func testAccCheckComputeImageHasSourceDisk(image *compute.Image) resource.TestCheckFunc { + return func(s *terraform.State) error { + if image.SourceType == "" { + return fmt.Errorf("No source disk") + } + return nil + } +} + +func testAccComputeImage_basic(name string) string { + return fmt.Sprintf(` +resource "google_compute_image" "foobar" { + name = "%s" + description = "description-test" + family = "family-test" + raw_disk { + source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.tar.gz" + } + labels = { + my-label = "my-label-value" + empty-label = "" + } +}`, name) +} + +func testAccComputeImage_license(name string) string { + return fmt.Sprintf(` +resource "google_compute_image" "foobar" { + name = "%s" + description = "description-test" + family = "family-test" + raw_disk { + source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.tar.gz" + } + labels = { + my-label = "my-label-value" + empty-label = "" + } + licenses = [ + "https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx", + ] +}`, name) +} + +func testAccComputeImage_update(name string) string { + return fmt.Sprintf(` +resource "google_compute_image" "foobar" { + name = "%s" + description = "description-test" + family = "family-test" + raw_disk { + source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.tar.gz" + } + labels = { + empty-label = "oh-look-theres-a-label-now" + new-field = "only-shows-up-when-updated" + } +}`, name) +} + +func testAccComputeImage_basedondisk() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "disk-test-%s" + zone = "us-central1-a" + image = "${data.google_compute_image.my_image.self_link}" +} +resource "google_compute_image" "foobar" { + name = "image-test-%s" + source_disk = "${google_compute_disk.foobar.self_link}" +}`, acctest.RandString(10), acctest.RandString(10)) +} diff --git a/provider/terraform/tests/resource_compute_instance_from_template_test.go b/provider/terraform/tests/resource_compute_instance_from_template_test.go new file mode 100644 index 000000000000..f250150ff0f8 --- /dev/null +++ b/provider/terraform/tests/resource_compute_instance_from_template_test.go @@ -0,0 +1,114 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + compute "google.golang.org/api/compute/v1" +) + +func TestAccComputeInstanceFromTemplate_basic(t *testing.T) { + t.Parallel() + + var instance compute.Instance + instanceName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + templateName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + resourceName := "google_compute_instance_from_template.foobar" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceFromTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceFromTemplate_basic(instanceName, templateName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists(resourceName, &instance), + + // Check that fields were set based on the template + resource.TestCheckResourceAttr(resourceName, "machine_type", "n1-standard-1"), + resource.TestCheckResourceAttr(resourceName, "attached_disk.#", "1"), + ), + }, + }, + }) +} + +func testAccCheckComputeInstanceFromTemplateDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_instance_from_template" { + continue + } + + _, err := config.clientCompute.Instances.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Instance still exists") + } + } + + return nil +} + +func testAccComputeInstanceFromTemplate_basic(instance, template string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + image = "${data.google_compute_image.my_image.self_link}" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + disk_size_gb = 100 + boot = true + } + + disk { + source = "${google_compute_disk.foobar.name}" + auto_delete = false + boot = false + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + can_ip_forward = true +} + +resource "google_compute_instance_from_template" "foobar" { + name = "%s" + zone = "us-central1-a" + + source_instance_template = "${google_compute_instance_template.foobar.self_link}" + + // Overrides + can_ip_forward = false + labels { + my_key = "my_value" + } +} +`, template, template, instance) +} diff --git a/provider/terraform/tests/resource_compute_instance_group_manager_test.go b/provider/terraform/tests/resource_compute_instance_group_manager_test.go new file mode 100644 index 000000000000..ef3d10e93cd5 --- /dev/null +++ b/provider/terraform/tests/resource_compute_instance_group_manager_test.go @@ -0,0 +1,1385 @@ +package google + +import ( + "fmt" + "reflect" + "strconv" + "strings" + "testing" + + computeBeta "google.golang.org/api/compute/v0.beta" + "google.golang.org/api/compute/v1" + + "sort" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccInstanceGroupManager_basic(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + + template := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + target := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm1 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm2 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGroupManager_basic(template, target, igm1, igm2), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerExists( + "google_compute_instance_group_manager.igm-basic", &manager), + testAccCheckInstanceGroupManagerExists( + "google_compute_instance_group_manager.igm-no-tp", &manager), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_group_manager.igm-basic", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + ResourceName: "google_compute_instance_group_manager.igm-no-tp", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccInstanceGroupManager_targetSizeZero(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + + templateName := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igmName := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGroupManager_targetSizeZero(templateName, igmName), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerExists( + "google_compute_instance_group_manager.igm-basic", &manager), + ), + }, + }, + }) + + if manager.TargetSize != 0 { + t.Errorf("Expected target_size to be 0, got %d", manager.TargetSize) + } +} + +func TestAccInstanceGroupManager_update(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + + template1 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + target1 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + target2 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + template2 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGroupManager_update(template1, target1, igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerExists( + "google_compute_instance_group_manager.igm-update", &manager), + testAccCheckInstanceGroupManagerUpdated("google_compute_instance_group_manager.igm-update", 2, []string{target1}, template1), + testAccCheckInstanceGroupManagerNamedPorts( + "google_compute_instance_group_manager.igm-update", + map[string]int64{"customhttp": 8080}, + &manager), + ), + }, + resource.TestStep{ + Config: testAccInstanceGroupManager_update2(template1, target1, target2, template2, igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerExists( + "google_compute_instance_group_manager.igm-update", &manager), + testAccCheckInstanceGroupManagerUpdated( + "google_compute_instance_group_manager.igm-update", 3, + []string{target1, target2}, template2), + testAccCheckInstanceGroupManagerNamedPorts( + "google_compute_instance_group_manager.igm-update", + map[string]int64{"customhttp": 8080, "customhttps": 8443}, + &manager), + ), + }, + }, + }) +} + +func TestAccInstanceGroupManager_updateLifecycle(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + + tag1 := "tag1" + tag2 := "tag2" + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGroupManager_updateLifecycle(tag1, igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerExists( + "google_compute_instance_group_manager.igm-update", &manager), + ), + }, + resource.TestStep{ + Config: testAccInstanceGroupManager_updateLifecycle(tag2, igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerExists( + "google_compute_instance_group_manager.igm-update", &manager), + testAccCheckInstanceGroupManagerTemplateTags( + "google_compute_instance_group_manager.igm-update", []string{tag2}), + ), + }, + }, + }) +} + +func TestAccInstanceGroupManager_updateStrategy(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGroupManager_updateStrategy(igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerExists( + "google_compute_instance_group_manager.igm-update-strategy", &manager), + testAccCheckInstanceGroupManagerUpdateStrategy( + "google_compute_instance_group_manager.igm-update-strategy", "NONE"), + ), + }, + }, + }) +} + +func TestAccInstanceGroupManager_rollingUpdatePolicy(t *testing.T) { + t.Parallel() + + var manager computeBeta.InstanceGroupManager + + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGroupManager_rollingUpdatePolicy(igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerBetaExists( + "google_compute_instance_group_manager.igm-rolling-update-policy", &manager), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "update_strategy", "ROLLING_UPDATE"), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.type", "PROACTIVE"), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.minimal_action", "REPLACE"), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_surge_percent", "50"), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_unavailable_percent", "50"), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.min_ready_sec", "20"), + ), + }, + resource.TestStep{ + Config: testAccInstanceGroupManager_rollingUpdatePolicy2(igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerBetaExists( + "google_compute_instance_group_manager.igm-rolling-update-policy", &manager), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "update_strategy", "ROLLING_UPDATE"), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.type", "PROACTIVE"), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.minimal_action", "REPLACE"), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_surge_fixed", "2"), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_unavailable_fixed", "2"), + resource.TestCheckResourceAttr( + "google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.min_ready_sec", "20"), + testAccCheckInstanceGroupManagerRollingUpdatePolicy( + &manager, "google_compute_instance_group_manager.igm-rolling-update-policy"), + ), + }, + }, + }) +} + +func TestAccInstanceGroupManager_separateRegions(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + + igm1 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm2 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGroupManager_separateRegions(igm1, igm2), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerExists( + "google_compute_instance_group_manager.igm-basic", &manager), + testAccCheckInstanceGroupManagerExists( + "google_compute_instance_group_manager.igm-basic-2", &manager), + ), + }, + }, + }) +} + +func TestAccInstanceGroupManager_versions(t *testing.T) { + t.Parallel() + + var manager computeBeta.InstanceGroupManager + + primaryTemplate := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + canaryTemplate := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGroupManager_versions(primaryTemplate, canaryTemplate, igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerBetaExists("google_compute_instance_group_manager.igm-basic", &manager), + testAccCheckInstanceGroupManagerVersions("google_compute_instance_group_manager.igm-basic", primaryTemplate, canaryTemplate), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_group_manager.igm-basic", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccInstanceGroupManager_autoHealingPolicies(t *testing.T) { + t.Parallel() + + var manager computeBeta.InstanceGroupManager + + template := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + target := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + hck := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGroupManager_autoHealingPolicies(template, target, igm, hck), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceGroupManagerBetaExists( + "google_compute_instance_group_manager.igm-basic", &manager), + testAccCheckInstanceGroupManagerAutoHealingPolicies("google_compute_instance_group_manager.igm-basic", hck, 10), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_group_manager.igm-basic", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// This test is to make sure that a single version resource can link to a versioned resource +// without perpetual diffs because the self links mismatch. +// Once auto_healing_policies is no longer beta, we will need to use a new field or resource +// with Beta fields. +func TestAccInstanceGroupManager_selfLinkStability(t *testing.T) { + t.Parallel() + + var manager computeBeta.InstanceGroupManager + + template := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + target := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + hck := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + autoscaler := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceGroupManager_selfLinkStability(template, target, igm, hck, autoscaler), + Check: testAccCheckInstanceGroupManagerBetaExists( + "google_compute_instance_group_manager.igm-basic", &manager), + }, + }, + }) +} + +func testAccCheckInstanceGroupManagerDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_instance_group_manager" { + continue + } + _, err := config.clientCompute.InstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("InstanceGroupManager still exists") + } + } + + return nil +} + +func testAccCheckInstanceGroupManagerExists(n string, manager *compute.InstanceGroupManager) 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) + + found, err := config.clientCompute.InstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("InstanceGroupManager not found") + } + + *manager = *found + + return nil + } +} + +func testAccCheckInstanceGroupManagerBetaExists(n string, manager *computeBeta.InstanceGroupManager) 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) + + found, err := config.clientComputeBeta.InstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("InstanceGroupManager not found") + } + + *manager = *found + + return nil + } +} + +func testAccCheckInstanceGroupManagerUpdated(n string, size int64, targetPools []string, template string) 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) + + manager, err := config.clientCompute.InstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return err + } + + // Cannot check the target pool as the instance creation is asynchronous. However, can + // check the target_size. + if manager.TargetSize != size { + return fmt.Errorf("instance count incorrect") + } + + tpNames := make([]string, 0, len(manager.TargetPools)) + for _, targetPool := range manager.TargetPools { + tpNames = append(tpNames, GetResourceNameFromSelfLink(targetPool)) + } + + sort.Strings(tpNames) + sort.Strings(targetPools) + if !reflect.DeepEqual(tpNames, targetPools) { + return fmt.Errorf("target pools incorrect. Expected %s, got %s", targetPools, tpNames) + } + + // check that the instance template updated + instanceTemplate, err := config.clientCompute.InstanceTemplates.Get( + config.Project, template).Do() + if err != nil { + return fmt.Errorf("Error reading instance template: %s", err) + } + + if instanceTemplate.Name != template { + return fmt.Errorf("instance template not updated") + } + + return nil + } +} + +func testAccCheckInstanceGroupManagerNamedPorts(n string, np map[string]int64, instanceGroupManager *compute.InstanceGroupManager) 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) + + manager, err := config.clientCompute.InstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return err + } + + var found bool + for _, namedPort := range manager.NamedPorts { + found = false + for name, port := range np { + if namedPort.Name == name && namedPort.Port == port { + found = true + } + } + if !found { + return fmt.Errorf("named port incorrect") + } + } + + return nil + } +} + +func testAccCheckInstanceGroupManagerVersions(n string, primaryTemplate string, canaryTemplate string) 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) + + manager, err := config.clientComputeBeta.InstanceGroupManagers.Get(config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if len(manager.Versions) != 2 { + return fmt.Errorf("Expected # of versions to be 2, got %d", len(manager.Versions)) + } + + primaryVersion := manager.Versions[0] + if !strings.Contains(primaryVersion.InstanceTemplate, primaryTemplate) { + return fmt.Errorf("Expected string \"%s\" to appear in \"%s\"", primaryTemplate, primaryVersion.InstanceTemplate) + } + + canaryVersion := manager.Versions[1] + if !strings.Contains(canaryVersion.InstanceTemplate, canaryTemplate) { + return fmt.Errorf("Expected string \"%s\" to appear in \"%s\"", canaryTemplate, canaryVersion.InstanceTemplate) + } + + return nil + } +} + +func testAccCheckInstanceGroupManagerAutoHealingPolicies(n, hck string, initialDelaySec int64) 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) + + manager, err := config.clientComputeBeta.InstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if len(manager.AutoHealingPolicies) != 1 { + return fmt.Errorf("Expected # of auto healing policies to be 1, got %d", len(manager.AutoHealingPolicies)) + } + autoHealingPolicy := manager.AutoHealingPolicies[0] + + if !strings.Contains(autoHealingPolicy.HealthCheck, hck) { + return fmt.Errorf("Expected string \"%s\" to appear in \"%s\"", hck, autoHealingPolicy.HealthCheck) + } + + if autoHealingPolicy.InitialDelaySec != initialDelaySec { + return fmt.Errorf("Expected auto healing policy inital delay to be %d, got %d", initialDelaySec, autoHealingPolicy.InitialDelaySec) + } + return nil + } +} + +func testAccCheckInstanceGroupManagerTemplateTags(n string, tags []string) 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) + + manager, err := config.clientCompute.InstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return err + } + + // check that the instance template updated + instanceTemplate, err := config.clientCompute.InstanceTemplates.Get( + config.Project, GetResourceNameFromSelfLink(manager.InstanceTemplate)).Do() + if err != nil { + return fmt.Errorf("Error reading instance template: %s", err) + } + + if !reflect.DeepEqual(instanceTemplate.Properties.Tags.Items, tags) { + return fmt.Errorf("instance template not updated") + } + + return nil + } +} + +func testAccCheckInstanceGroupManagerUpdateStrategy(n, strategy string) 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") + } + + if rs.Primary.Attributes["update_strategy"] != strategy { + return fmt.Errorf("Expected strategy to be %s, got %s", + strategy, rs.Primary.Attributes["update_strategy"]) + } + return nil + } +} + +func testAccCheckInstanceGroupManagerRollingUpdatePolicy(manager *computeBeta.InstanceGroupManager, resource string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs := s.RootModule().Resources[resource] + + updatePolicy := manager.UpdatePolicy + + surgeFixed, _ := strconv.ParseInt(rs.Primary.Attributes["rolling_update_policy.0.max_surge_fixed"], 10, 64) + if updatePolicy.MaxSurge.Fixed != surgeFixed { + return fmt.Errorf("Expected update policy MaxSurge to be %d, got %d", surgeFixed, updatePolicy.MaxSurge.Fixed) + } + + surgePercent, _ := strconv.ParseInt(rs.Primary.Attributes["rolling_update_policy.0.max_surge_percent"], 10, 64) + if updatePolicy.MaxSurge.Percent != surgePercent { + return fmt.Errorf("Expected update policy MaxSurge to be %d, got %d", surgePercent, updatePolicy.MaxSurge.Percent) + } + + unavailableFixed, _ := strconv.ParseInt(rs.Primary.Attributes["rolling_update_policy.0.max_unavailable_fixed"], 10, 64) + if updatePolicy.MaxUnavailable.Fixed != unavailableFixed { + return fmt.Errorf("Expected update policy MaxUnavailable to be %d, got %d", unavailableFixed, updatePolicy.MaxUnavailable.Fixed) + } + + unavailablePercent, _ := strconv.ParseInt(rs.Primary.Attributes["rolling_update_policy.0.max_unavailable_percent"], 10, 64) + if updatePolicy.MaxUnavailable.Percent != unavailablePercent { + return fmt.Errorf("Expected update policy MaxUnavailable to be %d, got %d", unavailablePercent, updatePolicy.MaxUnavailable.Percent) + } + + policyType := rs.Primary.Attributes["rolling_update_policy.0.type"] + if updatePolicy.Type != policyType { + return fmt.Errorf("Expected update policy Type to be \"%s\", got \"%s\"", policyType, updatePolicy.Type) + } + + policyAction := rs.Primary.Attributes["rolling_update_policy.0.minimal_action"] + if updatePolicy.MinimalAction != policyAction { + return fmt.Errorf("Expected update policy MinimalAction to be \"%s\", got \"%s\"", policyAction, updatePolicy.MinimalAction) + } + + minReadySec, _ := strconv.ParseInt(rs.Primary.Attributes["rolling_update_policy.0.min_ready_sec"], 10, 64) + if updatePolicy.MinReadySec != minReadySec { + return fmt.Errorf("Expected update policy MinReadySec to be %d, got %d", minReadySec, updatePolicy.MinReadySec) + } + return nil + } +} + +func testAccInstanceGroupManager_basic(template, target, igm1, igm2 string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_target_pool" "igm-basic" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" + } + + resource "google_compute_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] + base_instance_name = "igm-basic" + zone = "us-central1-c" + target_size = 2 + } + + resource "google_compute_instance_group_manager" "igm-no-tp" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm-no-tp" + zone = "us-central1-c" + target_size = 2 + } + `, template, target, igm1, igm2) +} + +func testAccInstanceGroupManager_targetSizeZero(template, igm string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm-basic" + zone = "us-central1-c" + } + `, template, igm) +} + +func testAccInstanceGroupManager_update(template, target, igm string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-update" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_target_pool" "igm-update" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" + } + + resource "google_compute_instance_group_manager" "igm-update" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-update.self_link}" + target_pools = ["${google_compute_target_pool.igm-update.self_link}"] + base_instance_name = "igm-update" + zone = "us-central1-c" + target_size = 2 + named_port { + name = "customhttp" + port = 8080 + } + }`, template, target, igm) +} + +// Change IGM's instance template and target size +func testAccInstanceGroupManager_update2(template1, target1, target2, template2, igm string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-update" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_target_pool" "igm-update" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" + } + + resource "google_compute_target_pool" "igm-update2" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" + } + + resource "google_compute_instance_template" "igm-update2" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_instance_group_manager" "igm-update" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-update2.self_link}" + target_pools = [ + "${google_compute_target_pool.igm-update.self_link}", + "${google_compute_target_pool.igm-update2.self_link}", + ] + base_instance_name = "igm-update" + zone = "us-central1-c" + target_size = 3 + named_port { + name = "customhttp" + port = 8080 + } + named_port { + name = "customhttps" + port = 8443 + } + }`, template1, target1, target2, template2, igm) +} + +func testAccInstanceGroupManager_updateLifecycle(tag, igm string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-update" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["%s"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + + lifecycle { + create_before_destroy = true + } + } + + resource "google_compute_instance_group_manager" "igm-update" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-update.self_link}" + base_instance_name = "igm-update" + zone = "us-central1-c" + target_size = 2 + named_port { + name = "customhttp" + port = 8080 + } + }`, tag, igm) +} + +func testAccInstanceGroupManager_updateStrategy(igm string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-update-strategy" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["terraform-testing"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + + lifecycle { + create_before_destroy = true + } + } + + resource "google_compute_instance_group_manager" "igm-update-strategy" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-update-strategy.self_link}" + base_instance_name = "igm-update-strategy" + zone = "us-central1-c" + target_size = 2 + update_strategy = "NONE" + named_port { + name = "customhttp" + port = 8080 + } + }`, igm) +} + +func testAccInstanceGroupManager_rollingUpdatePolicy(igm string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-rolling-update-policy" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["terraform-testing"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + + lifecycle { + create_before_destroy = true + } +} + +resource "google_compute_instance_group_manager" "igm-rolling-update-policy" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" + base_instance_name = "igm-rolling-update-policy" + zone = "us-central1-c" + target_size = 3 + update_strategy = "ROLLING_UPDATE" + rolling_update_policy { + type = "PROACTIVE" + minimal_action = "REPLACE" + max_surge_percent = 50 + max_unavailable_percent = 50 + min_ready_sec = 20 + } + named_port { + name = "customhttp" + port = 8080 + } +}`, igm) +} + +func testAccInstanceGroupManager_rollingUpdatePolicy2(igm string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-rolling-update-policy" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["terraform-testing"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + lifecycle { + create_before_destroy = true + } +} + +resource "google_compute_instance_group_manager" "igm-rolling-update-policy" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" + base_instance_name = "igm-rolling-update-policy" + zone = "us-central1-c" + target_size = 3 + update_strategy = "ROLLING_UPDATE" + rolling_update_policy { + type = "PROACTIVE" + minimal_action = "REPLACE" + max_surge_fixed = 2 + max_unavailable_fixed = 2 + min_ready_sec = 20 + } + named_port { + name = "customhttp" + port = 8080 + } +}`, igm) +} + +func testAccInstanceGroupManager_separateRegions(igm1, igm2 string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-basic" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm-basic" + zone = "us-central1-c" + target_size = 2 + } + + resource "google_compute_instance_group_manager" "igm-basic-2" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm-basic-2" + zone = "us-west1-b" + target_size = 2 + } + `, igm1, igm2) +} + +func testAccInstanceGroupManager_autoHealingPolicies(template, target, igm, hck string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + metadata { + foo = "bar" + } + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_target_pool" "igm-basic" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" +} + +resource "google_compute_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] + base_instance_name = "igm-basic" + zone = "us-central1-c" + target_size = 2 + auto_healing_policies { + health_check = "${google_compute_http_health_check.zero.self_link}" + initial_delay_sec = "10" + } +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + `, template, target, igm, hck) +} + +func testAccInstanceGroupManager_versions(primaryTemplate string, canaryTemplate string, igm string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-primary" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + metadata { + foo = "bar" + } + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_instance_template" "igm-canary" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + metadata { + foo = "bar" + } + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + base_instance_name = "igm-basic" + zone = "us-central1-c" + target_size = 2 + + version { + name = "primary" + instance_template = "${google_compute_instance_template.igm-primary.self_link}" + } + + version { + name = "canary" + instance_template = "${google_compute_instance_template.igm-canary.self_link}" + target_size { + fixed = 1 + } + } +} + `, primaryTemplate, canaryTemplate, igm) +} + +// This test is to make sure that a single version resource can link to a versioned resource +// without perpetual diffs because the self links mismatch. +// Once auto_healing_policies is no longer beta, we will need to use a new field or resource +// with Beta fields. +func testAccInstanceGroupManager_selfLinkStability(template, target, igm, hck, autoscaler string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + metadata { + foo = "bar" + } + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_target_pool" "igm-basic" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" +} + +resource "google_compute_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] + base_instance_name = "igm-basic" + zone = "us-central1-c" + target_size = 2 + auto_healing_policies { + health_check = "${google_compute_http_health_check.zero.self_link}" + initial_delay_sec = "10" + } +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_autoscaler" "foobar" { + name = "%s" + zone = "us-central1-c" + target = "${google_compute_instance_group_manager.igm-basic.self_link}" + autoscaling_policy = { + max_replicas = 10 + min_replicas = 1 + cooldown_period = 60 + cpu_utilization = { + target = 0.5 + } + } +} +`, template, target, igm, hck, autoscaler) +} diff --git a/provider/terraform/tests/resource_compute_instance_group_migrate_test.go b/provider/terraform/tests/resource_compute_instance_group_migrate_test.go new file mode 100644 index 000000000000..1e6b6469b2bb --- /dev/null +++ b/provider/terraform/tests/resource_compute_instance_group_migrate_test.go @@ -0,0 +1,111 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform/terraform" +) + +func TestComputeInstanceGroupMigrateState(t *testing.T) { + cases := map[string]struct { + StateVersion int + Attributes map[string]string + ExpectedAttributes map[string]string + ExpectedId string + Meta interface{} + }{ + "v1 to v2": { + StateVersion: 1, + Attributes: map[string]string{ + "zone": "us-central1-c", + "name": "instancegroup-test", + }, + ExpectedAttributes: map[string]string{ + "zone": "us-central1-c", + "name": "instancegroup-test", + }, + ExpectedId: "us-central1-c/instancegroup-test", + Meta: &Config{}, + }, + "v0 to v2": { + StateVersion: 0, + Attributes: map[string]string{ + "zone": "us-central1-c", + "name": "instancegroup-test", + "instances.#": "1", + "instances.0": "https://www.googleapis.com/compute/v1/projects/project_name/zones/zone_name/instances/instancegroup-test-1", + "instances.1": "https://www.googleapis.com/compute/v1/projects/project_name/zones/zone_name/instances/instancegroup-test-0", + }, + ExpectedAttributes: map[string]string{ + "zone": "us-central1-c", + "name": "instancegroup-test", + "instances.#": "1", + "instances.764135222": "https://www.googleapis.com/compute/v1/projects/project_name/zones/zone_name/instances/instancegroup-test-1", + "instances.1519187872": "https://www.googleapis.com/compute/v1/projects/project_name/zones/zone_name/instances/instancegroup-test-0", + }, + ExpectedId: "us-central1-c/instancegroup-test", + Meta: &Config{}, + }, + } + + for tn, tc := range cases { + is := &terraform.InstanceState{ + ID: "i-abc123", + Attributes: tc.Attributes, + } + is, err := resourceComputeInstanceGroupMigrateState( + tc.StateVersion, is, tc.Meta) + + if err != nil { + t.Fatalf("bad: %s, err: %#v", tn, err) + } + + if is.ID != tc.ExpectedId { + t.Fatalf("bad: %s\n\n expected: %s\n got: %s", tn, tc.ExpectedId, is.ID) + } + + for k, v := range tc.ExpectedAttributes { + if is.Attributes[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + tn, k, v, k, is.Attributes[k], is.Attributes) + } + } + } +} + +func TestComputeInstanceGroupMigrateState_empty(t *testing.T) { + cases := map[string]struct { + StateVersion int + }{ + "v0": { + StateVersion: 0, + }, + "v1": { + StateVersion: 1, + }, + } + + for tn, tc := range cases { + var is *terraform.InstanceState + var meta *Config + + // should handle nil + is, err := resourceComputeInstanceGroupMigrateState(tc.StateVersion, is, meta) + + if err != nil { + t.Fatalf("bad %s, err: %#v", tn, err) + } + if is != nil { + t.Fatalf("bad %s, expected nil instancestate, got: %#v", tn, is) + } + + // should handle non-nil but empty + is = &terraform.InstanceState{} + is, err = resourceComputeInstanceGroupMigrateState(tc.StateVersion, is, meta) + + if err != nil { + t.Fatalf("bad %s, err: %#v", tn, err) + } + } +} diff --git a/provider/terraform/tests/resource_compute_instance_group_test.go b/provider/terraform/tests/resource_compute_instance_group_test.go new file mode 100644 index 000000000000..ab14a3a01346 --- /dev/null +++ b/provider/terraform/tests/resource_compute_instance_group_test.go @@ -0,0 +1,588 @@ +package google + +import ( + "fmt" + "testing" + + "google.golang.org/api/compute/v1" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeInstanceGroup_basic(t *testing.T) { + t.Parallel() + + var instanceGroup compute.InstanceGroup + var instanceName = fmt.Sprintf("instancegroup-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccComputeInstanceGroup_destroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstanceGroup_basic(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccComputeInstanceGroup_exists( + "google_compute_instance_group.basic", &instanceGroup), + testAccComputeInstanceGroup_exists( + "google_compute_instance_group.empty", &instanceGroup), + ), + }, + { + ResourceName: "google_compute_instance_group.basic", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceGroup_recreatedInstances(t *testing.T) { + t.Parallel() + + var instanceGroup compute.InstanceGroup + var instanceName = fmt.Sprintf("instancegroup-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccComputeInstanceGroup_destroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstanceGroup_update(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccComputeInstanceGroup_exists( + "google_compute_instance_group.update", &instanceGroup), + ), + }, + { + Config: testAccComputeInstanceGroup_recreateInstances(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccComputeInstanceGroup_exists( + "google_compute_instance_group.update", &instanceGroup), + testAccComputeInstanceGroup_updated( + "google_compute_instance_group.update", 2, &instanceGroup), + ), + }, + }, + }) +} + +func TestAccComputeInstanceGroup_update(t *testing.T) { + t.Parallel() + + var instanceGroup compute.InstanceGroup + var instanceName = fmt.Sprintf("instancegroup-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccComputeInstanceGroup_destroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstanceGroup_update(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccComputeInstanceGroup_exists( + "google_compute_instance_group.update", &instanceGroup), + testAccComputeInstanceGroup_named_ports( + "google_compute_instance_group.update", + map[string]int64{"http": 8080, "https": 8443}, + &instanceGroup), + ), + }, + { + Config: testAccComputeInstanceGroup_update2(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccComputeInstanceGroup_exists( + "google_compute_instance_group.update", &instanceGroup), + testAccComputeInstanceGroup_updated( + "google_compute_instance_group.update", 1, &instanceGroup), + testAccComputeInstanceGroup_named_ports( + "google_compute_instance_group.update", + map[string]int64{"http": 8081, "test": 8444}, + &instanceGroup), + ), + }, + }, + }) +} + +func TestAccComputeInstanceGroup_outOfOrderInstances(t *testing.T) { + t.Parallel() + + var instanceGroup compute.InstanceGroup + var instanceName = fmt.Sprintf("instancegroup-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccComputeInstanceGroup_destroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstanceGroup_outOfOrderInstances(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccComputeInstanceGroup_exists( + "google_compute_instance_group.group", &instanceGroup), + ), + }, + }, + }) +} + +func TestAccComputeInstanceGroup_network(t *testing.T) { + t.Parallel() + + var instanceGroup compute.InstanceGroup + var instanceName = fmt.Sprintf("instancegroup-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccComputeInstanceGroup_destroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceGroup_network(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccComputeInstanceGroup_exists( + "google_compute_instance_group.with_instance", &instanceGroup), + testAccComputeInstanceGroup_hasCorrectNetwork( + "google_compute_instance_group.with_instance", "google_compute_network.ig_network", &instanceGroup), + testAccComputeInstanceGroup_exists( + "google_compute_instance_group.without_instance", &instanceGroup), + testAccComputeInstanceGroup_hasCorrectNetwork( + "google_compute_instance_group.without_instance", "google_compute_network.ig_network", &instanceGroup), + ), + }, + }, + }) +} + +func testAccComputeInstanceGroup_destroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_instance_group" { + continue + } + _, err := config.clientCompute.InstanceGroups.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.Attributes["name"]).Do() + if err == nil { + return fmt.Errorf("InstanceGroup still exists") + } + } + + return nil +} + +func testAccComputeInstanceGroup_exists(n string, instanceGroup *compute.InstanceGroup) 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) + + found, err := config.clientCompute.InstanceGroups.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.Attributes["name"]).Do() + if err != nil { + return err + } + + *instanceGroup = *found + + return nil + } +} + +func testAccComputeInstanceGroup_updated(n string, size int64, instanceGroup *compute.InstanceGroup) 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) + + instanceGroup, err := config.clientCompute.InstanceGroups.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.Attributes["name"]).Do() + if err != nil { + return err + } + + // Cannot check the target pool as the instance creation is asynchronous. However, can + // check the target_size. + if instanceGroup.Size != size { + return fmt.Errorf("instance count incorrect") + } + + return nil + } +} + +func testAccComputeInstanceGroup_named_ports(n string, np map[string]int64, instanceGroup *compute.InstanceGroup) 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) + + instanceGroup, err := config.clientCompute.InstanceGroups.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.Attributes["name"]).Do() + if err != nil { + return err + } + + var found bool + for _, namedPort := range instanceGroup.NamedPorts { + found = false + for name, port := range np { + if namedPort.Name == name && namedPort.Port == port { + found = true + } + } + if !found { + return fmt.Errorf("named port incorrect") + } + } + + return nil + } +} + +func testAccComputeInstanceGroup_hasCorrectNetwork(nInstanceGroup string, nNetwork string, instanceGroup *compute.InstanceGroup) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + rsInstanceGroup, ok := s.RootModule().Resources[nInstanceGroup] + if !ok { + return fmt.Errorf("Not found: %s", nInstanceGroup) + } + if rsInstanceGroup.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + instanceGroup, err := config.clientCompute.InstanceGroups.Get( + config.Project, rsInstanceGroup.Primary.Attributes["zone"], rsInstanceGroup.Primary.Attributes["name"]).Do() + if err != nil { + return err + } + + rsNetwork, ok := s.RootModule().Resources[nNetwork] + if !ok { + return fmt.Errorf("Not found: %s", nNetwork) + } + if rsNetwork.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + network, err := config.clientCompute.Networks.Get( + config.Project, rsNetwork.Primary.ID).Do() + if err != nil { + return err + } + + if instanceGroup.Network != network.SelfLink { + return fmt.Errorf("network incorrect: actual=%s vs expected=%s", instanceGroup.Network, network.SelfLink) + } + + return nil + } +} + +func testAccComputeInstanceGroup_basic(instance string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance" "ig_instance" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + zone = "us-central1-c" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + } + + resource "google_compute_instance_group" "basic" { + description = "Terraform test instance group" + name = "%s" + zone = "us-central1-c" + instances = [ "${google_compute_instance.ig_instance.self_link}" ] + named_port { + name = "http" + port = "8080" + } + named_port { + name = "https" + port = "8443" + } + } + + resource "google_compute_instance_group" "empty" { + description = "Terraform test instance group empty" + name = "%s-empty" + zone = "us-central1-c" + named_port { + name = "http" + port = "8080" + } + named_port { + name = "https" + port = "8443" + } + }`, instance, instance, instance) +} + +func testAccComputeInstanceGroup_update(instance string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance" "ig_instance" { + name = "%s-${count.index}" + machine_type = "n1-standard-1" + can_ip_forward = false + zone = "us-central1-c" + count = 2 + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + } + + resource "google_compute_instance_group" "update" { + description = "Terraform test instance group" + name = "%s" + zone = "us-central1-c" + instances = [ "${google_compute_instance.ig_instance.*.self_link}" ] + named_port { + name = "http" + port = "8080" + } + named_port { + name = "https" + port = "8443" + } + }`, instance, instance) +} + +// Change IGM's instance template and target size +func testAccComputeInstanceGroup_update2(instance string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance" "ig_instance" { + name = "%s-${count.index}" + machine_type = "n1-standard-1" + can_ip_forward = false + zone = "us-central1-c" + count = 1 + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + } + + resource "google_compute_instance_group" "update" { + description = "Terraform test instance group" + name = "%s" + zone = "us-central1-c" + instances = [ "${google_compute_instance.ig_instance.*.self_link}" ] + + named_port { + name = "http" + port = "8081" + } + named_port { + name = "test" + port = "8444" + } + }`, instance, instance) +} + +func testAccComputeInstanceGroup_recreateInstances(instance string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance" "ig_instance" { + name = "%s-${count.index}" + machine_type = "n1-standard-1" + can_ip_forward = false + zone = "us-central1-c" + count = 2 + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + metadata_startup_script = "echo 'foo'" + + network_interface { + network = "default" + } + } + + resource "google_compute_instance_group" "update" { + description = "Terraform test instance group" + name = "%s" + zone = "us-central1-c" + instances = [ "${google_compute_instance.ig_instance.*.self_link}" ] + named_port { + name = "http" + port = "8080" + } + named_port { + name = "https" + port = "8443" + } + }`, instance, instance) +} + +func testAccComputeInstanceGroup_outOfOrderInstances(instance string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance" "ig_instance" { + name = "%s-1" + machine_type = "n1-standard-1" + can_ip_forward = false + zone = "us-central1-c" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + } + + resource "google_compute_instance" "ig_instance_2" { + name = "%s-2" + machine_type = "n1-standard-1" + can_ip_forward = false + zone = "us-central1-c" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + } + + resource "google_compute_instance_group" "group" { + description = "Terraform test instance group" + name = "%s" + zone = "us-central1-c" + instances = [ "${google_compute_instance.ig_instance_2.self_link}", "${google_compute_instance.ig_instance.self_link}" ] + named_port { + name = "http" + port = "8080" + } + named_port { + name = "https" + port = "8443" + } + }`, instance, instance, instance) +} + +func testAccComputeInstanceGroup_network(instance string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_network" "ig_network" { + name = "%[1]s" + auto_create_subnetworks = true + } + + resource "google_compute_instance" "ig_instance" { + name = "%[1]s" + machine_type = "n1-standard-1" + can_ip_forward = false + zone = "us-central1-c" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "${google_compute_network.ig_network.name}" + } + } + + resource "google_compute_instance_group" "with_instance" { + description = "Terraform test instance group" + name = "%[1]s-with-instance" + zone = "us-central1-c" + instances = [ "${google_compute_instance.ig_instance.self_link}" ] + } + + resource "google_compute_instance_group" "without_instance" { + description = "Terraform test instance group" + name = "%[1]s-without-instance" + zone = "us-central1-c" + network = "${google_compute_network.ig_network.self_link}" + }`, instance) +} diff --git a/provider/terraform/tests/resource_compute_instance_migrate_test.go b/provider/terraform/tests/resource_compute_instance_migrate_test.go new file mode 100644 index 000000000000..871e22b0a868 --- /dev/null +++ b/provider/terraform/tests/resource_compute_instance_migrate_test.go @@ -0,0 +1,877 @@ +package google + +import ( + "fmt" + "log" + "os" + "testing" + + "google.golang.org/api/compute/v1" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestComputeInstanceMigrateState(t *testing.T) { + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + cases := map[string]struct { + StateVersion int + Attributes map[string]string + Expected map[string]string + }{ + "v0.4.2 and earlier": { + StateVersion: 0, + Attributes: map[string]string{ + "metadata.#": "2", + "metadata.0.foo": "bar", + "metadata.1.baz": "qux", + "metadata.2.with.dots": "should.work", + }, + Expected: map[string]string{ + "metadata.foo": "bar", + "metadata.baz": "qux", + "metadata.with.dots": "should.work", + }, + }, + "change scope from list to set": { + StateVersion: 1, + Attributes: map[string]string{ + "service_account.#": "1", + "service_account.0.email": "xxxxxx-compute@developer.gserviceaccount.com", + "service_account.0.scopes.#": "4", + "service_account.0.scopes.0": "https://www.googleapis.com/auth/compute", + "service_account.0.scopes.1": "https://www.googleapis.com/auth/datastore", + "service_account.0.scopes.2": "https://www.googleapis.com/auth/devstorage.full_control", + "service_account.0.scopes.3": "https://www.googleapis.com/auth/logging.write", + }, + Expected: map[string]string{ + "service_account.#": "1", + "service_account.0.email": "xxxxxx-compute@developer.gserviceaccount.com", + "service_account.0.scopes.#": "4", + "service_account.0.scopes.1693978638": "https://www.googleapis.com/auth/devstorage.full_control", + "service_account.0.scopes.172152165": "https://www.googleapis.com/auth/logging.write", + "service_account.0.scopes.299962681": "https://www.googleapis.com/auth/compute", + "service_account.0.scopes.3435931483": "https://www.googleapis.com/auth/datastore", + }, + }, + "add new create_timeout attribute": { + StateVersion: 2, + Attributes: map[string]string{}, + Expected: map[string]string{ + "create_timeout": "4", + }, + }, + "remove empty initialize_params": { + StateVersion: 5, + Attributes: map[string]string{ + "boot_disk.0.initialize_params.#": "1", + "boot_disk.0.initialize_params.0.size": "0", + }, + Expected: map[string]string{ + "boot_disk.0.initialize_params.#": "0", + }, + }, + } + + config := getInitializedConfig(t) + for tn, tc := range cases { + runInstanceMigrateTest(t, "i-abc123", tn, tc.StateVersion, tc.Attributes, tc.Expected, config) + } +} + +func TestComputeInstanceMigrateState_empty(t *testing.T) { + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + var is *terraform.InstanceState + var meta interface{} + + // should handle nil + is, err := resourceComputeInstanceMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } + if is != nil { + t.Fatalf("expected nil instancestate, got: %#v", is) + } + + // should handle non-nil but empty + is = &terraform.InstanceState{} + is, err = resourceComputeInstanceMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } +} + +func TestAccComputeInstanceMigrateState_bootDisk(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + config := getInitializedConfig(t) + zone := "us-central1-f" + + // Seed test data + instanceName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + instance := &compute.Instance{ + Name: instanceName, + Disks: []*compute.AttachedDisk{ + { + Boot: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + }, + MachineType: "zones/" + zone + "/machineTypes/n1-standard-1", + NetworkInterfaces: []*compute.NetworkInterface{ + { + Network: "global/networks/default", + }, + }, + } + op, err := config.clientCompute.Instances.Insert(config.Project, zone, instance).Do() + + if err != nil { + t.Fatalf("Error creating instance: %s", err) + } + waitErr := computeSharedOperationWait(config.clientCompute, op, config.Project, "instance to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpInstance(config, instanceName, zone) + + attributes := map[string]string{ + "disk.#": "1", + "disk.0.disk": "disk-1", + "disk.0.type": "pd-ssd", + "disk.0.auto_delete": "false", + "disk.0.size": "12", + "disk.0.device_name": "persistent-disk-0", + "disk.0.disk_encryption_key_raw": "encrypt-key", + "disk.0.disk_encryption_key_sha256": "encrypt-key-sha", + "zone": zone, + } + expected := map[string]string{ + "boot_disk.#": "1", + "boot_disk.0.auto_delete": "false", + "boot_disk.0.device_name": "persistent-disk-0", + "boot_disk.0.disk_encryption_key_raw": "encrypt-key", + "boot_disk.0.disk_encryption_key_sha256": "encrypt-key-sha", + "boot_disk.0.initialize_params.#": "1", + "boot_disk.0.initialize_params.0.size": "12", + "boot_disk.0.initialize_params.0.type": "pd-ssd", + "boot_disk.0.source": instanceName, + "zone": zone, + "create_timeout": "4", + } + + runInstanceMigrateTest(t, instanceName, "migrate disk to boot disk", 2 /* state version */, attributes, expected, config) +} + +func TestAccComputeInstanceMigrateState_v4FixBootDisk(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + config := getInitializedConfig(t) + zone := "us-central1-f" + + // Seed test data + instanceName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + instance := &compute.Instance{ + Name: instanceName, + Disks: []*compute.AttachedDisk{ + { + Boot: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + }, + MachineType: "zones/" + zone + "/machineTypes/n1-standard-1", + NetworkInterfaces: []*compute.NetworkInterface{ + { + Network: "global/networks/default", + }, + }, + } + op, err := config.clientCompute.Instances.Insert(config.Project, zone, instance).Do() + + if err != nil { + t.Fatalf("Error creating instance: %s", err) + } + waitErr := computeSharedOperationWait(config.clientCompute, op, config.Project, "instance to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpInstance(config, instanceName, zone) + + attributes := map[string]string{ + "disk.#": "1", + "disk.0.disk": "disk-1", + "disk.0.type": "pd-ssd", + "disk.0.auto_delete": "false", + "disk.0.size": "12", + "disk.0.device_name": "persistent-disk-0", + "disk.0.disk_encryption_key_raw": "encrypt-key", + "disk.0.disk_encryption_key_sha256": "encrypt-key-sha", + "zone": zone, + } + expected := map[string]string{ + "boot_disk.#": "1", + "boot_disk.0.auto_delete": "false", + "boot_disk.0.device_name": "persistent-disk-0", + "boot_disk.0.disk_encryption_key_raw": "encrypt-key", + "boot_disk.0.disk_encryption_key_sha256": "encrypt-key-sha", + "boot_disk.0.initialize_params.#": "1", + "boot_disk.0.initialize_params.0.size": "12", + "boot_disk.0.initialize_params.0.type": "pd-ssd", + "boot_disk.0.source": instanceName, + "zone": zone, + } + + runInstanceMigrateTest(t, instanceName, "migrate disk to boot disk", 4 /* state version */, attributes, expected, config) +} + +func TestAccComputeInstanceMigrateState_attachedDiskFromSource(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + config := getInitializedConfig(t) + zone := "us-central1-f" + + // Seed test data + diskName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + disk := &compute.Disk{ + Name: diskName, + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + Zone: zone, + } + op, err := config.clientCompute.Disks.Insert(config.Project, zone, disk).Do() + if err != nil { + t.Fatalf("Error creating disk: %s", err) + } + waitErr := computeSharedOperationWait(config.clientCompute, op, config.Project, "disk to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpDisk(config, diskName, zone) + + instanceName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + instance := &compute.Instance{ + Name: instanceName, + Disks: []*compute.AttachedDisk{ + { + Boot: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + { + Source: "projects/" + config.Project + "/zones/" + zone + "/disks/" + diskName, + }, + }, + MachineType: "zones/" + zone + "/machineTypes/n1-standard-1", + NetworkInterfaces: []*compute.NetworkInterface{ + { + Network: "global/networks/default", + }, + }, + } + op, err = config.clientCompute.Instances.Insert(config.Project, zone, instance).Do() + if err != nil { + t.Fatalf("Error creating instance: %s", err) + } + waitErr = computeSharedOperationWait(config.clientCompute, op, config.Project, "instance to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpInstance(config, instanceName, zone) + + attributes := map[string]string{ + "boot_disk.#": "1", + "disk.#": "1", + "disk.0.disk": diskName, + "disk.0.device_name": "persistent-disk-1", + "disk.0.disk_encryption_key_raw": "encrypt-key", + "disk.0.disk_encryption_key_sha256": "encrypt-key-sha", + "zone": zone, + } + expected := map[string]string{ + "boot_disk.#": "1", + "attached_disk.#": "1", + "attached_disk.0.source": "https://www.googleapis.com/compute/v1/projects/" + config.Project + "/zones/" + zone + "/disks/" + diskName, + "attached_disk.0.device_name": "persistent-disk-1", + "attached_disk.0.disk_encryption_key_raw": "encrypt-key", + "attached_disk.0.disk_encryption_key_sha256": "encrypt-key-sha", + "zone": zone, + "create_timeout": "4", + } + + runInstanceMigrateTest(t, instanceName, "migrate disk to attached disk", 2 /* state version */, attributes, expected, config) +} + +func TestAccComputeInstanceMigrateState_v4FixAttachedDiskFromSource(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + config := getInitializedConfig(t) + zone := "us-central1-f" + + // Seed test data + diskName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + disk := &compute.Disk{ + Name: diskName, + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + Zone: zone, + } + op, err := config.clientCompute.Disks.Insert(config.Project, zone, disk).Do() + if err != nil { + t.Fatalf("Error creating disk: %s", err) + } + waitErr := computeSharedOperationWait(config.clientCompute, op, config.Project, "disk to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpDisk(config, diskName, zone) + + instanceName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + instance := &compute.Instance{ + Name: instanceName, + Disks: []*compute.AttachedDisk{ + { + Boot: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + { + Source: "projects/" + config.Project + "/zones/" + zone + "/disks/" + diskName, + }, + }, + MachineType: "zones/" + zone + "/machineTypes/n1-standard-1", + NetworkInterfaces: []*compute.NetworkInterface{ + { + Network: "global/networks/default", + }, + }, + } + op, err = config.clientCompute.Instances.Insert(config.Project, zone, instance).Do() + if err != nil { + t.Fatalf("Error creating instance: %s", err) + } + waitErr = computeSharedOperationWait(config.clientCompute, op, config.Project, "instance to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpInstance(config, instanceName, zone) + + attributes := map[string]string{ + "boot_disk.#": "1", + "disk.#": "1", + "disk.0.disk": diskName, + "disk.0.device_name": "persistent-disk-1", + "disk.0.disk_encryption_key_raw": "encrypt-key", + "disk.0.disk_encryption_key_sha256": "encrypt-key-sha", + "zone": zone, + } + expected := map[string]string{ + "boot_disk.#": "1", + "attached_disk.#": "1", + "attached_disk.0.source": "https://www.googleapis.com/compute/v1/projects/" + config.Project + "/zones/" + zone + "/disks/" + diskName, + "attached_disk.0.device_name": "persistent-disk-1", + "attached_disk.0.disk_encryption_key_raw": "encrypt-key", + "attached_disk.0.disk_encryption_key_sha256": "encrypt-key-sha", + "zone": zone, + } + + runInstanceMigrateTest(t, instanceName, "migrate disk to attached disk", 4 /* state version */, attributes, expected, config) +} + +func TestAccComputeInstanceMigrateState_attachedDiskFromEncryptionKey(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + config := getInitializedConfig(t) + zone := "us-central1-f" + + instanceName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + instance := &compute.Instance{ + Name: instanceName, + Disks: []*compute.AttachedDisk{ + { + Boot: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + { + AutoDelete: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + DiskEncryptionKey: &compute.CustomerEncryptionKey{ + RawKey: "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=", + }, + }, + }, + MachineType: "zones/" + zone + "/machineTypes/n1-standard-1", + NetworkInterfaces: []*compute.NetworkInterface{ + { + Network: "global/networks/default", + }, + }, + } + op, err := config.clientCompute.Instances.Insert(config.Project, zone, instance).Do() + if err != nil { + t.Fatalf("Error creating instance: %s", err) + } + waitErr := computeSharedOperationWait(config.clientCompute, op, config.Project, "instance to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpInstance(config, instanceName, zone) + + attributes := map[string]string{ + "boot_disk.#": "1", + "disk.#": "1", + "disk.0.image": "projects/debian-cloud/global/images/family/debian-9", + "disk.0.disk_encryption_key_raw": "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=", + "disk.0.disk_encryption_key_sha256": "esTuF7d4eatX4cnc4JsiEiaI+Rff78JgPhA/v1zxX9E=", + "zone": zone, + } + expected := map[string]string{ + "boot_disk.#": "1", + "attached_disk.#": "1", + "attached_disk.0.source": "https://www.googleapis.com/compute/v1/projects/" + config.Project + "/zones/" + zone + "/disks/" + instanceName + "-1", + "attached_disk.0.device_name": "persistent-disk-1", + "attached_disk.0.disk_encryption_key_raw": "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=", + "attached_disk.0.disk_encryption_key_sha256": "esTuF7d4eatX4cnc4JsiEiaI+Rff78JgPhA/v1zxX9E=", + "zone": zone, + "create_timeout": "4", + } + + runInstanceMigrateTest(t, instanceName, "migrate disk to attached disk", 2 /* state version */, attributes, expected, config) +} + +func TestAccComputeInstanceMigrateState_v4FixAttachedDiskFromEncryptionKey(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + config := getInitializedConfig(t) + zone := "us-central1-f" + + instanceName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + instance := &compute.Instance{ + Name: instanceName, + Disks: []*compute.AttachedDisk{ + { + Boot: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + { + AutoDelete: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + DiskEncryptionKey: &compute.CustomerEncryptionKey{ + RawKey: "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=", + }, + }, + }, + MachineType: "zones/" + zone + "/machineTypes/n1-standard-1", + NetworkInterfaces: []*compute.NetworkInterface{ + { + Network: "global/networks/default", + }, + }, + } + op, err := config.clientCompute.Instances.Insert(config.Project, zone, instance).Do() + if err != nil { + t.Fatalf("Error creating instance: %s", err) + } + waitErr := computeSharedOperationWait(config.clientCompute, op, config.Project, "instance to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpInstance(config, instanceName, zone) + + attributes := map[string]string{ + "boot_disk.#": "1", + "disk.#": "1", + "disk.0.image": "projects/debian-cloud/global/images/family/debian-9", + "disk.0.disk_encryption_key_raw": "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=", + "disk.0.disk_encryption_key_sha256": "esTuF7d4eatX4cnc4JsiEiaI+Rff78JgPhA/v1zxX9E=", + "zone": zone, + } + expected := map[string]string{ + "boot_disk.#": "1", + "attached_disk.#": "1", + "attached_disk.0.source": "https://www.googleapis.com/compute/v1/projects/" + config.Project + "/zones/" + zone + "/disks/" + instanceName + "-1", + "attached_disk.0.device_name": "persistent-disk-1", + "attached_disk.0.disk_encryption_key_raw": "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=", + "attached_disk.0.disk_encryption_key_sha256": "esTuF7d4eatX4cnc4JsiEiaI+Rff78JgPhA/v1zxX9E=", + "zone": zone, + } + + runInstanceMigrateTest(t, instanceName, "migrate disk to attached disk", 4 /* state version */, attributes, expected, config) +} + +func TestAccComputeInstanceMigrateState_attachedDiskFromAutoDeleteAndImage(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + config := getInitializedConfig(t) + zone := "us-central1-f" + + instanceName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + instance := &compute.Instance{ + Name: instanceName, + Disks: []*compute.AttachedDisk{ + { + Boot: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + { + AutoDelete: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + { + AutoDelete: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/debian-9-stretch-v20180814", + }, + }, + }, + MachineType: "zones/" + zone + "/machineTypes/n1-standard-1", + NetworkInterfaces: []*compute.NetworkInterface{ + { + Network: "global/networks/default", + }, + }, + } + op, err := config.clientCompute.Instances.Insert(config.Project, zone, instance).Do() + if err != nil { + t.Fatalf("Error creating instance: %s", err) + } + waitErr := computeSharedOperationWait(config.clientCompute, op, config.Project, "instance to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpInstance(config, instanceName, zone) + + attributes := map[string]string{ + "boot_disk.#": "1", + "disk.#": "2", + "disk.0.image": "projects/debian-cloud/global/images/debian-9-stretch-v20180814", + "disk.0.auto_delete": "true", + "disk.1.image": "global/images/family/debian-9", + "disk.1.auto_delete": "true", + "zone": zone, + } + expected := map[string]string{ + "boot_disk.#": "1", + "attached_disk.#": "2", + "attached_disk.0.source": "https://www.googleapis.com/compute/v1/projects/" + config.Project + "/zones/" + zone + "/disks/" + instanceName + "-2", + "attached_disk.0.device_name": "persistent-disk-2", + "attached_disk.1.source": "https://www.googleapis.com/compute/v1/projects/" + config.Project + "/zones/" + zone + "/disks/" + instanceName + "-1", + "attached_disk.1.device_name": "persistent-disk-1", + "zone": zone, + "create_timeout": "4", + } + + runInstanceMigrateTest(t, instanceName, "migrate disk to attached disk", 2 /* state version */, attributes, expected, config) +} + +func TestAccComputeInstanceMigrateState_v4FixAttachedDiskFromAutoDeleteAndImage(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + config := getInitializedConfig(t) + zone := "us-central1-f" + + instanceName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + instance := &compute.Instance{ + Name: instanceName, + Disks: []*compute.AttachedDisk{ + { + Boot: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + { + AutoDelete: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + { + AutoDelete: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/debian-9-stretch-v20180814", + }, + }, + }, + MachineType: "zones/" + zone + "/machineTypes/n1-standard-1", + NetworkInterfaces: []*compute.NetworkInterface{ + { + Network: "global/networks/default", + }, + }, + } + op, err := config.clientCompute.Instances.Insert(config.Project, zone, instance).Do() + if err != nil { + t.Fatalf("Error creating instance: %s", err) + } + waitErr := computeSharedOperationWait(config.clientCompute, op, config.Project, "instance to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpInstance(config, instanceName, zone) + + attributes := map[string]string{ + "boot_disk.#": "1", + "disk.#": "2", + "disk.0.image": "projects/debian-cloud/global/images/debian-9-stretch-v20180814", + "disk.0.auto_delete": "true", + "disk.1.image": "global/images/family/debian-9", + "disk.1.auto_delete": "true", + "zone": zone, + } + expected := map[string]string{ + "boot_disk.#": "1", + "attached_disk.#": "2", + "attached_disk.0.source": "https://www.googleapis.com/compute/v1/projects/" + config.Project + "/zones/" + zone + "/disks/" + instanceName + "-2", + "attached_disk.0.device_name": "persistent-disk-2", + "attached_disk.1.source": "https://www.googleapis.com/compute/v1/projects/" + config.Project + "/zones/" + zone + "/disks/" + instanceName + "-1", + "attached_disk.1.device_name": "persistent-disk-1", + "zone": zone, + } + + runInstanceMigrateTest(t, instanceName, "migrate disk to attached disk", 4 /* state version */, attributes, expected, config) +} + +func TestAccComputeInstanceMigrateState_scratchDisk(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + config := getInitializedConfig(t) + zone := "us-central1-f" + + // Seed test data + instanceName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + instance := &compute.Instance{ + Name: instanceName, + Disks: []*compute.AttachedDisk{ + { + Boot: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + { + AutoDelete: true, + Type: "SCRATCH", + InitializeParams: &compute.AttachedDiskInitializeParams{ + DiskType: "zones/" + zone + "/diskTypes/local-ssd", + }, + }, + }, + MachineType: "zones/" + zone + "/machineTypes/n1-standard-1", + NetworkInterfaces: []*compute.NetworkInterface{ + { + Network: "global/networks/default", + }, + }, + } + op, err := config.clientCompute.Instances.Insert(config.Project, zone, instance).Do() + if err != nil { + t.Fatalf("Error creating instance: %s", err) + } + waitErr := computeSharedOperationWait(config.clientCompute, op, config.Project, "instance to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpInstance(config, instanceName, zone) + + attributes := map[string]string{ + "boot_disk.#": "1", + "disk.#": "1", + "disk.0.auto_delete": "true", + "disk.0.type": "local-ssd", + "disk.0.scratch": "true", + "zone": zone, + } + expected := map[string]string{ + "boot_disk.#": "1", + "scratch_disk.#": "1", + "scratch_disk.0.interface": "SCSI", + "zone": zone, + "create_timeout": "4", + } + + runInstanceMigrateTest(t, instanceName, "migrate disk to scratch disk", 2 /* state version */, attributes, expected, config) +} + +func TestAccComputeInstanceMigrateState_v4FixScratchDisk(t *testing.T) { + t.Parallel() + + if os.Getenv(resource.TestEnvVar) == "" { + t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) + } + config := getInitializedConfig(t) + zone := "us-central1-f" + + // Seed test data + instanceName := fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + instance := &compute.Instance{ + Name: instanceName, + Disks: []*compute.AttachedDisk{ + { + Boot: true, + InitializeParams: &compute.AttachedDiskInitializeParams{ + SourceImage: "projects/debian-cloud/global/images/family/debian-9", + }, + }, + { + AutoDelete: true, + Type: "SCRATCH", + InitializeParams: &compute.AttachedDiskInitializeParams{ + DiskType: "zones/" + zone + "/diskTypes/local-ssd", + }, + }, + }, + MachineType: "zones/" + zone + "/machineTypes/n1-standard-1", + NetworkInterfaces: []*compute.NetworkInterface{ + { + Network: "global/networks/default", + }, + }, + } + op, err := config.clientCompute.Instances.Insert(config.Project, zone, instance).Do() + if err != nil { + t.Fatalf("Error creating instance: %s", err) + } + waitErr := computeSharedOperationWait(config.clientCompute, op, config.Project, "instance to create") + if waitErr != nil { + t.Fatal(waitErr) + } + defer cleanUpInstance(config, instanceName, zone) + + attributes := map[string]string{ + "boot_disk.#": "1", + "disk.#": "1", + "disk.0.auto_delete": "true", + "disk.0.type": "local-ssd", + "disk.0.scratch": "true", + "zone": zone, + } + expected := map[string]string{ + "boot_disk.#": "1", + "scratch_disk.#": "1", + "scratch_disk.0.interface": "SCSI", + "zone": zone, + } + + runInstanceMigrateTest(t, instanceName, "migrate disk to scratch disk", 4 /* state version */, attributes, expected, config) +} + +func runInstanceMigrateTest(t *testing.T, id, testName string, version int, attributes, expected map[string]string, meta interface{}) { + is := &terraform.InstanceState{ + ID: id, + Attributes: attributes, + } + is, err := resourceComputeInstanceMigrateState(version, is, meta) + if err != nil { + t.Fatal(err) + } + + for k, v := range expected { + if attributes[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + testName, k, expected[k], k, attributes[k], attributes) + } + } + + for k, v := range attributes { + if expected[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + testName, k, expected[k], k, attributes[k], attributes) + } + } +} + +func cleanUpInstance(config *Config, instanceName, zone string) { + op, err := config.clientCompute.Instances.Delete(config.Project, zone, instanceName).Do() + if err != nil { + log.Printf("[WARNING] Error deleting instance %q, dangling resources may exist: %s", instanceName, err) + return + } + + // Wait for the operation to complete + opErr := computeOperationWait(config.clientCompute, op, config.Project, "instance to delete") + if opErr != nil { + log.Printf("[WARNING] Error deleting instance %q, dangling resources may exist: %s", instanceName, opErr) + } +} + +func cleanUpDisk(config *Config, diskName, zone string) { + op, err := config.clientCompute.Disks.Delete(config.Project, zone, diskName).Do() + if err != nil { + log.Printf("[WARNING] Error deleting disk %q, dangling resources may exist: %s", diskName, err) + return + } + + // Wait for the operation to complete + opErr := computeOperationWait(config.clientCompute, op, config.Project, "disk to delete") + if opErr != nil { + log.Printf("[WARNING] Error deleting disk %q, dangling resources may exist: %s", diskName, opErr) + } +} + +func getInitializedConfig(t *testing.T) *Config { + // Check that all required environment variables are set + testAccPreCheck(t) + + config := &Config{ + Project: getTestProjectFromEnv(), + Credentials: getTestCredsFromEnv(), + Region: getTestRegionFromEnv(), + } + err := config.loadAndValidate() + if err != nil { + t.Fatal(err) + } + return config +} diff --git a/provider/terraform/tests/resource_compute_instance_template_migrate_test.go b/provider/terraform/tests/resource_compute_instance_template_migrate_test.go new file mode 100644 index 000000000000..cc334b4f6098 --- /dev/null +++ b/provider/terraform/tests/resource_compute_instance_template_migrate_test.go @@ -0,0 +1,137 @@ +package google + +import ( + "testing" + + "reflect" + "strings" + + "github.com/hashicorp/terraform/terraform" +) + +func TestComputeInstanceTemplateMigrateState(t *testing.T) { + cases := map[string]struct { + StateVersion int + BeforeAttributes map[string]string + AfterAttributes map[string]string + ErrorStringExpected string + Meta interface{} + }{ + "invalid state version": { + StateVersion: -1, + ErrorStringExpected: "Unexpected schema version: -1", + }, + "simple automatic_restart case removal": { + StateVersion: 0, + BeforeAttributes: map[string]string{ + "automatic_restart": "true", + "scheduling.#": "1", + "scheduling.0.automatic_restart": "true", + }, + AfterAttributes: map[string]string{ + "scheduling.#": "1", + "scheduling.0.automatic_restart": "true", + }, + }, + "simple on_host_maintenance removal": { + StateVersion: 0, + BeforeAttributes: map[string]string{ + "on_host_maintenance": "MIGRATE", + "automatic_restart": "true", + "scheduling.#": "1", + "scheduling.0.automatic_restart": "true", + }, + AfterAttributes: map[string]string{ + "scheduling.#": "1", + "scheduling.0.automatic_restart": "true", + }, + }, + "missing scheduling block": { + StateVersion: 0, + BeforeAttributes: map[string]string{ + "automatic_restart": "true", + }, + AfterAttributes: map[string]string{ + "scheduling.#": "1", + "scheduling.0.automatic_restart": "true", + }, + }, + "empty scheduling block": { + StateVersion: 0, + BeforeAttributes: map[string]string{ + "automatic_restart": "true", + "scheduling.#": "0", + }, + AfterAttributes: map[string]string{ + "scheduling.#": "1", + "scheduling.0.automatic_restart": "true", + }, + }, + "error upon multiple scheduling block": { + StateVersion: 0, + BeforeAttributes: map[string]string{ + "automatic_restart": "true", + "scheduling.#": "2", + "scheduling.0.automatic_restart": "true", + "scheduling.1.automatic_restart": "true", + }, + ErrorStringExpected: "Found multiple scheduling blocks when there should only be one", + }, + "error upon differing automatic_restart values": { + StateVersion: 0, + BeforeAttributes: map[string]string{ + "automatic_restart": "true", + "scheduling.#": "1", + "scheduling.0.automatic_restart": "false", + }, + ErrorStringExpected: "Found differing values for automatic_restart in state, unsure how to proceed.", + }, + } + + for tn, tc := range cases { + is := &terraform.InstanceState{ + ID: "i-abc123", + Attributes: tc.BeforeAttributes, + } + is, err := resourceComputeInstanceTemplateMigrateState( + tc.StateVersion, is, tc.Meta) + + if err != nil { + if tc.ErrorStringExpected == "" { + t.Fatalf("bad: %s, err: %#v", tn, err) + } else if !strings.Contains(err.Error(), tc.ErrorStringExpected) { + t.Fatalf("Expected error containing string %s, instead found %#v", tc.ErrorStringExpected, err) + } else { + continue + } + } + + // Compare both maps for identity + if !reflect.DeepEqual(is.Attributes, tc.AfterAttributes) { + t.Fatalf("Expected attributes %#v, got attributes %#v", tc.AfterAttributes, is.Attributes) + } + } +} + +func TestComputeInstanceTemplateMigrateState_empty(t *testing.T) { + var is *terraform.InstanceState + var meta interface{} + + // should handle nil + is, err := resourceComputeInstanceTemplateMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } + if is != nil { + t.Fatalf("expected nil instancestate, got: %#v", is) + } + + // should handle non-nil but empty + is = &terraform.InstanceState{} + is, err = resourceComputeInstanceTemplateMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } +} diff --git a/provider/terraform/tests/resource_compute_instance_template_test.go b/provider/terraform/tests/resource_compute_instance_template_test.go new file mode 100644 index 000000000000..f15c36fb4114 --- /dev/null +++ b/provider/terraform/tests/resource_compute_instance_template_test.go @@ -0,0 +1,1392 @@ +package google + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +const DEFAULT_MIN_CPU_TEST_VALUE = "Intel Haswell" + +func TestAccComputeInstanceTemplate_basic(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + "google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateTag(&instanceTemplate, "foo"), + testAccCheckComputeInstanceTemplateMetadata(&instanceTemplate, "foo", "bar"), + testAccCheckComputeInstanceTemplateContainsLabel(&instanceTemplate, "my_label", "foobar"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_imageShorthand(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_imageShorthand(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + "google_compute_instance_template.foobar", &instanceTemplate), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_preemptible(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_preemptible(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + "google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateAutomaticRestart(&instanceTemplate, false), + testAccCheckComputeInstanceTemplatePreemptible(&instanceTemplate, true), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_IP(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_ip(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + "google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateNetwork(&instanceTemplate), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_networkTier(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_networkTier(), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_networkIP(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + networkIP := "10.128.0.2" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_networkIP(networkIP), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + "google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateNetwork(&instanceTemplate), + testAccCheckComputeInstanceTemplateNetworkIP( + "google_compute_instance_template.foobar", networkIP, &instanceTemplate), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +func TestAccComputeInstanceTemplate_networkIPAddress(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + ipAddress := "10.128.0.2" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_networkIPAddress(ipAddress), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + "google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateNetwork(&instanceTemplate), + testAccCheckComputeInstanceTemplateNetworkIPAddress( + "google_compute_instance_template.foobar", ipAddress, &instanceTemplate), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_disks(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_disks(), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_regionDisks(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_regionDisks(), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_subnet_auto(t *testing.T) { + t.Parallel() + + 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), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_subnet_custom(t *testing.T) { + t.Parallel() + + 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), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_subnet_xpn(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + org := getTestOrgFromEnv(t) + billingId := getTestBillingAccountFromEnv(t) + projectName := fmt.Sprintf("tf-xpntest-%d", time.Now().Unix()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_subnet_xpn(org, billingId, projectName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExistsInProject( + "google_compute_instance_template.foobar", fmt.Sprintf("%s-service", projectName), + &instanceTemplate), + testAccCheckComputeInstanceTemplateSubnetwork(&instanceTemplate), + ), + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_metadata_startup_script(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_startup_script(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + "google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateStartupScript(&instanceTemplate, "echo 'Hello'"), + ), + }, + }, + }) +} +func TestAccComputeInstanceTemplate_primaryAliasIpRange(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_primaryAliasIpRange(acctest.RandString(10)), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists("google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateHasAliasIpRange(&instanceTemplate, "", "/24"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_secondaryAliasIpRange(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_secondaryAliasIpRange(acctest.RandString(10)), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists("google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateHasAliasIpRange(&instanceTemplate, "inst-test-secondary", "/24"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeInstanceTemplate_guestAccelerator(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_guestAccelerator(acctest.RandString(10), 1), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists("google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateHasGuestAccelerator(&instanceTemplate, "nvidia-tesla-k80", 1), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + +} + +func TestAccComputeInstanceTemplate_guestAcceleratorSkip(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_guestAccelerator(acctest.RandString(10), 0), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists("google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateLacksGuestAccelerator(&instanceTemplate), + ), + }, + }, + }) + +} + +func TestAccComputeInstanceTemplate_minCpuPlatform(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceTemplateDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstanceTemplate_minCpuPlatform(acctest.RandString(10)), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists("google_compute_instance_template.foobar", &instanceTemplate), + testAccCheckComputeInstanceTemplateHasMinCpuPlatform(&instanceTemplate, DEFAULT_MIN_CPU_TEST_VALUE), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_instance_template.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckComputeInstanceTemplateDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_instance_template" { + continue + } + + _, err := config.clientCompute.InstanceTemplates.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Instance template still exists") + } + } + + return nil +} + +func testAccCheckComputeInstanceTemplateExists(n string, instanceTemplate *compute.InstanceTemplate) resource.TestCheckFunc { + return testAccCheckComputeInstanceTemplateExistsInProject(n, getTestProjectFromEnv(), instanceTemplate) +} + +func testAccCheckComputeInstanceTemplateExistsInProject(n, p string, instanceTemplate *compute.InstanceTemplate) 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) + + found, err := config.clientCompute.InstanceTemplates.Get( + p, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Instance template not found") + } + + *instanceTemplate = *found + + return nil + } +} + +func testAccCheckComputeInstanceTemplateMetadata( + instanceTemplate *compute.InstanceTemplate, + k string, v string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instanceTemplate.Properties.Metadata == nil { + return fmt.Errorf("no metadata") + } + + for _, item := range instanceTemplate.Properties.Metadata.Items { + if k != item.Key { + continue + } + + if item.Value != nil && v == *item.Value { + return nil + } + + return fmt.Errorf("bad value for %s: %s", k, *item.Value) + } + + return fmt.Errorf("metadata not found: %s", k) + } +} + +func testAccCheckComputeInstanceTemplateNetwork(instanceTemplate *compute.InstanceTemplate) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, i := range instanceTemplate.Properties.NetworkInterfaces { + for _, c := range i.AccessConfigs { + if c.NatIP == "" { + return fmt.Errorf("no NAT IP") + } + } + } + + return nil + } +} + +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 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 { + return fmt.Errorf("no tags") + } + + for _, k := range instanceTemplate.Properties.Tags.Items { + if k == n { + return nil + } + } + + return fmt.Errorf("tag not found: %s", n) + } +} + +func testAccCheckComputeInstanceTemplatePreemptible(instanceTemplate *compute.InstanceTemplate, preemptible bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instanceTemplate.Properties.Scheduling.Preemptible != preemptible { + return fmt.Errorf("Expected preemptible value %v, got %v", preemptible, instanceTemplate.Properties.Scheduling.Preemptible) + } + return nil + } +} + +func testAccCheckComputeInstanceTemplateAutomaticRestart(instanceTemplate *compute.InstanceTemplate, automaticRestart bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + ar := instanceTemplate.Properties.Scheduling.AutomaticRestart + if ar == nil { + return fmt.Errorf("Expected to see a value for AutomaticRestart, but got nil") + } + if *ar != automaticRestart { + return fmt.Errorf("Expected automatic restart value %v, got %v", automaticRestart, ar) + } + return nil + } +} + +func testAccCheckComputeInstanceTemplateStartupScript(instanceTemplate *compute.InstanceTemplate, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instanceTemplate.Properties.Metadata == nil && n == "" { + return nil + } else if instanceTemplate.Properties.Metadata == nil && n != "" { + return fmt.Errorf("Expected metadata.startup-script to be '%s', metadata wasn't set at all", n) + } + for _, item := range instanceTemplate.Properties.Metadata.Items { + if item.Key != "startup-script" { + continue + } + if item.Value != nil && *item.Value == n { + return nil + } else if item.Value == nil && n == "" { + return nil + } else if item.Value == nil && n != "" { + return fmt.Errorf("Expected metadata.startup-script to be '%s', wasn't set", n) + } else if *item.Value != n { + return fmt.Errorf("Expected metadata.startup-script to be '%s', got '%s'", n, *item.Value) + } + } + return fmt.Errorf("This should never be reached.") + } +} + +func testAccCheckComputeInstanceTemplateNetworkIP(n, networkIP string, instanceTemplate *compute.InstanceTemplate) resource.TestCheckFunc { + return func(s *terraform.State) error { + ip := instanceTemplate.Properties.NetworkInterfaces[0].NetworkIP + err := resource.TestCheckResourceAttr(n, "network_interface.0.network_ip", ip)(s) + if err != nil { + return err + } + return resource.TestCheckResourceAttr(n, "network_interface.0.network_ip", networkIP)(s) + } +} + +func testAccCheckComputeInstanceTemplateNetworkIPAddress(n, ipAddress string, instanceTemplate *compute.InstanceTemplate) resource.TestCheckFunc { + return func(s *terraform.State) error { + ip := instanceTemplate.Properties.NetworkInterfaces[0].NetworkIP + err := resource.TestCheckResourceAttr(n, "network_interface.0.network_ip", ip)(s) + if err != nil { + return err + } + return resource.TestCheckResourceAttr(n, "network_interface.0.network_ip", ipAddress)(s) + } +} + +func testAccCheckComputeInstanceTemplateContainsLabel(instanceTemplate *compute.InstanceTemplate, key string, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + v, ok := instanceTemplate.Properties.Labels[key] + if !ok { + return fmt.Errorf("Expected label with key '%s' not found", key) + } + if v != value { + return fmt.Errorf("Incorrect label value for key '%s': expected '%s' but found '%s'", key, value, v) + } + return nil + } +} + +func testAccCheckComputeInstanceTemplateHasAliasIpRange(instanceTemplate *compute.InstanceTemplate, subnetworkRangeName, iPCidrRange string) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, networkInterface := range instanceTemplate.Properties.NetworkInterfaces { + for _, aliasIpRange := range networkInterface.AliasIpRanges { + if aliasIpRange.SubnetworkRangeName == subnetworkRangeName && (aliasIpRange.IpCidrRange == iPCidrRange || ipCidrRangeDiffSuppress("ip_cidr_range", aliasIpRange.IpCidrRange, iPCidrRange, nil)) { + return nil + } + } + } + + return fmt.Errorf("Alias ip range with name %s and cidr %s not present", subnetworkRangeName, iPCidrRange) + } +} + +func testAccCheckComputeInstanceTemplateHasGuestAccelerator(instanceTemplate *compute.InstanceTemplate, acceleratorType string, acceleratorCount int64) resource.TestCheckFunc { + return func(s *terraform.State) error { + if len(instanceTemplate.Properties.GuestAccelerators) != 1 { + return fmt.Errorf("Expected only one guest accelerator") + } + + if !strings.HasSuffix(instanceTemplate.Properties.GuestAccelerators[0].AcceleratorType, acceleratorType) { + return fmt.Errorf("Wrong accelerator type: expected %v, got %v", acceleratorType, instanceTemplate.Properties.GuestAccelerators[0].AcceleratorType) + } + + if instanceTemplate.Properties.GuestAccelerators[0].AcceleratorCount != acceleratorCount { + return fmt.Errorf("Wrong accelerator acceleratorCount: expected %d, got %d", acceleratorCount, instanceTemplate.Properties.GuestAccelerators[0].AcceleratorCount) + } + + return nil + } +} + +func testAccCheckComputeInstanceTemplateLacksGuestAccelerator(instanceTemplate *compute.InstanceTemplate) resource.TestCheckFunc { + return func(s *terraform.State) error { + if len(instanceTemplate.Properties.GuestAccelerators) > 0 { + return fmt.Errorf("Expected no guest accelerators") + } + + return nil + } +} + +func testAccCheckComputeInstanceTemplateHasMinCpuPlatform(instanceTemplate *compute.InstanceTemplate, minCpuPlatform string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instanceTemplate.Properties.MinCpuPlatform != minCpuPlatform { + return fmt.Errorf("Wrong minimum CPU platform: expected %s, got %s", minCpuPlatform, instanceTemplate.Properties.MinCpuPlatform) + } + + return nil + } +} + +func testAccComputeInstanceTemplate_basic() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + scheduling { + preemptible = false + automatic_restart = true + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + + labels { + my_label = "foobar" + } +}`, acctest.RandString(10)) +} + +func testAccComputeInstanceTemplate_imageShorthand() string { + return fmt.Sprintf(` +resource "google_compute_image" "foobar" { + name = "test-%s" + description = "description-test" + family = "family-test" + raw_disk { + source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.tar.gz" + } + labels = { + my-label = "my-label-value" + empty-label = "" + } + timeouts { + create = "5m" + } +} + +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${google_compute_image.foobar.name}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + scheduling { + preemptible = false + automatic_restart = true + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + + labels { + my_label = "foobar" + } +}`, acctest.RandString(10), acctest.RandString(10)) +} + +func testAccComputeInstanceTemplate_preemptible() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + scheduling { + preemptible = true + automatic_restart = false + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +}`, acctest.RandString(10)) +} + +func testAccComputeInstanceTemplate_ip() string { + return fmt.Sprintf(` +resource "google_compute_address" "foo" { + name = "instancet-test-%s" +} + +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "n1-standard-1" + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + } + + network_interface { + network = "default" + access_config { + nat_ip = "${google_compute_address.foo.address}" + } + } + + metadata { + foo = "bar" + } +}`, acctest.RandString(10), acctest.RandString(10)) +} + +func testAccComputeInstanceTemplate_networkTier() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "n1-standard-1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + } + + network_interface { + network = "default" + access_config { + network_tier = "STANDARD" + } + } +}`, acctest.RandString(10)) +} + +func testAccComputeInstanceTemplate_networkIP(networkIP string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "n1-standard-1" + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + } + + network_interface { + network = "default" + network_ip = "%s" + } + + metadata { + foo = "bar" + } +}`, acctest.RandString(10), networkIP) +} + +func testAccComputeInstanceTemplate_networkIPAddress(ipAddress string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "n1-standard-1" + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + } + + network_interface { + network = "default" + network_ip = "%s" + } + + metadata { + foo = "bar" + } +}`, acctest.RandString(10), ipAddress) +} + +func testAccComputeInstanceTemplate_disks() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "instancet-test-%s" + image = "${data.google_compute_image.my_image.self_link}" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "n1-standard-1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + disk_size_gb = 100 + boot = true + } + + disk { + source = "${google_compute_disk.foobar.name}" + auto_delete = false + boot = false + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } +}`, acctest.RandString(10), acctest.RandString(10)) +} + +func testAccComputeInstanceTemplate_regionDisks() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_region_disk" "foobar" { + name = "instancet-test-%s" + size = 10 + type = "pd-ssd" + region = "us-central1" + replica_zones = ["us-central1-a", "us-central1-f"] +} + +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "n1-standard-1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + disk_size_gb = 100 + boot = true + } + + disk { + source = "${google_compute_region_disk.foobar.name}" + auto_delete = false + boot = false + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } +}`, acctest.RandString(10), acctest.RandString(10)) +} + +func testAccComputeInstanceTemplate_subnet_auto(network string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + 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 = "${data.google_compute_image.my_image.self_link}" + 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)) +} + +func testAccComputeInstanceTemplate_subnet_custom() string { + return 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}" +} + +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instance-test-%s" + machine_type = "n1-standard-1" + region = "us-central1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + 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)) +} + +func testAccComputeInstanceTemplate_subnet_xpn(org, billingId, projectName string) string { + return fmt.Sprintf(` + resource "google_project" "host_project" { + name = "Test Project XPN Host" + project_id = "%s-host" + org_id = "%s" + billing_account = "%s" + } + + resource "google_project_service" "host_project" { + project = "${google_project.host_project.project_id}" + service = "compute.googleapis.com" + } + + resource "google_compute_shared_vpc_host_project" "host_project" { + project = "${google_project_service.host_project.project}" + } + + resource "google_project" "service_project" { + name = "Test Project XPN Service" + project_id = "%s-service" + org_id = "%s" + billing_account = "%s" + } + + resource "google_project_service" "service_project" { + project = "${google_project.service_project.project_id}" + service = "compute.googleapis.com" + } + + resource "google_compute_shared_vpc_service_project" "service_project" { + host_project = "${google_compute_shared_vpc_host_project.host_project.project}" + service_project = "${google_project_service.service_project.project}" + } + + resource "google_compute_network" "network" { + name = "network-%s" + auto_create_subnetworks = false + project = "${google_compute_shared_vpc_host_project.host_project.project}" + } + + 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}" + project = "${google_compute_shared_vpc_host_project.host_project.project}" + } + + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "foobar" { + name = "instance-test-%s" + machine_type = "n1-standard-1" + region = "us-central1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + disk_size_gb = 10 + boot = true + } + + network_interface { + subnetwork = "${google_compute_subnetwork.subnetwork.name}" + subnetwork_project = "${google_compute_subnetwork.subnetwork.project}" + } + + metadata { + foo = "bar" + } + project = "${google_compute_shared_vpc_service_project.service_project.service_project}" + }`, projectName, org, billingId, projectName, org, billingId, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10)) +} + +func testAccComputeInstanceTemplate_startup_script() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instance-test-%s" + machine_type = "n1-standard-1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + disk_size_gb = 10 + boot = true + } + + metadata { + foo = "bar" + } + + network_interface{ + network = "default" + } + + metadata_startup_script = "echo 'Hello'" +}`, acctest.RandString(10)) +} + +func testAccComputeInstanceTemplate_primaryAliasIpRange(i string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instance-test-%s" + machine_type = "n1-standard-1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + disk_size_gb = 10 + boot = true + } + + metadata { + foo = "bar" + } + + network_interface{ + network = "default" + alias_ip_range { + ip_cidr_range = "/24" + } + } +}`, i) +} + +func testAccComputeInstanceTemplate_secondaryAliasIpRange(i string) string { + return fmt.Sprintf(` +resource "google_compute_network" "inst-test-network" { + name = "inst-test-network-%s" +} +resource "google_compute_subnetwork" "inst-test-subnetwork" { + name = "inst-test-subnetwork-%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-east1" + network = "${google_compute_network.inst-test-network.self_link}" + secondary_ip_range { + range_name = "inst-test-secondary" + ip_cidr_range = "172.16.0.0/20" + } +} +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} +resource "google_compute_instance_template" "foobar" { + name = "instance-test-%s" + machine_type = "n1-standard-1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + disk_size_gb = 10 + boot = true + } + + metadata { + foo = "bar" + } + + network_interface { + subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.self_link}" + + // Note that unlike compute instances, instance templates seem to be + // only able to specify the netmask here. Trying a full CIDR string + // results in: + // Invalid value for field 'resource.properties.networkInterfaces[0].aliasIpRanges[0].ipCidrRange': + // '172.16.0.0/24'. Alias IP CIDR range must be a valid netmask starting with '/' (e.g. '/24') + alias_ip_range { + subnetwork_range_name = "${google_compute_subnetwork.inst-test-subnetwork.secondary_ip_range.0.range_name}" + ip_cidr_range = "/24" + } + } +}`, i, i, i) +} + +func testAccComputeInstanceTemplate_guestAccelerator(i string, count uint8) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instance-test-%s" + machine_type = "n1-standard-1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + disk_size_gb = 10 + boot = true + } + + network_interface { + network = "default" + } + + scheduling { + # Instances with guest accelerators do not support live migration. + on_host_maintenance = "TERMINATE" + } + + guest_accelerator { + count = %d + type = "nvidia-tesla-k80" + } +}`, i, count) +} + +func testAccComputeInstanceTemplate_minCpuPlatform(i string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "instance-test-%s" + machine_type = "n1-standard-1" + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + disk_size_gb = 10 + boot = true + } + + network_interface { + network = "default" + } + + scheduling { + # Instances with guest accelerators do not support live migration. + on_host_maintenance = "TERMINATE" + } + + min_cpu_platform = "%s" +}`, i, DEFAULT_MIN_CPU_TEST_VALUE) +} diff --git a/provider/terraform/tests/resource_compute_instance_test.go b/provider/terraform/tests/resource_compute_instance_test.go new file mode 100644 index 000000000000..34690170f03e --- /dev/null +++ b/provider/terraform/tests/resource_compute_instance_test.go @@ -0,0 +1,3015 @@ +package google + +import ( + "fmt" + "strconv" + "strings" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func computeInstanceImportStep(zone, instanceName string, additionalImportIgnores []string) resource.TestStep { + // metadata is only read into state if set in the config + // since importing doesn't know whether metadata.startup_script vs metadata_startup_script is set in the config, + // it guesses metadata_startup_script + ignores := []string{"metadata.%", "metadata.startup-script", "metadata_startup_script"} + + return resource.TestStep{ + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), zone, instanceName), + ImportStateVerify: true, + ImportStateVerifyIgnore: append(ignores, additionalImportIgnores...), + } +} + +func TestAccComputeInstance_basic1(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_basic(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasInstanceId(&instance, "google_compute_instance.foobar"), + testAccCheckComputeInstanceTag(&instance, "foo"), + testAccCheckComputeInstanceLabel(&instance, "my_key", "my_value"), + testAccCheckComputeInstanceMetadata(&instance, "foo", "bar"), + testAccCheckComputeInstanceMetadata(&instance, "baz", "qux"), + testAccCheckComputeInstanceDisk(&instance, instanceName, true, true), + // by default, DeletionProtection is implicitly false. This should be false on any + // instance resource without an explicit deletion_protection = true declaration. + // Other tests check explicit true/false configs: TestAccComputeInstance_deletionProtectionExplicit[True | False] + testAccCheckComputeInstanceHasConfiguredDeletionProtection(&instance, false), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"metadata.baz", "metadata.foo"}), + }, + }) +} + +func TestAccComputeInstance_basic2(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_basic2(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceTag(&instance, "foo"), + testAccCheckComputeInstanceMetadata(&instance, "foo", "bar"), + testAccCheckComputeInstanceDisk(&instance, instanceName, true, true), + ), + }, + }, + }) +} + +func TestAccComputeInstance_basic3(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_basic3(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceTag(&instance, "foo"), + testAccCheckComputeInstanceMetadata(&instance, "foo", "bar"), + testAccCheckComputeInstanceDisk(&instance, instanceName, true, true), + ), + }, + }, + }) +} + +func TestAccComputeInstance_basic4(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_basic4(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceTag(&instance, "foo"), + testAccCheckComputeInstanceMetadata(&instance, "foo", "bar"), + testAccCheckComputeInstanceDisk(&instance, instanceName, true, true), + ), + }, + }, + }) +} + +func TestAccComputeInstance_basic5(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_basic5(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceTag(&instance, "foo"), + testAccCheckComputeInstanceMetadata(&instance, "foo", "bar"), + testAccCheckComputeInstanceDisk(&instance, instanceName, true, true), + ), + }, + }, + }) +} + +func TestAccComputeInstance_IP(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var ipName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_ip(ipName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceAccessConfigHasNatIP(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_PTRRecord(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var ptrName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var ipName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_PTRRecord(ptrName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceAccessConfigHasPTR(&instance), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"metadata.baz", "metadata.foo"}), + resource.TestStep{ + Config: testAccComputeInstance_ip(ipName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceAccessConfigHasNatIP(&instance), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"metadata.baz", "metadata.foo"}), + }, + }) +} + +func TestAccComputeInstance_networkTier(t *testing.T) { + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_networkTier(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceAccessConfigHasNatIP(&instance), + testAccCheckComputeInstanceHasAssignedNatIP, + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_diskEncryption(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + bootEncryptionKey := "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=" + bootEncryptionKeyHash := "esTuF7d4eatX4cnc4JsiEiaI+Rff78JgPhA/v1zxX9E=" + diskNameToEncryptionKey := map[string]*compute.CustomerEncryptionKey{ + fmt.Sprintf("instance-testd-%s", acctest.RandString(10)): { + RawKey: "Ym9vdDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=", + Sha256: "awJ7p57H+uVZ9axhJjl1D3lfC2MgA/wnt/z88Ltfvss=", + }, + fmt.Sprintf("instance-testd-%s", acctest.RandString(10)): { + RawKey: "c2Vjb25kNzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=", + Sha256: "7TpIwUdtCOJpq2m+3nt8GFgppu6a2Xsj1t0Gexk13Yc=", + }, + fmt.Sprintf("instance-testd-%s", acctest.RandString(10)): { + RawKey: "dGhpcmQ2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=", + Sha256: "b3pvaS7BjDbCKeLPPTx7yXBuQtxyMobCHN1QJR43xeM=", + }, + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_disks_encryption(bootEncryptionKey, diskNameToEncryptionKey, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceDiskEncryptionKey("google_compute_instance.foobar", &instance, bootEncryptionKeyHash, diskNameToEncryptionKey), + ), + }, + }, + }) +} + +func TestAccComputeInstance_attachedDisk(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskName = fmt.Sprintf("instance-testd-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_attachedDisk(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceDisk(&instance, diskName, false, false), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_attachedDisk_sourceUrl(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskName = fmt.Sprintf("instance-testd-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_attachedDisk_sourceUrl(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceDisk(&instance, diskName, false, false), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_attachedDisk_modeRo(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskName = fmt.Sprintf("instance-testd-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_attachedDisk_modeRo(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceDisk(&instance, diskName, false, false), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_attachedDiskUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskName = fmt.Sprintf("instance-testd-%s", acctest.RandString(10)) + var diskName2 = fmt.Sprintf("instance-testd-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_attachedDisk(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceDisk(&instance, diskName, false, false), + ), + }, + // check attaching + resource.TestStep{ + Config: testAccComputeInstance_addAttachedDisk(diskName, diskName2, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceDisk(&instance, diskName, false, false), + testAccCheckComputeInstanceDisk(&instance, diskName2, false, false), + ), + }, + // check detaching + resource.TestStep{ + Config: testAccComputeInstance_detachDisk(diskName, diskName2, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceDisk(&instance, diskName, false, false), + ), + }, + // check updating + resource.TestStep{ + Config: testAccComputeInstance_updateAttachedDiskEncryptionKey(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceDisk(&instance, diskName, false, false), + ), + }, + }, + }) +} + +func TestAccComputeInstance_bootDisk_source(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_bootDisk_source(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceBootDisk(&instance, diskName), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_bootDisk_sourceUrl(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_bootDisk_sourceUrl(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceBootDisk(&instance, diskName), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_bootDisk_type(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskType = "pd-ssd" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_bootDisk_type(instanceName, diskType), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceBootDiskType(instanceName, diskType), + ), + }, + }, + }) +} + +func TestAccComputeInstance_scratchDisk(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_scratchDisk(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.scratch", &instance), + testAccCheckComputeInstanceScratchDisk(&instance, []string{"NVME", "SCSI"}), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_forceNewAndChangeMetadata(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_basic(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + ), + }, + resource.TestStep{ + Config: testAccComputeInstance_forceNewAndChangeMetadata(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceMetadata( + &instance, "qux", "true"), + ), + }, + }, + }) +} + +func TestAccComputeInstance_update(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_basic(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + ), + }, + resource.TestStep{ + Config: testAccComputeInstance_update(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceMetadata( + &instance, "bar", "baz"), + testAccCheckComputeInstanceLabel(&instance, "only_me", "nothing_else"), + testAccCheckComputeInstanceTag(&instance, "baz"), + testAccCheckComputeInstanceAccessConfig(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_stopInstanceToUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + // Set fields that require stopping the instance + resource.TestStep{ + Config: testAccComputeInstance_stopInstanceToUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + // Check that updating them works + resource.TestStep{ + Config: testAccComputeInstance_stopInstanceToUpdate2(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + // Check that removing them works + resource.TestStep{ + Config: testAccComputeInstance_stopInstanceToUpdate3(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + }, + }) +} + +func TestAccComputeInstance_service_account(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_service_account(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceServiceAccount(&instance, + "https://www.googleapis.com/auth/compute.readonly"), + testAccCheckComputeInstanceServiceAccount(&instance, + "https://www.googleapis.com/auth/devstorage.read_only"), + testAccCheckComputeInstanceServiceAccount(&instance, + "https://www.googleapis.com/auth/userinfo.email"), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_scheduling(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_scheduling(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_subnet_auto(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_subnet_auto(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasSubnet(&instance), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_subnet_custom(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_subnet_custom(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasSubnet(&instance), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_subnet_xpn(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + org := getTestOrgFromEnv(t) + billingId := getTestBillingAccountFromEnv(t) + projectName := fmt.Sprintf("tf-xpntest-%d", time.Now().Unix()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_subnet_xpn(org, billingId, projectName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExistsInProject( + "google_compute_instance.foobar", fmt.Sprintf("%s-service", projectName), + &instance), + testAccCheckComputeInstanceHasSubnet(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_networkIPAuto(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_networkIPAuto(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasAnyNetworkIP(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_network_ip_custom(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var ipAddress = "10.0.200.200" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_network_ip_custom(instanceName, ipAddress), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasNetworkIP(&instance, ipAddress), + ), + }, + }, + }) +} + +func TestAccComputeInstance_private_image_family(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskName = fmt.Sprintf("instance-testd-%s", acctest.RandString(10)) + var familyName = fmt.Sprintf("instance-testf-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_private_image_family(diskName, familyName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_forceChangeMachineTypeManually(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_basic(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists("google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceUpdateMachineType("google_compute_instance.foobar"), + ), + ExpectNonEmptyPlan: true, + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"metadata.baz", "metadata.foo"}), + }, + }) +} + +func TestAccComputeInstance_multiNic(t *testing.T) { + t.Parallel() + + var instance compute.Instance + instanceName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + networkName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + subnetworkName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_multiNic(instanceName, networkName, subnetworkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists("google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMultiNic(&instance), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_guestAccelerator(t *testing.T) { + t.Parallel() + + var instance compute.Instance + instanceName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_guestAccelerator(instanceName, 1), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists("google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasGuestAccelerator(&instance, "nvidia-tesla-k80", 1), + ), + }, + computeInstanceImportStep("us-east1-d", instanceName, []string{"metadata.baz", "metadata.foo"}), + }, + }) + +} + +func TestAccComputeInstance_guestAcceleratorSkip(t *testing.T) { + t.Parallel() + + var instance compute.Instance + instanceName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_guestAccelerator(instanceName, 0), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists("google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceLacksGuestAccelerator(&instance), + ), + }, + }, + }) + +} + +func TestAccComputeInstance_minCpuPlatform(t *testing.T) { + t.Parallel() + + var instance compute.Instance + instanceName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_minCpuPlatform(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists("google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMinCpuPlatform(&instance, "Intel Haswell"), + ), + }, + computeInstanceImportStep("us-east1-d", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_deletionProtectionExplicitFalse(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_basic_deletionProtectionFalse(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasConfiguredDeletionProtection(&instance, false), + ), + }, + }, + }) +} + +func TestAccComputeInstance_deletionProtectionExplicitTrueAndUpdateFalse(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_basic_deletionProtectionTrue(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasConfiguredDeletionProtection(&instance, true), + ), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"metadata.foo"}), + // Update deletion_protection to false, otherwise the test harness can't delete the instance + resource.TestStep{ + Config: testAccComputeInstance_basic_deletionProtectionFalse(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasConfiguredDeletionProtection(&instance, false), + ), + }, + }, + }) +} + +func TestAccComputeInstance_primaryAliasIpRange(t *testing.T) { + t.Parallel() + + var instance compute.Instance + instanceName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_primaryAliasIpRange(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists("google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasAliasIpRange(&instance, "", "/24"), + ), + }, + computeInstanceImportStep("us-east1-d", instanceName, []string{}), + }, + }) +} + +func TestAccComputeInstance_secondaryAliasIpRange(t *testing.T) { + t.Parallel() + + var instance compute.Instance + instanceName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + networkName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + subnetName := fmt.Sprintf("terraform-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_secondaryAliasIpRange(networkName, subnetName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists("google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasAliasIpRange(&instance, "inst-test-secondary", "172.16.0.0/24"), + ), + }, + computeInstanceImportStep("us-east1-d", instanceName, []string{}), + resource.TestStep{ + Config: testAccComputeInstance_secondaryAliasIpRangeUpdate(networkName, subnetName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists("google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasAliasIpRange(&instance, "", "10.0.1.0/24"), + ), + }, + computeInstanceImportStep("us-east1-d", instanceName, []string{}), + }, + }) +} + +func testAccCheckComputeInstanceUpdateMachineType(n string) 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) + + op, err := config.clientCompute.Instances.Stop(config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return fmt.Errorf("Could not stop instance: %s", err) + } + err = computeOperationWait(config.clientCompute, op, config.Project, "Waiting on stop") + if err != nil { + return fmt.Errorf("Could not stop instance: %s", err) + } + + machineType := compute.InstancesSetMachineTypeRequest{ + MachineType: "zones/us-central1-a/machineTypes/f1-micro", + } + + op, err = config.clientCompute.Instances.SetMachineType( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID, &machineType).Do() + if err != nil { + return fmt.Errorf("Could not change machine type: %s", err) + } + err = computeOperationWait(config.clientCompute, op, config.Project, "Waiting machine type change") + if err != nil { + return fmt.Errorf("Could not change machine type: %s", err) + } + return nil + } +} + +func testAccCheckComputeInstanceDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_instance" { + continue + } + + _, err := config.clientCompute.Instances.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Instance still exists") + } + } + + return nil +} + +func testAccCheckComputeInstanceExists(n string, instance *compute.Instance) resource.TestCheckFunc { + return testAccCheckComputeInstanceExistsInProject(n, getTestProjectFromEnv(), instance) +} + +func testAccCheckComputeInstanceExistsInProject(n, p string, instance *compute.Instance) 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) + + found, err := config.clientCompute.Instances.Get( + p, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Instance not found") + } + + *instance = *found + + return nil + } +} + +func testAccCheckComputeInstanceMetadata( + instance *compute.Instance, + k string, v string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Metadata == nil { + return fmt.Errorf("no metadata") + } + + for _, item := range instance.Metadata.Items { + if k != item.Key { + continue + } + + if item.Value != nil && v == *item.Value { + return nil + } + + return fmt.Errorf("bad value for %s: %s", k, *item.Value) + } + + return fmt.Errorf("metadata not found: %s", k) + } +} + +func testAccCheckComputeInstanceAccessConfig(instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, i := range instance.NetworkInterfaces { + if len(i.AccessConfigs) == 0 { + return fmt.Errorf("no access_config") + } + } + + return nil + } +} + +func testAccCheckComputeInstanceAccessConfigHasNatIP(instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, i := range instance.NetworkInterfaces { + for _, c := range i.AccessConfigs { + if c.NatIP == "" { + return fmt.Errorf("no NAT IP") + } + } + } + + return nil + } +} + +func testAccCheckComputeInstanceAccessConfigHasPTR(instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, i := range instance.NetworkInterfaces { + for _, c := range i.AccessConfigs { + if c.PublicPtrDomainName == "" { + return fmt.Errorf("no PTR Record") + } + } + } + + return nil + } +} + +func testAccCheckComputeInstanceDisk(instance *compute.Instance, source string, delete bool, boot bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Disks == nil { + return fmt.Errorf("no disks") + } + + for _, disk := range instance.Disks { + if strings.HasSuffix(disk.Source, "/"+source) && disk.AutoDelete == delete && disk.Boot == boot { + return nil + } + } + + return fmt.Errorf("Disk not found: %s", source) + } +} + +func testAccCheckComputeInstanceHasInstanceId(instance *compute.Instance, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + remote := fmt.Sprintf("%d", instance.Id) + local := rs.Primary.Attributes["instance_id"] + + if remote != local { + return fmt.Errorf("Instance id stored does not match: remote has %#v but local has %#v", remote, + local) + } + + return nil + } +} + +func testAccCheckComputeInstanceBootDisk(instance *compute.Instance, source string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Disks == nil { + return fmt.Errorf("no disks") + } + + for _, disk := range instance.Disks { + if disk.Boot == true { + if strings.HasSuffix(disk.Source, source) { + return nil + } + } + } + + return fmt.Errorf("Boot disk not found with source %q", source) + } +} + +func testAccCheckComputeInstanceBootDiskType(instanceName string, diskType string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + // boot disk is named the same as the Instance + disk, err := config.clientCompute.Disks.Get(config.Project, "us-central1-a", instanceName).Do() + if err != nil { + return err + } + if strings.Contains(disk.Type, diskType) { + return nil + } + + return fmt.Errorf("Boot disk not found with type %q", diskType) + } +} + +func testAccCheckComputeInstanceScratchDisk(instance *compute.Instance, interfaces []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Disks == nil { + return fmt.Errorf("no disks") + } + + i := 0 + for _, disk := range instance.Disks { + if disk.Type == "SCRATCH" { + if i >= len(interfaces) { + return fmt.Errorf("Expected %d scratch disks, found more", len(interfaces)) + } + if disk.Interface != interfaces[i] { + return fmt.Errorf("Mismatched interface on scratch disk #%d, expected: %q, found: %q", + i, interfaces[i], disk.Interface) + } + i++ + } + } + + if i != len(interfaces) { + return fmt.Errorf("Expected %d scratch disks, found %d", len(interfaces), i) + } + + return nil + } +} + +func testAccCheckComputeInstanceDiskEncryptionKey(n string, instance *compute.Instance, bootDiskEncryptionKey string, diskNameToEncryptionKey map[string]*compute.CustomerEncryptionKey) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + for i, disk := range instance.Disks { + if disk.Boot { + attr := rs.Primary.Attributes["boot_disk.0.disk_encryption_key_sha256"] + if attr != bootDiskEncryptionKey { + return fmt.Errorf("Boot disk has wrong encryption key in state.\nExpected: %s\nActual: %s", bootDiskEncryptionKey, attr) + } + if disk.DiskEncryptionKey == nil && attr != "" { + return fmt.Errorf("Disk %d has mismatched encryption key.\nTF State: %+v\nGCP State: ", i, attr) + } + if disk.DiskEncryptionKey != nil && attr != disk.DiskEncryptionKey.Sha256 { + return fmt.Errorf("Disk %d has mismatched encryption key.\nTF State: %+v\nGCP State: %+v", + i, attr, disk.DiskEncryptionKey.Sha256) + } + } else { + if disk.DiskEncryptionKey != nil { + expectedKey := diskNameToEncryptionKey[GetResourceNameFromSelfLink(disk.Source)].Sha256 + if disk.DiskEncryptionKey.Sha256 != expectedKey { + return fmt.Errorf("Disk %d has unexpected encryption key in GCP.\nExpected: %s\nActual: %s", i, expectedKey, disk.DiskEncryptionKey.Sha256) + } + } + } + } + + numAttachedDisks, err := strconv.Atoi(rs.Primary.Attributes["attached_disk.#"]) + if err != nil { + return fmt.Errorf("Error converting value of attached_disk.#") + } + for i := 0; i < numAttachedDisks; i++ { + diskName := GetResourceNameFromSelfLink(rs.Primary.Attributes[fmt.Sprintf("attached_disk.%d.source", i)]) + encryptionKey := rs.Primary.Attributes[fmt.Sprintf("attached_disk.%d.disk_encryption_key_sha256", i)] + if key, ok := diskNameToEncryptionKey[diskName]; ok { + expectedEncryptionKey := key.Sha256 + if encryptionKey != expectedEncryptionKey { + return fmt.Errorf("Attached disk %d has unexpected encryption key in state.\nExpected: %s\nActual: %s", i, expectedEncryptionKey, encryptionKey) + } + } + } + return nil + } +} + +func testAccCheckComputeInstanceTag(instance *compute.Instance, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Tags == nil { + return fmt.Errorf("no tags") + } + + for _, k := range instance.Tags.Items { + if k == n { + return nil + } + } + + return fmt.Errorf("tag not found: %s", n) + } +} + +func testAccCheckComputeInstanceLabel(instance *compute.Instance, key string, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Labels == nil { + return fmt.Errorf("no labels found on instance %s", instance.Name) + } + + v, ok := instance.Labels[key] + if !ok { + return fmt.Errorf("No label found with key %s on instance %s", key, instance.Name) + } + if v != value { + return fmt.Errorf("Expected value '%s' but found value '%s' for label '%s' on instance %s", value, v, key, instance.Name) + } + + return nil + } +} + +func testAccCheckComputeInstanceServiceAccount(instance *compute.Instance, scope string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if count := len(instance.ServiceAccounts); count != 1 { + return fmt.Errorf("Wrong number of ServiceAccounts: expected 1, got %d", count) + } + + for _, val := range instance.ServiceAccounts[0].Scopes { + if val == scope { + return nil + } + } + + return fmt.Errorf("Scope not found: %s", scope) + } +} + +func testAccCheckComputeInstanceHasSubnet(instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, i := range instance.NetworkInterfaces { + if i.Subnetwork == "" { + return fmt.Errorf("no subnet") + } + } + + return nil + } +} + +func testAccCheckComputeInstanceHasAnyNetworkIP(instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, i := range instance.NetworkInterfaces { + if i.NetworkIP == "" { + return fmt.Errorf("no network_ip") + } + } + + return nil + } +} + +func testAccCheckComputeInstanceHasNetworkIP(instance *compute.Instance, networkIP string) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, i := range instance.NetworkInterfaces { + if i.NetworkIP != networkIP { + return fmt.Errorf("Wrong network_ip found: expected %v, got %v", networkIP, i.NetworkIP) + } + } + + return nil + } +} + +func testAccCheckComputeInstanceHasMultiNic(instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + if len(instance.NetworkInterfaces) < 2 { + return fmt.Errorf("only saw %d nics", len(instance.NetworkInterfaces)) + } + + return nil + } +} + +func testAccCheckComputeInstanceHasGuestAccelerator(instance *compute.Instance, acceleratorType string, acceleratorCount int64) resource.TestCheckFunc { + return func(s *terraform.State) error { + if len(instance.GuestAccelerators) != 1 { + return fmt.Errorf("Expected only one guest accelerator") + } + + if !strings.HasSuffix(instance.GuestAccelerators[0].AcceleratorType, acceleratorType) { + return fmt.Errorf("Wrong accelerator type: expected %v, got %v", acceleratorType, instance.GuestAccelerators[0].AcceleratorType) + } + + if instance.GuestAccelerators[0].AcceleratorCount != acceleratorCount { + return fmt.Errorf("Wrong accelerator acceleratorCount: expected %d, got %d", acceleratorCount, instance.GuestAccelerators[0].AcceleratorCount) + } + + return nil + } +} + +func testAccCheckComputeInstanceLacksGuestAccelerator(instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + if len(instance.GuestAccelerators) > 0 { + return fmt.Errorf("Expected no guest accelerators") + } + + return nil + } +} + +func testAccCheckComputeInstanceHasMinCpuPlatform(instance *compute.Instance, minCpuPlatform string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.MinCpuPlatform != minCpuPlatform { + return fmt.Errorf("Wrong minimum CPU platform: expected %s, got %s", minCpuPlatform, instance.MinCpuPlatform) + } + + return nil + } +} + +func testAccCheckComputeInstanceHasAliasIpRange(instance *compute.Instance, subnetworkRangeName, iPCidrRange string) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, networkInterface := range instance.NetworkInterfaces { + for _, aliasIpRange := range networkInterface.AliasIpRanges { + if aliasIpRange.SubnetworkRangeName == subnetworkRangeName && (aliasIpRange.IpCidrRange == iPCidrRange || ipCidrRangeDiffSuppress("ip_cidr_range", aliasIpRange.IpCidrRange, iPCidrRange, nil)) { + return nil + } + } + } + + return fmt.Errorf("Alias ip range with name %s and cidr %s not present", subnetworkRangeName, iPCidrRange) + } +} + +func testAccCheckComputeInstanceHasAssignedNatIP(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_instance" { + continue + } + ip := rs.Primary.Attributes["network_interface.0.access_config.0.nat_ip"] + if ip == "" { + return fmt.Errorf("No assigned NatIP for instance %s", rs.Primary.Attributes["name"]) + } + } + return nil +} + +func testAccCheckComputeInstanceHasConfiguredDeletionProtection(instance *compute.Instance, configuredDeletionProtection bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.DeletionProtection != configuredDeletionProtection { + return fmt.Errorf("Wrong deletion protection flag: expected %t, got %t", configuredDeletionProtection, instance.DeletionProtection) + } + + return nil + } +} + +func testAccComputeInstance_basic(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + //deletion_protection = false is implicit in this config due to default value + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + baz = "qux" + } + + metadata { + startup-script = "echo Hello" + } + + labels { + my_key = "my_value" + my_other_key = "my_other_value" + } +} +`, instance) +} + +func testAccComputeInstance_basic2(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } +} +`, instance) +} + +func testAccComputeInstance_basic3(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } +} +`, instance) +} + +func testAccComputeInstance_basic4(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + + metadata { + foo = "bar" + } +} +`, instance) +} + +func testAccComputeInstance_basic5(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } +} +`, instance) +} + +func testAccComputeInstance_basic_deletionProtectionFalse(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + deletion_protection = false + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } +} +`, instance) +} + +func testAccComputeInstance_basic_deletionProtectionTrue(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + deletion_protection = true + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } +} +`, instance) +} + +// Update zone to ForceNew, and change metadata k/v entirely +// Generates diff mismatch +func testAccComputeInstance_forceNewAndChangeMetadata(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-b" + tags = ["baz"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + access_config { } + } + + metadata { + qux = "true" + } +} +`, instance) +} + +// Update metadata, tags, and network_interface +func testAccComputeInstance_update(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["baz"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + access_config { } + } + + metadata { + bar = "baz" + startup-script = "echo Hello" + } + + labels { + only_me = "nothing_else" + } +} +`, instance) +} + +func testAccComputeInstance_ip(ip, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_address" "foo" { + name = "%s" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + access_config { + nat_ip = "${google_compute_address.foo.address}" + } + } + + metadata { + foo = "bar" + } +} +`, ip, instance) +} + +func testAccComputeInstance_PTRRecord(record, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + access_config { + public_ptr_domain_name = "test-record.%s.hashicorptest.com." + } + } + + metadata { + foo = "bar" + } +} +`, instance, record) +} + +func testAccComputeInstance_networkTier(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + access_config { + network_tier = "STANDARD" + } + } +} +`, instance) +} + +func testAccComputeInstance_disks_encryption(bootEncryptionKey string, diskNameToEncryptionKey map[string]*compute.CustomerEncryptionKey, instance string) string { + diskNames := []string{} + for k, _ := range diskNameToEncryptionKey { + diskNames = append(diskNames, k) + } + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" + + disk_encryption_key { + raw_key = "%s" + } +} + +resource "google_compute_disk" "foobar2" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" + + disk_encryption_key { + raw_key = "%s" + } +} + +resource "google_compute_disk" "foobar3" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" + + disk_encryption_key { + raw_key = "%s" + } +} + +resource "google_compute_disk" "foobar4" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + disk_encryption_key_raw = "%s" + } + + attached_disk { + source = "${google_compute_disk.foobar.self_link}" + disk_encryption_key_raw = "%s" + } + + attached_disk { + source = "${google_compute_disk.foobar2.self_link}" + disk_encryption_key_raw = "%s" + } + + attached_disk { + source = "${google_compute_disk.foobar4.self_link}" + } + + attached_disk { + source = "${google_compute_disk.foobar3.self_link}" + disk_encryption_key_raw = "%s" + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } +} +`, diskNames[0], diskNameToEncryptionKey[diskNames[0]].RawKey, + diskNames[1], diskNameToEncryptionKey[diskNames[1]].RawKey, + diskNames[2], diskNameToEncryptionKey[diskNames[2]].RawKey, + "instance-testd-"+acctest.RandString(10), + instance, bootEncryptionKey, + diskNameToEncryptionKey[diskNames[0]].RawKey, diskNameToEncryptionKey[diskNames[1]].RawKey, diskNameToEncryptionKey[diskNames[2]].RawKey) +} + +func testAccComputeInstance_attachedDisk(disk, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + attached_disk { + source = "${google_compute_disk.foobar.name}" + } + + network_interface { + network = "default" + } +} +`, disk, instance) +} + +func testAccComputeInstance_attachedDisk_sourceUrl(disk, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + attached_disk { + source = "${google_compute_disk.foobar.self_link}" + } + + network_interface { + network = "default" + } +} +`, disk, instance) +} + +func testAccComputeInstance_attachedDisk_modeRo(disk, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + attached_disk { + source = "${google_compute_disk.foobar.self_link}" + mode = "READ_ONLY" + } + + network_interface { + network = "default" + } +} +`, disk, instance) +} + +func testAccComputeInstance_addAttachedDisk(disk, disk2, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_disk" "foobar2" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + attached_disk { + source = "${google_compute_disk.foobar.name}" + } + + attached_disk { + source = "${google_compute_disk.foobar2.self_link}" + } + + network_interface { + network = "default" + } +} +`, disk, disk2, instance) +} + +func testAccComputeInstance_detachDisk(disk, disk2, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_disk" "foobar2" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + attached_disk { + source = "${google_compute_disk.foobar.name}" + } + + network_interface { + network = "default" + } +} +`, disk, disk2, instance) +} + +func testAccComputeInstance_updateAttachedDiskEncryptionKey(disk, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" + disk_encryption_key { + raw_key = "c2Vjb25kNzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI" + } +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + attached_disk { + source = "${google_compute_disk.foobar.name}" + disk_encryption_key_raw = "c2Vjb25kNzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI" + } + + network_interface { + network = "default" + } +} +`, disk, instance) +} + +func testAccComputeInstance_bootDisk_source(disk, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + zone = "us-central1-a" + image = "${data.google_compute_image.my_image.self_link}" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + source = "${google_compute_disk.foobar.name}" + } + + network_interface { + network = "default" + } +} +`, disk, instance) +} + +func testAccComputeInstance_bootDisk_sourceUrl(disk, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + zone = "us-central1-a" + image = "${data.google_compute_image.my_image.self_link}" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + source = "${google_compute_disk.foobar.self_link}" + } + + network_interface { + network = "default" + } +} +`, disk, instance) +} + +func testAccComputeInstance_bootDisk_type(instance string, diskType string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + type = "%s" + } + } + + network_interface { + network = "default" + } +} +`, instance, diskType) +} + +func testAccComputeInstance_scratchDisk(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "scratch" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + scratch_disk { + interface = "NVME" + } + + scratch_disk { + interface = "SCSI" + } + + network_interface { + network = "default" + } + +} +`, instance) +} + +func testAccComputeInstance_service_account(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + service_account { + scopes = [ + "userinfo-email", + "compute-ro", + "storage-ro", + ] + } +} +`, instance) +} + +func testAccComputeInstance_scheduling(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + scheduling { + automatic_restart = false + } +} +`, instance) +} + +func testAccComputeInstance_subnet_auto(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_network" "inst-test-network" { + name = "inst-test-network-%s" + + auto_create_subnetworks = true +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "${google_compute_network.inst-test-network.name}" + access_config { } + } + +} +`, acctest.RandString(10), instance) +} + +func testAccComputeInstance_subnet_custom(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_network" "inst-test-network" { + name = "inst-test-network-%s" + + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "inst-test-subnetwork" { + name = "inst-test-subnetwork-%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + network = "${google_compute_network.inst-test-network.self_link}" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.self_link}" + access_config { } + } + +} +`, acctest.RandString(10), acctest.RandString(10), instance) +} + +func testAccComputeInstance_subnet_xpn(org, billingId, projectName, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_project" "host_project" { + name = "Test Project XPN Host" + project_id = "%s-host" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_service" "host_project" { + project = "${google_project.host_project.project_id}" + service = "compute.googleapis.com" +} + +resource "google_compute_shared_vpc_host_project" "host_project" { + project = "${google_project_service.host_project.project}" +} + +resource "google_project" "service_project" { + name = "Test Project XPN Service" + project_id = "%s-service" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_service" "service_project" { + project = "${google_project.service_project.project_id}" + service = "compute.googleapis.com" +} + +resource "google_compute_shared_vpc_service_project" "service_project" { + host_project = "${google_compute_shared_vpc_host_project.host_project.project}" + service_project = "${google_project_service.service_project.project}" +} + + +resource "google_compute_network" "inst-test-network" { + name = "inst-test-network-%s" + project = "${google_compute_shared_vpc_host_project.host_project.project}" + + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "inst-test-subnetwork" { + name = "inst-test-subnetwork-%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + network = "${google_compute_network.inst-test-network.self_link}" + project = "${google_compute_shared_vpc_host_project.host_project.project}" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + project = "${google_compute_shared_vpc_service_project.service_project.service_project}" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.name}" + subnetwork_project = "${google_compute_subnetwork.inst-test-subnetwork.project}" + access_config { } + } + +} +`, projectName, org, billingId, projectName, org, billingId, acctest.RandString(10), acctest.RandString(10), instance) +} + +func testAccComputeInstance_networkIPAuto(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_network" "inst-test-network" { + name = "inst-test-network-%s" +} +resource "google_compute_subnetwork" "inst-test-subnetwork" { + name = "inst-test-subnetwork-%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + network = "${google_compute_network.inst-test-network.self_link}" +} +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.name}" + access_config { } + } + +} +`, acctest.RandString(10), acctest.RandString(10), instance) +} + +func testAccComputeInstance_network_ip_custom(instance, ipAddress string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_network" "inst-test-network" { + name = "inst-test-network-%s" +} +resource "google_compute_subnetwork" "inst-test-subnetwork" { + name = "inst-test-subnetwork-%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + network = "${google_compute_network.inst-test-network.self_link}" +} +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.name}" + network_ip = "%s" + access_config { } + } + +} +`, acctest.RandString(10), acctest.RandString(10), instance, ipAddress) +} + +func testAccComputeInstance_private_image_family(disk, family, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + zone = "us-central1-a" + image = "${data.google_compute_image.my_image.self_link}" +} + +resource "google_compute_image" "foobar" { + name = "%s-1" + source_disk = "${google_compute_disk.foobar.self_link}" + family = "%s" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "${google_compute_image.foobar.family}" + } + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } +} +`, disk, family, family, instance) +} + +func testAccComputeInstance_multiNic(instance, network, subnetwork string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.name}" + access_config { } + } + + network_interface { + network = "default" + } +} + +resource "google_compute_network" "inst-test-network" { + name = "%s" +} +resource "google_compute_subnetwork" "inst-test-subnetwork" { + name = "%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + network = "${google_compute_network.inst-test-network.self_link}" +} +`, instance, network, subnetwork) +} + +func testAccComputeInstance_guestAccelerator(instance string, count uint8) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-east1-d" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + scheduling { + # Instances with guest accelerators do not support live migration. + on_host_maintenance = "TERMINATE" + } + + guest_accelerator { + count = %d + type = "nvidia-tesla-k80" + } +}`, instance, count) +} + +func testAccComputeInstance_minCpuPlatform(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-east1-d" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + min_cpu_platform = "Intel Haswell" +}`, instance) +} + +func testAccComputeInstance_primaryAliasIpRange(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-east1-d" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + + alias_ip_range { + ip_cidr_range = "/24" + } + } +}`, instance) +} + +func testAccComputeInstance_secondaryAliasIpRange(network, subnet, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_network" "inst-test-network" { + name = "%s" +} +resource "google_compute_subnetwork" "inst-test-subnetwork" { + name = "%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-east1" + network = "${google_compute_network.inst-test-network.self_link}" + secondary_ip_range { + range_name = "inst-test-secondary" + ip_cidr_range = "172.16.0.0/20" + } + secondary_ip_range { + range_name = "inst-test-tertiary" + ip_cidr_range = "10.1.0.0/16" + } +} +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-east1-d" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.self_link}" + + alias_ip_range = [ + { + subnetwork_range_name = "${google_compute_subnetwork.inst-test-subnetwork.secondary_ip_range.0.range_name}" + ip_cidr_range = "172.16.0.0/24" + }, + { + subnetwork_range_name = "${google_compute_subnetwork.inst-test-subnetwork.secondary_ip_range.1.range_name}" + ip_cidr_range = "10.1.0.0/20" + } + ] + + } +}`, network, subnet, instance) +} + +func testAccComputeInstance_secondaryAliasIpRangeUpdate(network, subnet, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_network" "inst-test-network" { + name = "%s" +} +resource "google_compute_subnetwork" "inst-test-subnetwork" { + name = "%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-east1" + network = "${google_compute_network.inst-test-network.self_link}" + secondary_ip_range { + range_name = "inst-test-secondary" + ip_cidr_range = "172.16.0.0/20" + } + secondary_ip_range { + range_name = "inst-test-tertiary" + ip_cidr_range = "10.1.0.0/16" + } +} +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-east1-d" + + boot_disk { + initialize_params { + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + subnetwork = "${google_compute_subnetwork.inst-test-subnetwork.self_link}" + alias_ip_range { + ip_cidr_range = "10.0.1.0/24" + } + } +}`, network, subnet, instance) +} + +// Set fields that require stopping the instance: machine_type, min_cpu_platform, and service_account +func testAccComputeInstance_stopInstanceToUpdate(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + min_cpu_platform = "Intel Broadwell" + service_account { + scopes = [ + "userinfo-email", + "compute-ro", + "storage-ro", + ] + } + + allow_stopping_for_update = true +} +`, instance) +} + +// Update fields that require stopping the instance: machine_type, min_cpu_platform, and service_account +func testAccComputeInstance_stopInstanceToUpdate2(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-2" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + min_cpu_platform = "Intel Skylake" + service_account { + scopes = [ + "userinfo-email", + "compute-ro", + ] + } + + allow_stopping_for_update = true +} +`, instance) +} + +// Remove fields that require stopping the instance: min_cpu_platform and service_account (machine_type is Required) +func testAccComputeInstance_stopInstanceToUpdate3(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-2" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + allow_stopping_for_update = true +} +`, instance) +} diff --git a/provider/terraform/tests/resource_compute_network_peering_test.go b/provider/terraform/tests/resource_compute_network_peering_test.go new file mode 100644 index 000000000000..14accde2baea --- /dev/null +++ b/provider/terraform/tests/resource_compute_network_peering_test.go @@ -0,0 +1,125 @@ +package google + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" + "strings" + "testing" +) + +func TestAccComputeNetworkPeering_basic(t *testing.T) { + t.Parallel() + + var peering compute.NetworkPeering + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccComputeNetworkPeeringDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeNetworkPeering_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeNetworkPeeringExist("google_compute_network_peering.foo", &peering), + testAccCheckComputeNetworkPeeringAutoCreateRoutes(true, &peering), + testAccCheckComputeNetworkPeeringExist("google_compute_network_peering.bar", &peering), + testAccCheckComputeNetworkPeeringAutoCreateRoutes(true, &peering), + ), + }, + }, + }) + +} + +func testAccComputeNetworkPeeringDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_network_peering" { + continue + } + + _, err := config.clientCompute.Networks.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Network peering still exists") + } + } + + return nil +} + +func testAccCheckComputeNetworkPeeringExist(n string, peering *compute.NetworkPeering) 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) + + parts := strings.Split(rs.Primary.ID, "/") + if len(parts) != 2 { + return fmt.Errorf("Invalid network peering identifier: %s", rs.Primary.ID) + } + + networkName, peeringName := parts[0], parts[1] + + network, err := config.clientCompute.Networks.Get(config.Project, networkName).Do() + if err != nil { + return err + } + + found := findPeeringFromNetwork(network, peeringName) + if found == nil { + return fmt.Errorf("Network peering '%s' not found in network '%s'", peeringName, network.Name) + } + *peering = *found + + return nil + } +} + +func testAccCheckComputeNetworkPeeringAutoCreateRoutes(v bool, peering *compute.NetworkPeering) resource.TestCheckFunc { + return func(s *terraform.State) error { + if peering.AutoCreateRoutes != v { + return fmt.Errorf("should AutoCreateRoutes set to %t", v) + } + + return nil + } +} + +func testAccComputeNetworkPeering_basic() string { + return fmt.Sprintf(` +resource "google_compute_network" "network1" { + name = "network-test-1-%s" + auto_create_subnetworks = false +} + +resource "google_compute_network" "network2" { + name = "network-test-2-%s" + auto_create_subnetworks = false +} + +resource "google_compute_network_peering" "foo" { + name = "peering-test-1-%s" + network = "${google_compute_network.network1.self_link}" + peer_network = "${google_compute_network.network2.self_link}" +} + +resource "google_compute_network_peering" "bar" { + name = "peering-test-2-%s" + auto_create_routes = true + network = "${google_compute_network.network2.self_link}" + peer_network = "${google_compute_network.network1.self_link}" +} +`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10), acctest.RandString(10)) +} diff --git a/provider/terraform/tests/resource_compute_network_test.go b/provider/terraform/tests/resource_compute_network_test.go new file mode 100644 index 000000000000..56ae6320c6c6 --- /dev/null +++ b/provider/terraform/tests/resource_compute_network_test.go @@ -0,0 +1,302 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeNetwork_basic(t *testing.T) { + t.Parallel() + + var network compute.Network + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeNetworkDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeNetwork_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeNetworkExists( + "google_compute_network.foobar", &network), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_network.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeNetwork_auto_subnet(t *testing.T) { + t.Parallel() + + var network compute.Network + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeNetworkDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeNetwork_auto_subnet(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeNetworkExists( + "google_compute_network.bar", &network), + testAccCheckComputeNetworkIsAutoSubnet( + "google_compute_network.bar", &network), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_network.bar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeNetwork_custom_subnet(t *testing.T) { + t.Parallel() + + var network compute.Network + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeNetworkDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeNetwork_custom_subnet(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeNetworkExists( + "google_compute_network.baz", &network), + testAccCheckComputeNetworkIsCustomSubnet( + "google_compute_network.baz", &network), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_network.baz", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeNetwork_routing_mode(t *testing.T) { + t.Parallel() + + var network compute.Network + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeNetworkDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeNetwork_routing_mode("GLOBAL"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeNetworkExists( + "google_compute_network.acc_network_routing_mode", &network), + testAccCheckComputeNetworkHasRoutingMode( + "google_compute_network.acc_network_routing_mode", &network, "GLOBAL"), + ), + }, + // Test updating the routing field (only updateable field). + resource.TestStep{ + Config: testAccComputeNetwork_routing_mode("REGIONAL"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeNetworkExists( + "google_compute_network.acc_network_routing_mode", &network), + testAccCheckComputeNetworkHasRoutingMode( + "google_compute_network.acc_network_routing_mode", &network, "REGIONAL"), + ), + }, + }, + }) +} + +func TestAccComputeNetwork_default_routing_mode(t *testing.T) { + t.Parallel() + + var network compute.Network + + expectedRoutingMode := "REGIONAL" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeNetworkDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeNetwork_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeNetworkExists( + "google_compute_network.foobar", &network), + testAccCheckComputeNetworkHasRoutingMode( + "google_compute_network.foobar", &network, expectedRoutingMode), + ), + }, + }, + }) +} + +func testAccCheckComputeNetworkDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_network" { + continue + } + + _, err := config.clientCompute.Networks.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Network still exists") + } + } + + return nil +} + +func testAccCheckComputeNetworkExists(n string, network *compute.Network) 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) + + found, err := config.clientCompute.Networks.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Network not found") + } + + *network = *found + + return nil + } +} + +func testAccCheckComputeNetworkIsAutoSubnet(n string, network *compute.Network) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + found, err := config.clientCompute.Networks.Get( + config.Project, network.Name).Do() + if err != nil { + return err + } + + if !found.AutoCreateSubnetworks { + return fmt.Errorf("should have AutoCreateSubnetworks = true") + } + + if found.IPv4Range != "" { + return fmt.Errorf("should not have IPv4Range") + } + + return nil + } +} + +func testAccCheckComputeNetworkIsCustomSubnet(n string, network *compute.Network) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + found, err := config.clientCompute.Networks.Get( + config.Project, network.Name).Do() + if err != nil { + return err + } + + if found.AutoCreateSubnetworks { + return fmt.Errorf("should have AutoCreateSubnetworks = false") + } + + if found.IPv4Range != "" { + return fmt.Errorf("should not have IPv4Range") + } + + return nil + } +} + +func testAccCheckComputeNetworkHasRoutingMode(n string, network *compute.Network, routingMode string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.Attributes["routing_mode"] == "" { + return fmt.Errorf("Routing mode not found on resource") + } + + found, err := config.clientCompute.Networks.Get( + config.Project, network.Name).Do() + if err != nil { + return err + } + + foundRoutingMode := found.RoutingConfig.RoutingMode + + if routingMode != foundRoutingMode { + return fmt.Errorf("Expected routing mode %s to match actual routing mode %s", routingMode, foundRoutingMode) + } + + return nil + } +} + +func testAccComputeNetwork_basic() string { + return fmt.Sprintf(` +resource "google_compute_network" "foobar" { + name = "network-test-%s" +}`, acctest.RandString(10)) +} + +func testAccComputeNetwork_auto_subnet() string { + return fmt.Sprintf(` +resource "google_compute_network" "bar" { + name = "network-test-%s" + auto_create_subnetworks = true +}`, acctest.RandString(10)) +} + +func testAccComputeNetwork_custom_subnet() string { + return fmt.Sprintf(` +resource "google_compute_network" "baz" { + name = "network-test-%s" + auto_create_subnetworks = false +}`, acctest.RandString(10)) +} + +func testAccComputeNetwork_routing_mode(routingMode string) string { + return fmt.Sprintf(` +resource "google_compute_network" "acc_network_routing_mode" { + name = "network-test-%s" + routing_mode = "%s" +}`, acctest.RandString(10), routingMode) +} diff --git a/provider/terraform/tests/resource_compute_project_metadata_item_test.go b/provider/terraform/tests/resource_compute_project_metadata_item_test.go new file mode 100644 index 000000000000..a4334959ddad --- /dev/null +++ b/provider/terraform/tests/resource_compute_project_metadata_item_test.go @@ -0,0 +1,174 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeProjectMetadataItem_basic(t *testing.T) { + t.Parallel( + // Key must be unique to avoid concurrent tests interfering with each other + ) + + key := "myKey" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckProjectMetadataItemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProjectMetadataItem_basicWithResourceName("foobar", key, "myValue"), + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectMetadataItem_hasMetadata(key, "myValue"), + ), + }, + { + ResourceName: "google_compute_project_metadata_item.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeProjectMetadataItem_basicMultiple(t *testing.T) { + t.Parallel( + // Generate a config of two config keys + ) + + config := testAccProjectMetadataItem_basic("myKey", "myValue") + + testAccProjectMetadataItem_basic("myOtherKey", "myOtherValue") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckProjectMetadataItemDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectMetadataItem_hasMetadata("myKey", "myValue"), + testAccCheckProjectMetadataItem_hasMetadata("myOtherKey", "myOtherValue"), + ), + }, + }, + }) +} + +func TestAccComputeProjectMetadataItem_basicWithEmptyVal(t *testing.T) { + t.Parallel( + // Key must be unique to avoid concurrent tests interfering with each other + ) + + key := "myKey" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckProjectMetadataItemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProjectMetadataItem_basicWithResourceName("foobar", key, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectMetadataItem_hasMetadata(key, ""), + ), + }, + { + ResourceName: "google_compute_project_metadata_item.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeProjectMetadataItem_basicUpdate(t *testing.T) { + t.Parallel( + // Key must be unique to avoid concurrent tests interfering with each other + ) + + key := "myKey" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckProjectMetadataItemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProjectMetadataItem_basicWithResourceName("foobar", key, "myValue"), + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectMetadataItem_hasMetadata(key, "myValue"), + ), + }, + { + Config: testAccProjectMetadataItem_basicWithResourceName("foobar", key, "myUpdatedValue"), + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectMetadataItem_hasMetadata(key, "myUpdatedValue"), + ), + }, + }, + }) +} + +func testAccCheckProjectMetadataItem_hasMetadata(key, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + project, err := config.clientCompute.Projects.Get(config.Project).Do() + if err != nil { + return err + } + + metadata := flattenMetadata(project.CommonInstanceMetadata) + + val, ok := metadata[key] + if !ok { + return fmt.Errorf("Unable to find a value for key '%s'", key) + } + if val != value { + return fmt.Errorf("Value for key '%s' does not match. Expected '%s' but found '%s'", key, value, val) + } + return nil + } +} + +func testAccCheckProjectMetadataItemDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + project, err := config.clientCompute.Projects.Get(config.Project).Do() + if err != nil { + return err + } + + metadata := flattenMetadata(project.CommonInstanceMetadata) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_project_metadata_item" { + continue + } + + _, ok := metadata[rs.Primary.ID] + if ok { + return fmt.Errorf("Metadata key/value '%s': '%s' still exist", rs.Primary.Attributes["key"], rs.Primary.Attributes["value"]) + } + } + + return nil +} + +func testAccProjectMetadataItem_basic(key, val string) string { + return testAccProjectMetadataItem_basicWithResourceName(acctest.RandString(10), key, val) +} + +func testAccProjectMetadataItem_basicWithResourceName(resourceName, key, val string) string { + return fmt.Sprintf(` +resource "google_compute_project_metadata_item" "%s" { + key = "%s" + value = "%s" +} +`, resourceName, key, val) +} diff --git a/provider/terraform/tests/resource_compute_project_metadata_test.go b/provider/terraform/tests/resource_compute_project_metadata_test.go new file mode 100644 index 000000000000..0ab371893131 --- /dev/null +++ b/provider/terraform/tests/resource_compute_project_metadata_test.go @@ -0,0 +1,302 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +// Add two key value pairs +func TestAccComputeProjectMetadata_basic(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + billingId := getTestBillingAccountFromEnv(t) + var project compute.Project + projectID := "terrafom-test-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeProjectMetadataDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeProject_basic0_metadata(projectID, pname, org, billingId), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeProjectExists( + "google_compute_project_metadata.fizzbuzz", projectID, &project), + testAccCheckComputeProjectMetadataContains(projectID, "banana", "orange"), + testAccCheckComputeProjectMetadataContains(projectID, "sofa", "darwinism"), + testAccCheckComputeProjectMetadataSize(projectID, 2), + ), + }, + }, + }) +} + +// Add three key value pairs, then replace one and modify a second +func TestAccComputeProjectMetadata_modify_1(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + billingId := getTestBillingAccountFromEnv(t) + var project compute.Project + projectID := "terrafom-test-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeProjectMetadataDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeProject_modify0_metadata(projectID, pname, org, billingId), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeProjectExists( + "google_compute_project_metadata.fizzbuzz", projectID, &project), + testAccCheckComputeProjectMetadataContains(projectID, "paper", "pen"), + testAccCheckComputeProjectMetadataContains(projectID, "genghis_khan", "french bread"), + testAccCheckComputeProjectMetadataContains(projectID, "happy", "smiling"), + testAccCheckComputeProjectMetadataSize(projectID, 3), + ), + }, + + resource.TestStep{ + Config: testAccComputeProject_modify1_metadata(projectID, pname, org, billingId), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeProjectExists( + "google_compute_project_metadata.fizzbuzz", projectID, &project), + testAccCheckComputeProjectMetadataContains(projectID, "paper", "pen"), + testAccCheckComputeProjectMetadataContains(projectID, "paris", "french bread"), + testAccCheckComputeProjectMetadataContains(projectID, "happy", "laughing"), + testAccCheckComputeProjectMetadataSize(projectID, 3), + ), + }, + }, + }) +} + +// Add two key value pairs, and replace both +func TestAccComputeProjectMetadata_modify_2(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + billingId := getTestBillingAccountFromEnv(t) + var project compute.Project + projectID := "terraform-test-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeProjectMetadataDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeProject_basic0_metadata(projectID, pname, org, billingId), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeProjectExists( + "google_compute_project_metadata.fizzbuzz", projectID, &project), + testAccCheckComputeProjectMetadataContains(projectID, "banana", "orange"), + testAccCheckComputeProjectMetadataContains(projectID, "sofa", "darwinism"), + testAccCheckComputeProjectMetadataSize(projectID, 2), + ), + }, + + resource.TestStep{ + Config: testAccComputeProject_basic1_metadata(projectID, pname, org, billingId), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeProjectExists( + "google_compute_project_metadata.fizzbuzz", projectID, &project), + testAccCheckComputeProjectMetadataContains(projectID, "kiwi", "papaya"), + testAccCheckComputeProjectMetadataContains(projectID, "finches", "darwinism"), + testAccCheckComputeProjectMetadataSize(projectID, 2), + ), + }, + }, + }) +} + +func testAccCheckComputeProjectMetadataDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_project_metadata" { + continue + } + + project, err := config.clientCompute.Projects.Get(rs.Primary.ID).Do() + if err == nil && len(project.CommonInstanceMetadata.Items) > 0 { + return fmt.Errorf("Error, metadata items still exist in %s", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckComputeProjectExists(n, projectID string, project *compute.Project) 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) + + found, err := config.clientCompute.Projects.Get(projectID).Do() + if err != nil { + return err + } + + if "common_metadata" != rs.Primary.ID { + return fmt.Errorf("Common metadata not found, found %s", rs.Primary.ID) + } + + *project = *found + + return nil + } +} + +func testAccCheckComputeProjectMetadataContains(projectID, key, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + project, err := config.clientCompute.Projects.Get(projectID).Do() + if err != nil { + return fmt.Errorf("Error, failed to load project service for %s: %s", config.Project, err) + } + + for _, kv := range project.CommonInstanceMetadata.Items { + if kv.Key == key { + if kv.Value != nil && *kv.Value == value { + return nil + } else { + return fmt.Errorf("Error, key value mismatch, wanted (%s, %s), got (%s, %s)", + key, value, kv.Key, *kv.Value) + } + } + } + + return fmt.Errorf("Error, key %s not present in %s", key, project.SelfLink) + } +} + +func testAccCheckComputeProjectMetadataSize(projectID string, size int) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + project, err := config.clientCompute.Projects.Get(projectID).Do() + if err != nil { + return fmt.Errorf("Error, failed to load project service for %s: %s", config.Project, err) + } + + if size > len(project.CommonInstanceMetadata.Items) { + return fmt.Errorf("Error, expected at least %d metadata items, got %d", size, + len(project.CommonInstanceMetadata.Items)) + } + + return nil + } +} + +func testAccComputeProject_basic0_metadata(projectID, name, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "project" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_service" "compute" { + project = "${google_project.project.project_id}" + service = "compute.googleapis.com" +} + +resource "google_compute_project_metadata" "fizzbuzz" { + project = "${google_project.project.project_id}" + metadata { + banana = "orange" + sofa = "darwinism" + } + depends_on = ["google_project_service.compute"] +}`, projectID, name, org, billing) +} + +func testAccComputeProject_basic1_metadata(projectID, name, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "project" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_service" "compute" { + project = "${google_project.project.project_id}" + service = "compute.googleapis.com" +} + +resource "google_compute_project_metadata" "fizzbuzz" { + project = "${google_project.project.project_id}" + metadata { + kiwi = "papaya" + finches = "darwinism" + } + depends_on = ["google_project_service.compute"] +}`, projectID, name, org, billing) +} + +func testAccComputeProject_modify0_metadata(projectID, name, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "project" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_service" "compute" { + project = "${google_project.project.project_id}" + service = "compute.googleapis.com" +} + +resource "google_compute_project_metadata" "fizzbuzz" { + project = "${google_project.project.project_id}" + metadata { + paper = "pen" + genghis_khan = "french bread" + happy = "smiling" + } + depends_on = ["google_project_service.compute"] +}`, projectID, name, org, billing) +} + +func testAccComputeProject_modify1_metadata(projectID, name, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "project" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_service" "compute" { + project = "${google_project.project.project_id}" + service = "compute.googleapis.com" +} + +resource "google_compute_project_metadata" "fizzbuzz" { + project = "${google_project.project.project_id}" + metadata { + paper = "pen" + paris = "french bread" + happy = "laughing" + } + depends_on = ["google_project_service.compute"] +}`, projectID, name, org, billing) +} diff --git a/provider/terraform/tests/resource_compute_region_backend_service_test.go b/provider/terraform/tests/resource_compute_region_backend_service_test.go new file mode 100644 index 000000000000..bde3198c4995 --- /dev/null +++ b/provider/terraform/tests/resource_compute_region_backend_service_test.go @@ -0,0 +1,405 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeRegionBackendService_basic(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + extraCheckName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRegionBackendService_basic(serviceName, checkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionBackendServiceExists( + "google_compute_region_backend_service.foobar", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeRegionBackendService_basicModified( + serviceName, checkName, extraCheckName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionBackendServiceExists( + "google_compute_region_backend_service.foobar", &svc), + ), + }, + }, + }) +} + +func TestAccComputeRegionBackendService_withBackend(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + igName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + itName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRegionBackendService_withBackend( + serviceName, igName, itName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionBackendServiceExists( + "google_compute_region_backend_service.lipsum", &svc), + ), + }, + }, + }) + + if svc.TimeoutSec != 10 { + t.Errorf("Expected TimeoutSec == 10, got %d", svc.TimeoutSec) + } + if svc.Protocol != "TCP" { + t.Errorf("Expected Protocol to be TCP, got %q", svc.Protocol) + } + if len(svc.Backends) != 1 { + t.Errorf("Expected 1 backend, got %d", len(svc.Backends)) + } +} + +func TestAccComputeRegionBackendService_withBackendAndUpdate(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + igName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + itName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRegionBackendService_withBackend( + serviceName, igName, itName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionBackendServiceExists( + "google_compute_region_backend_service.lipsum", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeRegionBackendService_withBackend( + serviceName, igName, itName, checkName, 20), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionBackendServiceExists( + "google_compute_region_backend_service.lipsum", &svc), + ), + }, + }, + }) + + if svc.TimeoutSec != 20 { + t.Errorf("Expected TimeoutSec == 20, got %d", svc.TimeoutSec) + } + if svc.Protocol != "TCP" { + t.Errorf("Expected Protocol to be TCP, got %q", svc.Protocol) + } + if len(svc.Backends) != 1 { + t.Errorf("Expected 1 backend, got %d", len(svc.Backends)) + } +} + +func TestAccComputeRegionBackendService_withConnectionDraining(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRegionBackendService_withConnectionDraining(serviceName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionBackendServiceExists( + "google_compute_region_backend_service.foobar", &svc), + ), + }, + }, + }) + + if svc.ConnectionDraining.DrainingTimeoutSec != 10 { + t.Errorf("Expected ConnectionDraining.DrainingTimeoutSec == 10, got %d", svc.ConnectionDraining.DrainingTimeoutSec) + } +} + +func TestAccComputeRegionBackendService_withConnectionDrainingAndUpdate(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRegionBackendService_withConnectionDraining(serviceName, checkName, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionBackendServiceExists( + "google_compute_region_backend_service.foobar", &svc), + ), + }, + resource.TestStep{ + Config: testAccComputeRegionBackendService_basic(serviceName, checkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionBackendServiceExists( + "google_compute_region_backend_service.foobar", &svc), + ), + }, + }, + }) + + if svc.ConnectionDraining.DrainingTimeoutSec != 0 { + t.Errorf("Expected ConnectionDraining.DrainingTimeoutSec == 0, got %d", svc.ConnectionDraining.DrainingTimeoutSec) + } +} + +func TestAccComputeRegionBackendService_withSessionAffinity(t *testing.T) { + t.Parallel() + + serviceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + checkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var svc compute.BackendService + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRegionBackendServiceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRegionBackendService_withSessionAffinity( + serviceName, checkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionBackendServiceExists( + "google_compute_region_backend_service.foobar", &svc), + ), + }, + }, + }) + + if svc.SessionAffinity != "CLIENT_IP" { + t.Errorf("Expected Protocol to be CLIENT_IP, got %q", svc.SessionAffinity) + } +} + +func testAccCheckComputeRegionBackendServiceDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_region_backend_service" { + continue + } + + _, err := config.clientCompute.RegionBackendServices.Get( + config.Project, config.Region, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Backend service still exists") + } + } + + return nil +} + +func testAccCheckComputeRegionBackendServiceExists(n string, svc *compute.BackendService) 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) + + found, err := config.clientCompute.RegionBackendServices.Get( + config.Project, config.Region, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Backend service not found") + } + + *svc = *found + + return nil + } +} + +func testAccComputeRegionBackendService_basic(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_health_check.zero.self_link}"] + region = "us-central1" +} + +resource "google_compute_health_check" "zero" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + + tcp_health_check { + port = "80" + } +} +`, serviceName, checkName) +} + +func testAccComputeRegionBackendService_basicModified(serviceName, checkOne, checkTwo string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_health_check.one.self_link}"] + region = "us-central1" +} + +resource "google_compute_health_check" "zero" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + + tcp_health_check { + } +} + +resource "google_compute_health_check" "one" { + name = "%s" + check_interval_sec = 30 + timeout_sec = 30 + + tcp_health_check { + } +} +`, serviceName, checkOne, checkTwo) +} + +func testAccComputeRegionBackendService_withBackend( + serviceName, igName, itName, checkName string, timeout int64) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_region_backend_service" "lipsum" { + name = "%s" + description = "Hello World 1234" + protocol = "TCP" + region = "us-central1" + timeout_sec = %v + + backend { + group = "${google_compute_instance_group_manager.foobar.instance_group}" + } + + health_checks = ["${google_compute_health_check.default.self_link}"] +} + +resource "google_compute_instance_group_manager" "foobar" { + name = "%s" + instance_template = "${google_compute_instance_template.foobar.self_link}" + base_instance_name = "foobar" + zone = "us-central1-f" + target_size = 1 +} + +resource "google_compute_instance_template" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + + network_interface { + network = "default" + } + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } +} + +resource "google_compute_health_check" "default" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + + tcp_health_check { + + } +} +`, serviceName, timeout, igName, itName, checkName) +} + +func testAccComputeRegionBackendService_withSessionAffinity(serviceName, checkName string) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_health_check.zero.self_link}"] + region = "us-central1" + session_affinity = "CLIENT_IP" + +} + +resource "google_compute_health_check" "zero" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + + tcp_health_check { + port = "80" + } +} +`, serviceName, checkName) +} + +func testAccComputeRegionBackendService_withConnectionDraining(serviceName, checkName string, drainingTimeout int64) string { + return fmt.Sprintf(` +resource "google_compute_region_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_health_check.zero.self_link}"] + region = "us-central1" + connection_draining_timeout_sec = %v +} + +resource "google_compute_health_check" "zero" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + + tcp_health_check { + port = "80" + } +} +`, serviceName, drainingTimeout, checkName) +} diff --git a/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go b/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go new file mode 100644 index 000000000000..1d51a9171400 --- /dev/null +++ b/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go @@ -0,0 +1,1319 @@ +package google + +import ( + "fmt" + "reflect" + "strings" + "testing" + + computeBeta "google.golang.org/api/compute/v0.beta" + "google.golang.org/api/compute/v1" + + "sort" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccRegionInstanceGroupManager_basic(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + + template := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + target := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm1 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm2 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRegionInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_basic(template, target, igm1, igm2), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerExists( + "google_compute_region_instance_group_manager.igm-basic", &manager), + testAccCheckRegionInstanceGroupManagerExists( + "google_compute_region_instance_group_manager.igm-no-tp", &manager), + ), + }, + }, + }) +} + +func TestAccRegionInstanceGroupManager_targetSizeZero(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + + templateName := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igmName := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRegionInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_targetSizeZero(templateName, igmName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerExists( + "google_compute_region_instance_group_manager.igm-basic", &manager), + ), + }, + }, + }) + + if manager.TargetSize != 0 { + t.Errorf("Expected target_size to be 0, got %d", manager.TargetSize) + } +} + +func TestAccRegionInstanceGroupManager_update(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + + template1 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + target1 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + target2 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + template2 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRegionInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_update(template1, target1, igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerExists( + "google_compute_region_instance_group_manager.igm-update", &manager), + testAccCheckRegionInstanceGroupManagerNamedPorts( + "google_compute_region_instance_group_manager.igm-update", + map[string]int64{"customhttp": 8080}, + &manager), + ), + }, + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_update2(template1, target1, target2, template2, igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerExists( + "google_compute_region_instance_group_manager.igm-update", &manager), + testAccCheckRegionInstanceGroupManagerUpdated( + "google_compute_region_instance_group_manager.igm-update", 3, + []string{target1, target2}, template2), + testAccCheckRegionInstanceGroupManagerNamedPorts( + "google_compute_region_instance_group_manager.igm-update", + map[string]int64{"customhttp": 8080, "customhttps": 8443}, + &manager), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_region_instance_group_manager.igm-update", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"update_strategy"}, + }, + }, + }) +} + +func TestAccRegionInstanceGroupManager_updateLifecycle(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + + tag1 := "tag1" + tag2 := "tag2" + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRegionInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_updateLifecycle(tag1, igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerExists( + "google_compute_region_instance_group_manager.igm-update", &manager), + ), + }, + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_updateLifecycle(tag2, igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerExists( + "google_compute_region_instance_group_manager.igm-update", &manager), + testAccCheckRegionInstanceGroupManagerTemplateTags( + "google_compute_region_instance_group_manager.igm-update", []string{tag2}), + ), + }, + }, + }) +} + +func TestAccRegionInstanceGroupManager_updateStrategy(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_updateStrategy(igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerExists( + "google_compute_region_instance_group_manager.igm-update-strategy", &manager), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-update-strategy", "update_strategy", "NONE"), + ), + }, + }, + }) +} + +func TestAccRegionInstanceGroupManager_rollingUpdatePolicy(t *testing.T) { + t.Parallel() + + var manager computeBeta.InstanceGroupManager + + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_rollingUpdatePolicy(igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerBetaExists( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", &manager), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "update_strategy", "ROLLING_UPDATE"), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.type", "PROACTIVE"), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.minimal_action", "REPLACE"), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_surge_fixed", "2"), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_unavailable_fixed", "2"), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.min_ready_sec", "20"), + ), + }, + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_rollingUpdatePolicy2(igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerBetaExists( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", &manager), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "update_strategy", "ROLLING_UPDATE"), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.type", "PROACTIVE"), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.minimal_action", "REPLACE"), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_surge_fixed", "2"), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_unavailable_fixed", "0"), + resource.TestCheckResourceAttr( + "google_compute_region_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.min_ready_sec", "10"), + testAccCheckInstanceGroupManagerRollingUpdatePolicy( + &manager, "google_compute_region_instance_group_manager.igm-rolling-update-policy"), + ), + }, + }, + }) +} + +func TestAccRegionInstanceGroupManager_separateRegions(t *testing.T) { + t.Parallel() + + var manager compute.InstanceGroupManager + + igm1 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm2 := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRegionInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_separateRegions(igm1, igm2), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerExists( + "google_compute_region_instance_group_manager.igm-basic", &manager), + testAccCheckRegionInstanceGroupManagerExists( + "google_compute_region_instance_group_manager.igm-basic-2", &manager), + ), + }, + }, + }) +} + +func TestAccRegionInstanceGroupManager_versions(t *testing.T) { + t.Parallel() + + var manager computeBeta.InstanceGroupManager + + primaryTemplate := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + canaryTemplate := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRegionInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_versions(primaryTemplate, canaryTemplate, igm), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerBetaExists("google_compute_region_instance_group_manager.igm-basic", &manager), + testAccCheckRegionInstanceGroupManagerVersions("google_compute_region_instance_group_manager.igm-basic", primaryTemplate, canaryTemplate), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_region_instance_group_manager.igm-basic", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccRegionInstanceGroupManager_autoHealingPolicies(t *testing.T) { + t.Parallel() + + var manager computeBeta.InstanceGroupManager + + template := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + target := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + hck := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRegionInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_autoHealingPolicies(template, target, igm, hck), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerBetaExists( + "google_compute_region_instance_group_manager.igm-basic", &manager), + testAccCheckRegionInstanceGroupManagerAutoHealingPolicies("google_compute_region_instance_group_manager.igm-basic", hck, 10), + ), + }, + }, + }) +} + +func TestAccRegionInstanceGroupManager_distributionPolicy(t *testing.T) { + t.Parallel() + + var manager computeBeta.InstanceGroupManager + + template := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10)) + zones := []string{"us-central1-a", "us-central1-b"} + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRegionInstanceGroupManagerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRegionInstanceGroupManager_distributionPolicy(template, igm, zones), + Check: resource.ComposeTestCheckFunc( + testAccCheckRegionInstanceGroupManagerBetaExists( + "google_compute_region_instance_group_manager.igm-basic", &manager), + testAccCheckRegionInstanceGroupManagerDistributionPolicy("google_compute_region_instance_group_manager.igm-basic", zones), + ), + }, + }, + }) +} + +func testAccCheckRegionInstanceGroupManagerDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_region_instance_group_manager" { + continue + } + _, err := config.clientCompute.RegionInstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["region"], rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("RegionInstanceGroupManager still exists") + } + } + + return nil +} + +func testAccCheckRegionInstanceGroupManagerExists(n string, manager *compute.InstanceGroupManager) 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) + + found, err := config.clientCompute.RegionInstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["region"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("RegionInstanceGroupManager not found") + } + + *manager = *found + + return nil + } +} + +func testAccCheckRegionInstanceGroupManagerBetaExists(n string, manager *computeBeta.InstanceGroupManager) 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) + + found, err := config.clientComputeBeta.RegionInstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["region"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("RegionInstanceGroupManager not found") + } + + *manager = *found + + return nil + } +} + +func testAccCheckRegionInstanceGroupManagerUpdated(n string, size int64, targetPools []string, template string) 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) + + manager, err := config.clientCompute.RegionInstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["region"], rs.Primary.ID).Do() + if err != nil { + return err + } + + // Cannot check the target pool as the instance creation is asynchronous. However, can + // check the target_size. + if manager.TargetSize != size { + return fmt.Errorf("instance count incorrect") + } + + tpNames := make([]string, 0, len(manager.TargetPools)) + for _, targetPool := range manager.TargetPools { + tpNames = append(tpNames, GetResourceNameFromSelfLink(targetPool)) + } + + sort.Strings(tpNames) + sort.Strings(targetPools) + if !reflect.DeepEqual(tpNames, targetPools) { + return fmt.Errorf("target pools incorrect. Expected %s, got %s", targetPools, tpNames) + } + + // check that the instance template updated + instanceTemplate, err := config.clientCompute.InstanceTemplates.Get( + config.Project, template).Do() + if err != nil { + return fmt.Errorf("Error reading instance template: %s", err) + } + + if instanceTemplate.Name != template { + return fmt.Errorf("instance template not updated") + } + + return nil + } +} + +func testAccCheckRegionInstanceGroupManagerNamedPorts(n string, np map[string]int64, instanceGroupManager *compute.InstanceGroupManager) 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) + + manager, err := config.clientCompute.RegionInstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["region"], rs.Primary.ID).Do() + if err != nil { + return err + } + + var found bool + for _, namedPort := range manager.NamedPorts { + found = false + for name, port := range np { + if namedPort.Name == name && namedPort.Port == port { + found = true + } + } + if !found { + return fmt.Errorf("named port incorrect") + } + } + + return nil + } +} + +func testAccCheckRegionInstanceGroupManagerVersions(n string, primaryTemplate string, canaryTemplate string) 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) + + manager, err := config.clientComputeBeta.RegionInstanceGroupManagers.Get(config.Project, rs.Primary.Attributes["region"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if len(manager.Versions) != 2 { + return fmt.Errorf("Expected # of versions to be 2, got %d", len(manager.Versions)) + } + + primaryVersion := manager.Versions[0] + if !strings.Contains(primaryVersion.InstanceTemplate, primaryTemplate) { + return fmt.Errorf("Expected string \"%s\" to appear in \"%s\"", primaryTemplate, primaryVersion.InstanceTemplate) + } + + canaryVersion := manager.Versions[1] + if !strings.Contains(canaryVersion.InstanceTemplate, canaryTemplate) { + return fmt.Errorf("Expected string \"%s\" to appear in \"%s\"", canaryTemplate, canaryVersion.InstanceTemplate) + } + + return nil + } +} + +func testAccCheckRegionInstanceGroupManagerAutoHealingPolicies(n, hck string, initialDelaySec int64) 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) + + manager, err := config.clientComputeBeta.RegionInstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["region"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if len(manager.AutoHealingPolicies) != 1 { + return fmt.Errorf("Expected # of auto healing policies to be 1, got %d", len(manager.AutoHealingPolicies)) + } + autoHealingPolicy := manager.AutoHealingPolicies[0] + + if !strings.Contains(autoHealingPolicy.HealthCheck, hck) { + return fmt.Errorf("Expected string \"%s\" to appear in \"%s\"", hck, autoHealingPolicy.HealthCheck) + } + + if autoHealingPolicy.InitialDelaySec != initialDelaySec { + return fmt.Errorf("Expected auto healing policy inital delay to be %d, got %d", initialDelaySec, autoHealingPolicy.InitialDelaySec) + } + return nil + } +} + +func testAccCheckRegionInstanceGroupManagerDistributionPolicy(n string, zones []string) 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) + + manager, err := config.clientComputeBeta.RegionInstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["region"], rs.Primary.ID).Do() + if err != nil { + return err + } + + if manager.DistributionPolicy == nil { + return fmt.Errorf("Expected distribution policy to exist") + } + + zoneConfigs := manager.DistributionPolicy.Zones + if len(zoneConfigs) != len(zones) { + return fmt.Errorf("Expected number of zones in distribution policy to match; had %d, expected %d", len(zoneConfigs), len(zones)) + } + + sort.Strings(zones) + sortedExisting := make([]string, 0) + for _, zone := range zoneConfigs { + sortedExisting = append(sortedExisting, zone.Zone) + } + sort.Strings(sortedExisting) + + for i := 0; i < len(zones); i++ { + if !strings.HasSuffix(sortedExisting[i], zones[i]) { + return fmt.Errorf("found mismatched zone configuration: expected entry #%d as '%s', got %s", i, zones[i], sortedExisting[i]) + } + } + + return nil + } +} + +func testAccCheckRegionInstanceGroupManagerTemplateTags(n string, tags []string) 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) + + manager, err := config.clientCompute.RegionInstanceGroupManagers.Get( + config.Project, rs.Primary.Attributes["region"], rs.Primary.ID).Do() + if err != nil { + return err + } + + // check that the instance template updated + instanceTemplate, err := config.clientCompute.InstanceTemplates.Get( + config.Project, GetResourceNameFromSelfLink(manager.InstanceTemplate)).Do() + if err != nil { + return fmt.Errorf("Error reading instance template: %s", err) + } + + if !reflect.DeepEqual(instanceTemplate.Properties.Tags.Items, tags) { + return fmt.Errorf("instance template not updated") + } + + return nil + } +} + +func testAccRegionInstanceGroupManager_basic(template, target, igm1, igm2 string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_target_pool" "igm-basic" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" + } + + resource "google_compute_region_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] + base_instance_name = "igm-basic" + region = "us-central1" + target_size = 2 + } + + resource "google_compute_region_instance_group_manager" "igm-no-tp" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm-no-tp" + region = "us-central1" + target_size = 2 + } + `, template, target, igm1, igm2) +} + +func testAccRegionInstanceGroupManager_targetSizeZero(template, igm string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_region_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm-basic" + region = "us-central1" + } + `, template, igm) +} + +func testAccRegionInstanceGroupManager_update(template, target, igm string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-update" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_target_pool" "igm-update" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" + } + + resource "google_compute_region_instance_group_manager" "igm-update" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-update.self_link}" + target_pools = ["${google_compute_target_pool.igm-update.self_link}"] + base_instance_name = "igm-update" + region = "us-central1" + target_size = 2 + named_port { + name = "customhttp" + port = 8080 + } + }`, template, target, igm) +} + +// Change IGM's instance template and target size +func testAccRegionInstanceGroupManager_update2(template1, target1, target2, template2, igm string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-update" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_target_pool" "igm-update" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" + } + + resource "google_compute_target_pool" "igm-update2" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" + } + + resource "google_compute_instance_template" "igm-update2" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_region_instance_group_manager" "igm-update" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-update2.self_link}" + target_pools = [ + "${google_compute_target_pool.igm-update.self_link}", + "${google_compute_target_pool.igm-update2.self_link}", + ] + base_instance_name = "igm-update" + region = "us-central1" + target_size = 3 + named_port { + name = "customhttp" + port = 8080 + } + named_port { + name = "customhttps" + port = 8443 + } + }`, template1, target1, target2, template2, igm) +} + +func testAccRegionInstanceGroupManager_updateLifecycle(tag, igm string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-update" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["%s"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + + lifecycle { + create_before_destroy = true + } + } + + resource "google_compute_region_instance_group_manager" "igm-update" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-update.self_link}" + base_instance_name = "igm-update" + region = "us-central1" + target_size = 2 + named_port { + name = "customhttp" + port = 8080 + } + }`, tag, igm) +} + +func testAccRegionInstanceGroupManager_separateRegions(igm1, igm2 string) string { + return fmt.Sprintf(` + data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" + } + + resource "google_compute_instance_template" "igm-basic" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + } + + resource "google_compute_region_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm-basic" + region = "us-central1" + target_size = 2 + } + + resource "google_compute_region_instance_group_manager" "igm-basic-2" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm-basic-2" + region = "us-west1" + target_size = 2 + } + `, igm1, igm2) +} + +func testAccRegionInstanceGroupManager_autoHealingPolicies(template, target, igm, hck string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + metadata { + foo = "bar" + } + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_target_pool" "igm-basic" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" +} + +resource "google_compute_region_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] + base_instance_name = "igm-basic" + region = "us-central1" + target_size = 2 + auto_healing_policies { + health_check = "${google_compute_http_health_check.zero.self_link}" + initial_delay_sec = "10" + } +} + +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + `, template, target, igm, hck) +} +func testAccRegionInstanceGroupManager_versions(primaryTemplate string, canaryTemplate string, igm string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-primary" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + metadata { + foo = "bar" + } + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_instance_template" "igm-canary" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + metadata { + foo = "bar" + } + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} + +resource "google_compute_region_instance_group_manager" "igm-basic" { + description = "Terraform test region instance group manager" + name = "%s" + base_instance_name = "igm-basic" + region = "us-central1" + target_size = 2 + + version { + name = "primary" + instance_template = "${google_compute_instance_template.igm-primary.self_link}" + } + + version { + name = "canary" + instance_template = "${google_compute_instance_template.igm-canary.self_link}" + target_size { + fixed = 1 + } + } +} + `, primaryTemplate, canaryTemplate, igm) +} + +func testAccRegionInstanceGroupManager_distributionPolicy(template, igm string, zones []string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + metadata { + foo = "bar" + } +} + +resource "google_compute_region_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm-basic" + region = "us-central1" + target_size = 2 + distribution_policy_zones = ["%s"] +} + `, template, igm, strings.Join(zones, "\",\"")) +} + +func testAccRegionInstanceGroupManager_updateStrategy(igm string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-update-strategy" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["terraform-testing"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + + lifecycle { + create_before_destroy = true + } +} + +resource "google_compute_region_instance_group_manager" "igm-update-strategy" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-update-strategy.self_link}" + base_instance_name = "rigm-update-strategy" + region = "us-central1" + target_size = 2 + update_strategy = "NONE" + named_port { + name = "customhttp" + port = 8080 + } +}`, igm) +} + +func testAccRegionInstanceGroupManager_rollingUpdatePolicy(igm string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-rolling-update-policy" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["terraform-testing"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + + lifecycle { + create_before_destroy = true + } +} + +resource "google_compute_region_instance_group_manager" "igm-rolling-update-policy" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" + base_instance_name = "igm-rolling-update-policy" + region = "us-central1" + target_size = 4 + distribution_policy_zones = ["us-central1-a", "us-central1-f"] + update_strategy = "ROLLING_UPDATE" + + rolling_update_policy { + type = "PROACTIVE" + minimal_action = "REPLACE" + max_surge_fixed = 2 + max_unavailable_fixed = 2 + min_ready_sec = 20 + } + + named_port { + name = "customhttp" + port = 8080 + } +}`, igm) +} + +func testAccRegionInstanceGroupManager_rollingUpdatePolicy2(igm string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-rolling-update-policy" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["terraform-testing"] + + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } + + lifecycle { + create_before_destroy = true + } +} + +resource "google_compute_region_instance_group_manager" "igm-rolling-update-policy" { + description = "Terraform test instance group manager" + name = "%s" + instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" + base_instance_name = "igm-rolling-update-policy" + region = "us-central1" + distribution_policy_zones = ["us-central1-a", "us-central1-f"] + target_size = 3 + update_strategy = "ROLLING_UPDATE" + + rolling_update_policy { + type = "PROACTIVE" + minimal_action = "REPLACE" + max_surge_fixed = 2 + max_unavailable_fixed = 0 + min_ready_sec = 10 + } + named_port { + name = "customhttp" + port = 8080 + } +}`, igm) +} diff --git a/provider/terraform/tests/resource_compute_route_test.go b/provider/terraform/tests/resource_compute_route_test.go new file mode 100644 index 000000000000..8cd1ce216110 --- /dev/null +++ b/provider/terraform/tests/resource_compute_route_test.go @@ -0,0 +1,204 @@ +package google + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +func TestAccComputeRoute_basic(t *testing.T) { + t.Parallel() + + var route compute.Route + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRouteDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRoute_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRouteExists( + "google_compute_route.foobar", &route), + resource.TestMatchResourceAttr( + "google_compute_route.foobar", "description", regexp.MustCompile("This is a route")), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_route.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRoute_defaultInternetGateway(t *testing.T) { + t.Parallel() + + var route compute.Route + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRouteDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRoute_defaultInternetGateway(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRouteExists( + "google_compute_route.foobar", &route), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_route.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRoute_hopInstance(t *testing.T) { + var route compute.Route + + instanceName := "tf" + acctest.RandString(10) + zone := "us-central1-b" + instanceNameRegexp := regexp.MustCompile(fmt.Sprintf("projects/(.+)/zones/%s/instances/%s$", zone, instanceName)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRouteDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRoute_hopInstance(instanceName, zone), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRouteExists( + "google_compute_route.foobar", &route), + resource.TestMatchResourceAttr("google_compute_route.foobar", "next_hop_instance", instanceNameRegexp), + resource.TestMatchResourceAttr("google_compute_route.foobar", "next_hop_instance", instanceNameRegexp), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_route.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckComputeRouteDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_route" { + continue + } + + _, err := config.clientCompute.Routes.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Route still exists") + } + } + + return nil +} + +func testAccCheckComputeRouteExists(n string, route *compute.Route) 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) + + found, err := config.clientCompute.Routes.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Route not found") + } + + *route = *found + + return nil + } +} + +func testAccComputeRoute_basic() string { + return fmt.Sprintf(` +resource "google_compute_network" "foobar" { + name = "route-test-%s" + auto_create_subnetworks = false + ipv4_range = "10.0.0.0/16" +} + +resource "google_compute_route" "foobar" { + name = "route-test-%s" + description = "This is a route" + dest_range = "15.0.0.0/24" + network = "${google_compute_network.foobar.name}" + next_hop_ip = "10.0.1.5" +}`, acctest.RandString(10), acctest.RandString(10)) +} + +func testAccComputeRoute_defaultInternetGateway() string { + return fmt.Sprintf(` +resource "google_compute_route" "foobar" { + name = "route-test-%s" + dest_range = "0.0.0.0/0" + network = "default" + next_hop_gateway = "default-internet-gateway" + priority = 100 +}`, acctest.RandString(10)) +} + +func testAccComputeRoute_hopInstance(instanceName, zone string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foo" { + name = "%s" + machine_type = "n1-standard-1" + zone = "%s" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } +} + +resource "google_compute_route" "foobar" { + name = "route-test-%s" + dest_range = "0.0.0.0/0" + network = "default" + next_hop_instance = "${google_compute_instance.foo.name}" + next_hop_instance_zone = "${google_compute_instance.foo.zone}" + priority = 100 +}`, instanceName, zone, acctest.RandString(10)) +} diff --git a/provider/terraform/tests/resource_compute_router_interface_test.go b/provider/terraform/tests/resource_compute_router_interface_test.go new file mode 100644 index 000000000000..ca383b213de2 --- /dev/null +++ b/provider/terraform/tests/resource_compute_router_interface_test.go @@ -0,0 +1,289 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeRouterInterface_basic(t *testing.T) { + t.Parallel() + + testId := acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRouterInterfaceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRouterInterfaceBasic(testId), + Check: testAccCheckComputeRouterInterfaceExists( + "google_compute_router_interface.foobar"), + }, + resource.TestStep{ + ResourceName: "google_compute_router_interface.foobar", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAccComputeRouterInterfaceKeepRouter(testId), + Check: testAccCheckComputeRouterInterfaceDelete( + "google_compute_router_interface.foobar"), + }, + }, + }) +} + +func testAccCheckComputeRouterInterfaceDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + routersService := config.clientCompute.Routers + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_router" { + continue + } + + project, err := getTestProject(rs.Primary, config) + if err != nil { + return err + } + + region, err := getTestRegion(rs.Primary, config) + if err != nil { + return err + } + + routerName := rs.Primary.Attributes["router"] + + _, err = routersService.Get(project, region, routerName).Do() + + if err == nil { + return fmt.Errorf("Error, Router %s in region %s still exists", + routerName, region) + } + } + + return nil +} + +func testAccCheckComputeRouterInterfaceDelete(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + routersService := config.clientCompute.Routers + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_router_interface" { + continue + } + + project, err := getTestProject(rs.Primary, config) + if err != nil { + return err + } + + region, err := getTestRegion(rs.Primary, config) + if err != nil { + return err + } + + name := rs.Primary.Attributes["name"] + routerName := rs.Primary.Attributes["router"] + + router, err := routersService.Get(project, region, routerName).Do() + + if err != nil { + return fmt.Errorf("Error Reading Router %s: %s", routerName, err) + } + + ifaces := router.Interfaces + for _, iface := range ifaces { + + if iface.Name == name { + return fmt.Errorf("Interface %s still exists on router %s/%s", name, region, router.Name) + } + } + } + + return nil + } +} + +func testAccCheckComputeRouterInterfaceExists(n string) 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) + + project, err := getTestProject(rs.Primary, config) + if err != nil { + return err + } + + region, err := getTestRegion(rs.Primary, config) + if err != nil { + return err + } + + name := rs.Primary.Attributes["name"] + routerName := rs.Primary.Attributes["router"] + + routersService := config.clientCompute.Routers + router, err := routersService.Get(project, region, routerName).Do() + + if err != nil { + return fmt.Errorf("Error Reading Router %s: %s", routerName, err) + } + + for _, iface := range router.Interfaces { + + if iface.Name == name { + return nil + } + } + + return fmt.Errorf("Interface %s not found for router %s", name, router.Name) + } +} + +func testAccComputeRouterInterfaceBasic(testId string) string { + return fmt.Sprintf(` + resource "google_compute_network" "foobar" { + name = "router-interface-test-%s" + } + resource "google_compute_subnetwork" "foobar" { + name = "router-interface-test-subnetwork-%s" + network = "${google_compute_network.foobar.self_link}" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + } + resource "google_compute_address" "foobar" { + name = "router-interface-test-%s" + region = "${google_compute_subnetwork.foobar.region}" + } + resource "google_compute_vpn_gateway" "foobar" { + name = "router-interface-test-%s" + network = "${google_compute_network.foobar.self_link}" + region = "${google_compute_subnetwork.foobar.region}" + } + resource "google_compute_forwarding_rule" "foobar_esp" { + name = "router-interface-test-%s-1" + region = "${google_compute_vpn_gateway.foobar.region}" + ip_protocol = "ESP" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_forwarding_rule" "foobar_udp500" { + name = "router-interface-test-%s-2" + region = "${google_compute_forwarding_rule.foobar_esp.region}" + ip_protocol = "UDP" + port_range = "500-500" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_forwarding_rule" "foobar_udp4500" { + name = "router-interface-test-%s-3" + region = "${google_compute_forwarding_rule.foobar_udp500.region}" + ip_protocol = "UDP" + port_range = "4500-4500" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_router" "foobar"{ + name = "router-interface-test-%s" + region = "${google_compute_forwarding_rule.foobar_udp500.region}" + network = "${google_compute_network.foobar.self_link}" + bgp { + asn = 64514 + } + } + resource "google_compute_vpn_tunnel" "foobar" { + name = "router-interface-test-%s" + region = "${google_compute_forwarding_rule.foobar_udp4500.region}" + target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}" + shared_secret = "unguessable" + peer_ip = "8.8.8.8" + router = "${google_compute_router.foobar.name}" + } + resource "google_compute_router_interface" "foobar" { + name = "router-interface-test-%s" + router = "${google_compute_router.foobar.name}" + region = "${google_compute_router.foobar.region}" + ip_range = "169.254.3.1/30" + vpn_tunnel = "${google_compute_vpn_tunnel.foobar.name}" + } + `, testId, testId, testId, testId, testId, testId, testId, testId, testId, testId) +} + +func testAccComputeRouterInterfaceKeepRouter(testId string) string { + return fmt.Sprintf(` + resource "google_compute_network" "foobar" { + name = "router-interface-test-%s" + } + resource "google_compute_subnetwork" "foobar" { + name = "router-interface-test-subnetwork-%s" + network = "${google_compute_network.foobar.self_link}" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + } + resource "google_compute_address" "foobar" { + name = "router-interface-test-%s" + region = "${google_compute_subnetwork.foobar.region}" + } + resource "google_compute_vpn_gateway" "foobar" { + name = "router-interface-test-%s" + network = "${google_compute_network.foobar.self_link}" + region = "${google_compute_subnetwork.foobar.region}" + } + resource "google_compute_forwarding_rule" "foobar_esp" { + name = "router-interface-test-%s-1" + region = "${google_compute_vpn_gateway.foobar.region}" + ip_protocol = "ESP" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_forwarding_rule" "foobar_udp500" { + name = "router-interface-test-%s-2" + region = "${google_compute_forwarding_rule.foobar_esp.region}" + ip_protocol = "UDP" + port_range = "500-500" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_forwarding_rule" "foobar_udp4500" { + name = "router-interface-test-%s-3" + region = "${google_compute_forwarding_rule.foobar_udp500.region}" + ip_protocol = "UDP" + port_range = "4500-4500" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_router" "foobar"{ + name = "router-interface-test-%s" + region = "${google_compute_forwarding_rule.foobar_udp500.region}" + network = "${google_compute_network.foobar.self_link}" + bgp { + asn = 64514 + } + } + resource "google_compute_vpn_tunnel" "foobar" { + name = "router-interface-test-%s" + region = "${google_compute_forwarding_rule.foobar_udp4500.region}" + target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}" + shared_secret = "unguessable" + peer_ip = "8.8.8.8" + router = "${google_compute_router.foobar.name}" + } + `, testId, testId, testId, testId, testId, testId, testId, testId, testId) +} diff --git a/provider/terraform/tests/resource_compute_router_peer_test.go b/provider/terraform/tests/resource_compute_router_peer_test.go new file mode 100644 index 000000000000..04c89f6da8ec --- /dev/null +++ b/provider/terraform/tests/resource_compute_router_peer_test.go @@ -0,0 +1,305 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeRouterPeer_basic(t *testing.T) { + t.Parallel() + + testId := acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeRouterPeerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeRouterPeerBasic(testId), + Check: testAccCheckComputeRouterPeerExists( + "google_compute_router_peer.foobar"), + }, + resource.TestStep{ + ResourceName: "google_compute_router_peer.foobar", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAccComputeRouterPeerKeepRouter(testId), + Check: testAccCheckComputeRouterPeerDelete( + "google_compute_router_peer.foobar"), + }, + }, + }) +} + +func testAccCheckComputeRouterPeerDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + routersService := config.clientCompute.Routers + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_router" { + continue + } + + project, err := getTestProject(rs.Primary, config) + if err != nil { + return err + } + + region, err := getTestRegion(rs.Primary, config) + if err != nil { + return err + } + + routerName := rs.Primary.Attributes["router"] + + _, err = routersService.Get(project, region, routerName).Do() + + if err == nil { + return fmt.Errorf("Error, Router %s in region %s still exists", + routerName, region) + } + } + + return nil +} + +func testAccCheckComputeRouterPeerDelete(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + routersService := config.clientCompute.Routers + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_router_peer" { + continue + } + + project, err := getTestProject(rs.Primary, config) + if err != nil { + return err + } + + region, err := getTestRegion(rs.Primary, config) + if err != nil { + return err + } + + name := rs.Primary.Attributes["name"] + routerName := rs.Primary.Attributes["router"] + + router, err := routersService.Get(project, region, routerName).Do() + + if err != nil { + return fmt.Errorf("Error Reading Router %s: %s", routerName, err) + } + + peers := router.BgpPeers + for _, peer := range peers { + + if peer.Name == name { + return fmt.Errorf("Peer %s still exists on router %s/%s", name, region, router.Name) + } + } + } + + return nil + } +} + +func testAccCheckComputeRouterPeerExists(n string) 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) + + project, err := getTestProject(rs.Primary, config) + if err != nil { + return err + } + + region, err := getTestRegion(rs.Primary, config) + if err != nil { + return err + } + + name := rs.Primary.Attributes["name"] + routerName := rs.Primary.Attributes["router"] + + routersService := config.clientCompute.Routers + router, err := routersService.Get(project, region, routerName).Do() + + if err != nil { + return fmt.Errorf("Error Reading Router %s: %s", routerName, err) + } + + for _, peer := range router.BgpPeers { + + if peer.Name == name { + return nil + } + } + + return fmt.Errorf("Peer %s not found for router %s", name, router.Name) + } +} + +func testAccComputeRouterPeerBasic(testId string) string { + return fmt.Sprintf(` + resource "google_compute_network" "foobar" { + name = "router-peer-test-%s" + } + resource "google_compute_subnetwork" "foobar" { + name = "router-peer-test-subnetwork-%s" + network = "${google_compute_network.foobar.self_link}" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + } + resource "google_compute_address" "foobar" { + name = "router-peer-test-%s" + region = "${google_compute_subnetwork.foobar.region}" + } + resource "google_compute_vpn_gateway" "foobar" { + name = "router-peer-test-%s" + network = "${google_compute_network.foobar.self_link}" + region = "${google_compute_subnetwork.foobar.region}" + } + resource "google_compute_forwarding_rule" "foobar_esp" { + name = "router-peer-test-%s-1" + region = "${google_compute_vpn_gateway.foobar.region}" + ip_protocol = "ESP" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_forwarding_rule" "foobar_udp500" { + name = "router-peer-test-%s-2" + region = "${google_compute_forwarding_rule.foobar_esp.region}" + ip_protocol = "UDP" + port_range = "500-500" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_forwarding_rule" "foobar_udp4500" { + name = "router-peer-test-%s-3" + region = "${google_compute_forwarding_rule.foobar_udp500.region}" + ip_protocol = "UDP" + port_range = "4500-4500" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_router" "foobar"{ + name = "router-peer-test-%s" + region = "${google_compute_forwarding_rule.foobar_udp500.region}" + network = "${google_compute_network.foobar.self_link}" + bgp { + asn = 64514 + } + } + resource "google_compute_vpn_tunnel" "foobar" { + name = "router-peer-test-%s" + region = "${google_compute_forwarding_rule.foobar_udp4500.region}" + target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}" + shared_secret = "unguessable" + peer_ip = "8.8.8.8" + router = "${google_compute_router.foobar.name}" + } + resource "google_compute_router_interface" "foobar" { + name = "router-peer-test-%s" + router = "${google_compute_router.foobar.name}" + region = "${google_compute_router.foobar.region}" + ip_range = "169.254.3.1/30" + vpn_tunnel = "${google_compute_vpn_tunnel.foobar.name}" + } + resource "google_compute_router_peer" "foobar" { + name = "router-peer-test-%s" + router = "${google_compute_router.foobar.name}" + region = "${google_compute_router.foobar.region}" + peer_ip_address = "169.254.3.2" + peer_asn = 65515 + advertised_route_priority = 100 + interface = "${google_compute_router_interface.foobar.name}" + } + `, testId, testId, testId, testId, testId, testId, testId, testId, testId, testId, testId) +} + +func testAccComputeRouterPeerKeepRouter(testId string) string { + return fmt.Sprintf(` + resource "google_compute_network" "foobar" { + name = "router-peer-test-%s" + } + resource "google_compute_subnetwork" "foobar" { + name = "router-peer-test-subnetwork-%s" + network = "${google_compute_network.foobar.self_link}" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + } + resource "google_compute_address" "foobar" { + name = "router-peer-test-%s" + region = "${google_compute_subnetwork.foobar.region}" + } + resource "google_compute_vpn_gateway" "foobar" { + name = "router-peer-test-%s" + network = "${google_compute_network.foobar.self_link}" + region = "${google_compute_subnetwork.foobar.region}" + } + resource "google_compute_forwarding_rule" "foobar_esp" { + name = "router-peer-test-%s-1" + region = "${google_compute_vpn_gateway.foobar.region}" + ip_protocol = "ESP" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_forwarding_rule" "foobar_udp500" { + name = "router-peer-test-%s-2" + region = "${google_compute_forwarding_rule.foobar_esp.region}" + ip_protocol = "UDP" + port_range = "500-500" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_forwarding_rule" "foobar_udp4500" { + name = "router-peer-test-%s-3" + region = "${google_compute_forwarding_rule.foobar_udp500.region}" + ip_protocol = "UDP" + port_range = "4500-4500" + ip_address = "${google_compute_address.foobar.address}" + target = "${google_compute_vpn_gateway.foobar.self_link}" + } + resource "google_compute_router" "foobar"{ + name = "router-peer-test-%s" + region = "${google_compute_forwarding_rule.foobar_udp500.region}" + network = "${google_compute_network.foobar.self_link}" + bgp { + asn = 64514 + } + } + resource "google_compute_vpn_tunnel" "foobar" { + name = "router-peer-test-%s" + region = "${google_compute_forwarding_rule.foobar_udp4500.region}" + target_vpn_gateway = "${google_compute_vpn_gateway.foobar.self_link}" + shared_secret = "unguessable" + peer_ip = "8.8.8.8" + router = "${google_compute_router.foobar.name}" + } + resource "google_compute_router_interface" "foobar" { + name = "router-peer-test-%s" + router = "${google_compute_router.foobar.name}" + region = "${google_compute_router.foobar.region}" + ip_range = "169.254.3.1/30" + vpn_tunnel = "${google_compute_vpn_tunnel.foobar.name}" + } + `, testId, testId, testId, testId, testId, testId, testId, testId, testId, testId) +} diff --git a/provider/terraform/tests/resource_compute_security_policy_test.go b/provider/terraform/tests/resource_compute_security_policy_test.go new file mode 100644 index 000000000000..e1d6834e2ba3 --- /dev/null +++ b/provider/terraform/tests/resource_compute_security_policy_test.go @@ -0,0 +1,202 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeSecurityPolicy_basic(t *testing.T) { + t.Parallel() + + spName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSecurityPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSecurityPolicy_basic(spName), + }, + resource.TestStep{ + ResourceName: "google_compute_security_policy.policy", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSecurityPolicy_withRule(t *testing.T) { + t.Parallel() + + spName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSecurityPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSecurityPolicy_withRule(spName), + }, + resource.TestStep{ + ResourceName: "google_compute_security_policy.policy", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSecurityPolicy_update(t *testing.T) { + t.Parallel() + + spName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSecurityPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSecurityPolicy_withRule(spName), + }, + resource.TestStep{ + ResourceName: "google_compute_security_policy.policy", + ImportState: true, + ImportStateVerify: true, + }, + + resource.TestStep{ + Config: testAccComputeSecurityPolicy_update(spName), + }, + resource.TestStep{ + ResourceName: "google_compute_security_policy.policy", + ImportState: true, + ImportStateVerify: true, + }, + + resource.TestStep{ + Config: testAccComputeSecurityPolicy_withRule(spName), + }, + resource.TestStep{ + ResourceName: "google_compute_security_policy.policy", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckComputeSecurityPolicyDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_security_policy" { + continue + } + + pol := rs.Primary.ID + + _, err := config.clientComputeBeta.SecurityPolicies.Get(config.Project, pol).Do() + if err == nil { + return fmt.Errorf("Security policy %q still exists", pol) + } + } + + return nil +} + +func testAccComputeSecurityPolicy_basic(spName string) string { + return fmt.Sprintf(` +resource "google_compute_security_policy" "policy" { + name = "%s" + description = "basic security policy" +} +`, spName) +} + +func testAccComputeSecurityPolicy_withRule(spName string) string { + return fmt.Sprintf(` +resource "google_compute_security_policy" "policy" { + name = "%s" + + rule { + action = "allow" + priority = "2147483647" + match { + versioned_expr = "SRC_IPS_V1" + config { + src_ip_ranges = ["*"] + } + } + description = "default rule" + } + + rule { + action = "allow" + priority = "2000" + match { + versioned_expr = "SRC_IPS_V1" + config { + src_ip_ranges = ["10.0.0.0/24"] + } + } + preview = true + } +} +`, spName) +} + +func testAccComputeSecurityPolicy_update(spName string) string { + return fmt.Sprintf(` +resource "google_compute_security_policy" "policy" { + name = "%s" + description = "updated description" + + // keep this + rule { + action = "allow" + priority = "2147483647" + match { + versioned_expr = "SRC_IPS_V1" + config { + src_ip_ranges = ["*"] + } + } + description = "default rule" + } + + // add this + rule { + action = "deny(403)" + priority = "1000" + match { + versioned_expr = "SRC_IPS_V1" + config { + src_ip_ranges = ["10.0.1.0/24"] + } + } + } + + // update this + rule { + action = "allow" + priority = "2000" + match { + versioned_expr = "SRC_IPS_V1" + config { + src_ip_ranges = ["10.0.0.0/24"] + } + } + description = "updated description" + preview = false + } +} +`, spName) +} diff --git a/provider/terraform/tests/resource_compute_shared_vpc_test.go b/provider/terraform/tests/resource_compute_shared_vpc_test.go new file mode 100644 index 000000000000..6c31dcdd6943 --- /dev/null +++ b/provider/terraform/tests/resource_compute_shared_vpc_test.go @@ -0,0 +1,160 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeSharedVpc_basic(t *testing.T) { + org := getTestOrgFromEnv(t) + billingId := getTestBillingAccountFromEnv(t) + + hostProject := "xpn-host-" + acctest.RandString(10) + serviceProject := "xpn-service-" + acctest.RandString(10) + + hostProjectResourceName := "google_compute_shared_vpc_host_project.host" + serviceProjectResourceName := "google_compute_shared_vpc_service_project.service" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSharedVpc_basic(hostProject, serviceProject, org, billingId), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSharedVpcHostProject(hostProject, true), + testAccCheckComputeSharedVpcServiceProject(hostProject, serviceProject, true), + ), + }, + // Test import. + resource.TestStep{ + ResourceName: hostProjectResourceName, + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + ResourceName: serviceProjectResourceName, + ImportState: true, + ImportStateVerify: true, + }, + // Use a separate TestStep rather than a CheckDestroy because we need the project to still exist. + resource.TestStep{ + Config: testAccComputeSharedVpc_disabled(hostProject, serviceProject, org, billingId), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSharedVpcHostProject(hostProject, false), + testAccCheckComputeSharedVpcServiceProject(hostProject, serviceProject, false), + ), + }, + }, + }) +} + +func testAccCheckComputeSharedVpcHostProject(hostProject string, enabled bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + found, err := config.clientCompute.Projects.Get(hostProject).Do() + if err != nil { + return fmt.Errorf("Error reading project %s: %s", hostProject, err) + } + + if found.Name != hostProject { + return fmt.Errorf("Project %s not found", hostProject) + } + + if enabled != (found.XpnProjectStatus == "HOST") { + return fmt.Errorf("Project %q shared VPC status was not expected, got %q", hostProject, found.XpnProjectStatus) + } + + return nil + } +} + +func testAccCheckComputeSharedVpcServiceProject(hostProject, serviceProject string, enabled bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + serviceHostProject, err := config.clientCompute.Projects.GetXpnHost(serviceProject).Do() + if err != nil { + if enabled { + return fmt.Errorf("Expected service project to be enabled.") + } + return nil + } + + if enabled != (serviceHostProject.Name == hostProject) { + return fmt.Errorf("Wrong host project for the given service project. Expected '%s', got '%s'", hostProject, serviceHostProject.Name) + } + + return nil + } +} + +func testAccComputeSharedVpc_basic(hostProject, serviceProject, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "host" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project" "service" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_service" "host" { + project = "${google_project.host.project_id}" + service = "compute.googleapis.com" +} + +resource "google_project_service" "service" { + project = "${google_project.service.project_id}" + service = "compute.googleapis.com" +} + +resource "google_compute_shared_vpc_host_project" "host" { + project = "${google_project.host.project_id}" + depends_on = ["google_project_service.host"] +} + +resource "google_compute_shared_vpc_service_project" "service" { + host_project = "${google_project.host.project_id}" + service_project = "${google_project.service.project_id}" + depends_on = ["google_compute_shared_vpc_host_project.host", "google_project_service.service"] +}`, hostProject, hostProject, org, billing, serviceProject, serviceProject, org, billing) +} + +func testAccComputeSharedVpc_disabled(hostProject, serviceProject, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "host" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project" "service" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_service" "host" { + project = "${google_project.host.project_id}" + service = "compute.googleapis.com" +} + +resource "google_project_service" "service" { + project = "${google_project.service.project_id}" + service = "compute.googleapis.com" +} +`, hostProject, hostProject, org, billing, serviceProject, serviceProject, org, billing) +} diff --git a/provider/terraform/tests/resource_compute_snapshot_test.go b/provider/terraform/tests/resource_compute_snapshot_test.go new file mode 100644 index 000000000000..9b4d4c89a716 --- /dev/null +++ b/provider/terraform/tests/resource_compute_snapshot_test.go @@ -0,0 +1,260 @@ +package google + +import ( + "fmt" + "testing" + + "reflect" + "strings" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" + "google.golang.org/api/googleapi" +) + +func TestAccComputeSnapshot_basic(t *testing.T) { + t.Parallel() + + snapshotName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var snapshot compute.Snapshot + diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSnapshotDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSnapshot_basic(snapshotName, diskName, "my-value"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSnapshotExists( + "google_compute_snapshot.foobar", &snapshot), + ), + }, + }, + }) +} + +func TestAccComputeSnapshot_update(t *testing.T) { + t.Parallel() + + snapshotName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var snapshot compute.Snapshot + diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSnapshotDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSnapshot_basic(snapshotName, diskName, "my-value"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSnapshotExists( + "google_compute_snapshot.foobar", &snapshot), + ), + }, + resource.TestStep{ + Config: testAccComputeSnapshot_basic(snapshotName, diskName, "my-updated-value"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSnapshotExists( + "google_compute_snapshot.foobar", &snapshot), + ), + }, + }, + }) +} + +func TestAccComputeSnapshot_encryption(t *testing.T) { + t.Parallel() + + snapshotName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + var snapshot compute.Snapshot + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSnapshotDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSnapshot_encryption(snapshotName, diskName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSnapshotExists( + "google_compute_snapshot.foobar", &snapshot), + ), + }, + }, + }) +} + +func testAccCheckComputeSnapshotDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_snapshot" { + continue + } + + _, err := config.clientCompute.Snapshots.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { + return nil + } else if ok { + return fmt.Errorf("Error while requesting Google Cloud Plateform: http code error : %d, http message error: %s", gerr.Code, gerr.Message) + } + return fmt.Errorf("Error while requesting Google Cloud Plateform") + } + return fmt.Errorf("Snapshot still exists") + } + + return nil +} + +func testAccCheckComputeSnapshotExists(n string, snapshot *compute.Snapshot) 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) + + found, err := config.clientCompute.Snapshots.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Snapshot %s not found", n) + } + + attr := rs.Primary.Attributes["snapshot_encryption_key_sha256"] + if found.SnapshotEncryptionKey != nil && found.SnapshotEncryptionKey.Sha256 != attr { + return fmt.Errorf("Snapshot %s has mismatched encryption key (Sha256).\nTF State: %+v.\nGCP State: %+v", + n, attr, found.SnapshotEncryptionKey.Sha256) + } else if found.SnapshotEncryptionKey == nil && attr != "" { + return fmt.Errorf("Snapshot %s has mismatched encryption key.\nTF State: %+v.\nGCP State: %+v", + n, attr, found.SnapshotEncryptionKey) + } + + attr = rs.Primary.Attributes["source_disk_encryption_key_sha256"] + if found.SourceDiskEncryptionKey != nil && found.SourceDiskEncryptionKey.Sha256 != attr { + return fmt.Errorf("Snapshot %s has mismatched source disk encryption key (Sha256).\nTF State: %+v.\nGCP State: %+v", + n, attr, found.SourceDiskEncryptionKey.Sha256) + } else if found.SourceDiskEncryptionKey == nil && attr != "" { + return fmt.Errorf("Snapshot %s has mismatched source disk encryption key.\nTF State: %+v.\nGCP State: %+v", + n, attr, found.SourceDiskEncryptionKey) + } + + attr = rs.Primary.Attributes["source_disk_link"] + if found.SourceDisk != attr { + return fmt.Errorf("Snapshot %s has mismatched source disk link.\nTF State: %+v.\nGCP State: %+v", + n, attr, found.SourceDisk) + } + + foundDisk, errDisk := config.clientCompute.Disks.Get( + config.Project, rs.Primary.Attributes["zone"], rs.Primary.Attributes["source_disk"]).Do() + if errDisk != nil { + return errDisk + } + if foundDisk.SelfLink != attr { + return fmt.Errorf("Snapshot %s has mismatched source disk\nTF State: %+v.\nGCP State: %+v", + n, attr, foundDisk.SelfLink) + } + + attr = rs.Primary.Attributes["self_link"] + if found.SelfLink != attr { + return fmt.Errorf("Snapshot %s has mismatched self link.\nTF State: %+v.\nGCP State: %+v", + n, attr, found.SelfLink) + } + + // We should have a map + attr, ok = rs.Primary.Attributes["labels.%"] + if !ok { + return fmt.Errorf("Snapshot %s has no labels map in attributes", n) + } + // Parse out our map + attrMap := make(map[string]string) + for k, v := range rs.Primary.Attributes { + if !strings.HasPrefix(k, "labels.") || k == "labels.%" { + continue + } + key := k[len("labels."):] + attrMap[key] = v + } + if (len(attrMap) != 0 || len(found.Labels) != 0) && !reflect.DeepEqual(attrMap, found.Labels) { + return fmt.Errorf("Snapshot %s has mismatched labels.\nTF State: %+v\nGCP State: %+v", + n, attrMap, found.Labels) + } + + attr = rs.Primary.Attributes["label_fingerprint"] + if found.LabelFingerprint != attr { + return fmt.Errorf("Snapshot %s has mismatched label fingerprint\nTF State: %+v.\nGCP State: %+v", + n, attr, found.LabelFingerprint) + } + + *snapshot = *found + + return nil + } +} + +func testAccComputeSnapshot_basic(snapshotName, diskName, labelValue string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + image = "${data.google_compute_image.my_image.self_link}" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + +resource "google_compute_snapshot" "foobar" { + name = "%s" + source_disk = "${google_compute_disk.foobar.name}" + zone = "us-central1-a" + labels = { + my_label = "%s" + } +}`, diskName, snapshotName, labelValue) +} + +func testAccComputeSnapshot_encryption(snapshotName string, diskName string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + image = "${data.google_compute_image.my_image.self_link}" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" + disk_encryption_key { + raw_key = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=" + } +} +resource "google_compute_snapshot" "foobar" { + name = "%s" + source_disk = "${google_compute_disk.foobar.name}" + zone = "us-central1-a" + source_disk_encryption_key_raw = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=" + snapshot_encryption_key_raw = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=" +}`, diskName, snapshotName) +} diff --git a/provider/terraform/tests/resource_compute_ssl_policy_test.go b/provider/terraform/tests/resource_compute_ssl_policy_test.go new file mode 100644 index 000000000000..66843746c92f --- /dev/null +++ b/provider/terraform/tests/resource_compute_ssl_policy_test.go @@ -0,0 +1,402 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + compute "google.golang.org/api/compute/v1" +) + +func TestAccComputeSslPolicy_basic(t *testing.T) { + t.Parallel() + + var sslPolicy compute.SslPolicy + sslPolicyName := fmt.Sprintf("test-ssl-policy-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSslPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSslPolicyBasic(sslPolicyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSslPolicyExists( + "google_compute_ssl_policy.basic", &sslPolicy), + // test attribute default values + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.basic", "profile", "COMPATIBLE"), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.basic", "min_tls_version", "TLS_1_0"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_ssl_policy.basic", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSslPolicy_profile(t *testing.T) { + t.Parallel() + + var sslPolicy compute.SslPolicy + sslPolicyName := fmt.Sprintf("test-ssl-policy-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSslPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSslPolicyProfile(sslPolicyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSslPolicyExists( + "google_compute_ssl_policy.profile", &sslPolicy), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.profile", "profile", "MODERN"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_ssl_policy.profile", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSslPolicy_update(t *testing.T) { + t.Parallel() + + var sslPolicy compute.SslPolicy + sslPolicyName := fmt.Sprintf("test-ssl-policy-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSslPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSslUpdate1(sslPolicyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSslPolicyExists( + "google_compute_ssl_policy.update", &sslPolicy), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "profile", "MODERN"), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "min_tls_version", "TLS_1_0"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_ssl_policy.update", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAccComputeSslUpdate2(sslPolicyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSslPolicyExists( + "google_compute_ssl_policy.update", &sslPolicy), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "profile", "RESTRICTED"), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "min_tls_version", "TLS_1_2"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_ssl_policy.update", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSslPolicy_tls_version(t *testing.T) { + t.Parallel() + + var sslPolicy compute.SslPolicy + sslPolicyName := fmt.Sprintf("test-ssl-policy-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSslPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSslPolicyTlsVersion(sslPolicyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSslPolicyExists( + "google_compute_ssl_policy.tlsversion", &sslPolicy), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.tlsversion", "min_tls_version", "TLS_1_2"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_ssl_policy.tlsversion", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSslPolicy_custom(t *testing.T) { + t.Parallel() + + var sslPolicy compute.SslPolicy + sslPolicyName := fmt.Sprintf("test-ssl-policy-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSslPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSslPolicyCustom(sslPolicyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSslPolicyExists( + "google_compute_ssl_policy.custom", &sslPolicy), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.custom", "min_tls_version", "TLS_1_2"), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.custom", "profile", "CUSTOM"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_ssl_policy.custom", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSslPolicy_update_to_custom(t *testing.T) { + t.Parallel() + + var sslPolicy compute.SslPolicy + sslPolicyName := fmt.Sprintf("test-ssl-policy-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSslPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSslUpdate1(sslPolicyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSslPolicyExists( + "google_compute_ssl_policy.update", &sslPolicy), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "profile", "MODERN"), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "min_tls_version", "TLS_1_0"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_ssl_policy.update", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAccComputeSslUpdate3(sslPolicyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSslPolicyExists( + "google_compute_ssl_policy.update", &sslPolicy), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "profile", "CUSTOM"), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "min_tls_version", "TLS_1_1"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_ssl_policy.update", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSslPolicy_update_from_custom(t *testing.T) { + t.Parallel() + + var sslPolicy compute.SslPolicy + sslPolicyName := fmt.Sprintf("test-ssl-policy-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSslPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSslUpdate3(sslPolicyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSslPolicyExists( + "google_compute_ssl_policy.update", &sslPolicy), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "profile", "CUSTOM"), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "min_tls_version", "TLS_1_1"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_ssl_policy.update", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAccComputeSslUpdate1(sslPolicyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSslPolicyExists( + "google_compute_ssl_policy.update", &sslPolicy), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "profile", "MODERN"), + resource.TestCheckResourceAttr( + "google_compute_ssl_policy.update", "min_tls_version", "TLS_1_0"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_ssl_policy.update", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckComputeSslPolicyExists(n string, sslPolicy *compute.SslPolicy) 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) + + project, err := getTestProject(rs.Primary, config) + if err != nil { + return err + } + + name := rs.Primary.Attributes["name"] + + found, err := config.clientCompute.SslPolicies.Get( + project, name).Do() + if err != nil { + return fmt.Errorf("Error Reading SSL Policy %s: %s", name, err) + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("SSL Policy not found") + } + + *sslPolicy = *found + + return nil + } +} + +func testAccCheckComputeSslPolicyDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_ssl_policy" { + continue + } + + _, err := config.clientCompute.SslPolicies.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("SSL Policy still exists") + } + } + + return nil +} + +func testAccComputeSslPolicyBasic(resourceName string) string { + return fmt.Sprintf(` +resource "google_compute_ssl_policy" "basic" { + name = "%s" + description = "Generated by TF provider acceptance test" +} +`, resourceName) +} + +func testAccComputeSslPolicyProfile(resourceName string) string { + return fmt.Sprintf(` +resource "google_compute_ssl_policy" "profile" { + name = "%s" + description = "Generated by TF provider acceptance test" + profile = "MODERN" +} +`, resourceName) +} + +func testAccComputeSslUpdate1(resourceName string) string { + return fmt.Sprintf(` +resource "google_compute_ssl_policy" "update" { + name = "%s" + description = "Generated by TF provider acceptance test" + min_tls_version = "TLS_1_0" + profile = "MODERN" +} +`, resourceName) +} + +func testAccComputeSslUpdate2(resourceName string) string { + return fmt.Sprintf(` +resource "google_compute_ssl_policy" "update" { + name = "%s" + description = "Generated by TF provider acceptance test" + min_tls_version = "TLS_1_2" + profile = "RESTRICTED" +} +`, resourceName) +} + +func testAccComputeSslUpdate3(resourceName string) string { + return fmt.Sprintf(` +resource "google_compute_ssl_policy" "update" { + name = "%s" + description = "Generated by TF provider acceptance test" + min_tls_version = "TLS_1_1" + profile = "CUSTOM" + custom_features = ["TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"] +} +`, resourceName) +} + +func testAccComputeSslPolicyTlsVersion(resourceName string) string { + return fmt.Sprintf(` +resource "google_compute_ssl_policy" "tlsversion" { + name = "%s" + description = "Generated by TF provider acceptance test" + min_tls_version = "TLS_1_2" +} +`, resourceName) +} + +func testAccComputeSslPolicyCustom(resourceName string) string { + return fmt.Sprintf(` +resource "google_compute_ssl_policy" "custom" { + name = "%s" + description = "Generated by TF provider acceptance test" + min_tls_version = "TLS_1_2" + profile = "CUSTOM" + custom_features = ["TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"] +} +`, resourceName) +} diff --git a/provider/terraform/tests/resource_compute_subnetwork_iam_test.go b/provider/terraform/tests/resource_compute_subnetwork_iam_test.go new file mode 100644 index 000000000000..1c6cf705598f --- /dev/null +++ b/provider/terraform/tests/resource_compute_subnetwork_iam_test.go @@ -0,0 +1,247 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccComputeSubnetworkIamBinding(t *testing.T) { + t.Parallel() + + account := acctest.RandomWithPrefix("tf-test") + role := "roles/compute.networkUser" + region := getTestRegionFromEnv() + subnetwork := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccComputeSubnetworkIamBinding_basic(account, region, subnetwork, role), + }, + resource.TestStep{ + ResourceName: "google_compute_subnetwork_iam_binding.foo", + ImportStateId: fmt.Sprintf("%s/%s %s", region, subnetwork, role), + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Iam Binding update + Config: testAccComputeSubnetworkIamBinding_update(account, region, subnetwork, role), + }, + resource.TestStep{ + ResourceName: "google_compute_subnetwork_iam_binding.foo", + ImportStateId: fmt.Sprintf("%s/%s %s", region, subnetwork, role), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSubnetworkIamMember(t *testing.T) { + t.Parallel() + + project := getTestProjectFromEnv() + account := acctest.RandomWithPrefix("tf-test") + role := "roles/compute.networkUser" + region := getTestRegionFromEnv() + subnetwork := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test Iam Member creation (no update for member, no need to test) + Config: testAccComputeSubnetworkIamMember_basic(account, region, subnetwork, role), + }, + resource.TestStep{ + ResourceName: "google_compute_subnetwork_iam_member.foo", + ImportStateId: fmt.Sprintf("%s/%s %s serviceAccount:%s@%s.iam.gserviceaccount.com", region, subnetwork, role, account, project), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSubnetworkIamPolicy(t *testing.T) { + t.Parallel() + + project := getTestProjectFromEnv() + account := acctest.RandomWithPrefix("tf-test") + role := "roles/compute.networkUser" + region := getTestRegionFromEnv() + subnetwork := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccComputeSubnetworkIamPolicy_basic(account, region, subnetwork, role), + }, + // Test a few import formats + resource.TestStep{ + ResourceName: "google_compute_subnetwork_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/regions/%s/subnetworks/%s", project, region, subnetwork), + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + ResourceName: "google_compute_subnetwork_iam_policy.foo", + ImportStateId: fmt.Sprintf("%s/%s/%s", project, region, subnetwork), + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + ResourceName: "google_compute_subnetwork_iam_policy.foo", + ImportStateId: fmt.Sprintf("%s/%s", region, subnetwork), + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + ResourceName: "google_compute_subnetwork_iam_policy.foo", + ImportStateId: fmt.Sprintf("%s", subnetwork), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeSubnetworkIamBinding_basic(account, region, subnetworkName, roleId string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Iam Testing Account" +} + +resource "google_compute_network" "network" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "subnetwork" { + name = "%s" + region = "%s" + ip_cidr_range = "10.1.0.0/16" + network = "${google_compute_network.network.name}" +} + +resource "google_compute_subnetwork_iam_binding" "foo" { + project = "${google_compute_subnetwork.subnetwork.project}" + region = "${google_compute_subnetwork.subnetwork.region}" + subnetwork = "${google_compute_subnetwork.subnetwork.name}" + role = "%s" + members = ["serviceAccount:${google_service_account.test_account.email}"] +} +`, account, subnetworkName, subnetworkName, region, roleId) +} + +func testAccComputeSubnetworkIamBinding_update(account, region, subnetworkName, roleId string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Iam Testing Account" +} + +resource "google_service_account" "test_account_2" { + account_id = "%s-2" + display_name = "Iam Testing Account" +} + +resource "google_compute_network" "network" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "subnetwork" { + name = "%s" + region = "%s" + ip_cidr_range = "10.1.0.0/16" + network = "${google_compute_network.network.name}" +} + +resource "google_compute_subnetwork_iam_binding" "foo" { + project = "${google_compute_subnetwork.subnetwork.project}" + region = "${google_compute_subnetwork.subnetwork.region}" + subnetwork = "${google_compute_subnetwork.subnetwork.name}" + role = "%s" + members = [ + "serviceAccount:${google_service_account.test_account.email}", + "serviceAccount:${google_service_account.test_account_2.email}" + ] +} +`, account, account, subnetworkName, subnetworkName, region, roleId) +} + +func testAccComputeSubnetworkIamMember_basic(account, region, subnetworkName, roleId string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Iam Testing Account" +} + +resource "google_compute_network" "network" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "subnetwork" { + name = "%s" + region = "%s" + ip_cidr_range = "10.1.0.0/16" + network = "${google_compute_network.network.name}" +} + +resource "google_compute_subnetwork_iam_member" "foo" { + project = "${google_compute_subnetwork.subnetwork.project}" + region = "${google_compute_subnetwork.subnetwork.region}" + subnetwork = "${google_compute_subnetwork.subnetwork.name}" + role = "%s" + member = "serviceAccount:${google_service_account.test_account.email}" +} +`, account, subnetworkName, subnetworkName, region, roleId) +} + +func testAccComputeSubnetworkIamPolicy_basic(account, region, subnetworkName, roleId string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Iam Testing Account" +} + +resource "google_compute_network" "network" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "subnetwork" { + name = "%s" + region = "%s" + ip_cidr_range = "10.1.0.0/16" + network = "${google_compute_network.network.name}" +} + +data "google_iam_policy" "foo" { + binding { + role = "%s" + + members = ["serviceAccount:${google_service_account.test_account.email}"] + } +} + +resource "google_compute_subnetwork_iam_policy" "foo" { + project = "${google_compute_subnetwork.subnetwork.project}" + region = "${google_compute_subnetwork.subnetwork.region}" + subnetwork = "${google_compute_subnetwork.subnetwork.name}" + policy_data = "${data.google_iam_policy.foo.policy_data}" +} +`, account, subnetworkName, subnetworkName, region, roleId) +} diff --git a/provider/terraform/tests/resource_compute_subnetwork_test.go b/provider/terraform/tests/resource_compute_subnetwork_test.go new file mode 100644 index 000000000000..a5ab1dde0429 --- /dev/null +++ b/provider/terraform/tests/resource_compute_subnetwork_test.go @@ -0,0 +1,421 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/compute/v1" +) + +// Unit tests + +func TestIsShrinkageIpCidr(t *testing.T) { + cases := map[string]struct { + Old, New string + Shrinkage bool + }{ + "Expansion same network ip": { + Old: "10.0.0.0/24", + New: "10.0.0.0/16", + Shrinkage: false, + }, + "Expansion different network ip": { + Old: "10.0.1.0/24", + New: "10.0.0.0/16", + Shrinkage: false, + }, + "Shrinkage same network ip": { + Old: "10.0.0.0/16", + New: "10.0.0.0/24", + Shrinkage: true, + }, + "Shrinkage different network ip": { + Old: "10.0.0.0/16", + New: "10.1.0.0/16", + Shrinkage: true, + }, + } + + for tn, tc := range cases { + if isShrinkageIpCidr(tc.Old, tc.New, nil) != tc.Shrinkage { + t.Errorf("%s failed: Shrinkage should be %t", tn, tc.Shrinkage) + } + } +} + +// Acceptance tests + +func TestAccComputeSubnetwork_basic(t *testing.T) { + t.Parallel() + + var subnetwork1 compute.Subnetwork + var subnetwork2 compute.Subnetwork + + cnName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + subnetwork1Name := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + subnetwork2Name := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + subnetwork3Name := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSubnetworkDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSubnetwork_basic(cnName, subnetwork1Name, subnetwork2Name, subnetwork3Name), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSubnetworkExists( + "google_compute_subnetwork.network-ref-by-url", &subnetwork1), + testAccCheckComputeSubnetworkExists( + "google_compute_subnetwork.network-ref-by-name", &subnetwork2), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_subnetwork.network-ref-by-url", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + ResourceName: "google_compute_subnetwork.network-with-private-google-access", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeSubnetwork_update(t *testing.T) { + t.Parallel() + + var subnetwork compute.Subnetwork + + cnName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + subnetworkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSubnetworkDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSubnetwork_update1(cnName, "10.2.0.0/24", subnetworkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSubnetworkExists( + "google_compute_subnetwork.network-with-private-google-access", &subnetwork), + ), + }, + resource.TestStep{ + // Expand IP CIDR range and update private_ip_google_access + Config: testAccComputeSubnetwork_update2(cnName, "10.2.0.0/16", subnetworkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSubnetworkExists( + "google_compute_subnetwork.network-with-private-google-access", &subnetwork), + ), + }, + resource.TestStep{ + // Shrink IP CIDR range and update private_ip_google_access + Config: testAccComputeSubnetwork_update2(cnName, "10.2.0.0/24", subnetworkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSubnetworkExists( + "google_compute_subnetwork.network-with-private-google-access", &subnetwork), + ), + }, + }, + }) + + if subnetwork.PrivateIpGoogleAccess { + t.Errorf("Expected PrivateIpGoogleAccess to be false, got %v", subnetwork.PrivateIpGoogleAccess) + } +} + +func TestAccComputeSubnetwork_secondaryIpRanges(t *testing.T) { + t.Parallel() + + var subnetwork compute.Subnetwork + + cnName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + subnetworkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSubnetworkDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeSubnetwork_secondaryIpRanges_update1(cnName, subnetworkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSubnetworkExists("google_compute_subnetwork.network-with-private-secondary-ip-ranges", &subnetwork), + testAccCheckComputeSubnetworkHasSecondaryIpRange(&subnetwork, "tf-test-secondary-range-update1", "192.168.10.0/24"), + ), + }, + resource.TestStep{ + Config: testAccComputeSubnetwork_secondaryIpRanges_update2(cnName, subnetworkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSubnetworkExists("google_compute_subnetwork.network-with-private-secondary-ip-ranges", &subnetwork), + testAccCheckComputeSubnetworkHasSecondaryIpRange(&subnetwork, "tf-test-secondary-range-update1", "192.168.10.0/24"), + testAccCheckComputeSubnetworkHasSecondaryIpRange(&subnetwork, "tf-test-secondary-range-update2", "192.168.11.0/24"), + ), + }, + resource.TestStep{ + Config: testAccComputeSubnetwork_secondaryIpRanges_update1(cnName, subnetworkName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSubnetworkExists("google_compute_subnetwork.network-with-private-secondary-ip-ranges", &subnetwork), + testAccCheckComputeSubnetworkHasSecondaryIpRange(&subnetwork, "tf-test-secondary-range-update1", "192.168.10.0/24"), + testAccCheckComputeSubnetworkHasNotSecondaryIpRange(&subnetwork, "tf-test-secondary-range-update2", "192.168.11.0/24"), + ), + }, + }, + }) +} + +func TestAccComputeSubnetwork_flowLogs(t *testing.T) { + t.Parallel() + + var subnetwork compute.Subnetwork + + cnName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + subnetworkName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSubnetworkDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeSubnetwork_flowLogs(cnName, subnetworkName, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSubnetworkExists( + "google_compute_subnetwork.network-with-flow-logs", &subnetwork), + resource.TestCheckResourceAttr("google_compute_subnetwork.network-with-flow-logs", + "enable_flow_logs", "true"), + ), + }, + { + ResourceName: "google_compute_subnetwork.network-with-flow-logs", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeSubnetwork_flowLogs(cnName, subnetworkName, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeSubnetworkExists( + "google_compute_subnetwork.network-with-flow-logs", &subnetwork), + resource.TestCheckResourceAttr("google_compute_subnetwork.network-with-flow-logs", + "enable_flow_logs", "false"), + ), + }, + { + ResourceName: "google_compute_subnetwork.network-with-flow-logs", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckComputeSubnetworkDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_subnetwork" { + continue + } + + region, subnet_name := splitSubnetID(rs.Primary.ID) + _, err := config.clientCompute.Subnetworks.Get( + config.Project, region, subnet_name).Do() + if err == nil { + return fmt.Errorf("Network still exists") + } + } + + return nil +} + +func testAccCheckComputeSubnetworkExists(n string, subnetwork *compute.Subnetwork) 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) + + region, subnet_name := splitSubnetID(rs.Primary.ID) + found, err := config.clientCompute.Subnetworks.Get( + config.Project, region, subnet_name).Do() + if err != nil { + return err + } + + if found.Name != subnet_name { + return fmt.Errorf("Subnetwork not found") + } + + *subnetwork = *found + + return nil + } +} + +func testAccCheckComputeSubnetworkHasSecondaryIpRange(subnetwork *compute.Subnetwork, rangeName, ipCidrRange string) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, secondaryRange := range subnetwork.SecondaryIpRanges { + if secondaryRange.RangeName == rangeName { + if secondaryRange.IpCidrRange == ipCidrRange { + return nil + } + return fmt.Errorf("Secondary range %s has the wrong ip_cidr_range. Expected %s, got %s", rangeName, ipCidrRange, secondaryRange.IpCidrRange) + } + } + + return fmt.Errorf("Secondary range %s not found", rangeName) + } +} + +func testAccCheckComputeSubnetworkHasNotSecondaryIpRange(subnetwork *compute.Subnetwork, rangeName, ipCidrRange string) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, secondaryRange := range subnetwork.SecondaryIpRanges { + if secondaryRange.RangeName == rangeName { + if secondaryRange.IpCidrRange == ipCidrRange { + return fmt.Errorf("Secondary range %s has the wrong ip_cidr_range. Expected %s, got %s", rangeName, ipCidrRange, secondaryRange.IpCidrRange) + } + } + } + + return nil + } +} + +func testAccComputeSubnetwork_basic(cnName, subnetwork1Name, subnetwork2Name, subnetwork3Name string) string { + return fmt.Sprintf(` +resource "google_compute_network" "custom-test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "network-ref-by-url" { + name = "%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + network = "${google_compute_network.custom-test.self_link}" +} + + +resource "google_compute_subnetwork" "network-ref-by-name" { + name = "%s" + ip_cidr_range = "10.1.0.0/16" + region = "us-central1" + network = "${google_compute_network.custom-test.name}" +} + +resource "google_compute_subnetwork" "network-with-private-google-access" { + name = "%s" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = "${google_compute_network.custom-test.self_link}" + private_ip_google_access = true +} +`, cnName, subnetwork1Name, subnetwork2Name, subnetwork3Name) +} + +func testAccComputeSubnetwork_update1(cnName, cidrRange, subnetworkName string) string { + return fmt.Sprintf(` +resource "google_compute_network" "custom-test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "network-with-private-google-access" { + name = "%s" + ip_cidr_range = "%s" + region = "us-central1" + network = "${google_compute_network.custom-test.self_link}" + private_ip_google_access = true +} +`, cnName, subnetworkName, cidrRange) +} + +func testAccComputeSubnetwork_update2(cnName, cidrRange, subnetworkName string) string { + return fmt.Sprintf(` +resource "google_compute_network" "custom-test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "network-with-private-google-access" { + name = "%s" + ip_cidr_range = "%s" + region = "us-central1" + network = "${google_compute_network.custom-test.self_link}" +} +`, cnName, subnetworkName, cidrRange) +} + +func testAccComputeSubnetwork_secondaryIpRanges_update1(cnName, subnetworkName string) string { + return fmt.Sprintf(` +resource "google_compute_network" "custom-test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "network-with-private-secondary-ip-ranges" { + name = "%s" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = "${google_compute_network.custom-test.self_link}" + secondary_ip_range { + range_name = "tf-test-secondary-range-update1" + ip_cidr_range = "192.168.10.0/24" + } +} +`, cnName, subnetworkName) +} + +func testAccComputeSubnetwork_secondaryIpRanges_update2(cnName, subnetworkName string) string { + return fmt.Sprintf(` +resource "google_compute_network" "custom-test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "network-with-private-secondary-ip-ranges" { + name = "%s" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = "${google_compute_network.custom-test.self_link}" + secondary_ip_range { + range_name = "tf-test-secondary-range-update1" + ip_cidr_range = "192.168.10.0/24" + } + secondary_ip_range { + range_name = "tf-test-secondary-range-update2" + ip_cidr_range = "192.168.11.0/24" + }, +} +`, cnName, subnetworkName) +} + +func testAccComputeSubnetwork_flowLogs(cnName, subnetworkName string, enableLogs bool) string { + return fmt.Sprintf(` +resource "google_compute_network" "custom-test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "network-with-flow-logs" { + name = "%s" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + network = "${google_compute_network.custom-test.self_link}" + enable_flow_logs = %v +} +`, cnName, subnetworkName, enableLogs) +} diff --git a/provider/terraform/tests/resource_compute_target_http_proxy_test.go b/provider/terraform/tests/resource_compute_target_http_proxy_test.go new file mode 100644 index 000000000000..cd77c5982781 --- /dev/null +++ b/provider/terraform/tests/resource_compute_target_http_proxy_test.go @@ -0,0 +1,250 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeTargetHttpProxy_basic(t *testing.T) { + t.Parallel() + + target := fmt.Sprintf("thttp-test-%s", acctest.RandString(10)) + backend := fmt.Sprintf("thttp-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("thttp-test-%s", acctest.RandString(10)) + urlmap1 := fmt.Sprintf("thttp-test-%s", acctest.RandString(10)) + urlmap2 := fmt.Sprintf("thttp-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetHttpProxyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeTargetHttpProxy_basic1(target, backend, hc, urlmap1, urlmap2), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetHttpProxyExists( + "google_compute_target_http_proxy.foobar"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_target_http_proxy.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeTargetHttpProxy_update(t *testing.T) { + t.Parallel() + + target := fmt.Sprintf("thttp-test-%s", acctest.RandString(10)) + backend := fmt.Sprintf("thttp-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("thttp-test-%s", acctest.RandString(10)) + urlmap1 := fmt.Sprintf("thttp-test-%s", acctest.RandString(10)) + urlmap2 := fmt.Sprintf("thttp-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetHttpProxyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeTargetHttpProxy_basic1(target, backend, hc, urlmap1, urlmap2), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetHttpProxyExists( + "google_compute_target_http_proxy.foobar"), + ), + }, + + resource.TestStep{ + Config: testAccComputeTargetHttpProxy_basic2(target, backend, hc, urlmap1, urlmap2), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetHttpProxyExists( + "google_compute_target_http_proxy.foobar"), + ), + }, + }, + }) +} + +func testAccCheckComputeTargetHttpProxyDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_target_http_proxy" { + continue + } + + _, err := config.clientCompute.TargetHttpProxies.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("TargetHttpProxy still exists") + } + } + + return nil +} + +func testAccCheckComputeTargetHttpProxyExists(n string) 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) + + found, err := config.clientCompute.TargetHttpProxies.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("TargetHttpProxy not found") + } + + return nil + } +} + +func testAccComputeTargetHttpProxy_basic1(target, backend, hc, urlmap1, urlmap2 string) string { + return fmt.Sprintf(` + resource "google_compute_target_http_proxy" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar1.self_link}" + } + + resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + } + + resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 + } + + resource "google_compute_url_map" "foobar1" { + name = "%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + path_rule { + paths = ["/*"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } + } + + resource "google_compute_url_map" "foobar2" { + name = "%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + path_rule { + paths = ["/*"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } + } + `, target, backend, hc, urlmap1, urlmap2) +} + +func testAccComputeTargetHttpProxy_basic2(target, backend, hc, urlmap1, urlmap2 string) string { + return fmt.Sprintf(` + resource "google_compute_target_http_proxy" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + url_map = "${google_compute_url_map.foobar2.self_link}" + } + + resource "google_compute_backend_service" "foobar" { + name = "%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] + } + + resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 + } + + resource "google_compute_url_map" "foobar1" { + name = "%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + path_rule { + paths = ["/*"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } + } + + resource "google_compute_url_map" "foobar2" { + name = "%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + path_rule { + paths = ["/*"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } + } + `, target, backend, hc, urlmap1, urlmap2) +} diff --git a/provider/terraform/tests/resource_compute_target_pool_test.go b/provider/terraform/tests/resource_compute_target_pool_test.go new file mode 100644 index 000000000000..793f3171abd2 --- /dev/null +++ b/provider/terraform/tests/resource_compute_target_pool_test.go @@ -0,0 +1,151 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeTargetPool_basic(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetPoolDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeTargetPool_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetPoolExists( + "google_compute_target_pool.foo"), + testAccCheckComputeTargetPoolHealthCheck("google_compute_target_pool.foo", "google_compute_http_health_check.foobar"), + testAccCheckComputeTargetPoolExists( + "google_compute_target_pool.bar"), + testAccCheckComputeTargetPoolHealthCheck("google_compute_target_pool.bar", "google_compute_http_health_check.foobar"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_target_pool.foo", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckComputeTargetPoolDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_target_pool" { + continue + } + + _, err := config.clientCompute.TargetPools.Get( + config.Project, config.Region, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("TargetPool still exists") + } + } + + return nil +} + +func testAccCheckComputeTargetPoolExists(n string) 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) + + found, err := config.clientCompute.TargetPools.Get( + config.Project, config.Region, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("TargetPool not found") + } + + return nil + } +} + +func testAccCheckComputeTargetPoolHealthCheck(targetPool, healthCheck string) resource.TestCheckFunc { + return func(s *terraform.State) error { + targetPoolRes, ok := s.RootModule().Resources[targetPool] + if !ok { + return fmt.Errorf("Not found: %s", targetPool) + } + + healthCheckRes, ok := s.RootModule().Resources[healthCheck] + if !ok { + return fmt.Errorf("Not found: %s", healthCheck) + } + + hcLink := healthCheckRes.Primary.Attributes["self_link"] + if targetPoolRes.Primary.Attributes["health_checks.0"] != hcLink { + return fmt.Errorf("Health check not set up. Expected %q", hcLink) + } + + return nil + } +} + +func testAccComputeTargetPool_basic() string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_http_health_check" "foobar" { + name = "healthcheck-test-%s" + host = "example.com" +} + +resource "google_compute_instance" "foobar" { + name = "inst-tp-test-%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } +} + +resource "google_compute_target_pool" "foo" { + description = "Resource created for Terraform acceptance testing" + instances = ["${google_compute_instance.foobar.self_link}", "us-central1-b/bar"] + name = "tpool-test-%s" + session_affinity = "CLIENT_IP_PROTO" + health_checks = [ + "${google_compute_http_health_check.foobar.name}" + ] +} + +resource "google_compute_target_pool" "bar" { + description = "Resource created for Terraform acceptance testing" + name = "tpool-test-%s" + health_checks = [ + "${google_compute_http_health_check.foobar.self_link}" + ] +}`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10), acctest.RandString(10)) +} diff --git a/provider/terraform/tests/resource_compute_target_tcp_proxy_test.go b/provider/terraform/tests/resource_compute_target_tcp_proxy_test.go new file mode 100644 index 000000000000..dfbbf7dd07a8 --- /dev/null +++ b/provider/terraform/tests/resource_compute_target_tcp_proxy_test.go @@ -0,0 +1,165 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeTargetTcpProxy_basic(t *testing.T) { + t.Parallel() + + target := fmt.Sprintf("ttcp-test-%s", acctest.RandString(10)) + backend := fmt.Sprintf("ttcp-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("ttcp-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetTcpProxyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeTargetTcpProxy_basic1(target, backend, hc), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetTcpProxyExists( + "google_compute_target_tcp_proxy.foobar"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_target_tcp_proxy.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeTargetTcpProxy_update(t *testing.T) { + t.Parallel() + + target := fmt.Sprintf("ttcp-test-%s", acctest.RandString(10)) + backend := fmt.Sprintf("ttcp-test-%s", acctest.RandString(10)) + hc := fmt.Sprintf("ttcp-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeTargetTcpProxyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeTargetTcpProxy_basic1(target, backend, hc), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetTcpProxyExists( + "google_compute_target_tcp_proxy.foobar"), + ), + }, + resource.TestStep{ + Config: testAccComputeTargetTcpProxy_basic2(target, backend, hc), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeTargetTcpProxyExists( + "google_compute_target_tcp_proxy.foobar"), + ), + }, + }, + }) +} + +func testAccCheckComputeTargetTcpProxyDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_target_tcp_proxy" { + continue + } + + _, err := config.clientCompute.TargetTcpProxies.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("TargetTcpProxy still exists") + } + } + + return nil +} + +func testAccCheckComputeTargetTcpProxyExists(n string) 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) + + found, err := config.clientCompute.TargetTcpProxies.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("TargetTcpProxy not found") + } + + return nil + } +} + +func testAccComputeTargetTcpProxy_basic1(target, backend, hc string) string { + return fmt.Sprintf(` + resource "google_compute_target_tcp_proxy" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + backend_service = "${google_compute_backend_service.foobar.self_link}" + proxy_header = "NONE" + } + + resource "google_compute_backend_service" "foobar" { + name = "%s" + protocol = "TCP" + health_checks = ["${google_compute_health_check.zero.self_link}"] + } + + resource "google_compute_health_check" "zero" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "443" + } + } + `, target, backend, hc) +} + +func testAccComputeTargetTcpProxy_basic2(target, backend, hc string) string { + return fmt.Sprintf(` + resource "google_compute_target_tcp_proxy" "foobar" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + backend_service = "${google_compute_backend_service.foobar.self_link}" + proxy_header = "PROXY_V1" + } + + resource "google_compute_backend_service" "foobar" { + name = "%s" + protocol = "TCP" + health_checks = ["${google_compute_health_check.zero.self_link}"] + } + + resource "google_compute_health_check" "zero" { + name = "%s" + check_interval_sec = 1 + timeout_sec = 1 + tcp_health_check { + port = "443" + } + } + `, target, backend, hc) +} diff --git a/provider/terraform/tests/resource_compute_url_map_test.go b/provider/terraform/tests/resource_compute_url_map_test.go new file mode 100644 index 000000000000..ba10f19646b1 --- /dev/null +++ b/provider/terraform/tests/resource_compute_url_map_test.go @@ -0,0 +1,411 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeUrlMap_basic(t *testing.T) { + t.Parallel() + + bsName := fmt.Sprintf("urlmap-test-%s", acctest.RandString(10)) + hcName := fmt.Sprintf("urlmap-test-%s", acctest.RandString(10)) + umName := fmt.Sprintf("urlmap-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeUrlMap_basic1(bsName, hcName, umName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeUrlMapExists( + "google_compute_url_map.foobar"), + ), + }, + resource.TestStep{ + ResourceName: "google_compute_url_map.foobar", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"host_rule", "path_matcher", "test"}, + }, + }, + }) +} + +func TestAccComputeUrlMap_update_path_matcher(t *testing.T) { + t.Parallel() + + bsName := fmt.Sprintf("urlmap-test-%s", acctest.RandString(10)) + hcName := fmt.Sprintf("urlmap-test-%s", acctest.RandString(10)) + umName := fmt.Sprintf("urlmap-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeUrlMap_basic1(bsName, hcName, umName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeUrlMapExists( + "google_compute_url_map.foobar"), + ), + }, + + resource.TestStep{ + Config: testAccComputeUrlMap_basic2(bsName, hcName, umName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeUrlMapExists( + "google_compute_url_map.foobar"), + ), + }, + }, + }) +} + +func TestAccComputeUrlMap_advanced(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeUrlMap_advanced1(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeUrlMapExists( + "google_compute_url_map.foobar"), + ), + }, + + resource.TestStep{ + Config: testAccComputeUrlMap_advanced2(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeUrlMapExists( + "google_compute_url_map.foobar"), + ), + }, + }, + }) +} + +func TestAccComputeUrlMap_noPathRulesWithUpdate(t *testing.T) { + t.Parallel() + + bsName := fmt.Sprintf("urlmap-test-%s", acctest.RandString(10)) + hcName := fmt.Sprintf("urlmap-test-%s", acctest.RandString(10)) + umName := fmt.Sprintf("urlmap-test-%s", acctest.RandString(10)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeUrlMap_noPathRules(bsName, hcName, umName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeUrlMapExists( + "google_compute_url_map.foobar"), + ), + }, + resource.TestStep{ + Config: testAccComputeUrlMap_basic1(bsName, hcName, umName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeUrlMapExists( + "google_compute_url_map.foobar"), + ), + }, + }, + }) +} + +func testAccCheckComputeUrlMapDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_url_map" { + continue + } + + _, err := config.clientCompute.UrlMaps.Get( + config.Project, rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Url map still exists") + } + } + + return nil +} + +func testAccCheckComputeUrlMapExists(n string) 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) + + found, err := config.clientCompute.UrlMaps.Get( + config.Project, rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Url map not found") + } + return nil + } +} + +func testAccComputeUrlMap_basic1(bsName, hcName, umName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "urlmap-test-%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "urlmap-test-%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_url_map" "foobar" { + name = "urlmap-test-%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + + path_rule { + paths = ["/*"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } +} +`, bsName, hcName, umName) +} + +func testAccComputeUrlMap_basic2(bsName, hcName, umName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "urlmap-test-%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "urlmap-test-%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_url_map" "foobar" { + name = "urlmap-test-%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "blip" + } + + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "blip" + + path_rule { + paths = ["/*", "/home"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + + test { + host = "mysite.com" + path = "/test" + service = "${google_compute_backend_service.foobar.self_link}" + } +} +`, bsName, hcName, umName) +} + +func testAccComputeUrlMap_advanced1() string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "urlmap-test-%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "urlmap-test-%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_url_map" "foobar" { + name = "urlmap-test-%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "blop" + } + + host_rule { + hosts = ["myfavoritesite.com"] + path_matcher = "blip" + } + + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "blop" + + path_rule { + paths = ["/*", "/home"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "blip" + + path_rule { + paths = ["/*", "/home"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } +} +`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10)) +} + +func testAccComputeUrlMap_advanced2() string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "urlmap-test-%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "urlmap-test-%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_url_map" "foobar" { + name = "urlmap-test-%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "blep" + } + + host_rule { + hosts = ["myfavoritesite.com"] + path_matcher = "blip" + } + + host_rule { + hosts = ["myleastfavoritesite.com"] + path_matcher = "blub" + } + + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "blep" + + path_rule { + paths = ["/home"] + service = "${google_compute_backend_service.foobar.self_link}" + } + + path_rule { + paths = ["/login"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "blub" + + path_rule { + paths = ["/*", "/blub"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } + + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "blip" + + path_rule { + paths = ["/*", "/home"] + service = "${google_compute_backend_service.foobar.self_link}" + } + } +} +`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10)) +} + +func testAccComputeUrlMap_noPathRules(bsName, hcName, umName string) string { + return fmt.Sprintf(` +resource "google_compute_backend_service" "foobar" { + name = "urlmap-test-%s" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "urlmap-test-%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_url_map" "foobar" { + name = "urlmap-test-%s" + default_service = "${google_compute_backend_service.foobar.self_link}" + + host_rule { + hosts = ["mysite.com", "myothersite.com"] + path_matcher = "boop" + } + + path_matcher { + default_service = "${google_compute_backend_service.foobar.self_link}" + name = "boop" + } + + test { + host = "mysite.com" + path = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } +} +`, bsName, hcName, umName) +} diff --git a/provider/terraform/tests/resource_compute_vpn_gateway_test.go b/provider/terraform/tests/resource_compute_vpn_gateway_test.go new file mode 100644 index 000000000000..c922af914600 --- /dev/null +++ b/provider/terraform/tests/resource_compute_vpn_gateway_test.go @@ -0,0 +1,105 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "google.golang.org/api/compute/v1" +) + +func TestAccComputeVpnGateway_basic(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeVpnGatewayDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeVpnGateway_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeVpnGatewayExists( + "google_compute_vpn_gateway.foobar"), + testAccCheckComputeVpnGatewayExists( + "google_compute_vpn_gateway.baz"), + ), + }, + }, + }) +} + +func testAccCheckComputeVpnGatewayDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + project := config.Project + + vpnGatewaysService := compute.NewTargetVpnGatewaysService(config.clientCompute) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_network" { + continue + } + + region := rs.Primary.Attributes["region"] + name := rs.Primary.Attributes["name"] + + _, err := vpnGatewaysService.Get(project, region, name).Do() + + if err == nil { + return fmt.Errorf("Error, VPN Gateway %s in region %s still exists", + name, region) + } + } + + return nil +} + +func testAccCheckComputeVpnGatewayExists(n string) 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) + name := rs.Primary.Attributes["name"] + region := rs.Primary.Attributes["region"] + project := config.Project + + vpnGatewaysService := compute.NewTargetVpnGatewaysService(config.clientCompute) + _, err := vpnGatewaysService.Get(project, region, name).Do() + + if err != nil { + return fmt.Errorf("Error Reading VPN Gateway %s: %s", name, err) + } + + return nil + } +} + +func testAccComputeVpnGateway_basic() string { + return fmt.Sprintf(` +resource "google_compute_network" "foobar" { + name = "gateway-test-%s" + auto_create_subnetworks = false + ipv4_range = "10.0.0.0/16" +} + +resource "google_compute_vpn_gateway" "foobar" { + name = "gateway-test-%s" + network = "${google_compute_network.foobar.self_link}" + region = "us-central1" +} +resource "google_compute_vpn_gateway" "baz" { + name = "gateway-test-%s" + network = "${google_compute_network.foobar.name}" + region = "us-central1" +}`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10)) +}