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  }