From c47bc97fe244c0d1ed110b22b48a15162e2d1bbe Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 12 Dec 2024 12:47:48 -0500 Subject: [PATCH 01/35] Add existing changes for just modifying the attributes --- go.mod | 2 ++ .../provider/client_mock_workspaces_test.go | 4 +++ internal/provider/resource_tfe_project.go | 31 +++++++++++++++++++ .../provider/resource_tfe_project_test.go | 2 ++ internal/provider/resource_tfe_workspace.go | 22 +++++++++++-- 5 files changed, 58 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 00dc7bc9d..c8cf41cbf 100644 --- a/go.mod +++ b/go.mod @@ -82,3 +82,5 @@ require ( gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/hashicorp/go-tfe => ../go-tfe diff --git a/internal/provider/client_mock_workspaces_test.go b/internal/provider/client_mock_workspaces_test.go index 69894575d..7abfd1db9 100644 --- a/internal/provider/client_mock_workspaces_test.go +++ b/internal/provider/client_mock_workspaces_test.go @@ -157,6 +157,10 @@ func (m *mockWorkspaces) ListTags(ctx context.Context, workspaceID string, optio panic("not implemented") } +func (m *mockWorkspaces) ListEffectiveTagBindings(ctx context.Context, workspaceID string) ([]*tfe.EffectiveTagBinding, error) { + panic("not implemented") +} + func (m *mockWorkspaces) AddTags(ctx context.Context, workspaceID string, options tfe.WorkspaceAddTagsOptions) error { panic("not implemented") } diff --git a/internal/provider/resource_tfe_project.go b/internal/provider/resource_tfe_project.go index 6f4c3e4be..9e5a626a2 100644 --- a/internal/provider/resource_tfe_project.go +++ b/internal/provider/resource_tfe_project.go @@ -16,6 +16,7 @@ import ( "regexp" tfe "github.com/hashicorp/go-tfe" + "github.com/hashicorp/jsonapi" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -55,6 +56,12 @@ func resourceTFEProject() *schema.Resource { Computed: true, ForceNew: true, }, + + "auto_destroy_activity_duration": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^\d{1,4}[dh]$`), "must be 1-4 digits followed by d or h"), + }, }, } } @@ -73,6 +80,10 @@ func resourceTFEProjectCreate(ctx context.Context, d *schema.ResourceData, meta Description: tfe.String(d.Get("description").(string)), } + if v, ok := d.GetOk("auto_destroy_activity_duration"); ok { + options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(v.(string)) + } + log.Printf("[DEBUG] Create new project: %s", name) project, err := config.Client.Projects.Create(ctx, organization, options) if err != nil { @@ -102,6 +113,17 @@ func resourceTFEProjectRead(ctx context.Context, d *schema.ResourceData, meta in d.Set("description", project.Description) d.Set("organization", project.Organization.Name) + if project.AutoDestroyActivityDuration.IsSpecified() { + v, err := project.AutoDestroyActivityDuration.Get() + if err != nil { + return diag.Errorf("Error reading auto destroy activity duration: %v", err) + } + + d.Set("auto_destroy_activity_duration", v) + workspaces, err := project.Workspaces.Get() + + } + return nil } @@ -113,6 +135,15 @@ func resourceTFEProjectUpdate(ctx context.Context, d *schema.ResourceData, meta Description: tfe.String(d.Get("description").(string)), } + if d.HasChange("auto_destroy_activity_duration") { + duration, ok := d.GetOk("auto_destroy_activity_duration") + if !ok { + options.AutoDestroyActivityDuration = jsonapi.NewNullNullableAttr[string]() + } else { + options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(duration.(string)) + } + } + log.Printf("[DEBUG] Update configuration of project: %s", d.Id()) project, err := config.Client.Projects.Update(ctx, d.Id(), options) if err != nil { diff --git a/internal/provider/resource_tfe_project_test.go b/internal/provider/resource_tfe_project_test.go index a2070d885..7854f2815 100644 --- a/internal/provider/resource_tfe_project_test.go +++ b/internal/provider/resource_tfe_project_test.go @@ -37,6 +37,8 @@ func TestAccTFEProject_basic(t *testing.T) { "tfe_project.foobar", "description", "project description"), resource.TestCheckResourceAttr( "tfe_project.foobar", "organization", fmt.Sprintf("tst-terraform-%d", rInt)), + resource.TestCheckResourceAttr( + "tfe_project.foobar", "auto_destroy_activity_duration", "3d"), ), }, }, diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index 32d692b7d..b0bfe4f02 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -158,6 +158,12 @@ func resourceTFEWorkspace() *schema.Resource { Deprecated: "Use resource `tfe_workspace_settings` to modify the workspace `global_remote_state`. `global_remote_state` on `tfe_workspace` is no longer validated properly and will be removed in a future release of the provider.", }, + "inherits_project_auto_destroy": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "remote_state_consumer_ids": { Type: schema.TypeSet, Optional: true, @@ -362,6 +368,10 @@ func resourceTFEWorkspaceCreate(d *schema.ResourceData, meta interface{}) error } } + if v, ok := d.GetOk("inherits_project_auto_destroy"); ok { + options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) + } + if _, ok := d.GetOk("auto_destroy_at"); ok { autoDestroyAt, err := expandAutoDestroyAt(d) if err != nil { @@ -538,6 +548,7 @@ func resourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error { d.Set("working_directory", workspace.WorkingDirectory) d.Set("organization", workspace.Organization.Name) d.Set("resource_count", workspace.ResourceCount) + d.Set("inherits_project_auto_destroy", workspace.InheritsProjectAutoDestroy) if workspace.Links["self-html"] != nil { baseAPI := config.Client.BaseURL() @@ -573,12 +584,11 @@ func resourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error { } d.Set("auto_destroy_at", autoDestroyAt) - if workspace.AutoDestroyActivityDuration.IsSpecified() { + if workspace.AutoDestroyActivityDuration.IsSpecified() && !workspace.InheritsProjectAutoDestroy { v, err := workspace.AutoDestroyActivityDuration.Get() if err != nil { return fmt.Errorf("Error reading auto destroy activity duration: %w", err) } - d.Set("auto_destroy_activity_duration", v) } @@ -635,7 +645,7 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error d.HasChange("description") || d.HasChange("agent_pool_id") || d.HasChange("global_remote_state") || d.HasChange("structured_run_output_enabled") || d.HasChange("assessments_enabled") || d.HasChange("project_id") || - hasAutoDestroyAtChange(d) || d.HasChange("auto_destroy_activity_duration") { + hasAutoDestroyAtChange(d) || d.HasChange("auto_destroy_activity_duration") || d.HasChange("inherits_project_auto_destroy") { // Create a new options struct. options := tfe.WorkspaceUpdateOptions{ Name: tfe.String(d.Get("name").(string)), @@ -688,6 +698,12 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error } } + if d.HasChange("inherits_project_auto_destroy") { + if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { + options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) + } + } + if hasAutoDestroyAtChange(d) { autoDestroyAt, err := expandAutoDestroyAt(d) if err != nil { From 6bfea88085e14399f5948ae5511420d1d9d233c1 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 12 Dec 2024 13:23:13 -0500 Subject: [PATCH 02/35] Remove testing residues --- internal/provider/resource_tfe_project.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/provider/resource_tfe_project.go b/internal/provider/resource_tfe_project.go index 9e5a626a2..4dd4afb63 100644 --- a/internal/provider/resource_tfe_project.go +++ b/internal/provider/resource_tfe_project.go @@ -120,8 +120,6 @@ func resourceTFEProjectRead(ctx context.Context, d *schema.ResourceData, meta in } d.Set("auto_destroy_activity_duration", v) - workspaces, err := project.Workspaces.Get() - } return nil From bf80b24b7b719184f30f41260b27de4bd5d5223b Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 12 Dec 2024 16:52:49 -0500 Subject: [PATCH 03/35] Add conditional switch logic for nonbreaking changing --- internal/provider/resource_tfe_workspace.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index b0bfe4f02..42761fc61 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -161,7 +161,7 @@ func resourceTFEWorkspace() *schema.Resource { "inherits_project_auto_destroy": { Type: schema.TypeBool, Optional: true, - Default: true, + ForceNew: false, }, "remote_state_consumer_ids": { @@ -382,6 +382,7 @@ func resourceTFEWorkspaceCreate(d *schema.ResourceData, meta interface{}) error if v, ok := d.GetOk("auto_destroy_activity_duration"); ok { options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(v.(string)) + options.InheritsProjectAutoDestroy = tfe.Bool(false) } if v, ok := d.GetOk("execution_mode"); ok { @@ -698,12 +699,6 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error } } - if d.HasChange("inherits_project_auto_destroy") { - if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { - options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) - } - } - if hasAutoDestroyAtChange(d) { autoDestroyAt, err := expandAutoDestroyAt(d) if err != nil { @@ -716,8 +711,16 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error duration, ok := d.GetOk("auto_destroy_activity_duration") if !ok { options.AutoDestroyActivityDuration = jsonapi.NewNullNullableAttr[string]() + options.InheritsProjectAutoDestroy = tfe.Bool(true) } else { options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(duration.(string)) + options.InheritsProjectAutoDestroy = tfe.Bool(false) + } + } + + if d.HasChange("inherits_project_auto_destroy") { + if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { + options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) } } From cef9d18a894a5c43d04d888fcb1b3fe38212f1e3 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Mon, 16 Dec 2024 13:27:16 -0500 Subject: [PATCH 04/35] Remove automated boolean setting --- internal/provider/resource_tfe_workspace.go | 32 ++++++++++++--------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index 42761fc61..a4fa04f56 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -161,7 +161,7 @@ func resourceTFEWorkspace() *schema.Resource { "inherits_project_auto_destroy": { Type: schema.TypeBool, Optional: true, - ForceNew: false, + Default: true, }, "remote_state_consumer_ids": { @@ -382,7 +382,6 @@ func resourceTFEWorkspaceCreate(d *schema.ResourceData, meta interface{}) error if v, ok := d.GetOk("auto_destroy_activity_duration"); ok { options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(v.(string)) - options.InheritsProjectAutoDestroy = tfe.Bool(false) } if v, ok := d.GetOk("execution_mode"); ok { @@ -579,18 +578,20 @@ func resourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error { } d.Set("agent_pool_id", agentPoolID) - autoDestroyAt, err := flattenAutoDestroyAt(workspace.AutoDestroyAt) - if err != nil { - return fmt.Errorf("Error flattening auto destroy during read: %w", err) - } - d.Set("auto_destroy_at", autoDestroyAt) - - if workspace.AutoDestroyActivityDuration.IsSpecified() && !workspace.InheritsProjectAutoDestroy { - v, err := workspace.AutoDestroyActivityDuration.Get() + if !workspace.InheritsProjectAutoDestroy { + autoDestroyAt, err := flattenAutoDestroyAt(workspace.AutoDestroyAt) if err != nil { - return fmt.Errorf("Error reading auto destroy activity duration: %w", err) + return fmt.Errorf("Error flattening auto destroy during read: %w", err) + } + d.Set("auto_destroy_at", autoDestroyAt) + + if workspace.AutoDestroyActivityDuration.IsSpecified() { + v, err := workspace.AutoDestroyActivityDuration.Get() + if err != nil { + return fmt.Errorf("Error reading auto destroy activity duration: %w", err) + } + d.Set("auto_destroy_activity_duration", v) } - d.Set("auto_destroy_activity_duration", v) } var tagNames []interface{} @@ -711,10 +712,8 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error duration, ok := d.GetOk("auto_destroy_activity_duration") if !ok { options.AutoDestroyActivityDuration = jsonapi.NewNullNullableAttr[string]() - options.InheritsProjectAutoDestroy = tfe.Bool(true) } else { options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(duration.(string)) - options.InheritsProjectAutoDestroy = tfe.Bool(false) } } @@ -1097,6 +1096,11 @@ func customizeDiffAutoDestroyAt(_ context.Context, d *schema.ResourceDiff) error return nil } + // if the workspace inherits project auto destroy, we do not refresh the auto_destroy_at + if config.GetAttr("inherits_project_auto_destroy").True() { + return nil + } + // if config auto_destroy_at is unset but it exists in state, clear it out // required because auto_destroy_at is computed and we want to set it to null if _, ok := d.GetOk("auto_destroy_at"); ok && config.GetAttr("auto_destroy_at").IsNull() { From 01a4069d64df999320ec6068bbfb1e6f46d8a083 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 19 Dec 2024 15:27:56 -0500 Subject: [PATCH 05/35] Add all test changes --- .../provider/resource_tfe_project_test.go | 53 ++++- internal/provider/resource_tfe_workspace.go | 14 +- .../provider/resource_tfe_workspace_test.go | 212 +++++++++++++++++- 3 files changed, 264 insertions(+), 15 deletions(-) diff --git a/internal/provider/resource_tfe_project_test.go b/internal/provider/resource_tfe_project_test.go index 7854f2815..23b77a45d 100644 --- a/internal/provider/resource_tfe_project_test.go +++ b/internal/provider/resource_tfe_project_test.go @@ -37,8 +37,6 @@ func TestAccTFEProject_basic(t *testing.T) { "tfe_project.foobar", "description", "project description"), resource.TestCheckResourceAttr( "tfe_project.foobar", "organization", fmt.Sprintf("tst-terraform-%d", rInt)), - resource.TestCheckResourceAttr( - "tfe_project.foobar", "auto_destroy_activity_duration", "3d"), ), }, }, @@ -131,6 +129,43 @@ func TestAccTFEProject_import(t *testing.T) { }) } +func TestAccTFEProject_withAutoDestroy(t *testing.T) { + project := &tfe.Project{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckTFEProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFEProject_basicWithAutoDestroy(rInt, "3d"), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEProjectExists( + "tfe_project.foobar", project), + testAccCheckTFEProjectAttributes(project), + resource.TestCheckResourceAttr( + "tfe_project.foobar", "auto_destroy_activity_duration", "3d"), + ), + }, + { + Config: testAccTFEProject_basicWithAutoDestroy(rInt, "10m"), + ExpectError: regexp.MustCompile(`must be 1-4 digits followed by d or h`), + }, + { + Config: testAccTFEProject_basic(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEProjectExists( + "tfe_project.foobar", project), + testAccCheckTFEProjectAttributes(project), + resource.TestCheckResourceAttr( + "tfe_project.foobar", "auto_destroy_activity_duration", ""), + ), + }, + }, + }) +} + func testAccTFEProject_update(rInt int) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { @@ -184,6 +219,20 @@ resource "tfe_project" "foobar" { }`, rInt) } +func testAccTFEProject_basicWithAutoDestroy(rInt int, duration string) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_project" "foobar" { + organization = tfe_organization.foobar.name + name = "projecttest" + auto_destroy_activity_duration = "%s" +}`, rInt, duration) +} + func testAccCheckTFEProjectDestroy(s *terraform.State) error { config := testAccProvider.Meta().(ConfiguredClient) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index a4fa04f56..e59363d9c 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -368,7 +368,7 @@ func resourceTFEWorkspaceCreate(d *schema.ResourceData, meta interface{}) error } } - if v, ok := d.GetOk("inherits_project_auto_destroy"); ok { + if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) } @@ -700,6 +700,12 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error } } + if d.HasChange("inherits_project_auto_destroy") { + if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { + options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) + } + } + if hasAutoDestroyAtChange(d) { autoDestroyAt, err := expandAutoDestroyAt(d) if err != nil { @@ -717,12 +723,6 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error } } - if d.HasChange("inherits_project_auto_destroy") { - if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { - options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) - } - } - if d.HasChange("execution_mode") { if v, ok := d.GetOk("execution_mode"); ok { options.ExecutionMode = tfe.String(v.(string)) diff --git a/internal/provider/resource_tfe_workspace_test.go b/internal/provider/resource_tfe_workspace_test.go index 7d8a23526..5178f56c4 100644 --- a/internal/provider/resource_tfe_workspace_test.go +++ b/internal/provider/resource_tfe_workspace_test.go @@ -2815,6 +2815,126 @@ func TestAccTFEWorkspace_validationAutoDestroyDuration(t *testing.T) { }) } +func TestAccTFEWorkspace_createWithAutoDestroyDurationInProject(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckTFEWorkspaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFEWorkspace_basicWithAutoDestroyDurationInProject(rInt, "1d", "3d"), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_activity_duration", "3d"), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), + ), + }, + }, + }) +} + +func TestAccTFEWorkspace_updateWithAutoDestroyDurationInProject(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckTFEWorkspaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFEWorkspace_basicWithAutoDestroyDurationInProject(rInt, "1d", "3d"), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_activity_duration", "3d"), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), + ), + }, + { + Config: testAccTFEWorkspace_basicWithAutoDestroyDurationInProject(rInt, "2d", "5d"), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_activity_duration", "5d"), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), + ), + }, + { + Config: testAccTFEWorkspace_basicWithAutoDestroyDuration(rInt, "1d"), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_activity_duration", "1d"), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), + ), + }, + { + Config: testAccTFEWorkspace_basicInProject(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_activity_duration", ""), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "true"), + ), + }, + }, + }) +} + +func TestAccTFEWorkspace_createWithAutoDestroyAtInProject(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckTFEWorkspaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFEWorkspace_basicWithAutoDestroyAtInProject(rInt, "1d"), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_at", "2100-01-01T00:00:00Z"), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), + ), + }, + }, + }) +} + +func TestAccTFEWorkspace_updateWithAutoDestroyAtInProject(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckTFEWorkspaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFEWorkspace_basicWithAutoDestroyAtInProject(rInt, "1d"), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_at", "2100-01-01T00:00:00Z"), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), + ), + }, + { + Config: testAccTFEWorkspace_basicWithAutoDestroyAt(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_at", "2100-01-01T00:00:00Z"), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), + ), + }, + { + Config: testAccTFEWorkspace_basicInProject(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_at", ""), + resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "true"), + ), + }, + }, + }) +} + func TestAccTFEWorkspace_createWithSourceURL(t *testing.T) { rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() @@ -3136,15 +3256,45 @@ resource "tfe_organization" "foobar" { email = "admin@company.com" } +resource "tfe_project" "new_project" { + name = "testproject" + organization = tfe_organization.foobar.id +} + resource "tfe_workspace" "foobar" { - name = "workspace-test" - organization = tfe_organization.foobar.id - auto_apply = true - file_triggers_enabled = false - auto_destroy_at = "2100-01-01T00:00:00Z" + name = "workspace-test" + organization = tfe_organization.foobar.id + project_id = tfe_project.new_project.id + auto_apply = true + file_triggers_enabled = false + auto_destroy_at = "2100-01-01T00:00:00Z" + inherits_project_auto_destroy = false }`, rInt) } +func testAccTFEWorkspace_basicWithAutoDestroyAtInProject(rInt int, projectDuration string) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_project" "new_project" { + name = "testproject" + organization = tfe_organization.foobar.id + auto_destroy_activity_duration = "%s" +} + +resource "tfe_workspace" "foobar" { + name = "workspace-test" + organization = tfe_organization.foobar.id + project_id = tfe_project.new_project.id + auto_apply = true + auto_destroy_at = "2100-01-01T00:00:00Z" + inherits_project_auto_destroy = false +}`, rInt, projectDuration) +} + func testAccTFEWorkspace_basicWithAutoDestroyDuration(rInt int, value string) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { @@ -3152,15 +3302,65 @@ resource "tfe_organization" "foobar" { email = "admin@company.com" } +resource "tfe_project" "new_project" { + name = "testproject" + organization = tfe_organization.foobar.id +} + resource "tfe_workspace" "foobar" { name = "workspace-test" organization = tfe_organization.foobar.id + project_id = tfe_project.new_project.id auto_apply = true - file_triggers_enabled = false + file_triggers_enabled = false auto_destroy_activity_duration = "%s" + inherits_project_auto_destroy = false }`, rInt, value) } +func testAccTFEWorkspace_basicWithAutoDestroyDurationInProject(rInt int, projectDuration string, workspaceDuration string) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_project" "new_project" { + name = "testproject" + organization = tfe_organization.foobar.id + auto_destroy_activity_duration = "%s" +} + +resource "tfe_workspace" "foobar" { + name = "workspace-test" + organization = tfe_organization.foobar.id + project_id = tfe_project.new_project.id + auto_apply = true + auto_destroy_activity_duration = "%s" + inherits_project_auto_destroy = false +}`, rInt, projectDuration, workspaceDuration) +} + +func testAccTFEWorkspace_basicInProject(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_project" "new_project" { + name = "testproject" + organization = tfe_organization.foobar.id +} + +resource "tfe_workspace" "foobar" { + name = "workspace-test" + project_id = tfe_project.new_project.id + organization = tfe_organization.foobar.id + auto_apply = true +}`, rInt) +} + func testAccTFEWorkspace_operationsTrue(organization string) string { return fmt.Sprintf(` resource "tfe_workspace" "foobar" { From 468036ae15845563d348eb87f5a01bb383c44b7f Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 19 Dec 2024 16:55:54 -0500 Subject: [PATCH 06/35] Add changelog changes and datasource --- CHANGELOG.md | 6 ++++++ go.mod | 2 -- internal/provider/client_mock_workspaces_test.go | 4 ---- internal/provider/data_source_project.go | 14 ++++++++++++++ internal/provider/data_source_workspace.go | 6 ++++++ 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea95bf7e3..c16e4378f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,13 @@ ## Unreleased +BREAKING_CHANGES: +* `r/tfe_workspace`: Add attribute `inherits_project_auto_destroy` to tfe_workspace, existing workspaces with auto-destroy settings should add `inherits_project_auto_destroy: false` to tfe_workspace resource [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) + FEATURES: * `r/tfe_variable_set`: Add `parent_project_id` attribute, by @mkam [#1522](https://github.com/hashicorp/terraform-provider-tfe/pull/1522) +* `r/tfe_project`: Add `auto_destroy_activity_duration` field to the project resource, which automatically propagates auto-destroy settings to workspaces [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) +* `d/tfe_project`: Add `auto_destroy_activity_duration` field to the project datasource [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) +* `d/tfe_workspace`: Add attribute `inherits_project_auto_destroy` to tfe_workspace datasource [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) ## v0.61.0 diff --git a/go.mod b/go.mod index c8cf41cbf..00dc7bc9d 100644 --- a/go.mod +++ b/go.mod @@ -82,5 +82,3 @@ require ( gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -replace github.com/hashicorp/go-tfe => ../go-tfe diff --git a/internal/provider/client_mock_workspaces_test.go b/internal/provider/client_mock_workspaces_test.go index 7abfd1db9..69894575d 100644 --- a/internal/provider/client_mock_workspaces_test.go +++ b/internal/provider/client_mock_workspaces_test.go @@ -157,10 +157,6 @@ func (m *mockWorkspaces) ListTags(ctx context.Context, workspaceID string, optio panic("not implemented") } -func (m *mockWorkspaces) ListEffectiveTagBindings(ctx context.Context, workspaceID string) ([]*tfe.EffectiveTagBinding, error) { - panic("not implemented") -} - func (m *mockWorkspaces) AddTags(ctx context.Context, workspaceID string, options tfe.WorkspaceAddTagsOptions) error { panic("not implemented") } diff --git a/internal/provider/data_source_project.go b/internal/provider/data_source_project.go index c91e7571c..35753e38c 100644 --- a/internal/provider/data_source_project.go +++ b/internal/provider/data_source_project.go @@ -38,6 +38,11 @@ func dataSourceTFEProject() *schema.Resource { Optional: true, }, + "auto_destroy_activity_duration": { + Type: schema.TypeString, + Computed: true, + }, + "workspace_ids": { Type: schema.TypeSet, Optional: true, @@ -70,6 +75,15 @@ func dataSourceTFEProjectRead(ctx context.Context, d *schema.ResourceData, meta Name: projName, } + var autoDestroyDuration string + if project.AutoDestroyActivityDuration.IsSpecified() { + autoDestroyDuration, err = project.AutoDestroyActivityDuration.Get() + if err != nil { + return fmt.Errorf("Error reading auto destroy activity duration: %w", err) + } + } + d.Set("auto_destroy_activity_duration", autoDestroyDuration) + l, err := config.Client.Projects.List(ctx, orgName, options) if err != nil { return diag.Errorf("Error retrieving projects: %v", err) diff --git a/internal/provider/data_source_workspace.go b/internal/provider/data_source_workspace.go index 72c334a63..b10591a83 100644 --- a/internal/provider/data_source_workspace.go +++ b/internal/provider/data_source_workspace.go @@ -62,6 +62,11 @@ func dataSourceTFEWorkspace() *schema.Resource { Computed: true, }, + "inherits_project_auto_destroy": { + Type: schema.TypeBool, + Computed: true, + }, + "file_triggers_enabled": { Type: schema.TypeBool, Computed: true, @@ -249,6 +254,7 @@ func dataSourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error d.Set("file_triggers_enabled", workspace.FileTriggersEnabled) d.Set("operations", workspace.Operations) d.Set("policy_check_failures", workspace.PolicyCheckFailures) + d.Set("inherits_project_auto_destroy", workspace.InheritsProjectAutoDestroy) autoDestroyAt, err := flattenAutoDestroyAt(workspace.AutoDestroyAt) if err != nil { From 822df2e0d07fd6545b806852f9d585ba76d28051 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 19 Dec 2024 17:13:52 -0500 Subject: [PATCH 07/35] Add project data source changes --- internal/provider/data_source_project.go | 18 +++---- internal/provider/data_source_project_test.go | 50 +++++++++++++++++++ 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/internal/provider/data_source_project.go b/internal/provider/data_source_project.go index 35753e38c..f2f182dac 100644 --- a/internal/provider/data_source_project.go +++ b/internal/provider/data_source_project.go @@ -75,15 +75,6 @@ func dataSourceTFEProjectRead(ctx context.Context, d *schema.ResourceData, meta Name: projName, } - var autoDestroyDuration string - if project.AutoDestroyActivityDuration.IsSpecified() { - autoDestroyDuration, err = project.AutoDestroyActivityDuration.Get() - if err != nil { - return fmt.Errorf("Error reading auto destroy activity duration: %w", err) - } - } - d.Set("auto_destroy_activity_duration", autoDestroyDuration) - l, err := config.Client.Projects.List(ctx, orgName, options) if err != nil { return diag.Errorf("Error retrieving projects: %v", err) @@ -121,6 +112,15 @@ func dataSourceTFEProjectRead(ctx context.Context, d *schema.ResourceData, meta d.Set("workspace_ids", workspaces) d.Set("workspace_names", workspaceNames) d.Set("description", proj.Description) + + var autoDestroyDuration string + if proj.AutoDestroyActivityDuration.IsSpecified() { + autoDestroyDuration, err = proj.AutoDestroyActivityDuration.Get() + if err != nil { + return diag.Errorf("Error reading auto destroy activity duration: %v", err) + } + } + d.Set("auto_destroy_activity_duration", autoDestroyDuration) d.SetId(proj.ID) return nil } diff --git a/internal/provider/data_source_project_test.go b/internal/provider/data_source_project_test.go index 529dcd5e2..e37b0ea2a 100644 --- a/internal/provider/data_source_project_test.go +++ b/internal/provider/data_source_project_test.go @@ -62,6 +62,26 @@ func TestAccTFEProjectDataSource_caseInsensitive(t *testing.T) { }) } +func TestAccTFEProjectDataSource_basicWithAutoDestroy(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccTFEProjectDataSourceConfigWithAutoDestroy(rInt, "3d"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "data.tfe_project.foobar", "name", fmt.Sprintf("project-test-%d", rInt)), + resource.TestCheckResourceAttr( + "data.tfe_project.foobar", "auto_destroy_activity_duration", "3d"), + ), + }, + }, + }) +} + func testAccTFEProjectDataSourceConfig(rInt int) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { @@ -112,3 +132,33 @@ data "tfe_project" "foobar" { ] }`, rInt, rInt, rInt) } + +func testAccTFEProjectDataSourceConfigWithAutoDestroy(rInt int, duration string) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_project" "foobar" { + name = "project-test-%d" + description = "project description" + organization = tfe_organization.foobar.id + auto_destroy_activity_duration = "%s" +} + +data "tfe_project" "foobar" { + name = tfe_project.foobar.name + organization = tfe_project.foobar.organization + # Read the data source after creating the workspace, so counts match + depends_on = [ + tfe_workspace.foobar + ] +} + +resource "tfe_workspace" "foobar" { + name = "workspace-test-%d" + organization = tfe_organization.foobar.id + project_id = tfe_project.foobar.id +}`, rInt, rInt, duration, rInt) +} From 2152c801a95f1803d9840dc6c30458ed85ddffa8 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 19 Dec 2024 17:16:59 -0500 Subject: [PATCH 08/35] Add workspace data source tests --- internal/provider/data_source_workspace_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/internal/provider/data_source_workspace_test.go b/internal/provider/data_source_workspace_test.go index 7b2683e8c..f5c8f9159 100644 --- a/internal/provider/data_source_workspace_test.go +++ b/internal/provider/data_source_workspace_test.go @@ -184,7 +184,10 @@ func TestAccTFEWorkspaceDataSource_readAutoDestroyAt(t *testing.T) { }, { Config: testAccTFEWorkspaceDataSourceConfig_basicWithAutoDestroy(rInt), - Check: resource.TestCheckResourceAttr("data.tfe_workspace.foobar", "auto_destroy_at", "2100-01-01T00:00:00Z"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.tfe_workspace.foobar", "auto_destroy_at", "2100-01-01T00:00:00Z"), + resource.TestCheckResourceAttr("data.tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), + ), }, }, }) @@ -203,7 +206,10 @@ func TestAccTFEWorkspaceDataSource_readAutoDestroyDuration(t *testing.T) { }, { Config: testAccTFEWorkspaceDataSourceConfig_basicWithAutoDestroyDuration(rInt), - Check: resource.TestCheckResourceAttr("data.tfe_workspace.foobar", "auto_destroy_activity_duration", "1d"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.tfe_workspace.foobar", "auto_destroy_activity_duration", "1d"), + resource.TestCheckResourceAttr("data.tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), + ), }, }, }) @@ -312,6 +318,7 @@ resource "tfe_workspace" "foobar" { organization = tfe_organization.foobar.id description = "provider-testing" auto_destroy_at = "2100-01-01T00:00:00Z" + inherits_project_auto_destroy = false } data "tfe_workspace" "foobar" { @@ -332,6 +339,7 @@ resource "tfe_workspace" "foobar" { organization = tfe_organization.foobar.id description = "provider-testing" auto_destroy_activity_duration = "1d" + inherits_project_auto_destroy = false } data "tfe_workspace" "foobar" { From 63b929903305d0b2023149e94562324fce12d12c Mon Sep 17 00:00:00 2001 From: simonxmh Date: Fri, 20 Dec 2024 10:34:01 -0500 Subject: [PATCH 09/35] Add go mod replace for now --- go.mod | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.mod b/go.mod index 00dc7bc9d..c8cf41cbf 100644 --- a/go.mod +++ b/go.mod @@ -82,3 +82,5 @@ require ( gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/hashicorp/go-tfe => ../go-tfe From cfd4c95adef23670c789bcd133ad363e66be9d58 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Fri, 20 Dec 2024 13:57:30 -0500 Subject: [PATCH 10/35] Revert test for auto destroy --- internal/provider/resource_tfe_workspace_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/internal/provider/resource_tfe_workspace_test.go b/internal/provider/resource_tfe_workspace_test.go index 5178f56c4..36c468c85 100644 --- a/internal/provider/resource_tfe_workspace_test.go +++ b/internal/provider/resource_tfe_workspace_test.go @@ -3256,15 +3256,9 @@ resource "tfe_organization" "foobar" { email = "admin@company.com" } -resource "tfe_project" "new_project" { - name = "testproject" - organization = tfe_organization.foobar.id -} - resource "tfe_workspace" "foobar" { name = "workspace-test" organization = tfe_organization.foobar.id - project_id = tfe_project.new_project.id auto_apply = true file_triggers_enabled = false auto_destroy_at = "2100-01-01T00:00:00Z" @@ -3302,15 +3296,9 @@ resource "tfe_organization" "foobar" { email = "admin@company.com" } -resource "tfe_project" "new_project" { - name = "testproject" - organization = tfe_organization.foobar.id -} - resource "tfe_workspace" "foobar" { name = "workspace-test" organization = tfe_organization.foobar.id - project_id = tfe_project.new_project.id auto_apply = true file_triggers_enabled = false auto_destroy_activity_duration = "%s" From bbbc2cd72303b437a9b03c0feaa3f0a80478d937 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Mon, 6 Jan 2025 12:52:23 -0500 Subject: [PATCH 11/35] Change go mod, make automated toggle --- go.mod | 2 +- go.sum | 4 ++-- internal/provider/resource_tfe_workspace.go | 13 +++++++------ internal/provider/resource_tfe_workspace_test.go | 8 -------- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index c8cf41cbf..a6c128dea 100644 --- a/go.mod +++ b/go.mod @@ -83,4 +83,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/hashicorp/go-tfe => ../go-tfe +replace github.com/hashicorp/go-tfe => github.com/hashicorp/go-tfe v1.71.1-0.20241219170950-98975f447700 diff --git a/go.sum b/go.sum index e54be349f..a67d47f27 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISH github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-slug v0.16.0 h1:S/ko9fms1gf6305ktJNUKGxFmscZ+yWvAtsas0SYUyA= github.com/hashicorp/go-slug v0.16.0/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ= -github.com/hashicorp/go-tfe v1.70.0 h1:R5a9Z+jdVz6eRWtSLsl1nw+5Qe/swunZcJgeKK5NQtQ= -github.com/hashicorp/go-tfe v1.70.0/go.mod h1:2rOcdTxXwbWm0W7dCKjC3Ec8KQ+HhW165GiurXNshc4= +github.com/hashicorp/go-tfe v1.71.1-0.20241219170950-98975f447700 h1:h1cRUSLi8NpLG6R9PttXXx5QtG+zlJ3sLCW7s7dbSfE= +github.com/hashicorp/go-tfe v1.71.1-0.20241219170950-98975f447700/go.mod h1:2rOcdTxXwbWm0W7dCKjC3Ec8KQ+HhW165GiurXNshc4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index e59363d9c..f41bb76c9 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -700,12 +700,6 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error } } - if d.HasChange("inherits_project_auto_destroy") { - if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { - options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) - } - } - if hasAutoDestroyAtChange(d) { autoDestroyAt, err := expandAutoDestroyAt(d) if err != nil { @@ -720,6 +714,13 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error options.AutoDestroyActivityDuration = jsonapi.NewNullNullableAttr[string]() } else { options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(duration.(string)) + options.InheritsProjectAutoDestroy = tfe.Bool(false) + } + } + + if d.HasChange("inherits_project_auto_destroy") { + if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { + options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) } } diff --git a/internal/provider/resource_tfe_workspace_test.go b/internal/provider/resource_tfe_workspace_test.go index 36c468c85..b20110e3d 100644 --- a/internal/provider/resource_tfe_workspace_test.go +++ b/internal/provider/resource_tfe_workspace_test.go @@ -2859,14 +2859,6 @@ func TestAccTFEWorkspace_updateWithAutoDestroyDurationInProject(t *testing.T) { resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), ), }, - { - Config: testAccTFEWorkspace_basicWithAutoDestroyDuration(rInt, "1d"), - Check: resource.ComposeTestCheckFunc( - testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), - resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_activity_duration", "1d"), - resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), - ), - }, { Config: testAccTFEWorkspace_basicInProject(rInt), Check: resource.ComposeTestCheckFunc( From c05b61aa08311e06428c510005a0eb06fbc3ae1e Mon Sep 17 00:00:00 2001 From: simonxmh Date: Mon, 6 Jan 2025 12:54:00 -0500 Subject: [PATCH 12/35] Add missing interface method --- internal/provider/client_mock_workspaces_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/provider/client_mock_workspaces_test.go b/internal/provider/client_mock_workspaces_test.go index 69894575d..7abfd1db9 100644 --- a/internal/provider/client_mock_workspaces_test.go +++ b/internal/provider/client_mock_workspaces_test.go @@ -157,6 +157,10 @@ func (m *mockWorkspaces) ListTags(ctx context.Context, workspaceID string, optio panic("not implemented") } +func (m *mockWorkspaces) ListEffectiveTagBindings(ctx context.Context, workspaceID string) ([]*tfe.EffectiveTagBinding, error) { + panic("not implemented") +} + func (m *mockWorkspaces) AddTags(ctx context.Context, workspaceID string, options tfe.WorkspaceAddTagsOptions) error { panic("not implemented") } From 2d2c113f37bb0be2940fbf6f2922872223492479 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Tue, 7 Jan 2025 12:59:17 -0500 Subject: [PATCH 13/35] Reduce nesting and add automated logic --- internal/provider/data_source_project.go | 74 +++++++++++---------- internal/provider/resource_tfe_workspace.go | 8 ++- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/internal/provider/data_source_project.go b/internal/provider/data_source_project.go index f2f182dac..2cbf16ccc 100644 --- a/internal/provider/data_source_project.go +++ b/internal/provider/data_source_project.go @@ -82,48 +82,50 @@ func dataSourceTFEProjectRead(ctx context.Context, d *schema.ResourceData, meta for _, proj := range l.Items { // Case-insensitive uniqueness is enforced in TFC - if strings.EqualFold(proj.Name, projName) { - // Only now include workspaces to cut down on request load. - readOptions := &tfe.WorkspaceListOptions{ - ProjectID: proj.ID, + if !strings.EqualFold(proj.Name, projName) { + continue + } + // Only now include workspaces to cut down on request load. + readOptions := &tfe.WorkspaceListOptions{ + ProjectID: proj.ID, + } + var workspaces []interface{} + var workspaceNames []interface{} + for { + wl, err := config.Client.Workspaces.List(ctx, orgName, readOptions) + if err != nil { + return diag.Errorf("Error retrieving workspaces: %v", err) + } + + for _, workspace := range wl.Items { + workspaces = append(workspaces, workspace.ID) + workspaceNames = append(workspaceNames, workspace.Name) } - var workspaces []interface{} - var workspaceNames []interface{} - for { - wl, err := config.Client.Workspaces.List(ctx, orgName, readOptions) - if err != nil { - return diag.Errorf("Error retrieving workspaces: %v", err) - } - - for _, workspace := range wl.Items { - workspaces = append(workspaces, workspace.ID) - workspaceNames = append(workspaceNames, workspace.Name) - } - - // Exit the loop when we've seen all pages. - if wl.CurrentPage >= wl.TotalPages { - break - } - - // Update the page number to get the next page. - readOptions.PageNumber = wl.NextPage + + // Exit the loop when we've seen all pages. + if wl.CurrentPage >= wl.TotalPages { + break } - d.Set("workspace_ids", workspaces) - d.Set("workspace_names", workspaceNames) - d.Set("description", proj.Description) + // Update the page number to get the next page. + readOptions.PageNumber = wl.NextPage + } + + d.Set("workspace_ids", workspaces) + d.Set("workspace_names", workspaceNames) + d.Set("description", proj.Description) - var autoDestroyDuration string - if proj.AutoDestroyActivityDuration.IsSpecified() { - autoDestroyDuration, err = proj.AutoDestroyActivityDuration.Get() - if err != nil { - return diag.Errorf("Error reading auto destroy activity duration: %v", err) - } + var autoDestroyDuration string + if proj.AutoDestroyActivityDuration.IsSpecified() { + autoDestroyDuration, err = proj.AutoDestroyActivityDuration.Get() + if err != nil { + return diag.Errorf("Error reading auto destroy activity duration: %v", err) } - d.Set("auto_destroy_activity_duration", autoDestroyDuration) - d.SetId(proj.ID) - return nil } + d.Set("auto_destroy_activity_duration", autoDestroyDuration) + d.SetId(proj.ID) + + return nil } return diag.Errorf("could not find project %s/%s", orgName, projName) } diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index f41bb76c9..437725952 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -161,7 +161,6 @@ func resourceTFEWorkspace() *schema.Resource { "inherits_project_auto_destroy": { Type: schema.TypeBool, Optional: true, - Default: true, }, "remote_state_consumer_ids": { @@ -706,12 +705,19 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error expanding auto destroy during update: %w", err) } options.AutoDestroyAt = autoDestroyAt + + if autoDestroyAt.IsNull() { + options.InheritsProjectAutoDestroy = tfe.Bool(true) + } else { + options.InheritsProjectAutoDestroy = tfe.Bool(false) + } } if d.HasChange("auto_destroy_activity_duration") { duration, ok := d.GetOk("auto_destroy_activity_duration") if !ok { options.AutoDestroyActivityDuration = jsonapi.NewNullNullableAttr[string]() + options.InheritsProjectAutoDestroy = tfe.Bool(true) } else { options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(duration.(string)) options.InheritsProjectAutoDestroy = tfe.Bool(false) From c57f046fad6cd65c3788ada98a10f619b31726ec Mon Sep 17 00:00:00 2001 From: simonxmh Date: Tue, 7 Jan 2025 17:45:25 -0500 Subject: [PATCH 14/35] Use the config level setting --- internal/provider/resource_tfe_workspace.go | 23 ++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index 437725952..2564108b4 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -161,6 +161,7 @@ func resourceTFEWorkspace() *schema.Resource { "inherits_project_auto_destroy": { Type: schema.TypeBool, Optional: true, + Default: true, }, "remote_state_consumer_ids": { @@ -699,35 +700,33 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error } } + if d.HasChange("inherits_project_auto_destroy") { + if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { + options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) + } + } + if hasAutoDestroyAtChange(d) { autoDestroyAt, err := expandAutoDestroyAt(d) if err != nil { return fmt.Errorf("Error expanding auto destroy during update: %w", err) } options.AutoDestroyAt = autoDestroyAt - - if autoDestroyAt.IsNull() { - options.InheritsProjectAutoDestroy = tfe.Bool(true) - } else { - options.InheritsProjectAutoDestroy = tfe.Bool(false) - } } if d.HasChange("auto_destroy_activity_duration") { duration, ok := d.GetOk("auto_destroy_activity_duration") if !ok { options.AutoDestroyActivityDuration = jsonapi.NewNullNullableAttr[string]() - options.InheritsProjectAutoDestroy = tfe.Bool(true) } else { options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(duration.(string)) - options.InheritsProjectAutoDestroy = tfe.Bool(false) } } - if d.HasChange("inherits_project_auto_destroy") { - if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { - options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) - } + if d.GetRawConfig().GetAttr("auto_destroy_at").IsNull() && d.GetRawConfig().GetAttr("auto_destroy_activity_duration").IsNull() { + options.InheritsProjectAutoDestroy = tfe.Bool(true) + } else { + options.InheritsProjectAutoDestroy = tfe.Bool(false) } if d.HasChange("execution_mode") { From c2beedc5928b1b19ac43fab8b137b84ff94b0da5 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Wed, 8 Jan 2025 10:56:21 -0500 Subject: [PATCH 15/35] Change behavior of inherited field --- internal/provider/resource_tfe_workspace.go | 37 ++++++++++--------- .../provider/resource_tfe_workspace_test.go | 8 ---- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index 2564108b4..82b719986 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -161,7 +161,7 @@ func resourceTFEWorkspace() *schema.Resource { "inherits_project_auto_destroy": { Type: schema.TypeBool, Optional: true, - Default: true, + Computed: true, }, "remote_state_consumer_ids": { @@ -578,18 +578,18 @@ func resourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error { } d.Set("agent_pool_id", agentPoolID) - if !workspace.InheritsProjectAutoDestroy { - autoDestroyAt, err := flattenAutoDestroyAt(workspace.AutoDestroyAt) + autoDestroyAt, err := flattenAutoDestroyAt(workspace.AutoDestroyAt) + if err != nil { + return fmt.Errorf("Error flattening auto destroy during read: %w", err) + } + d.Set("auto_destroy_at", autoDestroyAt) + + if workspace.AutoDestroyActivityDuration.IsSpecified() { + v, err := workspace.AutoDestroyActivityDuration.Get() if err != nil { - return fmt.Errorf("Error flattening auto destroy during read: %w", err) + return fmt.Errorf("Error reading auto destroy activity duration: %w", err) } - d.Set("auto_destroy_at", autoDestroyAt) - - if workspace.AutoDestroyActivityDuration.IsSpecified() { - v, err := workspace.AutoDestroyActivityDuration.Get() - if err != nil { - return fmt.Errorf("Error reading auto destroy activity duration: %w", err) - } + if !workspace.InheritsProjectAutoDestroy { d.Set("auto_destroy_activity_duration", v) } } @@ -700,12 +700,6 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error } } - if d.HasChange("inherits_project_auto_destroy") { - if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { - options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) - } - } - if hasAutoDestroyAtChange(d) { autoDestroyAt, err := expandAutoDestroyAt(d) if err != nil { @@ -723,12 +717,19 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error } } + // Reset the inherits field to default if no auto destroy settings are present if d.GetRawConfig().GetAttr("auto_destroy_at").IsNull() && d.GetRawConfig().GetAttr("auto_destroy_activity_duration").IsNull() { options.InheritsProjectAutoDestroy = tfe.Bool(true) } else { options.InheritsProjectAutoDestroy = tfe.Bool(false) } + if d.HasChange("inherits_project_auto_destroy") { + if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { + options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) + } + } + if d.HasChange("execution_mode") { if v, ok := d.GetOk("execution_mode"); ok { options.ExecutionMode = tfe.String(v.(string)) @@ -1103,7 +1104,7 @@ func customizeDiffAutoDestroyAt(_ context.Context, d *schema.ResourceDiff) error } // if the workspace inherits project auto destroy, we do not refresh the auto_destroy_at - if config.GetAttr("inherits_project_auto_destroy").True() { + if config.GetAttr("inherits_project_auto_destroy").True() && config.GetAttr("auto_destroy_at").IsNull() { return nil } diff --git a/internal/provider/resource_tfe_workspace_test.go b/internal/provider/resource_tfe_workspace_test.go index b20110e3d..ad96266cc 100644 --- a/internal/provider/resource_tfe_workspace_test.go +++ b/internal/provider/resource_tfe_workspace_test.go @@ -2907,14 +2907,6 @@ func TestAccTFEWorkspace_updateWithAutoDestroyAtInProject(t *testing.T) { resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), ), }, - { - Config: testAccTFEWorkspace_basicWithAutoDestroyAt(rInt), - Check: resource.ComposeTestCheckFunc( - testAccCheckTFEWorkspaceExists("tfe_workspace.foobar", &tfe.Workspace{}, testAccProvider), - resource.TestCheckResourceAttr("tfe_workspace.foobar", "auto_destroy_at", "2100-01-01T00:00:00Z"), - resource.TestCheckResourceAttr("tfe_workspace.foobar", "inherits_project_auto_destroy", "false"), - ), - }, { Config: testAccTFEWorkspace_basicInProject(rInt), Check: resource.ComposeTestCheckFunc( From 54858bce6ff0df358f95957836a0edd1f504ec04 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 9 Jan 2025 13:24:44 -0500 Subject: [PATCH 16/35] Fix the data source test Add business org delegation to the datasource test Run go mod tidy Add interface stub Use datasource for organzation Reference data source Add missing reference Modify resource to directly reference orgname --- go.mod | 6 ++-- go.sum | 8 ++--- .../provider/client_mock_workspaces_test.go | 4 +++ internal/provider/data_source_project_test.go | 30 +++++++------------ 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index a6c128dea..afcdb141b 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-slug v0.16.0 - github.com/hashicorp/go-tfe v1.70.0 + github.com/hashicorp/go-tfe v1.73.0 github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/hcl/v2 v2.19.1 // indirect @@ -42,7 +42,7 @@ require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.3 - github.com/hashicorp/jsonapi v1.3.1 + github.com/hashicorp/jsonapi v1.3.2 github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.20.0 // indirect github.com/hashicorp/terraform-json v0.21.0 // indirect @@ -82,5 +82,3 @@ require ( gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -replace github.com/hashicorp/go-tfe => github.com/hashicorp/go-tfe v1.71.1-0.20241219170950-98975f447700 diff --git a/go.sum b/go.sum index a67d47f27..dc829d39e 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISH github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-slug v0.16.0 h1:S/ko9fms1gf6305ktJNUKGxFmscZ+yWvAtsas0SYUyA= github.com/hashicorp/go-slug v0.16.0/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ= -github.com/hashicorp/go-tfe v1.71.1-0.20241219170950-98975f447700 h1:h1cRUSLi8NpLG6R9PttXXx5QtG+zlJ3sLCW7s7dbSfE= -github.com/hashicorp/go-tfe v1.71.1-0.20241219170950-98975f447700/go.mod h1:2rOcdTxXwbWm0W7dCKjC3Ec8KQ+HhW165GiurXNshc4= +github.com/hashicorp/go-tfe v1.73.0 h1:6WuAFjNMd60Ko288K3NzCguBc+UjQQfgQoGQkiaJGQs= +github.com/hashicorp/go-tfe v1.73.0/go.mod h1:4/yd7fl8960i71v5q8cmpIiA5wB9rBRfvTpFfD/dbvE= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -79,8 +79,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= -github.com/hashicorp/jsonapi v1.3.1 h1:GtPvnmcWgYwCuDGvYT5VZBHcUyFdq9lSyCzDjn1DdPo= -github.com/hashicorp/jsonapi v1.3.1/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM= +github.com/hashicorp/jsonapi v1.3.2 h1:gP3fX2ZT7qXi+PbwieptzkspIohO2kCSiBUvUTBAbMs= +github.com/hashicorp/jsonapi v1.3.2/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo= diff --git a/internal/provider/client_mock_workspaces_test.go b/internal/provider/client_mock_workspaces_test.go index 7abfd1db9..eed1ec3e0 100644 --- a/internal/provider/client_mock_workspaces_test.go +++ b/internal/provider/client_mock_workspaces_test.go @@ -181,6 +181,10 @@ func (m *mockWorkspaces) DeleteDataRetentionPolicy(ctx context.Context, workspac panic("not implemented") } +func (m *mockWorkspaces) DeleteAllTagBindings(ctx context.Context, workspaceID string) error { + panic("not implemented") +} + //nolint:staticcheck // This method still needs to be implemented even though deprecated func (m *mockWorkspaces) ReadDataRetentionPolicy(ctx context.Context, workspaceID string) (*tfe.DataRetentionPolicy, error) { panic("not implemented") diff --git a/internal/provider/data_source_project_test.go b/internal/provider/data_source_project_test.go index e37b0ea2a..a945292bb 100644 --- a/internal/provider/data_source_project_test.go +++ b/internal/provider/data_source_project_test.go @@ -65,12 +65,19 @@ func TestAccTFEProjectDataSource_caseInsensitive(t *testing.T) { func TestAccTFEProjectDataSource_basicWithAutoDestroy(t *testing.T) { rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + tfeClient, err := getClientUsingEnv() + if err != nil { + t.Fatal(err) + } + org, orgCleanup := createBusinessOrganization(t, tfeClient) + t.Cleanup(orgCleanup) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccTFEProjectDataSourceConfigWithAutoDestroy(rInt, "3d"), + Config: testAccTFEProjectDataSourceConfigWithAutoDestroy(rInt, org.Name, "3d"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr( "data.tfe_project.foobar", "name", fmt.Sprintf("project-test-%d", rInt)), @@ -133,32 +140,17 @@ data "tfe_project" "foobar" { }`, rInt, rInt, rInt) } -func testAccTFEProjectDataSourceConfigWithAutoDestroy(rInt int, duration string) string { +func testAccTFEProjectDataSourceConfigWithAutoDestroy(rInt int, orgName string, duration string) string { return fmt.Sprintf(` -resource "tfe_organization" "foobar" { - name = "tst-terraform-%d" - email = "admin@company.com" -} - resource "tfe_project" "foobar" { name = "project-test-%d" description = "project description" - organization = tfe_organization.foobar.id + organization = "%s" auto_destroy_activity_duration = "%s" } data "tfe_project" "foobar" { name = tfe_project.foobar.name organization = tfe_project.foobar.organization - # Read the data source after creating the workspace, so counts match - depends_on = [ - tfe_workspace.foobar - ] -} - -resource "tfe_workspace" "foobar" { - name = "workspace-test-%d" - organization = tfe_organization.foobar.id - project_id = tfe_project.foobar.id -}`, rInt, rInt, duration, rInt) +}`, rInt, orgName, duration) } From 3c5ba7c6ac36f0f37a1e47824510a9509ce2e3f3 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 9 Jan 2025 16:50:55 -0500 Subject: [PATCH 17/35] Remove duplicate tag binding stub --- internal/provider/client_mock_workspaces_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/provider/client_mock_workspaces_test.go b/internal/provider/client_mock_workspaces_test.go index 6ffe4a1a2..31a87ae88 100644 --- a/internal/provider/client_mock_workspaces_test.go +++ b/internal/provider/client_mock_workspaces_test.go @@ -185,10 +185,6 @@ func (m *mockWorkspaces) DeleteDataRetentionPolicy(ctx context.Context, workspac panic("not implemented") } -func (m *mockWorkspaces) DeleteAllTagBindings(ctx context.Context, workspaceID string) error { - panic("not implemented") -} - //nolint:staticcheck // This method still needs to be implemented even though deprecated func (m *mockWorkspaces) ReadDataRetentionPolicy(ctx context.Context, workspaceID string) (*tfe.DataRetentionPolicy, error) { panic("not implemented") From 541a519d8edf5e0632bf59e5ad7f0c04c17feba9 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 9 Jan 2025 17:05:27 -0500 Subject: [PATCH 18/35] Disable beta tests --- internal/provider/data_source_project_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/provider/data_source_project_test.go b/internal/provider/data_source_project_test.go index a945292bb..0842e7ea8 100644 --- a/internal/provider/data_source_project_test.go +++ b/internal/provider/data_source_project_test.go @@ -63,6 +63,7 @@ func TestAccTFEProjectDataSource_caseInsensitive(t *testing.T) { } func TestAccTFEProjectDataSource_basicWithAutoDestroy(t *testing.T) { + skipUnlessBeta(t) rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() tfeClient, err := getClientUsingEnv() From bc421e4ab80c9725ba5c43ddd30cea0ac3ffcc67 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Fri, 10 Jan 2025 14:22:51 -0500 Subject: [PATCH 19/35] Fix creation flow --- internal/provider/resource_tfe_workspace.go | 16 +++++++--------- internal/provider/resource_tfe_workspace_test.go | 6 +----- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index 82b719986..9c3862004 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -368,20 +368,18 @@ func resourceTFEWorkspaceCreate(d *schema.ResourceData, meta interface{}) error } } - if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { - options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) - } - if _, ok := d.GetOk("auto_destroy_at"); ok { autoDestroyAt, err := expandAutoDestroyAt(d) if err != nil { return fmt.Errorf("Error expanding auto destroy during create: %w", err) } options.AutoDestroyAt = autoDestroyAt + options.InheritsProjectAutoDestroy = tfe.Bool(false) } if v, ok := d.GetOk("auto_destroy_activity_duration"); ok { options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(v.(string)) + options.InheritsProjectAutoDestroy = tfe.Bool(false) } if v, ok := d.GetOk("execution_mode"); ok { @@ -718,11 +716,11 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error } // Reset the inherits field to default if no auto destroy settings are present - if d.GetRawConfig().GetAttr("auto_destroy_at").IsNull() && d.GetRawConfig().GetAttr("auto_destroy_activity_duration").IsNull() { - options.InheritsProjectAutoDestroy = tfe.Bool(true) - } else { - options.InheritsProjectAutoDestroy = tfe.Bool(false) - } + // if d.GetRawConfig().GetAttr("auto_destroy_at").IsNull() && d.GetRawConfig().GetAttr("auto_destroy_activity_duration").IsNull() { + // options.InheritsProjectAutoDestroy = tfe.Bool(true) + // } else { + // options.InheritsProjectAutoDestroy = tfe.Bool(false) + // } if d.HasChange("inherits_project_auto_destroy") { if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { diff --git a/internal/provider/resource_tfe_workspace_test.go b/internal/provider/resource_tfe_workspace_test.go index ad96266cc..c6e66aa25 100644 --- a/internal/provider/resource_tfe_workspace_test.go +++ b/internal/provider/resource_tfe_workspace_test.go @@ -3246,7 +3246,6 @@ resource "tfe_workspace" "foobar" { auto_apply = true file_triggers_enabled = false auto_destroy_at = "2100-01-01T00:00:00Z" - inherits_project_auto_destroy = false }`, rInt) } @@ -3269,7 +3268,6 @@ resource "tfe_workspace" "foobar" { project_id = tfe_project.new_project.id auto_apply = true auto_destroy_at = "2100-01-01T00:00:00Z" - inherits_project_auto_destroy = false }`, rInt, projectDuration) } @@ -3286,7 +3284,6 @@ resource "tfe_workspace" "foobar" { auto_apply = true file_triggers_enabled = false auto_destroy_activity_duration = "%s" - inherits_project_auto_destroy = false }`, rInt, value) } @@ -3309,7 +3306,6 @@ resource "tfe_workspace" "foobar" { project_id = tfe_project.new_project.id auto_apply = true auto_destroy_activity_duration = "%s" - inherits_project_auto_destroy = false }`, rInt, projectDuration, workspaceDuration) } @@ -3909,7 +3905,7 @@ func testAccTFEWorkspace_updateRemoveVCSBlockFromTagsRegex(rInt int) string { description = "workspace-test-update-vcs-repo-tags-regex" organization = tfe_organization.foobar.id auto_apply = true - force_delete = true + force_delete = true file_triggers_enabled = true trigger_patterns = ["foo/**/*"] } From 0a185f2b3e43fc04da3e9003b281b530ff492d28 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Fri, 17 Jan 2025 10:45:40 -0500 Subject: [PATCH 20/35] Add workspace updates --- internal/provider/resource_tfe_workspace.go | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index 9c3862004..68645af2c 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -374,12 +374,10 @@ func resourceTFEWorkspaceCreate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error expanding auto destroy during create: %w", err) } options.AutoDestroyAt = autoDestroyAt - options.InheritsProjectAutoDestroy = tfe.Bool(false) } if v, ok := d.GetOk("auto_destroy_activity_duration"); ok { options.AutoDestroyActivityDuration = jsonapi.NewNullableAttrWithValue(v.(string)) - options.InheritsProjectAutoDestroy = tfe.Bool(false) } if v, ok := d.GetOk("execution_mode"); ok { @@ -645,7 +643,7 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error d.HasChange("description") || d.HasChange("agent_pool_id") || d.HasChange("global_remote_state") || d.HasChange("structured_run_output_enabled") || d.HasChange("assessments_enabled") || d.HasChange("project_id") || - hasAutoDestroyAtChange(d) || d.HasChange("auto_destroy_activity_duration") || d.HasChange("inherits_project_auto_destroy") { + hasAutoDestroyAtChange(d) || d.HasChange("auto_destroy_activity_duration") { // Create a new options struct. options := tfe.WorkspaceUpdateOptions{ Name: tfe.String(d.Get("name").(string)), @@ -715,19 +713,6 @@ func resourceTFEWorkspaceUpdate(d *schema.ResourceData, meta interface{}) error } } - // Reset the inherits field to default if no auto destroy settings are present - // if d.GetRawConfig().GetAttr("auto_destroy_at").IsNull() && d.GetRawConfig().GetAttr("auto_destroy_activity_duration").IsNull() { - // options.InheritsProjectAutoDestroy = tfe.Bool(true) - // } else { - // options.InheritsProjectAutoDestroy = tfe.Bool(false) - // } - - if d.HasChange("inherits_project_auto_destroy") { - if v, ok := d.GetOkExists("inherits_project_auto_destroy"); ok { - options.InheritsProjectAutoDestroy = tfe.Bool(v.(bool)) - } - } - if d.HasChange("execution_mode") { if v, ok := d.GetOk("execution_mode"); ok { options.ExecutionMode = tfe.String(v.(string)) @@ -1102,7 +1087,7 @@ func customizeDiffAutoDestroyAt(_ context.Context, d *schema.ResourceDiff) error } // if the workspace inherits project auto destroy, we do not refresh the auto_destroy_at - if config.GetAttr("inherits_project_auto_destroy").True() && config.GetAttr("auto_destroy_at").IsNull() { + if d.GetRawState().GetAttr("inherits_project_auto_destroy").True() && config.GetAttr("auto_destroy_at").IsNull() { return nil } From bde235b3380fdc093b551f34bb86e9ba73632de9 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Wed, 22 Jan 2025 09:42:13 -0800 Subject: [PATCH 21/35] Update change log --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46f591d20..e267f6c17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,8 @@ ## Unreleased -BREAKING_CHANGES: -* `r/tfe_workspace`: Add attribute `inherits_project_auto_destroy` to tfe_workspace, existing workspaces with auto-destroy settings should add `inherits_project_auto_destroy: false` to tfe_workspace resource [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) - FEATURES: * `r/tfe_project`: Add `auto_destroy_activity_duration` field to the project resource, which automatically propagates auto-destroy settings to workspaces [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) * `d/tfe_project`: Add `auto_destroy_activity_duration` field to the project datasource [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) -* `d/tfe_workspace`: Add attribute `inherits_project_auto_destroy` to tfe_workspace datasource [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) ## v0.62.0 From 696df89ef7c1322936fc4c1c9f49818bcb02d827 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Wed, 22 Jan 2025 09:44:50 -0800 Subject: [PATCH 22/35] Remove skip unless beta on project test --- internal/provider/data_source_project_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/provider/data_source_project_test.go b/internal/provider/data_source_project_test.go index 0842e7ea8..a945292bb 100644 --- a/internal/provider/data_source_project_test.go +++ b/internal/provider/data_source_project_test.go @@ -63,7 +63,6 @@ func TestAccTFEProjectDataSource_caseInsensitive(t *testing.T) { } func TestAccTFEProjectDataSource_basicWithAutoDestroy(t *testing.T) { - skipUnlessBeta(t) rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() tfeClient, err := getClientUsingEnv() From 786db2c905ff8bb863a7a342efdcef2bdc53db22 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Wed, 22 Jan 2025 09:47:44 -0800 Subject: [PATCH 23/35] update go-tfe dependency --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index f6ccfd2c9..f8c3fb4b2 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-slug v0.16.3 - github.com/hashicorp/go-tfe v1.73.0 + github.com/hashicorp/go-tfe v1.74.1 github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/hcl/v2 v2.19.1 // indirect @@ -61,7 +61,7 @@ require ( require ( github.com/hashicorp/terraform-plugin-framework-timetypes v0.3.0 github.com/hashicorp/terraform-plugin-log v0.9.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 go.uber.org/mock v0.4.0 ) diff --git a/go.sum b/go.sum index 728d72337..d4ea43ed2 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISH github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-slug v0.16.3 h1:pe0PMwz2UWN1168QksdW/d7u057itB2gY568iF0E2Ns= github.com/hashicorp/go-slug v0.16.3/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ= -github.com/hashicorp/go-tfe v1.73.0 h1:6WuAFjNMd60Ko288K3NzCguBc+UjQQfgQoGQkiaJGQs= -github.com/hashicorp/go-tfe v1.73.0/go.mod h1:4/yd7fl8960i71v5q8cmpIiA5wB9rBRfvTpFfD/dbvE= +github.com/hashicorp/go-tfe v1.74.1 h1:I/8fOwSYox17IZV7SULIQH0ZRPNL2g/biW6hHWnOTVY= +github.com/hashicorp/go-tfe v1.74.1/go.mod h1:kGHWMZ3HHjitgqON8nBZ4kPVJ3cLbzM4JMgmNVMs9aQ= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -153,8 +153,8 @@ github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2 github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= From 8b794ca88e9a9a219ca00af1227b580a3ac64381 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Wed, 22 Jan 2025 09:53:10 -0800 Subject: [PATCH 24/35] downgrade go-tfe --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f8c3fb4b2..682bd18ec 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-slug v0.16.3 - github.com/hashicorp/go-tfe v1.74.1 + github.com/hashicorp/go-tfe v1.73.0 github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/hcl/v2 v2.19.1 // indirect diff --git a/go.sum b/go.sum index d4ea43ed2..0f081e19b 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISH github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-slug v0.16.3 h1:pe0PMwz2UWN1168QksdW/d7u057itB2gY568iF0E2Ns= github.com/hashicorp/go-slug v0.16.3/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ= -github.com/hashicorp/go-tfe v1.74.1 h1:I/8fOwSYox17IZV7SULIQH0ZRPNL2g/biW6hHWnOTVY= -github.com/hashicorp/go-tfe v1.74.1/go.mod h1:kGHWMZ3HHjitgqON8nBZ4kPVJ3cLbzM4JMgmNVMs9aQ= +github.com/hashicorp/go-tfe v1.73.0 h1:6WuAFjNMd60Ko288K3NzCguBc+UjQQfgQoGQkiaJGQs= +github.com/hashicorp/go-tfe v1.73.0/go.mod h1:4/yd7fl8960i71v5q8cmpIiA5wB9rBRfvTpFfD/dbvE= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= From a5fb59543c737de84eb745730993df7a90382140 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Wed, 22 Jan 2025 10:04:28 -0800 Subject: [PATCH 25/35] Fix state detection --- internal/provider/resource_tfe_workspace.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index 68645af2c..e0e9f9623 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -1086,11 +1086,6 @@ func customizeDiffAutoDestroyAt(_ context.Context, d *schema.ResourceDiff) error return nil } - // if the workspace inherits project auto destroy, we do not refresh the auto_destroy_at - if d.GetRawState().GetAttr("inherits_project_auto_destroy").True() && config.GetAttr("auto_destroy_at").IsNull() { - return nil - } - // if config auto_destroy_at is unset but it exists in state, clear it out // required because auto_destroy_at is computed and we want to set it to null if _, ok := d.GetOk("auto_destroy_at"); ok && config.GetAttr("auto_destroy_at").IsNull() { @@ -1132,6 +1127,9 @@ func flattenAutoDestroyAt(a jsonapi.NullableAttr[time.Time]) (*string, error) { func hasAutoDestroyAtChange(d *schema.ResourceData) bool { state := d.GetRawState() if state.IsNull() { + if state.GetAttr("inherits_project_auto_destroy").True() { + return false + } return d.HasChange("auto_destroy_at") } From d3c92409b40bc0550cf6573939675edc94b15293 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 23 Jan 2025 11:07:54 -0800 Subject: [PATCH 26/35] Add provider doc update in projects doc --- website/docs/d/projects.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/d/projects.html.markdown b/website/docs/d/projects.html.markdown index 687dc5e88..18ed08d28 100644 --- a/website/docs/d/projects.html.markdown +++ b/website/docs/d/projects.html.markdown @@ -30,4 +30,4 @@ The following arguments are supported: * `name` - Name of the project. * `description` - Description of the organization. * `organization` - Name of the organization. - + * `auto_destroy_activity_duration` - A duration string for all workspaces in the project, representing time after each workspace's activity when an auto-destroy run will be triggered. From 0213836340ba91a4cc06df44de214fb2ee39c134 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Mon, 27 Jan 2025 09:04:57 -0800 Subject: [PATCH 27/35] Fix merged conflicts --- CHANGELOG.md | 7 +++++-- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cc470f0d..e39d00edb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## Unreleased +FEATURES: + +* `r/tfe_project`: Add `auto_destroy_activity_duration` field to the project resource, which automatically propagates auto-destroy settings to workspaces [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) +* `d/tfe_project`: Add `auto_destroy_activity_duration` field to the project datasource [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) + ## v.0.63.0 BUG FIXES: @@ -7,8 +12,6 @@ BUG FIXES: FEATURES: -* `r/tfe_project`: Add `auto_destroy_activity_duration` field to the project resource, which automatically propagates auto-destroy settings to workspaces [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) -* `d/tfe_project`: Add `auto_destroy_activity_duration` field to the project datasource [1550](https://github.com/hashicorp/terraform-provider-tfe/pull/1550) * **New Resource:** `tfe_team_notification_configuration` is a new resource for managing team notification configurations, by @jfreda ([#1540](https://github.com/hashicorp/terraform-provider-tfe/pull/1540)) ## v0.62.0 diff --git a/go.mod b/go.mod index 682bd18ec..f6ccfd2c9 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( require ( github.com/hashicorp/terraform-plugin-framework-timetypes v0.3.0 github.com/hashicorp/terraform-plugin-log v0.9.0 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.9.0 go.uber.org/mock v0.4.0 ) diff --git a/go.sum b/go.sum index 0f081e19b..728d72337 100644 --- a/go.sum +++ b/go.sum @@ -153,8 +153,8 @@ github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2 github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= From 8d46bb0ed82352bc17c5d514b7946e972510629d Mon Sep 17 00:00:00 2001 From: simonxmh Date: Thu, 23 Jan 2025 11:07:54 -0800 Subject: [PATCH 28/35] Add provider doc update in projects doc Update latest version to v0.63.0 cdktf: update documentation empty Fix merged conflicts --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e39d00edb..eb62887e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,6 @@ BUG FIXES: * `r/tfe_workspace_settings`: Allow multiple unknown values in `remote_state_consumer_ids`, by @brandonc [#1569](https://github.com/hashicorp/terraform-provider-tfe/pull/1569) FEATURES: - * **New Resource:** `tfe_team_notification_configuration` is a new resource for managing team notification configurations, by @jfreda ([#1540](https://github.com/hashicorp/terraform-provider-tfe/pull/1540)) ## v0.62.0 From 48315c8bd3e2a9c0ba9f67d5759bbf975d27d7a3 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Tue, 28 Jan 2025 14:46:13 -0800 Subject: [PATCH 29/35] Add ignore specificity based on attribute --- internal/provider/resource_tfe_workspace.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index e0e9f9623..993ad650c 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -1086,6 +1086,11 @@ func customizeDiffAutoDestroyAt(_ context.Context, d *schema.ResourceDiff) error return nil } + inheritsProjectAutoDestroy, ok := d.GetOk("inherits_project_auto_destroy") + if ok && inheritsProjectAutoDestroy.(bool) { + return nil + } + // if config auto_destroy_at is unset but it exists in state, clear it out // required because auto_destroy_at is computed and we want to set it to null if _, ok := d.GetOk("auto_destroy_at"); ok && config.GetAttr("auto_destroy_at").IsNull() { @@ -1127,9 +1132,6 @@ func flattenAutoDestroyAt(a jsonapi.NullableAttr[time.Time]) (*string, error) { func hasAutoDestroyAtChange(d *schema.ResourceData) bool { state := d.GetRawState() if state.IsNull() { - if state.GetAttr("inherits_project_auto_destroy").True() { - return false - } return d.HasChange("auto_destroy_at") } From fa1b6b5a71a023cf105d4a0f6b81458dfed99d21 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Tue, 28 Jan 2025 15:56:21 -0800 Subject: [PATCH 30/35] Change auto destroy activity duration into computed property --- internal/provider/resource_tfe_workspace.go | 25 +++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index 993ad650c..cc96a277e 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -63,6 +63,10 @@ func resourceTFEWorkspace() *schema.Resource { return err } + if err := customizeDiffAutoDestroyActivityDuration(c, d); err != nil { + return err + } + if d.HasChange("name") { if err := d.SetNewComputed("html_url"); err != nil { return err @@ -124,6 +128,7 @@ func resourceTFEWorkspace() *schema.Resource { "auto_destroy_activity_duration": { Type: schema.TypeString, + Computed: true, Optional: true, ConflictsWith: []string{"auto_destroy_at"}, ValidateFunc: validation.StringMatch(regexp.MustCompile(`^\d{1,4}[dh]$`), "must be 1-4 digits followed by d or h"), @@ -160,8 +165,9 @@ func resourceTFEWorkspace() *schema.Resource { "inherits_project_auto_destroy": { Type: schema.TypeBool, - Optional: true, + Optional: false, Computed: true, + Required: false, }, "remote_state_consumer_ids": { @@ -585,9 +591,7 @@ func resourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error { if err != nil { return fmt.Errorf("Error reading auto destroy activity duration: %w", err) } - if !workspace.InheritsProjectAutoDestroy { - d.Set("auto_destroy_activity_duration", v) - } + d.Set("auto_destroy_activity_duration", v) } var tagNames []interface{} @@ -1100,6 +1104,19 @@ func customizeDiffAutoDestroyAt(_ context.Context, d *schema.ResourceDiff) error return nil } +func customizeDiffAutoDestroyActivityDuration(_ context.Context, d *schema.ResourceDiff) error { + inheritsProjectAutoDestroy, ok := d.GetOk("inherits_project_auto_destroy") + if ok && inheritsProjectAutoDestroy.(bool) { + return nil + } + + if _, ok := d.GetOk("auto_destroy_activity_duration"); ok && d.GetRawConfig().GetAttr("auto_destroy_activity_duration").IsNull() { + return d.SetNew("auto_destroy_activity_duration", nil) + } + + return nil +} + func expandAutoDestroyAt(d *schema.ResourceData) (jsonapi.NullableAttr[time.Time], error) { v := d.GetRawConfig().GetAttr("auto_destroy_at") From 522bdd080c3e5216075c656d1f21af4fa7765571 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Tue, 28 Jan 2025 16:35:51 -0800 Subject: [PATCH 31/35] Fix data source test --- internal/provider/data_source_workspace_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/provider/data_source_workspace_test.go b/internal/provider/data_source_workspace_test.go index f5c8f9159..4a0057f41 100644 --- a/internal/provider/data_source_workspace_test.go +++ b/internal/provider/data_source_workspace_test.go @@ -318,7 +318,6 @@ resource "tfe_workspace" "foobar" { organization = tfe_organization.foobar.id description = "provider-testing" auto_destroy_at = "2100-01-01T00:00:00Z" - inherits_project_auto_destroy = false } data "tfe_workspace" "foobar" { @@ -339,7 +338,6 @@ resource "tfe_workspace" "foobar" { organization = tfe_organization.foobar.id description = "provider-testing" auto_destroy_activity_duration = "1d" - inherits_project_auto_destroy = false } data "tfe_workspace" "foobar" { From f571c80c758617433defa9c0b8a158cde86df11f Mon Sep 17 00:00:00 2001 From: simonxmh Date: Wed, 29 Jan 2025 10:36:31 -0800 Subject: [PATCH 32/35] Add read sensitivity to unsetting --- internal/provider/resource_tfe_workspace.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/provider/resource_tfe_workspace.go b/internal/provider/resource_tfe_workspace.go index cc96a277e..77cfdcaac 100644 --- a/internal/provider/resource_tfe_workspace.go +++ b/internal/provider/resource_tfe_workspace.go @@ -592,6 +592,8 @@ func resourceTFEWorkspaceRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error reading auto destroy activity duration: %w", err) } d.Set("auto_destroy_activity_duration", v) + } else { + d.Set("auto_destroy_activity_duration", nil) } var tagNames []interface{} From 6d46e79a52ab5cbc57c5869d300da65a4d86a7a1 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Mon, 3 Feb 2025 10:41:30 -0500 Subject: [PATCH 33/35] Add doc changes --- internal/provider/data_source_project_test.go | 1 + website/docs/d/workspace.html.markdown | 1 + website/docs/r/project.html.markdown | 1 + 3 files changed, 3 insertions(+) diff --git a/internal/provider/data_source_project_test.go b/internal/provider/data_source_project_test.go index a945292bb..9d339ae89 100644 --- a/internal/provider/data_source_project_test.go +++ b/internal/provider/data_source_project_test.go @@ -63,6 +63,7 @@ func TestAccTFEProjectDataSource_caseInsensitive(t *testing.T) { } func TestAccTFEProjectDataSource_basicWithAutoDestroy(t *testing.T) { + skipUnlessCloud(t) rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() tfeClient, err := getClientUsingEnv() diff --git a/website/docs/d/workspace.html.markdown b/website/docs/d/workspace.html.markdown index cc122baa5..69729ad37 100644 --- a/website/docs/d/workspace.html.markdown +++ b/website/docs/d/workspace.html.markdown @@ -40,6 +40,7 @@ In addition to all arguments above, the following attributes are exported: * `assessments_enabled` - (Available only in HCP Terraform) Indicates whether health assessments such as drift detection are enabled for the workspace. * `file_triggers_enabled` - Indicates whether runs are triggered based on the changed files in a VCS push (if `true`) or always triggered on every push (if `false`). * `global_remote_state` - (Optional) Whether the workspace should allow all workspaces in the organization to access its state data during runs. If false, then only specifically approved workspaces can access its state (determined by the `remote_state_consumer_ids` argument). +* `inherits_project_auto_destroy` - Indicates whether this workspace inherits project auto destroy settings. * `remote_state_consumer_ids` - (Optional) A set of workspace IDs that will be set as the remote state consumers for the given workspace. Cannot be used if `global_remote_state` is set to `true`. * `operations` - Indicates whether the workspace is using remote execution mode. Set to `false` to switch execution mode to local. `true` by default. * `policy_check_failures` - The number of policy check failures from the latest run. diff --git a/website/docs/r/project.html.markdown b/website/docs/r/project.html.markdown index 04471bb9c..44b7d3d2c 100644 --- a/website/docs/r/project.html.markdown +++ b/website/docs/r/project.html.markdown @@ -34,6 +34,7 @@ The following arguments are supported: * TFE versions v202405-1 and later support between 3-40 characters * `organization` - (Optional) Name of the organization. If omitted, organization must be defined in the provider config. * `description` - (Optional) A description for the project. +* `auto_destroy_activity_duration` - A duration string for all workspaces in the project, representing time after each workspace's activity when an auto-destroy run will be triggered. ## Attributes Reference From d0bf2d1b23946dcf91d56cf81286c811f2d0ad9f Mon Sep 17 00:00:00 2001 From: simonxmh Date: Mon, 3 Feb 2025 10:43:32 -0500 Subject: [PATCH 34/35] Use SkipIfEnterprise on datasource test --- internal/provider/data_source_project_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/data_source_project_test.go b/internal/provider/data_source_project_test.go index 9d339ae89..1e90d612e 100644 --- a/internal/provider/data_source_project_test.go +++ b/internal/provider/data_source_project_test.go @@ -63,7 +63,7 @@ func TestAccTFEProjectDataSource_caseInsensitive(t *testing.T) { } func TestAccTFEProjectDataSource_basicWithAutoDestroy(t *testing.T) { - skipUnlessCloud(t) + skipIfEnterprise(t) rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() tfeClient, err := getClientUsingEnv() From 6dd39b3b2056157097beaa835ffbada73d956412 Mon Sep 17 00:00:00 2001 From: simonxmh Date: Mon, 3 Feb 2025 12:02:06 -0500 Subject: [PATCH 35/35] Add inherits attr to attribute referene --- website/docs/r/workspace.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/workspace.html.markdown b/website/docs/r/workspace.html.markdown index 2011b481a..f2d4255e5 100644 --- a/website/docs/r/workspace.html.markdown +++ b/website/docs/r/workspace.html.markdown @@ -156,6 +156,7 @@ In addition to all arguments above, the following attributes are exported: * `id` - The workspace ID. * `resource_count` - The number of resources managed by the workspace. * `html_url` - The URL to the browsable HTML overview of the workspace. +* `inherits_project_auto_destroy` - Indicates whether this workspace inherits project auto destroy settings. ## Import