Support automatic kubernetes updates for stackit_ske_cluster (#360)

* 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

---------

Co-authored-by: Diogo Ferrão <diogo.ferrao@freiheit.com>
This commit is contained in:
Vicente Pinto 2024-05-14 14:55:33 +01:00 committed by GitHub
parent 94fbaf765c
commit 27b008a657
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 529 additions and 160 deletions

View file

@ -3,37 +3,33 @@ package ske_test
import (
"context"
"fmt"
"net/http"
"strings"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
"github.com/stackitcloud/stackit-sdk-go/services/ske/wait"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
)
var projectResource = map[string]string{
"project_id": testutil.ProjectId,
}
var clusterResource = map[string]string{
"project_id": testutil.ProjectId,
"name": fmt.Sprintf("cl-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)),
"name_min": fmt.Sprintf("cl-min-%s", acctest.RandStringFromCharSet(3, acctest.CharSetAlphaNum)),
"kubernetes_version": "1.25",
"kubernetes_version_used": "1.25.16",
"kubernetes_version_new": "1.26",
"kubernetes_version_used_new": "1.26.14",
"kubernetes_version_min": "1.26",
"kubernetes_version_used": "1.26.15",
"kubernetes_version_min_new": "1.27",
"kubernetes_version_used_new": "1.27.13",
"nodepool_name": "np-acc-test",
"nodepool_name_min": "np-acc-min-test",
"nodepool_machine_type": "b1.2",
"nodepool_os_version": "3760.2.0",
"nodepool_os_version_min": "3760.2.0",
"nodepool_os_version": "3815.2.1",
"nodepool_os_version_min": "3815.2.1",
"nodepool_os_name": "flatcar",
"nodepool_minimum": "2",
"nodepool_maximum": "3",
@ -71,14 +67,10 @@ func getConfig(version string, maintenanceEnd *string) string {
return fmt.Sprintf(`
%s
resource "stackit_ske_project" "project" {
project_id = "%s"
}
resource "stackit_ske_cluster" "cluster" {
project_id = stackit_ske_project.project.project_id
project_id = "%s"
name = "%s"
kubernetes_version = "%s"
kubernetes_version_min = "%s"
node_pools = [{
name = "%s"
machine_type = "%s"
@ -125,15 +117,14 @@ func getConfig(version string, maintenanceEnd *string) string {
}
resource "stackit_ske_kubeconfig" "kubeconfig" {
project_id = stackit_ske_project.project.project_id
project_id = stackit_ske_cluster.cluster.project_id
cluster_name = stackit_ske_cluster.cluster.name
expiration = "%s"
}
resource "stackit_ske_cluster" "cluster_min" {
project_id = stackit_ske_project.project.project_id
project_id = "%s"
name = "%s"
kubernetes_version = "%s"
node_pools = [{
name = "%s"
machine_type = "%s"
@ -151,7 +142,7 @@ func getConfig(version string, maintenanceEnd *string) string {
}
`,
testutil.SKEProviderConfig(),
projectResource["project_id"],
clusterResource["project_id"],
clusterResource["name"],
version,
clusterResource["nodepool_name"],
@ -187,8 +178,8 @@ func getConfig(version string, maintenanceEnd *string) string {
clusterResource["kubeconfig_expiration"],
// Minimal
clusterResource["project_id"],
clusterResource["name_min"],
clusterResource["kubernetes_version_new"],
clusterResource["nodepool_name_min"],
clusterResource["nodepool_machine_type"],
clusterResource["nodepool_os_version_min"],
@ -210,17 +201,11 @@ func TestAccSKE(t *testing.T) {
// 1) Creation
{
Config: getConfig(clusterResource["kubernetes_version"], nil),
Config: getConfig(clusterResource["kubernetes_version_min"], nil),
Check: resource.ComposeAggregateTestCheckFunc(
// project data
resource.TestCheckResourceAttr("stackit_ske_project.project", "project_id", projectResource["project_id"]),
// cluster data
resource.TestCheckResourceAttrPair(
"stackit_ske_project.project", "project_id",
"stackit_ske_cluster.cluster", "project_id",
),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "name", clusterResource["name"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "kubernetes_version", clusterResource["kubernetes_version"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "kubernetes_version_min", clusterResource["kubernetes_version_min"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "kubernetes_version_used", clusterResource["kubernetes_version_used"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.name", clusterResource["nodepool_name"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.availability_zones.#", "1"),
@ -270,13 +255,8 @@ func TestAccSKE(t *testing.T) {
resource.TestCheckResourceAttrSet("stackit_ske_kubeconfig.kubeconfig", "expires_at"),
// Minimal cluster
resource.TestCheckResourceAttrPair(
"stackit_ske_project.project", "project_id",
"stackit_ske_cluster.cluster_min", "project_id",
),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster_min", "name", clusterResource["name_min"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster_min", "kubernetes_version", clusterResource["kubernetes_version_new"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster_min", "kubernetes_version_used", clusterResource["kubernetes_version_used_new"]),
resource.TestCheckResourceAttrSet("stackit_ske_cluster.cluster_min", "kubernetes_version_used"),
resource.TestCheckNoResourceAttr("stackit_ske_cluster.cluster_min", "allow_privileged_containers"),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster_min", "node_pools.0.name", clusterResource["nodepool_name_min"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster_min", "node_pools.0.availability_zones.#", "1"),
@ -299,7 +279,7 @@ func TestAccSKE(t *testing.T) {
resource.TestCheckResourceAttrSet("stackit_ske_cluster.cluster_min", "maintenance.enable_machine_image_version_updates"),
resource.TestCheckResourceAttrSet("stackit_ske_cluster.cluster_min", "maintenance.start"),
resource.TestCheckResourceAttrSet("stackit_ske_cluster.cluster_min", "maintenance.end"),
resource.TestCheckResourceAttrSet("stackit_ske_cluster.cluster_min", "kube_config"),
resource.TestCheckNoResourceAttr("stackit_ske_cluster.cluster_min", "kube_config"),
),
},
// 2) Data source
@ -307,11 +287,6 @@ func TestAccSKE(t *testing.T) {
Config: fmt.Sprintf(`
%s
data "stackit_ske_project" "project" {
project_id = "%s"
depends_on = [stackit_ske_project.project]
}
data "stackit_ske_cluster" "cluster" {
project_id = "%s"
name = "%s"
@ -325,17 +300,13 @@ func TestAccSKE(t *testing.T) {
}
`,
getConfig(clusterResource["kubernetes_version"], nil),
projectResource["project_id"],
getConfig(clusterResource["kubernetes_version_min"], nil),
clusterResource["project_id"],
clusterResource["name"],
clusterResource["project_id"],
clusterResource["name_min"],
),
Check: resource.ComposeAggregateTestCheckFunc(
// project data
resource.TestCheckResourceAttr("data.stackit_ske_project.project", "id", projectResource["project_id"]),
// cluster data
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "id", fmt.Sprintf("%s,%s",
clusterResource["project_id"],
@ -343,7 +314,6 @@ func TestAccSKE(t *testing.T) {
)),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "project_id", clusterResource["project_id"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "name", clusterResource["name"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "kubernetes_version", clusterResource["kubernetes_version"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "kubernetes_version_used", clusterResource["kubernetes_version_used"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.name", clusterResource["nodepool_name"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster", "node_pools.0.availability_zones.#", "1"),
@ -380,8 +350,7 @@ func TestAccSKE(t *testing.T) {
// Minimal cluster
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster_min", "name", clusterResource["name_min"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster_min", "kubernetes_version", clusterResource["kubernetes_version_new"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster_min", "kubernetes_version_used", clusterResource["kubernetes_version_used_new"]),
resource.TestCheckResourceAttrSet("data.stackit_ske_cluster.cluster_min", "kubernetes_version_used"),
resource.TestCheckNoResourceAttr("data.stackit_ske_cluster.cluster_min", "allow_privileged_containers"),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster_min", "node_pools.0.name", clusterResource["nodepool_name_min"]),
resource.TestCheckResourceAttr("data.stackit_ske_cluster.cluster_min", "node_pools.0.availability_zones.#", "1"),
@ -404,23 +373,10 @@ func TestAccSKE(t *testing.T) {
resource.TestCheckResourceAttrSet("data.stackit_ske_cluster.cluster_min", "maintenance.enable_machine_image_version_updates"),
resource.TestCheckResourceAttrSet("data.stackit_ske_cluster.cluster_min", "maintenance.start"),
resource.TestCheckResourceAttrSet("data.stackit_ske_cluster.cluster_min", "maintenance.end"),
resource.TestCheckResourceAttrSet("data.stackit_ske_cluster.cluster_min", "kube_config"),
resource.TestCheckNoResourceAttr("data.stackit_ske_cluster.cluster_min", "kube_config"),
),
},
// 3) Import project
{
ResourceName: "stackit_ske_project.project",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
_, ok := s.RootModule().Resources["stackit_ske_project.project"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_ske_project.project")
}
return testutil.ProjectId, nil
},
ImportState: true,
ImportStateVerify: true,
},
// 4) Import cluster
// 3) Import cluster
{
ResourceName: "stackit_ske_cluster.cluster",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
@ -441,9 +397,9 @@ func TestAccSKE(t *testing.T) {
ImportState: true,
ImportStateVerify: true,
// The fields are not provided in the SKE API when disabled, although set actively.
ImportStateVerifyIgnore: []string{"kube_config", "extensions.argus.%", "extensions.argus.argus_instance_id", "extensions.argus.enabled", "extensions.acl.enabled", "extensions.acl.allowed_cidrs", "extensions.acl.allowed_cidrs.#", "extensions.acl.%"},
ImportStateVerifyIgnore: []string{"kubernetes_version_min", "kube_config", "extensions.argus.%", "extensions.argus.argus_instance_id", "extensions.argus.enabled", "extensions.acl.enabled", "extensions.acl.allowed_cidrs", "extensions.acl.allowed_cidrs.#", "extensions.acl.%"},
},
// 5) Import minimal cluster
// 4) Import minimal cluster
{
ResourceName: "stackit_ske_cluster.cluster_min",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
@ -463,16 +419,16 @@ func TestAccSKE(t *testing.T) {
},
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"kube_config"},
ImportStateVerifyIgnore: []string{"kubernetes_version_min", "kube_config"},
},
// 6) Update kubernetes version and maximum
// 5) Update kubernetes version and maximum
{
Config: getConfig(clusterResource["kubernetes_version_new"], utils.Ptr(clusterResource["maintenance_end_new"])),
Config: getConfig(clusterResource["kubernetes_version_min_new"], utils.Ptr(clusterResource["maintenance_end_new"])),
Check: resource.ComposeAggregateTestCheckFunc(
// cluster data
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "project_id", clusterResource["project_id"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "name", clusterResource["name"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "kubernetes_version", clusterResource["kubernetes_version_new"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "kubernetes_version_min", clusterResource["kubernetes_version_min_new"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "kubernetes_version_used", clusterResource["kubernetes_version_used_new"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.name", clusterResource["nodepool_name"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "node_pools.0.availability_zones.#", "1"),
@ -509,14 +465,14 @@ func TestAccSKE(t *testing.T) {
resource.TestCheckNoResourceAttr("stackit_ske_cluster.cluster", "kube_config"), // when using the kubeconfig resource, the kubeconfig field becomes null
),
},
// 7) Downgrade kubernetes version
// 6) Downgrade kubernetes version
{
Config: getConfig(clusterResource["kubernetes_version"], utils.Ptr(clusterResource["maintenance_end_new"])),
Config: getConfig(clusterResource["kubernetes_version_min"], utils.Ptr(clusterResource["maintenance_end_new"])),
Check: resource.ComposeAggregateTestCheckFunc(
// cluster data
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "project_id", clusterResource["project_id"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "name", clusterResource["name"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "kubernetes_version", clusterResource["kubernetes_version"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "kubernetes_version_min", clusterResource["kubernetes_version_min"]),
resource.TestCheckResourceAttr("stackit_ske_cluster.cluster", "kubernetes_version_used", clusterResource["kubernetes_version_used"]),
),
},
@ -530,9 +486,7 @@ func testAccCheckSKEDestroy(s *terraform.State) error {
var client *ske.APIClient
var err error
if testutil.SKECustomEndpoint == "" {
client, err = ske.NewAPIClient(
config.WithRegion("eu01"),
)
client, err = ske.NewAPIClient()
} else {
client, err = ske.NewAPIClient(
config.WithEndpoint(testutil.SKECustomEndpoint),
@ -542,34 +496,35 @@ func testAccCheckSKEDestroy(s *terraform.State) error {
return fmt.Errorf("creating client: %w", err)
}
projectsToDestroy := []string{}
clustersToDestroy := []string{}
for _, rs := range s.RootModule().Resources {
if rs.Type != "stackit_ske_project" {
if rs.Type != "stackit_ske_cluster" {
continue
}
projectsToDestroy = append(projectsToDestroy, rs.Primary.ID)
// cluster terraform ID: = "[project_id],[cluster_name]"
clusterName := strings.Split(rs.Primary.ID, core.Separator)[1]
clustersToDestroy = append(clustersToDestroy, clusterName)
}
for _, projectId := range projectsToDestroy {
_, err := client.GetServiceStatus(ctx, projectId).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 {
return fmt.Errorf("could not convert error to GenericOpenApiError in acc test destruction, %w", err)
}
if oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusForbidden {
// Already gone
continue
}
return fmt.Errorf("getting project: %w", err)
}
_, err = client.DisableServiceExecute(ctx, projectId)
if err != nil {
return fmt.Errorf("destroying project %s during CheckDestroy: %w", projectId, err)
clustersResp, err := client.ListClusters(ctx, testutil.ProjectId).Execute()
if err != nil {
return fmt.Errorf("getting clustersResp: %w", err)
}
items := *clustersResp.Items
for i := range items {
if items[i].Name == nil {
continue
}
_, err = wait.DisableServiceWaitHandler(ctx, client, projectId).WaitWithContext(ctx)
if err != nil {
return fmt.Errorf("destroying project %s during CheckDestroy: waiting for deletion %w", projectId, err)
if utils.Contains(clustersToDestroy, *items[i].Name) {
_, err := client.DeleteClusterExecute(ctx, testutil.ProjectId, *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)
if err != nil {
return fmt.Errorf("destroying cluster %s during CheckDestroy: waiting for deletion %w", *items[i].Name, err)
}
}
}
return nil