Skip to content

Commit

Permalink
azurerm_machine_learning_workspace - Add support for `serverless_co…
Browse files Browse the repository at this point in the history
…mpute` (#25660)

* add support for serverlesscompute

* add support for serverlesscompute

* fix test case

* fix test case

* fix test case

* fix test case

* change default value

* 1. rename custom_subnet_id to custom_subnet_id
2. rename no_public_ip_enabled to public_ip_enabled
3. add test case for serverless compute
4. update document

* formant
  • Loading branch information
xuzhang3 authored May 29, 2024
1 parent 9b893ce commit 0263f62
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,26 @@ func resourceMachineLearningWorkspace() *pluginsdk.Resource {
Default: false,
},

"serverless_compute": {
Type: pluginsdk.TypeList,
Optional: true,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"subnet_id": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: commonids.ValidateSubnetID,
},
"public_ip_enabled": {
Type: pluginsdk.TypeBool,
Optional: true,
Default: false,
},
},
},
},

"discovery_url": {
Type: pluginsdk.TypeString,
Computed: true,
Expand Down Expand Up @@ -315,6 +335,22 @@ func resourceMachineLearningWorkspaceCreateOrUpdate(d *pluginsdk.ResourceData, m
},
}

serverlessCompute := expandMachineLearningWorkspaceServerlessCompute(d.Get("serverless_compute").([]interface{}))
if serverlessCompute != nil {
if *serverlessCompute.ServerlessComputeNoPublicIP && serverlessCompute.ServerlessComputeCustomSubnet == nil && !networkAccessBehindVnetEnabled {
return fmt.Errorf("`public_ip_enabled` must be set to `true` if `subnet_id` is not set and `public_network_access_enabled` is `false`")
}

if serverlessCompute.ServerlessComputeCustomSubnet == nil {
oldVal, newVal := d.GetChange("serverless_compute.0.public_ip_enabled")
if oldVal.(bool) && !newVal.(bool) {
return fmt.Errorf(" Not supported to update `public_ip_enabled` from `true` to `false` when `subnet_id` is null or empty")
}
}
}

workspace.Properties.ServerlessComputeSettings = serverlessCompute

if networkAccessBehindVnetEnabled {
workspace.Properties.PublicNetworkAccess = pointer.To(workspaces.PublicNetworkAccessEnabled)
}
Expand Down Expand Up @@ -422,6 +458,7 @@ func resourceMachineLearningWorkspaceRead(d *pluginsdk.ResourceData, meta interf
d.Set("v1_legacy_mode_enabled", props.V1LegacyMode)
d.Set("workspace_id", props.WorkspaceId)
d.Set("managed_network", flattenMachineLearningWorkspaceManagedNetwork(props.ManagedNetwork))
d.Set("serverless_compute", flattenMachineLearningWorkspaceServerlessCompute(props.ServerlessComputeSettings))

kvId, err := commonids.ParseKeyVaultIDInsensitively(*props.KeyVault)
if err != nil {
Expand Down Expand Up @@ -678,3 +715,39 @@ func flattenMachineLearningWorkspaceManagedNetwork(i *workspaces.ManagedNetworkS

return &[]interface{}{out}
}

func expandMachineLearningWorkspaceServerlessCompute(i []interface{}) *workspaces.ServerlessComputeSettings {
if len(i) == 0 || i[0] == nil {
return nil
}

v := i[0].(map[string]interface{})

serverlessCompute := workspaces.ServerlessComputeSettings{
ServerlessComputeNoPublicIP: pointer.To(!v["public_ip_enabled"].(bool)),
}

if subnetId, ok := v["subnet_id"].(string); ok && subnetId != "" {
serverlessCompute.ServerlessComputeCustomSubnet = pointer.To(subnetId)
}

return &serverlessCompute
}

func flattenMachineLearningWorkspaceServerlessCompute(i *workspaces.ServerlessComputeSettings) *[]interface{} {
if i == nil {
return &[]interface{}{}
}

out := map[string]interface{}{}

if i.ServerlessComputeCustomSubnet != nil {
out["subnet_id"] = *i.ServerlessComputeCustomSubnet
}

if i.ServerlessComputeNoPublicIP != nil {
out["public_ip_enabled"] = !*i.ServerlessComputeNoPublicIP
}

return &[]interface{}{out}
}
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,41 @@ func TestAccMachineLearningWorkspace_purgeSoftDelete(t *testing.T) {
})
}

