github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/google/resource_compute_url_map.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strconv"
     7  
     8  	"github.com/hashicorp/terraform/helper/schema"
     9  	"google.golang.org/api/compute/v1"
    10  	"google.golang.org/api/googleapi"
    11  )
    12  
    13  func resourceComputeUrlMap() *schema.Resource {
    14  	return &schema.Resource{
    15  		Create: resourceComputeUrlMapCreate,
    16  		Read:   resourceComputeUrlMapRead,
    17  		Update: resourceComputeUrlMapUpdate,
    18  		Delete: resourceComputeUrlMapDelete,
    19  
    20  		Schema: map[string]*schema.Schema{
    21  			"default_service": &schema.Schema{
    22  				Type:     schema.TypeString,
    23  				Required: true,
    24  			},
    25  
    26  			"name": &schema.Schema{
    27  				Type:     schema.TypeString,
    28  				Required: true,
    29  				ForceNew: true,
    30  			},
    31  
    32  			"description": &schema.Schema{
    33  				Type:     schema.TypeString,
    34  				Optional: true,
    35  			},
    36  
    37  			"fingerprint": &schema.Schema{
    38  				Type:     schema.TypeString,
    39  				Computed: true,
    40  			},
    41  
    42  			"host_rule": &schema.Schema{
    43  				Type:     schema.TypeSet,
    44  				Optional: true,
    45  				// TODO(evandbrown): Enable when lists support validation
    46  				//ValidateFunc: validateHostRules,
    47  				Elem: &schema.Resource{
    48  					Schema: map[string]*schema.Schema{
    49  						"description": &schema.Schema{
    50  							Type:     schema.TypeString,
    51  							Optional: true,
    52  						},
    53  
    54  						"hosts": &schema.Schema{
    55  							Type:     schema.TypeList,
    56  							Required: true,
    57  							Elem:     &schema.Schema{Type: schema.TypeString},
    58  						},
    59  
    60  						"path_matcher": &schema.Schema{
    61  							Type:     schema.TypeString,
    62  							Required: true,
    63  						},
    64  					},
    65  				},
    66  			},
    67  
    68  			"id": &schema.Schema{
    69  				Type:     schema.TypeString,
    70  				Computed: true,
    71  			},
    72  
    73  			"path_matcher": &schema.Schema{
    74  				Type:     schema.TypeList,
    75  				Optional: true,
    76  				Elem: &schema.Resource{
    77  					Schema: map[string]*schema.Schema{
    78  						"default_service": &schema.Schema{
    79  							Type:     schema.TypeString,
    80  							Required: true,
    81  						},
    82  
    83  						"description": &schema.Schema{
    84  							Type:     schema.TypeString,
    85  							Optional: true,
    86  						},
    87  
    88  						"name": &schema.Schema{
    89  							Type:     schema.TypeString,
    90  							Required: true,
    91  						},
    92  
    93  						"path_rule": &schema.Schema{
    94  							Type:     schema.TypeList,
    95  							Required: true,
    96  							Elem: &schema.Resource{
    97  								Schema: map[string]*schema.Schema{
    98  									"paths": &schema.Schema{
    99  										Type:     schema.TypeList,
   100  										Required: true,
   101  										Elem:     &schema.Schema{Type: schema.TypeString},
   102  									},
   103  
   104  									"service": &schema.Schema{
   105  										Type:     schema.TypeString,
   106  										Required: true,
   107  									},
   108  								},
   109  							},
   110  						},
   111  					},
   112  				},
   113  			},
   114  
   115  			"project": &schema.Schema{
   116  				Type:     schema.TypeString,
   117  				Optional: true,
   118  				ForceNew: true,
   119  			},
   120  
   121  			"self_link": &schema.Schema{
   122  				Type:     schema.TypeString,
   123  				Computed: true,
   124  			},
   125  
   126  			"test": &schema.Schema{
   127  				Type:     schema.TypeList,
   128  				Optional: true,
   129  				Elem: &schema.Resource{
   130  					Schema: map[string]*schema.Schema{
   131  						"description": &schema.Schema{
   132  							Type:     schema.TypeString,
   133  							Optional: true,
   134  						},
   135  
   136  						"host": &schema.Schema{
   137  							Type:     schema.TypeString,
   138  							Required: true,
   139  						},
   140  
   141  						"path": &schema.Schema{
   142  							Type:     schema.TypeString,
   143  							Required: true,
   144  						},
   145  
   146  						"service": &schema.Schema{
   147  							Type:     schema.TypeString,
   148  							Required: true,
   149  						},
   150  					},
   151  				},
   152  			},
   153  		},
   154  	}
   155  }
   156  
   157  func createHostRule(v interface{}) *compute.HostRule {
   158  	_hostRule := v.(map[string]interface{})
   159  
   160  	_hosts := _hostRule["hosts"].([]interface{})
   161  	hosts := make([]string, len(_hosts))
   162  
   163  	for i, v := range _hosts {
   164  		hosts[i] = v.(string)
   165  	}
   166  
   167  	pathMatcher := _hostRule["path_matcher"].(string)
   168  
   169  	hostRule := &compute.HostRule{
   170  		Hosts:       hosts,
   171  		PathMatcher: pathMatcher,
   172  	}
   173  
   174  	if v, ok := _hostRule["description"]; ok {
   175  		hostRule.Description = v.(string)
   176  	}
   177  
   178  	return hostRule
   179  }
   180  
   181  func createPathMatcher(v interface{}) *compute.PathMatcher {
   182  	_pathMatcher := v.(map[string]interface{})
   183  
   184  	_pathRules := _pathMatcher["path_rule"].([]interface{})
   185  	pathRules := make([]*compute.PathRule, len(_pathRules))
   186  
   187  	for ip, vp := range _pathRules {
   188  		_pathRule := vp.(map[string]interface{})
   189  
   190  		_paths := _pathRule["paths"].([]interface{})
   191  		paths := make([]string, len(_paths))
   192  
   193  		for ipp, vpp := range _paths {
   194  			paths[ipp] = vpp.(string)
   195  		}
   196  
   197  		service := _pathRule["service"].(string)
   198  
   199  		pathRule := &compute.PathRule{
   200  			Paths:   paths,
   201  			Service: service,
   202  		}
   203  
   204  		pathRules[ip] = pathRule
   205  	}
   206  
   207  	name := _pathMatcher["name"].(string)
   208  	defaultService := _pathMatcher["default_service"].(string)
   209  
   210  	pathMatcher := &compute.PathMatcher{
   211  		PathRules:      pathRules,
   212  		Name:           name,
   213  		DefaultService: defaultService,
   214  	}
   215  
   216  	if vp, okp := _pathMatcher["description"]; okp {
   217  		pathMatcher.Description = vp.(string)
   218  	}
   219  
   220  	return pathMatcher
   221  }
   222  
   223  func createUrlMapTest(v interface{}) *compute.UrlMapTest {
   224  	_test := v.(map[string]interface{})
   225  
   226  	host := _test["host"].(string)
   227  	path := _test["path"].(string)
   228  	service := _test["service"].(string)
   229  
   230  	test := &compute.UrlMapTest{
   231  		Host:    host,
   232  		Path:    path,
   233  		Service: service,
   234  	}
   235  
   236  	if vp, okp := _test["description"]; okp {
   237  		test.Description = vp.(string)
   238  	}
   239  
   240  	return test
   241  }
   242  
   243  func resourceComputeUrlMapCreate(d *schema.ResourceData, meta interface{}) error {
   244  	config := meta.(*Config)
   245  
   246  	project, err := getProject(d, config)
   247  	if err != nil {
   248  		return err
   249  	}
   250  
   251  	name := d.Get("name").(string)
   252  	defaultService := d.Get("default_service").(string)
   253  
   254  	urlMap := &compute.UrlMap{
   255  		Name:           name,
   256  		DefaultService: defaultService,
   257  	}
   258  
   259  	if v, ok := d.GetOk("description"); ok {
   260  		urlMap.Description = v.(string)
   261  	}
   262  
   263  	_hostRules := d.Get("host_rule").(*schema.Set)
   264  	urlMap.HostRules = make([]*compute.HostRule, _hostRules.Len())
   265  
   266  	for i, v := range _hostRules.List() {
   267  		urlMap.HostRules[i] = createHostRule(v)
   268  	}
   269  
   270  	_pathMatchers := d.Get("path_matcher").([]interface{})
   271  	urlMap.PathMatchers = make([]*compute.PathMatcher, len(_pathMatchers))
   272  
   273  	for i, v := range _pathMatchers {
   274  		urlMap.PathMatchers[i] = createPathMatcher(v)
   275  	}
   276  
   277  	_tests := make([]interface{}, 0)
   278  	if v, ok := d.GetOk("test"); ok {
   279  		_tests = v.([]interface{})
   280  	}
   281  	urlMap.Tests = make([]*compute.UrlMapTest, len(_tests))
   282  
   283  	for i, v := range _tests {
   284  		urlMap.Tests[i] = createUrlMapTest(v)
   285  	}
   286  
   287  	op, err := config.clientCompute.UrlMaps.Insert(project, urlMap).Do()
   288  
   289  	if err != nil {
   290  		return fmt.Errorf("Error, failed to insert Url Map %s: %s", name, err)
   291  	}
   292  
   293  	err = computeOperationWaitGlobal(config, op, project, "Insert Url Map")
   294  
   295  	if err != nil {
   296  		return fmt.Errorf("Error, failed waitng to insert Url Map %s: %s", name, err)
   297  	}
   298  
   299  	return resourceComputeUrlMapRead(d, meta)
   300  }
   301  
   302  func resourceComputeUrlMapRead(d *schema.ResourceData, meta interface{}) error {
   303  	config := meta.(*Config)
   304  
   305  	project, err := getProject(d, config)
   306  	if err != nil {
   307  		return err
   308  	}
   309  
   310  	name := d.Get("name").(string)
   311  
   312  	urlMap, err := config.clientCompute.UrlMaps.Get(project, name).Do()
   313  
   314  	if err != nil {
   315  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   316  			log.Printf("[WARN] Removing URL Map %q because it's gone", d.Get("name").(string))
   317  			// The resource doesn't exist anymore
   318  			d.SetId("")
   319  
   320  			return nil
   321  		}
   322  
   323  		return fmt.Errorf("Error, failed to get Url Map %s: %s", name, err)
   324  	}
   325  
   326  	d.SetId(name)
   327  	d.Set("self_link", urlMap.SelfLink)
   328  	d.Set("id", strconv.FormatUint(urlMap.Id, 10))
   329  	d.Set("fingerprint", urlMap.Fingerprint)
   330  
   331  	hostRuleMap := make(map[string]*compute.HostRule)
   332  	for _, v := range urlMap.HostRules {
   333  		hostRuleMap[v.PathMatcher] = v
   334  	}
   335  
   336  	/* Only read host rules into our TF state that we have defined */
   337  	_hostRules := d.Get("host_rule").(*schema.Set).List()
   338  	_newHostRules := make([]interface{}, 0)
   339  	for _, v := range _hostRules {
   340  		_hostRule := v.(map[string]interface{})
   341  		_pathMatcher := _hostRule["path_matcher"].(string)
   342  
   343  		/* Delete local entries that are no longer found on the GCE server */
   344  		if hostRule, ok := hostRuleMap[_pathMatcher]; ok {
   345  			_newHostRule := make(map[string]interface{})
   346  			_newHostRule["path_matcher"] = _pathMatcher
   347  
   348  			hostsSet := make(map[string]bool)
   349  			for _, host := range hostRule.Hosts {
   350  				hostsSet[host] = true
   351  			}
   352  
   353  			/* Only store hosts we are keeping track of */
   354  			_newHosts := make([]interface{}, 0)
   355  			for _, vp := range _hostRule["hosts"].([]interface{}) {
   356  				if _, okp := hostsSet[vp.(string)]; okp {
   357  					_newHosts = append(_newHosts, vp)
   358  				}
   359  			}
   360  
   361  			_newHostRule["hosts"] = _newHosts
   362  			_newHostRule["description"] = hostRule.Description
   363  
   364  			_newHostRules = append(_newHostRules, _newHostRule)
   365  		}
   366  	}
   367  
   368  	d.Set("host_rule", _newHostRules)
   369  
   370  	pathMatcherMap := make(map[string]*compute.PathMatcher)
   371  	for _, v := range urlMap.PathMatchers {
   372  		pathMatcherMap[v.Name] = v
   373  	}
   374  
   375  	/* Only read path matchers into our TF state that we have defined */
   376  	_pathMatchers := d.Get("path_matcher").([]interface{})
   377  	_newPathMatchers := make([]interface{}, 0)
   378  	for _, v := range _pathMatchers {
   379  		_pathMatcher := v.(map[string]interface{})
   380  		_name := _pathMatcher["name"].(string)
   381  
   382  		if pathMatcher, ok := pathMatcherMap[_name]; ok {
   383  			_newPathMatcher := make(map[string]interface{})
   384  			_newPathMatcher["name"] = _name
   385  			_newPathMatcher["default_service"] = pathMatcher.DefaultService
   386  			_newPathMatcher["description"] = pathMatcher.Description
   387  
   388  			_newPathRules := make([]interface{}, len(pathMatcher.PathRules))
   389  			for ip, pathRule := range pathMatcher.PathRules {
   390  				_newPathRule := make(map[string]interface{})
   391  				_newPathRule["service"] = pathRule.Service
   392  				_paths := make([]interface{}, len(pathRule.Paths))
   393  
   394  				for ipp, vpp := range pathRule.Paths {
   395  					_paths[ipp] = vpp
   396  				}
   397  
   398  				_newPathRule["paths"] = _paths
   399  
   400  				_newPathRules[ip] = _newPathRule
   401  			}
   402  
   403  			_newPathMatcher["path_rule"] = _newPathRules
   404  			_newPathMatchers = append(_newPathMatchers, _newPathMatcher)
   405  		}
   406  	}
   407  
   408  	d.Set("path_matcher", _newPathMatchers)
   409  
   410  	testMap := make(map[string]*compute.UrlMapTest)
   411  	for _, v := range urlMap.Tests {
   412  		testMap[fmt.Sprintf("%s/%s", v.Host, v.Path)] = v
   413  	}
   414  
   415  	_tests := make([]interface{}, 0)
   416  	/* Only read tests into our TF state that we have defined */
   417  	if v, ok := d.GetOk("test"); ok {
   418  		_tests = v.([]interface{})
   419  	}
   420  	_newTests := make([]interface{}, 0)
   421  	for _, v := range _tests {
   422  		_test := v.(map[string]interface{})
   423  		_host := _test["host"].(string)
   424  		_path := _test["path"].(string)
   425  
   426  		/* Delete local entries that are no longer found on the GCE server */
   427  		if test, ok := testMap[fmt.Sprintf("%s/%s", _host, _path)]; ok {
   428  			_newTest := make(map[string]interface{})
   429  			_newTest["host"] = _host
   430  			_newTest["path"] = _path
   431  			_newTest["description"] = test.Description
   432  			_newTest["service"] = test.Service
   433  
   434  			_newTests = append(_newTests, _newTest)
   435  		}
   436  	}
   437  
   438  	d.Set("test", _newTests)
   439  
   440  	return nil
   441  }
   442  
   443  func resourceComputeUrlMapUpdate(d *schema.ResourceData, meta interface{}) error {
   444  	config := meta.(*Config)
   445  
   446  	project, err := getProject(d, config)
   447  	if err != nil {
   448  		return err
   449  	}
   450  
   451  	name := d.Get("name").(string)
   452  	urlMap, err := config.clientCompute.UrlMaps.Get(project, name).Do()
   453  	if err != nil {
   454  		return fmt.Errorf("Error, failed to get Url Map %s: %s", name, err)
   455  	}
   456  
   457  	urlMap.DefaultService = d.Get("default_service").(string)
   458  
   459  	if v, ok := d.GetOk("description"); ok {
   460  		urlMap.Description = v.(string)
   461  	}
   462  
   463  	if d.HasChange("host_rule") {
   464  		_oldHostRules, _newHostRules := d.GetChange("host_rule")
   465  		_oldHostRulesMap := make(map[string]interface{})
   466  		_newHostRulesMap := make(map[string]interface{})
   467  
   468  		for _, v := range _oldHostRules.(*schema.Set).List() {
   469  			_hostRule := v.(map[string]interface{})
   470  			_oldHostRulesMap[_hostRule["path_matcher"].(string)] = v
   471  		}
   472  
   473  		for _, v := range _newHostRules.(*schema.Set).List() {
   474  			_hostRule := v.(map[string]interface{})
   475  			_newHostRulesMap[_hostRule["path_matcher"].(string)] = v
   476  		}
   477  
   478  		newHostRules := make([]*compute.HostRule, 0)
   479  		/* Decide which host rules to keep */
   480  		for _, v := range urlMap.HostRules {
   481  			/* If it's in the old state, we have ownership over the host rule */
   482  			if vOld, ok := _oldHostRulesMap[v.PathMatcher]; ok {
   483  				if vNew, ok := _newHostRulesMap[v.PathMatcher]; ok {
   484  					/* Adjust for any changes made to this rule */
   485  					_newHostRule := vNew.(map[string]interface{})
   486  					_oldHostRule := vOld.(map[string]interface{})
   487  					_newHostsSet := make(map[string]bool)
   488  					_oldHostsSet := make(map[string]bool)
   489  
   490  					hostRule := &compute.HostRule{
   491  						PathMatcher: v.PathMatcher,
   492  					}
   493  
   494  					for _, v := range _newHostRule["hosts"].([]interface{}) {
   495  						_newHostsSet[v.(string)] = true
   496  					}
   497  
   498  					for _, v := range _oldHostRule["hosts"].([]interface{}) {
   499  						_oldHostsSet[v.(string)] = true
   500  					}
   501  
   502  					/* Only add hosts that have been added locally or are new,
   503  					 * not touching those from the GCE server state */
   504  					for _, host := range v.Hosts {
   505  						_, okNew := _newHostsSet[host]
   506  						_, okOld := _oldHostsSet[host]
   507  
   508  						/* Drop deleted hosts */
   509  						if okOld && !okNew {
   510  							continue
   511  						}
   512  
   513  						hostRule.Hosts = append(hostRule.Hosts, host)
   514  
   515  						/* Kep track of the fact that this host was added */
   516  						delete(_newHostsSet, host)
   517  					}
   518  
   519  					/* Now add in the brand new entries */
   520  					for host, _ := range _newHostsSet {
   521  						hostRule.Hosts = append(hostRule.Hosts, host)
   522  					}
   523  
   524  					if v, ok := _newHostRule["description"]; ok {
   525  						hostRule.Description = v.(string)
   526  					}
   527  
   528  					newHostRules = append(newHostRules, hostRule)
   529  
   530  					/* Record that we've include this host rule */
   531  					delete(_newHostRulesMap, v.PathMatcher)
   532  				} else {
   533  					/* It's been deleted */
   534  					continue
   535  				}
   536  			} else {
   537  				if vNew, ok := _newHostRulesMap[v.PathMatcher]; ok {
   538  					newHostRules = append(newHostRules, createHostRule(vNew))
   539  
   540  					/* Record that we've include this host rule */
   541  					delete(_newHostRulesMap, v.PathMatcher)
   542  				} else {
   543  					/* It wasn't created or modified locally */
   544  					newHostRules = append(newHostRules, v)
   545  				}
   546  			}
   547  		}
   548  
   549  		/* Record brand new host rules (ones not deleted above) */
   550  		for _, v := range _newHostRulesMap {
   551  			newHostRules = append(newHostRules, createHostRule(v))
   552  		}
   553  
   554  		urlMap.HostRules = newHostRules
   555  	}
   556  
   557  	if d.HasChange("path_matcher") {
   558  		_oldPathMatchers, _newPathMatchers := d.GetChange("path_matcher")
   559  		_oldPathMatchersMap := make(map[string]interface{})
   560  		_newPathMatchersMap := make(map[string]interface{})
   561  
   562  		for _, v := range _oldPathMatchers.([]interface{}) {
   563  			_pathMatcher := v.(map[string]interface{})
   564  			_oldPathMatchersMap[_pathMatcher["name"].(string)] = v
   565  		}
   566  
   567  		for _, v := range _newPathMatchers.([]interface{}) {
   568  			_pathMatcher := v.(map[string]interface{})
   569  			_newPathMatchersMap[_pathMatcher["name"].(string)] = v
   570  		}
   571  
   572  		newPathMatchers := make([]*compute.PathMatcher, 0)
   573  		/* Decide which path matchers to keep */
   574  		for _, v := range urlMap.PathMatchers {
   575  			/* If it's in the old state, we have ownership over the host rule */
   576  			_, okOld := _oldPathMatchersMap[v.Name]
   577  			vNew, okNew := _newPathMatchersMap[v.Name]
   578  
   579  			/* Drop deleted entries */
   580  			if okOld && !okNew {
   581  				continue
   582  			}
   583  
   584  			/* Don't change entries that don't belong to us */
   585  			if !okNew {
   586  				newPathMatchers = append(newPathMatchers, v)
   587  			} else {
   588  				newPathMatchers = append(newPathMatchers, createPathMatcher(vNew))
   589  
   590  				delete(_newPathMatchersMap, v.Name)
   591  			}
   592  		}
   593  
   594  		/* Record brand new host rules */
   595  		for _, v := range _newPathMatchersMap {
   596  			newPathMatchers = append(newPathMatchers, createPathMatcher(v))
   597  		}
   598  
   599  		urlMap.PathMatchers = newPathMatchers
   600  	}
   601  
   602  	if d.HasChange("test") {
   603  		_oldTests, _newTests := d.GetChange("test")
   604  		_oldTestsMap := make(map[string]interface{})
   605  		_newTestsMap := make(map[string]interface{})
   606  
   607  		for _, v := range _oldTests.([]interface{}) {
   608  			_test := v.(map[string]interface{})
   609  			ident := fmt.Sprintf("%s/%s", _test["host"].(string), _test["path"].(string))
   610  			_oldTestsMap[ident] = v
   611  		}
   612  
   613  		for _, v := range _newTests.([]interface{}) {
   614  			_test := v.(map[string]interface{})
   615  			ident := fmt.Sprintf("%s/%s", _test["host"].(string), _test["path"].(string))
   616  			_newTestsMap[ident] = v
   617  		}
   618  
   619  		newTests := make([]*compute.UrlMapTest, 0)
   620  		/* Decide which path matchers to keep */
   621  		for _, v := range urlMap.Tests {
   622  			ident := fmt.Sprintf("%s/%s", v.Host, v.Path)
   623  			/* If it's in the old state, we have ownership over the host rule */
   624  			_, okOld := _oldTestsMap[ident]
   625  			vNew, okNew := _newTestsMap[ident]
   626  
   627  			/* Drop deleted entries */
   628  			if okOld && !okNew {
   629  				continue
   630  			}
   631  
   632  			/* Don't change entries that don't belong to us */
   633  			if !okNew {
   634  				newTests = append(newTests, v)
   635  			} else {
   636  				newTests = append(newTests, createUrlMapTest(vNew))
   637  
   638  				delete(_newTestsMap, ident)
   639  			}
   640  		}
   641  
   642  		/* Record brand new host rules */
   643  		for _, v := range _newTestsMap {
   644  			newTests = append(newTests, createUrlMapTest(v))
   645  		}
   646  
   647  		urlMap.Tests = newTests
   648  	}
   649  	op, err := config.clientCompute.UrlMaps.Update(project, urlMap.Name, urlMap).Do()
   650  
   651  	if err != nil {
   652  		return fmt.Errorf("Error, failed to update Url Map %s: %s", name, err)
   653  	}
   654  
   655  	err = computeOperationWaitGlobal(config, op, project, "Update Url Map")
   656  
   657  	if err != nil {
   658  		return fmt.Errorf("Error, failed waitng to update Url Map %s: %s", name, err)
   659  	}
   660  
   661  	return resourceComputeUrlMapRead(d, meta)
   662  }
   663  
   664  func resourceComputeUrlMapDelete(d *schema.ResourceData, meta interface{}) error {
   665  	config := meta.(*Config)
   666  
   667  	project, err := getProject(d, config)
   668  	if err != nil {
   669  		return err
   670  	}
   671  
   672  	name := d.Get("name").(string)
   673  
   674  	op, err := config.clientCompute.UrlMaps.Delete(project, name).Do()
   675  
   676  	if err != nil {
   677  		return fmt.Errorf("Error, failed to delete Url Map %s: %s", name, err)
   678  	}
   679  
   680  	err = computeOperationWaitGlobal(config, op, project, "Delete Url Map")
   681  
   682  	if err != nil {
   683  		return fmt.Errorf("Error, failed waitng to delete Url Map %s: %s", name, err)
   684  	}
   685  
   686  	return nil
   687  }
   688  
   689  func validateHostRules(v interface{}, k string) (ws []string, es []error) {
   690  	pathMatchers := make(map[string]bool)
   691  	hostRules := v.([]interface{})
   692  	for _, hri := range hostRules {
   693  		hr := hri.(map[string]interface{})
   694  		pm := hr["path_matcher"].(string)
   695  		if pathMatchers[pm] {
   696  			es = append(es, fmt.Errorf("Multiple host_rule entries with the same path_matcher are not allowed. Please collapse all hosts with the same path_matcher into one host_rule"))
   697  			return
   698  		}
   699  		pathMatchers[pm] = true
   700  	}
   701  	return
   702  }