diff --git a/docs/resources/cluster_aks.md b/docs/resources/cluster_aks.md index 5ff545625..a1334479e 100644 --- a/docs/resources/cluster_aks.md +++ b/docs/resources/cluster_aks.md @@ -321,12 +321,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_aws.md b/docs/resources/cluster_aws.md index ba45b5fa9..9deb7d215 100644 --- a/docs/resources/cluster_aws.md +++ b/docs/resources/cluster_aws.md @@ -349,12 +349,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_azure.md b/docs/resources/cluster_azure.md index f01e640e7..bc1f47b7e 100644 --- a/docs/resources/cluster_azure.md +++ b/docs/resources/cluster_azure.md @@ -382,12 +382,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_custom_cloud.md b/docs/resources/cluster_custom_cloud.md index 00ead17f0..b614212dd 100644 --- a/docs/resources/cluster_custom_cloud.md +++ b/docs/resources/cluster_custom_cloud.md @@ -348,12 +348,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_edge_native.md b/docs/resources/cluster_edge_native.md index e32f50f6f..b45200faf 100644 --- a/docs/resources/cluster_edge_native.md +++ b/docs/resources/cluster_edge_native.md @@ -310,12 +310,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_edge_vsphere.md b/docs/resources/cluster_edge_vsphere.md index d600ab171..c157939f7 100644 --- a/docs/resources/cluster_edge_vsphere.md +++ b/docs/resources/cluster_edge_vsphere.md @@ -265,12 +265,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_eks.md b/docs/resources/cluster_eks.md index 8cdcf56a1..ffa22c7f6 100644 --- a/docs/resources/cluster_eks.md +++ b/docs/resources/cluster_eks.md @@ -358,12 +358,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_gcp.md b/docs/resources/cluster_gcp.md index d45eee278..4321c03ae 100644 --- a/docs/resources/cluster_gcp.md +++ b/docs/resources/cluster_gcp.md @@ -300,12 +300,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_gke.md b/docs/resources/cluster_gke.md index 738216f2f..97f28cda4 100644 --- a/docs/resources/cluster_gke.md +++ b/docs/resources/cluster_gke.md @@ -263,12 +263,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_maas.md b/docs/resources/cluster_maas.md index 577768c34..ec2d5dec6 100644 --- a/docs/resources/cluster_maas.md +++ b/docs/resources/cluster_maas.md @@ -358,12 +358,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_openstack.md b/docs/resources/cluster_openstack.md index 8cd2ec28b..75a006fd4 100644 --- a/docs/resources/cluster_openstack.md +++ b/docs/resources/cluster_openstack.md @@ -303,12 +303,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/cluster_vsphere.md b/docs/resources/cluster_vsphere.md index 567898281..9d8fcbf6d 100644 --- a/docs/resources/cluster_vsphere.md +++ b/docs/resources/cluster_vsphere.md @@ -355,12 +355,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `scan_policy` diff --git a/docs/resources/virtual_cluster.md b/docs/resources/virtual_cluster.md index 0726d01df..54f9fc0a6 100644 --- a/docs/resources/virtual_cluster.md +++ b/docs/resources/virtual_cluster.md @@ -194,12 +194,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `resources` diff --git a/docs/resources/workspace.md b/docs/resources/workspace.md index a8db000a6..35519309e 100644 --- a/docs/resources/workspace.md +++ b/docs/resources/workspace.md @@ -126,6 +126,10 @@ Required: - `uid` (String) +Read-Only: + +- `cluster_name` (String) + ### Nested Schema for `backup_policy` @@ -180,12 +184,22 @@ Optional: Required: - `name` (String) Name of the namespace. This is the name of the Kubernetes namespace in the cluster. -- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}` +- `resource_allocation` (Map of String) Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}` Optional: +- `cluster_resource_allocations` (Block List) (see [below for nested schema](#nestedblock--namespaces--cluster_resource_allocations)) - `images_blacklist` (List of String) List of images to disallow for the namespace. For example, `['nginx:latest', 'redis:latest']` + +### Nested Schema for `namespaces.cluster_resource_allocations` + +Required: + +- `resource_allocation` (Map of String) Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration. +- `uid` (String) + + ### Nested Schema for `workspace_quota` @@ -193,4 +207,5 @@ Optional: Optional: - `cpu` (Number) CPU that the entire workspace is allowed to consume. The default value is 0, which imposes no limit. +- `gpu` (Number) GPU that the entire workspace is allowed to consume. The default value is 0, which imposes no limit. - `memory` (Number) Memory in Mib that the entire workspace is allowed to consume. The default value is 0, which imposes no limit. \ No newline at end of file diff --git a/examples/resources/spectrocloud_workspace/resource.tf b/examples/resources/spectrocloud_workspace/resource.tf index 96301bec0..d5b8f4b23 100644 --- a/examples/resources/spectrocloud_workspace/resource.tf +++ b/examples/resources/spectrocloud_workspace/resource.tf @@ -1,16 +1,20 @@ +# Example demonstrating workspace with GPU support, cluster-specific resource allocations, and cluster names + data "spectrocloud_cluster" "cluster1" { - name = "tf-si-cluster" + name = "api-aks-cazfl" } resource "spectrocloud_workspace" "workspace" { name = "wsp-tf-123" description = "test123" workspace_quota { - cpu = 5 - memory = 4064 + cpu = 16 + memory = 32768 + gpu = 4 } clusters { uid = data.spectrocloud_cluster.cluster1.id + # cluster_name is computed automatically by fetching cluster details from the API } cluster_rbac_binding { @@ -35,53 +39,43 @@ resource "spectrocloud_workspace" "workspace" { } } - cluster_rbac_binding { - type = "RoleBinding" - namespace = "test5ns" - role = { - kind = "Role" - name = "testrolefromns3" - } - subjects { - type = "User" - name = "testUserRoleFromNS3" - } - subjects { - type = "Group" - name = "testGroupFromNS3" - } - subjects { - type = "ServiceAccount" - name = "testrolesubject3" - namespace = "testrolenamespace" - } - } - namespaces { - name = "test5ns" + name = "multi-cluster-ns" resource_allocation = { - cpu_cores = "2" - memory_MiB = "2048" + cpu_cores = "8" + memory_MiB = "8192" + gpu_limit = "2" + gpu_provider = "nvidia" + } + + # Cluster-specific resource allocations + cluster_resource_allocations { + uid = data.spectrocloud_cluster.cluster1.id + resource_allocation = { + cpu_cores = "4" + memory_MiB = "4096" + gpu_limit = "1" + } } - images_blacklist = ["1", "2", "3"] + images_blacklist = ["nginx:latest", "redis:latest"] } - backup_policy { - schedule = "0 0 * * SUN" - backup_location_id = data.spectrocloud_backup_storage_location.bsl.id - prefix = "prod-backup" - expiry_in_hour = 7200 - include_disks = false - include_cluster_resources = true + # backup_policy { + # schedule = "0 0 * * SUN" + # backup_location_id = data.spectrocloud_backup_storage_location.bsl.id + # prefix = "prod-backup" + # expiry_in_hour = 7200 + # include_disks = false + # include_cluster_resources = true - namespaces = ["test5ns"] - include_all_clusters = true - cluster_uids = [data.spectrocloud_cluster.cluster1.id] - } + # namespaces = ["test5ns", "multi-cluster-ns"] + # include_all_clusters = true + # cluster_uids = [data.spectrocloud_cluster.cluster1.id] + # } } -data "spectrocloud_backup_storage_location" "bsl" { - name = "test-aws-s3" -} +# data "spectrocloud_backup_storage_location" "bsl" { +# name = "test-aws-s3" +# } diff --git a/spectrocloud/resource_workspace.go b/spectrocloud/resource_workspace.go index 8c6513540..8abebe2e6 100644 --- a/spectrocloud/resource_workspace.go +++ b/spectrocloud/resource_workspace.go @@ -3,11 +3,13 @@ package spectrocloud import ( "context" "errors" + "fmt" + "strings" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/spectrocloud/palette-sdk-go/api/models" "github.com/spectrocloud/palette-sdk-go/client" - "strings" "github.com/spectrocloud/terraform-provider-spectrocloud/spectrocloud/schemas" ) @@ -20,6 +22,9 @@ func resourceWorkspace() *schema.Resource { DeleteContext: resourceWorkspaceDelete, SchemaVersion: 2, + Importer: &schema.ResourceImporter{ + StateContext: resourceWorkspaceImport, + }, Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, @@ -54,6 +59,11 @@ func resourceWorkspace() *schema.Resource { Optional: true, Description: "Memory in Mib that the entire workspace is allowed to consume. The default value is 0, which imposes no limit.", }, + "gpu": { + Type: schema.TypeInt, + Optional: true, + Description: "GPU that the entire workspace is allowed to consume. The default value is 0, which imposes no limit.", + }, }, }, }, @@ -67,6 +77,10 @@ func resourceWorkspace() *schema.Resource { Type: schema.TypeString, Required: true, }, + "cluster_name": { + Type: schema.TypeString, + Computed: true, + }, }, }, }, @@ -82,7 +96,7 @@ func resourceWorkspaceCreate(ctx context.Context, d *schema.ResourceData, m inte var diags diag.Diagnostics - workspace := toWorkspace(d) + workspace := toWorkspace(d, c) uid, err := c.CreateWorkspace(workspace) if err != nil { @@ -115,7 +129,7 @@ func resourceWorkspaceRead(_ context.Context, d *schema.ResourceData, m interfac if err := d.Set("workspace_quota", wsQuota); err != nil { return diag.FromErr(err) } - fp := flattenWorkspaceClusters(workspace) + fp := flattenWorkspaceClusters(workspace, c) if err := d.Set("clusters", fp); err != nil { return diag.FromErr(err) } @@ -144,10 +158,19 @@ func resourceWorkspaceRead(_ context.Context, d *schema.ResourceData, m interfac func flattenWorkspaceQuota(workspace *models.V1Workspace) []interface{} { wsq := make([]interface{}, 0) if workspace.Spec.Quota.ResourceAllocation != nil { - wsq = append(wsq, map[string]interface{}{ + quota := map[string]interface{}{ "cpu": workspace.Spec.Quota.ResourceAllocation.CPUCores, "memory": workspace.Spec.Quota.ResourceAllocation.MemoryMiB, - }) + } + + // Handle GPU configuration if present + if workspace.Spec.Quota.ResourceAllocation.GpuConfig != nil { + quota["gpu"] = int(workspace.Spec.Quota.ResourceAllocation.GpuConfig.Limit) + } else { + quota["gpu"] = 0 + } + + wsq = append(wsq, quota) } return wsq } @@ -180,7 +203,7 @@ func resourceWorkspaceUpdate(ctx context.Context, d *schema.ResourceData, m inte if d.HasChange("clusters") || d.HasChange("workspace_quota") { // resource allocation should go first because clusters are inside. - namespaces := toUpdateWorkspaceNamespaces(d) + namespaces := toUpdateWorkspaceNamespaces(d, c) if err := c.UpdateWorkspaceResourceAllocation(d.Id(), namespaces); err != nil { return diag.FromErr(err) } @@ -196,7 +219,7 @@ func resourceWorkspaceUpdate(ctx context.Context, d *schema.ResourceData, m inte } } if d.HasChange("namespaces") { - if err := c.UpdateWorkspaceResourceAllocation(d.Id(), toUpdateWorkspaceNamespaces(d)); err != nil { + if err := c.UpdateWorkspaceResourceAllocation(d.Id(), toUpdateWorkspaceNamespaces(d, c)); err != nil { return diag.FromErr(err) } } @@ -246,7 +269,7 @@ func resourceWorkspaceDelete(ctx context.Context, d *schema.ResourceData, m inte return diags } -func toWorkspace(d *schema.ResourceData) *models.V1WorkspaceEntity { +func toWorkspace(d *schema.ResourceData, c *client.V1Client) *models.V1WorkspaceEntity { annotations := make(map[string]string) if len(d.Get("description").(string)) > 0 { annotations["description"] = d.Get("description").(string) @@ -262,7 +285,7 @@ func toWorkspace(d *schema.ResourceData) *models.V1WorkspaceEntity { Spec: &models.V1WorkspaceSpec{ ClusterNamespaces: toWorkspaceNamespaces(d), ClusterRbacs: toWorkspaceRBACs(d), - ClusterRefs: toClusterRefs(d), + ClusterRefs: toClusterRefs(d, c), Policies: toWorkspacePolicies(d), Quota: toQuota(d), }, @@ -270,3 +293,32 @@ func toWorkspace(d *schema.ResourceData) *models.V1WorkspaceEntity { return workspace } + +func resourceWorkspaceImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + c := getV1ClientWithResourceContext(m, "") + + // The import ID should be the workspace UID + workspaceUID := d.Id() + + // Validate that the workspace exists and we can access it + workspace, err := c.GetWorkspace(workspaceUID) + if err != nil { + return nil, fmt.Errorf("could not retrieve workspace for import: %s", err) + } + if workspace == nil { + return nil, fmt.Errorf("workspace with ID %s not found", workspaceUID) + } + + // Set the workspace name from the retrieved workspace + if err := d.Set("name", workspace.Metadata.Name); err != nil { + return nil, err + } + + // Read all workspace data to populate the state + diags := resourceWorkspaceRead(ctx, d, m) + if diags.HasError() { + return nil, fmt.Errorf("could not read workspace for import: %v", diags) + } + + return []*schema.ResourceData{d}, nil +} diff --git a/spectrocloud/resource_workspace_test.go b/spectrocloud/resource_workspace_test.go index db3449a30..025522dd7 100644 --- a/spectrocloud/resource_workspace_test.go +++ b/spectrocloud/resource_workspace_test.go @@ -91,7 +91,7 @@ func TestToWorkspace(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Initialize resource data with input d := schema.TestResourceDataRaw(t, resourceWorkspace().Schema, tt.input) - result := toWorkspace(d) + result := toWorkspace(d, nil) // nil client for unit test // Compare the expected and actual result assert.Equal(t, tt.expected.Metadata.Name, result.Metadata.Name) diff --git a/spectrocloud/schemas/cluster_namespaces.go b/spectrocloud/schemas/cluster_namespaces.go index 19ac31893..f03fb8a66 100644 --- a/spectrocloud/schemas/cluster_namespaces.go +++ b/spectrocloud/schemas/cluster_namespaces.go @@ -20,7 +20,27 @@ func ClusterNamespacesSchema() *schema.Schema { Elem: &schema.Schema{ Type: schema.TypeString, }, - Description: "Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048'}`", + Description: "Resource allocation for the namespace. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1', gpu_provider: 'nvidia'}`", + }, + "cluster_resource_allocations": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uid": { + Type: schema.TypeString, + Required: true, + }, + "resource_allocation": { + Type: schema.TypeMap, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "Resource allocation for the cluster. This is a map containing the resource type and the resource value. For example, `{cpu_cores: '2', memory_MiB: '2048', gpu_limit: '1'}`. Note: gpu_provider is not supported here; use the default resource_allocation for GPU provider configuration.", + }, + }, + }, }, "images_blacklist": { Type: schema.TypeList, diff --git a/spectrocloud/workspace_cluster.go b/spectrocloud/workspace_cluster.go index 9a6df1ed5..36c3ea8b8 100644 --- a/spectrocloud/workspace_cluster.go +++ b/spectrocloud/workspace_cluster.go @@ -3,9 +3,10 @@ package spectrocloud import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/spectrocloud/palette-sdk-go/api/models" + "github.com/spectrocloud/palette-sdk-go/client" ) -func toClusterRefs(d *schema.ResourceData) []*models.V1WorkspaceClusterRef { +func toClusterRefs(d *schema.ResourceData, c *client.V1Client) []*models.V1WorkspaceClusterRef { clusterRefs := make([]*models.V1WorkspaceClusterRef, 0) clusters := d.Get("clusters") @@ -15,8 +16,23 @@ func toClusterRefs(d *schema.ResourceData) []*models.V1WorkspaceClusterRef { for _, cluster := range clusters.(*schema.Set).List() { clusterValue := cluster.(map[string]interface{}) uid := clusterValue["uid"].(string) + + // Try to get cluster name from the data first (if available from computed field) + clusterName := "" + if nameValue, exists := clusterValue["cluster_name"]; exists && nameValue != nil { + clusterName = nameValue.(string) + } + + // If cluster name is not available in data and client is provided, fetch it from API + if clusterName == "" && c != nil { + if clusterDetails, err := c.GetCluster(uid); err == nil && clusterDetails != nil { + clusterName = clusterDetails.Metadata.Name + } + } + clusterRefs = append(clusterRefs, &models.V1WorkspaceClusterRef{ - ClusterUID: uid, + ClusterUID: uid, + ClusterName: clusterName, }) } diff --git a/spectrocloud/workspace_common.go b/spectrocloud/workspace_common.go index 4ab437625..c0a4142f4 100644 --- a/spectrocloud/workspace_common.go +++ b/spectrocloud/workspace_common.go @@ -3,9 +3,10 @@ package spectrocloud import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/spectrocloud/palette-sdk-go/api/models" + "github.com/spectrocloud/palette-sdk-go/client" ) -func flattenWorkspaceClusters(workspace *models.V1Workspace) []interface{} { +func flattenWorkspaceClusters(workspace *models.V1Workspace, c *client.V1Client) []interface{} { clusters := workspace.Spec.ClusterRefs if len(clusters) > 0 { @@ -16,6 +17,19 @@ func flattenWorkspaceClusters(workspace *models.V1Workspace) []interface{} { wsp_cluster["uid"] = cluster.ClusterUID + // Fetch cluster name using the API (if client is available) + if c != nil { + if clusterDetails, err := c.GetCluster(cluster.ClusterUID); err == nil && clusterDetails != nil { + wsp_cluster["cluster_name"] = clusterDetails.Metadata.Name + } else { + // If we can't fetch the cluster name, set it to empty string + wsp_cluster["cluster_name"] = "" + } + } else { + // For tests or when client is not available + wsp_cluster["cluster_name"] = "" + } + wsp_clusters = append(wsp_clusters, wsp_cluster) } diff --git a/spectrocloud/workspace_namespace.go b/spectrocloud/workspace_namespace.go index 9acbaca42..b1db7ad54 100644 --- a/spectrocloud/workspace_namespace.go +++ b/spectrocloud/workspace_namespace.go @@ -1,14 +1,52 @@ package spectrocloud import ( - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/spectrocloud/palette-sdk-go/api/models" "math" "regexp" "strconv" "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/palette-sdk-go/api/models" + "github.com/spectrocloud/palette-sdk-go/client" ) +// Helper function to create V1WorkspaceResourceAllocation from resource allocation map +func toWorkspaceResourceAllocation(resourceAllocation map[string]interface{}) (*models.V1WorkspaceResourceAllocation, error) { + cpu_cores, err := strconv.ParseFloat(resourceAllocation["cpu_cores"].(string), 64) + if err != nil { + return nil, err + } + + memory_MiB, err := strconv.ParseFloat(resourceAllocation["memory_MiB"].(string), 64) + if err != nil { + return nil, err + } + + resource_alloc := &models.V1WorkspaceResourceAllocation{ + CPUCores: cpu_cores, + MemoryMiB: memory_MiB, + } + + // Handle GPU configuration if specified + if gpuLimit, exists := resourceAllocation["gpu_limit"]; exists && gpuLimit.(string) != "" { + gpuVal, err := strconv.Atoi(gpuLimit.(string)) + if err == nil && gpuVal > 0 { + provider := "nvidia" // Default provider for cluster allocations + // gpu_provider is optional - mainly used for default resource allocations + if gpuProvider, providerExists := resourceAllocation["gpu_provider"]; providerExists && gpuProvider.(string) != "" { + provider = gpuProvider.(string) + } + resource_alloc.GpuConfig = &models.V1GpuConfig{ + Limit: int32(gpuVal), + Provider: &provider, + } + } + } + + return resource_alloc, nil +} + func toWorkspaceNamespaces(d *schema.ResourceData) []*models.V1WorkspaceClusterNamespace { workspaceNamespaces := make([]*models.V1WorkspaceClusterNamespace, 0) if d.Get("namespaces") == nil { @@ -22,26 +60,38 @@ func toWorkspaceNamespaces(d *schema.ResourceData) []*models.V1WorkspaceClusterN return workspaceNamespaces } -func toWorkspaceNamespace(clusterRbacBinding interface{}) *models.V1WorkspaceClusterNamespace { - m := clusterRbacBinding.(map[string]interface{}) +func toWorkspaceNamespace(clusterNamespaceConfig interface{}) *models.V1WorkspaceClusterNamespace { + m := clusterNamespaceConfig.(map[string]interface{}) + // Handle default resource allocation resourceAllocation, _ := m["resource_allocation"].(map[string]interface{}) - - cpu_cores, err := strconv.ParseFloat(resourceAllocation["cpu_cores"].(string), 64) + defaultResourceAlloc, err := toWorkspaceResourceAllocation(resourceAllocation) if err != nil { return nil } - memory_MiB, err := strconv.ParseFloat(resourceAllocation["memory_MiB"].(string), 64) - if err != nil { - return nil - } - - resource_alloc := &models.V1WorkspaceResourceAllocation{ - CPUCores: cpu_cores, - MemoryMiB: memory_MiB, + // Handle cluster resource allocations + var clusterResourceAllocations []*models.V1ClusterResourceAllocation + if clusterAllocationsData, exists := m["cluster_resource_allocations"]; exists { + clusterAllocations := clusterAllocationsData.([]interface{}) + for _, clusterAlloc := range clusterAllocations { + clusterAllocMap := clusterAlloc.(map[string]interface{}) + uid := clusterAllocMap["uid"].(string) + clusterResourceAllocation := clusterAllocMap["resource_allocation"].(map[string]interface{}) + + resourceAlloc, err := toWorkspaceResourceAllocation(clusterResourceAllocation) + if err != nil { + continue // Skip invalid allocations + } + + clusterResourceAllocations = append(clusterResourceAllocations, &models.V1ClusterResourceAllocation{ + ClusterUID: uid, + ResourceAllocation: resourceAlloc, + }) + } } + // Handle images blacklist images, _ := m["images_blacklist"].([]interface{}) blacklist := make([]string, 0) for _, image := range images { @@ -58,8 +108,8 @@ func toWorkspaceNamespace(clusterRbacBinding interface{}) *models.V1WorkspaceClu Name: name, IsRegex: IsRegex, NamespaceResourceAllocation: &models.V1WorkspaceNamespaceResourceAllocation{ - ClusterResourceAllocations: nil, - DefaultResourceAllocation: resource_alloc, + ClusterResourceAllocations: clusterResourceAllocations, + DefaultResourceAllocation: defaultResourceAlloc, }, } @@ -90,30 +140,74 @@ func IsRegex(name string) bool { } -func toUpdateWorkspaceNamespaces(d *schema.ResourceData) *models.V1WorkspaceClusterNamespacesEntity { +func toUpdateWorkspaceNamespaces(d *schema.ResourceData, c *client.V1Client) *models.V1WorkspaceClusterNamespacesEntity { return &models.V1WorkspaceClusterNamespacesEntity{ ClusterNamespaces: toWorkspaceNamespaces(d), - ClusterRefs: toClusterRefs(d), + ClusterRefs: toClusterRefs(d, c), Quota: toQuota(d), } } +// Helper function to flatten V1WorkspaceResourceAllocation to resource allocation map +// includeProvider controls whether to include gpu_provider field (true for default allocations, false for cluster allocations) +func flattenWorkspaceResourceAllocation(resourceAlloc *models.V1WorkspaceResourceAllocation, includeProvider bool) map[string]interface{} { + result := make(map[string]interface{}) + + result["cpu_cores"] = strconv.Itoa(int(math.Round(resourceAlloc.CPUCores))) + result["memory_MiB"] = strconv.Itoa(int(math.Round(resourceAlloc.MemoryMiB))) + + // Handle GPU configuration if present + if resourceAlloc.GpuConfig != nil { + result["gpu_limit"] = strconv.Itoa(int(resourceAlloc.GpuConfig.Limit)) + // Only include gpu_provider for default resource allocations, not cluster-specific ones + if includeProvider { + if resourceAlloc.GpuConfig.Provider != nil { + result["gpu_provider"] = *resourceAlloc.GpuConfig.Provider + } else { + result["gpu_provider"] = "nvidia" // Default provider + } + } + } else { + result["gpu_limit"] = "0" + if includeProvider { + result["gpu_provider"] = "" + } + } + + return result +} + func flattenWorkspaceClusterNamespaces(items []*models.V1WorkspaceClusterNamespace) []interface{} { result := make([]interface{}, 0) for _, namespace := range items { flattenNamespace := make(map[string]interface{}) flattenNamespace["name"] = namespace.Name - flattenResourceAllocation := make(map[string]interface{}) - defaultAllocation := namespace.NamespaceResourceAllocation.DefaultResourceAllocation - flattenResourceAllocation["cpu_cores"] = strconv.Itoa(int(math.Round(defaultAllocation.CPUCores))) - flattenResourceAllocation["memory_MiB"] = strconv.Itoa(int(math.Round(defaultAllocation.MemoryMiB))) + // Flatten default resource allocation using helper (include gpu_provider) + if namespace.NamespaceResourceAllocation != nil && namespace.NamespaceResourceAllocation.DefaultResourceAllocation != nil { + flattenNamespace["resource_allocation"] = flattenWorkspaceResourceAllocation(namespace.NamespaceResourceAllocation.DefaultResourceAllocation, true) + } - flattenNamespace["resource_allocation"] = flattenResourceAllocation + // Flatten cluster resource allocations (exclude gpu_provider) + if namespace.NamespaceResourceAllocation != nil && len(namespace.NamespaceResourceAllocation.ClusterResourceAllocations) > 0 { + clusterAllocations := make([]interface{}, 0) + for _, clusterAlloc := range namespace.NamespaceResourceAllocation.ClusterResourceAllocations { + clusterAllocMap := map[string]interface{}{ + "uid": clusterAlloc.ClusterUID, + } + if clusterAlloc.ResourceAllocation != nil { + clusterAllocMap["resource_allocation"] = flattenWorkspaceResourceAllocation(clusterAlloc.ResourceAllocation, false) + } + clusterAllocations = append(clusterAllocations, clusterAllocMap) + } + flattenNamespace["cluster_resource_allocations"] = clusterAllocations + } + // Handle images blacklist if namespace.Image != nil { flattenNamespace["images_blacklist"] = namespace.Image.BlackListedImages } + result = append(result, flattenNamespace) } return result diff --git a/spectrocloud/workspace_rbac.go b/spectrocloud/workspace_rbac.go index 0dfae09c9..1df443021 100644 --- a/spectrocloud/workspace_rbac.go +++ b/spectrocloud/workspace_rbac.go @@ -30,10 +30,21 @@ func toQuota(d *schema.ResourceData) *models.V1WorkspaceQuota { } q := wsQuota.([]interface{})[0].(map[string]interface{}) + resourceAllocation := &models.V1WorkspaceResourceAllocation{ + CPUCores: float64(q["cpu"].(int)), + MemoryMiB: float64(q["memory"].(int)), + } + + // Handle GPU configuration if specified + if gpuVal, exists := q["gpu"]; exists && gpuVal.(int) > 0 { + provider := "nvidia" // Default to nvidia as it's the only supported provider + resourceAllocation.GpuConfig = &models.V1GpuConfig{ + Limit: int32(gpuVal.(int)), + Provider: &provider, + } + } + return &models.V1WorkspaceQuota{ - ResourceAllocation: &models.V1WorkspaceResourceAllocation{ - CPUCores: float64(q["cpu"].(int)), - MemoryMiB: float64(q["memory"].(int)), - }, + ResourceAllocation: resourceAllocation, } } diff --git a/spectrocloud/workspace_test.go b/spectrocloud/workspace_test.go index 110974770..37e05a144 100644 --- a/spectrocloud/workspace_test.go +++ b/spectrocloud/workspace_test.go @@ -1,10 +1,11 @@ package spectrocloud import ( + "testing" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/spectrocloud/palette-sdk-go/api/models" "github.com/stretchr/testify/assert" - "testing" ) func TestToWorkspacePolicies(t *testing.T) { @@ -65,10 +66,10 @@ func TestFlattenWorkspaceClusters(t *testing.T) { }, } - result := flattenWorkspaceClusters(workspace) + result := flattenWorkspaceClusters(workspace, nil) // nil client for unit test expected := []interface{}{ - map[string]interface{}{"uid": "cluster-1"}, - map[string]interface{}{"uid": "cluster-2"}, + map[string]interface{}{"uid": "cluster-1", "cluster_name": ""}, + map[string]interface{}{"uid": "cluster-2", "cluster_name": ""}, } assert.Equal(t, expected, result) @@ -81,7 +82,7 @@ func TestFlattenWorkspaceClusters_Empty(t *testing.T) { }, } - result := flattenWorkspaceClusters(workspace) + result := flattenWorkspaceClusters(workspace, nil) // nil client for unit test assert.Equal(t, 0, len(result)) }