Skip to content

Commit 13ed4e9

Browse files
committed
fix issues with organization not being set after applies and add support for dont_delete policies
1 parent 89d70db commit 13ed4e9

File tree

3 files changed

+249
-38
lines changed

3 files changed

+249
-38
lines changed

internal/provider/data_retention_policy.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,31 @@ func modelFromTFEDataRetentionPolicyDontDelete(model modelTFEDataRetentionPolicy
6161
}
6262
}
6363

64+
func modelFromTFELegacyDataRetentionPolicy(ctx context.Context, model modelTFEDataRetentionPolicy, legacy *tfe.DataRetentionPolicy) (modelTFEDataRetentionPolicy, diag.Diagnostics) {
65+
deleteOlderThan := modelTFEDeleteOlderThan{
66+
Days: types.NumberValue(big.NewFloat(float64(legacy.DeleteOlderThanNDays))),
67+
}
68+
deleteOlderThanObject, diags := types.ObjectValueFrom(ctx, deleteOlderThan.AttributeTypes(), deleteOlderThan)
69+
70+
return modelTFEDataRetentionPolicy{
71+
ID: types.StringValue(legacy.ID),
72+
Organization: model.Organization,
73+
WorkspaceId: model.WorkspaceId,
74+
DeleteOlderThan: deleteOlderThanObject,
75+
DontDelete: types.ObjectNull(map[string]attr.Type{}),
76+
}, diags
77+
}
78+
6479
func modelFromTFEDataRetentionPolicyChoice(ctx context.Context, model modelTFEDataRetentionPolicy, choice *tfe.DataRetentionPolicyChoice) (modelTFEDataRetentionPolicy, diag.Diagnostics) {
6580
if choice.DataRetentionPolicyDeleteOlder != nil {
6681
return modelFromTFEDataRetentionPolicyDeleteOlder(ctx, model, choice.DataRetentionPolicyDeleteOlder)
6782
}
6883

6984
var emptyDiag []diag.Diagnostic
70-
return modelFromTFEDataRetentionPolicyDontDelete(model, choice.DataRetentionPolicyDontDelete), emptyDiag
85+
if choice.DataRetentionPolicyDontDelete != nil {
86+
return modelFromTFEDataRetentionPolicyDontDelete(model, choice.DataRetentionPolicyDontDelete), emptyDiag
87+
}
88+
89+
legacyPolicy := choice.ConvertToLegacyStruct()
90+
return modelFromTFELegacyDataRetentionPolicy(ctx, model, legacyPolicy)
7191
}

internal/provider/resource_tfe_data_retention_policy.go

