Ft/region adjustment ske (#909)

* feat(ske): Region adjustment

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>

* Renaming argus to observability

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>

* Remove deprecated field allow_privileged_containers

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>

* Add and adjust unit tests for deprecated argus extension

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>

* Remove deprecated kubernetesVersion field

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>

* Generate docs

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>

* Review findings

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>

---------

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>
This commit is contained in:
Alexander Dahmen 2025-07-14 08:15:31 +02:00 committed by GitHub
parent 8e776757ea
commit e38c5aee4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 539 additions and 409 deletions

View file

@ -33,14 +33,10 @@ data "stackit_ske_cluster" "example" {
### Read-Only ### Read-Only
- `allow_privileged_containers` (Boolean, Deprecated) DEPRECATED as of Kubernetes 1.25+
Flag to specify if privileged mode for containers is enabled or not.
This should be used with care since it also disables a couple of other features like the use of some volume type (e.g. PVCs).
- `egress_address_ranges` (List of String) The outgoing network ranges (in CIDR notation) of traffic originating from workload on the cluster. - `egress_address_ranges` (List of String) The outgoing network ranges (in CIDR notation) of traffic originating from workload on the cluster.
- `extensions` (Attributes) A single extensions block as defined below (see [below for nested schema](#nestedatt--extensions)) - `extensions` (Attributes) A single extensions block as defined below (see [below for nested schema](#nestedatt--extensions))
- `hibernations` (Attributes List) One or more hibernation block as defined below. (see [below for nested schema](#nestedatt--hibernations)) - `hibernations` (Attributes List) One or more hibernation block as defined below. (see [below for nested schema](#nestedatt--hibernations))
- `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`name`". - `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`name`".
- `kubernetes_version` (String, Deprecated) Kubernetes version. This field is deprecated, use `kubernetes_version_used` instead
- `kubernetes_version_min` (String) The minimum Kubernetes version, this field is always nil. SKE automatically updates the cluster Kubernetes version if you have set `maintenance.enable_kubernetes_version_updates` to true or if there is a mandatory update, as described in [Updates for Kubernetes versions and Operating System versions in SKE](https://docs.stackit.cloud/stackit/en/version-updates-in-ske-10125631.html). To get the current kubernetes version being used for your cluster, use the `kubernetes_version_used` field. - `kubernetes_version_min` (String) The minimum Kubernetes version, this field is always nil. SKE automatically updates the cluster Kubernetes version if you have set `maintenance.enable_kubernetes_version_updates` to true or if there is a mandatory update, as described in [Updates for Kubernetes versions and Operating System versions in SKE](https://docs.stackit.cloud/stackit/en/version-updates-in-ske-10125631.html). To get the current kubernetes version being used for your cluster, use the `kubernetes_version_used` field.
- `kubernetes_version_used` (String) Full Kubernetes version used. For example, if `1.22` was selected, this value may result to `1.22.15` - `kubernetes_version_used` (String) Full Kubernetes version used. For example, if `1.22` was selected, this value may result to `1.22.15`
- `maintenance` (Attributes) A single maintenance block as defined below (see [below for nested schema](#nestedatt--maintenance)) - `maintenance` (Attributes) A single maintenance block as defined below (see [below for nested schema](#nestedatt--maintenance))
@ -54,8 +50,9 @@ This should be used with care since it also disables a couple of other features
Read-Only: Read-Only:
- `acl` (Attributes) Cluster access control configuration (see [below for nested schema](#nestedatt--extensions--acl)) - `acl` (Attributes) Cluster access control configuration (see [below for nested schema](#nestedatt--extensions--acl))
- `argus` (Attributes) A single argus block as defined below (see [below for nested schema](#nestedatt--extensions--argus)) - `argus` (Attributes, Deprecated) A single argus block as defined below. This field is deprecated and will be removed 06 January 2026. (see [below for nested schema](#nestedatt--extensions--argus))
- `dns` (Attributes) DNS extension configuration (see [below for nested schema](#nestedatt--extensions--dns)) - `dns` (Attributes) DNS extension configuration (see [below for nested schema](#nestedatt--extensions--dns))
- `observability` (Attributes) A single observability block as defined below. (see [below for nested schema](#nestedatt--extensions--observability))
<a id="nestedatt--extensions--acl"></a> <a id="nestedatt--extensions--acl"></a>
### Nested Schema for `extensions.acl` ### Nested Schema for `extensions.acl`
@ -84,6 +81,15 @@ Read-Only:
- `zones` (List of String) Specify a list of domain filters for externalDNS (e.g., `foo.runs.onstackit.cloud`) - `zones` (List of String) Specify a list of domain filters for externalDNS (e.g., `foo.runs.onstackit.cloud`)
<a id="nestedatt--extensions--observability"></a>
### Nested Schema for `extensions.observability`
Read-Only:
- `enabled` (Boolean) Flag to enable/disable Observability extensions.
- `instance_id` (String) Observability instance ID to choose which Observability instance is used. Required when enabled is set to `true`.
<a id="nestedatt--hibernations"></a> <a id="nestedatt--hibernations"></a>
### Nested Schema for `hibernations` ### Nested Schema for `hibernations`

View file

@ -50,12 +50,8 @@ resource "stackit_ske_cluster" "example" {
### Optional ### Optional
- `allow_privileged_containers` (Boolean) Flag to specify if privileged mode for containers is enabled or not.
This should be used with care since it also disables a couple of other features like the use of some volume type (e.g. PVCs).
Deprecated as of Kubernetes 1.25 and later
- `extensions` (Attributes) A single extensions block as defined below. (see [below for nested schema](#nestedatt--extensions)) - `extensions` (Attributes) A single extensions block as defined below. (see [below for nested schema](#nestedatt--extensions))
- `hibernations` (Attributes List) One or more hibernation block as defined below. (see [below for nested schema](#nestedatt--hibernations)) - `hibernations` (Attributes List) One or more hibernation block as defined below. (see [below for nested schema](#nestedatt--hibernations))
- `kubernetes_version` (String, Deprecated) Kubernetes version. Must only contain major and minor version (e.g. 1.22). This field is deprecated, use `kubernetes_version_min instead`
- `kubernetes_version_min` (String) The minimum Kubernetes version. This field will be used to set the minimum kubernetes version on creation/update of the cluster. If unset, the latest supported Kubernetes version will be used. SKE automatically updates the cluster Kubernetes version if you have set `maintenance.enable_kubernetes_version_updates` to true or if there is a mandatory update, as described in [Updates for Kubernetes versions and Operating System versions in SKE](https://docs.stackit.cloud/stackit/en/version-updates-in-ske-10125631.html). To get the current kubernetes version being used for your cluster, use the read-only `kubernetes_version_used` field. - `kubernetes_version_min` (String) The minimum Kubernetes version. This field will be used to set the minimum kubernetes version on creation/update of the cluster. If unset, the latest supported Kubernetes version will be used. SKE automatically updates the cluster Kubernetes version if you have set `maintenance.enable_kubernetes_version_updates` to true or if there is a mandatory update, as described in [Updates for Kubernetes versions and Operating System versions in SKE](https://docs.stackit.cloud/stackit/en/version-updates-in-ske-10125631.html). To get the current kubernetes version being used for your cluster, use the read-only `kubernetes_version_used` field.
- `maintenance` (Attributes) A single maintenance block as defined below. (see [below for nested schema](#nestedatt--maintenance)) - `maintenance` (Attributes) A single maintenance block as defined below. (see [below for nested schema](#nestedatt--maintenance))
- `network` (Attributes) Network block as defined below. (see [below for nested schema](#nestedatt--network)) - `network` (Attributes) Network block as defined below. (see [below for nested schema](#nestedatt--network))
@ -117,8 +113,9 @@ Optional:
Optional: Optional:
- `acl` (Attributes) Cluster access control configuration. (see [below for nested schema](#nestedatt--extensions--acl)) - `acl` (Attributes) Cluster access control configuration. (see [below for nested schema](#nestedatt--extensions--acl))
- `argus` (Attributes) A single argus block as defined below. (see [below for nested schema](#nestedatt--extensions--argus)) - `argus` (Attributes, Deprecated) A single argus block as defined below. This field is deprecated and will be removed 06 January 2026. (see [below for nested schema](#nestedatt--extensions--argus))
- `dns` (Attributes) DNS extension configuration (see [below for nested schema](#nestedatt--extensions--dns)) - `dns` (Attributes) DNS extension configuration (see [below for nested schema](#nestedatt--extensions--dns))
- `observability` (Attributes) A single observability block as defined below. (see [below for nested schema](#nestedatt--extensions--observability))
<a id="nestedatt--extensions--acl"></a> <a id="nestedatt--extensions--acl"></a>
### Nested Schema for `extensions.acl` ### Nested Schema for `extensions.acl`
@ -153,6 +150,18 @@ Optional:
- `zones` (List of String) Specify a list of domain filters for externalDNS (e.g., `foo.runs.onstackit.cloud`) - `zones` (List of String) Specify a list of domain filters for externalDNS (e.g., `foo.runs.onstackit.cloud`)
<a id="nestedatt--extensions--observability"></a>
### Nested Schema for `extensions.observability`
Required:
- `enabled` (Boolean) Flag to enable/disable Observability extensions.
Optional:
- `instance_id` (String) Observability instance ID to choose which Observability instance is used. Required when enabled is set to `true`.
<a id="nestedatt--hibernations"></a> <a id="nestedatt--hibernations"></a>
### Nested Schema for `hibernations` ### Nested Schema for `hibernations`

View file

@ -32,6 +32,7 @@ resource "stackit_ske_kubeconfig" "example" {
- `expiration` (Number) Expiration time of the kubeconfig, in seconds. Defaults to `3600` - `expiration` (Number) Expiration time of the kubeconfig, in seconds. Defaults to `3600`
- `refresh` (Boolean) If set to true, the provider will check if the kubeconfig has expired and will generated a new valid one in-place - `refresh` (Boolean) If set to true, the provider will check if the kubeconfig has expired and will generated a new valid one in-place
- `region` (String) The resource region. If not defined, the provider region is used.
### Read-Only ### Read-Only

2
go.mod
View file

@ -34,7 +34,7 @@ require (
github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.2.0 github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.2.0
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.9.0 github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.9.0
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.1 github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.1
github.com/stackitcloud/stackit-sdk-go/services/ske v0.27.0 github.com/stackitcloud/stackit-sdk-go/services/ske v1.0.0
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.0 github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.0
github.com/teambition/rrule-go v1.8.2 github.com/teambition/rrule-go v1.8.2
golang.org/x/mod v0.26.0 golang.org/x/mod v0.26.0

4
go.sum
View file

@ -198,8 +198,8 @@ github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.9.0 h1:2d28WFQ
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.9.0/go.mod h1:t77MA8uyEU9KZd1On5JpnxI3xhVPKIS8WutStqvU8Cw= github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.9.0/go.mod h1:t77MA8uyEU9KZd1On5JpnxI3xhVPKIS8WutStqvU8Cw=
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.1 h1:h1TsWatlsexLeKdkb3L8chcxaXJOy/cLXctsRxhb4xg= github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.1 h1:h1TsWatlsexLeKdkb3L8chcxaXJOy/cLXctsRxhb4xg=
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.1/go.mod h1:M4xZ2BnmROvLV2MrAP6A8o9BnyT0CkvpEcP8lBOfRs8= github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.1/go.mod h1:M4xZ2BnmROvLV2MrAP6A8o9BnyT0CkvpEcP8lBOfRs8=
github.com/stackitcloud/stackit-sdk-go/services/ske v0.27.0 h1:bwLmLXvtCl1XkPRP+YrXwfz+lBMaGWH/crlNbYtxeqE= github.com/stackitcloud/stackit-sdk-go/services/ske v1.0.0 h1:PX8VTo2UhPd6BeEaCHFlpIkDbk9OFQEO6eJJ8JkxesA=
github.com/stackitcloud/stackit-sdk-go/services/ske v0.27.0/go.mod h1:V09NmPahuUiuZEogVPgxuVqqti0th5B7TVAjuiM09mE= github.com/stackitcloud/stackit-sdk-go/services/ske v1.0.0/go.mod h1:V09NmPahuUiuZEogVPgxuVqqti0th5B7TVAjuiM09mE=
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.0 h1:pUl/981oAXPnZd7++69NfEWv6JwW9UpxER16XxQUdOk= github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.0 h1:pUl/981oAXPnZd7++69NfEWv6JwW9UpxER16XxQUdOk=
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.0/go.mod h1:S04/QsQrB2EgYGjl62BO+9QUswrlRBoBosigrhdmccM= github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.0/go.mod h1:S04/QsQrB2EgYGjl62BO+9QUswrlRBoBosigrhdmccM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

View file

@ -79,20 +79,10 @@ func (r *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
Description: `The minimum Kubernetes version, this field is always nil. ` + SKEUpdateDoc + " To get the current kubernetes version being used for your cluster, use the `kubernetes_version_used` field.", Description: `The minimum Kubernetes version, this field is always nil. ` + SKEUpdateDoc + " To get the current kubernetes version being used for your cluster, use the `kubernetes_version_used` field.",
Computed: true, Computed: true,
}, },
"kubernetes_version": schema.StringAttribute{
Description: "Kubernetes version. This field is deprecated, use `kubernetes_version_used` instead",
Computed: true,
DeprecationMessage: "This field is always nil, use `kubernetes_version_used` to get the cluster kubernetes version. This field would cause errors when the cluster got a kubernetes version minor upgrade, either triggered by automatic or forceful updates.",
},
"kubernetes_version_used": schema.StringAttribute{ "kubernetes_version_used": schema.StringAttribute{
Description: "Full Kubernetes version used. For example, if `1.22` was selected, this value may result to `1.22.15`", Description: "Full Kubernetes version used. For example, if `1.22` was selected, this value may result to `1.22.15`",
Computed: true, Computed: true,
}, },
"allow_privileged_containers": schema.BoolAttribute{
Description: "DEPRECATED as of Kubernetes 1.25+\n Flag to specify if privileged mode for containers is enabled or not.\nThis should be used with care since it also disables a couple of other features like the use of some volume type (e.g. PVCs).",
DeprecationMessage: "Please remove this flag from your configuration when using Kubernetes version 1.25+.",
Computed: true,
},
"egress_address_ranges": schema.ListAttribute{ "egress_address_ranges": schema.ListAttribute{
Description: "The outgoing network ranges (in CIDR notation) of traffic originating from workload on the cluster.", Description: "The outgoing network ranges (in CIDR notation) of traffic originating from workload on the cluster.",
Computed: true, Computed: true,
@ -262,8 +252,9 @@ func (r *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
Computed: true, Computed: true,
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"argus": schema.SingleNestedAttribute{ "argus": schema.SingleNestedAttribute{
Description: "A single argus block as defined below", Description: "A single argus block as defined below. This field is deprecated and will be removed 06 January 2026.",
Computed: true, DeprecationMessage: "Use observability instead.",
Computed: true,
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"enabled": schema.BoolAttribute{ "enabled": schema.BoolAttribute{
Description: "Flag to enable/disable argus extensions.", Description: "Flag to enable/disable argus extensions.",
@ -275,6 +266,20 @@ func (r *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
}, },
}, },
}, },
"observability": schema.SingleNestedAttribute{
Description: "A single observability block as defined below.",
Computed: true,
Attributes: map[string]schema.Attribute{
"enabled": schema.BoolAttribute{
Description: "Flag to enable/disable Observability extensions.",
Computed: true,
},
"instance_id": schema.StringAttribute{
Description: "Observability instance ID to choose which Observability instance is used. Required when enabled is set to `true`.",
Computed: true,
},
},
},
"acl": schema.SingleNestedAttribute{ "acl": schema.SingleNestedAttribute{
Description: "Cluster access control configuration", Description: "Cluster access control configuration",
Computed: true, Computed: true,
@ -331,7 +336,7 @@ func (r *clusterDataSource) Read(ctx context.Context, req datasource.ReadRequest
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "name", name) ctx = tflog.SetField(ctx, "name", name)
ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "region", region)
clusterResp, err := r.client.GetCluster(ctx, projectId, name).Execute() clusterResp, err := r.client.GetCluster(ctx, projectId, region, name).Execute()
if err != nil { if err != nil {
utils.LogError( utils.LogError(
ctx, ctx,

View file

@ -12,7 +12,6 @@ import (
serviceenablementUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/serviceenablement/utils" serviceenablementUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/serviceenablement/utils"
skeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/utils" skeUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/utils"
"github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/diag"
@ -66,25 +65,23 @@ var (
) )
type skeClient interface { type skeClient interface {
GetClusterExecute(ctx context.Context, projectId, clusterName string) (*ske.Cluster, error) GetClusterExecute(ctx context.Context, projectId, region, clusterName string) (*ske.Cluster, error)
} }
type Model struct { type Model struct {
Id types.String `tfsdk:"id"` // needed by TF Id types.String `tfsdk:"id"` // needed by TF
ProjectId types.String `tfsdk:"project_id"` ProjectId types.String `tfsdk:"project_id"`
Name types.String `tfsdk:"name"` Name types.String `tfsdk:"name"`
KubernetesVersionMin types.String `tfsdk:"kubernetes_version_min"` KubernetesVersionMin types.String `tfsdk:"kubernetes_version_min"`
KubernetesVersion types.String `tfsdk:"kubernetes_version"` KubernetesVersionUsed types.String `tfsdk:"kubernetes_version_used"`
KubernetesVersionUsed types.String `tfsdk:"kubernetes_version_used"` NodePools types.List `tfsdk:"node_pools"`
AllowPrivilegedContainers types.Bool `tfsdk:"allow_privileged_containers"` Maintenance types.Object `tfsdk:"maintenance"`
NodePools types.List `tfsdk:"node_pools"` Network types.Object `tfsdk:"network"`
Maintenance types.Object `tfsdk:"maintenance"` Hibernations types.List `tfsdk:"hibernations"`
Network types.Object `tfsdk:"network"` Extensions types.Object `tfsdk:"extensions"`
Hibernations types.List `tfsdk:"hibernations"` EgressAddressRanges types.List `tfsdk:"egress_address_ranges"`
Extensions types.Object `tfsdk:"extensions"` PodAddressRanges types.List `tfsdk:"pod_address_ranges"`
EgressAddressRanges types.List `tfsdk:"egress_address_ranges"` Region types.String `tfsdk:"region"`
PodAddressRanges types.List `tfsdk:"pod_address_ranges"`
Region types.String `tfsdk:"region"`
} }
// Struct corresponding to Model.NodePools[i] // Struct corresponding to Model.NodePools[i]
@ -185,16 +182,18 @@ var hibernationTypes = map[string]attr.Type{
// Struct corresponding to Model.Extensions // Struct corresponding to Model.Extensions
type extensions struct { type extensions struct {
Argus types.Object `tfsdk:"argus"` Argus types.Object `tfsdk:"argus"`
ACL types.Object `tfsdk:"acl"` Observability types.Object `tfsdk:"observability"`
DNS types.Object `tfsdk:"dns"` ACL types.Object `tfsdk:"acl"`
DNS types.Object `tfsdk:"dns"`
} }
// Types corresponding to extensions // Types corresponding to extensions
var extensionsTypes = map[string]attr.Type{ var extensionsTypes = map[string]attr.Type{
"argus": basetypes.ObjectType{AttrTypes: argusTypes}, "argus": basetypes.ObjectType{AttrTypes: argusTypes},
"acl": basetypes.ObjectType{AttrTypes: aclTypes}, "observability": basetypes.ObjectType{AttrTypes: observabilityTypes},
"dns": basetypes.ObjectType{AttrTypes: dnsTypes}, "acl": basetypes.ObjectType{AttrTypes: aclTypes},
"dns": basetypes.ObjectType{AttrTypes: dnsTypes},
} }
// Struct corresponding to extensions.ACL // Struct corresponding to extensions.ACL
@ -221,6 +220,18 @@ var argusTypes = map[string]attr.Type{
"argus_instance_id": basetypes.StringType{}, "argus_instance_id": basetypes.StringType{},
} }
// Struct corresponding to extensions.Observability
type observability struct {
Enabled types.Bool `tfsdk:"enabled"`
InstanceId types.String `tfsdk:"instance_id"`
}
// Types corresponding to observability
var observabilityTypes = map[string]attr.Type{
"enabled": basetypes.BoolType{},
"instance_id": basetypes.StringType{},
}
// Struct corresponding to extensions.DNS // Struct corresponding to extensions.DNS
type dns struct { type dns struct {
Enabled types.Bool `tfsdk:"enabled"` Enabled types.Bool `tfsdk:"enabled"`
@ -353,33 +364,10 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
validate.VersionNumber(), validate.VersionNumber(),
}, },
}, },
"kubernetes_version": schema.StringAttribute{
Description: "Kubernetes version. Must only contain major and minor version (e.g. 1.22). This field is deprecated, use `kubernetes_version_min instead`",
Optional: true,
DeprecationMessage: "Use `kubernetes_version_min instead`. Setting a specific kubernetes version would cause errors during minor version upgrades due to forced updates. In those cases, this field might not represent the actual kubernetes version used in the cluster.",
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplaceIf(stringplanmodifier.RequiresReplaceIfFunc(func(_ context.Context, sr planmodifier.StringRequest, rrifr *stringplanmodifier.RequiresReplaceIfFuncResponse) {
if sr.StateValue.IsNull() || sr.PlanValue.IsNull() {
return
}
planVersion := fmt.Sprintf("v%s", sr.PlanValue.ValueString())
stateVersion := fmt.Sprintf("v%s", sr.StateValue.ValueString())
rrifr.RequiresReplace = semver.Compare(planVersion, stateVersion) < 0
}), "Kubernetes version", "If the Kubernetes version is a downgrade, the cluster will be replaced"),
},
Validators: []validator.String{
validate.MinorVersionNumber(),
},
},
"kubernetes_version_used": schema.StringAttribute{ "kubernetes_version_used": schema.StringAttribute{
Description: "Full Kubernetes version used. For example, if 1.22 was set in `kubernetes_version_min`, this value may result to 1.22.15. " + SKEUpdateDoc, Description: "Full Kubernetes version used. For example, if 1.22 was set in `kubernetes_version_min`, this value may result to 1.22.15. " + SKEUpdateDoc,
Computed: true, Computed: true,
}, },
"allow_privileged_containers": schema.BoolAttribute{
Description: "Flag to specify if privileged mode for containers is enabled or not.\nThis should be used with care since it also disables a couple of other features like the use of some volume type (e.g. PVCs).\nDeprecated as of Kubernetes 1.25 and later",
Optional: true,
},
"egress_address_ranges": schema.ListAttribute{ "egress_address_ranges": schema.ListAttribute{
Description: "The outgoing network ranges (in CIDR notation) of traffic originating from workload on the cluster.", Description: "The outgoing network ranges (in CIDR notation) of traffic originating from workload on the cluster.",
Computed: true, Computed: true,
@ -607,8 +595,9 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
}, },
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"argus": schema.SingleNestedAttribute{ "argus": schema.SingleNestedAttribute{
Description: "A single argus block as defined below.", Description: "A single argus block as defined below. This field is deprecated and will be removed 06 January 2026.",
Optional: true, DeprecationMessage: "Use observability instead.",
Optional: true,
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"enabled": schema.BoolAttribute{ "enabled": schema.BoolAttribute{
Description: "Flag to enable/disable Argus extensions.", Description: "Flag to enable/disable Argus extensions.",
@ -620,6 +609,20 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
}, },
}, },
}, },
"observability": schema.SingleNestedAttribute{
Description: "A single observability block as defined below.",
Optional: true,
Attributes: map[string]schema.Attribute{
"enabled": schema.BoolAttribute{
Description: "Flag to enable/disable Observability extensions.",
Required: true,
},
"instance_id": schema.StringAttribute{
Description: "Observability instance ID to choose which Observability instance is used. Required when enabled is set to `true`.",
Optional: true,
},
},
},
"acl": schema.SingleNestedAttribute{ "acl": schema.SingleNestedAttribute{
Description: "Cluster access control configuration.", Description: "Cluster access control configuration.",
Optional: true, Optional: true,
@ -669,43 +672,34 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
} }
} }
// ConfigValidators validate the resource configuration // The argus extension is deprecated but can still be used until it is removed on 06 January 2026.
func (r *clusterResource) ConfigValidators(_ context.Context) []resource.ConfigValidator { func (r *clusterResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
return []resource.ConfigValidator{ var resourceModel Model
// will raise an error if both fields are set simultaneously resp.Diagnostics.Append(req.Config.Get(ctx, &resourceModel)...)
resourcevalidator.Conflicting( if resp.Diagnostics.HasError() {
path.MatchRoot("kubernetes_version"), return
path.MatchRoot("kubernetes_version_min"),
),
} }
// function is used in order to be able to write easier unit tests
validateConfig(ctx, &resp.Diagnostics, &resourceModel)
} }
// needs to be executed inside the Create and Update methods func validateConfig(ctx context.Context, respDiags *diag.Diagnostics, model *Model) {
// since ValidateConfig runs before variables are rendered to their value, // If no extensions are configured, return without error.
// which causes errors like this: https://github.com/stackitcloud/terraform-provider-stackit/issues/201 if utils.IsUndefined(model.Extensions) {
func checkAllowPrivilegedContainers(allowPrivilegeContainers types.Bool, kubernetesVersion types.String) diag.Diagnostics { return
var diags diag.Diagnostics
// if kubernetesVersion is null, the latest one will be used and allowPriviledgeContainers will not be supported
if kubernetesVersion.IsNull() {
if !allowPrivilegeContainers.IsNull() {
diags.AddError("'Allow privilege containers' deprecated", "This field is deprecated as of Kubernetes 1.25 and later. Please remove this field")
}
return diags
} }
comparison := semver.Compare(fmt.Sprintf("v%s", kubernetesVersion.ValueString()), "v1.25") extensions := &extensions{}
if comparison < 0 { diags := model.Extensions.As(ctx, extensions, basetypes.ObjectAsOptions{})
if allowPrivilegeContainers.IsNull() { respDiags.Append(diags...)
diags.AddError("'Allow privilege containers' missing", "This field is required for Kubernetes prior to 1.25") if respDiags.HasError() {
} return
} else {
if !allowPrivilegeContainers.IsNull() {
diags.AddError("'Allow privilege containers' deprecated", "This field is deprecated as of Kubernetes 1.25 and later. Please remove this field")
}
} }
return diags if !utils.IsUndefined(extensions.Argus) && !utils.IsUndefined(extensions.Observability) {
core.LogAndAddError(ctx, respDiags, "Error configuring cluster", "You cannot provide both the `argus` and `observability` extension fields simultaneously. Please remove the deprecated `argus` field, and use `observability`.")
}
} }
// Create creates the resource and sets the initial Terraform state. // Create creates the resource and sets the initial Terraform state.
@ -717,17 +711,6 @@ func (r *clusterResource) Create(ctx context.Context, req resource.CreateRequest
return return
} }
kubernetesVersion := model.KubernetesVersionMin
// needed for backwards compatibility following kubernetes_version field deprecation
if kubernetesVersion.IsNull() {
kubernetesVersion = model.KubernetesVersion
}
diags = checkAllowPrivilegedContainers(model.AllowPrivilegedContainers, kubernetesVersion)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
projectId := model.ProjectId.ValueString() projectId := model.ProjectId.ValueString()
region := model.Region.ValueString() region := model.Region.ValueString()
clusterName := model.Name.ValueString() clusterName := model.Name.ValueString()
@ -748,7 +731,7 @@ func (r *clusterResource) Create(ctx context.Context, req resource.CreateRequest
return return
} }
availableKubernetesVersions, availableMachines, err := r.loadAvailableVersions(ctx) availableKubernetesVersions, availableMachines, err := r.loadAvailableVersions(ctx, region)
if err != nil { if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating cluster", fmt.Sprintf("Loading available Kubernetes and machine image versions: %v", err)) core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating cluster", fmt.Sprintf("Loading available Kubernetes and machine image versions: %v", err))
return return
@ -795,9 +778,9 @@ func sortK8sVersions(versions []ske.KubernetesVersion) {
// loadAvailableVersions loads the available k8s and machine versions from the API. // loadAvailableVersions loads the available k8s and machine versions from the API.
// The k8s versions are sorted descending order, i.e. the latest versions (including previews) // The k8s versions are sorted descending order, i.e. the latest versions (including previews)
// are listed first // are listed first
func (r *clusterResource) loadAvailableVersions(ctx context.Context) ([]ske.KubernetesVersion, []ske.MachineImage, error) { func (r *clusterResource) loadAvailableVersions(ctx context.Context, region string) ([]ske.KubernetesVersion, []ske.MachineImage, error) {
c := r.skeClient c := r.skeClient
res, err := c.ListProviderOptions(ctx).Execute() res, err := c.ListProviderOptions(ctx, region).Execute()
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("calling API: %w", err) return nil, nil, fmt.Errorf("calling API: %w", err)
} }
@ -817,7 +800,7 @@ func (r *clusterResource) loadAvailableVersions(ctx context.Context) ([]ske.Kube
// a map with the machine image for each nodepool, which can be used to check the current machine image versions. // a map with the machine image for each nodepool, which can be used to check the current machine image versions.
// if the cluster doesn't exist or some error occurs, returns nil for both // if the cluster doesn't exist or some error occurs, returns nil for both
func getCurrentVersions(ctx context.Context, c skeClient, m *Model) (kubernetesVersion *string, nodePoolMachineImages map[string]*ske.Image) { func getCurrentVersions(ctx context.Context, c skeClient, m *Model) (kubernetesVersion *string, nodePoolMachineImages map[string]*ske.Image) {
res, err := c.GetClusterExecute(ctx, m.ProjectId.ValueString(), m.Name.ValueString()) res, err := c.GetClusterExecute(ctx, m.ProjectId.ValueString(), m.Region.ValueString(), m.Name.ValueString())
if err != nil || res == nil { if err != nil || res == nil {
return nil, nil return nil, nil
} }
@ -891,19 +874,19 @@ func (r *clusterResource) createOrUpdateCluster(ctx context.Context, diags *diag
Network: network, Network: network,
Nodepools: &nodePools, Nodepools: &nodePools,
} }
_, err = r.skeClient.CreateOrUpdateCluster(ctx, projectId, name).CreateOrUpdateClusterPayload(payload).Execute() _, err = r.skeClient.CreateOrUpdateCluster(ctx, projectId, region, name).CreateOrUpdateClusterPayload(payload).Execute()
if err != nil { if err != nil {
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Calling API: %v", err)) core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Calling API: %v", err))
return return
} }
waitResp, err := skeWait.CreateOrUpdateClusterWaitHandler(ctx, r.skeClient, projectId, name).WaitWithContext(ctx) waitResp, err := skeWait.CreateOrUpdateClusterWaitHandler(ctx, r.skeClient, projectId, region, name).WaitWithContext(ctx)
if err != nil { if err != nil {
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Cluster creation waiting: %v", err)) core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Cluster creation waiting: %v", err))
return return
} }
if waitResp.Status.Error != nil && waitResp.Status.Error.Message != nil && *waitResp.Status.Error.Code == ske.RUNTIMEERRORCODE_ARGUS_INSTANCE_NOT_FOUND { if waitResp.Status.Error != nil && waitResp.Status.Error.Message != nil && *waitResp.Status.Error.Code == ske.RUNTIMEERRORCODE_OBSERVABILITY_INSTANCE_NOT_FOUND {
core.LogAndAddWarning(ctx, diags, "Warning during creating/updating cluster", fmt.Sprintf("Cluster is in Impaired state due to an invalid argus instance id, the cluster is usable but metrics won't be forwarded: %s", *waitResp.Status.Error.Message)) core.LogAndAddWarning(ctx, diags, "Warning during creating/updating cluster", fmt.Sprintf("Cluster is in Impaired state due to an invalid observability instance id, the cluster is usable but metrics won't be forwarded: %s", *waitResp.Status.Error.Message))
} }
err = mapFields(ctx, waitResp, model, region) err = mapFields(ctx, waitResp, model, region)
@ -1242,8 +1225,20 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error)
} }
} }
var skeArgus *ske.Argus var skeObservability *ske.Observability
if !(ex.Argus.IsNull() || ex.Argus.IsUnknown()) { if !utils.IsUndefined(ex.Observability) {
observability := observability{}
diags = ex.Observability.As(ctx, &observability, basetypes.ObjectAsOptions{})
if diags.HasError() {
return nil, fmt.Errorf("converting extensions.observability object: %v", diags.Errors())
}
observabilityEnabled := conversion.BoolValueToPointer(observability.Enabled)
observabilityInstanceId := conversion.StringValueToPointer(observability.InstanceId)
skeObservability = &ske.Observability{
Enabled: observabilityEnabled,
InstanceId: observabilityInstanceId,
}
} else if !utils.IsUndefined(ex.Argus) { // Fallback to deprecated argus
argus := argus{} argus := argus{}
diags = ex.Argus.As(ctx, &argus, basetypes.ObjectAsOptions{}) diags = ex.Argus.As(ctx, &argus, basetypes.ObjectAsOptions{})
if diags.HasError() { if diags.HasError() {
@ -1251,9 +1246,9 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error)
} }
argusEnabled := conversion.BoolValueToPointer(argus.Enabled) argusEnabled := conversion.BoolValueToPointer(argus.Enabled)
argusInstanceId := conversion.StringValueToPointer(argus.ArgusInstanceId) argusInstanceId := conversion.StringValueToPointer(argus.ArgusInstanceId)
skeArgus = &ske.Argus{ skeObservability = &ske.Observability{
Enabled: argusEnabled, Enabled: argusEnabled,
ArgusInstanceId: argusInstanceId, InstanceId: argusInstanceId,
} }
} }
@ -1278,9 +1273,9 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error)
} }
return &ske.Extension{ return &ske.Extension{
Acl: skeAcl, Acl: skeAcl,
Argus: skeArgus, Observability: skeObservability,
Dns: skeDNS, Dns: skeDNS,
}, nil }, nil
} }
@ -1372,7 +1367,6 @@ func mapFields(ctx context.Context, cl *ske.Cluster, m *Model, region string) er
if cl.Kubernetes != nil { if cl.Kubernetes != nil {
m.KubernetesVersionUsed = types.StringPointerValue(cl.Kubernetes.Version) m.KubernetesVersionUsed = types.StringPointerValue(cl.Kubernetes.Version)
m.AllowPrivilegedContainers = types.BoolPointerValue(cl.Kubernetes.AllowPrivilegedContainers)
} }
m.EgressAddressRanges = types.ListNull(types.StringType) m.EgressAddressRanges = types.ListNull(types.StringType)
@ -1706,7 +1700,7 @@ func getMaintenanceTimes(ctx context.Context, cl *ske.Cluster, m *Model) (startT
return startTime, endTime, nil return startTime, endTime, nil
} }
func checkDisabledExtensions(ctx context.Context, ex extensions) (aclDisabled, argusDisabled, dnsDisabled bool, err error) { func checkDisabledExtensions(ctx context.Context, ex *extensions) (aclDisabled, observabilityDisabled, dnsDisabled bool, err error) {
var diags diag.Diagnostics var diags diag.Diagnostics
acl := acl{} acl := acl{}
if ex.ACL.IsNull() { if ex.ACL.IsNull() {
@ -1718,14 +1712,22 @@ func checkDisabledExtensions(ctx context.Context, ex extensions) (aclDisabled, a
} }
} }
argus := argus{} observability := observability{}
if ex.Argus.IsNull() { if ex.Argus.IsNull() && ex.Observability.IsNull() {
argus.Enabled = types.BoolValue(false) observability.Enabled = types.BoolValue(false)
} else { } else if !ex.Argus.IsNull() {
argus := argus{}
diags = ex.Argus.As(ctx, &argus, basetypes.ObjectAsOptions{}) diags = ex.Argus.As(ctx, &argus, basetypes.ObjectAsOptions{})
if diags.HasError() { if diags.HasError() {
return false, false, false, fmt.Errorf("converting extensions.argus object: %v", diags.Errors()) return false, false, false, fmt.Errorf("converting extensions.argus object: %v", diags.Errors())
} }
observability.Enabled = argus.Enabled
observability.InstanceId = argus.ArgusInstanceId
} else {
diags = ex.Observability.As(ctx, &observability, basetypes.ObjectAsOptions{})
if diags.HasError() {
return false, false, false, fmt.Errorf("converting extensions.observability object: %v", diags.Errors())
}
} }
dns := dns{} dns := dns{}
@ -1738,7 +1740,7 @@ func checkDisabledExtensions(ctx context.Context, ex extensions) (aclDisabled, a
} }
} }
return !acl.Enabled.ValueBool(), !argus.Enabled.ValueBool(), !dns.Enabled.ValueBool(), nil return !acl.Enabled.ValueBool(), !observability.Enabled.ValueBool(), !dns.Enabled.ValueBool(), nil
} }
func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error { func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error {
@ -1765,12 +1767,12 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error {
// If we parse that object into the terraform model, it will produce an inconsistent result after apply // If we parse that object into the terraform model, it will produce an inconsistent result after apply
// error // error
aclDisabled, argusDisabled, dnsDisabled, err := checkDisabledExtensions(ctx, ex) aclDisabled, observabilityDisabled, dnsDisabled, err := checkDisabledExtensions(ctx, &ex)
if err != nil { if err != nil {
return fmt.Errorf("checking if extensions are disabled: %w", err) return fmt.Errorf("checking if extensions are disabled: %w", err)
} }
disabledExtensions := false disabledExtensions := false
if aclDisabled && argusDisabled && dnsDisabled { if aclDisabled && observabilityDisabled && dnsDisabled {
disabledExtensions = true disabledExtensions = true
} }
@ -1807,29 +1809,55 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error {
aclExtension = ex.ACL aclExtension = ex.ACL
} }
// Deprecated: argus won't be received from backend. Use observabilty instead.
argusExtension := types.ObjectNull(argusTypes) argusExtension := types.ObjectNull(argusTypes)
if cl.Extensions.Argus != nil { observabilityExtension := types.ObjectNull(observabilityTypes)
if cl.Extensions.Observability != nil {
enabled := types.BoolNull() enabled := types.BoolNull()
if cl.Extensions.Argus.Enabled != nil { if cl.Extensions.Observability.Enabled != nil {
enabled = types.BoolValue(*cl.Extensions.Argus.Enabled) enabled = types.BoolValue(*cl.Extensions.Observability.Enabled)
} }
argusInstanceId := types.StringNull() observabilityInstanceId := types.StringNull()
if cl.Extensions.Argus.ArgusInstanceId != nil { if cl.Extensions.Observability.InstanceId != nil {
argusInstanceId = types.StringValue(*cl.Extensions.Argus.ArgusInstanceId) observabilityInstanceId = types.StringValue(*cl.Extensions.Observability.InstanceId)
}
observabilityExtensionValues := map[string]attr.Value{
"enabled": enabled,
"instance_id": observabilityInstanceId,
} }
argusExtensionValues := map[string]attr.Value{ argusExtensionValues := map[string]attr.Value{
"enabled": enabled, "enabled": enabled,
"argus_instance_id": argusInstanceId, "argus_instance_id": observabilityInstanceId,
} }
observabilityExtension, diags = types.ObjectValue(observabilityTypes, observabilityExtensionValues)
if diags.HasError() {
return fmt.Errorf("creating observability extension: %w", core.DiagsToError(diags))
}
argusExtension, diags = types.ObjectValue(argusTypes, argusExtensionValues)
if diags.HasError() {
return fmt.Errorf("creating argus extension: %w", core.DiagsToError(diags))
}
} else if observabilityDisabled && !ex.Observability.IsNull() {
observabilityExtension = ex.Observability
// set deprecated argus extension
observability := observability{}
diags = ex.Observability.As(ctx, &observability, basetypes.ObjectAsOptions{})
if diags.HasError() {
return fmt.Errorf("converting extensions.observability object: %v", diags.Errors())
}
argusExtensionValues := map[string]attr.Value{
"enabled": observability.Enabled,
"argus_instance_id": observability.InstanceId,
}
argusExtension, diags = types.ObjectValue(argusTypes, argusExtensionValues) argusExtension, diags = types.ObjectValue(argusTypes, argusExtensionValues)
if diags.HasError() { if diags.HasError() {
return fmt.Errorf("creating argus extension: %w", core.DiagsToError(diags)) return fmt.Errorf("creating argus extension: %w", core.DiagsToError(diags))
} }
} else if argusDisabled && !ex.Argus.IsNull() {
argusExtension = ex.Argus
} }
dnsExtension := types.ObjectNull(dnsTypes) dnsExtension := types.ObjectNull(dnsTypes)
@ -1857,10 +1885,23 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error {
dnsExtension = ex.DNS dnsExtension = ex.DNS
} }
extensionsValues := map[string]attr.Value{ // Deprecation: Argus was renamed to observability. Depending on which attribute was used in the terraform config the
"acl": aclExtension, // according one has to be set here.
"argus": argusExtension, var extensionsValues map[string]attr.Value
"dns": dnsExtension, if utils.IsUndefined(ex.Argus) {
extensionsValues = map[string]attr.Value{
"acl": aclExtension,
"argus": types.ObjectNull(argusTypes),
"observability": observabilityExtension,
"dns": dnsExtension,
}
} else {
extensionsValues = map[string]attr.Value{
"acl": aclExtension,
"argus": argusExtension,
"observability": types.ObjectNull(observabilityTypes),
"dns": dnsExtension,
}
} }
extensions, diags := types.ObjectValue(extensionsTypes, extensionsValues) extensions, diags := types.ObjectValue(extensionsTypes, extensionsValues)
@ -1873,22 +1914,13 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error {
func toKubernetesPayload(m *Model, availableVersions []ske.KubernetesVersion, currentKubernetesVersion *string, diags *diag.Diagnostics) (kubernetesPayload *ske.Kubernetes, hasDeprecatedVersion bool, err error) { func toKubernetesPayload(m *Model, availableVersions []ske.KubernetesVersion, currentKubernetesVersion *string, diags *diag.Diagnostics) (kubernetesPayload *ske.Kubernetes, hasDeprecatedVersion bool, err error) {
providedVersionMin := m.KubernetesVersionMin.ValueStringPointer() providedVersionMin := m.KubernetesVersionMin.ValueStringPointer()
if !m.KubernetesVersion.IsNull() {
// kubernetes_version field deprecation
// this if clause should be removed once kubernetes_version field is completely removed
// kubernetes_version field value is used as minimum kubernetes version
providedVersionMin = conversion.StringValueToPointer(m.KubernetesVersion)
}
versionUsed, hasDeprecatedVersion, err := latestMatchingKubernetesVersion(availableVersions, providedVersionMin, currentKubernetesVersion, diags) versionUsed, hasDeprecatedVersion, err := latestMatchingKubernetesVersion(availableVersions, providedVersionMin, currentKubernetesVersion, diags)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("getting latest matching kubernetes version: %w", err) return nil, false, fmt.Errorf("getting latest matching kubernetes version: %w", err)
} }
k := &ske.Kubernetes{ k := &ske.Kubernetes{
Version: versionUsed, Version: versionUsed,
AllowPrivilegedContainers: conversion.BoolValueToPointer(m.AllowPrivilegedContainers),
} }
return k, hasDeprecatedVersion, nil return k, hasDeprecatedVersion, nil
} }
@ -2069,7 +2101,7 @@ func (r *clusterResource) Read(ctx context.Context, req resource.ReadRequest, re
ctx = tflog.SetField(ctx, "name", name) ctx = tflog.SetField(ctx, "name", name)
ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "region", region)
clResp, err := r.skeClient.GetCluster(ctx, projectId, name).Execute() clResp, err := r.skeClient.GetCluster(ctx, projectId, region, name).Execute()
if err != nil { if err != nil {
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
if ok && oapiErr.StatusCode == http.StatusNotFound { if ok && oapiErr.StatusCode == http.StatusNotFound {
@ -2102,18 +2134,6 @@ func (r *clusterResource) Update(ctx context.Context, req resource.UpdateRequest
return return
} }
kubernetesVersion := model.KubernetesVersionMin
// needed for backwards compatibility following kubernetes_version field deprecation
if kubernetesVersion.IsNull() {
kubernetesVersion = model.KubernetesVersion
}
diags = checkAllowPrivilegedContainers(model.AllowPrivilegedContainers, kubernetesVersion)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
projectId := model.ProjectId.ValueString() projectId := model.ProjectId.ValueString()
clName := model.Name.ValueString() clName := model.Name.ValueString()
region := model.Region.ValueString() region := model.Region.ValueString()
@ -2121,7 +2141,7 @@ func (r *clusterResource) Update(ctx context.Context, req resource.UpdateRequest
ctx = tflog.SetField(ctx, "name", clName) ctx = tflog.SetField(ctx, "name", clName)
ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "region", region)
availableKubernetesVersions, availableMachines, err := r.loadAvailableVersions(ctx) availableKubernetesVersions, availableMachines, err := r.loadAvailableVersions(ctx, region)
if err != nil { if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating cluster", fmt.Sprintf("Loading available Kubernetes and machine image versions: %v", err)) core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating cluster", fmt.Sprintf("Loading available Kubernetes and machine image versions: %v", err))
return return
@ -2156,12 +2176,12 @@ func (r *clusterResource) Delete(ctx context.Context, req resource.DeleteRequest
ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "region", region)
c := r.skeClient c := r.skeClient
_, err := c.DeleteCluster(ctx, projectId, name).Execute() _, err := c.DeleteCluster(ctx, projectId, region, name).Execute()
if err != nil { if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting cluster", fmt.Sprintf("Calling API: %v", err)) core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting cluster", fmt.Sprintf("Calling API: %v", err))
return return
} }
_, err = skeWait.DeleteClusterWaitHandler(ctx, r.skeClient, projectId, name).WaitWithContext(ctx) _, err = skeWait.DeleteClusterWaitHandler(ctx, r.skeClient, projectId, region, name).WaitWithContext(ctx)
if err != nil { if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting cluster", fmt.Sprintf("Cluster deletion waiting: %v", err)) core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting cluster", fmt.Sprintf("Cluster deletion waiting: %v", err))
return return

View file

@ -23,7 +23,9 @@ type skeClientMocked struct {
getClusterResp *ske.Cluster getClusterResp *ske.Cluster
} }
func (c *skeClientMocked) GetClusterExecute(_ context.Context, _, _ string) (*ske.Cluster, error) { const testRegion = "region"
func (c *skeClientMocked) GetClusterExecute(_ context.Context, _, _, _ string) (*ske.Cluster, error) {
if c.returnError { if c.returnError {
return nil, fmt.Errorf("get cluster failed") return nil, fmt.Errorf("get cluster failed")
} }
@ -33,7 +35,6 @@ func (c *skeClientMocked) GetClusterExecute(_ context.Context, _, _ string) (*sk
func TestMapFields(t *testing.T) { func TestMapFields(t *testing.T) {
cs := ske.ClusterStatusState("OK") cs := ske.ClusterStatusState("OK")
const testRegion = "region"
tests := []struct { tests := []struct {
description string description string
stateExtensions types.Object stateExtensions types.Object
@ -52,19 +53,17 @@ func TestMapFields(t *testing.T) {
}, },
testRegion, testRegion,
Model{ Model{
Id: types.StringValue("pid,region,name"), Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"), Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(), NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
AllowPrivilegedContainers: types.BoolNull(), Maintenance: types.ObjectNull(maintenanceTypes),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}), Network: types.ObjectNull(networkTypes),
Maintenance: types.ObjectNull(maintenanceTypes), Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Network: types.ObjectNull(networkTypes), Extensions: types.ObjectNull(extensionsTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}), EgressAddressRanges: types.ListNull(types.StringType),
Extensions: types.ObjectNull(extensionsTypes), PodAddressRanges: types.ListNull(types.StringType),
EgressAddressRanges: types.ListNull(types.StringType), Region: types.StringValue(testRegion),
PodAddressRanges: types.ListNull(types.StringType),
Region: types.StringValue(testRegion),
}, },
true, true,
}, },
@ -78,9 +77,9 @@ func TestMapFields(t *testing.T) {
AllowedCidrs: &[]string{"cidr1"}, AllowedCidrs: &[]string{"cidr1"},
Enabled: utils.Ptr(true), Enabled: utils.Ptr(true),
}, },
Argus: &ske.Argus{ Observability: &ske.Observability{
ArgusInstanceId: utils.Ptr("aid"), InstanceId: utils.Ptr("aid"),
Enabled: utils.Ptr(true), Enabled: utils.Ptr(true),
}, },
Dns: &ske.DNS{ Dns: &ske.DNS{
Zones: &[]string{"foo.onstackit.cloud"}, Zones: &[]string{"foo.onstackit.cloud"},
@ -97,8 +96,7 @@ func TestMapFields(t *testing.T) {
}, },
}, },
Kubernetes: &ske.Kubernetes{ Kubernetes: &ske.Kubernetes{
AllowPrivilegedContainers: utils.Ptr(true), Version: utils.Ptr("1.2.3"),
Version: utils.Ptr("1.2.3"),
}, },
Maintenance: &ske.Maintenance{ Maintenance: &ske.Maintenance{
AutoUpdate: &ske.MaintenanceAutoUpdate{ AutoUpdate: &ske.MaintenanceAutoUpdate{
@ -157,12 +155,10 @@ func TestMapFields(t *testing.T) {
}, },
testRegion, testRegion,
Model{ Model{
Id: types.StringValue("pid,region,name"), Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"), Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(), KubernetesVersionUsed: types.StringValue("1.2.3"),
KubernetesVersionUsed: types.StringValue("1.2.3"),
AllowPrivilegedContainers: types.BoolValue(true),
EgressAddressRanges: types.ListValueMust( EgressAddressRanges: types.ListValueMust(
types.StringType, types.StringType,
[]attr.Value{ []attr.Value{
@ -256,9 +252,10 @@ func TestMapFields(t *testing.T) {
types.StringValue("cidr1"), types.StringValue("cidr1"),
}), }),
}), }),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{ "argus": types.ObjectNull(argusTypes),
"enabled": types.BoolValue(true), "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"argus_instance_id": types.StringValue("aid"), "enabled": types.BoolValue(true),
"instance_id": types.StringValue("aid"),
}), }),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{ "dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(true), "enabled": types.BoolValue(true),
@ -281,19 +278,17 @@ func TestMapFields(t *testing.T) {
}, },
testRegion, testRegion,
Model{ Model{
Id: types.StringValue("pid,region,name"), Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"), Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(), NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
AllowPrivilegedContainers: types.BoolNull(), Maintenance: types.ObjectNull(maintenanceTypes),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}), Network: types.ObjectNull(networkTypes),
Maintenance: types.ObjectNull(maintenanceTypes), Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Network: types.ObjectNull(networkTypes), Extensions: types.ObjectNull(extensionsTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}), EgressAddressRanges: types.ListNull(types.StringType),
Extensions: types.ObjectNull(extensionsTypes), PodAddressRanges: types.ListNull(types.StringType),
EgressAddressRanges: types.ListNull(types.StringType), Region: types.StringValue(testRegion),
PodAddressRanges: types.ListNull(types.StringType),
Region: types.StringValue(testRegion),
}, },
true, true,
}, },
@ -307,9 +302,9 @@ func TestMapFields(t *testing.T) {
AllowedCidrs: nil, AllowedCidrs: nil,
Enabled: utils.Ptr(true), Enabled: utils.Ptr(true),
}, },
Argus: &ske.Argus{ Observability: &ske.Observability{
ArgusInstanceId: nil, InstanceId: nil,
Enabled: utils.Ptr(true), Enabled: utils.Ptr(true),
}, },
Dns: &ske.DNS{ Dns: &ske.DNS{
Zones: nil, Zones: nil,
@ -320,24 +315,23 @@ func TestMapFields(t *testing.T) {
}, },
testRegion, testRegion,
Model{ Model{
Id: types.StringValue("pid,region,name"), Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"), Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(), NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
AllowPrivilegedContainers: types.BoolNull(), Maintenance: types.ObjectNull(maintenanceTypes),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}), Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Maintenance: types.ObjectNull(maintenanceTypes), EgressAddressRanges: types.ListNull(types.StringType),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}), PodAddressRanges: types.ListNull(types.StringType),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{ Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{ "acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{
"enabled": types.BoolValue(true), "enabled": types.BoolValue(true),
"allowed_cidrs": types.ListNull(types.StringType), "allowed_cidrs": types.ListNull(types.StringType),
}), }),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{ "argus": types.ObjectNull(argusTypes),
"enabled": types.BoolValue(true), "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"argus_instance_id": types.StringNull(), "enabled": types.BoolValue(true),
"instance_id": types.StringNull(),
}), }),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{ "dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(true), "enabled": types.BoolValue(true),
@ -355,9 +349,10 @@ func TestMapFields(t *testing.T) {
"enabled": types.BoolValue(false), "enabled": types.BoolValue(false),
"allowed_cidrs": types.ListNull(types.StringType), "allowed_cidrs": types.ListNull(types.StringType),
}), }),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{ "argus": types.ObjectNull(argusTypes),
"enabled": types.BoolValue(false), "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"argus_instance_id": types.StringNull(), "enabled": types.BoolValue(false),
"instance_id": types.StringNull(),
}), }),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{ "dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(false), "enabled": types.BoolValue(false),
@ -371,24 +366,23 @@ func TestMapFields(t *testing.T) {
}, },
testRegion, testRegion,
Model{ Model{
Id: types.StringValue("pid,region,name"), Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"), Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(), NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
AllowPrivilegedContainers: types.BoolNull(), Maintenance: types.ObjectNull(maintenanceTypes),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}), Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Maintenance: types.ObjectNull(maintenanceTypes), EgressAddressRanges: types.ListNull(types.StringType),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}), PodAddressRanges: types.ListNull(types.StringType),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{ Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{ "acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{
"enabled": types.BoolValue(false), "enabled": types.BoolValue(false),
"allowed_cidrs": types.ListNull(types.StringType), "allowed_cidrs": types.ListNull(types.StringType),
}), }),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{ "argus": types.ObjectNull(argusTypes),
"enabled": types.BoolValue(false), "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"argus_instance_id": types.StringNull(), "enabled": types.BoolValue(false),
"instance_id": types.StringNull(),
}), }),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{ "dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(false), "enabled": types.BoolValue(false),
@ -400,7 +394,7 @@ func TestMapFields(t *testing.T) {
true, true,
}, },
{ {
"extensions_only_argus_disabled", "extensions_only_observability_disabled",
types.ObjectValueMust(extensionsTypes, map[string]attr.Value{ types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{ "acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{
"enabled": types.BoolValue(true), "enabled": types.BoolValue(true),
@ -408,9 +402,10 @@ func TestMapFields(t *testing.T) {
types.StringValue("cidr1"), types.StringValue("cidr1"),
}), }),
}), }),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{ "argus": types.ObjectNull(argusTypes),
"enabled": types.BoolValue(false), "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"argus_instance_id": types.StringValue("id"), "enabled": types.BoolValue(false),
"instance_id": types.StringValue("id"),
}), }),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{ "dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(true), "enabled": types.BoolValue(true),
@ -433,16 +428,14 @@ func TestMapFields(t *testing.T) {
}, },
testRegion, testRegion,
Model{ Model{
Id: types.StringValue("pid,region,name"), Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"), Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(), NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
AllowPrivilegedContainers: types.BoolNull(), Maintenance: types.ObjectNull(maintenanceTypes),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}), Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Maintenance: types.ObjectNull(maintenanceTypes), EgressAddressRanges: types.ListNull(types.StringType),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}), PodAddressRanges: types.ListNull(types.StringType),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{ Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{ "acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{
"enabled": types.BoolValue(true), "enabled": types.BoolValue(true),
@ -450,9 +443,10 @@ func TestMapFields(t *testing.T) {
types.StringValue("cidr1"), types.StringValue("cidr1"),
}), }),
}), }),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{ "argus": types.ObjectNull(argusTypes),
"enabled": types.BoolValue(false), "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"argus_instance_id": types.StringValue("id"), "enabled": types.BoolValue(false),
"instance_id": types.StringValue("id"),
}), }),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{ "dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(true), "enabled": types.BoolValue(true),
@ -473,18 +467,16 @@ func TestMapFields(t *testing.T) {
}, },
testRegion, testRegion,
Model{ Model{
Id: types.StringValue("pid,region,name"), Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"), Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(), NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
AllowPrivilegedContainers: types.BoolNull(), Maintenance: types.ObjectNull(maintenanceTypes),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}), Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Maintenance: types.ObjectNull(maintenanceTypes), Extensions: types.ObjectNull(extensionsTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}), EgressAddressRanges: types.ListNull(types.StringType),
Extensions: types.ObjectNull(extensionsTypes), PodAddressRanges: types.ListNull(types.StringType),
EgressAddressRanges: types.ListNull(types.StringType), Region: types.StringValue(testRegion),
PodAddressRanges: types.ListNull(types.StringType),
Region: types.StringValue(testRegion),
}, },
true, true,
}, },
@ -535,9 +527,9 @@ func TestMapFields(t *testing.T) {
AllowedCidrs: &[]string{"cidr1"}, AllowedCidrs: &[]string{"cidr1"},
Enabled: utils.Ptr(true), Enabled: utils.Ptr(true),
}, },
Argus: &ske.Argus{ Observability: &ske.Observability{
ArgusInstanceId: utils.Ptr("aid"), InstanceId: utils.Ptr("aid"),
Enabled: utils.Ptr(true), Enabled: utils.Ptr(true),
}, },
Dns: &ske.DNS{ Dns: &ske.DNS{
Zones: &[]string{"zone1"}, Zones: &[]string{"zone1"},
@ -554,8 +546,7 @@ func TestMapFields(t *testing.T) {
}, },
}, },
Kubernetes: &ske.Kubernetes{ Kubernetes: &ske.Kubernetes{
AllowPrivilegedContainers: utils.Ptr(true), Version: utils.Ptr("1.2.3"),
Version: utils.Ptr("1.2.3"),
}, },
Maintenance: &ske.Maintenance{ Maintenance: &ske.Maintenance{
AutoUpdate: &ske.MaintenanceAutoUpdate{ AutoUpdate: &ske.MaintenanceAutoUpdate{
@ -605,14 +596,12 @@ func TestMapFields(t *testing.T) {
}, },
testRegion, testRegion,
Model{ Model{
Id: types.StringValue("pid,region,name"), Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"), Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(), KubernetesVersionUsed: types.StringValue("1.2.3"),
KubernetesVersionUsed: types.StringValue("1.2.3"), EgressAddressRanges: types.ListNull(types.StringType),
AllowPrivilegedContainers: types.BoolValue(true), PodAddressRanges: types.ListNull(types.StringType),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
NodePools: types.ListValueMust( NodePools: types.ListValueMust(
types.ObjectType{AttrTypes: nodePoolTypes}, types.ObjectType{AttrTypes: nodePoolTypes},
[]attr.Value{ []attr.Value{
@ -680,9 +669,10 @@ func TestMapFields(t *testing.T) {
types.StringValue("cidr1"), types.StringValue("cidr1"),
}), }),
}), }),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{ "argus": types.ObjectNull(argusTypes),
"enabled": types.BoolValue(true), "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"argus_instance_id": types.StringValue("aid"), "enabled": types.BoolValue(true),
"instance_id": types.StringValue("aid"),
}), }),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{ "dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(true), "enabled": types.BoolValue(true),
@ -1894,92 +1884,6 @@ func TestGetMaintenanceTimes(t *testing.T) {
} }
} }
func TestCheckAllowPrivilegedContainers(t *testing.T) {
tests := []struct {
description string
kubernetesVersion *string
allowPrivilegeContainers *bool
isValid bool
}{
{
description: "null_version_1_flag_deprecated",
kubernetesVersion: nil,
allowPrivilegeContainers: nil,
isValid: true,
},
{
description: "null_version_2_flag_deprecated",
kubernetesVersion: nil,
allowPrivilegeContainers: utils.Ptr(false),
isValid: false,
},
{
description: "flag_required_1",
kubernetesVersion: utils.Ptr("0.999.999"),
allowPrivilegeContainers: nil,
isValid: false,
},
{
description: "flag_required_2",
kubernetesVersion: utils.Ptr("0.999.999"),
allowPrivilegeContainers: utils.Ptr(false),
isValid: true,
},
{
description: "flag_required_3",
kubernetesVersion: utils.Ptr("1.24.999"),
allowPrivilegeContainers: nil,
isValid: false,
},
{
description: "flag_required_4",
kubernetesVersion: utils.Ptr("1.24.999"),
allowPrivilegeContainers: utils.Ptr(false),
isValid: true,
},
{
description: "flag_deprecated_1",
kubernetesVersion: utils.Ptr("1.25"),
allowPrivilegeContainers: nil,
isValid: true,
},
{
description: "flag_deprecated_2",
kubernetesVersion: utils.Ptr("1.25"),
allowPrivilegeContainers: utils.Ptr(false),
isValid: false,
},
{
description: "flag_deprecated_3",
kubernetesVersion: utils.Ptr("2.0.0"),
allowPrivilegeContainers: nil,
isValid: true,
},
{
description: "flag_deprecated_4",
kubernetesVersion: utils.Ptr("2.0.0"),
allowPrivilegeContainers: utils.Ptr(false),
isValid: false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
diags := checkAllowPrivilegedContainers(
types.BoolPointerValue(tt.allowPrivilegeContainers),
types.StringPointerValue(tt.kubernetesVersion),
)
if tt.isValid && diags.HasError() {
t.Errorf("checkAllowPrivilegedContainers failed on valid input: %v", core.DiagsToError(diags))
}
if !tt.isValid && !diags.HasError() {
t.Errorf("checkAllowPrivilegedContainers didn't fail on valid input")
}
})
}
}
func TestGetCurrentVersion(t *testing.T) { func TestGetCurrentVersion(t *testing.T) {
tests := []struct { tests := []struct {
description string description string
@ -2587,3 +2491,149 @@ func TestSortK8sVersion(t *testing.T) {
}) })
} }
} }
func TestValidateConfig(t *testing.T) {
tests := []struct {
name string
model *Model
wantErr bool
}{
{
name: "argus and observability null",
model: &Model{
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectNull(aclTypes),
"dns": types.ObjectNull(dnsTypes),
"argus": types.ObjectNull(argusTypes),
"observability": types.ObjectNull(observabilityTypes),
}),
},
wantErr: false,
},
{
name: "argus and observability unknown",
model: &Model{
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectNull(aclTypes),
"dns": types.ObjectNull(dnsTypes),
"argus": types.ObjectUnknown(argusTypes),
"observability": types.ObjectUnknown(observabilityTypes),
}),
},
wantErr: false,
},
{
name: "argus null and observability set",
model: &Model{
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectNull(aclTypes),
"dns": types.ObjectNull(dnsTypes),
"argus": types.ObjectNull(argusTypes),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"instance_id": types.StringValue("aid"),
}),
}),
},
wantErr: false,
},
{
name: "argus null and observability unknown",
model: &Model{
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectNull(aclTypes),
"dns": types.ObjectNull(dnsTypes),
"argus": types.ObjectNull(argusTypes),
"observability": types.ObjectUnknown(observabilityTypes),
}),
},
wantErr: false,
},
{
name: "argus set and observability null",
model: &Model{
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectNull(aclTypes),
"dns": types.ObjectNull(dnsTypes),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"argus_instance_id": types.StringValue("aid"),
}),
"observability": types.ObjectNull(observabilityTypes),
}),
},
wantErr: false,
},
{
name: "argus set and observability unknown",
model: &Model{
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectNull(aclTypes),
"dns": types.ObjectNull(dnsTypes),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"argus_instance_id": types.StringValue("aid"),
}),
"observability": types.ObjectUnknown(observabilityTypes),
}),
},
wantErr: false,
},
{
name: "argus and observability both set",
model: &Model{
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectNull(aclTypes),
"dns": types.ObjectNull(dnsTypes),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"argus_instance_id": types.StringValue("aid"),
}),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"instance_id": types.StringValue("aid"),
}),
}),
},
wantErr: true,
},
{
name: "argus unknown observability null",
model: &Model{
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectNull(aclTypes),
"dns": types.ObjectNull(dnsTypes),
"argus": types.ObjectUnknown(argusTypes),
"observability": types.ObjectNull(observabilityTypes),
}),
},
wantErr: false,
},
{
name: "argus unknown observability set",
model: &Model{
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectNull(aclTypes),
"dns": types.ObjectNull(dnsTypes),
"argus": types.ObjectUnknown(argusTypes),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"instance_id": types.StringValue("aid"),
}),
}),
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
diags := diag.Diagnostics{}
validateConfig(ctx, &diags, tt.model)
if diags.HasError() != tt.wantErr {
t.Errorf("validateConfig() = %v, want %v", diags.HasError(), tt.wantErr)
}
})
}
}

