Skip to content

Commit 95dec49

Browse files
author
Uroš Simović
committed
feat: add stateful_list resource
1 parent 81c6ae9 commit 95dec49

File tree

3 files changed

+210
-0
lines changed

3 files changed

+210
-0
lines changed

docs/resources/stateful_list.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "misc_stateful_list Resource - misc"
4+
subcategory: ""
5+
description: |-
6+
Stateful list takes items from the input and preserve them in the output. The item will always be preserved in the output even if removed from the input. Once in, always out!
7+
---
8+
9+
# misc_stateful_list (Resource)
10+
11+
Stateful list takes items from the input and preserve them in the output. The item will always be preserved in the output even if removed from the input. Once in, always out!
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Required
19+
20+
- `input` (Set of String) Set of strings to preserve in the output.
21+
22+
### Read-Only
23+
24+
- `id` (String) Random id.
25+
- `output` (Set of String) Always preserved input. Once in, always out.

misc/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,6 @@ func (p *kiwiProvider) DataSources(_ context.Context) []func() datasource.DataSo
5252
func (p *kiwiProvider) Resources(_ context.Context) []func() resource.Resource {
5353
return []func() resource.Resource{
5454
NewClaimFromPoolResource,
55+
NewStatefulListResource,
5556
}
5657
}

misc/stateful_list_resource.go

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package misc
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/diag"
8+
"github.com/hashicorp/terraform-plugin-framework/path"
9+
"github.com/hashicorp/terraform-plugin-framework/resource"
10+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
11+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
12+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
13+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
14+
"github.com/hashicorp/terraform-plugin-framework/types"
15+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
16+
)
17+
18+
// Ensure the implementation satisfies the expected interfaces.
19+
var (
20+
_ resource.Resource = &statefulList{}
21+
_ resource.ResourceWithImportState = &statefulList{}
22+
_ resource.ResourceWithModifyPlan = &statefulList{}
23+
)
24+
25+
// NewStatefulList is a helper function to simplify the provider implementation.
26+
func NewStatefulListResource() resource.Resource {
27+
return &statefulList{}
28+
}
29+
30+
// statefulList is the resource implementation.
31+
type statefulList struct{}
32+
33+
// statefulListModel maps the resource schema data.
34+
type statefulListModel struct {
35+
ID basetypes.StringValue `tfsdk:"id"`
36+
Input basetypes.SetValue `tfsdk:"input"`
37+
Output basetypes.SetValue `tfsdk:"output"`
38+
}
39+
40+
// Metadata returns the data source type name.
41+
func (r *statefulList) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
42+
resp.TypeName = req.ProviderTypeName + "_stateful_list"
43+
}
44+
45+
// Schema defines the schema for the data source.
46+
func (r *statefulList) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
47+
resp.Schema = schema.Schema{
48+
Description: "Stateful list takes items from the input and preserve them in the output. " +
49+
"The item will always be preserved in the output even if removed from the input. " +
50+
"Once in, always out!",
51+
Attributes: map[string]schema.Attribute{
52+
"id": schema.StringAttribute{
53+
Description: "Random id.",
54+
Computed: true,
55+
PlanModifiers: []planmodifier.String{
56+
stringplanmodifier.UseStateForUnknown(),
57+
},
58+
},
59+
"input": schema.SetAttribute{
60+
ElementType: types.StringType,
61+
Description: "Set of strings to preserve in the output.",
62+
Required: true,
63+
},
64+
"output": schema.SetAttribute{
65+
ElementType: types.StringType,
66+
Description: "Always preserved input. Once in, always out.",
67+
Computed: true,
68+
},
69+
},
70+
}
71+
}
72+
73+
// Create creates the resource and sets the initial Terraform state.
74+
func (r *statefulList) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
75+
// Retrieve values from plan
76+
var plan statefulListModel
77+
diags := req.Plan.Get(ctx, &plan)
78+
resp.Diagnostics.Append(diags...)
79+
if resp.Diagnostics.HasError() {
80+
return
81+
}
82+
plan.ID = types.StringValue(time.Now().Format(time.RFC3339Nano))
83+
84+
// Set state to fully populated data
85+
diags = resp.State.Set(ctx, plan)
86+
resp.Diagnostics.Append(diags...)
87+
if resp.Diagnostics.HasError() {
88+
return
89+
}
90+
}
91+
92+
// Read refreshes the Terraform state with the latest data.
93+
func (r *statefulList) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
94+
return
95+
}
96+
97+
func (r *statefulList) update(ctx context.Context, tfplan tfsdk.Plan, tfstate tfsdk.State, diag *diag.Diagnostics) (plan statefulListModel) {
98+
diags := tfplan.Get(ctx, &plan)
99+
diag.Append(diags...)
100+
if diag.HasError() {
101+
return
102+
}
103+
104+
var state statefulListModel
105+
tfstate.Get(ctx, &state)
106+
diag.Append(diags...)
107+
if diag.HasError() {
108+
return
109+
}
110+
111+
stateOutput := []string{}
112+
if !tfstate.Raw.IsNull() {
113+
diags = state.Output.ElementsAs(ctx, &stateOutput, false)
114+
diag.Append(diags...)
115+
if diag.HasError() {
116+
return
117+
}
118+
}
119+
120+
planInput := []string{}
121+
diag.Append(plan.Input.ElementsAs(ctx, &planInput, false)...)
122+
if diag.HasError() {
123+
return
124+
}
125+
126+
for _, i := range planInput {
127+
if !stringInSlice(i, stateOutput) {
128+
stateOutput = append(stateOutput, i)
129+
}
130+
}
131+
132+
planOutput, diags := basetypes.NewSetValueFrom(ctx, types.StringType, stateOutput)
133+
diag.Append(diags...)
134+
if diag.HasError() {
135+
return
136+
}
137+
138+
plan.Output = planOutput
139+
140+
return
141+
}
142+
143+
// ModifyPlan modifies plan in a way to show all the changes before the apply
144+
func (r *statefulList) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
145+
146+
// don't modify on delete
147+
if req.Plan.Raw.IsNull() {
148+
return
149+
}
150+
151+
plan := r.update(ctx, req.Plan, req.State, &resp.Diagnostics)
152+
if resp.Diagnostics.HasError() {
153+
return
154+
}
155+
156+
resp.Diagnostics.Append(resp.Plan.Set(ctx, plan)...)
157+
if resp.Diagnostics.HasError() {
158+
return
159+
}
160+
}
161+
162+
// Update updates the resource and sets the updated Terraform state on success.
163+
func (r *statefulList) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
164+
plan := r.update(ctx, req.Plan, req.State, &resp.Diagnostics)
165+
if resp.Diagnostics.HasError() {
166+
return
167+
}
168+
169+
diags := resp.State.Set(ctx, plan)
170+
resp.Diagnostics.Append(diags...)
171+
if resp.Diagnostics.HasError() {
172+
return
173+
}
174+
}
175+
176+
// Delete deletes the resource and removes the Terraform state on success.
177+
func (r *statefulList) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
178+
return
179+
}
180+
181+
func (r *statefulList) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
182+
// Retrieve import ID and save to id attribute
183+
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
184+
}

0 commit comments

Comments
 (0)