Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Resource: azurerm_kusto_cluster #4129

Merged
merged 23 commits into from
Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1ff71bf
add kusto client
r0bnet Aug 18, 2019
b5b5e12
add basic cluster implementation, no tests yet
r0bnet Aug 19, 2019
cb7bdd2
add cluster resource to provider.go; do some refactoring
r0bnet Aug 19, 2019
664564f
add new validation for cluster name; fix bug that resource is not fou…
r0bnet Aug 19, 2019
735990c
add documentation; fix bugs; remove external trusted clients because …
r0bnet Aug 20, 2019
5f159c7
make parsing cluster id more resilient
r0bnet Aug 21, 2019
8cd86c6
remove duplicate line in go.mod
r0bnet Aug 21, 2019
b897c11
documentation fix
r0bnet Aug 21, 2019
6149032
minor bug fixes
r0bnet Aug 21, 2019
03917d1
add basic, tags and sku tests
r0bnet Aug 21, 2019
13a917e
add documentation link
r0bnet Aug 21, 2019
03515fa
Merge branch 'master' into resource-azurerm_kusto_cluster
r0bnet Aug 21, 2019
a6f1a5e
change error result order to comply with golang conventions (not node)
r0bnet Aug 21, 2019
a72967d
fix import ordering to comply linting rules
r0bnet Aug 21, 2019
2cc5f9c
fix imports ordering
r0bnet Aug 22, 2019
fff75cf
Merge branch 'master' into resource-azurerm_kusto_cluster
r0bnet Aug 22, 2019
a8269f9
change first letter of clusters to upper case (that's how the API is …
r0bnet Aug 23, 2019
3133fb6
Merge branch 'master' into resource-azurerm_kusto_cluster
r0bnet Aug 26, 2019
13e9a4b
fix documentation
r0bnet Aug 26, 2019
6311064
Merge branch 'master' into resource-azurerm_kusto_cluster
r0bnet Aug 29, 2019
621194f
fix github review comments
r0bnet Aug 29, 2019
83457b4
add Kusto as required resource provider
r0bnet Aug 29, 2019
7f3bc78
fix tags test
r0bnet Aug 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/hdinsight"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/iothub"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/keyvault"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/kusto"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/loganalytics"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/logic"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/managementgroup"
Expand Down Expand Up @@ -114,6 +115,7 @@ type ArmClient struct {
hdinsight *hdinsight.Client
iothub *iothub.Client
keyvault *keyvault.Client
kusto *kusto.Client
logAnalytics *loganalytics.Client
logic *logic.Client
managementGroups *managementgroup.Client
Expand Down Expand Up @@ -254,6 +256,7 @@ func getArmClient(c *authentication.Config, skipProviderRegistration bool, partn
client.hdinsight = hdinsight.BuildClient(o)
client.iothub = iothub.BuildClient(o)
client.keyvault = keyvault.BuildClient(o)
client.kusto = kusto.BuildClient(o)
client.logic = logic.BuildClient(o)
client.logAnalytics = loganalytics.BuildClient(o)
client.maps = maps.BuildClient(o)
Expand Down
20 changes: 20 additions & 0 deletions azurerm/internal/services/kusto/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package kusto

import (
"github.com/Azure/azure-sdk-for-go/services/kusto/mgmt/2019-01-21/kusto"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common"
)

type Client struct {
ClustersClient *kusto.ClustersClient
}

func BuildClient(o *common.ClientOptions) *Client {

ClustersClient := kusto.NewClustersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&ClustersClient.Client, o.ResourceManagerAuthorizer)

return &Client{
ClustersClient: &ClustersClient,
}
}
1 change: 1 addition & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_key_vault_secret": resourceArmKeyVaultSecret(),
"azurerm_key_vault": resourceArmKeyVault(),
"azurerm_kubernetes_cluster": resourceArmKubernetesCluster(),
"azurerm_kusto_cluster": resourceArmKustoCluster(),
"azurerm_lb_backend_address_pool": resourceArmLoadBalancerBackendAddressPool(),
"azurerm_lb_nat_pool": resourceArmLoadBalancerNatPool(),
"azurerm_lb_nat_rule": resourceArmLoadBalancerNatRule(),
Expand Down
276 changes: 276 additions & 0 deletions azurerm/resource_arm_kusto_cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
package azurerm

import (
"fmt"
"log"
"regexp"
"strings"

"github.com/Azure/azure-sdk-for-go/services/kusto/mgmt/2019-01-21/kusto"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmKustoCluster() *schema.Resource {
return &schema.Resource{
Create: resourceArmKustoClusterCreateUpdate,
Read: resourceArmKustoClusterRead,
Update: resourceArmKustoClusterCreateUpdate,
Delete: resourceArmKustoClusterDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateAzureRMKustoClusterName,
},

"resource_group_name": azure.SchemaResourceGroupName(),

"location": azure.SchemaLocation(),

"sku": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateAzureRMKustoClusterSkuName(),
},

"capacity": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(1, 1000),
},
},
},
},

