From abc8a121fe99c597bc342033531dd1be748b02b6 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Wed, 12 May 2021 12:08:19 +0200 Subject: [PATCH 01/18] Add basic compute cluster resource --- ...chine_learning_compute_cluster_resource.go | 438 ++++++++++++++++++ ..._learning_compute_cluster_resource_test.go | 270 +++++++++++ .../machinelearning/parse/compute_cluster.go | 75 +++ .../parse/compute_cluster_test.go | 128 +++++ .../services/machinelearning/registration.go | 1 + .../services/machinelearning/resourceids.go | 1 + .../validate/compute_cluster_id.go | 23 + .../validate/compute_cluster_id_test.go | 88 ++++ ...ine_learning_compute_cluster.html.markdown | 98 ++++ 9 files changed, 1122 insertions(+) create mode 100644 azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go create mode 100644 azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go create mode 100644 azurerm/internal/services/machinelearning/parse/compute_cluster.go create mode 100644 azurerm/internal/services/machinelearning/parse/compute_cluster_test.go create mode 100644 azurerm/internal/services/machinelearning/validate/compute_cluster_id.go create mode 100644 azurerm/internal/services/machinelearning/validate/compute_cluster_id_test.go create mode 100644 website/docs/r/machine_learning_compute_cluster.html.markdown diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go new file mode 100644 index 000000000000..2937927cadb7 --- /dev/null +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -0,0 +1,438 @@ +package machinelearning + +import ( + "fmt" + "time" + + "github.com/Azure/azure-sdk-for-go/services/machinelearningservices/mgmt/2020-04-01/machinelearningservices" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/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/internal/clients" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + + azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceComputeCluster() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeClusterCreate, + Read: resourceComputeClusterRead, + Update: resourceComputeClusterUpdate, + Delete: resourceComputeClusterDelete, + + Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { + _, err := parse.ComputeClusterID(id) + return err + }), + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "machine_learning_workspace_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "location": azure.SchemaLocation(), + + "vm_size": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "vm_priority": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "identity": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(machinelearningservices.SystemAssigned), + }, false), + }, + "principal_id": { + Type: schema.TypeString, + Computed: true, + }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "scale_settings": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_node_count": { + Type: schema.TypeInt, + Required: true, + }, + "min_node_count": { + Type: schema.TypeInt, + Required: true, + }, + "node_idle_time_before_scale_down": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + + "description": { + Type: schema.TypeString, + Optional: true, + }, + + "subnet_resource_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "tags": tags.Schema(), + }, + } +} + +func resourceComputeClusterCreate(d *schema.ResourceData, meta interface{}) error { + mlWorkspacesClient := meta.(*clients.Client).MachineLearning.WorkspacesClient + mlComputeClient := meta.(*clients.Client).MachineLearning.MachineLearningComputeClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + name := d.Get("name").(string) + + // Get Machine Learning Workspace Name and Resource Group from ID + unparsedWorkspaceID := d.Get("machine_learning_workspace_id").(string) + + workspaceID, err := parse.WorkspaceID(unparsedWorkspaceID) + if err != nil { + return err + } + + existing, err := mlComputeClient.Get(ctx, workspaceID.ResourceGroup, workspaceID.Name, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("error checking for existing Compute Cluster %q in Workspace %q (Resource Group %q): %s", + name, workspaceID.Name, workspaceID.ResourceGroup, err) + } + } + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_machine_learning_compute_cluster", *existing.ID) + } + + // AML Compute Cluster configuration (not needed here presumably) + vmSize := d.Get("vm_size").(string) + vmPriority := d.Get("vm_priority").(string) + + inputScaleSettings := d.Get("scale_settings").([]interface{}) + scaleSettings := expandScaleSettings(inputScaleSettings) + + subnetResourceId := d.Get("subnet_resource_id").(string) + + description := d.Get("description").(string) + + location := d.Get("location").(string) + + identity := d.Get("identity").([]interface{}) + + // Get SKU from Workspace + workspace, err := mlWorkspacesClient.Get(ctx, workspaceID.ResourceGroup, workspaceID.Name) + if err != nil { + return err + } + sku := workspace.Sku + + t := d.Get("tags").(map[string]interface{}) + + subnetId := machinelearningservices.ResourceID{ID: utils.String(subnetResourceId)} + + computeClusterProperties := machinelearningservices.AmlCompute{ + // Properties - AML Compute properties + Properties: &machinelearningservices.AmlComputeProperties{ + // VMSize - Virtual Machine Size + VMSize: utils.String(vmSize), + // VMPriority - Virtual Machine priority. Possible values include: 'Dedicated', 'LowPriority' + VMPriority: machinelearningservices.VMPriority(vmPriority), + // ScaleSettings - Scale settings for AML Compute + ScaleSettings: scaleSettings, + // UserAccountCredentials - Credentials for an administrator user account that will be created on each compute node. + // Subnet - Virtual network subnet resource ID the compute nodes belong to. + Subnet: &subnetId, + // RemoteLoginPortPublicAccess - State of the public SSH port. Possible values are: Disabled - Indicates that the public ssh port is closed on all nodes of the cluster. Enabled - Indicates that the public ssh port is open on all nodes of the cluster. NotSpecified - Indicates that the public ssh port is closed on all nodes of the cluster if VNet is defined, else is open all public nodes. It can be default only during cluster creation time, after creation it will be either enabled or disabled. Possible values include: 'RemoteLoginPortPublicAccessEnabled', 'RemoteLoginPortPublicAccessDisabled', 'RemoteLoginPortPublicAccessNotSpecified' + // AllocationState - READ-ONLY; Allocation state of the compute. Possible values are: steady - Indicates that the compute is not resizing. There are no changes to the number of compute nodes in the compute in progress. A compute enters this state when it is created and when no operations are being performed on the compute to change the number of compute nodes. resizing - Indicates that the compute is resizing; that is, compute nodes are being added to or removed from the compute. Possible values include: 'Steady', 'Resizing' + // AllocationStateTransitionTime - READ-ONLY; The time at which the compute entered its current allocation state. + // Errors - READ-ONLY; Collection of errors encountered by various compute nodes during node setup. + // CurrentNodeCount - READ-ONLY; The number of compute nodes currently assigned to the compute. + // TargetNodeCount - READ-ONLY; The target number of compute nodes for the compute. If the allocationState is resizing, this property denotes the target node count for the ongoing resize operation. If the allocationState is steady, this property denotes the target node count for the previous resize operation. + // NodeStateCounts - READ-ONLY; Counts of various node states on the compute. + }, + // ComputeLocation - Location for the underlying compute + ComputeLocation: &location, + // ProvisioningState - READ-ONLY; The provision state of the cluster. Valid values are Unknown, Updating, Provisioning, Succeeded, and Failed. Possible values include: 'ProvisioningStateUnknown', 'ProvisioningStateUpdating', 'ProvisioningStateCreating', 'ProvisioningStateDeleting', 'ProvisioningStateSucceeded', 'ProvisioningStateFailed', 'ProvisioningStateCanceled' + // Description - The description of the Machine Learning compute. + Description: &description, + // CreatedOn - READ-ONLY; The date and time when the compute was created. + // ModifiedOn - READ-ONLY; The date and time when the compute was last modified. + // ResourceID - ARM resource id of the underlying compute + // ProvisioningErrors - READ-ONLY; Errors during provisioning + // IsAttachedCompute - READ-ONLY; Indicating whether the compute was provisioned by user and brought from outside if true, or machine learning service provisioned it if false. + // ComputeType - Possible values include: 'ComputeTypeCompute', 'ComputeTypeAKS1', 'ComputeTypeAmlCompute1', 'ComputeTypeVirtualMachine1', 'ComputeTypeHDInsight1', 'ComputeTypeDataFactory1', 'ComputeTypeDatabricks1', 'ComputeTypeDataLakeAnalytics1' + ComputeType: "ComputeTypeAmlCompute1", + } + + amlComputeProperties, isAmlCompute := (machinelearningservices.BasicCompute).AsAmlCompute(computeClusterProperties) + if !isAmlCompute { + return fmt.Errorf("error: No Compute cluster") + } + + computeClusterParameters := machinelearningservices.ComputeResource{ + // Properties - Compute properties + Properties: amlComputeProperties, + // ID - READ-ONLY; Specifies the resource ID. + // Name - READ-ONLY; Specifies the name of the resource. + // Identity - The identity of the resource. + Identity: expandComputeClusterIdentity(identity), + // Location - Specifies the location of the resource. + Location: &location, + // Type - READ-ONLY; Specifies the type of the resource. + // Tags - Contains resource tags defined as key/value pairs. + Tags: tags.Expand(t), + // Sku - The sku of the workspace. + Sku: sku, + } + + future, err := mlComputeClient.CreateOrUpdate(ctx, workspaceID.ResourceGroup, workspaceID.Name, name, computeClusterParameters) + if err != nil { + return fmt.Errorf("error creating Compute Cluster %q in workspace %q (Resource Group %q): %+v", + name, workspaceID.Name, workspaceID.ResourceGroup, err) + } + if err := future.WaitForCompletionRef(ctx, mlComputeClient.Client); err != nil { + return fmt.Errorf("error waiting for creation of Compute Cluster %q in workspace %q (Resource Group %q): %+v", + name, workspaceID.Name, workspaceID.ResourceGroup, err) + } + resp, err := mlComputeClient.Get(ctx, workspaceID.ResourceGroup, workspaceID.Name, name) + if err != nil { + return fmt.Errorf("error retrieving Compute Cluster Compute %q in workspace %q (Resource Group %q): %+v", + name, workspaceID.Name, workspaceID.ResourceGroup, err) + } + + if resp.ID == nil { + return fmt.Errorf("cannot read Compute Cluster ID %q in workspace %q (Resource Group %q) ID", + name, workspaceID.Name, workspaceID.ResourceGroup) + } + + id, err := parse.ComputeClusterID(*resp.ID) + if err != nil { + return err + } + d.SetId(id.ID()) + + return resourceComputeClusterRead(d, meta) +} + +func resourceComputeClusterRead(d *schema.ResourceData, meta interface{}) error { + mlComputeClient := meta.(*clients.Client).MachineLearning.MachineLearningComputeClient + mlWorkspacesClient := meta.(*clients.Client).MachineLearning.WorkspacesClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.ComputeClusterID(d.Id()) + if err != nil { + return fmt.Errorf("error parsing Compute Cluster ID `%q`: %+v", d.Id(), err) + } + + computeResource, err := mlComputeClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.ComputeName) + if err != nil { + if utils.ResponseWasNotFound(computeResource.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("error making Read request on Compute Cluster %q in Workspace %q (Resource Group %q): %+v", + id.ComputeName, id.WorkspaceName, id.ResourceGroup, err) + } + + d.Set("name", id.ComputeName) + + workspaceResp, err := mlWorkspacesClient.Get(ctx, id.ResourceGroup, id.WorkspaceName) + if err != nil { + return err + } + d.Set("machine_learning_workspace_id", workspaceResp.ID) + + // use ComputeResource to get to AKS Cluster ID and other properties + computeCluster, isComputeCluster := (machinelearningservices.BasicCompute).AsAmlCompute(computeResource.Properties) + if !isComputeCluster { + return fmt.Errorf("compute resource %s is not an Aml Compute cluster", id.ComputeName) + } + + d.Set("vm_size", computeCluster.Properties.VMSize) + d.Set("vm_priority", computeCluster.Properties.VMPriority) + + d.Set("scale_settings", flattenScaleSettings(computeCluster.Properties.ScaleSettings)) + + d.Set("subnet_resource_id", computeCluster.Properties.Subnet.ID) + + if location := computeResource.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + if err := d.Set("identity", flattenComputeClusterIdentity(computeResource.Identity)); err != nil { + return fmt.Errorf("error flattening identity on Workspace %q (Resource Group %q): %+v", + id.ComputeName, id.ResourceGroup, err) + } + + return tags.FlattenAndSet(d, computeResource.Tags) +} + +func resourceComputeClusterUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).MachineLearning.MachineLearningComputeClient + ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.ComputeClusterID(d.Id()) + if err != nil { + return err + } + + update := machinelearningservices.ClusterUpdateParameters{ + ClusterUpdateProperties: &machinelearningservices.ClusterUpdateProperties{}, + } + + if d.HasChange("scale_settings") { + scaleSettings := d.Get("scale_settings").([]interface{}) + update.ScaleSettings = expandScaleSettings(scaleSettings) + } + + if _, err := client.Update(ctx, id.ResourceGroup, id.WorkspaceName, id.ComputeName, update); err != nil { + return fmt.Errorf("error updating Machine Learning Workspace %q (Resource Group %q): %+v", id.WorkspaceName, id.ResourceGroup, err) + } + + return resourceComputeClusterRead(d, meta) +} + +func resourceComputeClusterDelete(d *schema.ResourceData, meta interface{}) error { + mlComputeClient := meta.(*clients.Client).MachineLearning.MachineLearningComputeClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + id, err := parse.ComputeClusterID(d.Id()) + if err != nil { + return fmt.Errorf("error parsing Compute Cluster ID `%q`: %+v", d.Id(), err) + } + underlying_resource_action := machinelearningservices.Detach + future, err := mlComputeClient.Delete(ctx, id.ResourceGroup, id.WorkspaceName, id.ComputeName, underlying_resource_action) + if err != nil { + return fmt.Errorf("error deleting Compute Cluster %q in workspace %q (Resource Group %q): %+v", id.ComputeName, id.WorkspaceName, id.ResourceGroup, err) + } + if err := future.WaitForCompletionRef(ctx, mlComputeClient.Client); err != nil { + return fmt.Errorf("error waiting for deletion of Compute Cluster %q in workspace %q (Resource Group %q): %+v", id.ComputeName, id.WorkspaceName, id.ResourceGroup, err) + } + return nil +} + +func expandScaleSettings(input []interface{}) *machinelearningservices.ScaleSettings { + if len(input) == 0 { + return nil + } + + v := input[0].(map[string]interface{}) + + max_node_count := int32(v["max_node_count"].(int)) + min_node_count := int32(v["min_node_count"].(int)) + node_idle_time_before_scale_down := string(v["node_idle_time_before_scale_down"].(string)) + + return &machinelearningservices.ScaleSettings{ + MaxNodeCount: &max_node_count, + MinNodeCount: &min_node_count, + NodeIdleTimeBeforeScaleDown: &node_idle_time_before_scale_down, + } +} + +func flattenScaleSettings(scaleSettings *machinelearningservices.ScaleSettings) []interface{} { + if scaleSettings == nil { + return []interface{}{} + } + + return []interface{}{ + map[string]interface{}{ + "max_node_count": scaleSettings.MaxNodeCount, + "min_node_count": scaleSettings.MinNodeCount, + "node_idle_time_before_scale_down": scaleSettings.NodeIdleTimeBeforeScaleDown, + }, + } +} + +func expandComputeClusterIdentity(input []interface{}) *machinelearningservices.Identity { + if len(input) == 0 { + return nil + } + + v := input[0].(map[string]interface{}) + + return &machinelearningservices.Identity{ + Type: machinelearningservices.ResourceIdentityType(v["type"].(string)), + } +} + +func flattenComputeClusterIdentity(identity *machinelearningservices.Identity) []interface{} { + if identity == nil { + return []interface{}{} + } + + t := string(identity.Type) + + principalID := "" + if identity.PrincipalID != nil { + principalID = *identity.PrincipalID + } + + tenantID := "" + if identity.TenantID != nil { + tenantID = *identity.TenantID + } + + return []interface{}{ + map[string]interface{}{ + "type": t, + "principal_id": principalID, + "tenant_id": tenantID, + }, + } +} diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go new file mode 100644 index 000000000000..09a7199f1688 --- /dev/null +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go @@ -0,0 +1,270 @@ +package machinelearning_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +type ComputeClusterResource struct{} + +func TestAccComputeCluster_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_machine_learning_compute_cluster", "test") + r := ComputeClusterResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("identity.#").HasValue("1"), + check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), + check.That(data.ResourceName).Key("identity.0.principal_id").Exists(), + check.That(data.ResourceName).Key("identity.0.tenant_id").Exists(), + check.That(data.ResourceName).Key("scale_settings.#").HasValue("1"), + check.That(data.ResourceName).Key("scale_settings.0.max_node_count").Exists(), + check.That(data.ResourceName).Key("scale_settings.0.min_node_count").Exists(), + check.That(data.ResourceName).Key("scale_settings.0.node_idle_time_before_scale_down").Exists(), + ), + }, + data.ImportStep(), + }) +} + +func TestAccComputeCluster_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_machine_learning_compute_cluster", "test") + r := ComputeClusterResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("identity.#").HasValue("1"), + check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), + check.That(data.ResourceName).Key("identity.0.principal_id").Exists(), + check.That(data.ResourceName).Key("identity.0.tenant_id").Exists(), + check.That(data.ResourceName).Key("scale_settings.#").HasValue("1"), + check.That(data.ResourceName).Key("scale_settings.0.max_node_count").Exists(), + check.That(data.ResourceName).Key("scale_settings.0.min_node_count").Exists(), + check.That(data.ResourceName).Key("scale_settings.0.node_idle_time_before_scale_down").Exists(), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccComputeCluster_basicUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_machine_learning_compute_cluster", "test") + r := ComputeClusterResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("identity.#").HasValue("1"), + check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), + check.That(data.ResourceName).Key("identity.0.principal_id").Exists(), + check.That(data.ResourceName).Key("identity.0.tenant_id").Exists(), + check.That(data.ResourceName).Key("scale_settings.#").HasValue("1"), + check.That(data.ResourceName).Key("scale_settings.0.max_node_count").Exists(), + check.That(data.ResourceName).Key("scale_settings.0.min_node_count").Exists(), + check.That(data.ResourceName).Key("scale_settings.0.node_idle_time_before_scale_down").Exists(), + ), + }, + data.ImportStep(), + { + Config: r.basicUpdate(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("identity.#").HasValue("1"), + check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), + check.That(data.ResourceName).Key("identity.0.principal_id").Exists(), + check.That(data.ResourceName).Key("identity.0.tenant_id").Exists(), + check.That(data.ResourceName).Key("scale_settings.#").HasValue("1"), + check.That(data.ResourceName).Key("scale_settings.0.max_node_count").Exists(), + check.That(data.ResourceName).Key("scale_settings.0.min_node_count").Exists(), + check.That(data.ResourceName).Key("scale_settings.0.node_idle_time_before_scale_down").Exists(), + ), + }, + data.ImportStep(), + }) +} + +func (r ComputeClusterResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { + computeClusterClient := client.MachineLearning.MachineLearningComputeClient + id, err := parse.ComputeClusterID(state.ID) + + if err != nil { + return nil, err + } + + computeResource, err := computeClusterClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.ComputeName) + if err != nil { + if utils.ResponseWasNotFound(computeResource.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving Machine Learning Compute Cluster %q: %+v", state.ID, err) + } + return utils.Bool(computeResource.Properties != nil), nil +} + +func (r ComputeClusterResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_machine_learning_compute_cluster" "test" { + name = "CC-%d" + location = azurerm_resource_group.test.location + vm_priority = "LowPriority" + vm_size = "STANDARD_DS2_V2" + machine_learning_workspace_id = azurerm_machine_learning_workspace.test.id + subnet_resource_id = azurerm_subnet.test.id + + scale_settings { + min_node_count = 0 + max_node_count = 1 + node_idle_time_before_scale_down = "PT30S" # 30 seconds + } + + identity { + type = "SystemAssigned" + } +} +`, template, data.RandomIntOfLength(8)) +} + +func (r ComputeClusterResource) basicUpdate(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_machine_learning_compute_cluster" "test" { + name = "CC-%d" + location = azurerm_resource_group.test.location + vm_priority = "LowPriority" + vm_size = "STANDARD_DS2_V2" + machine_learning_workspace_id = azurerm_machine_learning_workspace.test.id + subnet_resource_id = azurerm_subnet.test.id + + scale_settings { + min_node_count = 1 + max_node_count = 2 + node_idle_time_before_scale_down = "PT60S" # 60 seconds + } + + identity { + type = "SystemAssigned" + } +} +`, template, data.RandomIntOfLength(8)) +} + +func (r ComputeClusterResource) requiresImport(data acceptance.TestData) string { + template := r.basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_machine_learning_compute_cluster" "import" { + name = azurerm_machine_learning_compute_cluster.test.name + location = azurerm_machine_learning_compute_cluster.test.location + vm_priority = azurerm_machine_learning_compute_cluster.test.vm_priority + vm_size = azurerm_machine_learning_compute_cluster.test.vm_size + machine_learning_workspace_id = azurerm_machine_learning_compute_cluster.test.id + + scale_settings { + min_node_count = 0 + max_node_count = 1 + node_idle_time_before_scale_down = "PT2M" # 120 seconds + } + + identity { + type = "SystemAssigned" + } +} + +`, template) +} + +func (r ComputeClusterResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} + } + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-ml-%[1]d" + location = "%[2]s" + tags = { + "stage" = "test" + } +} + +resource "azurerm_application_insights" "test" { + name = "acctestai-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + application_type = "web" +} + +resource "azurerm_key_vault" "test" { + name = "acctestvault%[3]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + tenant_id = data.azurerm_client_config.current.tenant_id + + sku_name = "standard" + + purge_protection_enabled = true +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%[4]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_machine_learning_workspace" "test" { + name = "acctest-MLW%[5]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + application_insights_id = azurerm_application_insights.test.id + key_vault_id = azurerm_key_vault.test.id + storage_account_id = azurerm_storage_account.test.id + + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%[6]d" + address_space = ["10.1.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_subnet" "test" { + name = "acctestsubnet%[7]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefix = "10.1.0.0/24" +} +`, data.RandomInteger, data.Locations.Primary, + data.RandomIntOfLength(12), data.RandomIntOfLength(15), data.RandomIntOfLength(16), + data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) +} diff --git a/azurerm/internal/services/machinelearning/parse/compute_cluster.go b/azurerm/internal/services/machinelearning/parse/compute_cluster.go new file mode 100644 index 000000000000..4dcc2dd18144 --- /dev/null +++ b/azurerm/internal/services/machinelearning/parse/compute_cluster.go @@ -0,0 +1,75 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type ComputeClusterId struct { + SubscriptionId string + ResourceGroup string + WorkspaceName string + ComputeName string +} + +func NewComputeClusterID(subscriptionId, resourceGroup, workspaceName, computeName string) ComputeClusterId { + return ComputeClusterId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + WorkspaceName: workspaceName, + ComputeName: computeName, + } +} + +func (id ComputeClusterId) String() string { + segments := []string{ + fmt.Sprintf("Compute Name %q", id.ComputeName), + fmt.Sprintf("Workspace Name %q", id.WorkspaceName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Compute Cluster", segmentsStr) +} + +func (id ComputeClusterId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.MachineLearningServices/workspaces/%s/computes/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.WorkspaceName, id.ComputeName) +} + +// ComputeClusterID parses a ComputeCluster ID into an ComputeClusterId struct +func ComputeClusterID(input string) (*ComputeClusterId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := ComputeClusterId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.WorkspaceName, err = id.PopSegment("workspaces"); err != nil { + return nil, err + } + if resourceId.ComputeName, err = id.PopSegment("computes"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/azurerm/internal/services/machinelearning/parse/compute_cluster_test.go b/azurerm/internal/services/machinelearning/parse/compute_cluster_test.go new file mode 100644 index 000000000000..de390027ca48 --- /dev/null +++ b/azurerm/internal/services/machinelearning/parse/compute_cluster_test.go @@ -0,0 +1,128 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = ComputeClusterId{} + +func TestComputeClusterIDFormatter(t *testing.T) { + actual := NewComputeClusterID("00000000-0000-0000-0000-000000000000", "resGroup1", "workspace1", "cluster1").ID() + expected := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestComputeClusterID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *ComputeClusterId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Error: true, + }, + + { + // missing WorkspaceName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/", + Error: true, + }, + + { + // missing value for WorkspaceName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/", + Error: true, + }, + + { + // missing ComputeName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/", + Error: true, + }, + + { + // missing value for ComputeName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1", + Expected: &ComputeClusterId{ + SubscriptionId: "00000000-0000-0000-0000-000000000000", + ResourceGroup: "resGroup1", + WorkspaceName: "workspace1", + ComputeName: "cluster1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/00000000-0000-0000-0000-000000000000/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.MACHINELEARNINGSERVICES/WORKSPACES/WORKSPACE1/COMPUTES/CLUSTER1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := ComputeClusterID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.WorkspaceName != v.Expected.WorkspaceName { + t.Fatalf("Expected %q but got %q for WorkspaceName", v.Expected.WorkspaceName, actual.WorkspaceName) + } + if actual.ComputeName != v.Expected.ComputeName { + t.Fatalf("Expected %q but got %q for ComputeName", v.Expected.ComputeName, actual.ComputeName) + } + } +} diff --git a/azurerm/internal/services/machinelearning/registration.go b/azurerm/internal/services/machinelearning/registration.go index e8fcd46fdefb..5d2caba3af04 100644 --- a/azurerm/internal/services/machinelearning/registration.go +++ b/azurerm/internal/services/machinelearning/registration.go @@ -29,5 +29,6 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ "azurerm_machine_learning_workspace": resourceMachineLearningWorkspace(), "azurerm_machine_learning_inference_cluster": resourceAksInferenceCluster(), + "azurerm_machine_learning_compute_cluster": resourceComputeCluster(), } } diff --git a/azurerm/internal/services/machinelearning/resourceids.go b/azurerm/internal/services/machinelearning/resourceids.go index 18ebbab752ba..a13519b21232 100644 --- a/azurerm/internal/services/machinelearning/resourceids.go +++ b/azurerm/internal/services/machinelearning/resourceids.go @@ -3,3 +3,4 @@ package machinelearning //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=InferenceCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=KubernetesCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.ContainerService/managedClusters/cluster1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Workspace -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ComputeCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1 diff --git a/azurerm/internal/services/machinelearning/validate/compute_cluster_id.go b/azurerm/internal/services/machinelearning/validate/compute_cluster_id.go new file mode 100644 index 000000000000..45d84480db6d --- /dev/null +++ b/azurerm/internal/services/machinelearning/validate/compute_cluster_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/parse" +) + +func ComputeClusterID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.ComputeClusterID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/azurerm/internal/services/machinelearning/validate/compute_cluster_id_test.go b/azurerm/internal/services/machinelearning/validate/compute_cluster_id_test.go new file mode 100644 index 000000000000..540d30dd9d62 --- /dev/null +++ b/azurerm/internal/services/machinelearning/validate/compute_cluster_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestComputeClusterID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Valid: false, + }, + + { + // missing WorkspaceName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/", + Valid: false, + }, + + { + // missing value for WorkspaceName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/", + Valid: false, + }, + + { + // missing ComputeName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/", + Valid: false, + }, + + { + // missing value for ComputeName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/00000000-0000-0000-0000-000000000000/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.MACHINELEARNINGSERVICES/WORKSPACES/WORKSPACE1/COMPUTES/CLUSTER1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := ComputeClusterID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/website/docs/r/machine_learning_compute_cluster.html.markdown b/website/docs/r/machine_learning_compute_cluster.html.markdown new file mode 100644 index 000000000000..6975a64e18e8 --- /dev/null +++ b/website/docs/r/machine_learning_compute_cluster.html.markdown @@ -0,0 +1,98 @@ +--- +subcategory: "Machine Learning" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_machine_learning_compute_cluster" +description: |- + Manages a Machine Learning Compute Cluster. +--- + +# azurerm_machine_learning_compute_cluster + +Manages a Machine Learning Compute Cluster. + +## Example Usage + +```hcl +resource "azurerm_machine_learning_compute_cluster" "example" { + name = "example" + location = "West Europe" + vm_size = "Standard_DS2_v2" + + identity { + type = "SystemAssigned" + } + + scale_settings { + max_node_count = 1 + min_node_count = 0 + node_idle_time_before_scale_down = "PT30S" + } + machine_learning_workspace_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1" + vm_priority = "LowPriority" +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `identity` - (Required) A `identity` block as defined below. Changing this forces a new Machine Learning Compute Cluster to be created. + +* `location` - (Required) The Azure Region where the Machine Learning Compute Cluster should exist. Changing this forces a new Machine Learning Compute Cluster to be created. + +* `machine_learning_workspace_id` - (Required) The ID of the Machine Learning Workspace. Changing this forces a new Machine Learning Compute Cluster to be created. + +* `name` - (Required) The name which should be used for this Machine Learning Compute Cluster. Changing this forces a new Machine Learning Compute Cluster to be created. + +* `scale_settings` - (Required) A `scale_settings` block as defined below. + +* `vm_priority` - (Required) The priority of the VM. Changing this forces a new Machine Learning Compute Cluster to be created. + +* `vm_size` - (Required) The size of the VM. Changing this forces a new Machine Learning Compute Cluster to be created. + +--- + +* `description` - (Optional) The description of the Machine Learning compute. + +* `subnet_resource_id` - (Optional) The ID of the Subnet that the Compute Cluster should reside in. Changing this forces a new Machine Learning Compute Cluster to be created. + +* `tags` - (Optional) A mapping of tags which should be assigned to the Machine Learning Compute Cluster. + +--- + +A `identity` block supports the following: + +* `type` - (Required) The Type of Identity which should be used for this Disk Encryption Set. At this time the only possible value is SystemAssigned. + +--- + +A `scale_settings` block supports the following: + +* `max_node_count` - (Required) Maximum node count. + +* `min_node_count` - (Required) Minimal node count. + +* `node_idle_time_before_scale_down` - (Required) Node Idle Time Before Scale Down: defines the time until the compute is shutdown when it has gone into Idle state. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Machine Learning Compute Cluster. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Machine Learning Compute Cluster. +* `read` - (Defaults to 5 minutes) Used when retrieving the Machine Learning Compute Cluster. +* `update` - (Defaults to 30 minutes) Used when updating the Machine Learning Compute Cluster. +* `delete` - (Defaults to 30 minutes) Used when deleting the Machine Learning Compute Cluster. + +## Import + +Machine Learning Compute Clusters can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_machine_learning_compute_cluster.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1 +``` \ No newline at end of file From 871128d291e78dce5578403cb8c8ed815f11eefd Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Wed, 12 May 2021 19:03:32 +0200 Subject: [PATCH 02/18] Address review#1 --- ...chine_learning_compute_cluster_resource.go | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index 2937927cadb7..102ef4fe02b3 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -140,9 +140,7 @@ func resourceComputeClusterCreate(d *schema.ResourceData, meta interface{}) erro name := d.Get("name").(string) // Get Machine Learning Workspace Name and Resource Group from ID - unparsedWorkspaceID := d.Get("machine_learning_workspace_id").(string) - - workspaceID, err := parse.WorkspaceID(unparsedWorkspaceID) + workspaceID, err := parse.WorkspaceID(d.Get("machine_learning_workspace_id").(string)) if err != nil { return err } @@ -162,8 +160,7 @@ func resourceComputeClusterCreate(d *schema.ResourceData, meta interface{}) erro vmSize := d.Get("vm_size").(string) vmPriority := d.Get("vm_priority").(string) - inputScaleSettings := d.Get("scale_settings").([]interface{}) - scaleSettings := expandScaleSettings(inputScaleSettings) + scaleSettings := expandScaleSettings(d.Get("scale_settings").([]interface{})) subnetResourceId := d.Get("subnet_resource_id").(string) @@ -248,21 +245,9 @@ func resourceComputeClusterCreate(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("error waiting for creation of Compute Cluster %q in workspace %q (Resource Group %q): %+v", name, workspaceID.Name, workspaceID.ResourceGroup, err) } - resp, err := mlComputeClient.Get(ctx, workspaceID.ResourceGroup, workspaceID.Name, name) - if err != nil { - return fmt.Errorf("error retrieving Compute Cluster Compute %q in workspace %q (Resource Group %q): %+v", - name, workspaceID.Name, workspaceID.ResourceGroup, err) - } - if resp.ID == nil { - return fmt.Errorf("cannot read Compute Cluster ID %q in workspace %q (Resource Group %q) ID", - name, workspaceID.Name, workspaceID.ResourceGroup) - } - - id, err := parse.ComputeClusterID(*resp.ID) - if err != nil { - return err - } + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + id := parse.NewComputeClusterID(subscriptionId, workspaceID.ResourceGroup, workspaceID.Name, name) d.SetId(id.ID()) return resourceComputeClusterRead(d, meta) From 48cd1c7e35e0fd15e32182f1d05db346805e111e Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Sat, 15 May 2021 20:46:03 +0200 Subject: [PATCH 03/18] Remove Update --- ...chine_learning_compute_cluster_resource.go | 152 +++++------------- ..._learning_compute_cluster_resource_test.go | 64 -------- 2 files changed, 39 insertions(+), 177 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index 102ef4fe02b3..ebedea975c23 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -25,7 +25,6 @@ func resourceComputeCluster() *schema.Resource { return &schema.Resource{ Create: resourceComputeClusterCreate, Read: resourceComputeClusterRead, - Update: resourceComputeClusterUpdate, Delete: resourceComputeClusterDelete, Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { @@ -36,7 +35,6 @@ func resourceComputeCluster() *schema.Resource { Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(30 * time.Minute), Read: schema.DefaultTimeout(5 * time.Minute), - Update: schema.DefaultTimeout(30 * time.Minute), Delete: schema.DefaultTimeout(30 * time.Minute), }, @@ -77,6 +75,7 @@ func resourceComputeCluster() *schema.Resource { "type": { Type: schema.TypeString, Required: true, + ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ string(machinelearningservices.SystemAssigned), }, false), @@ -84,10 +83,12 @@ func resourceComputeCluster() *schema.Resource { "principal_id": { Type: schema.TypeString, Computed: true, + ForceNew: true, }, "tenant_id": { Type: schema.TypeString, Computed: true, + ForceNew: true, }, }, }, @@ -96,20 +97,24 @@ func resourceComputeCluster() *schema.Resource { "scale_settings": { Type: schema.TypeList, Required: true, + ForceNew: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "max_node_count": { Type: schema.TypeInt, Required: true, + ForceNew: true, }, "min_node_count": { Type: schema.TypeInt, Required: true, + ForceNew: true, }, "node_idle_time_before_scale_down": { Type: schema.TypeString, Required: true, + ForceNew: true, }, }, }, @@ -118,6 +123,7 @@ func resourceComputeCluster() *schema.Resource { "description": { Type: schema.TypeString, Optional: true, + ForceNew: true, }, "subnet_resource_id": { @@ -126,7 +132,7 @@ func resourceComputeCluster() *schema.Resource { ForceNew: true, }, - "tags": tags.Schema(), + "tags": tags.ForceNewSchema(), }, } } @@ -156,93 +162,44 @@ func resourceComputeClusterCreate(d *schema.ResourceData, meta interface{}) erro return tf.ImportAsExistsError("azurerm_machine_learning_compute_cluster", *existing.ID) } - // AML Compute Cluster configuration (not needed here presumably) - vmSize := d.Get("vm_size").(string) - vmPriority := d.Get("vm_priority").(string) - - scaleSettings := expandScaleSettings(d.Get("scale_settings").([]interface{})) - - subnetResourceId := d.Get("subnet_resource_id").(string) - - description := d.Get("description").(string) - - location := d.Get("location").(string) - - identity := d.Get("identity").([]interface{}) - - // Get SKU from Workspace - workspace, err := mlWorkspacesClient.Get(ctx, workspaceID.ResourceGroup, workspaceID.Name) - if err != nil { - return err - } - sku := workspace.Sku - - t := d.Get("tags").(map[string]interface{}) - - subnetId := machinelearningservices.ResourceID{ID: utils.String(subnetResourceId)} - computeClusterProperties := machinelearningservices.AmlCompute{ - // Properties - AML Compute properties Properties: &machinelearningservices.AmlComputeProperties{ - // VMSize - Virtual Machine Size - VMSize: utils.String(vmSize), - // VMPriority - Virtual Machine priority. Possible values include: 'Dedicated', 'LowPriority' - VMPriority: machinelearningservices.VMPriority(vmPriority), - // ScaleSettings - Scale settings for AML Compute - ScaleSettings: scaleSettings, - // UserAccountCredentials - Credentials for an administrator user account that will be created on each compute node. - // Subnet - Virtual network subnet resource ID the compute nodes belong to. - Subnet: &subnetId, - // RemoteLoginPortPublicAccess - State of the public SSH port. Possible values are: Disabled - Indicates that the public ssh port is closed on all nodes of the cluster. Enabled - Indicates that the public ssh port is open on all nodes of the cluster. NotSpecified - Indicates that the public ssh port is closed on all nodes of the cluster if VNet is defined, else is open all public nodes. It can be default only during cluster creation time, after creation it will be either enabled or disabled. Possible values include: 'RemoteLoginPortPublicAccessEnabled', 'RemoteLoginPortPublicAccessDisabled', 'RemoteLoginPortPublicAccessNotSpecified' - // AllocationState - READ-ONLY; Allocation state of the compute. Possible values are: steady - Indicates that the compute is not resizing. There are no changes to the number of compute nodes in the compute in progress. A compute enters this state when it is created and when no operations are being performed on the compute to change the number of compute nodes. resizing - Indicates that the compute is resizing; that is, compute nodes are being added to or removed from the compute. Possible values include: 'Steady', 'Resizing' - // AllocationStateTransitionTime - READ-ONLY; The time at which the compute entered its current allocation state. - // Errors - READ-ONLY; Collection of errors encountered by various compute nodes during node setup. - // CurrentNodeCount - READ-ONLY; The number of compute nodes currently assigned to the compute. - // TargetNodeCount - READ-ONLY; The target number of compute nodes for the compute. If the allocationState is resizing, this property denotes the target node count for the ongoing resize operation. If the allocationState is steady, this property denotes the target node count for the previous resize operation. - // NodeStateCounts - READ-ONLY; Counts of various node states on the compute. + VMSize: utils.String(d.Get("vm_size").(string)), + VMPriority: machinelearningservices.VMPriority(d.Get("vm_priority").(string)), + ScaleSettings: expandScaleSettings(d.Get("scale_settings").([]interface{})), + Subnet: &machinelearningservices.ResourceID{ID: utils.String(d.Get("subnet_resource_id").(string))}, }, - // ComputeLocation - Location for the underlying compute - ComputeLocation: &location, - // ProvisioningState - READ-ONLY; The provision state of the cluster. Valid values are Unknown, Updating, Provisioning, Succeeded, and Failed. Possible values include: 'ProvisioningStateUnknown', 'ProvisioningStateUpdating', 'ProvisioningStateCreating', 'ProvisioningStateDeleting', 'ProvisioningStateSucceeded', 'ProvisioningStateFailed', 'ProvisioningStateCanceled' - // Description - The description of the Machine Learning compute. - Description: &description, - // CreatedOn - READ-ONLY; The date and time when the compute was created. - // ModifiedOn - READ-ONLY; The date and time when the compute was last modified. - // ResourceID - ARM resource id of the underlying compute - // ProvisioningErrors - READ-ONLY; Errors during provisioning - // IsAttachedCompute - READ-ONLY; Indicating whether the compute was provisioned by user and brought from outside if true, or machine learning service provisioned it if false. - // ComputeType - Possible values include: 'ComputeTypeCompute', 'ComputeTypeAKS1', 'ComputeTypeAmlCompute1', 'ComputeTypeVirtualMachine1', 'ComputeTypeHDInsight1', 'ComputeTypeDataFactory1', 'ComputeTypeDatabricks1', 'ComputeTypeDataLakeAnalytics1' - ComputeType: "ComputeTypeAmlCompute1", + ComputeLocation: utils.String(d.Get("location").(string)), + Description: utils.String(d.Get("description").(string)), + ComputeType: "ComputeTypeAmlCompute1", } amlComputeProperties, isAmlCompute := (machinelearningservices.BasicCompute).AsAmlCompute(computeClusterProperties) if !isAmlCompute { - return fmt.Errorf("error: No Compute cluster") + return fmt.Errorf("no compute cluster") + } + + // Get SKU from Workspace + workspace, err := mlWorkspacesClient.Get(ctx, workspaceID.ResourceGroup, workspaceID.Name) + if err != nil { + return err } computeClusterParameters := machinelearningservices.ComputeResource{ - // Properties - Compute properties Properties: amlComputeProperties, - // ID - READ-ONLY; Specifies the resource ID. - // Name - READ-ONLY; Specifies the name of the resource. - // Identity - The identity of the resource. - Identity: expandComputeClusterIdentity(identity), - // Location - Specifies the location of the resource. - Location: &location, - // Type - READ-ONLY; Specifies the type of the resource. - // Tags - Contains resource tags defined as key/value pairs. - Tags: tags.Expand(t), - // Sku - The sku of the workspace. - Sku: sku, + Identity: expandComputeClusterIdentity(d.Get("identity").([]interface{})), + Location: computeClusterProperties.ComputeLocation, + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + Sku: workspace.Sku, } future, err := mlComputeClient.CreateOrUpdate(ctx, workspaceID.ResourceGroup, workspaceID.Name, name, computeClusterParameters) if err != nil { - return fmt.Errorf("error creating Compute Cluster %q in workspace %q (Resource Group %q): %+v", + return fmt.Errorf("creating Compute Cluster %q in workspace %q (Resource Group %q): %+v", name, workspaceID.Name, workspaceID.ResourceGroup, err) } if err := future.WaitForCompletionRef(ctx, mlComputeClient.Client); err != nil { - return fmt.Errorf("error waiting for creation of Compute Cluster %q in workspace %q (Resource Group %q): %+v", + return fmt.Errorf("waiting for creation of Compute Cluster %q in workspace %q (Resource Group %q): %+v", name, workspaceID.Name, workspaceID.ResourceGroup, err) } @@ -261,7 +218,7 @@ func resourceComputeClusterRead(d *schema.ResourceData, meta interface{}) error id, err := parse.ComputeClusterID(d.Id()) if err != nil { - return fmt.Errorf("error parsing Compute Cluster ID `%q`: %+v", d.Id(), err) + return fmt.Errorf("parsing Compute Cluster ID `%q`: %+v", d.Id(), err) } computeResource, err := mlComputeClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.ComputeName) @@ -270,17 +227,17 @@ func resourceComputeClusterRead(d *schema.ResourceData, meta interface{}) error d.SetId("") return nil } - return fmt.Errorf("error making Read request on Compute Cluster %q in Workspace %q (Resource Group %q): %+v", + return fmt.Errorf("making Read request on Compute Cluster %q in Workspace %q (Resource Group %q): %+v", id.ComputeName, id.WorkspaceName, id.ResourceGroup, err) } d.Set("name", id.ComputeName) - workspaceResp, err := mlWorkspacesClient.Get(ctx, id.ResourceGroup, id.WorkspaceName) + workspace, err := mlWorkspacesClient.Get(ctx, id.ResourceGroup, id.WorkspaceName) if err != nil { return err } - d.Set("machine_learning_workspace_id", workspaceResp.ID) + d.Set("machine_learning_workspace_id", workspace.ID) // use ComputeResource to get to AKS Cluster ID and other properties computeCluster, isComputeCluster := (machinelearningservices.BasicCompute).AsAmlCompute(computeResource.Properties) @@ -290,9 +247,7 @@ func resourceComputeClusterRead(d *schema.ResourceData, meta interface{}) error d.Set("vm_size", computeCluster.Properties.VMSize) d.Set("vm_priority", computeCluster.Properties.VMPriority) - d.Set("scale_settings", flattenScaleSettings(computeCluster.Properties.ScaleSettings)) - d.Set("subnet_resource_id", computeCluster.Properties.Subnet.ID) if location := computeResource.Location; location != nil { @@ -300,54 +255,27 @@ func resourceComputeClusterRead(d *schema.ResourceData, meta interface{}) error } if err := d.Set("identity", flattenComputeClusterIdentity(computeResource.Identity)); err != nil { - return fmt.Errorf("error flattening identity on Workspace %q (Resource Group %q): %+v", + return fmt.Errorf("flattening identity on Workspace %q (Resource Group %q): %+v", id.ComputeName, id.ResourceGroup, err) } return tags.FlattenAndSet(d, computeResource.Tags) } -func resourceComputeClusterUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).MachineLearning.MachineLearningComputeClient - ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) - defer cancel() - - id, err := parse.ComputeClusterID(d.Id()) - if err != nil { - return err - } - - update := machinelearningservices.ClusterUpdateParameters{ - ClusterUpdateProperties: &machinelearningservices.ClusterUpdateProperties{}, - } - - if d.HasChange("scale_settings") { - scaleSettings := d.Get("scale_settings").([]interface{}) - update.ScaleSettings = expandScaleSettings(scaleSettings) - } - - if _, err := client.Update(ctx, id.ResourceGroup, id.WorkspaceName, id.ComputeName, update); err != nil { - return fmt.Errorf("error updating Machine Learning Workspace %q (Resource Group %q): %+v", id.WorkspaceName, id.ResourceGroup, err) - } - - return resourceComputeClusterRead(d, meta) -} - func resourceComputeClusterDelete(d *schema.ResourceData, meta interface{}) error { mlComputeClient := meta.(*clients.Client).MachineLearning.MachineLearningComputeClient ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() id, err := parse.ComputeClusterID(d.Id()) if err != nil { - return fmt.Errorf("error parsing Compute Cluster ID `%q`: %+v", d.Id(), err) + return fmt.Errorf("parsing Compute Cluster ID `%q`: %+v", d.Id(), err) } - underlying_resource_action := machinelearningservices.Detach - future, err := mlComputeClient.Delete(ctx, id.ResourceGroup, id.WorkspaceName, id.ComputeName, underlying_resource_action) + future, err := mlComputeClient.Delete(ctx, id.ResourceGroup, id.WorkspaceName, id.ComputeName, machinelearningservices.Detach) if err != nil { - return fmt.Errorf("error deleting Compute Cluster %q in workspace %q (Resource Group %q): %+v", id.ComputeName, id.WorkspaceName, id.ResourceGroup, err) + return fmt.Errorf("deleting Compute Cluster %q in workspace %q (Resource Group %q): %+v", id.ComputeName, id.WorkspaceName, id.ResourceGroup, err) } if err := future.WaitForCompletionRef(ctx, mlComputeClient.Client); err != nil { - return fmt.Errorf("error waiting for deletion of Compute Cluster %q in workspace %q (Resource Group %q): %+v", id.ComputeName, id.WorkspaceName, id.ResourceGroup, err) + return fmt.Errorf("waiting for deletion of Compute Cluster %q in workspace %q (Resource Group %q): %+v", id.ComputeName, id.WorkspaceName, id.ResourceGroup, err) } return nil } @@ -401,8 +329,6 @@ func flattenComputeClusterIdentity(identity *machinelearningservices.Identity) [ return []interface{}{} } - t := string(identity.Type) - principalID := "" if identity.PrincipalID != nil { principalID = *identity.PrincipalID @@ -415,7 +341,7 @@ func flattenComputeClusterIdentity(identity *machinelearningservices.Identity) [ return []interface{}{ map[string]interface{}{ - "type": t, + "type": string(identity.Type), "principal_id": principalID, "tenant_id": tenantID, }, diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go index 09a7199f1688..df067782daff 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go @@ -62,44 +62,6 @@ func TestAccComputeCluster_requiresImport(t *testing.T) { }) } -func TestAccComputeCluster_basicUpdate(t *testing.T) { - data := acceptance.BuildTestData(t, "azurerm_machine_learning_compute_cluster", "test") - r := ComputeClusterResource{} - - data.ResourceTest(t, r, []resource.TestStep{ - { - Config: r.basic(data), - Check: resource.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("identity.#").HasValue("1"), - check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), - check.That(data.ResourceName).Key("identity.0.principal_id").Exists(), - check.That(data.ResourceName).Key("identity.0.tenant_id").Exists(), - check.That(data.ResourceName).Key("scale_settings.#").HasValue("1"), - check.That(data.ResourceName).Key("scale_settings.0.max_node_count").Exists(), - check.That(data.ResourceName).Key("scale_settings.0.min_node_count").Exists(), - check.That(data.ResourceName).Key("scale_settings.0.node_idle_time_before_scale_down").Exists(), - ), - }, - data.ImportStep(), - { - Config: r.basicUpdate(data), - Check: resource.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("identity.#").HasValue("1"), - check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), - check.That(data.ResourceName).Key("identity.0.principal_id").Exists(), - check.That(data.ResourceName).Key("identity.0.tenant_id").Exists(), - check.That(data.ResourceName).Key("scale_settings.#").HasValue("1"), - check.That(data.ResourceName).Key("scale_settings.0.max_node_count").Exists(), - check.That(data.ResourceName).Key("scale_settings.0.min_node_count").Exists(), - check.That(data.ResourceName).Key("scale_settings.0.node_idle_time_before_scale_down").Exists(), - ), - }, - data.ImportStep(), - }) -} - func (r ComputeClusterResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { computeClusterClient := client.MachineLearning.MachineLearningComputeClient id, err := parse.ComputeClusterID(state.ID) @@ -144,32 +106,6 @@ resource "azurerm_machine_learning_compute_cluster" "test" { `, template, data.RandomIntOfLength(8)) } -func (r ComputeClusterResource) basicUpdate(data acceptance.TestData) string { - template := r.template(data) - return fmt.Sprintf(` -%s - -resource "azurerm_machine_learning_compute_cluster" "test" { - name = "CC-%d" - location = azurerm_resource_group.test.location - vm_priority = "LowPriority" - vm_size = "STANDARD_DS2_V2" - machine_learning_workspace_id = azurerm_machine_learning_workspace.test.id - subnet_resource_id = azurerm_subnet.test.id - - scale_settings { - min_node_count = 1 - max_node_count = 2 - node_idle_time_before_scale_down = "PT60S" # 60 seconds - } - - identity { - type = "SystemAssigned" - } -} -`, template, data.RandomIntOfLength(8)) -} - func (r ComputeClusterResource) requiresImport(data acceptance.TestData) string { template := r.basic(data) return fmt.Sprintf(` From b79336631e6c52c1c0a15017cbe9d7e6d46a0e36 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Sun, 16 May 2021 12:58:02 +0200 Subject: [PATCH 04/18] Fix requiresImport test --- .../machine_learning_compute_cluster_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go index df067782daff..99748a298e74 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go @@ -116,7 +116,7 @@ resource "azurerm_machine_learning_compute_cluster" "import" { location = azurerm_machine_learning_compute_cluster.test.location vm_priority = azurerm_machine_learning_compute_cluster.test.vm_priority vm_size = azurerm_machine_learning_compute_cluster.test.vm_size - machine_learning_workspace_id = azurerm_machine_learning_compute_cluster.test.id + machine_learning_workspace_id = azurerm_machine_learning_compute_cluster.test.machine_learning_workspace_id scale_settings { min_node_count = 0 From 26db4c43573a32e6a15e13719fc62013d5d69e32 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Sun, 16 May 2021 13:19:51 +0200 Subject: [PATCH 05/18] update docs --- ...ine_learning_compute_cluster.html.markdown | 102 ++++++++++++++---- 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/website/docs/r/machine_learning_compute_cluster.html.markdown b/website/docs/r/machine_learning_compute_cluster.html.markdown index 6975a64e18e8..dfa11c311b93 100644 --- a/website/docs/r/machine_learning_compute_cluster.html.markdown +++ b/website/docs/r/machine_learning_compute_cluster.html.markdown @@ -9,26 +9,91 @@ description: |- # azurerm_machine_learning_compute_cluster Manages a Machine Learning Compute Cluster. +**NOTE:** At this point in time the resource cannot be updated (not supported by the backend Azure Go SDK). Therefore it can only be created and deleted, not updated. At the moment, there is also no possibility to specify ssh User Account Credentials to ssh into the compute cluster. ## Example Usage ```hcl -resource "azurerm_machine_learning_compute_cluster" "example" { - name = "example" - location = "West Europe" - vm_size = "Standard_DS2_v2" +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "example" { + name = "example-rg" + location = "west europe" + tags = { + "stage" = "example" + } +} + +resource "azurerm_application_insights" "example" { + name = "example-ai" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + application_type = "web" +} + +resource "azurerm_key_vault" "example" { + name = "example-kv" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + tenant_id = data.azurerm_client_config.current.tenant_id + + sku_name = "standard" + + purge_protection_enabled = true +} + +resource "azurerm_storage_account" "example" { + name = "examplesa" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_machine_learning_workspace" "example" { + name = "example-mlw" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + application_insights_id = azurerm_application_insights.example.id + key_vault_id = azurerm_key_vault.example.id + storage_account_id = azurerm_storage_account.example.id identity { - type = "SystemAssigned" + type = "SystemAssigned" } +} + +resource "azurerm_virtual_network" "example" { + name = "example-vnet" + address_space = ["10.1.0.0/16"] + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_subnet" "example" { + name = "example-subnet" + resource_group_name = azurerm_resource_group.example.name + virtual_network_name = azurerm_virtual_network.example.name + address_prefixes = ["10.1.0.0/24"] +} + +resource "azurerm_machine_learning_compute_cluster" "test" { + name = "example" + location = "West Europe" + vm_priority = "LowPriority" + vm_size = "Standard_DS2_v2" + machine_learning_workspace_id = azurerm_machine_learning_workspace.example.id + subnet_resource_id = azurerm_subnet.example.id scale_settings { - max_node_count = 1 - min_node_count = 0 - node_idle_time_before_scale_down = "PT30S" + min_node_count = 0 + max_node_count = 1 + node_idle_time_before_scale_down = "PT30S" # 30 seconds + } + + identity { + type = "SystemAssigned" } - machine_learning_workspace_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1" - vm_priority = "LowPriority" } ``` @@ -44,7 +109,7 @@ The following arguments are supported: * `name` - (Required) The name which should be used for this Machine Learning Compute Cluster. Changing this forces a new Machine Learning Compute Cluster to be created. -* `scale_settings` - (Required) A `scale_settings` block as defined below. +* `scale_settings` - (Required) A `scale_settings` block as defined below. Changing this forces a new Machine Learning Compute Cluster to be created. * `vm_priority` - (Required) The priority of the VM. Changing this forces a new Machine Learning Compute Cluster to be created. @@ -52,27 +117,27 @@ The following arguments are supported: --- -* `description` - (Optional) The description of the Machine Learning compute. +* `description` - (Optional) The description of the Machine Learning compute. Changing this forces a new Machine Learning Compute Cluster to be created. * `subnet_resource_id` - (Optional) The ID of the Subnet that the Compute Cluster should reside in. Changing this forces a new Machine Learning Compute Cluster to be created. -* `tags` - (Optional) A mapping of tags which should be assigned to the Machine Learning Compute Cluster. +* `tags` - (Optional) A mapping of tags which should be assigned to the Machine Learning Compute Cluster. Changing this forces a new Machine Learning Compute Cluster to be created. --- A `identity` block supports the following: -* `type` - (Required) The Type of Identity which should be used for this Disk Encryption Set. At this time the only possible value is SystemAssigned. +* `type` - (Required) The Type of Identity which should be used for this Disk Encryption Set. At this time the only possible value is SystemAssigned. Changing this forces a new Machine Learning Compute Cluster to be created. --- A `scale_settings` block supports the following: -* `max_node_count` - (Required) Maximum node count. +* `max_node_count` - (Required) Maximum node count. Changing this forces a new Machine Learning Compute Cluster to be created. -* `min_node_count` - (Required) Minimal node count. +* `min_node_count` - (Required) Minimal node count. Changing this forces a new Machine Learning Compute Cluster to be created. -* `node_idle_time_before_scale_down` - (Required) Node Idle Time Before Scale Down: defines the time until the compute is shutdown when it has gone into Idle state. +* `node_idle_time_before_scale_down` - (Required) Node Idle Time Before Scale Down: defines the time until the compute is shutdown when it has gone into Idle state. Is defined according to W3C XML schema standard for duration. Changing this forces a new Machine Learning Compute Cluster to be created. ## Attributes Reference @@ -86,7 +151,6 @@ The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/d * `create` - (Defaults to 30 minutes) Used when creating the Machine Learning Compute Cluster. * `read` - (Defaults to 5 minutes) Used when retrieving the Machine Learning Compute Cluster. -* `update` - (Defaults to 30 minutes) Used when updating the Machine Learning Compute Cluster. * `delete` - (Defaults to 30 minutes) Used when deleting the Machine Learning Compute Cluster. ## Import @@ -95,4 +159,4 @@ Machine Learning Compute Clusters can be imported using the `resource id`, e.g. ```shell terraform import azurerm_machine_learning_compute_cluster.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1 -``` \ No newline at end of file +``` From 8ee86829096829458f2ca1f201fb48a4afd42bd6 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Sun, 16 May 2021 22:40:52 +0200 Subject: [PATCH 06/18] update read of ml workspace id --- .../machine_learning_compute_cluster_resource.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index ebedea975c23..68f0d4b06d23 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -212,7 +212,6 @@ func resourceComputeClusterCreate(d *schema.ResourceData, meta interface{}) erro func resourceComputeClusterRead(d *schema.ResourceData, meta interface{}) error { mlComputeClient := meta.(*clients.Client).MachineLearning.MachineLearningComputeClient - mlWorkspacesClient := meta.(*clients.Client).MachineLearning.WorkspacesClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() @@ -233,11 +232,9 @@ func resourceComputeClusterRead(d *schema.ResourceData, meta interface{}) error d.Set("name", id.ComputeName) - workspace, err := mlWorkspacesClient.Get(ctx, id.ResourceGroup, id.WorkspaceName) - if err != nil { - return err - } - d.Set("machine_learning_workspace_id", workspace.ID) + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + workspaceId := parse.NewWorkspaceID(subscriptionId, id.ResourceGroup, id.WorkspaceName) + d.Set("machine_learning_workspace_id", workspaceId.ID()) // use ComputeResource to get to AKS Cluster ID and other properties computeCluster, isComputeCluster := (machinelearningservices.BasicCompute).AsAmlCompute(computeResource.Properties) From 9786f12826419965261e87172d90c4e8890f25e2 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Sun, 16 May 2021 22:02:12 +0200 Subject: [PATCH 07/18] chore - use auto-generated parsers and validators for resource `azurerm_machine_learning_workspace` (#11739) --- azurerm/internal/services/machinelearning/resourceids.go | 1 + 1 file changed, 1 insertion(+) diff --git a/azurerm/internal/services/machinelearning/resourceids.go b/azurerm/internal/services/machinelearning/resourceids.go index a13519b21232..a32e04ff4f62 100644 --- a/azurerm/internal/services/machinelearning/resourceids.go +++ b/azurerm/internal/services/machinelearning/resourceids.go @@ -4,3 +4,4 @@ package machinelearning //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=KubernetesCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.ContainerService/managedClusters/cluster1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Workspace -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ComputeCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Workspace -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1 From 162178ffae735fe4d6d26c81167733a69a454b09 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Sun, 16 May 2021 23:06:22 +0200 Subject: [PATCH 08/18] fix tflint errors --- ...chine_learning_compute_cluster_resource.go | 2 - ..._learning_compute_cluster_resource_test.go | 138 +++++++++--------- 2 files changed, 69 insertions(+), 71 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index 68f0d4b06d23..4dd0aaf22d4a 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -83,12 +83,10 @@ func resourceComputeCluster() *schema.Resource { "principal_id": { Type: schema.TypeString, Computed: true, - ForceNew: true, }, "tenant_id": { Type: schema.TypeString, Computed: true, - ForceNew: true, }, }, }, diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go index 99748a298e74..656ed190cf0b 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go @@ -94,9 +94,9 @@ resource "azurerm_machine_learning_compute_cluster" "test" { subnet_resource_id = azurerm_subnet.test.id scale_settings { - min_node_count = 0 - max_node_count = 1 - node_idle_time_before_scale_down = "PT30S" # 30 seconds + min_node_count = 0 + max_node_count = 1 + node_idle_time_before_scale_down = "PT30S" # 30 seconds } identity { @@ -112,21 +112,21 @@ func (r ComputeClusterResource) requiresImport(data acceptance.TestData) string %s resource "azurerm_machine_learning_compute_cluster" "import" { - name = azurerm_machine_learning_compute_cluster.test.name - location = azurerm_machine_learning_compute_cluster.test.location - vm_priority = azurerm_machine_learning_compute_cluster.test.vm_priority - vm_size = azurerm_machine_learning_compute_cluster.test.vm_size - machine_learning_workspace_id = azurerm_machine_learning_compute_cluster.test.machine_learning_workspace_id - - scale_settings { - min_node_count = 0 - max_node_count = 1 - node_idle_time_before_scale_down = "PT2M" # 120 seconds - } - - identity { - type = "SystemAssigned" - } + name = azurerm_machine_learning_compute_cluster.test.name + location = azurerm_machine_learning_compute_cluster.test.location + vm_priority = azurerm_machine_learning_compute_cluster.test.vm_priority + vm_size = azurerm_machine_learning_compute_cluster.test.vm_size + machine_learning_workspace_id = azurerm_machine_learning_compute_cluster.test.machine_learning_workspace_id + + scale_settings { + min_node_count = 0 + max_node_count = 1 + node_idle_time_before_scale_down = "PT2M" # 120 seconds + } + + identity { + type = "SystemAssigned" + } } `, template) @@ -135,70 +135,70 @@ resource "azurerm_machine_learning_compute_cluster" "import" { func (r ComputeClusterResource) template(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { - features {} - } - + features {} +} + data "azurerm_client_config" "current" {} - + resource "azurerm_resource_group" "test" { - name = "acctestRG-ml-%[1]d" - location = "%[2]s" - tags = { - "stage" = "test" - } + name = "acctestRG-ml-%[1]d" + location = "%[2]s" + tags = { + "stage" = "test" + } } - + resource "azurerm_application_insights" "test" { - name = "acctestai-%[1]d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - application_type = "web" + name = "acctestai-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + application_type = "web" } - + resource "azurerm_key_vault" "test" { - name = "acctestvault%[3]d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - tenant_id = data.azurerm_client_config.current.tenant_id - - sku_name = "standard" - - purge_protection_enabled = true -} - + name = "acctestvault%[3]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + tenant_id = data.azurerm_client_config.current.tenant_id + + sku_name = "standard" + + purge_protection_enabled = true +} + resource "azurerm_storage_account" "test" { - name = "acctestsa%[4]d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - account_tier = "Standard" - account_replication_type = "LRS" + name = "acctestsa%[4]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_tier = "Standard" + account_replication_type = "LRS" } - + resource "azurerm_machine_learning_workspace" "test" { - name = "acctest-MLW%[5]d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - application_insights_id = azurerm_application_insights.test.id - key_vault_id = azurerm_key_vault.test.id - storage_account_id = azurerm_storage_account.test.id - - identity { - type = "SystemAssigned" - } + name = "acctest-MLW%[5]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + application_insights_id = azurerm_application_insights.test.id + key_vault_id = azurerm_key_vault.test.id + storage_account_id = azurerm_storage_account.test.id + + identity { + type = "SystemAssigned" + } } - + resource "azurerm_virtual_network" "test" { - name = "acctestvirtnet%[6]d" - address_space = ["10.1.0.0/16"] - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name + name = "acctestvirtnet%[6]d" + address_space = ["10.1.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name } - + resource "azurerm_subnet" "test" { - name = "acctestsubnet%[7]d" - resource_group_name = azurerm_resource_group.test.name - virtual_network_name = azurerm_virtual_network.test.name - address_prefix = "10.1.0.0/24" + name = "acctestsubnet%[7]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefix = "10.1.0.0/24" } `, data.RandomInteger, data.Locations.Primary, data.RandomIntOfLength(12), data.RandomIntOfLength(15), data.RandomIntOfLength(16), From 9d69494a597d1fd365460da38d0b826da1621869 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Mon, 17 May 2021 08:06:22 +0200 Subject: [PATCH 09/18] fix golint: unnecessary conversion for node_idle_time_before_scale_down --- .../machine_learning_compute_cluster_resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index 4dd0aaf22d4a..aa1ca8dfb0fb 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -284,7 +284,7 @@ func expandScaleSettings(input []interface{}) *machinelearningservices.ScaleSett max_node_count := int32(v["max_node_count"].(int)) min_node_count := int32(v["min_node_count"].(int)) - node_idle_time_before_scale_down := string(v["node_idle_time_before_scale_down"].(string)) + node_idle_time_before_scale_down := v["node_idle_time_before_scale_down"].(string) return &machinelearningservices.ScaleSettings{ MaxNodeCount: &max_node_count, From 1f02fa067051d7d22585fe8594beacd084a2bf93 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Tue, 18 May 2021 08:37:37 +0200 Subject: [PATCH 10/18] Address review#2: remove compute type --- .../machinelearning/machine_learning_compute_cluster_resource.go | 1 - 1 file changed, 1 deletion(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index aa1ca8dfb0fb..ec9df42ed396 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -169,7 +169,6 @@ func resourceComputeClusterCreate(d *schema.ResourceData, meta interface{}) erro }, ComputeLocation: utils.String(d.Get("location").(string)), Description: utils.String(d.Get("description").(string)), - ComputeType: "ComputeTypeAmlCompute1", } amlComputeProperties, isAmlCompute := (machinelearningservices.BasicCompute).AsAmlCompute(computeClusterProperties) From 9f0e1b8dffaf8b172ee14da13138a55a4a54ab06 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Thu, 20 May 2021 01:25:34 +0200 Subject: [PATCH 11/18] new resource `azurerm_machine_learning_inference_cluster` (#11550) Fixes #11252 --- .../machine_learning_inference_cluster_resource.go | 7 ++----- azurerm/internal/services/machinelearning/resourceids.go | 2 ++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_inference_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_inference_cluster_resource.go index 20f9302ca67c..afdc3cde0875 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_inference_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_inference_cluster_resource.go @@ -6,18 +6,15 @@ import ( "github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2021-03-01/containerservice" "github.com/Azure/azure-sdk-for-go/services/machinelearningservices/mgmt/2020-04-01/machinelearningservices" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/validation" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/parse" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" diff --git a/azurerm/internal/services/machinelearning/resourceids.go b/azurerm/internal/services/machinelearning/resourceids.go index a32e04ff4f62..a6953c451f73 100644 --- a/azurerm/internal/services/machinelearning/resourceids.go +++ b/azurerm/internal/services/machinelearning/resourceids.go @@ -4,4 +4,6 @@ package machinelearning //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=KubernetesCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.ContainerService/managedClusters/cluster1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Workspace -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ComputeCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=InferenceCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=KubernetesCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.ContainerService/managedClusters/cluster1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Workspace -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1 From 74107d9515c41924f6f4e5aa7ee1c37cea5c07bd Mon Sep 17 00:00:00 2001 From: Vladimir Lazarenko Date: Wed, 26 May 2021 11:30:53 +0200 Subject: [PATCH 12/18] Rebase --- ...chine_learning_compute_cluster_resource.go | 63 +++++++++---------- ..._learning_compute_cluster_resource_test.go | 13 ++-- ...ine_learning_inference_cluster_resource.go | 5 +- 3 files changed, 39 insertions(+), 42 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index ec9df42ed396..c2b6a18d1a71 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -6,9 +6,9 @@ import ( "github.com/Azure/azure-sdk-for-go/services/machinelearningservices/mgmt/2020-04-01/machinelearningservices" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" @@ -16,37 +16,36 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/parse" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" - azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -func resourceComputeCluster() *schema.Resource { - return &schema.Resource{ +func resourceComputeCluster() *pluginsdk.Resource { + return &pluginsdk.Resource{ Create: resourceComputeClusterCreate, Read: resourceComputeClusterRead, Delete: resourceComputeClusterDelete, - Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { _, err := parse.ComputeClusterID(id) return err }), - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(30 * time.Minute), - Read: schema.DefaultTimeout(5 * time.Minute), - Delete: schema.DefaultTimeout(30 * time.Minute), + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), }, - Schema: map[string]*schema.Schema{ + Schema: map[string]*pluginsdk.Schema{ "name": { - Type: schema.TypeString, + Type: pluginsdk.TypeString, Required: true, ForceNew: true, }, "machine_learning_workspace_id": { - Type: schema.TypeString, + Type: pluginsdk.TypeString, Required: true, ForceNew: true, }, @@ -54,26 +53,26 @@ func resourceComputeCluster() *schema.Resource { "location": azure.SchemaLocation(), "vm_size": { - Type: schema.TypeString, + Type: pluginsdk.TypeString, Required: true, ForceNew: true, }, "vm_priority": { - Type: schema.TypeString, + Type: pluginsdk.TypeString, Required: true, ForceNew: true, }, "identity": { - Type: schema.TypeList, + Type: pluginsdk.TypeList, Required: true, ForceNew: true, MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ "type": { - Type: schema.TypeString, + Type: pluginsdk.TypeString, Required: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ @@ -81,11 +80,11 @@ func resourceComputeCluster() *schema.Resource { }, false), }, "principal_id": { - Type: schema.TypeString, + Type: pluginsdk.TypeString, Computed: true, }, "tenant_id": { - Type: schema.TypeString, + Type: pluginsdk.TypeString, Computed: true, }, }, @@ -93,24 +92,24 @@ func resourceComputeCluster() *schema.Resource { }, "scale_settings": { - Type: schema.TypeList, + Type: pluginsdk.TypeList, Required: true, ForceNew: true, MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ "max_node_count": { - Type: schema.TypeInt, + Type: pluginsdk.TypeInt, Required: true, ForceNew: true, }, "min_node_count": { - Type: schema.TypeInt, + Type: pluginsdk.TypeInt, Required: true, ForceNew: true, }, "node_idle_time_before_scale_down": { - Type: schema.TypeString, + Type: pluginsdk.TypeString, Required: true, ForceNew: true, }, @@ -119,13 +118,13 @@ func resourceComputeCluster() *schema.Resource { }, "description": { - Type: schema.TypeString, + Type: pluginsdk.TypeString, Optional: true, ForceNew: true, }, "subnet_resource_id": { - Type: schema.TypeString, + Type: pluginsdk.TypeString, Optional: true, ForceNew: true, }, @@ -135,7 +134,7 @@ func resourceComputeCluster() *schema.Resource { } } -func resourceComputeClusterCreate(d *schema.ResourceData, meta interface{}) error { +func resourceComputeClusterCreate(d *pluginsdk.ResourceData, meta interface{}) error { mlWorkspacesClient := meta.(*clients.Client).MachineLearning.WorkspacesClient mlComputeClient := meta.(*clients.Client).MachineLearning.MachineLearningComputeClient ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) @@ -207,7 +206,7 @@ func resourceComputeClusterCreate(d *schema.ResourceData, meta interface{}) erro return resourceComputeClusterRead(d, meta) } -func resourceComputeClusterRead(d *schema.ResourceData, meta interface{}) error { +func resourceComputeClusterRead(d *pluginsdk.ResourceData, meta interface{}) error { mlComputeClient := meta.(*clients.Client).MachineLearning.MachineLearningComputeClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() @@ -256,7 +255,7 @@ func resourceComputeClusterRead(d *schema.ResourceData, meta interface{}) error return tags.FlattenAndSet(d, computeResource.Tags) } -func resourceComputeClusterDelete(d *schema.ResourceData, meta interface{}) error { +func resourceComputeClusterDelete(d *pluginsdk.ResourceData, meta interface{}) error { mlComputeClient := meta.(*clients.Client).MachineLearning.MachineLearningComputeClient ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go index 656ed190cf0b..bedcca50c992 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go @@ -5,12 +5,11 @@ import ( "fmt" "testing" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -20,10 +19,10 @@ func TestAccComputeCluster_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_machine_learning_compute_cluster", "test") r := ComputeClusterResource{} - data.ResourceTest(t, r, []resource.TestStep{ + data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), - Check: resource.ComposeTestCheckFunc( + Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("identity.#").HasValue("1"), check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), @@ -43,10 +42,10 @@ func TestAccComputeCluster_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_machine_learning_compute_cluster", "test") r := ComputeClusterResource{} - data.ResourceTest(t, r, []resource.TestStep{ + data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), - Check: resource.ComposeTestCheckFunc( + Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("identity.#").HasValue("1"), check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), @@ -62,7 +61,7 @@ func TestAccComputeCluster_requiresImport(t *testing.T) { }) } -func (r ComputeClusterResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { +func (r ComputeClusterResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { computeClusterClient := client.MachineLearning.MachineLearningComputeClient id, err := parse.ComputeClusterID(state.ID) diff --git a/azurerm/internal/services/machinelearning/machine_learning_inference_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_inference_cluster_resource.go index afdc3cde0875..21483890c0a2 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_inference_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_inference_cluster_resource.go @@ -9,13 +9,12 @@ import ( "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/internal/clients" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/validation" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/parse" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/suppress" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) From 51d5ca1aeb4e30c216acc7529e78e1dd5fae0269 Mon Sep 17 00:00:00 2001 From: Vladimir Lazarenko Date: Wed, 26 May 2021 13:30:28 +0200 Subject: [PATCH 13/18] Rebase --- .../machine_learning_compute_cluster_resource.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index c2b6a18d1a71..4d4b8b48df05 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -5,17 +5,13 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/services/machinelearningservices/mgmt/2020-04-01/machinelearningservices" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/validation" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/parse" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" - + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) From ce605a4c31064b0a5ebbc50181ccbaa4eba4d8d3 Mon Sep 17 00:00:00 2001 From: Vladimir Lazarenko Date: Wed, 26 May 2021 13:55:03 +0200 Subject: [PATCH 14/18] Remove extra generators --- azurerm/internal/services/machinelearning/resourceids.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/azurerm/internal/services/machinelearning/resourceids.go b/azurerm/internal/services/machinelearning/resourceids.go index a6953c451f73..05f1cce453cb 100644 --- a/azurerm/internal/services/machinelearning/resourceids.go +++ b/azurerm/internal/services/machinelearning/resourceids.go @@ -1,8 +1,5 @@ package machinelearning -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=InferenceCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=KubernetesCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.ContainerService/managedClusters/cluster1 -//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Workspace -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ComputeCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=InferenceCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.MachineLearningServices/workspaces/workspace1/computes/cluster1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=KubernetesCluster -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.ContainerService/managedClusters/cluster1 From 3c3a28739c1970853016dca5e98cbedfe43da2ac Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Thu, 10 Jun 2021 22:38:59 +0200 Subject: [PATCH 15/18] address review --- .../machine_learning_compute_cluster_resource.go | 1 + .../machine_learning_compute_cluster.html.markdown | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index 4d4b8b48df05..a62ca4aad96e 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -58,6 +58,7 @@ func resourceComputeCluster() *pluginsdk.Resource { Type: pluginsdk.TypeString, Required: true, ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{string(machinelearningservices.Dedicated), string(machinelearningservices.LowPriority)}, false), }, "identity": { diff --git a/website/docs/r/machine_learning_compute_cluster.html.markdown b/website/docs/r/machine_learning_compute_cluster.html.markdown index dfa11c311b93..910e739eb696 100644 --- a/website/docs/r/machine_learning_compute_cluster.html.markdown +++ b/website/docs/r/machine_learning_compute_cluster.html.markdown @@ -100,21 +100,20 @@ resource "azurerm_machine_learning_compute_cluster" "test" { ## Arguments Reference The following arguments are supported: - -* `identity` - (Required) A `identity` block as defined below. Changing this forces a new Machine Learning Compute Cluster to be created. - -* `location` - (Required) The Azure Region where the Machine Learning Compute Cluster should exist. Changing this forces a new Machine Learning Compute Cluster to be created. +* `name` - (Required) The name which should be used for this Machine Learning Compute Cluster. Changing this forces a new Machine Learning Compute Cluster to be created. * `machine_learning_workspace_id` - (Required) The ID of the Machine Learning Workspace. Changing this forces a new Machine Learning Compute Cluster to be created. -* `name` - (Required) The name which should be used for this Machine Learning Compute Cluster. Changing this forces a new Machine Learning Compute Cluster to be created. - -* `scale_settings` - (Required) A `scale_settings` block as defined below. Changing this forces a new Machine Learning Compute Cluster to be created. +* `location` - (Required) The Azure Region where the Machine Learning Compute Cluster should exist. Changing this forces a new Machine Learning Compute Cluster to be created. * `vm_priority` - (Required) The priority of the VM. Changing this forces a new Machine Learning Compute Cluster to be created. * `vm_size` - (Required) The size of the VM. Changing this forces a new Machine Learning Compute Cluster to be created. +* `identity` - (Required) A `identity` block as defined below. Changing this forces a new Machine Learning Compute Cluster to be created. + +* `scale_settings` - (Required) A `scale_settings` block as defined below. Changing this forces a new Machine Learning Compute Cluster to be created. + --- * `description` - (Optional) The description of the Machine Learning compute. Changing this forces a new Machine Learning Compute Cluster to be created. From 2c823ce04166afd912e5b89309bfa4f6f8444441 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Thu, 10 Jun 2021 22:40:51 +0200 Subject: [PATCH 16/18] make fmt --- .../machine_learning_compute_cluster_resource.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index a62ca4aad96e..8bf127fd4599 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -55,9 +55,9 @@ func resourceComputeCluster() *pluginsdk.Resource { }, "vm_priority": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, ValidateFunc: validation.StringInSlice([]string{string(machinelearningservices.Dedicated), string(machinelearningservices.LowPriority)}, false), }, From bf8388933738ed10912615ad444d7a770a1ef2ab Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Fri, 11 Jun 2021 09:03:44 +0200 Subject: [PATCH 17/18] address review: rename node_idle_time_before_scale_down to scale_down_nodes_after_idle_duration --- .../machine_learning_compute_cluster_resource.go | 12 ++++++------ ...machine_learning_compute_cluster_resource_test.go | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go index 8bf127fd4599..c4fca8612b11 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource.go @@ -105,7 +105,7 @@ func resourceComputeCluster() *pluginsdk.Resource { Required: true, ForceNew: true, }, - "node_idle_time_before_scale_down": { + "scale_down_nodes_after_idle_duration": { Type: pluginsdk.TypeString, Required: true, ForceNew: true, @@ -279,12 +279,12 @@ func expandScaleSettings(input []interface{}) *machinelearningservices.ScaleSett max_node_count := int32(v["max_node_count"].(int)) min_node_count := int32(v["min_node_count"].(int)) - node_idle_time_before_scale_down := v["node_idle_time_before_scale_down"].(string) + scale_down_nodes_after_idle_duration := v["scale_down_nodes_after_idle_duration"].(string) return &machinelearningservices.ScaleSettings{ MaxNodeCount: &max_node_count, MinNodeCount: &min_node_count, - NodeIdleTimeBeforeScaleDown: &node_idle_time_before_scale_down, + NodeIdleTimeBeforeScaleDown: &scale_down_nodes_after_idle_duration, } } @@ -295,9 +295,9 @@ func flattenScaleSettings(scaleSettings *machinelearningservices.ScaleSettings) return []interface{}{ map[string]interface{}{ - "max_node_count": scaleSettings.MaxNodeCount, - "min_node_count": scaleSettings.MinNodeCount, - "node_idle_time_before_scale_down": scaleSettings.NodeIdleTimeBeforeScaleDown, + "max_node_count": scaleSettings.MaxNodeCount, + "min_node_count": scaleSettings.MinNodeCount, + "scale_down_nodes_after_idle_duration": scaleSettings.NodeIdleTimeBeforeScaleDown, }, } } diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go index bedcca50c992..bc97bb570827 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go @@ -31,7 +31,7 @@ func TestAccComputeCluster_basic(t *testing.T) { check.That(data.ResourceName).Key("scale_settings.#").HasValue("1"), check.That(data.ResourceName).Key("scale_settings.0.max_node_count").Exists(), check.That(data.ResourceName).Key("scale_settings.0.min_node_count").Exists(), - check.That(data.ResourceName).Key("scale_settings.0.node_idle_time_before_scale_down").Exists(), + check.That(data.ResourceName).Key("scale_settings.0.scale_down_nodes_after_idle_duration").Exists(), ), }, data.ImportStep(), @@ -54,7 +54,7 @@ func TestAccComputeCluster_requiresImport(t *testing.T) { check.That(data.ResourceName).Key("scale_settings.#").HasValue("1"), check.That(data.ResourceName).Key("scale_settings.0.max_node_count").Exists(), check.That(data.ResourceName).Key("scale_settings.0.min_node_count").Exists(), - check.That(data.ResourceName).Key("scale_settings.0.node_idle_time_before_scale_down").Exists(), + check.That(data.ResourceName).Key("scale_settings.0.scale_down_nodes_after_idle_duration").Exists(), ), }, data.RequiresImportErrorStep(r.requiresImport), @@ -95,7 +95,7 @@ resource "azurerm_machine_learning_compute_cluster" "test" { scale_settings { min_node_count = 0 max_node_count = 1 - node_idle_time_before_scale_down = "PT30S" # 30 seconds + scale_down_nodes_after_idle_duration = "PT30S" # 30 seconds } identity { @@ -120,7 +120,7 @@ resource "azurerm_machine_learning_compute_cluster" "import" { scale_settings { min_node_count = 0 max_node_count = 1 - node_idle_time_before_scale_down = "PT2M" # 120 seconds + scale_down_nodes_after_idle_duration = "PT2M" # 120 seconds } identity { From bcc528bd4c395cd149b0064c93329cf24defabd6 Mon Sep 17 00:00:00 2001 From: Michael Gross Date: Fri, 11 Jun 2021 09:27:59 +0200 Subject: [PATCH 18/18] terrafmt fmt -f on test file --- .../machine_learning_compute_cluster_resource_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go index bc97bb570827..34167af6b531 100644 --- a/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go +++ b/azurerm/internal/services/machinelearning/machine_learning_compute_cluster_resource_test.go @@ -93,8 +93,8 @@ resource "azurerm_machine_learning_compute_cluster" "test" { subnet_resource_id = azurerm_subnet.test.id scale_settings { - min_node_count = 0 - max_node_count = 1 + min_node_count = 0 + max_node_count = 1 scale_down_nodes_after_idle_duration = "PT30S" # 30 seconds } @@ -118,8 +118,8 @@ resource "azurerm_machine_learning_compute_cluster" "import" { machine_learning_workspace_id = azurerm_machine_learning_compute_cluster.test.machine_learning_workspace_id scale_settings { - min_node_count = 0 - max_node_count = 1 + min_node_count = 0 + max_node_count = 1 scale_down_nodes_after_idle_duration = "PT2M" # 120 seconds }