Skip to content

Commit 4f31de4

Browse files
committed
chore: no operation when change plan
Signed-off-by: Sammy Huang <[email protected]>
1 parent ffa8e51 commit 4f31de4

File tree

3 files changed

+192
-4
lines changed

3 files changed

+192
-4
lines changed

internal/cluster/cluster_resource.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,43 @@ func (r *ClusterResource) handleLabelsUpdate(ctx context.Context, plan, state Cl
424424
return diags
425425
}
426426

427+
func getPlanRank(plan string) int {
428+
switch zilliz.Plan(plan) {
429+
case zilliz.FreePlan:
430+
return 0
431+
case zilliz.ServerlessPlan:
432+
return 1
433+
case zilliz.StandardPlan:
434+
return 2
435+
case zilliz.EnterprisePlan:
436+
return 3
437+
default:
438+
return -1
439+
}
440+
}
441+
442+
func validatePlanUpgrade(currentPlan, newPlan string) error {
443+
if currentPlan == newPlan {
444+
return nil
445+
}
446+
447+
currentRank := getPlanRank(currentPlan)
448+
newRank := getPlanRank(newPlan)
449+
450+
if currentRank == -1 {
451+
return fmt.Errorf("invalid current plan: %s", currentPlan)
452+
}
453+
if newRank == -1 {
454+
return fmt.Errorf("invalid new plan: %s", newPlan)
455+
}
456+
457+
if newRank < currentRank {
458+
return fmt.Errorf("plan downgrade is not allowed. Cannot change from %s to %s. Only plan upgrades are supported", currentPlan, newPlan)
459+
}
460+
461+
return nil
462+
}
463+
427464
func (r *ClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
428465
tflog.Info(ctx, "Update Cluster...")
429466

@@ -451,6 +488,18 @@ func (r *ClusterResource) Update(ctx context.Context, req resource.UpdateRequest
451488
return
452489
}
453490

491+
if plan.isClusterPlanChanged(state) {
492+
// Validate plan upgrade - only allow upgrades, not downgrades
493+
if err := validatePlanUpgrade(state.Plan.ValueString(), plan.Plan.ValueString()); err != nil {
494+
resp.Diagnostics.AddError("Invalid Plan Change", err.Error())
495+
return
496+
}
497+
// no op - plan changes are not yet supported by the API
498+
// directly set the plan to the new plan
499+
// TODO: implement later on when openapi is ready
500+
state.Plan = plan.Plan
501+
}
502+
454503
if plan.isCuSizeChanged(state) {
455504
resp.Diagnostics.Append(r.handleCuSizeUpdate(ctx, plan, state)...)
456505
if resp.Diagnostics.HasError() {

internal/cluster/cluster_resource_test.go

Lines changed: 138 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cluster_test
22

33
import (
44
"fmt"
5+
"regexp"
56
"testing"
67

78
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
@@ -16,6 +17,11 @@ func TestAccClusterResource(t *testing.T) {
1617
t.Run("FreePlan", testAccClusterResourceFreePlan)
1718
t.Run("ServerlessPlan", testAccClusterResourceServerlessPlan)
1819
t.Run("StandardPlan", testAccClusterResourceStandardPlan)
20+
21+
t.Run("Plan", func(t *testing.T) {
22+
t.Run("UpdatePlan", testAccClusterResourceUpdatePlan)
23+
t.Run("PreventPlanDowngrade", testAccClusterResourcePreventPlanDowngrade)
24+
})
1925
})
2026
t.Run("BYOCEnv", func(t *testing.T) {
2127
t.Run("UpdateLabels", testAccClusterResourceUpdateLabels)
@@ -53,7 +59,7 @@ resource "zillizcloud_cluster" "test" {
5359
ResourceName: "zillizcloud_cluster.test",
5460
ImportState: true,
5561
ImportStateVerify: true,
56-
ImportStateVerifyIgnore: []string{"password", "prompt", "username", "replica"},
62+
ImportStateVerifyIgnore: []string{"password", "prompt", "username", "replica", "plan"},
5763
ImportStateIdFunc: func(state *terraform.State) (string, error) {
5864
rs, ok := state.RootModule().Resources["zillizcloud_cluster.test"]
5965
if !ok {
@@ -100,7 +106,7 @@ resource "zillizcloud_cluster" "test" {
100106
ResourceName: "zillizcloud_cluster.test",
101107
ImportState: true,
102108
ImportStateVerify: true,
103-
ImportStateVerifyIgnore: []string{"password", "prompt", "username", "replica"},
109+
ImportStateVerifyIgnore: []string{"password", "prompt", "username", "replica", "plan"},
104110
ImportStateIdFunc: func(state *terraform.State) (string, error) {
105111
rs, ok := state.RootModule().Resources["zillizcloud_cluster.test"]
106112
if !ok {
@@ -158,7 +164,7 @@ resource "zillizcloud_cluster" "test" {
158164
ResourceName: "zillizcloud_cluster.test",
159165
ImportState: true,
160166
ImportStateVerify: true,
161-
ImportStateVerifyIgnore: []string{"password", "prompt", "username"},
167+
ImportStateVerifyIgnore: []string{"password", "prompt", "username", "plan"},
162168
ImportStateIdFunc: func(state *terraform.State) (string, error) {
163169
rs, ok := state.RootModule().Resources["zillizcloud_cluster.test"]
164170
if !ok {
@@ -316,3 +322,132 @@ func testAccClusterResourceUpdateLabels(t *testing.T) {
316322
},
317323
})
318324
}
325+
326+
// test update plan field
327+
func testAccClusterResourceUpdatePlan(t *testing.T) {
328+
t.Parallel()
329+
resource.Test(t, resource.TestCase{
330+
ProtoV6ProviderFactories: provider.TestAccProtoV6ProviderFactories,
331+
Steps: []resource.TestStep{
332+
{
333+
Config: provider.ProviderConfig + `
334+
data "zillizcloud_project" "default" {
335+
}
336+
337+
resource "zillizcloud_cluster" "test" {
338+
cluster_name = "test-plan-update-cluster"
339+
project_id = data.zillizcloud_project.default.id
340+
plan = "Standard"
341+
cu_size = 1
342+
cu_type = "Performance-optimized"
343+
}
344+
`,
345+
Check: resource.ComposeAggregateTestCheckFunc(
346+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "cluster_name", "test-plan-update-cluster"),
347+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "plan", "Standard"),
348+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "cu_size", "1"),
349+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "cu_type", "Performance-optimized"),
350+
),
351+
},
352+
// update plan from Standard to Enterprise
353+
{
354+
Config: provider.ProviderConfig + `
355+
data "zillizcloud_project" "default" {
356+
}
357+
358+
resource "zillizcloud_cluster" "test" {
359+
cluster_name = "test-plan-update-cluster"
360+
project_id = data.zillizcloud_project.default.id
361+
plan = "Enterprise"
362+
cu_size = 1
363+
cu_type = "Performance-optimized"
364+
}
365+
`,
366+
Check: resource.ComposeAggregateTestCheckFunc(
367+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "cluster_name", "test-plan-update-cluster"),
368+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "plan", "Enterprise"),
369+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "cu_size", "1"),
370+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "cu_type", "Performance-optimized"),
371+
),
372+
},
373+
},
374+
})
375+
}
376+
377+
// test to prevent plan downgrade
378+
func testAccClusterResourcePreventPlanDowngrade(t *testing.T) {
379+
t.Parallel()
380+
resource.Test(t, resource.TestCase{
381+
ProtoV6ProviderFactories: provider.TestAccProtoV6ProviderFactories,
382+
Steps: []resource.TestStep{
383+
{
384+
Config: provider.ProviderConfig + `
385+
data "zillizcloud_project" "default" {
386+
}
387+
388+
resource "zillizcloud_cluster" "test" {
389+
cluster_name = "test-plan-downgrade-prevention"
390+
project_id = data.zillizcloud_project.default.id
391+
plan = "Enterprise"
392+
cu_size = 1
393+
cu_type = "Performance-optimized"
394+
}
395+
`,
396+
Check: resource.ComposeAggregateTestCheckFunc(
397+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "cluster_name", "test-plan-downgrade-prevention"),
398+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "plan", "Enterprise"),
399+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "cu_size", "1"),
400+
resource.TestCheckResourceAttr("zillizcloud_cluster.test", "cu_type", "Performance-optimized"),
401+
),
402+
},
403+
// attempt to downgrade plan from Enterprise to Standard - should fail
404+
{
405+
Config: provider.ProviderConfig + `
406+
data "zillizcloud_project" "default" {
407+
}
408+
409+
resource "zillizcloud_cluster" "test" {
410+
cluster_name = "test-plan-downgrade-prevention"
411+
project_id = data.zillizcloud_project.default.id
412+
plan = "Standard"
413+
cu_size = 1
414+
cu_type = "Performance-optimized"
415+
}
416+
`,
417+
ExpectError: regexp.MustCompile("plan downgrade is not allowed.*"),
418+
},
419+
// attempt to downgrade plan from Enterprise to Serverless - should fail
420+
{
421+
Config: provider.ProviderConfig + `
422+
data "zillizcloud_project" "default" {
423+
}
424+
425+
resource "zillizcloud_cluster" "test" {
426+
cluster_name = "test-plan-downgrade-prevention"
427+
project_id = data.zillizcloud_project.default.id
428+
plan = "Serverless"
429+
cu_size = 1
430+
cu_type = "Performance-optimized"
431+
}
432+
`,
433+
ExpectError: regexp.MustCompile("plan downgrade is not allowed.*"),
434+
},
435+
// attempt to downgrade plan from Enterprise to Free - should fail
436+
{
437+
Config: provider.ProviderConfig + `
438+
data "zillizcloud_project" "default" {
439+
}
440+
441+
resource "zillizcloud_cluster" "test" {
442+
cluster_name = "test-plan-downgrade-prevention"
443+
project_id = data.zillizcloud_project.default.id
444+
plan = "Free"
445+
cu_size = 1
446+
cu_type = "Performance-optimized"
447+
}
448+
`,
449+
ExpectError: regexp.MustCompile("plan downgrade is not allowed.*"),
450+
},
451+
},
452+
})
453+
}

internal/cluster/model.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (c *ClusterResourceModel) populate(input *ClusterResourceModel) {
4848
c.ConnectAddress = input.ConnectAddress
4949
c.PrivateLinkAddress = input.PrivateLinkAddress
5050
c.CreateTime = input.CreateTime
51-
c.Plan = input.Plan
51+
// c.Plan = input.Plan
5252
c.Replica = input.Replica
5353
c.CuSize = input.CuSize
5454
c.CuType = input.CuType
@@ -69,6 +69,10 @@ func (c *ClusterResourceModel) isCuSizeChanged(other ClusterResourceModel) bool
6969
return c.CuSize.ValueInt64() != other.CuSize.ValueInt64()
7070
}
7171

72+
func (c *ClusterResourceModel) isClusterPlanChanged(other ClusterResourceModel) bool {
73+
return c.Plan.ValueString() != other.Plan.ValueString()
74+
}
75+
7276
func (c *ClusterResourceModel) isReplicaChanged(other ClusterResourceModel) bool {
7377
return c.Replica.ValueInt64() != other.Replica.ValueInt64()
7478
}

0 commit comments

Comments
 (0)