diff --git a/azurerm/internal/services/kusto/kusto_cluster_resource.go b/azurerm/internal/services/kusto/kusto_cluster_resource.go index beeaac70010e..dbb3e4ecff05 100644 --- a/azurerm/internal/services/kusto/kusto_cluster_resource.go +++ b/azurerm/internal/services/kusto/kusto_cluster_resource.go @@ -135,6 +135,18 @@ func resourceArmKustoCluster() *schema.Resource { }, }, + "language_extensions": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(kusto.PYTHON), + string(kusto.R), + }, false), + }, + }, + "uri": { Type: schema.TypeString, Computed: true, @@ -232,6 +244,46 @@ func resourceArmKustoClusterCreateUpdate(d *schema.ResourceData, meta interface{ d.SetId(*resp.ID) + if v, ok := d.GetOk("language_extensions"); ok { + languageExtensions := expandKustoClusterLanguageExtensions(v.([]interface{})) + + currentLanguageExtensions, err := client.ListLanguageExtensions(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error reading current added language extensions from Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + languageExtensionsToAdd := diffLanguageExtensions(*languageExtensions.Value, *currentLanguageExtensions.Value) + if len(languageExtensionsToAdd) > 0 { + languageExtensionsListToAdd := kusto.LanguageExtensionsList{ + Value: &languageExtensionsToAdd, + } + + addLanguageExtensionsFuture, err := client.AddLanguageExtensions(ctx, resourceGroup, name, languageExtensionsListToAdd) + if err != nil { + return fmt.Errorf("Error adding language extensions to Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err = addLanguageExtensionsFuture.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for completion of adding language extensions to Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + + languageExtensionsToRemove := diffLanguageExtensions(*currentLanguageExtensions.Value, *languageExtensions.Value) + if len(languageExtensionsToRemove) > 0 { + languageExtensionsListToRemove := kusto.LanguageExtensionsList{ + Value: &languageExtensionsToRemove, + } + + removeLanguageExtensionsFuture, err := client.RemoveLanguageExtensions(ctx, resourceGroup, name, languageExtensionsListToRemove) + if err != nil { + return fmt.Errorf("Error removing language extensions from Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if err = removeLanguageExtensionsFuture.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for completion of removing language extensions from Kusto Cluster %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + } + return resourceArmKustoClusterRead(d, meta) } @@ -279,6 +331,7 @@ func resourceArmKustoClusterRead(d *schema.ResourceData, meta interface{}) error d.Set("enable_streaming_ingest", clusterProperties.EnableStreamingIngest) d.Set("enable_purge", clusterProperties.EnablePurge) d.Set("virtual_network_configuration", flatteKustoClusterVNET(clusterProperties.VirtualNetworkConfiguration)) + d.Set("language_extensions", flattenKustoClusterLanguageExtensions(clusterProperties.LanguageExtensions)) d.Set("uri", clusterProperties.URI) d.Set("data_ingestion_uri", clusterProperties.DataIngestionURI) } @@ -366,6 +419,24 @@ func expandKustoClusterVNET(input []interface{}) *kusto.VirtualNetworkConfigurat } } +func expandKustoClusterLanguageExtensions(input []interface{}) *kusto.LanguageExtensionsList { + if len(input) == 0 { + return nil + } + + extensions := make([]kusto.LanguageExtension, 0) + for _, language := range input { + v := kusto.LanguageExtension{ + LanguageExtensionName: kusto.LanguageExtensionName(language.(string)), + } + extensions = append(extensions, v) + } + + return &kusto.LanguageExtensionsList{ + Value: &extensions, + } +} + func flattenKustoClusterSku(sku *kusto.AzureSku) []interface{} { if sku == nil { return []interface{}{} @@ -410,3 +481,32 @@ func flatteKustoClusterVNET(vnet *kusto.VirtualNetworkConfiguration) []interface return []interface{}{output} } + +func flattenKustoClusterLanguageExtensions(extensions *kusto.LanguageExtensionsList) []interface{} { + if extensions == nil { + return []interface{}{} + } + + output := make([]interface{}, 0) + for _, v := range *extensions.Value { + output = append(output, v.LanguageExtensionName) + } + + return output +} + +func diffLanguageExtensions(a, b []kusto.LanguageExtension) []kusto.LanguageExtension { + target := make(map[string]bool) + for _, x := range b { + target[string(x.LanguageExtensionName)] = true + } + + diff := make([]kusto.LanguageExtension, 0) + for _, x := range a { + if _, ok := target[string(x.LanguageExtensionName)]; !ok { + diff = append(diff, x) + } + } + + return diff +} diff --git a/azurerm/internal/services/kusto/tests/kusto_cluster_resource_test.go b/azurerm/internal/services/kusto/tests/kusto_cluster_resource_test.go index d00e29eb2811..20b02844808a 100644 --- a/azurerm/internal/services/kusto/tests/kusto_cluster_resource_test.go +++ b/azurerm/internal/services/kusto/tests/kusto_cluster_resource_test.go @@ -195,6 +195,37 @@ func TestAccAzureRMKustoCluster_vnet(t *testing.T) { }) } +func TestAccAzureRMKustoCluster_languageExtensions(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_kusto_cluster", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMKustoClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMKustoCluster_languageExtensions(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoClusterExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "language_extensions.#", "2"), + resource.TestCheckResourceAttr(data.ResourceName, "language_extensions.0", "PYTHON"), + resource.TestCheckResourceAttr(data.ResourceName, "language_extensions.1", "R"), + ), + }, + data.ImportStep(), + { + Config: testAccAzureRMKustoCluster_languageExtensionsRemove(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKustoClusterExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "language_extensions.#", "1"), + resource.TestCheckResourceAttr(data.ResourceName, "language_extensions.0", "R"), + ), + }, + data.ImportStep(), + }, + }) +} + func testAccAzureRMKustoCluster_basic(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -378,34 +409,59 @@ resource "azurerm_kusto_cluster" "test" { type = "SystemAssigned" } } -`, data.RandomInteger, data.Locations.Primary, data.RandomString) + `, data.RandomInteger, data.Locations.Primary, data.RandomString) } -func testCheckAzureRMKustoClusterDestroy(s *terraform.State) error { - client := acceptance.AzureProvider.Meta().(*clients.Client).Kusto.ClustersClient - ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext +func testAccAzureRMKustoCluster_languageExtensions(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} - for _, rs := range s.RootModule().Resources { - if rs.Type != "azurerm_kusto_cluster" { - continue - } +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} - name := rs.Primary.Attributes["name"] - resourceGroup := rs.Primary.Attributes["resource_group_name"] +resource "azurerm_kusto_cluster" "test" { + name = "acctestkc%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name - resp, err := client.Get(ctx, resourceGroup, name) + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } - if err != nil { - if utils.ResponseWasNotFound(resp.Response) { - return nil - } - return err - } + language_extensions = ["PYTHON", "R"] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString) +} - return nil - } +func testAccAzureRMKustoCluster_languageExtensionsRemove(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} - return nil +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_kusto_cluster" "test" { + name = "acctestkc%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + sku { + name = "Dev(No SLA)_Standard_D11_v2" + capacity = 1 + } + + language_extensions = ["R"] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString) } func testAccAzureRMKustoCluster_vnet(data acceptance.TestData) string { @@ -493,6 +549,33 @@ resource "azurerm_kusto_cluster" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString, data.RandomString, data.RandomString, data.RandomString, data.RandomString) } +func testCheckAzureRMKustoClusterDestroy(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).Kusto.ClustersClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_kusto_cluster" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := client.Get(ctx, resourceGroup, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + return err + } + + return nil + } + + return nil +} + func testCheckAzureRMKustoClusterExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { client := acceptance.AzureProvider.Meta().(*clients.Client).Kusto.ClustersClient diff --git a/website/docs/r/kusto_cluster.html.markdown b/website/docs/r/kusto_cluster.html.markdown index 2ac81770333c..be2e11a5d699 100644 --- a/website/docs/r/kusto_cluster.html.markdown +++ b/website/docs/r/kusto_cluster.html.markdown @@ -56,6 +56,8 @@ The following arguments are supported: * `virtual_network_configuration`- (Optional) A `virtual_network_configuration` block as defined below. +* `language_extensions` - (Optional) An list of `language_extensions` to enable. Valid values are: `PYTHON` and `R`. + * `tags` - (Optional) A mapping of tags to assign to the resource. * `zones` - (Optional) A list of Availability Zones in which the cluster instances should be created in. Changing this forces a new resource to be created.