648 lines
17 KiB
Go
648 lines
17 KiB
Go
package azurerm
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
"bytes"
|
|
|
|
"github.com/Azure/azure-sdk-for-go/arm/containerservice"
|
|
"github.com/hashicorp/terraform/helper/hashcode"
|
|
"github.com/hashicorp/terraform/helper/resource"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
)
|
|
|
|
func resourceArmContainerService() *schema.Resource {
|
|
return &schema.Resource{
|
|
Create: resourceArmContainerServiceCreate,
|
|
Read: resourceArmContainerServiceRead,
|
|
Update: resourceArmContainerServiceCreate,
|
|
Delete: resourceArmContainerServiceDelete,
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
"name": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"location": locationSchema(),
|
|
|
|
"resource_group_name": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"orchestration_platform": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
ValidateFunc: validateArmContainerServiceOrchestrationPlatform,
|
|
},
|
|
|
|
"master_profile": {
|
|
Type: schema.TypeSet,
|
|
Required: true,
|
|
MaxItems: 1,
|
|
Elem: &schema.Resource{
|
|
Schema: map[string]*schema.Schema{
|
|
"count": {
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
Default: 1,
|
|
ValidateFunc: validateArmContainerServiceMasterProfileCount,
|
|
},
|
|
|
|
"dns_prefix": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
|
|
"fqdn": {
|
|
Type: schema.TypeString,
|
|
Computed: true,
|
|
},
|
|
},
|
|
},
|
|
Set: resourceAzureRMContainerServiceMasterProfileHash,
|
|
},
|
|
|
|
"linux_profile": {
|
|
Type: schema.TypeSet,
|
|
Required: true,
|
|
MaxItems: 1,
|
|
Elem: &schema.Resource{
|
|
Schema: map[string]*schema.Schema{
|
|
"admin_username": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
"ssh_key": {
|
|
Type: schema.TypeSet,
|
|
Required: true,
|
|
MaxItems: 1,
|
|
Elem: &schema.Resource{
|
|
Schema: map[string]*schema.Schema{
|
|
"key_data": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Set: resourceAzureRMContainerServiceLinuxProfilesHash,
|
|
},
|
|
|
|
"agent_pool_profile": {
|
|
Type: schema.TypeSet,
|
|
Required: true,
|
|
MaxItems: 1,
|
|
Elem: &schema.Resource{
|
|
Schema: map[string]*schema.Schema{
|
|
"name": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"count": {
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
Default: 1,
|
|
ValidateFunc: validateArmContainerServiceAgentPoolProfileCount,
|
|
},
|
|
|
|
"dns_prefix": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"fqdn": {
|
|
Type: schema.TypeString,
|
|
Computed: true,
|
|
},
|
|
|
|
"vm_size": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
Set: resourceAzureRMContainerServiceAgentPoolProfilesHash,
|
|
},
|
|
|
|
"service_principal": {
|
|
Type: schema.TypeSet,
|
|
Optional: true,
|
|
MaxItems: 1,
|
|
Elem: &schema.Resource{
|
|
Schema: map[string]*schema.Schema{
|
|
"client_id": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
|
|
"client_secret": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
Sensitive: true,
|
|
},
|
|
},
|
|
},
|
|
Set: resourceAzureRMContainerServiceServicePrincipalProfileHash,
|
|
},
|
|
|
|
"diagnostics_profile": {
|
|
Type: schema.TypeSet,
|
|
Required: true,
|
|
MaxItems: 1,
|
|
Elem: &schema.Resource{
|
|
Schema: map[string]*schema.Schema{
|
|
"enabled": {
|
|
Type: schema.TypeBool,
|
|
Required: true,
|
|
},
|
|
|
|
"storage_uri": {
|
|
Type: schema.TypeString,
|
|
Computed: true,
|
|
},
|
|
},
|
|
},
|
|
Set: resourceAzureRMContainerServiceDiagnosticProfilesHash,
|
|
},
|
|
|
|
"tags": tagsSchema(),
|
|
},
|
|
}
|
|
}
|
|
|
|
func resourceArmContainerServiceCreate(d *schema.ResourceData, meta interface{}) error {
|
|
client := meta.(*ArmClient)
|
|
containerServiceClient := client.containerServicesClient
|
|
|
|
log.Printf("[INFO] preparing arguments for Azure ARM Container Service creation.")
|
|
|
|
resGroup := d.Get("resource_group_name").(string)
|
|
name := d.Get("name").(string)
|
|
location := d.Get("location").(string)
|
|
|
|
orchestrationPlatform := d.Get("orchestration_platform").(string)
|
|
|
|
masterProfile := expandAzureRmContainerServiceMasterProfile(d)
|
|
linuxProfile := expandAzureRmContainerServiceLinuxProfile(d)
|
|
agentProfiles := expandAzureRmContainerServiceAgentProfiles(d)
|
|
diagnosticsProfile := expandAzureRmContainerServiceDiagnostics(d)
|
|
|
|
tags := d.Get("tags").(map[string]interface{})
|
|
|
|
parameters := containerservice.ContainerService{
|
|
Name: &name,
|
|
Location: &location,
|
|
Properties: &containerservice.Properties{
|
|
MasterProfile: &masterProfile,
|
|
LinuxProfile: &linuxProfile,
|
|
OrchestratorProfile: &containerservice.OrchestratorProfile{
|
|
OrchestratorType: containerservice.OchestratorTypes(orchestrationPlatform),
|
|
},
|
|
AgentPoolProfiles: &agentProfiles,
|
|
DiagnosticsProfile: &diagnosticsProfile,
|
|
},
|
|
Tags: expandTags(tags),
|
|
}
|
|
|
|
servicePrincipalProfile := expandAzureRmContainerServiceServicePrincipal(d)
|
|
if servicePrincipalProfile != nil {
|
|
parameters.ServicePrincipalProfile = servicePrincipalProfile
|
|
}
|
|
|
|
_, err := containerServiceClient.CreateOrUpdate(resGroup, name, parameters, make(chan struct{}))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
read, err := containerServiceClient.Get(resGroup, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if read.ID == nil {
|
|
return fmt.Errorf("Cannot read Container Service %s (resource group %s) ID", name, resGroup)
|
|
}
|
|
|
|
log.Printf("[DEBUG] Waiting for Container Service (%s) to become available", d.Get("name"))
|
|
stateConf := &resource.StateChangeConf{
|
|
Pending: []string{"Updating", "Creating"},
|
|
Target: []string{"Succeeded"},
|
|
Refresh: containerServiceStateRefreshFunc(client, resGroup, name),
|
|
Timeout: 30 * time.Minute,
|
|
MinTimeout: 15 * time.Second,
|
|
}
|
|
if _, err := stateConf.WaitForState(); err != nil {
|
|
return fmt.Errorf("Error waiting for Container Service (%s) to become available: %s", d.Get("name"), err)
|
|
}
|
|
|
|
d.SetId(*read.ID)
|
|
|
|
return resourceArmContainerServiceRead(d, meta)
|
|
}
|
|
|
|
func resourceArmContainerServiceRead(d *schema.ResourceData, meta interface{}) error {
|
|
containerServiceClient := meta.(*ArmClient).containerServicesClient
|
|
|
|
id, err := parseAzureResourceID(d.Id())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resGroup := id.ResourceGroup
|
|
name := id.Path["containerServices"]
|
|
|
|
resp, err := containerServiceClient.Get(resGroup, name)
|
|
if err != nil {
|
|
return fmt.Errorf("Error making Read request on Azure Container Service %s: %s", name, err)
|
|
}
|
|
if resp.StatusCode == http.StatusNotFound {
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
|
|
d.Set("name", resp.Name)
|
|
d.Set("location", azureRMNormalizeLocation(*resp.Location))
|
|
d.Set("resource_group_name", resGroup)
|
|
|
|
d.Set("orchestration_platform", string(resp.Properties.OrchestratorProfile.OrchestratorType))
|
|
|
|
masterProfiles := flattenAzureRmContainerServiceMasterProfile(*resp.Properties.MasterProfile)
|
|
d.Set("master_profile", &masterProfiles)
|
|
|
|
linuxProfile := flattenAzureRmContainerServiceLinuxProfile(*resp.Properties.LinuxProfile)
|
|
d.Set("linux_profile", &linuxProfile)
|
|
|
|
agentPoolProfiles := flattenAzureRmContainerServiceAgentPoolProfiles(resp.Properties.AgentPoolProfiles)
|
|
d.Set("agent_pool_profile", &agentPoolProfiles)
|
|
|
|
servicePrincipal := flattenAzureRmContainerServiceServicePrincipalProfile(resp.Properties.ServicePrincipalProfile)
|
|
if servicePrincipal != nil {
|
|
d.Set("service_principal", servicePrincipal)
|
|
}
|
|
|
|
diagnosticProfile := flattenAzureRmContainerServiceDiagnosticsProfile(resp.Properties.DiagnosticsProfile)
|
|
if diagnosticProfile != nil {
|
|
d.Set("diagnostics_profile", diagnosticProfile)
|
|
}
|
|
|
|
flattenAndSetTags(d, resp.Tags)
|
|
|
|
return nil
|
|
}
|
|
|
|
func resourceArmContainerServiceDelete(d *schema.ResourceData, meta interface{}) error {
|
|
client := meta.(*ArmClient)
|
|
containerServiceClient := client.containerServicesClient
|
|
|
|
id, err := parseAzureResourceID(d.Id())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resGroup := id.ResourceGroup
|
|
name := id.Path["containerServices"]
|
|
|
|
resp, err := containerServiceClient.Delete(resGroup, name, make(chan struct{}))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("Error issuing Azure ARM delete request of Container Service '%s': %s", name, err)
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func flattenAzureRmContainerServiceMasterProfile(profile containerservice.MasterProfile) *schema.Set {
|
|
masterProfiles := &schema.Set{
|
|
F: resourceAzureRMContainerServiceMasterProfileHash,
|
|
}
|
|
|
|
masterProfile := make(map[string]interface{}, 2)
|
|
|
|
masterProfile["count"] = int(*profile.Count)
|
|
masterProfile["dns_prefix"] = *profile.DNSPrefix
|
|
|
|
masterProfiles.Add(masterProfile)
|
|
|
|
return masterProfiles
|
|
}
|
|
|
|
func flattenAzureRmContainerServiceLinuxProfile(profile containerservice.LinuxProfile) *schema.Set {
|
|
profiles := &schema.Set{
|
|
F: resourceAzureRMContainerServiceLinuxProfilesHash,
|
|
}
|
|
|
|
values := map[string]interface{}{}
|
|
|
|
sshKeys := &schema.Set{
|
|
F: resourceAzureRMContainerServiceLinuxProfilesSSHKeysHash,
|
|
}
|
|
for _, ssh := range *profile.SSH.PublicKeys {
|
|
keys := map[string]interface{}{}
|
|
keys["key_data"] = *ssh.KeyData
|
|
sshKeys.Add(keys)
|
|
}
|
|
|
|
values["admin_username"] = *profile.AdminUsername
|
|
values["ssh_key"] = sshKeys
|
|
profiles.Add(values)
|
|
|
|
return profiles
|
|
}
|
|
|
|
func flattenAzureRmContainerServiceAgentPoolProfiles(profiles *[]containerservice.AgentPoolProfile) *schema.Set {
|
|
agentPoolProfiles := &schema.Set{
|
|
F: resourceAzureRMContainerServiceAgentPoolProfilesHash,
|
|
}
|
|
|
|
for _, profile := range *profiles {
|
|
agentPoolProfile := map[string]interface{}{}
|
|
agentPoolProfile["count"] = int(*profile.Count)
|
|
agentPoolProfile["dns_prefix"] = *profile.DNSPrefix
|
|
agentPoolProfile["fqdn"] = *profile.Fqdn
|
|
agentPoolProfile["name"] = *profile.Name
|
|
agentPoolProfile["vm_size"] = string(profile.VMSize)
|
|
agentPoolProfiles.Add(agentPoolProfile)
|
|
}
|
|
|
|
return agentPoolProfiles
|
|
}
|
|
|
|
func flattenAzureRmContainerServiceServicePrincipalProfile(profile *containerservice.ServicePrincipalProfile) *schema.Set {
|
|
|
|
if profile == nil {
|
|
return nil
|
|
}
|
|
|
|
servicePrincipalProfiles := &schema.Set{
|
|
F: resourceAzureRMContainerServiceServicePrincipalProfileHash,
|
|
}
|
|
|
|
values := map[string]interface{}{}
|
|
|
|
values["client_id"] = *profile.ClientID
|
|
if profile.Secret != nil {
|
|
values["client_secret"] = *profile.Secret
|
|
}
|
|
|
|
servicePrincipalProfiles.Add(values)
|
|
|
|
return servicePrincipalProfiles
|
|
}
|
|
|
|
func flattenAzureRmContainerServiceDiagnosticsProfile(profile *containerservice.DiagnosticsProfile) *schema.Set {
|
|
diagnosticProfiles := &schema.Set{
|
|
F: resourceAzureRMContainerServiceDiagnosticProfilesHash,
|
|
}
|
|
|
|
values := map[string]interface{}{}
|
|
|
|
values["enabled"] = *profile.VMDiagnostics.Enabled
|
|
if profile.VMDiagnostics.StorageURI != nil {
|
|
values["storage_uri"] = *profile.VMDiagnostics.StorageURI
|
|
}
|
|
diagnosticProfiles.Add(values)
|
|
|
|
return diagnosticProfiles
|
|
}
|
|
|
|
func expandAzureRmContainerServiceDiagnostics(d *schema.ResourceData) containerservice.DiagnosticsProfile {
|
|
configs := d.Get("diagnostics_profile").(*schema.Set).List()
|
|
profile := containerservice.DiagnosticsProfile{}
|
|
|
|
data := configs[0].(map[string]interface{})
|
|
|
|
enabled := data["enabled"].(bool)
|
|
|
|
profile = containerservice.DiagnosticsProfile{
|
|
VMDiagnostics: &containerservice.VMDiagnostics{
|
|
Enabled: &enabled,
|
|
},
|
|
}
|
|
|
|
return profile
|
|
}
|
|
|
|
func expandAzureRmContainerServiceLinuxProfile(d *schema.ResourceData) containerservice.LinuxProfile {
|
|
profiles := d.Get("linux_profile").(*schema.Set).List()
|
|
config := profiles[0].(map[string]interface{})
|
|
|
|
adminUsername := config["admin_username"].(string)
|
|
|
|
linuxKeys := config["ssh_key"].(*schema.Set).List()
|
|
sshPublicKeys := []containerservice.SSHPublicKey{}
|
|
|
|
key := linuxKeys[0].(map[string]interface{})
|
|
keyData := key["key_data"].(string)
|
|
|
|
sshPublicKey := containerservice.SSHPublicKey{
|
|
KeyData: &keyData,
|
|
}
|
|
|
|
sshPublicKeys = append(sshPublicKeys, sshPublicKey)
|
|
|
|
profile := containerservice.LinuxProfile{
|
|
AdminUsername: &adminUsername,
|
|
SSH: &containerservice.SSHConfiguration{
|
|
PublicKeys: &sshPublicKeys,
|
|
},
|
|
}
|
|
|
|
return profile
|
|
}
|
|
|
|
func expandAzureRmContainerServiceMasterProfile(d *schema.ResourceData) containerservice.MasterProfile {
|
|
configs := d.Get("master_profile").(*schema.Set).List()
|
|
config := configs[0].(map[string]interface{})
|
|
|
|
count := int32(config["count"].(int))
|
|
dnsPrefix := config["dns_prefix"].(string)
|
|
|
|
profile := containerservice.MasterProfile{
|
|
Count: &count,
|
|
DNSPrefix: &dnsPrefix,
|
|
}
|
|
|
|
return profile
|
|
}
|
|
|
|
func expandAzureRmContainerServiceServicePrincipal(d *schema.ResourceData) *containerservice.ServicePrincipalProfile {
|
|
|
|
value, exists := d.GetOk("service_principal")
|
|
if !exists {
|
|
return nil
|
|
}
|
|
|
|
configs := value.(*schema.Set).List()
|
|
|
|
config := configs[0].(map[string]interface{})
|
|
|
|
clientId := config["client_id"].(string)
|
|
clientSecret := config["client_secret"].(string)
|
|
|
|
principal := containerservice.ServicePrincipalProfile{
|
|
ClientID: &clientId,
|
|
Secret: &clientSecret,
|
|
}
|
|
|
|
return &principal
|
|
}
|
|
|
|
func expandAzureRmContainerServiceAgentProfiles(d *schema.ResourceData) []containerservice.AgentPoolProfile {
|
|
configs := d.Get("agent_pool_profile").(*schema.Set).List()
|
|
config := configs[0].(map[string]interface{})
|
|
profiles := make([]containerservice.AgentPoolProfile, 0, len(configs))
|
|
|
|
name := config["name"].(string)
|
|
count := int32(config["count"].(int))
|
|
dnsPrefix := config["dns_prefix"].(string)
|
|
vmSize := config["vm_size"].(string)
|
|
|
|
profile := containerservice.AgentPoolProfile{
|
|
Name: &name,
|
|
Count: &count,
|
|
VMSize: containerservice.VMSizeTypes(vmSize),
|
|
DNSPrefix: &dnsPrefix,
|
|
}
|
|
|
|
profiles = append(profiles, profile)
|
|
|
|
return profiles
|
|
}
|
|
|
|
func containerServiceStateRefreshFunc(client *ArmClient, resourceGroupName string, containerServiceName string) resource.StateRefreshFunc {
|
|
return func() (interface{}, string, error) {
|
|
res, err := client.containerServicesClient.Get(resourceGroupName, containerServiceName)
|
|
if err != nil {
|
|
return nil, "", fmt.Errorf("Error issuing read request in containerServiceStateRefreshFunc to Azure ARM for Container Service '%s' (RG: '%s'): %s", containerServiceName, resourceGroupName, err)
|
|
}
|
|
|
|
return res, *res.Properties.ProvisioningState, nil
|
|
}
|
|
}
|
|
|
|
func resourceAzureRMContainerServiceMasterProfileHash(v interface{}) int {
|
|
var buf bytes.Buffer
|
|
m := v.(map[string]interface{})
|
|
|
|
count := m["count"].(int)
|
|
dnsPrefix := m["dns_prefix"].(string)
|
|
|
|
buf.WriteString(fmt.Sprintf("%d-", count))
|
|
buf.WriteString(fmt.Sprintf("%s-", dnsPrefix))
|
|
|
|
return hashcode.String(buf.String())
|
|
}
|
|
|
|
func resourceAzureRMContainerServiceLinuxProfilesHash(v interface{}) int {
|
|
var buf bytes.Buffer
|
|
m := v.(map[string]interface{})
|
|
|
|
adminUsername := m["admin_username"].(string)
|
|
|
|
buf.WriteString(fmt.Sprintf("%s-", adminUsername))
|
|
|
|
return hashcode.String(buf.String())
|
|
}
|
|
|
|
func resourceAzureRMContainerServiceLinuxProfilesSSHKeysHash(v interface{}) int {
|
|
var buf bytes.Buffer
|
|
m := v.(map[string]interface{})
|
|
|
|
keyData := m["key_data"].(string)
|
|
|
|
buf.WriteString(fmt.Sprintf("%s-", keyData))
|
|
|
|
return hashcode.String(buf.String())
|
|
}
|
|
|
|
func resourceAzureRMContainerServiceAgentPoolProfilesHash(v interface{}) int {
|
|
var buf bytes.Buffer
|
|
m := v.(map[string]interface{})
|
|
|
|
count := m["count"].(int)
|
|
dnsPrefix := m["dns_prefix"].(string)
|
|
name := m["name"].(string)
|
|
vm_size := m["vm_size"].(string)
|
|
|
|
buf.WriteString(fmt.Sprintf("%d-", count))
|
|
buf.WriteString(fmt.Sprintf("%s-", dnsPrefix))
|
|
buf.WriteString(fmt.Sprintf("%s-", name))
|
|
buf.WriteString(fmt.Sprintf("%s-", vm_size))
|
|
|
|
return hashcode.String(buf.String())
|
|
}
|
|
|
|
func resourceAzureRMContainerServiceServicePrincipalProfileHash(v interface{}) int {
|
|
var buf bytes.Buffer
|
|
m := v.(map[string]interface{})
|
|
|
|
clientId := m["client_id"].(string)
|
|
buf.WriteString(fmt.Sprintf("%s-", clientId))
|
|
|
|
return hashcode.String(buf.String())
|
|
}
|
|
|
|
func resourceAzureRMContainerServiceDiagnosticProfilesHash(v interface{}) int {
|
|
var buf bytes.Buffer
|
|
m := v.(map[string]interface{})
|
|
|
|
enabled := m["enabled"].(bool)
|
|
|
|
buf.WriteString(fmt.Sprintf("%t", enabled))
|
|
|
|
return hashcode.String(buf.String())
|
|
}
|
|
|
|
func validateArmContainerServiceOrchestrationPlatform(v interface{}, k string) (ws []string, errors []error) {
|
|
value := v.(string)
|
|
capacities := map[string]bool{
|
|
"DCOS": true,
|
|
"Kubernetes": true,
|
|
"Swarm": true,
|
|
}
|
|
|
|
if !capacities[value] {
|
|
errors = append(errors, fmt.Errorf("Container Service: Orchestration Platgorm can only be DCOS / Kubernetes / Swarm"))
|
|
}
|
|
return
|
|
}
|
|
|
|
func validateArmContainerServiceMasterProfileCount(v interface{}, k string) (ws []string, errors []error) {
|
|
value := v.(int)
|
|
capacities := map[int]bool{
|
|
1: true,
|
|
3: true,
|
|
5: true,
|
|
}
|
|
|
|
if !capacities[value] {
|
|
errors = append(errors, fmt.Errorf("The number of master nodes must be 1, 3 or 5."))
|
|
}
|
|
return
|
|
}
|
|
|
|
func validateArmContainerServiceAgentPoolProfileCount(v interface{}, k string) (ws []string, errors []error) {
|
|
value := v.(int)
|
|
if value > 100 || 0 >= value {
|
|
errors = append(errors, fmt.Errorf("The Count for an Agent Pool Profile can only be between 1 and 100."))
|
|
}
|
|
return
|
|
}
|