"uri": {
Type: schema.TypeString,
Computed: true,
},

"data_ingestion_uri": {
Type: schema.TypeString,
Computed: true,
},

"tags": tagsSchema(),
r0bnet marked this conversation as resolved.
Show resolved Hide resolved
},
}
}

func resourceArmKustoClusterCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).kusto.ClustersClient
ctx := meta.(*ArmClient).StopContext

log.Printf("[INFO] preparing arguments for Azure Kusto Cluster creation.")

name := d.Get("name").(string)
resourceGroup := d.Get("resource_group_name").(string)

if requireResourcesToBeImported && d.IsNewResource() {
server, err := client.Get(ctx, resourceGroup, name)
if err != nil {
if !utils.ResponseWasNotFound(server.Response) {
return fmt.Errorf("Error checking for presence of existing Kusto Cluster %q (Resource Group %q): %s", name, resourceGroup, err)
}
}

if server.ID != nil && *server.ID != "" {
return tf.ImportAsExistsError("azurerm_kusto_cluster", *server.ID)
}
}

location := azure.NormalizeLocation(d.Get("location").(string))

sku, err := expandKustoClusterSku(d)
if err != nil {
return err
}

clusterProperties := kusto.ClusterProperties{}

tags := d.Get("tags").(map[string]interface{})
r0bnet marked this conversation as resolved.
Show resolved Hide resolved

kustoCluster := kusto.Cluster{
Name: &name,
Location: &location,
Sku: sku,
ClusterProperties: &clusterProperties,
Tags: expandTags(tags),
r0bnet marked this conversation as resolved.
Show resolved Hide resolved
}

future, err := client.CreateOrUpdate(ctx, resourceGroup, name, kustoCluster)
if err != nil {
return fmt.Errorf("Error creating or updating Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for completion of Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err)
}

resp, getDetailsErr := client.Get(ctx, resourceGroup, name)
if getDetailsErr != nil {
return fmt.Errorf("Error retrieving Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if resp.ID == nil {
return fmt.Errorf("Cannot read ID for Kusto Cluster %q (Resource Group %q)", name, resourceGroup)
}

d.SetId(*resp.ID)

return resourceArmKustoClusterRead(d, meta)
}

func resourceArmKustoClusterRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).kusto.ClustersClient
ctx := meta.(*ArmClient).StopContext

id, err := azure.ParseAzureResourceID(d.Id())
if err != nil {
return err
}

resourceGroup := id.ResourceGroup
name := id.Path["Clusters"]

clusterResponse, err := client.Get(ctx, resourceGroup, name)

