diff --git a/docs/data-sources/ske_cluster.md b/docs/data-sources/ske_cluster.md index 755198d3c..34f1b8b78 100644 --- a/docs/data-sources/ske_cluster.md +++ b/docs/data-sources/ske_cluster.md @@ -117,8 +117,17 @@ Read-Only: Read-Only: +- `control_plane` (Attributes) Control plane for the cluster. (see [below for nested schema](#nestedatt--network--control_plane)) - `id` (String) ID of the STACKIT Network Area (SNA) network into which the cluster will be deployed. + +### Nested Schema for `network.control_plane` + +Read-Only: + +- `access_scope` (String) Access scope of the control plane. It defines if the Kubernetes control plane is public or only available inside a STACKIT Network Area.Possible values are: `PUBLIC`, `SNA`. + + ### Nested Schema for `node_pools` diff --git a/docs/data-sources/ske_kubernetes_versions.md b/docs/data-sources/ske_kubernetes_versions.md index c64bab2aa..bd3edbd2a 100644 --- a/docs/data-sources/ske_kubernetes_versions.md +++ b/docs/data-sources/ske_kubernetes_versions.md @@ -43,7 +43,7 @@ resource "stackit_ske_cluster" "example" { ### Optional - `region` (String) Region override. If omitted, the provider’s region will be used. -- `version_state` (String) If specified, only returns Kubernetes versions with this version state. Possible values are: `UNSPECIFIED`, `SUPPORTED`. +- `version_state` (String) If specified, only returns Kubernetes versions with this version state. Possible values are: `SUPPORTED`. ### Read-Only diff --git a/docs/data-sources/ske_machine_image_versions.md b/docs/data-sources/ske_machine_image_versions.md index eaab52642..9ad650b60 100644 --- a/docs/data-sources/ske_machine_image_versions.md +++ b/docs/data-sources/ske_machine_image_versions.md @@ -53,7 +53,7 @@ resource "stackit_ske_cluster" "example" { ### Optional - `region` (String) Region override. If omitted, the provider’s region will be used. -- `version_state` (String) Filter returned machine image versions by their state. Possible values are: `UNSPECIFIED`, `SUPPORTED`. +- `version_state` (String) Filter returned machine image versions by their state. Possible values are: `SUPPORTED`. ### Read-Only diff --git a/docs/resources/ske_cluster.md b/docs/resources/ske_cluster.md index 44616c9be..02383aeae 100644 --- a/docs/resources/ske_cluster.md +++ b/docs/resources/ske_cluster.md @@ -39,6 +39,9 @@ resource "stackit_ske_cluster" "example" { start = "01:00:00Z" end = "02:00:00Z" } + network = { + access_scope = "PUBLIC" + } } # Only use the import statement, if you want to import an existing ske cluster @@ -204,4 +207,12 @@ Optional: Optional: +- `control_plane` (Attributes) Control plane for the cluster. (see [below for nested schema](#nestedatt--network--control_plane)) - `id` (String) ID of the STACKIT Network Area (SNA) network into which the cluster will be deployed. + + +### Nested Schema for `network.control_plane` + +Optional: + +- `access_scope` (String) Access scope of the control plane. It defines if the Kubernetes control plane is public or only available inside a STACKIT Network Area.Possible values are: `PUBLIC`, `SNA`. diff --git a/examples/resources/stackit_ske_cluster/resource.tf b/examples/resources/stackit_ske_cluster/resource.tf index e87958fd2..6514f5eb1 100644 --- a/examples/resources/stackit_ske_cluster/resource.tf +++ b/examples/resources/stackit_ske_cluster/resource.tf @@ -21,6 +21,9 @@ resource "stackit_ske_cluster" "example" { start = "01:00:00Z" end = "02:00:00Z" } + network = { + access_scope = "PUBLIC" + } } # Only use the import statement, if you want to import an existing ske cluster diff --git a/go.mod b/go.mod index a6d9d6e10..39bbaa751 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.11.6 github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.7 github.com/stackitcloud/stackit-sdk-go/services/sfs v0.3.0 - github.com/stackitcloud/stackit-sdk-go/services/ske v1.6.3 + github.com/stackitcloud/stackit-sdk-go/services/ske v1.7.0 github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.3 github.com/teambition/rrule-go v1.8.2 golang.org/x/mod v0.32.0 diff --git a/go.sum b/go.sum index 513d85b9e..c8c89e295 100644 --- a/go.sum +++ b/go.sum @@ -207,6 +207,8 @@ github.com/stackitcloud/stackit-sdk-go/services/sfs v0.3.0 h1:4567q2dFp3Hw+5Kx+N github.com/stackitcloud/stackit-sdk-go/services/sfs v0.3.0/go.mod h1:r5lBwzJpJe2xBIYctkVIIpaZ41Y6vUEpkmsWR2VoQJs= github.com/stackitcloud/stackit-sdk-go/services/ske v1.6.3 h1:c+nQMvSml08cdRF1kE24vCw0r/l56olP/svQyhcnKOs= github.com/stackitcloud/stackit-sdk-go/services/ske v1.6.3/go.mod h1:1Jr+ImrmPERxbYnlTy6O2aSZYNnREf2qQyysv6YC1RY= +github.com/stackitcloud/stackit-sdk-go/services/ske v1.7.0 h1:l1QjxW7sdE/6B6BZtHxbmus8XJdI9KDuXX3fwUa5fog= +github.com/stackitcloud/stackit-sdk-go/services/ske v1.7.0/go.mod h1:1Jr+ImrmPERxbYnlTy6O2aSZYNnREf2qQyysv6YC1RY= github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.3 h1:AQrcr+qeIuZob+3TT2q1L4WOPtpsu5SEpkTnOUHDqfE= github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.3/go.mod h1:8BBGC69WFXWWmKgzSjgE4HvsI7pEgO0RN2cASwuPJ18= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/stackit/internal/services/ske/cluster/datasource.go b/stackit/internal/services/ske/cluster/datasource.go index 2a7feca9c..58af37c02 100644 --- a/stackit/internal/services/ske/cluster/datasource.go +++ b/stackit/internal/services/ske/cluster/datasource.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" + sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" skeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/utils" @@ -223,6 +224,16 @@ func (r *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest validate.UUID(), }, }, + "control_plane": schema.SingleNestedAttribute{ + Description: "Control plane for the cluster.", + Computed: true, + Attributes: map[string]schema.Attribute{ + "access_scope": schema.StringAttribute{ + Description: "Access scope of the control plane. It defines if the Kubernetes control plane is public or only available inside a STACKIT Network Area." + utils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(ske.AllowedAccessScopeEnumValues)...), + Computed: true, + }, + }, + }, }, }, diff --git a/stackit/internal/services/ske/cluster/resource.go b/stackit/internal/services/ske/cluster/resource.go index a01a37a05..de86f775e 100644 --- a/stackit/internal/services/ske/cluster/resource.go +++ b/stackit/internal/services/ske/cluster/resource.go @@ -157,12 +157,23 @@ var maintenanceTypes = map[string]attr.Type{ // Struct corresponding to Model.Network type network struct { - ID types.String `tfsdk:"id"` + ID types.String `tfsdk:"id"` + ControlPlane types.Object `tfsdk:"control_plane"` } // Types corresponding to network var networkTypes = map[string]attr.Type{ - "id": basetypes.StringType{}, + "id": basetypes.StringType{}, + "control_plane": types.ObjectType{AttrTypes: controlPlaneTypes}, +} + +type controlPlane struct { + AccessScope types.String `tfsdk:"access_scope"` +} + +// Types corresponding to control plane +var controlPlaneTypes = map[string]attr.Type{ + "access_scope": basetypes.StringType{}, } // Struct corresponding to Model.Hibernations[i] @@ -563,6 +574,16 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re stringplanmodifier.RequiresReplace(), }, }, + "control_plane": schema.SingleNestedAttribute{ + Description: "Control plane for the cluster.", + Optional: true, + Attributes: map[string]schema.Attribute{ + "access_scope": schema.StringAttribute{ + Description: "Access scope of the control plane. It defines if the Kubernetes control plane is public or only available inside a STACKIT Network Area." + utils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(ske.AllowedAccessScopeEnumValues)...), + Optional: true, + }, + }, + }, }, }, "hibernations": schema.ListNestedAttribute{ @@ -1352,8 +1373,21 @@ func toNetworkPayload(ctx context.Context, m *Model) (*ske.Network, error) { return nil, fmt.Errorf("converting network object: %v", diags.Errors()) } + var networkControlPlane *ske.V2ControlPlaneNetwork + if !(network.ControlPlane.IsNull() || network.ControlPlane.IsUnknown()) { + networkControlPlaneModel := controlPlane{} + diags = network.ControlPlane.As(ctx, &networkControlPlaneModel, basetypes.ObjectAsOptions{}) + if diags.HasError() { + return nil, fmt.Errorf("converting network control plane: %w", core.DiagsToError(diags)) + } + networkControlPlane = &ske.V2ControlPlaneNetwork{ + AccessScope: ske.V2ControlPlaneNetworkGetAccessScopeAttributeType(conversion.StringValueToPointer(networkControlPlaneModel.AccessScope)), + } + } + return &ske.Network{ - Id: conversion.StringValueToPointer(network.ID), + Id: conversion.StringValueToPointer(network.ID), + ControlPlane: networkControlPlane, }, nil } @@ -1657,12 +1691,32 @@ func mapNetwork(cl *ske.Cluster, m *Model) error { return nil } + var diags diag.Diagnostics id := types.StringNull() if cl.Network.Id != nil { id = types.StringValue(*cl.Network.Id) } + + networkControlPlane := types.ObjectNull(controlPlaneTypes) + if cl.Network.ControlPlane != nil { + controlPlaneAccessScope := types.StringNull() + if cl.Network.ControlPlane.AccessScope != nil { + controlPlaneAccessScope = types.StringValue(string(cl.Network.ControlPlane.GetAccessScope())) + } + + controlPlaneValues := map[string]attr.Value{ + "access_scope": controlPlaneAccessScope, + } + + networkControlPlane, diags = types.ObjectValue(controlPlaneTypes, controlPlaneValues) + if diags.HasError() { + return fmt.Errorf("creating network control plane: %w", core.DiagsToError(diags)) + } + } + networkValues := map[string]attr.Value{ - "id": id, + "id": id, + "control_plane": networkControlPlane, } networkObject, diags := types.ObjectValue(networkTypes, networkValues) if diags.HasError() { diff --git a/stackit/internal/services/ske/cluster/resource_test.go b/stackit/internal/services/ske/cluster/resource_test.go index e29c15cc8..f8d7137e2 100644 --- a/stackit/internal/services/ske/cluster/resource_test.go +++ b/stackit/internal/services/ske/cluster/resource_test.go @@ -110,6 +110,9 @@ func TestMapFields(t *testing.T) { }, Network: &ske.Network{ Id: utils.Ptr("nid"), + ControlPlane: &ske.V2ControlPlaneNetwork{ + AccessScope: ske.V2ControlPlaneNetworkGetAccessScopeAttributeType(utils.Ptr("SNA")), + }, }, Name: utils.Ptr("name"), Nodepools: &[]ske.Nodepool{ @@ -231,6 +234,9 @@ func TestMapFields(t *testing.T) { }), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ "id": types.StringValue("nid"), + "control_plane": types.ObjectValueMust(controlPlaneTypes, map[string]attr.Value{ + "access_scope": types.StringValue("SNA"), + }), }), Hibernations: types.ListValueMust( types.ObjectType{AttrTypes: hibernationTypes}, @@ -560,6 +566,9 @@ func TestMapFields(t *testing.T) { }, Network: &ske.Network{ Id: utils.Ptr("nid"), + ControlPlane: &ske.V2ControlPlaneNetwork{ + AccessScope: ske.V2ControlPlaneNetworkGetAccessScopeAttributeType(utils.Ptr("SNA")), + }, }, Name: utils.Ptr("name"), Nodepools: &[]ske.Nodepool{ @@ -648,6 +657,9 @@ func TestMapFields(t *testing.T) { }), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ "id": types.StringValue("nid"), + "control_plane": types.ObjectValueMust(controlPlaneTypes, map[string]attr.Value{ + "access_scope": types.StringValue("SNA"), + }), }), Hibernations: types.ListValueMust( types.ObjectType{AttrTypes: hibernationTypes}, @@ -2239,10 +2251,16 @@ func TestToNetworkPayload(t *testing.T) { Name: types.StringValue("name"), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ "id": types.StringValue("nid"), + "control_plane": types.ObjectValueMust(controlPlaneTypes, map[string]attr.Value{ + "access_scope": types.StringValue("SNA"), + }), }), }, &ske.Network{ Id: utils.Ptr("nid"), + ControlPlane: &ske.V2ControlPlaneNetwork{ + AccessScope: ske.V2ControlPlaneNetworkGetAccessScopeAttributeType(utils.Ptr("SNA")), + }, }, true, }, @@ -2252,12 +2270,29 @@ func TestToNetworkPayload(t *testing.T) { ProjectId: types.StringValue("pid"), Name: types.StringValue("name"), Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ - "id": types.StringNull(), + "id": types.StringNull(), + "control_plane": types.ObjectNull(controlPlaneTypes), }), }, &ske.Network{}, true, }, + { + "no_control_plane", + &Model{ + ProjectId: types.StringValue("pid"), + Name: types.StringValue("name"), + Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{ + "id": types.StringValue("nid"), + "control_plane": types.ObjectNull(controlPlaneTypes), + }), + }, + &ske.Network{ + Id: utils.Ptr("nid"), + ControlPlane: nil, + }, + true, + }, { "no_network", &Model{ diff --git a/stackit/internal/services/ske/ske_acc_test.go b/stackit/internal/services/ske/ske_acc_test.go index c862daa98..d394ca9d5 100644 --- a/stackit/internal/services/ske/ske_acc_test.go +++ b/stackit/internal/services/ske/ske_acc_test.go @@ -92,6 +92,7 @@ var testConfigVarsMax = config.Variables{ "refresh_before": config.StringVariable("600"), "dns_zone_name": config.StringVariable("acc-" + acctest.RandStringFromCharSet(6, acctest.CharSetAlpha)), "dns_name": config.StringVariable("acc-" + acctest.RandStringFromCharSet(6, acctest.CharSetAlpha) + ".runs.onstackit.cloud"), + "network_control_plane_access_scope": config.StringVariable("PUBLIC"), } var testConfigDatasource = config.Variables{ @@ -299,6 +300,8 @@ func TestAccSKEMax(t *testing.T) { resource.TestCheckResourceAttrSet("stackit_ske_cluster.cluster", "pod_address_ranges.0"), resource.TestCheckResourceAttrSet("stackit_ske_cluster.cluster", "kubernetes_version_used"), + resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "network.control_plane.access_scope", testutil.ConvertConfigVariable(testConfigVarsMax["network_control_plane_access_scope"])), + // Kubeconfig resource.TestCheckResourceAttrPair( "stackit_ske_kubeconfig.kubeconfig", "project_id", @@ -373,6 +376,8 @@ func TestAccSKEMax(t *testing.T) { resource.TestCheckResourceAttrSet("data.stackit_ske_cluster.cluster", "pod_address_ranges.0"), resource.TestCheckResourceAttrSet("data.stackit_ske_cluster.cluster", "kubernetes_version_used"), + + resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "network.control_plane.access_scope", testutil.ConvertConfigVariable(testConfigVarsMax["network_control_plane_access_scope"])), ), }, // 3) Import cluster diff --git a/stackit/internal/services/ske/testdata/resource-max.tf b/stackit/internal/services/ske/testdata/resource-max.tf index 192c9138f..fde7ff1cc 100644 --- a/stackit/internal/services/ske/testdata/resource-max.tf +++ b/stackit/internal/services/ske/testdata/resource-max.tf @@ -35,6 +35,7 @@ variable "refresh" {} variable "refresh_before" {} variable "dns_zone_name" {} variable "dns_name" {} +variable "network_control_plane_access_scope" {} resource "stackit_ske_cluster" "cluster" { project_id = var.project_id @@ -92,6 +93,11 @@ resource "stackit_ske_cluster" "cluster" { end = var.maintenance_end } region = var.region + network = { + control_plane = { + access_scope = var.network_control_plane_access_scope + } + } } resource "stackit_ske_kubeconfig" "kubeconfig" {