func TestAccMachineLearningWorkspace_serverlessCompute(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_machine_learning_workspace", "test")
r := WorkspaceResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.serverlessCompute(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("serverless_compute.#").HasValue("1"),
check.That(data.ResourceName).Key("serverless_compute.0.subnet_id").Exists(),
check.That(data.ResourceName).Key("serverless_compute.0.public_ip_enabled").HasValue("false"),
),
},
data.ImportStep(),
})
}

func TestAccMachineLearningWorkspace_serverlessCompute_withoutSubnet(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_machine_learning_workspace", "test")
r := WorkspaceResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.serverlessComputeWithoutSubnet(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("serverless_compute.#").HasValue("1"),
check.That(data.ResourceName).Key("serverless_compute.0.public_ip_enabled").HasValue("true"),
),
},
data.ImportStep(),
})
}

func (r WorkspaceResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
workspacesClient := client.MachineLearning.Workspaces
id, err := workspaces.ParseWorkspaceID(state.ID)
Expand Down Expand Up @@ -1166,3 +1201,76 @@ resource "azurerm_machine_learning_workspace" "test" {
}
`, template, data.RandomInteger)
}

func (r WorkspaceResource) serverlessCompute(data acceptance.TestData) string {
template := r.template(data)
return fmt.Sprintf(`
%s
resource "azurerm_virtual_network" "test" {
name = "acctestvirtnet%[2]d"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
}
resource "azurerm_subnet" "test" {
name = "internal"
resource_group_name = azurerm_resource_group.test.name
virtual_network_name = azurerm_virtual_network.test.name
address_prefixes = ["10.0.2.0/24"]
}
resource "azurerm_machine_learning_workspace" "test" {
name = "acctest-MLW-%[2]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
serverless_compute {
subnet_id = azurerm_subnet.test.id
}
identity {
type = "SystemAssigned"
}
}
`, template, data.RandomInteger)
}

func (r WorkspaceResource) serverlessComputeWithoutSubnet(data acceptance.TestData) string {
template := r.template(data)
return fmt.Sprintf(`
%s
resource "azurerm_virtual_network" "test" {
name = "acctestvirtnet%[2]d"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
}
resource "azurerm_subnet" "test" {
name = "internal"
resource_group_name = azurerm_resource_group.test.name
virtual_network_name = azurerm_virtual_network.test.name
address_prefixes = ["10.0.2.0/24"]
}
resource "azurerm_machine_learning_workspace" "test" {
name = "acctest-MLW-%[2]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
serverless_compute {
public_ip_enabled = true
}
identity {
type = "SystemAssigned"
}
}
`, template, data.RandomInteger)
}
12 changes: 12 additions & 0 deletions website/docs/r/machine_learning_workspace.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ The following arguments are supported:

* `sku_name` - (Optional) SKU/edition of the Machine Learning Workspace, possible values are `Free`, `Basic`, `Standard` and `Premium`. Defaults to `Basic`.

* `serverless_compute` - (Optional) A `serverless_compute` block as defined below.

* `tags` - (Optional) A mapping of tags to assign to the resource.

---
Expand Down Expand Up @@ -422,6 +424,16 @@ An `managed_network` block supports the following:

---

A `serverless_compute` block supports the following:

* `subnet_id` - (Optional) The ID of an existing Virtual Network Subnet in which the serverless compute nodes should be deployed to.

* `public_ip_enabled` - (Optional) Should serverless compute nodes deployed in a custom Virtual Network have public IP addresses enabled for a workspace with private endpoint? Defaults to `false`.

~> **Note:** `public_ip_enabled` cannot be updated from `true` to `false` when `subnet_id` is not set. `public_ip_enabled` must be set to `true` if `subnet_id` is not set and when `public_network_access_enabled` is `false`.

---

An `feature_store` block supports the following:

* `computer_spark_runtime_version` - (Optional) The version of Spark runtime.
Expand Down

0 comments on commit 0263f62

Please sign in to comment.