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
- `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.
- `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))
- `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_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))
@ -54,8 +50,9 @@ This should be used with care since it also disables a couple of other features
Read-Only:
- `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))
- `observability` (Attributes) A single observability block as defined below. (see [below for nested schema](#nestedatt--extensions--observability))
<a id="nestedatt--extensions--acl"></a>
### 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`)
<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>
### Nested Schema for `hibernations`

View file

@ -50,12 +50,8 @@ resource "stackit_ske_cluster" "example" {
### 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))
- `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.
- `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))
@ -117,8 +113,9 @@ Optional:
Optional:
- `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))
- `observability` (Attributes) A single observability block as defined below. (see [below for nested schema](#nestedatt--extensions--observability))
<a id="nestedatt--extensions--acl"></a>
### 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`)
<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>
### 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`
- `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

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/serviceaccount v0.9.0
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/teambition/rrule-go v1.8.2
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/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/ske v0.27.0 h1:bwLmLXvtCl1XkPRP+YrXwfz+lBMaGWH/crlNbYtxeqE=
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 h1:PX8VTo2UhPd6BeEaCHFlpIkDbk9OFQEO6eJJ8JkxesA=
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/go.mod h1:S04/QsQrB2EgYGjl62BO+9QUswrlRBoBosigrhdmccM=
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.",
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{
Description: "Full Kubernetes version used. For example, if `1.22` was selected, this value may result to `1.22.15`",
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{
Description: "The outgoing network ranges (in CIDR notation) of traffic originating from workload on the cluster.",
Computed: true,
@ -262,8 +252,9 @@ func (r *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
Computed: true,
Attributes: map[string]schema.Attribute{
"argus": schema.SingleNestedAttribute{
Description: "A single argus block as defined below",
Computed: true,
Description: "A single argus block as defined below. This field is deprecated and will be removed 06 January 2026.",
DeprecationMessage: "Use observability instead.",
Computed: true,
Attributes: map[string]schema.Attribute{
"enabled": schema.BoolAttribute{
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{
Description: "Cluster access control configuration",
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, "name", name)
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 {
utils.LogError(
ctx,

View file

@ -12,7 +12,6 @@ import (
serviceenablementUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/serviceenablement/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/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
@ -66,25 +65,23 @@ var (
)
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 {
Id types.String `tfsdk:"id"` // needed by TF
ProjectId types.String `tfsdk:"project_id"`
Name types.String `tfsdk:"name"`
KubernetesVersionMin types.String `tfsdk:"kubernetes_version_min"`
KubernetesVersion types.String `tfsdk:"kubernetes_version"`
KubernetesVersionUsed types.String `tfsdk:"kubernetes_version_used"`
AllowPrivilegedContainers types.Bool `tfsdk:"allow_privileged_containers"`
NodePools types.List `tfsdk:"node_pools"`
Maintenance types.Object `tfsdk:"maintenance"`
Network types.Object `tfsdk:"network"`
Hibernations types.List `tfsdk:"hibernations"`
Extensions types.Object `tfsdk:"extensions"`
EgressAddressRanges types.List `tfsdk:"egress_address_ranges"`
PodAddressRanges types.List `tfsdk:"pod_address_ranges"`
Region types.String `tfsdk:"region"`
Id types.String `tfsdk:"id"` // needed by TF
ProjectId types.String `tfsdk:"project_id"`
Name types.String `tfsdk:"name"`
KubernetesVersionMin types.String `tfsdk:"kubernetes_version_min"`
KubernetesVersionUsed types.String `tfsdk:"kubernetes_version_used"`
NodePools types.List `tfsdk:"node_pools"`
Maintenance types.Object `tfsdk:"maintenance"`
Network types.Object `tfsdk:"network"`
Hibernations types.List `tfsdk:"hibernations"`
Extensions types.Object `tfsdk:"extensions"`
EgressAddressRanges types.List `tfsdk:"egress_address_ranges"`
PodAddressRanges types.List `tfsdk:"pod_address_ranges"`
Region types.String `tfsdk:"region"`
}
// Struct corresponding to Model.NodePools[i]
@ -185,16 +182,18 @@ var hibernationTypes = map[string]attr.Type{
// Struct corresponding to Model.Extensions
type extensions struct {
Argus types.Object `tfsdk:"argus"`
ACL types.Object `tfsdk:"acl"`
DNS types.Object `tfsdk:"dns"`
Argus types.Object `tfsdk:"argus"`
Observability types.Object `tfsdk:"observability"`
ACL types.Object `tfsdk:"acl"`
DNS types.Object `tfsdk:"dns"`
}
// Types corresponding to extensions
var extensionsTypes = map[string]attr.Type{
"argus": basetypes.ObjectType{AttrTypes: argusTypes},
"acl": basetypes.ObjectType{AttrTypes: aclTypes},
"dns": basetypes.ObjectType{AttrTypes: dnsTypes},
"argus": basetypes.ObjectType{AttrTypes: argusTypes},
"observability": basetypes.ObjectType{AttrTypes: observabilityTypes},
"acl": basetypes.ObjectType{AttrTypes: aclTypes},
"dns": basetypes.ObjectType{AttrTypes: dnsTypes},
}
// Struct corresponding to extensions.ACL
@ -221,6 +220,18 @@ var argusTypes = map[string]attr.Type{
"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
type dns struct {
Enabled types.Bool `tfsdk:"enabled"`
@ -353,33 +364,10 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
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{
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,
},
"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{
Description: "The outgoing network ranges (in CIDR notation) of traffic originating from workload on the cluster.",
Computed: true,
@ -607,8 +595,9 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
Attributes: map[string]schema.Attribute{
"argus": schema.SingleNestedAttribute{
Description: "A single argus block as defined below.",
Optional: true,
Description: "A single argus block as defined below. This field is deprecated and will be removed 06 January 2026.",
DeprecationMessage: "Use observability instead.",
Optional: true,
Attributes: map[string]schema.Attribute{
"enabled": schema.BoolAttribute{
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{
Description: "Cluster access control configuration.",
Optional: true,
@ -669,43 +672,34 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
}
}
// ConfigValidators validate the resource configuration
func (r *clusterResource) ConfigValidators(_ context.Context) []resource.ConfigValidator {
return []resource.ConfigValidator{
// will raise an error if both fields are set simultaneously
resourcevalidator.Conflicting(
path.MatchRoot("kubernetes_version"),
path.MatchRoot("kubernetes_version_min"),
),
// The argus extension is deprecated but can still be used until it is removed on 06 January 2026.
func (r *clusterResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
var resourceModel Model
resp.Diagnostics.Append(req.Config.Get(ctx, &resourceModel)...)
if resp.Diagnostics.HasError() {
return
}
// 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
// since ValidateConfig runs before variables are rendered to their value,
// which causes errors like this: https://github.com/stackitcloud/terraform-provider-stackit/issues/201
func checkAllowPrivilegedContainers(allowPrivilegeContainers types.Bool, kubernetesVersion types.String) diag.Diagnostics {
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
func validateConfig(ctx context.Context, respDiags *diag.Diagnostics, model *Model) {
// If no extensions are configured, return without error.
if utils.IsUndefined(model.Extensions) {
return
}
comparison := semver.Compare(fmt.Sprintf("v%s", kubernetesVersion.ValueString()), "v1.25")
if comparison < 0 {
if allowPrivilegeContainers.IsNull() {
diags.AddError("'Allow privilege containers' missing", "This field is required for Kubernetes prior to 1.25")
}
} 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")
}
extensions := &extensions{}
diags := model.Extensions.As(ctx, extensions, basetypes.ObjectAsOptions{})
respDiags.Append(diags...)
if respDiags.HasError() {
return
}
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.
@ -717,17 +711,6 @@ func (r *clusterResource) Create(ctx context.Context, req resource.CreateRequest
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()
region := model.Region.ValueString()
clusterName := model.Name.ValueString()
@ -748,7 +731,7 @@ func (r *clusterResource) Create(ctx context.Context, req resource.CreateRequest
return
}
availableKubernetesVersions, availableMachines, err := r.loadAvailableVersions(ctx)
availableKubernetesVersions, availableMachines, err := r.loadAvailableVersions(ctx, region)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating cluster", fmt.Sprintf("Loading available Kubernetes and machine image versions: %v", err))
return
@ -795,9 +778,9 @@ func sortK8sVersions(versions []ske.KubernetesVersion) {
// 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)
// 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
res, err := c.ListProviderOptions(ctx).Execute()
res, err := c.ListProviderOptions(ctx, region).Execute()
if err != nil {
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.
// 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) {
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 {
return nil, nil
}
@ -891,19 +874,19 @@ func (r *clusterResource) createOrUpdateCluster(ctx context.Context, diags *diag
Network: network,
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 {
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Calling API: %v", err))
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 {
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Cluster creation waiting: %v", err))
return
}
if waitResp.Status.Error != nil && waitResp.Status.Error.Message != nil && *waitResp.Status.Error.Code == ske.RUNTIMEERRORCODE_ARGUS_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))
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 observability instance id, the cluster is usable but metrics won't be forwarded: %s", *waitResp.Status.Error.Message))
}
err = mapFields(ctx, waitResp, model, region)
@ -1242,8 +1225,20 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error)
}
}
var skeArgus *ske.Argus
if !(ex.Argus.IsNull() || ex.Argus.IsUnknown()) {
var skeObservability *ske.Observability
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{}
diags = ex.Argus.As(ctx, &argus, basetypes.ObjectAsOptions{})
if diags.HasError() {
@ -1251,9 +1246,9 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error)
}
argusEnabled := conversion.BoolValueToPointer(argus.Enabled)
argusInstanceId := conversion.StringValueToPointer(argus.ArgusInstanceId)
skeArgus = &ske.Argus{
Enabled: argusEnabled,
ArgusInstanceId: argusInstanceId,
skeObservability = &ske.Observability{
Enabled: argusEnabled,
InstanceId: argusInstanceId,
}
}
@ -1278,9 +1273,9 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error)
}
return &ske.Extension{
Acl: skeAcl,
Argus: skeArgus,
Dns: skeDNS,
Acl: skeAcl,
Observability: skeObservability,
Dns: skeDNS,
}, nil
}
@ -1372,7 +1367,6 @@ func mapFields(ctx context.Context, cl *ske.Cluster, m *Model, region string) er
if cl.Kubernetes != nil {
m.KubernetesVersionUsed = types.StringPointerValue(cl.Kubernetes.Version)
m.AllowPrivilegedContainers = types.BoolPointerValue(cl.Kubernetes.AllowPrivilegedContainers)
}
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
}
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
acl := acl{}
if ex.ACL.IsNull() {
@ -1718,14 +1712,22 @@ func checkDisabledExtensions(ctx context.Context, ex extensions) (aclDisabled, a
}
}
argus := argus{}
if ex.Argus.IsNull() {
argus.Enabled = types.BoolValue(false)
} else {
observability := observability{}
if ex.Argus.IsNull() && ex.Observability.IsNull() {
observability.Enabled = types.BoolValue(false)
} else if !ex.Argus.IsNull() {
argus := argus{}
diags = ex.Argus.As(ctx, &argus, basetypes.ObjectAsOptions{})
if diags.HasError() {
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{}
@ -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 {
@ -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
// error
aclDisabled, argusDisabled, dnsDisabled, err := checkDisabledExtensions(ctx, ex)
aclDisabled, observabilityDisabled, dnsDisabled, err := checkDisabledExtensions(ctx, &ex)
if err != nil {
return fmt.Errorf("checking if extensions are disabled: %w", err)
}
disabledExtensions := false
if aclDisabled && argusDisabled && dnsDisabled {
if aclDisabled && observabilityDisabled && dnsDisabled {
disabledExtensions = true
}
@ -1807,29 +1809,55 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error {
aclExtension = ex.ACL
}
// Deprecated: argus won't be received from backend. Use observabilty instead.
argusExtension := types.ObjectNull(argusTypes)
if cl.Extensions.Argus != nil {
observabilityExtension := types.ObjectNull(observabilityTypes)
if cl.Extensions.Observability != nil {
enabled := types.BoolNull()
if cl.Extensions.Argus.Enabled != nil {
enabled = types.BoolValue(*cl.Extensions.Argus.Enabled)
if cl.Extensions.Observability.Enabled != nil {
enabled = types.BoolValue(*cl.Extensions.Observability.Enabled)
}
argusInstanceId := types.StringNull()
if cl.Extensions.Argus.ArgusInstanceId != nil {
argusInstanceId = types.StringValue(*cl.Extensions.Argus.ArgusInstanceId)
observabilityInstanceId := types.StringNull()
if cl.Extensions.Observability.InstanceId != nil {
observabilityInstanceId = types.StringValue(*cl.Extensions.Observability.InstanceId)
}
observabilityExtensionValues := map[string]attr.Value{
"enabled": enabled,
"instance_id": observabilityInstanceId,
}
argusExtensionValues := map[string]attr.Value{
"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)
if diags.HasError() {
return fmt.Errorf("creating argus extension: %w", core.DiagsToError(diags))
}
} else if argusDisabled && !ex.Argus.IsNull() {
argusExtension = ex.Argus
}
dnsExtension := types.ObjectNull(dnsTypes)
@ -1857,10 +1885,23 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error {
dnsExtension = ex.DNS
}
extensionsValues := map[string]attr.Value{
"acl": aclExtension,
"argus": argusExtension,
"dns": dnsExtension,
// Deprecation: Argus was renamed to observability. Depending on which attribute was used in the terraform config the
// according one has to be set here.
var extensionsValues map[string]attr.Value
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)
@ -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) {
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)
if err != nil {
return nil, false, fmt.Errorf("getting latest matching kubernetes version: %w", err)
}
k := &ske.Kubernetes{
Version: versionUsed,
AllowPrivilegedContainers: conversion.BoolValueToPointer(m.AllowPrivilegedContainers),
Version: versionUsed,
}
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, "region", region)
clResp, err := r.skeClient.GetCluster(ctx, projectId, name).Execute()
clResp, err := r.skeClient.GetCluster(ctx, projectId, region, name).Execute()
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
if ok && oapiErr.StatusCode == http.StatusNotFound {
@ -2102,18 +2134,6 @@ func (r *clusterResource) Update(ctx context.Context, req resource.UpdateRequest
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()
clName := model.Name.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, "region", region)
availableKubernetesVersions, availableMachines, err := r.loadAvailableVersions(ctx)
availableKubernetesVersions, availableMachines, err := r.loadAvailableVersions(ctx, region)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating cluster", fmt.Sprintf("Loading available Kubernetes and machine image versions: %v", err))
return
@ -2156,12 +2176,12 @@ func (r *clusterResource) Delete(ctx context.Context, req resource.DeleteRequest
ctx = tflog.SetField(ctx, "region", region)
c := r.skeClient
_, err := c.DeleteCluster(ctx, projectId, name).Execute()
_, err := c.DeleteCluster(ctx, projectId, region, name).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting cluster", fmt.Sprintf("Calling API: %v", err))
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 {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting cluster", fmt.Sprintf("Cluster deletion waiting: %v", err))
return

View file

@ -23,7 +23,9 @@ type skeClientMocked struct {
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 {
return nil, fmt.Errorf("get cluster failed")
}
@ -33,7 +35,6 @@ func (c *skeClientMocked) GetClusterExecute(_ context.Context, _, _ string) (*sk
func TestMapFields(t *testing.T) {
cs := ske.ClusterStatusState("OK")
const testRegion = "region"
tests := []struct {
description string
stateExtensions types.Object
@ -52,19 +53,17 @@ func TestMapFields(t *testing.T) {
},
testRegion,
Model{
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(),
AllowPrivilegedContainers: types.BoolNull(),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Network: types.ObjectNull(networkTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Extensions: types.ObjectNull(extensionsTypes),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Region: types.StringValue(testRegion),
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Network: types.ObjectNull(networkTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Extensions: types.ObjectNull(extensionsTypes),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Region: types.StringValue(testRegion),
},
true,
},
@ -78,9 +77,9 @@ func TestMapFields(t *testing.T) {
AllowedCidrs: &[]string{"cidr1"},
Enabled: utils.Ptr(true),
},
Argus: &ske.Argus{
ArgusInstanceId: utils.Ptr("aid"),
Enabled: utils.Ptr(true),
Observability: &ske.Observability{
InstanceId: utils.Ptr("aid"),
Enabled: utils.Ptr(true),
},
Dns: &ske.DNS{
Zones: &[]string{"foo.onstackit.cloud"},
@ -97,8 +96,7 @@ func TestMapFields(t *testing.T) {
},
},
Kubernetes: &ske.Kubernetes{
AllowPrivilegedContainers: utils.Ptr(true),
Version: utils.Ptr("1.2.3"),
Version: utils.Ptr("1.2.3"),
},
Maintenance: &ske.Maintenance{
AutoUpdate: &ske.MaintenanceAutoUpdate{
@ -157,12 +155,10 @@ func TestMapFields(t *testing.T) {
},
testRegion,
Model{
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(),
KubernetesVersionUsed: types.StringValue("1.2.3"),
AllowPrivilegedContainers: types.BoolValue(true),
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersionUsed: types.StringValue("1.2.3"),
EgressAddressRanges: types.ListValueMust(
types.StringType,
[]attr.Value{
@ -256,9 +252,10 @@ func TestMapFields(t *testing.T) {
types.StringValue("cidr1"),
}),
}),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"argus_instance_id": types.StringValue("aid"),
"argus": types.ObjectNull(argusTypes),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"instance_id": types.StringValue("aid"),
}),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
@ -281,19 +278,17 @@ func TestMapFields(t *testing.T) {
},
testRegion,
Model{
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(),
AllowPrivilegedContainers: types.BoolNull(),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Network: types.ObjectNull(networkTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Extensions: types.ObjectNull(extensionsTypes),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Region: types.StringValue(testRegion),
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Network: types.ObjectNull(networkTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Extensions: types.ObjectNull(extensionsTypes),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Region: types.StringValue(testRegion),
},
true,
},
@ -307,9 +302,9 @@ func TestMapFields(t *testing.T) {
AllowedCidrs: nil,
Enabled: utils.Ptr(true),
},
Argus: &ske.Argus{
ArgusInstanceId: nil,
Enabled: utils.Ptr(true),
Observability: &ske.Observability{
InstanceId: nil,
Enabled: utils.Ptr(true),
},
Dns: &ske.DNS{
Zones: nil,
@ -320,24 +315,23 @@ func TestMapFields(t *testing.T) {
},
testRegion,
Model{
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(),
AllowPrivilegedContainers: types.BoolNull(),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"allowed_cidrs": types.ListNull(types.StringType),
}),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"argus_instance_id": types.StringNull(),
"argus": types.ObjectNull(argusTypes),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"instance_id": types.StringNull(),
}),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
@ -355,9 +349,10 @@ func TestMapFields(t *testing.T) {
"enabled": types.BoolValue(false),
"allowed_cidrs": types.ListNull(types.StringType),
}),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
"argus_instance_id": types.StringNull(),
"argus": types.ObjectNull(argusTypes),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
"instance_id": types.StringNull(),
}),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
@ -371,24 +366,23 @@ func TestMapFields(t *testing.T) {
},
testRegion,
Model{
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(),
AllowPrivilegedContainers: types.BoolNull(),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
"allowed_cidrs": types.ListNull(types.StringType),
}),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
"argus_instance_id": types.StringNull(),
"argus": types.ObjectNull(argusTypes),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
"instance_id": types.StringNull(),
}),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
@ -400,7 +394,7 @@ func TestMapFields(t *testing.T) {
true,
},
{
"extensions_only_argus_disabled",
"extensions_only_observability_disabled",
types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
@ -408,9 +402,10 @@ func TestMapFields(t *testing.T) {
types.StringValue("cidr1"),
}),
}),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
"argus_instance_id": types.StringValue("id"),
"argus": types.ObjectNull(argusTypes),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
"instance_id": types.StringValue("id"),
}),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
@ -433,16 +428,14 @@ func TestMapFields(t *testing.T) {
},
testRegion,
Model{
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(),
AllowPrivilegedContainers: types.BoolNull(),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Extensions: types.ObjectValueMust(extensionsTypes, map[string]attr.Value{
"acl": types.ObjectValueMust(aclTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
@ -450,9 +443,10 @@ func TestMapFields(t *testing.T) {
types.StringValue("cidr1"),
}),
}),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
"argus_instance_id": types.StringValue("id"),
"argus": types.ObjectNull(argusTypes),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"enabled": types.BoolValue(false),
"instance_id": types.StringValue("id"),
}),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
@ -473,18 +467,16 @@ func TestMapFields(t *testing.T) {
},
testRegion,
Model{
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(),
AllowPrivilegedContainers: types.BoolNull(),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Extensions: types.ObjectNull(extensionsTypes),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Region: types.StringValue(testRegion),
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Extensions: types.ObjectNull(extensionsTypes),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Region: types.StringValue(testRegion),
},
true,
},
@ -535,9 +527,9 @@ func TestMapFields(t *testing.T) {
AllowedCidrs: &[]string{"cidr1"},
Enabled: utils.Ptr(true),
},
Argus: &ske.Argus{
ArgusInstanceId: utils.Ptr("aid"),
Enabled: utils.Ptr(true),
Observability: &ske.Observability{
InstanceId: utils.Ptr("aid"),
Enabled: utils.Ptr(true),
},
Dns: &ske.DNS{
Zones: &[]string{"zone1"},
@ -554,8 +546,7 @@ func TestMapFields(t *testing.T) {
},
},
Kubernetes: &ske.Kubernetes{
AllowPrivilegedContainers: utils.Ptr(true),
Version: utils.Ptr("1.2.3"),
Version: utils.Ptr("1.2.3"),
},
Maintenance: &ske.Maintenance{
AutoUpdate: &ske.MaintenanceAutoUpdate{
@ -605,14 +596,12 @@ func TestMapFields(t *testing.T) {
},
testRegion,
Model{
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(),
KubernetesVersionUsed: types.StringValue("1.2.3"),
AllowPrivilegedContainers: types.BoolValue(true),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
Id: types.StringValue("pid,region,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersionUsed: types.StringValue("1.2.3"),
EgressAddressRanges: types.ListNull(types.StringType),
PodAddressRanges: types.ListNull(types.StringType),
NodePools: types.ListValueMust(
types.ObjectType{AttrTypes: nodePoolTypes},
[]attr.Value{
@ -680,9 +669,10 @@ func TestMapFields(t *testing.T) {
types.StringValue("cidr1"),
}),
}),
"argus": types.ObjectValueMust(argusTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"argus_instance_id": types.StringValue("aid"),
"argus": types.ObjectNull(argusTypes),
"observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
"enabled": types.BoolValue(true),
"instance_id": types.StringValue("aid"),
}),
"dns": types.ObjectValueMust(dnsTypes, map[string]attr.Value{
"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) {
tests := []struct {
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"`
ExpiresAt types.String `tfsdk:"expires_at"`
CreationTime types.String `tfsdk:"creation_time"`
Region types.String `tfsdk:"region"`
}
// NewKubeconfigResource is a helper function to simplify the provider implementation.
@ -55,7 +56,8 @@ func NewKubeconfigResource() resource.Resource {
// kubeconfigResource is the resource implementation.
type kubeconfigResource struct {
client *ske.APIClient
client *ske.APIClient
providerData core.ProviderData
}
// 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.
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 {
return
}
apiClient := skeUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics)
apiClient := skeUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}
@ -91,6 +94,7 @@ func (r *kubeconfigResource) Schema(_ context.Context, _ resource.SchemaRequest,
"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",
"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{
@ -172,6 +176,15 @@ func (r *kubeconfigResource) Schema(_ context.Context, _ resource.SchemaRequest,
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
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.
@ -196,12 +234,14 @@ func (r *kubeconfigResource) Create(ctx context.Context, req resource.CreateRequ
projectId := model.ProjectId.ValueString()
clusterName := model.ClusterName.ValueString()
kubeconfigUUID := uuid.New().String()
region := model.Region.ValueString()
model.KubeconfigId = types.StringValue(kubeconfigUUID)
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "cluster_name", clusterName)
ctx = tflog.SetField(ctx, "kube_config_id", kubeconfigUUID)
ctx = tflog.SetField(ctx, "region", region)
err := r.createKubeconfig(ctx, &model)
if err != nil {
@ -234,11 +274,13 @@ func (r *kubeconfigResource) Read(ctx context.Context, req resource.ReadRequest,
projectId := model.ProjectId.ValueString()
clusterName := model.ClusterName.ValueString()
kubeconfigUUID := model.KubeconfigId.ValueString()
region := model.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "cluster_name", clusterName)
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 {
utils.LogError(
ctx,
@ -298,7 +340,7 @@ func (r *kubeconfigResource) createKubeconfig(ctx context.Context, model *Model)
return fmt.Errorf("creating API payload: %w", err)
}
// 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 {
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()
clusterName := model.ClusterName.ValueString()
kubeconfigUUID := model.KubeconfigId.ValueString()
region := model.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "cluster_name", clusterName)
ctx = tflog.SetField(ctx, "kube_config_id", kubeconfigUUID)
ctx = tflog.SetField(ctx, "region", region)
// kubeconfig is deleted automatically from the state
tflog.Info(ctx, "SKE kubeconfig deleted")

View file

@ -74,7 +74,7 @@ var testConfigVarsMax = config.Variables{
"nodepool_volume_type": config.StringVariable("storage_premium_perf0"),
"ext_acl_enabled": config.StringVariable("true"),
"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"),
"nodepool_hibernations1_start": config.StringVariable("0 18 * * *"),
"nodepool_hibernations1_end": config.StringVariable("59 23 * * *"),
@ -199,7 +199,7 @@ func TestAccSKEMin(t *testing.T) {
ImportState: true,
ImportStateVerify: true,
// 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
{
@ -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.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.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.zones.#", "1"),
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.allowed_cidrs.#", "1"),
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.zones.#", "1"),
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,
ImportStateVerify: true,
// 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
{
@ -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.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.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.zones.#", "1"),
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 err error
if testutil.SKECustomEndpoint == "" {
client, err = ske.NewAPIClient(
coreConfig.WithRegion(testutil.Region),
)
client, err = ske.NewAPIClient()
} else {
client, err = ske.NewAPIClient(
coreConfig.WithEndpoint(testutil.SKECustomEndpoint),
@ -480,7 +478,7 @@ func testAccCheckSKEDestroy(s *terraform.State) error {
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 {
return fmt.Errorf("getting clustersResp: %w", err)
}
@ -491,11 +489,11 @@ func testAccCheckSKEDestroy(s *terraform.State) error {
continue
}
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 {
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 {
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
if testutil.SKECustomEndpoint == "" {
client, err = ske.NewAPIClient(coreConfig.WithRegion("eu01"))
client, err = ske.NewAPIClient()
} else {
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())
}
options, err := client.ListProviderOptions(ctx).Execute()
options, err := client.ListProviderOptions(ctx, testutil.Region).Execute()
if err != nil {
panic("failed to fetch SKE provider options: " + err.Error())
}

View file

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

View file

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

View file

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