Skip to content

Commit 4103c33

Browse files
authored
fix(dns): store IDs immediately after provisioning (#1022)
relates to STACKITTPR-373
1 parent f043398 commit 4103c33

File tree

4 files changed

+132
-17
lines changed

4 files changed

+132
-17
lines changed

stackit/internal/services/dns/recordset/resource.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
99
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
1010
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
11-
"github.com/hashicorp/terraform-plugin-framework/path"
1211
"github.com/hashicorp/terraform-plugin-framework/resource"
1312
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
1413
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
@@ -218,7 +217,16 @@ func (r *recordSetResource) Create(ctx context.Context, req resource.CreateReque
218217
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating record set", fmt.Sprintf("Calling API: %v", err))
219218
return
220219
}
221-
ctx = tflog.SetField(ctx, "record_set_id", *recordSetResp.Rrset.Id)
220+
221+
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
222+
utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
223+
"project_id": projectId,
224+
"zone_id": zoneId,
225+
"record_set_id": *recordSetResp.Rrset.Id,
226+
})
227+
if resp.Diagnostics.HasError() {
228+
return
229+
}
222230

223231
waitResp, err := wait.CreateRecordSetWaitHandler(ctx, r.client, projectId, zoneId, *recordSetResp.Rrset.Id).WaitWithContext(ctx)
224232
if err != nil {
@@ -372,9 +380,11 @@ func (r *recordSetResource) ImportState(ctx context.Context, req resource.Import
372380
return
373381
}
374382

375-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
376-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("zone_id"), idParts[1])...)
377-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("record_set_id"), idParts[2])...)
383+
utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]interface{}{
384+
"project_id": idParts[0],
385+
"zone_id": idParts[1],
386+
"record_set_id": idParts[2],
387+
})
378388
tflog.Info(ctx, "DNS record set state imported")
379389
}
380390

stackit/internal/services/dns/zone/resource.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
1212
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
1313
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
14-
"github.com/hashicorp/terraform-plugin-framework/path"
1514
"github.com/hashicorp/terraform-plugin-framework/resource"
1615
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
1716
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
@@ -280,8 +279,7 @@ func (r *zoneResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
280279
func (r *zoneResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
281280
// Retrieve values from plan
282281
var model Model
283-
diags := req.Plan.Get(ctx, &model)
284-
resp.Diagnostics.Append(diags...)
282+
resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...)
285283
if resp.Diagnostics.HasError() {
286284
return
287285
}
@@ -301,9 +299,17 @@ func (r *zoneResource) Create(ctx context.Context, req resource.CreateRequest, r
301299
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating zone", fmt.Sprintf("Calling API: %v", err))
302300
return
303301
}
302+
303+
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
304304
zoneId := *createResp.Zone.Id
305+
utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]interface{}{
306+
"project_id": projectId,
307+
"zone_id": zoneId,
308+
})
309+
if resp.Diagnostics.HasError() {
310+
return
311+
}
305312

306-
ctx = tflog.SetField(ctx, "zone_id", zoneId)
307313
waitResp, err := wait.CreateZoneWaitHandler(ctx, r.client, projectId, zoneId).WaitWithContext(ctx)
308314
if err != nil {
309315
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating zone", fmt.Sprintf("Zone creation waiting: %v", err))
@@ -317,8 +323,7 @@ func (r *zoneResource) Create(ctx context.Context, req resource.CreateRequest, r
317323
return
318324
}
319325
// Set state to fully populated data
320-
diags = resp.State.Set(ctx, model)
321-
resp.Diagnostics.Append(diags...)
326+
resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
322327
if resp.Diagnostics.HasError() {
323328
return
324329
}
@@ -451,13 +456,11 @@ func (r *zoneResource) ImportState(ctx context.Context, req resource.ImportState
451456
return
452457
}
453458

454-
projectId := idParts[0]
455-
zoneId := idParts[1]
456-
ctx = tflog.SetField(ctx, "project_id", projectId)
457-
ctx = tflog.SetField(ctx, "zone_id", zoneId)
459+
utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]interface{}{
460+
"project_id": idParts[0],
461+
"zone_id": idParts[1],
462+
})
458463

459-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), projectId)...)
460-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("zone_id"), zoneId)...)
461464
tflog.Info(ctx, "DNS zone state imported")
462465
}
463466

stackit/internal/utils/utils.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"regexp"
88
"strings"
99

