From 1bafa3a13e51ece8e4f4b404a04c9ffa2177a8d9 Mon Sep 17 00:00:00 2001 From: Susana Ferreira Date: Mon, 26 May 2025 17:48:03 +0000 Subject: [PATCH 1/2] chore: add minimum TTL value for expiration.policy.ttl --- provider/workspace_preset.go | 4 ++-- provider/workspace_preset_test.go | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/provider/workspace_preset.go b/provider/workspace_preset.go index e9c697c..b648d11 100644 --- a/provider/workspace_preset.go +++ b/provider/workspace_preset.go @@ -103,8 +103,8 @@ func workspacePresetDataSource() *schema.Resource { Description: "Time in seconds after which an unclaimed prebuild is considered expired and eligible for cleanup.", Required: true, ForceNew: true, - // Ensure TTL is between 0 and 31536000 seconds (1 year) to prevent stale prebuilds - ValidateFunc: validation.IntBetween(0, 31536000), + // Ensure TTL is between 3600 seconds (1 hour) and 31536000 seconds (1 year) + ValidateFunc: validation.IntBetween(3600, 31536000), }, }, }, diff --git a/provider/workspace_preset_test.go b/provider/workspace_preset_test.go index d9f7da4..c542a88 100644 --- a/provider/workspace_preset_test.go +++ b/provider/workspace_preset_test.go @@ -186,6 +186,23 @@ func TestWorkspacePreset(t *testing.T) { return nil }, }, + { + Name: "Prebuilds block with expiration_policy.ttl set to 30 minutes (below 1 hour limit)", + Config: ` + data "coder_workspace_preset" "preset_1" { + name = "preset_1" + parameters = { + "region" = "us-east1-a" + } + prebuilds { + instances = 1 + expiration_policy { + ttl = 1800 + } + } + }`, + ExpectError: regexp.MustCompile(`expected prebuilds.0.expiration_policy.0.ttl to be in the range \(3600 - 31536000\), got 1800`), + }, { Name: "Prebuilds block with expiration_policy.ttl set to 2 years (exceeds 1 year limit)", Config: ` @@ -201,7 +218,7 @@ func TestWorkspacePreset(t *testing.T) { } } }`, - ExpectError: regexp.MustCompile(`expected prebuilds.0.expiration_policy.0.ttl to be in the range \(0 - 31536000\), got 63072000`), + ExpectError: regexp.MustCompile(`expected prebuilds.0.expiration_policy.0.ttl to be in the range \(3600 - 31536000\), got 63072000`), }, { Name: "Prebuilds is set with a expiration_policy field with its required fields and an unexpected argument", From d419bd8bda8832dfd2d189113c0d872c2d53430f Mon Sep 17 00:00:00 2001 From: Susana Ferreira Date: Tue, 27 May 2025 09:13:14 +0000 Subject: [PATCH 2/2] chore: support 0 as a valid ttl value in expiration_policy to disable expiration --- provider/workspace_preset.go | 14 +++++++++++-- provider/workspace_preset_test.go | 33 ++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/provider/workspace_preset.go b/provider/workspace_preset.go index b648d11..e0f2276 100644 --- a/provider/workspace_preset.go +++ b/provider/workspace_preset.go @@ -2,6 +2,7 @@ package provider import ( "context" + "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -103,8 +104,17 @@ func workspacePresetDataSource() *schema.Resource { Description: "Time in seconds after which an unclaimed prebuild is considered expired and eligible for cleanup.", Required: true, ForceNew: true, - // Ensure TTL is between 3600 seconds (1 hour) and 31536000 seconds (1 year) - ValidateFunc: validation.IntBetween(3600, 31536000), + // Ensure TTL is either 0 (to disable expiration) or between 3600 seconds (1 hour) and 31536000 seconds (1 year) + ValidateFunc: func(val interface{}, key string) ([]string, []error) { + v := val.(int) + if v == 0 { + return nil, nil + } + if v < 3600 || v > 31536000 { + return nil, []error{fmt.Errorf("%q must be 0 or between 3600 and 31536000, got %d", key, v)} + } + return nil, nil + }, }, }, }, diff --git a/provider/workspace_preset_test.go b/provider/workspace_preset_test.go index c542a88..b8e752a 100644 --- a/provider/workspace_preset_test.go +++ b/provider/workspace_preset_test.go @@ -157,7 +157,7 @@ func TestWorkspacePreset(t *testing.T) { expiration_policy {} } }`, - ExpectError: regexp.MustCompile("The argument \"ttl\" is required, but no definition was found."), + ExpectError: regexp.MustCompile(`The argument "ttl" is required, but no definition was found.`), }, { Name: "Prebuilds is set with a expiration_policy field with its required fields", @@ -186,6 +186,33 @@ func TestWorkspacePreset(t *testing.T) { return nil }, }, + { + Name: "Prebuilds block with expiration_policy.ttl set to 0 seconds (disables expiration)", + Config: ` + data "coder_workspace_preset" "preset_1" { + name = "preset_1" + parameters = { + "region" = "us-east1-a" + } + prebuilds { + instances = 1 + expiration_policy { + ttl = 0 + } + } + }`, + ExpectError: nil, + Check: func(state *terraform.State) error { + require.Len(t, state.Modules, 1) + require.Len(t, state.Modules[0].Resources, 1) + resource := state.Modules[0].Resources["data.coder_workspace_preset.preset_1"] + require.NotNil(t, resource) + attrs := resource.Primary.Attributes + require.Equal(t, attrs["name"], "preset_1") + require.Equal(t, attrs["prebuilds.0.expiration_policy.0.ttl"], "0") + return nil + }, + }, { Name: "Prebuilds block with expiration_policy.ttl set to 30 minutes (below 1 hour limit)", Config: ` @@ -201,7 +228,7 @@ func TestWorkspacePreset(t *testing.T) { } } }`, - ExpectError: regexp.MustCompile(`expected prebuilds.0.expiration_policy.0.ttl to be in the range \(3600 - 31536000\), got 1800`), + ExpectError: regexp.MustCompile(`"prebuilds.0.expiration_policy.0.ttl" must be 0 or between 3600 and 31536000, got 1800`), }, { Name: "Prebuilds block with expiration_policy.ttl set to 2 years (exceeds 1 year limit)", @@ -218,7 +245,7 @@ func TestWorkspacePreset(t *testing.T) { } } }`, - ExpectError: regexp.MustCompile(`expected prebuilds.0.expiration_policy.0.ttl to be in the range \(3600 - 31536000\), got 63072000`), + ExpectError: regexp.MustCompile(`"prebuilds.0.expiration_policy.0.ttl" must be 0 or between 3600 and 31536000, got 63072000`), }, { Name: "Prebuilds is set with a expiration_policy field with its required fields and an unexpected argument",