if err != nil {
if utils.ResponseWasNotFound(clusterResponse.Response) {
d.SetId("")
return nil
}
return fmt.Errorf("Error retrieving Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err)
}

d.Set("name", name)
d.Set("resource_group_name", resourceGroup)

if location := clusterResponse.Location; location != nil {
d.Set("location", azure.NormalizeLocation(*location))
}

if err := d.Set("sku", flattenKustoClusterSku(clusterResponse.Sku)); err != nil {
return fmt.Errorf("Error setting `sku`: %+v", err)
}

clusterProperties := clusterResponse.ClusterProperties
r0bnet marked this conversation as resolved.
Show resolved Hide resolved

d.Set("uri", clusterProperties.URI)
d.Set("data_ingestion_uri", clusterProperties.DataIngestionURI)

flattenAndSetTags(d, clusterResponse.Tags)
r0bnet marked this conversation as resolved.
Show resolved Hide resolved

return nil
r0bnet marked this conversation as resolved.
Show resolved Hide resolved
}

func resourceArmKustoClusterDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).kusto.ClustersClient
ctx := meta.(*ArmClient).StopContext

id, err := azure.ParseAzureResourceID(d.Id())
if err != nil {
return err
}

resGroup := id.ResourceGroup
name := id.Path["Clusters"]

future, err := client.Delete(ctx, resGroup, name)
if err != nil {
return fmt.Errorf("Error deleting Kusto Cluster %q (Resource Group %q): %+v", name, resGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for deletion of Kusto Cluster %q (Resource Group %q): %+v", name, resGroup, err)
}

return nil
}

func validateAzureRMKustoClusterName(v interface{}, k string) (warnings []string, errors []error) {
name := v.(string)

if !regexp.MustCompile(`^[a-z][a-z0-9]+$`).MatchString(name) {
errors = append(errors, fmt.Errorf("%q must begin with a letter and may only contain alphanumeric characters: %q", k, name))
}

if len(name) < 4 || len(name) > 22 {
errors = append(errors, fmt.Errorf("%q must be (inclusive) between 4 and 22 characters long but is %d", k, len(name)))
}

return warnings, errors
}

func validateAzureRMKustoClusterSkuName() schema.SchemaValidateFunc {
// using hard coded values because they're not like this in the sdk as constants
// found them here: https://docs.microsoft.com/en-us/rest/api/azurerekusto/clusters/createorupdate#azureskuname
katbyte marked this conversation as resolved.
Show resolved Hide resolved
possibleSkuNames := []string{
"Dev(No SLA)_Standard_D11_v2",
katbyte marked this conversation as resolved.
Show resolved Hide resolved
"Standard_D11_v2",
"Standard_D12_v2",
"Standard_D13_v2",
"Standard_D14_v2",
"Standard_DS13_v2+1TB_PS",
"Standard_DS13_v2+2TB_PS",
"Standard_DS14_v2+3TB_PS",
"Standard_DS14_v2+4TB_PS",
"Standard_L16s",
"Standard_L4s",
"Standard_L8s",
}

return validation.StringInSlice(possibleSkuNames, false)
}

func expandKustoClusterSku(d *schema.ResourceData) (*kusto.AzureSku, error) {
skuList := d.Get("sku").([]interface{})

sku := skuList[0].(map[string]interface{})
name := sku["name"].(string)

skuNamePrefixToTier := map[string]string{
"Dev(No SLA)": "Basic",
"Standard": "Standard",
}

skuNamePrefix := strings.Split(sku["name"].(string), "_")[0]
tier, ok := skuNamePrefixToTier[skuNamePrefix]
if !ok {
return nil, fmt.Errorf("sku name begins with invalid tier, possible are Dev(No SLA) and Standard but is: %q", skuNamePrefix)
}
capacity := sku["capacity"].(int)

azureSku := &kusto.AzureSku{
Name: kusto.AzureSkuName(name),
Tier: &tier,
Capacity: utils.Int32(int32(capacity)),
}

return azureSku, nil
}

func flattenKustoClusterSku(sku *kusto.AzureSku) []interface{} {
return []interface{}{
r0bnet marked this conversation as resolved.
Show resolved Hide resolved
map[string]interface{}{
"name": string(sku.Name),
"capacity": int(*sku.Capacity),
r0bnet marked this conversation as resolved.
Show resolved Hide resolved
},
}
}
Loading