View file

@ -46,6 +46,7 @@ type Model struct {
Refresh types.Bool `tfsdk:"refresh"` Refresh types.Bool `tfsdk:"refresh"`
ExpiresAt types.String `tfsdk:"expires_at"` ExpiresAt types.String `tfsdk:"expires_at"`
CreationTime types.String `tfsdk:"creation_time"` CreationTime types.String `tfsdk:"creation_time"`
Region types.String `tfsdk:"region"`
} }
// NewKubeconfigResource is a helper function to simplify the provider implementation. // NewKubeconfigResource is a helper function to simplify the provider implementation.
@ -55,7 +56,8 @@ func NewKubeconfigResource() resource.Resource {
// kubeconfigResource is the resource implementation. // kubeconfigResource is the resource implementation.
type kubeconfigResource struct { type kubeconfigResource struct {
client *ske.APIClient client *ske.APIClient
providerData core.ProviderData
} }
// Metadata returns the resource type name. // Metadata returns the resource type name.
@ -65,12 +67,13 @@ func (r *kubeconfigResource) Metadata(_ context.Context, req resource.MetadataRe
// Configure adds the provider configured client to the resource. // Configure adds the provider configured client to the resource.
func (r *kubeconfigResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { func (r *kubeconfigResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) var ok bool
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
if !ok { if !ok {
return return
} }
apiClient := skeUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) apiClient := skeUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
@ -91,6 +94,7 @@ func (r *kubeconfigResource) Schema(_ context.Context, _ resource.SchemaRequest,
"expires_at": "Timestamp when the kubeconfig expires", "expires_at": "Timestamp when the kubeconfig expires",
"refresh": "If set to true, the provider will check if the kubeconfig has expired and will generated a new valid one in-place", "refresh": "If set to true, the provider will check if the kubeconfig has expired and will generated a new valid one in-place",
"creation_time": "Date-time when the kubeconfig was created", "creation_time": "Date-time when the kubeconfig was created",
"region": "The resource region. If not defined, the provider region is used.",
} }
resp.Schema = schema.Schema{ resp.Schema = schema.Schema{
@ -172,6 +176,15 @@ func (r *kubeconfigResource) Schema(_ context.Context, _ resource.SchemaRequest,
stringplanmodifier.UseStateForUnknown(), stringplanmodifier.UseStateForUnknown(),
}, },
}, },
"region": schema.StringAttribute{
Optional: true,
// must be computed to allow for storing the override value from the provider
Computed: true,
Description: descriptions["region"],
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
}, },
} }
} }
@ -183,6 +196,31 @@ func (r *kubeconfigResource) ModifyPlan(ctx context.Context, req resource.Modify
// Planned to create a kubeconfig // Planned to create a kubeconfig
core.LogAndAddWarning(ctx, &resp.Diagnostics, "Planned to create kubeconfig", "Once this resource is created, you will no longer be able to use the deprecated credentials endpoints and the kube_config field on the cluster resource will be empty for this cluster. For more info check How to Rotate SKE Credentials (https://docs.stackit.cloud/stackit/en/how-to-rotate-ske-credentials-200016334.html)") core.LogAndAddWarning(ctx, &resp.Diagnostics, "Planned to create kubeconfig", "Once this resource is created, you will no longer be able to use the deprecated credentials endpoints and the kube_config field on the cluster resource will be empty for this cluster. For more info check How to Rotate SKE Credentials (https://docs.stackit.cloud/stackit/en/how-to-rotate-ske-credentials-200016334.html)")
} }
var configModel Model
// skip initial empty configuration to avoid follow-up errors
if req.Config.Raw.IsNull() {
return
}
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
if resp.Diagnostics.HasError() {
return
}
var planModel Model
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
if resp.Diagnostics.HasError() {
return
}
utils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
if resp.Diagnostics.HasError() {
return
}
} }
// Create creates the resource and sets the initial Terraform state. // Create creates the resource and sets the initial Terraform state.
@ -196,12 +234,14 @@ func (r *kubeconfigResource) Create(ctx context.Context, req resource.CreateRequ
projectId := model.ProjectId.ValueString() projectId := model.ProjectId.ValueString()
clusterName := model.ClusterName.ValueString() clusterName := model.ClusterName.ValueString()
kubeconfigUUID := uuid.New().String() kubeconfigUUID := uuid.New().String()
region := model.Region.ValueString()
model.KubeconfigId = types.StringValue(kubeconfigUUID) model.KubeconfigId = types.StringValue(kubeconfigUUID)
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "cluster_name", clusterName) ctx = tflog.SetField(ctx, "cluster_name", clusterName)
ctx = tflog.SetField(ctx, "kube_config_id", kubeconfigUUID) ctx = tflog.SetField(ctx, "kube_config_id", kubeconfigUUID)
ctx = tflog.SetField(ctx, "region", region)
err := r.createKubeconfig(ctx, &model) err := r.createKubeconfig(ctx, &model)
if err != nil { if err != nil {
@ -234,11 +274,13 @@ func (r *kubeconfigResource) Read(ctx context.Context, req resource.ReadRequest,
projectId := model.ProjectId.ValueString() projectId := model.ProjectId.ValueString()
clusterName := model.ClusterName.ValueString() clusterName := model.ClusterName.ValueString()
kubeconfigUUID := model.KubeconfigId.ValueString() kubeconfigUUID := model.KubeconfigId.ValueString()
region := model.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "cluster_name", clusterName) ctx = tflog.SetField(ctx, "cluster_name", clusterName)
ctx = tflog.SetField(ctx, "kube_config_id", kubeconfigUUID) ctx = tflog.SetField(ctx, "kube_config_id", kubeconfigUUID)
ctx = tflog.SetField(ctx, "region", region)
cluster, err := r.client.GetClusterExecute(ctx, projectId, clusterName) cluster, err := r.client.GetClusterExecute(ctx, projectId, region, clusterName)
if err != nil { if err != nil {
utils.LogError( utils.LogError(
ctx, ctx,
@ -298,7 +340,7 @@ func (r *kubeconfigResource) createKubeconfig(ctx context.Context, model *Model)
return fmt.Errorf("creating API payload: %w", err) return fmt.Errorf("creating API payload: %w", err)
} }
// Create new kubeconfig // Create new kubeconfig
kubeconfigResp, err := r.client.CreateKubeconfig(ctx, model.ProjectId.ValueString(), model.ClusterName.ValueString()).CreateKubeconfigPayload(*payload).Execute() kubeconfigResp, err := r.client.CreateKubeconfig(ctx, model.ProjectId.ValueString(), model.Region.ValueString(), model.ClusterName.ValueString()).CreateKubeconfigPayload(*payload).Execute()
if err != nil { if err != nil {
return fmt.Errorf("calling API: %w", err) return fmt.Errorf("calling API: %w", err)
} }
@ -331,9 +373,11 @@ func (r *kubeconfigResource) Delete(ctx context.Context, req resource.DeleteRequ
projectId := model.ProjectId.ValueString() projectId := model.ProjectId.ValueString()
clusterName := model.ClusterName.ValueString() clusterName := model.ClusterName.ValueString()
kubeconfigUUID := model.KubeconfigId.ValueString() kubeconfigUUID := model.KubeconfigId.ValueString()
region := model.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "cluster_name", clusterName) ctx = tflog.SetField(ctx, "cluster_name", clusterName)
ctx = tflog.SetField(ctx, "kube_config_id", kubeconfigUUID) ctx = tflog.SetField(ctx, "kube_config_id", kubeconfigUUID)
ctx = tflog.SetField(ctx, "region", region)
// kubeconfig is deleted automatically from the state // kubeconfig is deleted automatically from the state
tflog.Info(ctx, "SKE kubeconfig deleted") tflog.Info(ctx, "SKE kubeconfig deleted")

View file

@ -74,7 +74,7 @@ var testConfigVarsMax = config.Variables{
"nodepool_volume_type": config.StringVariable("storage_premium_perf0"), "nodepool_volume_type": config.StringVariable("storage_premium_perf0"),
"ext_acl_enabled": config.StringVariable("true"), "ext_acl_enabled": config.StringVariable("true"),
"ext_acl_allowed_cidr1": config.StringVariable("10.0.100.0/24"), "ext_acl_allowed_cidr1": config.StringVariable("10.0.100.0/24"),
"ext_argus_enabled": config.StringVariable("false"), "ext_observability_enabled": config.StringVariable("false"),
"ext_dns_enabled": config.StringVariable("true"), "ext_dns_enabled": config.StringVariable("true"),
"nodepool_hibernations1_start": config.StringVariable("0 18 * * *"), "nodepool_hibernations1_start": config.StringVariable("0 18 * * *"),
"nodepool_hibernations1_end": config.StringVariable("59 23 * * *"), "nodepool_hibernations1_end": config.StringVariable("59 23 * * *"),
@ -199,7 +199,7 @@ func TestAccSKEMin(t *testing.T) {
ImportState: true, ImportState: true,
ImportStateVerify: true, ImportStateVerify: true,
// The fields are not provided in the SKE API when disabled, although set actively. // The fields are not provided in the SKE API when disabled, although set actively.
ImportStateVerifyIgnore: []string{"kubernetes_version_min", "node_pools.0.os_version_min", "extensions.argus.%", "extensions.argus.argus_instance_id", "extensions.argus.enabled"}, ImportStateVerifyIgnore: []string{"kubernetes_version_min", "node_pools.0.os_version_min", "extensions.observability.%", "extensions.observability.instance_id", "extensions.observability.enabled"},
}, },
// 4) Update kubernetes version, OS version and maintenance end, downgrade of kubernetes version // 4) Update kubernetes version, OS version and maintenance end, downgrade of kubernetes version
{ {
@ -269,7 +269,7 @@ func TestAccSKEMax(t *testing.T) {
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.enabled", testutil.ConvertConfigVariable(testConfigVarsMax["ext_acl_enabled"])), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.enabled", testutil.ConvertConfigVariable(testConfigVarsMax["ext_acl_enabled"])),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.#", "1"), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.#", "1"),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.0", testutil.ConvertConfigVariable(testConfigVarsMax["ext_acl_allowed_cidr1"])), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.0", testutil.ConvertConfigVariable(testConfigVarsMax["ext_acl_allowed_cidr1"])),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.argus.enabled", testutil.ConvertConfigVariable(testConfigVarsMax["ext_argus_enabled"])), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.observability.enabled", testutil.ConvertConfigVariable(testConfigVarsMax["ext_observability_enabled"])),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.enabled", testutil.ConvertConfigVariable(testConfigVarsMax["ext_dns_enabled"])), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.enabled", testutil.ConvertConfigVariable(testConfigVarsMax["ext_dns_enabled"])),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.zones.#", "1"), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.zones.#", "1"),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.zones.0", testutil.ConvertConfigVariable(testConfigVarsMax["dns_name"])), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.zones.0", testutil.ConvertConfigVariable(testConfigVarsMax["dns_name"])),
@ -341,7 +341,7 @@ func TestAccSKEMax(t *testing.T) {
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.acl.enabled", testutil.ConvertConfigVariable(testConfigVarsMax["ext_acl_enabled"])), resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.acl.enabled", testutil.ConvertConfigVariable(testConfigVarsMax["ext_acl_enabled"])),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.#", "1"), resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.#", "1"),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.0", testutil.ConvertConfigVariable(testConfigVarsMax["ext_acl_allowed_cidr1"])), resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.0", testutil.ConvertConfigVariable(testConfigVarsMax["ext_acl_allowed_cidr1"])),
// no check for argus, as it was disabled in the setup // no check for observability, as it was disabled in the setup
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.dns.enabled", testutil.ConvertConfigVariable(testConfigVarsMax["ext_dns_enabled"])), resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.dns.enabled", testutil.ConvertConfigVariable(testConfigVarsMax["ext_dns_enabled"])),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.dns.zones.#", "1"), resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.dns.zones.#", "1"),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.dns.zones.0", testutil.ConvertConfigVariable(testConfigVarsMax["dns_name"])), resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "extensions.dns.zones.0", testutil.ConvertConfigVariable(testConfigVarsMax["dns_name"])),
@ -388,7 +388,7 @@ func TestAccSKEMax(t *testing.T) {
ImportState: true, ImportState: true,
ImportStateVerify: true, ImportStateVerify: true,
// The fields are not provided in the SKE API when disabled, although set actively. // The fields are not provided in the SKE API when disabled, although set actively.
ImportStateVerifyIgnore: []string{"kubernetes_version_min", "node_pools.0.os_version_min", "extensions.argus.%", "extensions.argus.argus_instance_id", "extensions.argus.enabled"}, ImportStateVerifyIgnore: []string{"kubernetes_version_min", "node_pools.0.os_version_min", "extensions.observability.%", "extensions.observability.instance_id", "extensions.observability.enabled"},
}, },
// 4) Update kubernetes version, OS version and maintenance end, downgrade of kubernetes version // 4) Update kubernetes version, OS version and maintenance end, downgrade of kubernetes version
{ {
@ -424,7 +424,7 @@ func TestAccSKEMax(t *testing.T) {
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.enabled", testutil.ConvertConfigVariable(configVarsMaxUpdated()["ext_acl_enabled"])), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.enabled", testutil.ConvertConfigVariable(configVarsMaxUpdated()["ext_acl_enabled"])),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.#", "1"), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.#", "1"),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.0", testutil.ConvertConfigVariable(configVarsMaxUpdated()["ext_acl_allowed_cidr1"])), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.acl.allowed_cidrs.0", testutil.ConvertConfigVariable(configVarsMaxUpdated()["ext_acl_allowed_cidr1"])),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.argus.enabled", testutil.ConvertConfigVariable(configVarsMaxUpdated()["ext_argus_enabled"])), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.observability.enabled", testutil.ConvertConfigVariable(configVarsMaxUpdated()["ext_observability_enabled"])),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.enabled", testutil.ConvertConfigVariable(configVarsMaxUpdated()["ext_dns_enabled"])), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.enabled", testutil.ConvertConfigVariable(configVarsMaxUpdated()["ext_dns_enabled"])),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.zones.#", "1"), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.zones.#", "1"),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.zones.0", testutil.ConvertConfigVariable(configVarsMaxUpdated()["dns_name"])), resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "extensions.dns.zones.0", testutil.ConvertConfigVariable(configVarsMaxUpdated()["dns_name"])),
@ -458,9 +458,7 @@ func testAccCheckSKEDestroy(s *terraform.State) error {
var client *ske.APIClient var client *ske.APIClient
var err error var err error
if testutil.SKECustomEndpoint == "" { if testutil.SKECustomEndpoint == "" {
client, err = ske.NewAPIClient( client, err = ske.NewAPIClient()
coreConfig.WithRegion(testutil.Region),
)
} else { } else {
client, err = ske.NewAPIClient( client, err = ske.NewAPIClient(
coreConfig.WithEndpoint(testutil.SKECustomEndpoint), coreConfig.WithEndpoint(testutil.SKECustomEndpoint),
@ -480,7 +478,7 @@ func testAccCheckSKEDestroy(s *terraform.State) error {
clustersToDestroy = append(clustersToDestroy, clusterName) clustersToDestroy = append(clustersToDestroy, clusterName)
} }
clustersResp, err := client.ListClusters(ctx, testutil.ProjectId).Execute() clustersResp, err := client.ListClusters(ctx, testutil.ProjectId, testutil.Region).Execute()
if err != nil { if err != nil {
return fmt.Errorf("getting clustersResp: %w", err) return fmt.Errorf("getting clustersResp: %w", err)
} }
@ -491,11 +489,11 @@ func testAccCheckSKEDestroy(s *terraform.State) error {
continue continue
} }
if utils.Contains(clustersToDestroy, *items[i].Name) { if utils.Contains(clustersToDestroy, *items[i].Name) {
_, err := client.DeleteClusterExecute(ctx, testutil.ProjectId, *items[i].Name) _, err := client.DeleteClusterExecute(ctx, testutil.ProjectId, testutil.Region, *items[i].Name)
if err != nil { if err != nil {
return fmt.Errorf("destroying cluster %s during CheckDestroy: %w", *items[i].Name, err) return fmt.Errorf("destroying cluster %s during CheckDestroy: %w", *items[i].Name, err)
} }
_, err = wait.DeleteClusterWaitHandler(ctx, client, testutil.ProjectId, *items[i].Name).WaitWithContext(ctx) _, err = wait.DeleteClusterWaitHandler(ctx, client, testutil.ProjectId, testutil.Region, *items[i].Name).WaitWithContext(ctx)
if err != nil { if err != nil {
return fmt.Errorf("destroying cluster %s during CheckDestroy: waiting for deletion %w", *items[i].Name, err) return fmt.Errorf("destroying cluster %s during CheckDestroy: waiting for deletion %w", *items[i].Name, err)
} }
@ -525,7 +523,7 @@ func NewSkeProviderOptions(nodePoolOs string) *SkeProviderOptions {
var err error var err error
if testutil.SKECustomEndpoint == "" { if testutil.SKECustomEndpoint == "" {
client, err = ske.NewAPIClient(coreConfig.WithRegion("eu01")) client, err = ske.NewAPIClient()
} else { } else {
client, err = ske.NewAPIClient(coreConfig.WithEndpoint(testutil.SKECustomEndpoint)) client, err = ske.NewAPIClient(coreConfig.WithEndpoint(testutil.SKECustomEndpoint))
} }
@ -534,7 +532,7 @@ func NewSkeProviderOptions(nodePoolOs string) *SkeProviderOptions {
panic("failed to create SKE client: " + err.Error()) panic("failed to create SKE client: " + err.Error())
} }
options, err := client.ListProviderOptions(ctx).Execute() options, err := client.ListProviderOptions(ctx, testutil.Region).Execute()
if err != nil { if err != nil {
panic("failed to fetch SKE provider options: " + err.Error()) panic("failed to fetch SKE provider options: " + err.Error())
} }

View file

@ -19,7 +19,7 @@ variable "nodepool_volume_size" {}
variable "nodepool_volume_type" {} variable "nodepool_volume_type" {}
variable "ext_acl_enabled" {} variable "ext_acl_enabled" {}
variable "ext_acl_allowed_cidr1" {} variable "ext_acl_allowed_cidr1" {}
variable "ext_argus_enabled" {} variable "ext_observability_enabled" {}
variable "ext_dns_enabled" {} variable "ext_dns_enabled" {}
variable "nodepool_hibernations1_start" {} variable "nodepool_hibernations1_start" {}
variable "nodepool_hibernations1_end" {} variable "nodepool_hibernations1_end" {}
@ -70,8 +70,8 @@ resource "stackit_ske_cluster" "cluster" {
enabled = var.ext_acl_enabled enabled = var.ext_acl_enabled
allowed_cidrs = [var.ext_acl_allowed_cidr1] allowed_cidrs = [var.ext_acl_allowed_cidr1]
} }
argus = { observability = {
enabled = var.ext_argus_enabled enabled = var.ext_observability_enabled
} }
dns = { dns = {
enabled = var.ext_dns_enabled enabled = var.ext_dns_enabled

View file

@ -18,8 +18,6 @@ func ConfigureClient(ctx context.Context, providerData *core.ProviderData, diags
} }
if providerData.SKECustomEndpoint != "" { if providerData.SKECustomEndpoint != "" {
apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(providerData.SKECustomEndpoint)) apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(providerData.SKECustomEndpoint))
} else {
apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(providerData.GetRegion()))
} }
apiClient, err := ske.NewAPIClient(apiClientConfigOptions...) apiClient, err := ske.NewAPIClient(apiClientConfigOptions...)
if err != nil { if err != nil {

View file

@ -46,7 +46,6 @@ func TestConfigureClient(t *testing.T) {
expected: func() *ske.APIClient { expected: func() *ske.APIClient {
apiClient, err := ske.NewAPIClient( apiClient, err := ske.NewAPIClient(
utils.UserAgentConfigOption(testVersion), utils.UserAgentConfigOption(testVersion),
config.WithRegion("eu01"),
) )
if err != nil { if err != nil {
t.Errorf("error configuring client: %v", err) t.Errorf("error configuring client: %v", err)