Lines changed: 88 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package provider
33
import (
44
"context"
55
"fmt"
6-
"strings"
7-
86
"github.com/hashicorp/go-tfe"
97
"github.com/hashicorp/terraform-plugin-framework/path"
108
"github.com/hashicorp/terraform-plugin-framework/resource"
@@ -16,14 +14,16 @@ import (
1614
"github.com/hashicorp/terraform-plugin-framework-validators/objectvalidator"
1715
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1816
"github.com/hashicorp/terraform-plugin-framework/types"
17+
"github.com/hashicorp/terraform-plugin-framework/diag"
18+
"strings"
1919
"github.com/hashicorp/terraform-plugin-framework/resource/schema/numberplanmodifier"
20+
"github.com/hashicorp/terraform-plugin-framework-validators/numbervalidator"
2021
)
2122

2223
// Ensure provider defined types fully satisfy framework interfaces.
2324
var _ resource.Resource = &resourceTFEDataRetentionPolicy{}
2425
var _ resource.ResourceWithConfigure = &resourceTFEDataRetentionPolicy{}
2526
var _ resource.ResourceWithImportState = &resourceTFEDataRetentionPolicy{}
26-
var _ resource.ResourceWithModifyPlan = &resourceTFEDataRetentionPolicy{}
2727

2828
func NewDataRetentionPolicyResource() resource.Resource {
2929
return &resourceTFEDataRetentionPolicy{}
@@ -38,10 +38,6 @@ func (r *resourceTFEDataRetentionPolicy) Metadata(ctx context.Context, req resou
3838
resp.TypeName = req.ProviderTypeName + "_data_retention_policy"
3939
}
4040

41-
func (r *resourceTFEDataRetentionPolicy) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
42-
modifyPlanForDefaultOrganizationChange(ctx, r.config.Organization, req.State, req.Config, req.Plan, resp)
43-
}
44-
4541
func (r *resourceTFEDataRetentionPolicy) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
4642
resp.Schema = schema.Schema{
4743
Description: "Manages the data retention policies for a specific workspace or an the entire organization.",
@@ -60,6 +56,7 @@ func (r *resourceTFEDataRetentionPolicy) Schema(ctx context.Context, req resourc
6056
Optional: true,
6157
Computed: true,
6258
PlanModifiers: []planmodifier.String{
59+
stringplanmodifier.UseStateForUnknown(),
6360
stringplanmodifier.RequiresReplace(),
6461
},
6562
},
@@ -77,17 +74,17 @@ func (r *resourceTFEDataRetentionPolicy) Schema(ctx context.Context, req resourc
7774
Attributes: map[string]schema.Attribute{
7875
"days": schema.NumberAttribute{
7976
Description: "Number of days",
80-
Required: true,
77+
Optional: true,
8178
PlanModifiers: []planmodifier.Number{
8279
numberplanmodifier.RequiresReplace(),
8380
},
81+
Validators: []validator.Number{
82+
numbervalidator.ExactlyOneOf(
83+
path.MatchRelative().AtParent().AtParent().AtName("dont_delete"),
84+
),
85+
},
8486
},
8587
},
86-
Validators: []validator.Object{
87-
objectvalidator.ExactlyOneOf(
88-
path.MatchRelative().AtParent().AtName("dont_delete"),
89-
),
90-
},
9188
},
9289
"dont_delete": schema.SingleNestedBlock{
9390
Attributes: map[string]schema.Attribute{},
@@ -128,11 +125,7 @@ func (r *resourceTFEDataRetentionPolicy) Create(ctx context.Context, req resourc
128125
return
129126
}
130127

131-
var organization string
132-
if plan.WorkspaceId.IsNull() {
133-
resp.Diagnostics.Append(r.config.dataOrDefaultOrganization(ctx, req.Plan, &organization)...)
134-
plan.Organization = types.StringValue(organization)
135-
}
128+
r.ensureOrganizationIsSet(ctx, &plan, req.Plan, &resp.Diagnostics)
136129

137130
if resp.Diagnostics.HasError() {
138131
return
@@ -150,6 +143,19 @@ func (r *resourceTFEDataRetentionPolicy) Create(ctx context.Context, req resourc
150143

151144
}
152145

146+
func (r *resourceTFEDataRetentionPolicy) ensureOrganizationIsSet(ctx context.Context, model *modelTFEDataRetentionPolicy, data AttrGettable, diags *diag.Diagnostics) {
147+
if !model.Organization.IsUnknown() || model.Organization.ValueString() != "" {
148+
// skip this method if the organization has already been set
149+
return
150+
}
151+
152+
if model.WorkspaceId.IsNull() {
153+
var organization string
154+
diags.Append(r.config.dataOrDefaultOrganization(ctx, data, &organization)...)
155+
model.Organization = types.StringValue(organization)
156+
}
157+
}
158+
153159
func (r *resourceTFEDataRetentionPolicy) createDeleteOlderThanRetentionPolicy(ctx context.Context, plan modelTFEDataRetentionPolicy, resp *resource.CreateResponse) {
154160
deleteOlderThan := &modelTFEDeleteOlderThan{}
155161

@@ -183,8 +189,12 @@ func (r *resourceTFEDataRetentionPolicy) createDeleteOlderThanRetentionPolicy(ct
183189
return
184190
}
185191

192+
// set organization if it is still not known after creating the data retention policy
193+
r.ensureOrganizationSetAfterApply(&result, &resp.Diagnostics)
194+
186195
// Save data into Terraform state
187-
resp.Diagnostics.Append(resp.State.Set(ctx, &result)...)
196+
diags = resp.State.Set(ctx, &result)
197+
resp.Diagnostics.Append(diags...)
188198
}
189199

190200
func (r *resourceTFEDataRetentionPolicy) createDontDeleteRetentionPolicy(ctx context.Context, plan modelTFEDataRetentionPolicy, resp *resource.CreateResponse) {
@@ -213,8 +223,23 @@ func (r *resourceTFEDataRetentionPolicy) createDontDeleteRetentionPolicy(ctx con
213223

214224
result := modelFromTFEDataRetentionPolicyDontDelete(plan, dataRetentionPolicy)
215225

226+
// set organization if it is still not known after creating the data retention policy
227+
r.ensureOrganizationSetAfterApply(&result, &resp.Diagnostics)
228+
216229
// Save data into Terraform state
217-
resp.Diagnostics.Append(resp.State.Set(ctx, &result)...)
230+
diags = resp.State.Set(ctx, &result)
231+
resp.Diagnostics.Append(diags...)
232+
}
233+
234+
func (r *resourceTFEDataRetentionPolicy) ensureOrganizationSetAfterApply(policy *modelTFEDataRetentionPolicy, diags *diag.Diagnostics) {
235+
if policy.Organization.IsUnknown() {
236+
workspace, err := r.config.Client.Workspaces.ReadByID(ctx, policy.WorkspaceId.ValueString())
237+
if err != nil {
238+
diags.AddError("Unable to create data retention policy", err.Error())
239+
return
240+
}
241+
policy.Organization = types.StringValue(workspace.Organization.Name)
242+
}
218243
}
219244

220245
func (r *resourceTFEDataRetentionPolicy) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
@@ -289,18 +314,50 @@ func (r *resourceTFEDataRetentionPolicy) Delete(ctx context.Context, req resourc
289314
}
290315

291316
func (r *resourceTFEDataRetentionPolicy) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
292-
s := strings.SplitN(req.ID, "/", 2)
317+
s := strings.Split(req.ID, "/")
318+
if len(s) >= 3 {
319+
resp.Diagnostics.AddError("Error importing workspace settings", fmt.Sprintf(
320+
"invalid workspace input format: %s (expected <ORGANIZATION>/<WORKSPACE NAME> or <ORGANIZATION>)",
321+
req.ID,
322+
))
323+
} else if len(s) == 2 {
324+
workspaceID, err := fetchWorkspaceExternalID(s[0]+"/"+s[1], r.config.Client)
325+
if err != nil {
326+
resp.Diagnostics.AddError("Error importing data retention policy", fmt.Sprintf(
327+
"error retrieving workspace with name %s from organization %s: %s", s[1], s[0], err.Error(),
328+
))
329+
}
293330

294-
if len(s) != 2 && len(s) != 1 {
295-
resp.Diagnostics.AddError(
296-
"Error importing variable",
297-
fmt.Sprintf("Invalid variable import format: %s (expected <ORGANIZATION>/<WORKSPACE ID> or <ORGANIZATION>)", req.ID),
298-
)
299-
return
331+
policy, err := r.config.Client.Workspaces.ReadDataRetentionPolicyChoice(ctx, workspaceID)
332+
if err != nil {
333+
resp.Diagnostics.AddError("Error importing data retention policy", fmt.Sprintf(
334+
"error retrieving data policy for workspace %s from organization %s: %s", s[1], s[0], err.Error(),
335+
))
336+
}
337+
338+
req.ID = r.getPolicyID(policy)
339+
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("workspace_id"), workspaceID)...)
340+
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("organization"), s[0])...)
341+
} else if len(s) == 1 {
342+
policy, err := r.config.Client.Organizations.ReadDataRetentionPolicyChoice(ctx, s[0])
343+
if err != nil {
344+
resp.Diagnostics.AddError("Error importing data retention policy", fmt.Sprintf(
345+
"error retrieving data policy for organization %s: %s", s[0], err.Error(),
346+
))
347+
}
348+
req.ID = r.getPolicyID(policy)
349+
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("organization"), s[0])...)
350+
}
351+
}
352+
353+
func (r *resourceTFEDataRetentionPolicy) getPolicyID(policy *tfe.DataRetentionPolicyChoice) string {
354+
if policy.DataRetentionPolicyDeleteOlder != nil {
355+
return policy.DataRetentionPolicyDeleteOlder.ID
356+
}
357+
358+
if policy.DataRetentionPolicyDontDelete != nil {
359+
return policy.DataRetentionPolicyDontDelete.ID
300360
}
301-
org := s[0]
302-
wsId := s[1]
303361

304-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("organization"), org)...)
305-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("workspace_id"), wsId)...)
362+
return policy.ConvertToLegacyStruct().ID
306363
}

0 commit comments

Comments
 (0)