Skip to content

Commit 7d0a9a7

Browse files
authored
feat(k8s): type change should migrate cluster when possible (#2115)
1 parent 29114d2 commit 7d0a9a7

22 files changed

+15872
-7669
lines changed

docs/resources/k8s_cluster.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,13 @@ The following arguments are supported:
210210

211211
- `name` - (Required) The name for the Kubernetes cluster.
212212

213-
- `type` - (Optional) The type of Kubernetes cluster. Possible values are: `kapsule` or `multicloud`.
213+
- `type` - (Optional) The type of Kubernetes cluster. Possible values are:
214+
215+
- for mutualized clusters: `kapsule` or `multicloud`
216+
217+
- for dedicated Kapsule clusters: `kapsule-dedicated-4`, `kapsule-dedicated-8` or `kapsule-dedicated-16`.
218+
219+
- for dedicated Kosmos clusters: `multicloud-dedicated-4`, `multicloud-dedicated-8` or `multicloud-dedicated-16`.
214220

215221
- `description` - (Optional) A description for the Kubernetes cluster.
216222

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
github.com/nats-io/jwt/v2 v2.5.0
1717
github.com/nats-io/nats.go v1.28.0
1818
github.com/robfig/cron/v3 v3.0.1
19-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20.0.20230821160755-82a4aa87ba70
19+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20.0.20230901111648-9e540fe63734
2020
github.com/stretchr/testify v1.8.4
2121
)
2222

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
173173
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
174174
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
175175
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
176-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20.0.20230821160755-82a4aa87ba70 h1:k8EwAXHaONvKu7l55pnC4EoY/XpNyM0/5J/+Bruw2PU=
177-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20.0.20230821160755-82a4aa87ba70/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
176+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20.0.20230901111648-9e540fe63734 h1:KAxCqrhRP6DAclE0uzBcwUJtAfkdUfEA75rhUFKj4cU=
177+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20.0.20230901111648-9e540fe63734/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
178178
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
179179
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
180180
github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=

scaleway/helpers.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
2020
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2121
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
22+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
2223
"github.com/scaleway/scaleway-sdk-go/namegenerator"
2324
"github.com/scaleway/scaleway-sdk-go/scw"
2425
)
@@ -1101,3 +1102,38 @@ func sliceContainsString(slice []string, str string) bool {
11011102
}
11021103
return false
11031104
}
1105+
1106+
// testAccCheckScalewayResourceIDPersisted checks that the ID of the resource is the same throughout tests of migration or mutation
1107+
// It can be used to check that no ForceNew has been done
1108+
func testAccCheckScalewayResourceIDPersisted(resourceName string, resourceID *string) resource.TestCheckFunc {
1109+
return func(s *terraform.State) error {
1110+
rs, ok := s.RootModule().Resources[resourceName]
1111+
if !ok {
1112+
return fmt.Errorf("resource was not found: %s", resourceName)
1113+
}
1114+
if *resourceID != "" && *resourceID != rs.Primary.ID {
1115+
return fmt.Errorf("resource ID changed when it should have persisted")
1116+
}
1117+
*resourceID = rs.Primary.ID
1118+
return nil
1119+
}
1120+
}
1121+
1122+
// testAccCheckScalewayResourceIDChanged checks that the ID of the resource has indeed changed, in case of ForceNew for example.
1123+
// It will fail if resourceID is empty so be sure to use testAccCheckScalewayResourceIDPersisted first in a test suite.
1124+
func testAccCheckScalewayResourceIDChanged(resourceName string, resourceID *string) resource.TestCheckFunc {
1125+
return func(s *terraform.State) error {
1126+
if resourceID == nil || *resourceID == "" {
1127+
return fmt.Errorf("resourceID was not set")
1128+
}
1129+
rs, ok := s.RootModule().Resources[resourceName]
1130+
if !ok {
1131+
return fmt.Errorf("resource was not found: %s", resourceName)
1132+
}
1133+
if *resourceID == rs.Primary.ID {
1134+
return fmt.Errorf("resource ID persisted when it should have changed")
1135+
}
1136+
*resourceID = rs.Primary.ID
1137+
return nil
1138+
}
1139+
}

