Skip to content

Commit 940b15e

Browse files
Fix kubernetes_version_min field logic (#363)
* new field kubernets_version_min and deprecate kubernetes_version * Fix lint and tests * Update acc test * Deprecate datasource field, fix checkAllowPrivilegedContainers * Update acc test, datasource and descriptions * Update acc test * Improve descriptions, fix bug * Improve docs, fix acc test * Update docs * Update docs, fix acc test * Update stackit/internal/services/ske/cluster/resource.go Co-authored-by: Diogo Ferrão <diogo.ferrao@freiheit.com> * Fix links * Default ske auto-update to true * Check current cluster version * Add unit test --------- Co-authored-by: Diogo Ferrão <diogo.ferrao@freiheit.com>
1 parent 56036e8 commit 940b15e

3 files changed

Lines changed: 307 additions & 42 deletions

File tree

docs/resources/ske_cluster.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Deprecated as of Kubernetes 1.25 and later
5353
- `extensions` (Attributes) A single extensions block as defined below. (see [below for nested schema](#nestedatt--extensions))
5454
- `hibernations` (Attributes List) One or more hibernation block as defined below. (see [below for nested schema](#nestedatt--hibernations))
5555
- `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`
56-
- `kubernetes_version_min` (String) The minimum Kubernetes version. This field will be used to set the kubernetes version on creation/update of the cluster and can only by incremented. A downgrade of the version requires a replace 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.
56+
- `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 and can only by incremented. 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.
5757
- `maintenance` (Attributes) A single maintenance block as defined below. (see [below for nested schema](#nestedatt--maintenance))
5858

5959
### Read-Only

stackit/internal/services/ske/cluster/resource.go

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ var (
5959
_ resource.ResourceWithImportState = &clusterResource{}
6060
)
6161

62+
type skeClient interface {
63+
GetClusterExecute(ctx context.Context, projectId, clusterName string) (*ske.Cluster, error)
64+
}
65+
6266
type Model struct {
6367
Id types.String `tfsdk:"id"` // needed by TF
6468
ProjectId types.String `tfsdk:"project_id"`
@@ -275,19 +279,8 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
275279
},
276280
},
277281
"kubernetes_version_min": schema.StringAttribute{
278-
Description: "The minimum Kubernetes version. This field will be used to set the kubernetes version on creation/update of the cluster and can only by incremented. A downgrade of the version requires a replace of the cluster. If unset, the latest supported Kubernetes version will be used. " + SKEUpdateDoc + " To get the current kubernetes version being used for your cluster, use the read-only `kubernetes_version_used` field.",
282+
Description: "The minimum Kubernetes version. This field will be used to set the minimum kubernetes version on creation/update of the cluster and can only by incremented. If unset, the latest supported Kubernetes version will be used. " + SKEUpdateDoc + " To get the current kubernetes version being used for your cluster, use the read-only `kubernetes_version_used` field.",
279283
Optional: true,
280-
PlanModifiers: []planmodifier.String{
281-
stringplanmodifier.RequiresReplaceIf(stringplanmodifier.RequiresReplaceIfFunc(func(ctx context.Context, sr planmodifier.StringRequest, rrifr *stringplanmodifier.RequiresReplaceIfFuncResponse) {
282-
if sr.StateValue.IsNull() || sr.PlanValue.IsNull() {
283-
return
284-
}
285-
planVersion := fmt.Sprintf("v%s", sr.PlanValue.ValueString())
286-
stateVersion := fmt.Sprintf("v%s", sr.StateValue.ValueString())
287-
288-
rrifr.RequiresReplace = semver.Compare(planVersion, stateVersion) < 0
289-
}), "Kubernetes minimum version", "If the Kubernetes version is a downgrade, the cluster will be replaced"),
290-
},
291284
Validators: []validator.String{
292285
validate.VersionNumber(),
293286
},
@@ -631,7 +624,7 @@ func (r *clusterResource) Create(ctx context.Context, req resource.CreateRequest
631624
return
632625
}
633626

634-
r.createOrUpdateCluster(ctx, &resp.Diagnostics, &model, availableVersions)
627+
r.createOrUpdateCluster(ctx, &resp.Diagnostics, &model, availableVersions, nil)
635628
if resp.Diagnostics.HasError() {
636629
return
637630
}
@@ -659,11 +652,26 @@ func (r *clusterResource) loadAvailableVersions(ctx context.Context) ([]ske.Kube
659652
return *res.KubernetesVersions, nil
660653
}
661654

662-
func (r *clusterResource) createOrUpdateCluster(ctx context.Context, diags *diag.Diagnostics, model *Model, availableVersions []ske.KubernetesVersion) {
655+
// getCurrentKubernetesVersion makes a call to get the details of a cluster and returns the current kubernetes version
656+
// if the cluster doesn't exist or some error occurs, returns nil
657+
func getCurrentKubernetesVersion(ctx context.Context, c skeClient, m *Model) *string {
658+
res, err := c.GetClusterExecute(ctx, m.ProjectId.ValueString(), m.Name.ValueString())
659+
if err != nil {
660+
return nil
661+
}
662+
663+
if res != nil && res.Kubernetes != nil {
664+
return res.Kubernetes.Version
665+
}
666+
667+
return nil
668+
}
669+
670+
func (r *clusterResource) createOrUpdateCluster(ctx context.Context, diags *diag.Diagnostics, model *Model, availableVersions []ske.KubernetesVersion, currentKubernetesVersion *string) {
663671
// cluster vars
664672
projectId := model.ProjectId.ValueString()
665673
name := model.Name.ValueString()
666-
kubernetes, hasDeprecatedVersion, err := toKubernetesPayload(model, availableVersions)
674+
kubernetes, hasDeprecatedVersion, err := toKubernetesPayload(model, availableVersions, currentKubernetesVersion)
667675
if err != nil {
668676
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating cluster config API payload: %v", err))
669677
return
@@ -1373,8 +1381,18 @@ func mapExtensions(ctx context.Context, cl *ske.Cluster, m *Model) error {
13731381
return nil
13741382
}
13751383

1376-
func toKubernetesPayload(m *Model, availableVersions []ske.KubernetesVersion) (kubernetesPayload *ske.Kubernetes, hasDeprecatedVersion bool, err error) {
1377-
versionUsed, hasDeprecatedVersion, err := latestMatchingVersion(availableVersions, conversion.StringValueToPointer(m.KubernetesVersion), conversion.StringValueToPointer(m.KubernetesVersionMin))
1384+
func toKubernetesPayload(m *Model, availableVersions []ske.KubernetesVersion, currentKubernetesVersion *string) (kubernetesPayload *ske.Kubernetes, hasDeprecatedVersion bool, err error) {
1385+
providedVersionMin := m.KubernetesVersionMin.ValueStringPointer()
1386+
1387+
if !m.KubernetesVersion.IsNull() {
1388+
// kubernetes_version field deprecation
1389+
// this if clause should be removed once kubernetes_version field is completely removed
1390+
// kubernetes_version field value is used as minimum kubernetes version
1391+
// and currenteKubernetesVersion is ignored
1392+
providedVersionMin = conversion.StringValueToPointer(m.KubernetesVersion)
1393+
}
1394+
1395+
versionUsed, hasDeprecatedVersion, err := latestMatchingVersion(availableVersions, providedVersionMin, currentKubernetesVersion)
13781396
if err != nil {
13791397
return nil, false, fmt.Errorf("getting latest matching kubernetes version: %w", err)
13801398
}
@@ -1386,36 +1404,44 @@ func toKubernetesPayload(m *Model, availableVersions []ske.KubernetesVersion) (k
13861404
return k, hasDeprecatedVersion, nil
13871405
}
13881406

1389-
func latestMatchingVersion(availableVersions []ske.KubernetesVersion, providedVersion, providedVersionMin *string) (version *string, deprecated bool, err error) {
1407+
func latestMatchingVersion(availableVersions []ske.KubernetesVersion, kubernetesVersionMin, currentKubernetesVersion *string) (version *string, deprecated bool, err error) {
13901408
deprecated = false
13911409

13921410
if availableVersions == nil {
13931411
return nil, false, fmt.Errorf("nil available kubernetes versions")
13941412
}
13951413

1396-
if providedVersionMin == nil {
1397-
if providedVersion == nil {
1398-
// kubernetes_version field deprecation
1399-
// this if clause should be removed once kubernetes_version field is completely removed
1414+
if kubernetesVersionMin == nil {
1415+
if currentKubernetesVersion == nil {
14001416
latestVersion, err := getLatestSupportedKubernetesVersion(availableVersions)
14011417
if err != nil {
14021418
return nil, false, fmt.Errorf("get latest supported kubernetes version: %w", err)
14031419
}
14041420
return latestVersion, false, nil
14051421
}
1406-
// kubernetes_version field deprecation
1407-
// kubernetes_version field value is assigned to kubernets_version_min for backwards compatibility
1408-
providedVersionMin = providedVersion
1422+
kubernetesVersionMin = currentKubernetesVersion
1423+
} else if currentKubernetesVersion != nil {
1424+
// For an already existing cluster, if kubernetes_version_min is set to a lower version than what is being used in the cluster
1425+
// return the currently used version
1426+
kubernetesVersionUsed := *currentKubernetesVersion
1427+
kubernetesVersionMinString := *kubernetesVersionMin
1428+
1429+
minVersionPrefixed := "v" + kubernetesVersionMinString
1430+
usedVersionPrefixed := "v" + kubernetesVersionUsed
1431+
1432+
if semver.Compare(minVersionPrefixed, usedVersionPrefixed) == -1 {
1433+
kubernetesVersionMin = currentKubernetesVersion
1434+
}
14091435
}
14101436

14111437
var fullVersion bool
14121438
versionExp := validate.FullVersionRegex
14131439
versionRegex := regexp.MustCompile(versionExp)
1414-
if versionRegex.MatchString(*providedVersionMin) {
1440+
if versionRegex.MatchString(*kubernetesVersionMin) {
14151441
fullVersion = true
14161442
}
14171443

1418-
providedVersionPrefixed := "v" + *providedVersionMin
1444+
providedVersionPrefixed := "v" + *kubernetesVersionMin
14191445

14201446
if !semver.IsValid(providedVersionPrefixed) {
14211447
return nil, false, fmt.Errorf("provided version is invalid")
@@ -1565,7 +1591,9 @@ func (r *clusterResource) Update(ctx context.Context, req resource.UpdateRequest
15651591
return
15661592
}
15671593

1568-
r.createOrUpdateCluster(ctx, &resp.Diagnostics, &model, availableVersions)
1594+
currentKubernetesVersion := getCurrentKubernetesVersion(ctx, r.client, &model)
1595+
1596+
r.createOrUpdateCluster(ctx, &resp.Diagnostics, &model, availableVersions, currentKubernetesVersion)
15691597
if resp.Diagnostics.HasError() {
15701598
return
15711599
}

0 commit comments

Comments
 (0)