github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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  	_, err := containerServiceClient.CreateOrUpdate(resGroup, name, parameters, make(chan struct{}))
   228  	if err != nil {
   229  		return err
   230  	}
   231  
   232  	read, err := containerServiceClient.Get(resGroup, name)
   233  	if err != nil {
   234  		return err
   235  	}
   236  
   237  	if read.ID == nil {
   238  		return fmt.Errorf("Cannot read Container Service %s (resource group %s) ID", name, resGroup)
   239  	}
   240  
   241  	log.Printf("[DEBUG] Waiting for Container Service (%s) to become available", d.Get("name"))
   242  	stateConf := &resource.StateChangeConf{
   243  		Pending:    []string{"Updating", "Creating"},
   244  		Target:     []string{"Succeeded"},
   245  		Refresh:    containerServiceStateRefreshFunc(client, resGroup, name),
   246  		Timeout:    30 * time.Minute,
   247  		MinTimeout: 15 * time.Second,
   248  	}
   249  	if _, err := stateConf.WaitForState(); err != nil {
   250  		return fmt.Errorf("Error waiting for Container Service (%s) to become available: %s", d.Get("name"), err)
   251  	}
   252  
   253  	d.SetId(*read.ID)
   254  
   255  	return resourceArmContainerServiceRead(d, meta)
   256  }
   257  
   258  func resourceArmContainerServiceRead(d *schema.ResourceData, meta interface{}) error {
   259  	containerServiceClient := meta.(*ArmClient).containerServicesClient
   260  
   261  	id, err := parseAzureResourceID(d.Id())
   262  	if err != nil {
   263  		return err
   264  	}
   265  	resGroup := id.ResourceGroup
   266  	name := id.Path["containerServices"]
   267  
   268  	resp, err := containerServiceClient.Get(resGroup, name)
   269  	if err != nil {
   270  		return fmt.Errorf("Error making Read request on Azure Container Service %s: %s", name, err)
   271  	}
   272  	if resp.StatusCode == http.StatusNotFound {
   273  		d.SetId("")
   274  		return nil
   275  	}
   276  
   277  	d.Set("name", resp.Name)
   278  	d.Set("location", azureRMNormalizeLocation(*resp.Location))
   279  	d.Set("resource_group_name", resGroup)
   280  
   281  	d.Set("orchestration_platform", string(resp.Properties.OrchestratorProfile.OrchestratorType))
   282  
   283  	masterProfiles := flattenAzureRmContainerServiceMasterProfile(*resp.Properties.MasterProfile)
   284  	d.Set("master_profile", &masterProfiles)
   285  
   286  	linuxProfile := flattenAzureRmContainerServiceLinuxProfile(*resp.Properties.LinuxProfile)
   287  	d.Set("linux_profile", &linuxProfile)
   288  
   289  	agentPoolProfiles := flattenAzureRmContainerServiceAgentPoolProfiles(resp.Properties.AgentPoolProfiles)
   290  	d.Set("agent_pool_profile", &agentPoolProfiles)
   291  
   292  	servicePrincipal := flattenAzureRmContainerServiceServicePrincipalProfile(resp.Properties.ServicePrincipalProfile)
   293  	if servicePrincipal != nil {
   294  		d.Set("service_principal", servicePrincipal)
   295  	}
   296  
   297  	diagnosticProfile := flattenAzureRmContainerServiceDiagnosticsProfile(resp.Properties.DiagnosticsProfile)
   298  	if diagnosticProfile != nil {
   299  		d.Set("diagnostics_profile", diagnosticProfile)
   300  	}
   301  
   302  	flattenAndSetTags(d, resp.Tags)
   303  
   304  	return nil
   305  }
   306  
   307  func resourceArmContainerServiceDelete(d *schema.ResourceData, meta interface{}) error {
   308  	client := meta.(*ArmClient)
   309  	containerServiceClient := client.containerServicesClient
   310  
   311  	id, err := parseAzureResourceID(d.Id())
   312  	if err != nil {
   313  		return err
   314  	}
   315  	resGroup := id.ResourceGroup
   316  	name := id.Path["containerServices"]
   317  
   318  	resp, err := containerServiceClient.Delete(resGroup, name, make(chan struct{}))
   319  	if err != nil {
   320  		return err
   321  	}
   322  
   323  	if resp.StatusCode != http.StatusOK {
   324  		return fmt.Errorf("Error issuing Azure ARM delete request of Container Service '%s': %s", name, err)
   325  	}
   326  
   327  	return nil
   328  
   329  }
   330  
   331  func flattenAzureRmContainerServiceMasterProfile(profile containerservice.MasterProfile) *schema.Set {
   332  	masterProfiles := &schema.Set{
   333  		F: resourceAzureRMContainerServiceMasterProfileHash,
   334  	}
   335  
   336  	masterProfile := make(map[string]interface{}, 2)
   337  
   338  	masterProfile["count"] = int(*profile.Count)
   339  	masterProfile["dns_prefix"] = *profile.DNSPrefix
   340  
   341  	masterProfiles.Add(masterProfile)
   342  
   343  	return masterProfiles
   344  }
   345  
   346  func flattenAzureRmContainerServiceLinuxProfile(profile containerservice.LinuxProfile) *schema.Set {
   347  	profiles := &schema.Set{
   348  		F: resourceAzureRMContainerServiceLinuxProfilesHash,
   349  	}
   350  
   351  	values := map[string]interface{}{}
   352  
   353  	sshKeys := &schema.Set{
   354  		F: resourceAzureRMContainerServiceLinuxProfilesSSHKeysHash,
   355  	}
   356  	for _, ssh := range *profile.SSH.PublicKeys {
   357  		keys := map[string]interface{}{}
   358  		keys["key_data"] = *ssh.KeyData
   359  		sshKeys.Add(keys)
   360  	}
   361  
   362  	values["admin_username"] = *profile.AdminUsername
   363  	values["ssh_key"] = &sshKeys
   364  	profiles.Add(values)
   365  
   366  	return profiles
   367  }
   368  
   369  func flattenAzureRmContainerServiceAgentPoolProfiles(profiles *[]containerservice.AgentPoolProfile) *schema.Set {
   370  	agentPoolProfiles := &schema.Set{
   371  		F: resourceAzureRMContainerServiceAgentPoolProfilesHash,
   372  	}
   373  
   374  	for _, profile := range *profiles {
   375  		agentPoolProfile := map[string]interface{}{}
   376  		agentPoolProfile["count"] = int(*profile.Count)
   377  		agentPoolProfile["dns_prefix"] = *profile.DNSPrefix
   378  		agentPoolProfile["fqdn"] = *profile.Fqdn
   379  		agentPoolProfile["name"] = *profile.Name
   380  		agentPoolProfile["vm_size"] = string(profile.VMSize)
   381  		agentPoolProfiles.Add(agentPoolProfile)
   382  	}
   383  
   384  	return agentPoolProfiles
   385  }
   386  
   387  func flattenAzureRmContainerServiceServicePrincipalProfile(profile *containerservice.ServicePrincipalProfile) *schema.Set {
   388  
   389  	if profile == nil {
   390  		return nil
   391  	}
   392  
   393  	servicePrincipalProfiles := &schema.Set{
   394  		F: resourceAzureRMContainerServiceServicePrincipalProfileHash,
   395  	}
   396  
   397  	values := map[string]interface{}{}
   398  
   399  	values["client_id"] = *profile.ClientID
   400  	if profile.Secret != nil {
   401  		values["client_secret"] = *profile.Secret
   402  	}
   403  
   404  	servicePrincipalProfiles.Add(values)
   405  
   406  	return servicePrincipalProfiles
   407  }
   408  
   409  func flattenAzureRmContainerServiceDiagnosticsProfile(profile *containerservice.DiagnosticsProfile) *schema.Set {
   410  	diagnosticProfiles := &schema.Set{
   411  		F: resourceAzureRMContainerServiceDiagnosticProfilesHash,
   412  	}
   413  
   414  	values := map[string]interface{}{}
   415  
   416  	values["enabled"] = *profile.VMDiagnostics.Enabled
   417  	if profile.VMDiagnostics.StorageURI != nil {
   418  		values["storage_uri"] = *profile.VMDiagnostics.StorageURI
   419  	}
   420  	diagnosticProfiles.Add(values)
   421  
   422  	return diagnosticProfiles
   423  }
   424  
   425  func expandAzureRmContainerServiceDiagnostics(d *schema.ResourceData) containerservice.DiagnosticsProfile {
   426  	configs := d.Get("diagnostics_profile").(*schema.Set).List()
   427  	profile := containerservice.DiagnosticsProfile{}
   428  
   429  	data := configs[0].(map[string]interface{})
   430  
   431  	enabled := data["enabled"].(bool)
   432  
   433  	profile = containerservice.DiagnosticsProfile{
   434  		VMDiagnostics: &containerservice.VMDiagnostics{
   435  			Enabled: &enabled,
   436  		},
   437  	}
   438  
   439  	return profile
   440  }
   441  
   442  func expandAzureRmContainerServiceLinuxProfile(d *schema.ResourceData) containerservice.LinuxProfile {
   443  	profiles := d.Get("linux_profile").(*schema.Set).List()
   444  	config := profiles[0].(map[string]interface{})
   445  
   446  	adminUsername := config["admin_username"].(string)
   447  
   448  	linuxKeys := config["ssh_key"].(*schema.Set).List()
   449  	sshPublicKeys := []containerservice.SSHPublicKey{}
   450  
   451  	key := linuxKeys[0].(map[string]interface{})
   452  	keyData := key["key_data"].(string)
   453  
   454  	sshPublicKey := containerservice.SSHPublicKey{
   455  		KeyData: &keyData,
   456  	}
   457  
   458  	sshPublicKeys = append(sshPublicKeys, sshPublicKey)
   459  
   460  	profile := containerservice.LinuxProfile{
   461  		AdminUsername: &adminUsername,
   462  		SSH: &containerservice.SSHConfiguration{
   463  			PublicKeys: &sshPublicKeys,
   464  		},
   465  	}
   466  
   467  	return profile
   468  }
   469  
   470  func expandAzureRmContainerServiceMasterProfile(d *schema.ResourceData) containerservice.MasterProfile {
   471  	configs := d.Get("master_profile").(*schema.Set).List()
   472  	config := configs[0].(map[string]interface{})
   473  
   474  	count := int32(config["count"].(int))
   475  	dnsPrefix := config["dns_prefix"].(string)
   476  
   477  	profile := containerservice.MasterProfile{
   478  		Count:     &count,
   479  		DNSPrefix: &dnsPrefix,
   480  	}
   481  
   482  	return profile
   483  }
   484  
   485  func expandAzureRmContainerServiceServicePrincipal(d *schema.ResourceData) *containerservice.ServicePrincipalProfile {
   486  
   487  	value, exists := d.GetOk("service_principal")
   488  	if !exists {
   489  		return nil
   490  	}
   491  
   492  	configs := value.(*schema.Set).List()
   493  
   494  	config := configs[0].(map[string]interface{})
   495  
   496  	clientId := config["client_id"].(string)
   497  	clientSecret := config["client_secret"].(string)
   498  
   499  	principal := containerservice.ServicePrincipalProfile{
   500  		ClientID: &clientId,
   501  		Secret:   &clientSecret,
   502  	}
   503  
   504  	return &principal
   505  }
   506  
   507  func expandAzureRmContainerServiceAgentProfiles(d *schema.ResourceData) []containerservice.AgentPoolProfile {
   508  	configs := d.Get("agent_pool_profile").(*schema.Set).List()
   509  	config := configs[0].(map[string]interface{})
   510  	profiles := make([]containerservice.AgentPoolProfile, 0, len(configs))
   511  
   512  	name := config["name"].(string)
   513  	count := int32(config["count"].(int))
   514  	dnsPrefix := config["dns_prefix"].(string)
   515  	vmSize := config["vm_size"].(string)
   516  
   517  	profile := containerservice.AgentPoolProfile{
   518  		Name:      &name,
   519  		Count:     &count,
   520  		VMSize:    containerservice.VMSizeTypes(vmSize),
   521  		DNSPrefix: &dnsPrefix,
   522  	}
   523  
   524  	profiles = append(profiles, profile)
   525  
   526  	return profiles
   527  }
   528  
   529  func containerServiceStateRefreshFunc(client *ArmClient, resourceGroupName string, containerServiceName string) resource.StateRefreshFunc {
   530  	return func() (interface{}, string, error) {
   531  		res, err := client.containerServicesClient.Get(resourceGroupName, containerServiceName)
   532  		if err != nil {
   533  			return nil, "", fmt.Errorf("Error issuing read request in containerServiceStateRefreshFunc to Azure ARM for Container Service '%s' (RG: '%s'): %s", containerServiceName, resourceGroupName, err)
   534  		}
   535  
   536  		return res, *res.Properties.ProvisioningState, nil
   537  	}
   538  }
   539  
   540  func resourceAzureRMContainerServiceMasterProfileHash(v interface{}) int {
   541  	var buf bytes.Buffer
   542  	m := v.(map[string]interface{})
   543  
   544  	count := m["count"].(int)
   545  	dnsPrefix := m["dns_prefix"].(string)
   546  
   547  	buf.WriteString(fmt.Sprintf("%d-", count))
   548  	buf.WriteString(fmt.Sprintf("%s-", dnsPrefix))
   549  
   550  	return hashcode.String(buf.String())
   551  }
   552  
   553  func resourceAzureRMContainerServiceLinuxProfilesHash(v interface{}) int {
   554  	var buf bytes.Buffer
   555  	m := v.(map[string]interface{})
   556  
   557  	adminUsername := m["admin_username"].(string)
   558  
   559  	buf.WriteString(fmt.Sprintf("%s-", adminUsername))
   560  
   561  	return hashcode.String(buf.String())
   562  }
   563  
   564  func resourceAzureRMContainerServiceLinuxProfilesSSHKeysHash(v interface{}) int {
   565  	var buf bytes.Buffer
   566  	m := v.(map[string]interface{})
   567  
   568  	keyData := m["key_data"].(string)
   569  
   570  	buf.WriteString(fmt.Sprintf("%s-", keyData))
   571  
   572  	return hashcode.String(buf.String())
   573  }
   574  
   575  func resourceAzureRMContainerServiceAgentPoolProfilesHash(v interface{}) int {
   576  	var buf bytes.Buffer
   577  	m := v.(map[string]interface{})
   578  
   579  	count := m["count"].(int)
   580  	dnsPrefix := m["dns_prefix"].(string)
   581  	name := m["name"].(string)
   582  	vm_size := m["vm_size"].(string)
   583  
   584  	buf.WriteString(fmt.Sprintf("%d-", count))
   585  	buf.WriteString(fmt.Sprintf("%s-", dnsPrefix))
   586  	buf.WriteString(fmt.Sprintf("%s-", name))
   587  	buf.WriteString(fmt.Sprintf("%s-", vm_size))
   588  
   589  	return hashcode.String(buf.String())
   590  }
   591  
   592  func resourceAzureRMContainerServiceServicePrincipalProfileHash(v interface{}) int {
   593  	var buf bytes.Buffer
   594  	m := v.(map[string]interface{})
   595  
   596  	clientId := m["client_id"].(string)
   597  	buf.WriteString(fmt.Sprintf("%s-", clientId))
   598  
   599  	return hashcode.String(buf.String())
   600  }
   601  
   602  func resourceAzureRMContainerServiceDiagnosticProfilesHash(v interface{}) int {
   603  	var buf bytes.Buffer
   604  	m := v.(map[string]interface{})
   605  
   606  	enabled := m["enabled"].(bool)
   607  
   608  	buf.WriteString(fmt.Sprintf("%t", enabled))
   609  
   610  	return hashcode.String(buf.String())
   611  }
   612  
   613  func validateArmContainerServiceOrchestrationPlatform(v interface{}, k string) (ws []string, errors []error) {
   614  	value := v.(string)
   615  	capacities := map[string]bool{
   616  		"DCOS":       true,
   617  		"Kubernetes": true,
   618  		"Swarm":      true,
   619  	}
   620  
   621  	if !capacities[value] {
   622  		errors = append(errors, fmt.Errorf("Container Service: Orchestration Platgorm can only be DCOS / Kubernetes / Swarm"))
   623  	}
   624  	return
   625  }
   626  
   627  func validateArmContainerServiceMasterProfileCount(v interface{}, k string) (ws []string, errors []error) {
   628  	value := v.(int)
   629  	capacities := map[int]bool{
   630  		1: true,
   631  		3: true,
   632  		5: true,
   633  	}
   634  
   635  	if !capacities[value] {
   636  		errors = append(errors, fmt.Errorf("The number of master nodes must be 1, 3 or 5."))
   637  	}
   638  	return
   639  }
   640  
   641  func validateArmContainerServiceAgentPoolProfileCount(v interface{}, k string) (ws []string, errors []error) {
   642  	value := v.(int)
   643  	if value > 100 || 0 >= value {
   644  		errors = append(errors, fmt.Errorf("The Count for an Agent Pool Profile can only be between 1 and 100."))
   645  	}
   646  	return
   647  }