scaleway/resource_k8s_cluster.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ func resourceScalewayK8SCluster() *schema.Resource {
4141
"type": {
4242
Type: schema.TypeString,
4343
Optional: true,
44-
ForceNew: true,
4544
Computed: true,
4645
Description: "The type of cluster",
4746
},
@@ -274,6 +273,33 @@ func resourceScalewayK8SCluster() *schema.Resource {
274273
}
275274
return nil
276275
},
276+
func(ctx context.Context, diff *schema.ResourceDiff, i interface{}) error {
277+
if diff.HasChange("type") && diff.Id() != "" {
278+
k8sAPI, region, clusterID, err := k8sAPIWithRegionAndID(i, diff.Id())
279+
if err != nil {
280+
return err
281+
}
282+
possibleTypes, err := k8sAPI.ListClusterAvailableTypes(&k8s.ListClusterAvailableTypesRequest{
283+
Region: region,
284+
ClusterID: clusterID,
285+
}, scw.WithContext(ctx))
286+
if err != nil {
287+
return err
288+
}
289+
290+
planned := diff.Get("type")
291+
for _, possibleType := range possibleTypes.ClusterTypes {
292+
if possibleType.Name == planned {
293+
return nil
294+
}
295+
}
296+
err = diff.ForceNew("type")
297+
if err != nil {
298+
return err
299+
}
300+
}
301+
return nil
302+
},
277303
),
278304
}
279305
}
@@ -551,6 +577,25 @@ func resourceScalewayK8SClusterUpdate(ctx context.Context, d *schema.ResourceDat
551577
return diag.FromErr(err)
552578
}
553579

580+
if d.HasChange("type") {
581+
_, err = waitK8SCluster(ctx, k8sAPI, region, clusterID, defaultK8SClusterTimeout)
582+
if err != nil {
583+
return diag.FromErr(err)
584+
}
585+
_, err = k8sAPI.SetClusterType(&k8s.SetClusterTypeRequest{
586+
Region: region,
587+
ClusterID: clusterID,
588+
Type: d.Get("type").(string),
589+
}, scw.WithContext(ctx))
590+
if err != nil {
591+
return diag.FromErr(err)
592+
}
593+
_, err = waitK8SCluster(ctx, k8sAPI, region, clusterID, defaultK8SClusterTimeout)
594+
if err != nil {
595+
return diag.FromErr(err)
596+
}
597+
}
598+
554599
canUpgrade := false
555600

556601
////

scaleway/resource_k8s_cluster_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,88 @@ func TestAccScalewayK8SCluster_Multicloud(t *testing.T) {
452452
})
453453
}
454454

