Skip to content

Commit e58bb23

Browse files
committed
implement create and read for tfe_data_retention_policies
1 parent 6130fb4 commit e58bb23

File tree

4 files changed

+492
-0
lines changed

4 files changed

+492
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package provider
2+
3+
import (
4+
"github.com/hashicorp/go-tfe"
5+
"github.com/hashicorp/terraform-plugin-framework/types"
6+
"context"
7+
"github.com/hashicorp/terraform-plugin-framework/attr"
8+
"github.com/hashicorp/terraform-plugin-framework/diag"
9+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
10+
"math/big"
11+
)
12+
13+
type modelTFEDataRetentionPolicy struct {
14+
ID types.String `tfsdk:"id"`
15+
Organization types.String `tfsdk:"organization"`
16+
WorkspaceId types.String `tfsdk:"workspace_id"`
17+
DeleteOlderThan types.Object `tfsdk:"delete_older_than"`
18+
DontDelete types.Object `tfsdk:"dont_delete"`
19+
}
20+
21+
type modelTFEDeleteOlderThan struct {
22+
Days types.Number `tfsdk:"days"`
23+
}
24+
25+
func (m modelTFEDeleteOlderThan) AttributeTypes() map[string]attr.Type {
26+
return map[string]attr.Type{
27+
"days": types.NumberType,
28+
}
29+
}
30+
31+
func DontDeleteEmptyObject() basetypes.ObjectValue {
32+
object, diags := types.ObjectValue(map[string]attr.Type{}, map[string]attr.Value{})
33+
if diags.HasError() {
34+
panic(diags.Errors())
35+
}
36+
return object
37+
}
38+
39+
func modelFromTFEDataRetentionPolicyDeleteOlder(ctx context.Context, model modelTFEDataRetentionPolicy, deleteOlder *tfe.DataRetentionPolicyDeleteOlder) (modelTFEDataRetentionPolicy, diag.Diagnostics) {
40+
deleteOlderThan := modelTFEDeleteOlderThan{
41+
Days: types.NumberValue(big.NewFloat(float64(deleteOlder.DeleteOlderThanNDays))),
42+
}
43+
deleteOlderThanObject, diags := types.ObjectValueFrom(ctx, deleteOlderThan.AttributeTypes(), deleteOlderThan)
44+
45+
return modelTFEDataRetentionPolicy{
46+
ID: types.StringValue(deleteOlder.ID),
47+
Organization: model.Organization,
48+
WorkspaceId: model.WorkspaceId,
49+
DeleteOlderThan: deleteOlderThanObject,
50+
DontDelete: types.ObjectNull(map[string]attr.Type{}),
51+
}, diags
52+
}
53+
54+
func modelFromTFEDataRetentionPolicyDontDelete(model modelTFEDataRetentionPolicy, dontDelete *tfe.DataRetentionPolicyDontDelete) modelTFEDataRetentionPolicy {
55+
return modelTFEDataRetentionPolicy{
56+
ID: types.StringValue(dontDelete.ID),
57+
Organization: model.Organization,
58+
WorkspaceId: model.WorkspaceId,
59+
DeleteOlderThan: types.ObjectNull(modelTFEDeleteOlderThan{}.AttributeTypes()),
60+
DontDelete: DontDeleteEmptyObject(),
61+
}
62+
}
63+
64+
func modelFromTFEDataRetentionPolicyChoice(ctx context.Context, model modelTFEDataRetentionPolicy, choice *tfe.DataRetentionPolicyChoice) (modelTFEDataRetentionPolicy, diag.Diagnostics) {
65+
if choice.DataRetentionPolicyDeleteOlder != nil {
66+
return modelFromTFEDataRetentionPolicyDeleteOlder(ctx, model, choice.DataRetentionPolicyDeleteOlder)
67+
}
68+
69+
var emptyDiag []diag.Diagnostic
70+
return modelFromTFEDataRetentionPolicyDontDelete(model, choice.DataRetentionPolicyDontDelete), emptyDiag
71+
}

