From ccb3f92bd1104eaf833eadc824078f17d620aec5 Mon Sep 17 00:00:00 2001 From: Lars Wander Date: Fri, 30 Oct 2015 17:27:12 -0400 Subject: [PATCH] provider/google: Added url map resource + tests & documentation --- builtin/providers/google/config.go | 2 +- builtin/providers/google/provider.go | 3 +- .../resource_compute_instance_template.go | 15 +- .../google/resource_compute_url_map.go | 649 ++++++++++++++++++ .../google/resource_compute_url_map_test.go | 311 +++++++++ builtin/providers/google/test_util.go | 3 +- .../google/r/compute_url_map.html.markdown | 141 ++++ website/source/layouts/google.erb | 4 + 8 files changed, 1116 insertions(+), 12 deletions(-) create mode 100644 builtin/providers/google/resource_compute_url_map.go create mode 100644 builtin/providers/google/resource_compute_url_map_test.go create mode 100644 website/source/docs/providers/google/r/compute_url_map.html.markdown diff --git a/builtin/providers/google/config.go b/builtin/providers/google/config.go index f6599161b..3edb68ef0 100644 --- a/builtin/providers/google/config.go +++ b/builtin/providers/google/config.go @@ -17,8 +17,8 @@ import ( "google.golang.org/api/compute/v1" "google.golang.org/api/container/v1" "google.golang.org/api/dns/v1" - "google.golang.org/api/storage/v1" "google.golang.org/api/sqladmin/v1beta4" + "google.golang.org/api/storage/v1" ) // Config is the configuration structure used to instantiate the Google diff --git a/builtin/providers/google/provider.go b/builtin/providers/google/provider.go index fdfd925ce..f1d5c17ce 100644 --- a/builtin/providers/google/provider.go +++ b/builtin/providers/google/provider.go @@ -43,18 +43,19 @@ func Provider() terraform.ResourceProvider { "google_compute_global_address": resourceComputeGlobalAddress(), "google_compute_http_health_check": resourceComputeHttpHealthCheck(), "google_compute_instance": resourceComputeInstance(), + "google_compute_instance_group_manager": resourceComputeInstanceGroupManager(), "google_compute_instance_template": resourceComputeInstanceTemplate(), "google_compute_network": resourceComputeNetwork(), "google_compute_project_metadata": resourceComputeProjectMetadata(), "google_compute_route": resourceComputeRoute(), "google_compute_ssl_certificate": resourceComputeSslCertificate(), "google_compute_target_pool": resourceComputeTargetPool(), + "google_compute_url_map": resourceComputeUrlMap(), "google_compute_vpn_gateway": resourceComputeVpnGateway(), "google_compute_vpn_tunnel": resourceComputeVpnTunnel(), "google_container_cluster": resourceContainerCluster(), "google_dns_managed_zone": resourceDnsManagedZone(), "google_dns_record_set": resourceDnsRecordSet(), - "google_compute_instance_group_manager": resourceComputeInstanceGroupManager(), "google_sql_database": resourceSqlDatabase(), "google_sql_database_instance": resourceSqlDatabaseInstance(), "google_storage_bucket": resourceStorageBucket(), diff --git a/builtin/providers/google/resource_compute_instance_template.go b/builtin/providers/google/resource_compute_instance_template.go index effee9de8..48be445cb 100644 --- a/builtin/providers/google/resource_compute_instance_template.go +++ b/builtin/providers/google/resource_compute_instance_template.go @@ -159,17 +159,17 @@ func resourceComputeInstanceTemplate() *schema.Resource { }, "automatic_restart": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - ForceNew: true, + Type: schema.TypeBool, + Optional: true, + Default: true, + ForceNew: true, Deprecated: "Please use `scheduling.automatic_restart` instead", }, "on_host_maintenance": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + ForceNew: true, Deprecated: "Please use `scheduling.on_host_maintenance` instead", }, @@ -413,7 +413,6 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac } } - serviceAccountsCount := d.Get("service_account.#").(int) serviceAccounts := make([]*compute.ServiceAccount, 0, serviceAccountsCount) for i := 0; i < serviceAccountsCount; i++ { diff --git a/builtin/providers/google/resource_compute_url_map.go b/builtin/providers/google/resource_compute_url_map.go new file mode 100644 index 000000000..4b29c4360 --- /dev/null +++ b/builtin/providers/google/resource_compute_url_map.go @@ -0,0 +1,649 @@ +package google + +import ( + "fmt" + "strconv" + + "github.com/hashicorp/terraform/helper/schema" + "google.golang.org/api/compute/v1" +) + +func resourceComputeUrlMap() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeUrlMapCreate, + Read: resourceComputeUrlMapRead, + Update: resourceComputeUrlMapUpdate, + Delete: resourceComputeUrlMapDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "default_service": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "fingerprint": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "host_rule": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "hosts": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "path_matcher": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + + "path_matcher": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_service": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "path_rule": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "paths": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "service": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + }, + }, + + "self_link": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "test": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "host": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "path": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "service": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + } +} + +func createHostRule(v interface{}) *compute.HostRule { + _hostRule := v.(map[string]interface{}) + + _hosts := _hostRule["hosts"].([]interface{}) + hosts := make([]string, len(_hosts)) + + for i, v := range _hosts { + hosts[i] = v.(string) + } + + pathMatcher := _hostRule["path_matcher"].(string) + + hostRule := &compute.HostRule{ + Hosts: hosts, + PathMatcher: pathMatcher, + } + + if v, ok := _hostRule["description"]; ok { + hostRule.Description = v.(string) + } + + return hostRule +} + +func createPathMatcher(v interface{}) *compute.PathMatcher { + _pathMatcher := v.(map[string]interface{}) + + _pathRules := _pathMatcher["path_rule"].([]interface{}) + pathRules := make([]*compute.PathRule, len(_pathRules)) + + for ip, vp := range _pathRules { + _pathRule := vp.(map[string]interface{}) + + _paths := _pathRule["paths"].([]interface{}) + paths := make([]string, len(_paths)) + + for ipp, vpp := range _paths { + paths[ipp] = vpp.(string) + } + + service := _pathRule["service"].(string) + + pathRule := &compute.PathRule{ + Paths: paths, + Service: service, + } + + pathRules[ip] = pathRule + } + + name := _pathMatcher["name"].(string) + defaultService := _pathMatcher["default_service"].(string) + + pathMatcher := &compute.PathMatcher{ + PathRules: pathRules, + Name: name, + DefaultService: defaultService, + } + + if vp, okp := _pathMatcher["description"]; okp { + pathMatcher.Description = vp.(string) + } + + return pathMatcher +} + +func createUrlMapTest(v interface{}) *compute.UrlMapTest { + _test := v.(map[string]interface{}) + + host := _test["host"].(string) + path := _test["path"].(string) + service := _test["service"].(string) + + test := &compute.UrlMapTest{ + Host: host, + Path: path, + Service: service, + } + + if vp, okp := _test["description"]; okp { + test.Description = vp.(string) + } + + return test +} + +func resourceComputeUrlMapCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + name := d.Get("name").(string) + defaultService := d.Get("default_service").(string) + + urlMap := &compute.UrlMap{ + Name: name, + DefaultService: defaultService, + } + + if v, ok := d.GetOk("description"); ok { + urlMap.Description = v.(string) + } + + _hostRules := d.Get("host_rule").([]interface{}) + urlMap.HostRules = make([]*compute.HostRule, len(_hostRules)) + + for i, v := range _hostRules { + urlMap.HostRules[i] = createHostRule(v) + } + + _pathMatchers := d.Get("path_matcher").([]interface{}) + urlMap.PathMatchers = make([]*compute.PathMatcher, len(_pathMatchers)) + + for i, v := range _pathMatchers { + urlMap.PathMatchers[i] = createPathMatcher(v) + } + + _tests := make([]interface{}, 0) + if v, ok := d.GetOk("test"); ok { + _tests = v.([]interface{}) + } + urlMap.Tests = make([]*compute.UrlMapTest, len(_tests)) + + for i, v := range _tests { + urlMap.Tests[i] = createUrlMapTest(v) + } + + op, err := config.clientCompute.UrlMaps.Insert(config.Project, urlMap).Do() + + if err != nil { + return fmt.Errorf("Error, failed to insert Url Map %s: %s", name, err) + } + + err = computeOperationWaitGlobal(config, op, "Insert Url Map") + + if err != nil { + return fmt.Errorf("Error, failed waitng to insert Url Map %s: %s", name, err) + } + + return resourceComputeUrlMapRead(d, meta) +} + +func resourceComputeUrlMapRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + name := d.Get("name").(string) + + urlMap, err := config.clientCompute.UrlMaps.Get(config.Project, name).Do() + + if err != nil { + return fmt.Errorf("Error, failed to get Url Map %s: %s", name, err) + } + + d.SetId(name) + d.Set("self_link", urlMap.SelfLink) + d.Set("id", strconv.FormatUint(urlMap.Id, 10)) + d.Set("fingerprint", urlMap.Fingerprint) + + hostRuleMap := make(map[string]*compute.HostRule) + for _, v := range urlMap.HostRules { + hostRuleMap[v.PathMatcher] = v + } + + /* Only read host rules into our TF state that we have defined */ + _hostRules := d.Get("host_rule").([]interface{}) + _newHostRules := make([]interface{}, 0) + for _, v := range _hostRules { + _hostRule := v.(map[string]interface{}) + _pathMatcher := _hostRule["path_matcher"].(string) + + /* Delete local entries that are no longer found on the GCE server */ + if hostRule, ok := hostRuleMap[_pathMatcher]; ok { + _newHostRule := make(map[string]interface{}) + _newHostRule["path_matcher"] = _pathMatcher + + hostsSet := make(map[string]bool) + for _, host := range hostRule.Hosts { + hostsSet[host] = true + } + + /* Only store hosts we are keeping track of */ + _newHosts := make([]interface{}, 0) + for _, vp := range _hostRule["hosts"].([]interface{}) { + if _, okp := hostsSet[vp.(string)]; okp { + _newHosts = append(_newHosts, vp) + } + } + + _newHostRule["hosts"] = _newHosts + _newHostRule["description"] = hostRule.Description + + _newHostRules = append(_newHostRules, _newHostRule) + } + } + + d.Set("host_rule", _newHostRules) + + pathMatcherMap := make(map[string]*compute.PathMatcher) + for _, v := range urlMap.PathMatchers { + pathMatcherMap[v.Name] = v + } + + /* Only read path matchers into our TF state that we have defined */ + _pathMatchers := d.Get("path_matcher").([]interface{}) + _newPathMatchers := make([]interface{}, 0) + for _, v := range _pathMatchers { + _pathMatcher := v.(map[string]interface{}) + _name := _pathMatcher["name"].(string) + + if pathMatcher, ok := pathMatcherMap[_name]; ok { + _newPathMatcher := make(map[string]interface{}) + _newPathMatcher["name"] = _name + _newPathMatcher["default_service"] = pathMatcher.DefaultService + _newPathMatcher["description"] = pathMatcher.Description + + _newPathRules := make([]interface{}, len(pathMatcher.PathRules)) + for ip, pathRule := range pathMatcher.PathRules { + _newPathRule := make(map[string]interface{}) + _newPathRule["service"] = pathRule.Service + _paths := make([]interface{}, len(pathRule.Paths)) + + for ipp, vpp := range pathRule.Paths { + _paths[ipp] = vpp + } + + _newPathRule["paths"] = _paths + + _newPathRules[ip] = _newPathRule + } + + _newPathMatcher["path_rule"] = _newPathRules + _newPathMatchers = append(_newPathMatchers, _newPathMatcher) + } + } + + d.Set("path_matcher", _newPathMatchers) + + testMap := make(map[string]*compute.UrlMapTest) + for _, v := range urlMap.Tests { + testMap[fmt.Sprintf("%s/%s", v.Host, v.Path)] = v + } + + _tests := make([]interface{}, 0) + /* Only read tests into our TF state that we have defined */ + if v, ok := d.GetOk("test"); ok { + _tests = v.([]interface{}) + } + _newTests := make([]interface{}, 0) + for _, v := range _tests { + _test := v.(map[string]interface{}) + _host := _test["host"].(string) + _path := _test["path"].(string) + + /* Delete local entries that are no longer found on the GCE server */ + if test, ok := testMap[fmt.Sprintf("%s/%s", _host, _path)]; ok { + _newTest := make(map[string]interface{}) + _newTest["host"] = _host + _newTest["path"] = _path + _newTest["description"] = test.Description + _newTest["service"] = test.Service + + _newTests = append(_newTests, _newTest) + } + } + + d.Set("test", _newTests) + + return nil +} + +func resourceComputeUrlMapUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + name := d.Get("name").(string) + urlMap, err := config.clientCompute.UrlMaps.Get(config.Project, name).Do() + if err != nil { + return fmt.Errorf("Error, failed to get Url Map %s: %s", name, err) + } + + urlMap.DefaultService = d.Get("default_service").(string) + + if v, ok := d.GetOk("description"); ok { + urlMap.Description = v.(string) + } + + if d.HasChange("host_rule") { + _oldHostRules, _newHostRules := d.GetChange("host_rule") + _oldHostRulesMap := make(map[string]interface{}) + _newHostRulesMap := make(map[string]interface{}) + + for _, v := range _oldHostRules.([]interface{}) { + _hostRule := v.(map[string]interface{}) + _oldHostRulesMap[_hostRule["path_matcher"].(string)] = v + } + + for _, v := range _newHostRules.([]interface{}) { + _hostRule := v.(map[string]interface{}) + _newHostRulesMap[_hostRule["path_matcher"].(string)] = v + } + + newHostRules := make([]*compute.HostRule, 0) + /* Decide which host rules to keep */ + for _, v := range urlMap.HostRules { + /* If it's in the old state, we have ownership over the host rule */ + if vOld, ok := _oldHostRulesMap[v.PathMatcher]; ok { + if vNew, ok := _newHostRulesMap[v.PathMatcher]; ok { + /* Adjust for any changes made to this rule */ + _newHostRule := vNew.(map[string]interface{}) + _oldHostRule := vOld.(map[string]interface{}) + _newHostsSet := make(map[string]bool) + _oldHostsSet := make(map[string]bool) + + hostRule := &compute.HostRule{ + PathMatcher: v.PathMatcher, + } + + for _, v := range _newHostRule["hosts"].([]interface{}) { + _newHostsSet[v.(string)] = true + } + + for _, v := range _oldHostRule["hosts"].([]interface{}) { + _oldHostsSet[v.(string)] = true + } + + /* Only add hosts that have been added locally or are new, + * not touching those from the GCE server state */ + for _, host := range v.Hosts { + _, okNew := _newHostsSet[host] + _, okOld := _oldHostsSet[host] + + /* Drop deleted hosts */ + if okOld && !okNew { + continue + } + + hostRule.Hosts = append(hostRule.Hosts, host) + + /* Kep track of the fact that this host was added */ + delete(_newHostsSet, host) + } + + /* Now add in the brand new entries */ + for host, _ := range _oldHostsSet { + hostRule.Hosts = append(hostRule.Hosts, host) + } + + if v, ok := _newHostRule["description"]; ok { + hostRule.Description = v.(string) + } + + newHostRules = append(newHostRules, hostRule) + + /* Record that we've include this host rule */ + delete(_newHostRulesMap, v.PathMatcher) + } else { + /* It's been deleted */ + continue + } + } else { + if vNew, ok := _newHostRulesMap[v.PathMatcher]; ok { + newHostRules = append(newHostRules, createHostRule(vNew)) + + /* Record that we've include this host rule */ + delete(_newHostRulesMap, v.PathMatcher) + } else { + /* It wasn't created or modified locally */ + newHostRules = append(newHostRules, v) + } + } + } + + /* Record brand new host rules (ones not deleted above) */ + for _, v := range _newHostRulesMap { + newHostRules = append(newHostRules, createHostRule(v)) + } + + urlMap.HostRules = newHostRules + } + + if d.HasChange("path_matcher") { + _oldPathMatchers, _newPathMatchers := d.GetChange("path_matcher") + _oldPathMatchersMap := make(map[string]interface{}) + _newPathMatchersMap := make(map[string]interface{}) + + for _, v := range _oldPathMatchers.([]interface{}) { + _pathMatcher := v.(map[string]interface{}) + _oldPathMatchersMap[_pathMatcher["name"].(string)] = v + } + + for _, v := range _newPathMatchers.([]interface{}) { + _pathMatcher := v.(map[string]interface{}) + _newPathMatchersMap[_pathMatcher["name"].(string)] = v + } + + newPathMatchers := make([]*compute.PathMatcher, 0) + /* Decide which path matchers to keep */ + for _, v := range urlMap.PathMatchers { + /* If it's in the old state, we have ownership over the host rule */ + _, okOld := _oldPathMatchersMap[v.Name] + vNew, okNew := _newPathMatchersMap[v.Name] + + /* Drop deleted entries */ + if okOld && !okNew { + continue + } + + /* Don't change entries that don't belong to us */ + if !okNew { + newPathMatchers = append(newPathMatchers, v) + } else { + newPathMatchers = append(newPathMatchers, createPathMatcher(vNew)) + + delete(_newPathMatchersMap, v.Name) + } + } + + /* Record brand new host rules */ + for _, v := range _newPathMatchersMap { + newPathMatchers = append(newPathMatchers, createPathMatcher(v)) + } + + urlMap.PathMatchers = newPathMatchers + } + + if d.HasChange("tests") { + _oldTests, _newTests := d.GetChange("path_matcher") + _oldTestsMap := make(map[string]interface{}) + _newTestsMap := make(map[string]interface{}) + + for _, v := range _oldTests.([]interface{}) { + _test := v.(map[string]interface{}) + ident := fmt.Sprintf("%s/%s", _test["host"].(string), _test["path"].(string)) + _oldTestsMap[ident] = v + } + + for _, v := range _newTests.([]interface{}) { + _test := v.(map[string]interface{}) + ident := fmt.Sprintf("%s/%s", _test["host"].(string), _test["path"].(string)) + _newTestsMap[ident] = v + } + + newTests := make([]*compute.UrlMapTest, 0) + /* Decide which path matchers to keep */ + for _, v := range urlMap.Tests { + ident := fmt.Sprintf("%s/%s", v.Host, v.Path) + /* If it's in the old state, we have ownership over the host rule */ + _, okOld := _oldTestsMap[ident] + vNew, okNew := _newTestsMap[ident] + + /* Drop deleted entries */ + if okOld && !okNew { + continue + } + + /* Don't change entries that don't belong to us */ + if !okNew { + newTests = append(newTests, v) + } else { + newTests = append(newTests, createUrlMapTest(vNew)) + + delete(_newTestsMap, ident) + } + } + + /* Record brand new host rules */ + for _, v := range _newTestsMap { + newTests = append(newTests, createUrlMapTest(v)) + } + + urlMap.Tests = newTests + } + + op, err := config.clientCompute.UrlMaps.Update(config.Project, urlMap.Name, urlMap).Do() + + if err != nil { + return fmt.Errorf("Error, failed to update Url Map %s: %s", name, err) + } + + err = computeOperationWaitGlobal(config, op, "Update Url Map") + + if err != nil { + return fmt.Errorf("Error, failed waitng to update Url Map %s: %s", name, err) + } + + return resourceComputeUrlMapRead(d, meta) +} + +func resourceComputeUrlMapDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + name := d.Get("name").(string) + + op, err := config.clientCompute.UrlMaps.Delete(config.Project, name).Do() + + if err != nil { + return fmt.Errorf("Error, failed to delete Url Map %s: %s", name, err) + } + + err = computeOperationWaitGlobal(config, op, "Delete Url Map") + + if err != nil { + return fmt.Errorf("Error, failed waitng to delete Url Map %s: %s", name, err) + } + + return nil +} diff --git a/builtin/providers/google/resource_compute_url_map_test.go b/builtin/providers/google/resource_compute_url_map_test.go new file mode 100644 index 000000000..ac2f08b13 --- /dev/null +++ b/builtin/providers/google/resource_compute_url_map_test.go @@ -0,0 +1,311 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeUrlMap_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeUrlMap_basic1, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeUrlMapExists( + "google_compute_url_map.foobar"), + ), + }, + }, + }) +} + +func TestAccComputeUrlMap_update_path_matcher(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeUrlMapDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeUrlMap_basic1, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeUrlMapExists( + "google_compute_url_map.foobar"), + ), + }, + + resource.TestStep{ + Config: testAccComputeUrlMap_basic2, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeUrlMapExists( + "google_compute_url_map.foobar"), + ), + }, + }, + }) +} + +func TestAccComputeUrlMap_advanced(t *testing.T) { + 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 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 + } +} + +const testAccComputeUrlMap_basic1 = ` +resource "google_compute_backend_service" "foobar" { + name = "service" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "tf-test-zero" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_url_map" "foobar" { + name = "myurlmap" + 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}" + } +} +` + +const testAccComputeUrlMap_basic2 = ` +resource "google_compute_backend_service" "foobar" { + name = "service" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "tf-test-zero" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_url_map" "foobar" { + name = "myurlmap" + 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 = "/*" + service = "${google_compute_backend_service.foobar.self_link}" + } +} +` + +const testAccComputeUrlMap_advanced1 = ` +resource "google_compute_backend_service" "foobar" { + name = "service" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "tf-test-zero" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_url_map" "foobar" { + name = "myurlmap" + 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}" + } + } +} +` + +const testAccComputeUrlMap_advanced2 = ` +resource "google_compute_backend_service" "foobar" { + name = "service" + health_checks = ["${google_compute_http_health_check.zero.self_link}"] +} + +resource "google_compute_http_health_check" "zero" { + name = "tf-test-zero" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + +resource "google_compute_url_map" "foobar" { + name = "myurlmap" + 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}" + } + } +} +` diff --git a/builtin/providers/google/test_util.go b/builtin/providers/google/test_util.go index 46d0579b0..09fcaaffa 100644 --- a/builtin/providers/google/test_util.go +++ b/builtin/providers/google/test_util.go @@ -1,9 +1,8 @@ package google - import ( - "time" "math/rand" + "time" ) func genRandInt() int { diff --git a/website/source/docs/providers/google/r/compute_url_map.html.markdown b/website/source/docs/providers/google/r/compute_url_map.html.markdown new file mode 100644 index 000000000..b7cdfdcf7 --- /dev/null +++ b/website/source/docs/providers/google/r/compute_url_map.html.markdown @@ -0,0 +1,141 @@ +--- +layout: "google" +page_title: "Google: google_compute_url_map" +sidebar_current: "docs-google-resource-url-map" +description: |- + Manages a URL Map resource in GCE. +--- + +# google\_compute\_url\_map + +Manages a URL Map resource within GCE. For more information see +[the official documentation](https://cloud.google.com/compute/docs/load-balancing/http/url-map) +and +[API](https://cloud.google.com/compute/docs/reference/latest/urlMaps). + + +## Example Usage + +``` +resource "google_compute_url_map" "foobar" { + name = "urlmap" + description = "a description" + default_service = "${google_compute_backend_service.home.self_link}" + + host_rule { + hosts = ["mysite.com"] + path_matcher = "allpaths" + } + + path_matcher { + default_service = "${google_compute_backend_service.home.self_link}" + name = "allpaths" + path_rule { + paths = ["/home"] + service = "${google_compute_backend_service.home.self_link}" + } + + path_rule { + paths = ["/login"] + service = "${google_compute_backend_service.login.self_link}" + } + } + + test { + service = "${google_compute_backend_service.home.self_link}" + host = "hi.com" + path = "/home" + } +} + +resource "google_compute_backend_service" "login" { + name = "login-backend" + port_name = "http" + protocol = "HTTP" + timeout_sec = 10 + region = "us-central1" + + health_checks = ["${google_compute_http_health_check.default.self_link}"] +} + +resource "google_compute_backend_service" "home" { + name = "home-backend" + port_name = "http" + protocol = "HTTP" + timeout_sec = 10 + region = "us-central1" + + health_checks = ["${google_compute_http_health_check.default.self_link}"] +} + +resource "google_compute_http_health_check" "default" { + name = "test" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A unique name for the resource, required by GCE. + Changing this forces a new resource to be created. + +* `description` - (Optional) A brief description of this resource. + +* `default_service` - (Required) The URL of the backend service to use when none of the + given rules match. See the documentation for formatting the service URL + [here](https://cloud.google.com/compute/docs/reference/latest/urlMaps#defaultService) + +The `host_rule` block supports: (Note that this block can be defined an arbitrary +number of times.) + +* `hosts` (Required) - A list of hosts to match against. See the documention + for formatting each host [here](https://cloud.google.com/compute/docs/reference/latest/urlMaps#hostRules.hosts) + +* `description` - (Optional) An optional description of the host rule. + +* `path_matcher` - (Required) The name of the `path_matcher` (defined below) + to apply this host rule to. + +The `path_matcher` block supports: (Note that this block can be defined an arbitrary +number of times.) + +* `default_service` - (Required) The URL for the backend service to use if none + of the given paths match. See the documentation for formatting the service URL + [here](https://cloud.google.com/compute/docs/reference/latest/urlMaps#pathMatcher.defaultService) + +* `name` - (Required) The name of the `path_matcher` resource. Used by the `host_rule` + block above. + +* `description` - (Optional) An optional description of the host rule. + +The `path_matcher.path_rule` sub-block supports: (Note that this block can be defined an arbitrary +number of times.) + +* `paths` - (Required) The list of paths to match against. See the + documentation for formatting these [here](https://cloud.google.com/compute/docs/reference/latest/urlMaps#pathMatchers.pathRules.paths) + +* `default_service` - (Required) The URL for the backend service to use if any + of the given paths match. See the documentation for formatting the service URL + [here](https://cloud.google.com/compute/docs/reference/latest/urlMaps#pathMatcher.defaultService) + +The optional `test` block supports: (Note that this block can be defined an arbitary +number of times.) + +* `service` - (Required) The service that should be matched by this test. + +* `host` - (Required) The host component of the URL being tested. + +* `path` - (Required) The path component of the URL being tested. + +* `description` - (Optional) An optional description of this test. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The GCE assigned ID of the resource. +* `self_link` - A GCE assigned link to the resource. diff --git a/website/source/layouts/google.erb b/website/source/layouts/google.erb index 6aeceb833..f5f87d885 100644 --- a/website/source/layouts/google.erb +++ b/website/source/layouts/google.erb @@ -77,6 +77,10 @@ google_compute_target_pool + > + google_compute_url_map + + > google_compute_vpn_gateway