github.com/IBM-Cloud/terraform@v0.6.4-0.20170726051544-8872b87621df/builtin/providers/google/resource_compute_url_map.go (about)

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