internal/provider/provider_next.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ func (p *frameworkProvider) Resources(ctx context.Context) []func() resource.Res
140140
NewRegistryGPGKeyResource,
141141
NewRegistryProviderResource,
142142
NewResourceVariable,
143+
NewDataRetentionPolicyResource,
143144
NewResourceWorkspaceSettings,
144145
NewSAMLSettingsResource,
145146
NewTestVariableResource,
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/hashicorp/go-tfe"
9+
"github.com/hashicorp/terraform-plugin-framework/path"
10+
"github.com/hashicorp/terraform-plugin-framework/resource"
11+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
12+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
13+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
14+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
15+
"github.com/hashicorp/terraform-plugin-log/tflog"
16+
"github.com/hashicorp/terraform-plugin-framework-validators/objectvalidator"
17+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
18+
"github.com/hashicorp/terraform-plugin-framework/types"
19+
)
20+
21+
// Ensure provider defined types fully satisfy framework interfaces.
22+
var _ resource.Resource = &resourceTFEDataRetentionPolicy{}
23+
var _ resource.ResourceWithConfigure = &resourceTFEDataRetentionPolicy{}
24+
var _ resource.ResourceWithImportState = &resourceTFEDataRetentionPolicy{}
25+
var _ resource.ResourceWithModifyPlan = &resourceTFEDataRetentionPolicy{}
26+
27+
func NewDataRetentionPolicyResource() resource.Resource {
28+
return &resourceTFEDataRetentionPolicy{}
29+
}
30+
31+
// resourceTFEDataRetentionPolicy implements the tfe_data_retention_policy resource type
32+
type resourceTFEDataRetentionPolicy struct {
33+
config ConfiguredClient
34+
}
35+
36+
func (r *resourceTFEDataRetentionPolicy) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
37+
resp.TypeName = req.ProviderTypeName + "_data_retention_policy"
38+
}
39+
40+
func (r *resourceTFEDataRetentionPolicy) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
41+
modifyPlanForDefaultOrganizationChange(ctx, r.config.Organization, req.State, req.Config, req.Plan, resp)
42+
}
43+
44+
func (r *resourceTFEDataRetentionPolicy) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
45+
resp.Schema = schema.Schema{
46+
Description: "Manages the data retention policies for a specific workspace or an the entire organization.",
47+
Version: 1,
48+
49+
Attributes: map[string]schema.Attribute{
50+
"id": schema.StringAttribute{
51+
Description: "ID of the Data Retention Policy.",
52+
Computed: true,
53+
PlanModifiers: []planmodifier.String{
54+
stringplanmodifier.UseStateForUnknown(),
55+
},
56+
},
57+
"organization": schema.StringAttribute{
58+
Description: "Name of the organization. If omitted, organization must be defined in the provider config.",
59+
Optional: true,
60+
},
61+
"workspace_id": schema.StringAttribute{
62+
Description: "ID of the workspace that the data retention policy should apply to. If omitted, the data retention policy will apply to the entire organization.",
63+
Optional: true,
64+
},
65+
},
66+
Blocks: map[string]schema.Block{
67+
"delete_older_than": schema.SingleNestedBlock{
68+
Description: "Sets the maximum number of days, months, years data is allowed to exist before it is scheduled for deletion. Cannot be configured if the dont_delete attribute is also configured.",
69+
Attributes: map[string]schema.Attribute{
70+
"days": schema.NumberAttribute{
71+
Description: "Number of days",
72+
Required: true,
73+
},
74+
},
75+
Validators: []validator.Object{
76+
objectvalidator.ExactlyOneOf(
77+
path.MatchRelative().AtParent().AtName("dont_delete"),
78+
),
79+
},
80+
},
81+
"dont_delete": schema.SingleNestedBlock{
82+
Attributes: map[string]schema.Attribute{},
83+
Validators: []validator.Object{
84+
objectvalidator.ExactlyOneOf(
85+
path.MatchRelative().AtParent().AtName("delete_older_than"),
86+
),
87+
},
88+
},
89+
},
90+
}
91+
}
92+
93+
// Configure implements resource.ResourceWithConfigure
94+
func (r *resourceTFEDataRetentionPolicy) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
95+
// Prevent panic if the provider has not been configured.
96+
if req.ProviderData == nil {
97+
return
98+
}
99+
100+
client, ok := req.ProviderData.(ConfiguredClient)
101+
if !ok {
102+
resp.Diagnostics.AddError(
103+
"Unexpected resource Configure type",
104+
fmt.Sprintf("Expected tfe.ConfiguredClient, got %T. This is a bug in the tfe provider, so please report it on GitHub.", req.ProviderData),
105+
)
106+
}
107+
r.config = client
108+
}
109+
110+
func (r *resourceTFEDataRetentionPolicy) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
111+
var plan modelTFEDataRetentionPolicy
112+
113+
// Read Terraform plan data into the model
114+
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
115+
116+
if resp.Diagnostics.HasError() {
117+
return
118+
}
119+
120+
var organization string
121+
if plan.WorkspaceId.IsNull() {
122+
resp.Diagnostics.Append(r.config.dataOrDefaultOrganization(ctx, req.Plan, &organization)...)
123+
plan.Organization = types.StringValue(organization)
124+
}
125+
126+
if resp.Diagnostics.HasError() {
127+
return
128+
}
129+
130+
if !plan.DeleteOlderThan.IsNull() {
131+
r.createDeleteOlderThanRetentionPolicy(ctx, plan, resp)
132+
return
133+
}
134+
135+
if !plan.DontDelete.IsNull() {
136+
r.createDontDeleteRetentionPolicy(ctx, plan, resp)
137+
return
138+
}
139+
140+
}
141+
142+
func (r *resourceTFEDataRetentionPolicy) createDeleteOlderThanRetentionPolicy(ctx context.Context, plan modelTFEDataRetentionPolicy, resp *resource.CreateResponse) {
143+
deleteOlderThan := &modelTFEDeleteOlderThan{}
144+
145+
diags := plan.DeleteOlderThan.As(ctx, &deleteOlderThan, basetypes.ObjectAsOptions{})
146+
if diags.HasError() {
147+
resp.Diagnostics.Append(diags...)
148+
return
149+
}
150+
151+
deleteOlderThanDays, _ := deleteOlderThan.Days.ValueBigFloat().Int64()
152+
options := tfe.DataRetentionPolicyDeleteOlderSetOptions{
153+
DeleteOlderThanNDays: int(deleteOlderThanDays),
154+
}
155+
156+
tflog.Debug(ctx, "Creating data retention policy")
157+
var dataRetentionPolicy *tfe.DataRetentionPolicyDeleteOlder
158+
var err error
159+
if plan.WorkspaceId.IsNull() {
160+
dataRetentionPolicy, err = r.config.Client.Organizations.SetDataRetentionPolicyDeleteOlder(ctx, plan.Organization.ValueString(), options)
161+
} else {
162+
dataRetentionPolicy, err = r.config.Client.Workspaces.SetDataRetentionPolicyDeleteOlder(ctx, plan.WorkspaceId.ValueString(), options)
163+
}
164+
if err != nil {
165+
resp.Diagnostics.AddError("Unable to create data retention policy", err.Error())
166+
return
167+
}
168+
169+
result, diags := modelFromTFEDataRetentionPolicyDeleteOlder(ctx, plan, dataRetentionPolicy)
170+
if diags.HasError() {
171+
resp.Diagnostics.Append(diags...)
172+
return
173+
}
174+
175+
// Save data into Terraform state
176+
resp.Diagnostics.Append(resp.State.Set(ctx, &result)...)
177+
}
178+
179+
func (r *resourceTFEDataRetentionPolicy) createDontDeleteRetentionPolicy(ctx context.Context, plan modelTFEDataRetentionPolicy, resp *resource.CreateResponse) {
180+
deleteOlderThan := &modelTFEDeleteOlderThan{}
181+
182+
diags := plan.DeleteOlderThan.As(ctx, &deleteOlderThan, basetypes.ObjectAsOptions{})
183+
if diags.HasError() {
184+
resp.Diagnostics.Append(diags...)
185+
return
186+
}
187+
188+
options := tfe.DataRetentionPolicyDontDeleteSetOptions{}
189+
190+
tflog.Debug(ctx, "Creating data retention policy")
191+
var dataRetentionPolicy *tfe.DataRetentionPolicyDontDelete
192+
var err error
193+
if plan.WorkspaceId.IsNull() {
194+
dataRetentionPolicy, err = r.config.Client.Organizations.SetDataRetentionPolicyDontDelete(ctx, plan.Organization.ValueString(), options)
195+
} else {
196+
dataRetentionPolicy, err = r.config.Client.Workspaces.SetDataRetentionPolicyDontDelete(ctx, plan.WorkspaceId.ValueString(), options)
197+
}
198+
if err != nil {
199+
resp.Diagnostics.AddError("Unable to create data retention policy", err.Error())
200+
return
201+
}
202+
203+
result := modelFromTFEDataRetentionPolicyDontDelete(plan, dataRetentionPolicy)
204+
205+
// Save data into Terraform state
206+
resp.Diagnostics.Append(resp.State.Set(ctx, &result)...)
207+
}
208+
209+
func (r *resourceTFEDataRetentionPolicy) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
210+
var state modelTFEDataRetentionPolicy
211+
212+
// Read Terraform prior state data into the model
213+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
214+
215+
if resp.Diagnostics.HasError() {
216+
return
217+
}
218+
219+
var policy *tfe.DataRetentionPolicyChoice
220+
var err error
221+
if state.WorkspaceId.IsNull() {
222+
policy, err = r.config.Client.Organizations.ReadDataRetentionPolicyChoice(ctx, state.Organization.ValueString())
223+
if err != nil {
224+
resp.Diagnostics.AddError("Failed to read data retention policy", err.Error())
225+
return
226+
}
227+
} else {
228+
policy, err = r.config.Client.Workspaces.ReadDataRetentionPolicyChoice(ctx, state.WorkspaceId.ValueString())
229+
if err != nil {
230+
resp.Diagnostics.AddError("Failed to read data retention policy", err.Error())
231+
return
232+
}
233+
}
234+
result, diags := modelFromTFEDataRetentionPolicyChoice(ctx, state, policy)
235+
if diags.HasError() {
236+
resp.Diagnostics.Append(diags...)
237+
return
238+
}
239+
240+
// Save data into Terraform state
241+
resp.Diagnostics.Append(resp.State.Set(ctx, &result)...)
242+
}
243+
244+
func (r *resourceTFEDataRetentionPolicy) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
245+
// If the resource does not support modification and should always be recreated on
246+
// configuration value updates, the Update logic can be left empty and ensure all
247+
// configurable schema attributes implement the resource.RequiresReplace()
248+
// attribute plan modifier.
249+
resp.Diagnostics.AddError("Update not supported", "The update operation is not supported on this resource. This is a bug in the provider.")
250+
}
251+
252+
func (r *resourceTFEDataRetentionPolicy) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
253+
//var state modelTFERegistryGPGKey
254+
//
255+
//// Read Terraform prior state data into the model
256+
//resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
257+
//
258+
//if resp.Diagnostics.HasError() {
259+
// return
260+
//}
261+
//
262+
//keyID := tfe.GPGKeyID{
263+
// RegistryName: "private",
264+
// Namespace: state.Organization.ValueString(),
265+
// KeyID: state.ID.ValueString(),
266+
//}
267+
//
268+
//tflog.Debug(ctx, "Deleting private registry GPG key")
269+
//err := r.config.Client.GPGKeys.Delete(ctx, keyID)
270+
//if err != nil {
271+
// resp.Diagnostics.AddError("Unable to delete private registry GPG key", err.Error())
272+
// return
273+
//}
274+
}
275+
276+
func (r *resourceTFEDataRetentionPolicy) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
277+
s := strings.SplitN(req.ID, "/", 2)
278+
if len(s) != 2 {
279+
resp.Diagnostics.AddError(
280+
"Error importing variable",
281+
fmt.Sprintf("Invalid variable import format: %s (expected <ORGANIZATION>/<KEY ID>)", req.ID),
282+
)
283+
return
284+
}
285+
org := s[0]
286+
id := s[1]
287+
288+
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("organization"), org)...)
289+
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), id)...)
290+
}

0 commit comments

Comments
 (0)