10+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
11+
1012
"github.com/hashicorp/terraform-plugin-framework/attr"
1113
"github.com/hashicorp/terraform-plugin-framework/diag"
1214
"github.com/hashicorp/terraform-plugin-framework/path"
@@ -186,3 +188,11 @@ func CheckListRemoval(ctx context.Context, configModelList, planModelList types.
186188
}
187189
}
188190
}
191+
192+
// SetAndLogStateFields writes the given map of key-value pairs to the state
193+
func SetAndLogStateFields(ctx context.Context, diags *diag.Diagnostics, state *tfsdk.State, values map[string]any) {
194+
for key, val := range values {
195+
ctx = tflog.SetField(ctx, key, val)
196+
diags.Append(state.SetAttribute(ctx, path.Root(key), val)...)
197+
}
198+
}

stackit/internal/utils/utils_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import (
66
"reflect"
77
"testing"
88

9+
"github.com/hashicorp/terraform-plugin-framework/diag"
10+
"github.com/hashicorp/terraform-plugin-go/tftypes"
11+
912
"github.com/google/go-cmp/cmp"
1013
"github.com/hashicorp/terraform-plugin-framework/attr"
1114
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
@@ -551,3 +554,92 @@ func TestCheckListRemoval(t *testing.T) {
551554
})
552555
}
553556
}
557+
558+
func TestSetAndLogStateFields(t *testing.T) {
559+
testSchema := schema.Schema{
560+
Attributes: map[string]schema.Attribute{
561+
"project_id": schema.StringAttribute{},
562+
"instance_id": schema.StringAttribute{},
563+
},
564+
}
565+
566+
type args struct {
567+
diags *diag.Diagnostics
568+
state *tfsdk.State
569+
values map[string]interface{}
570+
}
571+
type want struct {
572+
hasError bool
573+
state *tfsdk.State
574+
}
575+
tests := []struct {
576+
name string
577+
args args
578+
want want
579+
}{
580+
{
581+
name: "empty map",
582+
args: args{
583+
diags: &diag.Diagnostics{},
584+
state: &tfsdk.State{},
585+
values: map[string]interface{}{},
586+
},
587+
want: want{
588+
hasError: false,
589+
state: &tfsdk.State{},
590+
},
591+
},
592+
{
593+
name: "base",
594+
args: args{
595+
diags: &diag.Diagnostics{},
596+
state: func() *tfsdk.State {
597+
ctx := context.Background()
598+
state := tfsdk.State{
599+
Raw: tftypes.NewValue(testSchema.Type().TerraformType(ctx), map[string]tftypes.Value{
600+
"project_id": tftypes.NewValue(tftypes.String, "9b15d120-86f8-45f5-81d8-a554f09c7582"),
601+
"instance_id": tftypes.NewValue(tftypes.String, nil),
602+
}),
603+
Schema: testSchema,
604+
}
605+
return &state
606+
}(),
607+
values: map[string]interface{}{
608+
"project_id": "a414f971-3f7a-4e9a-8671-51a8acb7bcc8",
609+
"instance_id": "97073250-8cad-46c3-8424-6258ac0b3731",
610+
},
611+
},
612+
want: want{
613+
hasError: false,
614+
state: func() *tfsdk.State {
615+
ctx := context.Background()
616+
state := tfsdk.State{
617+
Raw: tftypes.NewValue(testSchema.Type().TerraformType(ctx), map[string]tftypes.Value{
618+
"project_id": tftypes.NewValue(tftypes.String, nil),
619+
"instance_id": tftypes.NewValue(tftypes.String, nil),
620+
}),
621+
Schema: testSchema,
622+
}
623+
state.SetAttribute(ctx, path.Root("project_id"), "a414f971-3f7a-4e9a-8671-51a8acb7bcc8")
624+
state.SetAttribute(ctx, path.Root("instance_id"), "97073250-8cad-46c3-8424-6258ac0b3731")
625+
return &state
626+
}(),
627+
},
628+
},
629+
}
630+
for _, tt := range tests {
631+
t.Run(tt.name, func(t *testing.T) {
632+
ctx := context.Background()
633+
SetAndLogStateFields(ctx, tt.args.diags, tt.args.state, tt.args.values)
634+
635+
if tt.args.diags.HasError() != tt.want.hasError {
636+
t.Errorf("TestSetAndLogStateFields() error count = %v, hasErr %v", tt.args.diags.ErrorsCount(), tt.want.hasError)
637+
}
638+
639+
diff := cmp.Diff(tt.args.state, tt.want.state)
640+
if diff != "" {
641+
t.Fatalf("Data does not match: %s", diff)
642+
}
643+
})
644+
}
645+
}

0 commit comments

Comments
 (0)