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 }