diff --git a/docs/data-sources/ske_cluster.md b/docs/data-sources/ske_cluster.md index f9aedba9..7a2aa438 100644 --- a/docs/data-sources/ske_cluster.md +++ b/docs/data-sources/ske_cluster.md @@ -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)) ### 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`) + +### 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`. + + ### Nested Schema for `hibernations` diff --git a/docs/resources/ske_cluster.md b/docs/resources/ske_cluster.md index e2f56f28..51cc60e2 100644 --- a/docs/resources/ske_cluster.md +++ b/docs/resources/ske_cluster.md @@ -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)) ### 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`) + +### 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`. + + ### Nested Schema for `hibernations` diff --git a/docs/resources/ske_kubeconfig.md b/docs/resources/ske_kubeconfig.md index 20ef1ac0..10d15401 100644 --- a/docs/resources/ske_kubeconfig.md +++ b/docs/resources/ske_kubeconfig.md @@ -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 diff --git a/go.mod b/go.mod index 9c0ff64e..5fe7514e 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 849e03ab..f3ac126e 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/stackit/internal/services/ske/cluster/datasource.go b/stackit/internal/services/ske/cluster/datasource.go index 099aeb96..65501161 100644 --- a/stackit/internal/services/ske/cluster/datasource.go +++ b/stackit/internal/services/ske/cluster/datasource.go @@ -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, diff --git a/stackit/internal/services/ske/cluster/resource.go b/stackit/internal/services/ske/cluster/resource.go index 411db9b4..130b7c8f 100644 --- a/stackit/internal/services/ske/cluster/resource.go +++ b/stackit/internal/services/ske/cluster/resource.go @@ -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 diff --git a/stackit/internal/services/ske/cluster/resource_test.go b/stackit/internal/services/ske/cluster/resource_test.go index 6fa00433..e29c15cc 100644 --- a/stackit/internal/services/ske/cluster/resource_test.go +++ b/stackit/internal/services/ske/cluster/resource_test.go @@ -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) + } + }) + } +} diff --git a/stackit/internal/services/ske/kubeconfig/resource.go b/stackit/internal/services/ske/kubeconfig/resource.go index 77e66b86..2424e035 100644 --- a/stackit/internal/services/ske/kubeconfig/resource.go +++ b/stackit/internal/services/ske/kubeconfig/resource.go @@ -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") diff --git a/stackit/internal/services/ske/ske_acc_test.go b/stackit/internal/services/ske/ske_acc_test.go index cc539f0c..b596f189 100644 --- a/stackit/internal/services/ske/ske_acc_test.go +++ b/stackit/internal/services/ske/ske_acc_test.go @@ -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()) } diff --git a/stackit/internal/services/ske/testdata/resource-max.tf b/stackit/internal/services/ske/testdata/resource-max.tf index c30bb30c..1bda783d 100644 --- a/stackit/internal/services/ske/testdata/resource-max.tf +++ b/stackit/internal/services/ske/testdata/resource-max.tf @@ -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 diff --git a/stackit/internal/services/ske/utils/util.go b/stackit/internal/services/ske/utils/util.go index 7dbc31c3..91e89b17 100644 --- a/stackit/internal/services/ske/utils/util.go +++ b/stackit/internal/services/ske/utils/util.go @@ -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 { diff --git a/stackit/internal/services/ske/utils/util_test.go b/stackit/internal/services/ske/utils/util_test.go index e813c5d2..501406aa 100644 --- a/stackit/internal/services/ske/utils/util_test.go +++ b/stackit/internal/services/ske/utils/util_test.go @@ -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)