455+
func TestAccScalewayK8SCluster_TypeChange(t *testing.T) {
456+
tt := NewTestTools(t)
457+
defer tt.Cleanup()
458+
459+
latestK8SVersion := testAccScalewayK8SClusterGetLatestK8SVersion(tt)
460+
461+
clusterID := ""
462+
463+
resource.ParallelTest(t, resource.TestCase{
464+
PreCheck: func() {
465+
testAccPreCheck(t)
466+
},
467+
ProviderFactories: tt.ProviderFactories,
468+
CheckDestroy: testAccCheckScalewayK8SClusterDestroy(tt),
469+
Steps: []resource.TestStep{
470+
{
471+
// 1 : Start with a mutualized Kapsule cluster
472+
Config: testAccCheckScalewayK8SClusterTypeChange("kapsule", "cilium", latestK8SVersion),
473+
Check: resource.ComposeTestCheckFunc(
474+
testAccCheckScalewayK8SClusterExists(tt, "scaleway_k8s_cluster.type-change"),
475+
resource.TestCheckResourceAttr("scaleway_k8s_cluster.type-change", "type", "kapsule"),
476+
testAccCheckScalewayResourceIDPersisted("scaleway_k8s_cluster.type-change", &clusterID),
477+
),
478+
},
479+
{
480+
// 2 : Upgrade to a dedicated Kapsule --> should migrate
481+
Config: testAccCheckScalewayK8SClusterTypeChange("kapsule-dedicated-4", "cilium", latestK8SVersion), // + testAccCheckScalewayK8SClusterTypeChangeCopy(latestK8SVersion),
482+
Check: resource.ComposeTestCheckFunc(
483+
testAccCheckScalewayK8SClusterExists(tt, "scaleway_k8s_cluster.type-change"),
484+
resource.TestCheckResourceAttr("scaleway_k8s_cluster.type-change", "type", "kapsule-dedicated-4"),
485+
testAccCheckScalewayResourceIDPersisted("scaleway_k8s_cluster.type-change", &clusterID),
486+
),
487+
},
488+
{
489+
// 3 : Upgrade to an even bigger dedicated Kapsule --> should migrate
490+
Config: testAccCheckScalewayK8SClusterTypeChange("kapsule-dedicated-16", "cilium", latestK8SVersion), // + testAccCheckScalewayK8SClusterTypeChangeCopy(latestK8SVersion),
491+
Check: resource.ComposeTestCheckFunc(
492+
testAccCheckScalewayK8SClusterExists(tt, "scaleway_k8s_cluster.type-change"),
493+
resource.TestCheckResourceAttr("scaleway_k8s_cluster.type-change", "type", "kapsule-dedicated-16"),
494+
testAccCheckScalewayResourceIDPersisted("scaleway_k8s_cluster.type-change", &clusterID),
495+
),
496+
},
497+
{
498+
// 4 : Downgrade to a smaller dedicated Kapsule --> should recreate
499+
Config: testAccCheckScalewayK8SClusterTypeChange("kapsule-dedicated-8", "cilium", latestK8SVersion), // + testAccCheckScalewayK8SClusterTypeChangeCopy(latestK8SVersion),
500+
Check: resource.ComposeTestCheckFunc(
501+
testAccCheckScalewayK8SClusterExists(tt, "scaleway_k8s_cluster.type-change"),
502+
resource.TestCheckResourceAttr("scaleway_k8s_cluster.type-change", "type", "kapsule-dedicated-8"),
503+
testAccCheckScalewayResourceIDChanged("scaleway_k8s_cluster.type-change", &clusterID),
504+
),
505+
},
506+
{
507+
// 5 : Change to a dedicated Kosmos --> should recreate
508+
Config: testAccCheckScalewayK8SClusterTypeChange("multicloud-dedicated-4", "kilo", latestK8SVersion), // + testAccCheckScalewayK8SClusterTypeChangeCopy(latestK8SVersion),
509+
Check: resource.ComposeTestCheckFunc(
510+
testAccCheckScalewayK8SClusterExists(tt, "scaleway_k8s_cluster.type-change"),
511+
resource.TestCheckResourceAttr("scaleway_k8s_cluster.type-change", "type", "multicloud-dedicated-4"),
512+
testAccCheckScalewayResourceIDChanged("scaleway_k8s_cluster.type-change", &clusterID),
513+
),
514+
},
515+
{
516+
// 6 : Upgrade to a bigger dedicated Kosmos --> should migrate
517+
Config: testAccCheckScalewayK8SClusterTypeChange("multicloud-dedicated-8", "kilo", latestK8SVersion), // + testAccCheckScalewayK8SClusterTypeChangeCopy(latestK8SVersion),
518+
Check: resource.ComposeTestCheckFunc(
519+
testAccCheckScalewayK8SClusterExists(tt, "scaleway_k8s_cluster.type-change"),
520+
resource.TestCheckResourceAttr("scaleway_k8s_cluster.type-change", "type", "multicloud-dedicated-8"),
521+
testAccCheckScalewayResourceIDPersisted("scaleway_k8s_cluster.type-change", &clusterID),
522+
),
523+
},
524+
{
525+
// 7 : Downgrade to a mutualized Kosmos --> should recreate
526+
Config: testAccCheckScalewayK8SClusterTypeChange("multicloud", "kilo", latestK8SVersion), // + testAccCheckScalewayK8SClusterTypeChangeCopy(latestK8SVersion),
527+
Check: resource.ComposeTestCheckFunc(
528+
testAccCheckScalewayK8SClusterExists(tt, "scaleway_k8s_cluster.type-change"),
529+
resource.TestCheckResourceAttr("scaleway_k8s_cluster.type-change", "type", "multicloud"),
530+
testAccCheckScalewayResourceIDChanged("scaleway_k8s_cluster.type-change", &clusterID),
531+
),
532+
},
533+
},
534+
})
535+
}
536+
455537
func testAccCheckScalewayK8SClusterDestroy(tt *TestTools) resource.TestCheckFunc {
456538
return func(state *terraform.State) error {
457539
for _, rs := range state.RootModule().Resources {
@@ -734,3 +816,15 @@ resource "scaleway_k8s_pool" "multicloud" {
734816
}
735817
`, version)
736818
}
819+
820+
func testAccCheckScalewayK8SClusterTypeChange(clusterType, cni, version string) string {
821+
return fmt.Sprintf(`
822+
resource "scaleway_k8s_cluster" "type-change" {
823+
type = "%s"
824+
cni = "%s"
825+
version = "%s"
826+
name = "test-type-change"
827+
tags = [ "terraform-test", "scaleway_k8s_cluster", "type-change" ]
828+
delete_additional_resources = true
829+
}`, clusterType, cni, version)
830+
}

0 commit comments

Comments
 (0)