github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/azurerm/resource_arm_container_service.go (about) 1 package azurerm 2 3 import ( 4 "fmt" 5 "log" 6 7 "net/http" 8 9 "time" 10 11 "bytes" 12 13 "github.com/Azure/azure-sdk-for-go/arm/containerservice" 14 "github.com/hashicorp/terraform/helper/hashcode" 15 "github.com/hashicorp/terraform/helper/resource" 16 "github.com/hashicorp/terraform/helper/schema" 17 ) 18 19 func resourceArmContainerService() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceArmContainerServiceCreate, 22 Read: resourceArmContainerServiceRead, 23 Update: resourceArmContainerServiceCreate, 24 Delete: resourceArmContainerServiceDelete, 25 26 Schema: map[string]*schema.Schema{ 27 "name": { 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 }, 32 33 "location": locationSchema(), 34 35 "resource_group_name": { 36 Type: schema.TypeString, 37 Required: true, 38 ForceNew: true, 39 }, 40 41 "orchestration_platform": { 42 Type: schema.TypeString, 43 Required: true, 44 ForceNew: true, 45 ValidateFunc: validateArmContainerServiceOrchestrationPlatform, 46 }, 47 48 "master_profile": { 49 Type: schema.TypeSet, 50 Required: true, 51 MaxItems: 1, 52 Elem: &schema.Resource{ 53 Schema: map[string]*schema.Schema{ 54 "count": { 55 Type: schema.TypeInt, 56 Optional: true, 57 Default: 1, 58 ValidateFunc: validateArmContainerServiceMasterProfileCount, 59 }, 60 61 "dns_prefix": { 62 Type: schema.TypeString, 63 Required: true, 64 }, 65 66 "fqdn": { 67 Type: schema.TypeString, 68 Computed: true, 69 }, 70 }, 71 }, 72 Set: resourceAzureRMContainerServiceMasterProfileHash, 73 }, 74 75 "linux_profile": { 76 Type: schema.TypeSet, 77 Required: true, 78 MaxItems: 1, 79 Elem: &schema.Resource{ 80 Schema: map[string]*schema.Schema{ 81 "admin_username": { 82 Type: schema.TypeString, 83 Required: true, 84 }, 85 "ssh_key": { 86 Type: schema.TypeSet, 87 Required: true, 88 MaxItems: 1, 89 Elem: &schema.Resource{ 90 Schema: map[string]*schema.Schema{ 91 "key_data": { 92 Type: schema.TypeString, 93 Required: true, 94 }, 95 }, 96 }, 97 }, 98 }, 99 }, 100 Set: resourceAzureRMContainerServiceLinuxProfilesHash, 101 }, 102 103 "agent_pool_profile": { 104 Type: schema.TypeSet, 105 Required: true, 106 MaxItems: 1, 107 Elem: &schema.Resource{ 108 Schema: map[string]*schema.Schema{ 109 "name": { 110 Type: schema.TypeString, 111 Required: true, 112 ForceNew: true, 113 }, 114 115 "count": { 116 Type: schema.TypeInt, 117 Optional: true, 118 Default: 1, 119 ValidateFunc: validateArmContainerServiceAgentPoolProfileCount, 120 }, 121 122 "dns_prefix": { 123 Type: schema.TypeString, 124 Required: true, 125 ForceNew: true, 126 }, 127 128 "fqdn": { 129 Type: schema.TypeString, 130 Computed: true, 131 }, 132 133 "vm_size": { 134 Type: schema.TypeString, 135 Required: true, 136 }, 137 }, 138 }, 139 Set: resourceAzureRMContainerServiceAgentPoolProfilesHash, 140 }, 141 142 "service_principal": { 143 Type: schema.TypeSet, 144 Optional: true, 145 MaxItems: 1, 146 Elem: &schema.Resource{ 147 Schema: map[string]*schema.Schema{ 148 "client_id": { 149 Type: schema.TypeString, 150 Required: true, 151 }, 152 153 "client_secret": { 154 Type: schema.TypeString, 155 Required: true, 156 Sensitive: true, 157 }, 158 }, 159 }, 160 Set: resourceAzureRMContainerServiceServicePrincipalProfileHash, 161 }, 162 163 "diagnostics_profile": { 164 Type: schema.TypeSet, 165 Required: true, 166 MaxItems: 1, 167 Elem: &schema.Resource{ 168 Schema: map[string]*schema.Schema{ 169 "enabled": { 170 Type: schema.TypeBool, 171 Required: true, 172 }, 173 174 "storage_uri": { 175 Type: schema.TypeString, 176 Computed: true, 177 }, 178 }, 179 }, 180 Set: resourceAzureRMContainerServiceDiagnosticProfilesHash, 181 }, 182 183 "tags": tagsSchema(), 184 }, 185 } 186 } 187 188 func resourceArmContainerServiceCreate(d *schema.ResourceData, meta interface{}) error { 189 client := meta.(*ArmClient) 190 containerServiceClient := client.containerServicesClient 191 192 log.Printf("[INFO] preparing arguments for Azure ARM Container Service creation.") 193 194 resGroup := d.Get("resource_group_name").(string) 195 name := d.Get("name").(string) 196 location := d.Get("location").(string) 197 198 orchestrationPlatform := d.Get("orchestration_platform").(string) 199 200 masterProfile := expandAzureRmContainerServiceMasterProfile(d) 201 linuxProfile := expandAzureRmContainerServiceLinuxProfile(d) 202 agentProfiles := expandAzureRmContainerServiceAgentProfiles(d) 203 diagnosticsProfile := expandAzureRmContainerServiceDiagnostics(d) 204 205 tags := d.Get("tags").(map[string]interface{}) 206 207 parameters := containerservice.ContainerService{ 208 Name: &name, 209 Location: &location, 210 Properties: &containerservice.Properties{ 211 MasterProfile: &masterProfile, 212 LinuxProfile: &linuxProfile, 213 OrchestratorProfile: &containerservice.OrchestratorProfile{ 214 OrchestratorType: containerservice.OchestratorTypes(orchestrationPlatform), 215 }, 216 AgentPoolProfiles: &agentProfiles, 217 DiagnosticsProfile: &diagnosticsProfile, 218 }, 219 Tags: expandTags(tags), 220 } 221 222 servicePrincipalProfile := expandAzureRmContainerServiceServicePrincipal(d) 223 if servicePrincipalProfile != nil { 224 parameters.ServicePrincipalProfile = servicePrincipalProfile 225 } 226 227 _, error := containerServiceClient.CreateOrUpdate(resGroup, name, parameters, make(chan struct{})) 228 err := <-error 229 if err != nil { 230 return err 231 } 232 233 read, err := containerServiceClient.Get(resGroup, name) 234 if err != nil { 235 return err 236 } 237 238 if read.ID == nil { 239 return fmt.Errorf("Cannot read Container Service %s (resource group %s) ID", name, resGroup) 240 } 241 242 log.Printf("[DEBUG] Waiting for Container Service (%s) to become available", d.Get("name")) 243 stateConf := &resource.StateChangeConf{ 244 Pending: []string{"Updating", "Creating"}, 245 Target: []string{"Succeeded"}, 246 Refresh: containerServiceStateRefreshFunc(client, resGroup, name), 247 Timeout: 30 * time.Minute, 248 MinTimeout: 15 * time.Second, 249 } 250 if _, err := stateConf.WaitForState(); err != nil { 251 return fmt.Errorf("Error waiting for Container Service (%s) to become available: %s", d.Get("name"), err) 252 } 253 254 d.SetId(*read.ID) 255 256 return resourceArmContainerServiceRead(d, meta) 257 } 258 259 func resourceArmContainerServiceRead(d *schema.ResourceData, meta interface{}) error { 260 containerServiceClient := meta.(*ArmClient).containerServicesClient 261 262 id, err := parseAzureResourceID(d.Id()) 263 if err != nil { 264 return err 265 } 266 resGroup := id.ResourceGroup 267 name := id.Path["containerServices"] 268 269 resp, err := containerServiceClient.Get(resGroup, name) 270 if err != nil { 271 return fmt.Errorf("Error making Read request on Azure Container Service %s: %s", name, err) 272 } 273 if resp.StatusCode == http.StatusNotFound { 274 d.SetId("") 275 return nil 276 } 277 278 d.Set("name", resp.Name) 279 d.Set("location", azureRMNormalizeLocation(*resp.Location)) 280 d.Set("resource_group_name", resGroup) 281 282 d.Set("orchestration_platform", string(resp.Properties.OrchestratorProfile.OrchestratorType)) 283 284 masterProfiles := flattenAzureRmContainerServiceMasterProfile(*resp.Properties.MasterProfile) 285 d.Set("master_profile", &masterProfiles) 286 287 linuxProfile := flattenAzureRmContainerServiceLinuxProfile(*resp.Properties.LinuxProfile) 288 d.Set("linux_profile", &linuxProfile) 289 290 agentPoolProfiles := flattenAzureRmContainerServiceAgentPoolProfiles(resp.Properties.AgentPoolProfiles) 291 d.Set("agent_pool_profile", &agentPoolProfiles) 292 293 servicePrincipal := flattenAzureRmContainerServiceServicePrincipalProfile(resp.Properties.ServicePrincipalProfile) 294 if servicePrincipal != nil { 295 d.Set("service_principal", servicePrincipal) 296 } 297 298 diagnosticProfile := flattenAzureRmContainerServiceDiagnosticsProfile(resp.Properties.DiagnosticsProfile) 299 if diagnosticProfile != nil { 300 d.Set("diagnostics_profile", diagnosticProfile) 301 } 302 303 flattenAndSetTags(d, resp.Tags) 304 305 return nil 306 } 307 308 func resourceArmContainerServiceDelete(d *schema.ResourceData, meta interface{}) error { 309 client := meta.(*ArmClient) 310 containerServiceClient := client.containerServicesClient 311 312 id, err := parseAzureResourceID(d.Id()) 313 if err != nil { 314 return err 315 } 316 resGroup := id.ResourceGroup 317 name := id.Path["containerServices"] 318 319 delResp, error := containerServiceClient.Delete(resGroup, name, make(chan struct{})) 320 resp := <-delResp 321 err = <-error 322 if err != nil { 323 return err 324 } 325 326 if resp.StatusCode != http.StatusOK { 327 return fmt.Errorf("Error issuing Azure ARM delete request of Container Service '%s': %s", name, err) 328 } 329 330 return nil 331 332 } 333 334 func flattenAzureRmContainerServiceMasterProfile(profile containerservice.MasterProfile) *schema.Set { 335 masterProfiles := &schema.Set{ 336 F: resourceAzureRMContainerServiceMasterProfileHash, 337 } 338 339 masterProfile := make(map[string]interface{}, 2) 340 341 masterProfile["count"] = int(*profile.Count) 342 masterProfile["dns_prefix"] = *profile.DNSPrefix 343 344 masterProfiles.Add(masterProfile) 345 346 return masterProfiles 347 } 348 349 func flattenAzureRmContainerServiceLinuxProfile(profile containerservice.LinuxProfile) *schema.Set { 350 profiles := &schema.Set{ 351 F: resourceAzureRMContainerServiceLinuxProfilesHash, 352 } 353 354 values := map[string]interface{}{} 355 356 sshKeys := &schema.Set{ 357 F: resourceAzureRMContainerServiceLinuxProfilesSSHKeysHash, 358 } 359 for _, ssh := range *profile.SSH.PublicKeys { 360 keys := map[string]interface{}{} 361 keys["key_data"] = *ssh.KeyData 362 sshKeys.Add(keys) 363 } 364 365 values["admin_username"] = *profile.AdminUsername 366 values["ssh_key"] = sshKeys 367 profiles.Add(values) 368 369 return profiles 370 } 371 372 func flattenAzureRmContainerServiceAgentPoolProfiles(profiles *[]containerservice.AgentPoolProfile) *schema.Set { 373 agentPoolProfiles := &schema.Set{ 374 F: resourceAzureRMContainerServiceAgentPoolProfilesHash, 375 } 376 377 for _, profile := range *profiles { 378 agentPoolProfile := map[string]interface{}{} 379 agentPoolProfile["count"] = int(*profile.Count) 380 agentPoolProfile["dns_prefix"] = *profile.DNSPrefix 381 agentPoolProfile["fqdn"] = *profile.Fqdn 382 agentPoolProfile["name"] = *profile.Name 383 agentPoolProfile["vm_size"] = string(profile.VMSize) 384 agentPoolProfiles.Add(agentPoolProfile) 385 } 386 387 return agentPoolProfiles 388 } 389 390 func flattenAzureRmContainerServiceServicePrincipalProfile(profile *containerservice.ServicePrincipalProfile) *schema.Set { 391 392 if profile == nil { 393 return nil 394 } 395 396 servicePrincipalProfiles := &schema.Set{ 397 F: resourceAzureRMContainerServiceServicePrincipalProfileHash, 398 } 399 400 values := map[string]interface{}{} 401 402 values["client_id"] = *profile.ClientID 403 if profile.Secret != nil { 404 values["client_secret"] = *profile.Secret 405 } 406 407 servicePrincipalProfiles.Add(values) 408 409 return servicePrincipalProfiles 410 } 411 412 func flattenAzureRmContainerServiceDiagnosticsProfile(profile *containerservice.DiagnosticsProfile) *schema.Set { 413 diagnosticProfiles := &schema.Set{ 414 F: resourceAzureRMContainerServiceDiagnosticProfilesHash, 415 } 416 417 values := map[string]interface{}{} 418 419 values["enabled"] = *profile.VMDiagnostics.Enabled 420 if profile.VMDiagnostics.StorageURI != nil { 421 values["storage_uri"] = *profile.VMDiagnostics.StorageURI 422 } 423 diagnosticProfiles.Add(values) 424 425 return diagnosticProfiles 426 } 427 428 func expandAzureRmContainerServiceDiagnostics(d *schema.ResourceData) containerservice.DiagnosticsProfile { 429 configs := d.Get("diagnostics_profile").(*schema.Set).List() 430 profile := containerservice.DiagnosticsProfile{} 431 432 data := configs[0].(map[string]interface{}) 433 434 enabled := data["enabled"].(bool) 435 436 profile = containerservice.DiagnosticsProfile{ 437 VMDiagnostics: &containerservice.VMDiagnostics{ 438 Enabled: &enabled, 439 }, 440 } 441 442 return profile 443 } 444 445 func expandAzureRmContainerServiceLinuxProfile(d *schema.ResourceData) containerservice.LinuxProfile { 446 profiles := d.Get("linux_profile").(*schema.Set).List() 447 config := profiles[0].(map[string]interface{}) 448 449 adminUsername := config["admin_username"].(string) 450 451 linuxKeys := config["ssh_key"].(*schema.Set).List() 452 sshPublicKeys := []containerservice.SSHPublicKey{} 453 454 key := linuxKeys[0].(map[string]interface{}) 455 keyData := key["key_data"].(string) 456 457 sshPublicKey := containerservice.SSHPublicKey{ 458 KeyData: &keyData, 459 } 460 461 sshPublicKeys = append(sshPublicKeys, sshPublicKey) 462 463 profile := containerservice.LinuxProfile{ 464 AdminUsername: &adminUsername, 465 SSH: &containerservice.SSHConfiguration{ 466 PublicKeys: &sshPublicKeys, 467 }, 468 } 469 470 return profile 471 } 472 473 func expandAzureRmContainerServiceMasterProfile(d *schema.ResourceData) containerservice.MasterProfile { 474 configs := d.Get("master_profile").(*schema.Set).List() 475 config := configs[0].(map[string]interface{}) 476 477 count := int32(config["count"].(int)) 478 dnsPrefix := config["dns_prefix"].(string) 479 480 profile := containerservice.MasterProfile{ 481 Count: &count, 482 DNSPrefix: &dnsPrefix, 483 } 484 485 return profile 486 } 487 488 func expandAzureRmContainerServiceServicePrincipal(d *schema.ResourceData) *containerservice.ServicePrincipalProfile { 489 490 value, exists := d.GetOk("service_principal") 491 if !exists { 492 return nil 493 } 494 495 configs := value.(*schema.Set).List() 496 497 config := configs[0].(map[string]interface{}) 498 499 clientId := config["client_id"].(string) 500 clientSecret := config["client_secret"].(string) 501 502 principal := containerservice.ServicePrincipalProfile{ 503 ClientID: &clientId, 504 Secret: &clientSecret, 505 } 506 507 return &principal 508 } 509 510 func expandAzureRmContainerServiceAgentProfiles(d *schema.ResourceData) []containerservice.AgentPoolProfile { 511 configs := d.Get("agent_pool_profile").(*schema.Set).List() 512 config := configs[0].(map[string]interface{}) 513 profiles := make([]containerservice.AgentPoolProfile, 0, len(configs)) 514 515 name := config["name"].(string) 516 count := int32(config["count"].(int)) 517 dnsPrefix := config["dns_prefix"].(string) 518 vmSize := config["vm_size"].(string) 519 520 profile := containerservice.AgentPoolProfile{ 521 Name: &name, 522 Count: &count, 523 VMSize: containerservice.VMSizeTypes(vmSize), 524 DNSPrefix: &dnsPrefix, 525 } 526 527 profiles = append(profiles, profile) 528 529 return profiles 530 } 531 532 func containerServiceStateRefreshFunc(client *ArmClient, resourceGroupName string, containerServiceName string) resource.StateRefreshFunc { 533 return func() (interface{}, string, error) { 534 res, err := client.containerServicesClient.Get(resourceGroupName, containerServiceName) 535 if err != nil { 536 return nil, "", fmt.Errorf("Error issuing read request in containerServiceStateRefreshFunc to Azure ARM for Container Service '%s' (RG: '%s'): %s", containerServiceName, resourceGroupName, err) 537 } 538 539 return res, *res.Properties.ProvisioningState, nil 540 } 541 } 542 543 func resourceAzureRMContainerServiceMasterProfileHash(v interface{}) int { 544 var buf bytes.Buffer 545 m := v.(map[string]interface{}) 546 547 count := m["count"].(int) 548 dnsPrefix := m["dns_prefix"].(string) 549 550 buf.WriteString(fmt.Sprintf("%d-", count)) 551 buf.WriteString(fmt.Sprintf("%s-", dnsPrefix)) 552 553 return hashcode.String(buf.String()) 554 } 555 556 func resourceAzureRMContainerServiceLinuxProfilesHash(v interface{}) int { 557 var buf bytes.Buffer 558 m := v.(map[string]interface{}) 559 560 adminUsername := m["admin_username"].(string) 561 562 buf.WriteString(fmt.Sprintf("%s-", adminUsername)) 563 564 return hashcode.String(buf.String()) 565 } 566 567 func resourceAzureRMContainerServiceLinuxProfilesSSHKeysHash(v interface{}) int { 568 var buf bytes.Buffer 569 m := v.(map[string]interface{}) 570 571 keyData := m["key_data"].(string) 572 573 buf.WriteString(fmt.Sprintf("%s-", keyData)) 574 575 return hashcode.String(buf.String()) 576 } 577 578 func resourceAzureRMContainerServiceAgentPoolProfilesHash(v interface{}) int { 579 var buf bytes.Buffer 580 m := v.(map[string]interface{}) 581 582 count := m["count"].(int) 583 dnsPrefix := m["dns_prefix"].(string) 584 name := m["name"].(string) 585 vm_size := m["vm_size"].(string) 586 587 buf.WriteString(fmt.Sprintf("%d-", count)) 588 buf.WriteString(fmt.Sprintf("%s-", dnsPrefix)) 589 buf.WriteString(fmt.Sprintf("%s-", name)) 590 buf.WriteString(fmt.Sprintf("%s-", vm_size)) 591 592 return hashcode.String(buf.String()) 593 } 594 595 func resourceAzureRMContainerServiceServicePrincipalProfileHash(v interface{}) int { 596 var buf bytes.Buffer 597 m := v.(map[string]interface{}) 598 599 clientId := m["client_id"].(string) 600 buf.WriteString(fmt.Sprintf("%s-", clientId)) 601 602 return hashcode.String(buf.String()) 603 } 604 605 func resourceAzureRMContainerServiceDiagnosticProfilesHash(v interface{}) int { 606 var buf bytes.Buffer 607 m := v.(map[string]interface{}) 608 609 enabled := m["enabled"].(bool) 610 611 buf.WriteString(fmt.Sprintf("%t", enabled)) 612 613 return hashcode.String(buf.String()) 614 } 615 616 func validateArmContainerServiceOrchestrationPlatform(v interface{}, k string) (ws []string, errors []error) { 617 value := v.(string) 618 capacities := map[string]bool{ 619 "DCOS": true, 620 "Kubernetes": true, 621 "Swarm": true, 622 } 623 624 if !capacities[value] { 625 errors = append(errors, fmt.Errorf("Container Service: Orchestration Platgorm can only be DCOS / Kubernetes / Swarm")) 626 } 627 return 628 } 629 630 func validateArmContainerServiceMasterProfileCount(v interface{}, k string) (ws []string, errors []error) { 631 value := v.(int) 632 capacities := map[int]bool{ 633 1: true, 634 3: true, 635 5: true, 636 } 637 638 if !capacities[value] { 639 errors = append(errors, fmt.Errorf("The number of master nodes must be 1, 3 or 5.")) 640 } 641 return 642 } 643 644 func validateArmContainerServiceAgentPoolProfileCount(v interface{}, k string) (ws []string, errors []error) { 645 value := v.(int) 646 if value > 100 || 0 >= value { 647 errors = append(errors, fmt.Errorf("The Count for an Agent Pool Profile can only be between 1 and 100.")) 648 } 649 return 650 }