Skip to content

Commit 1f7fca2

Browse files
Marcel JacekMarcel Jacek
authored andcommitted
fix: save ID to state after provisioning
relates to STACKITTPR-584
1 parent eb4f28d commit 1f7fca2

10 files changed

Lines changed: 510 additions & 36 deletions

File tree

stackit/internal/services/edgecloud/instance/resource.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"strings"
1010

1111
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
12-
"github.com/hashicorp/terraform-plugin-framework/path"
1312
"github.com/hashicorp/terraform-plugin-framework/resource"
1413
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
1514
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
@@ -281,12 +280,17 @@ func (i *instanceResource) Create(ctx context.Context, req resource.CreateReques
281280
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", "API returned nil Instance ID")
282281
return
283282
}
283+
284284
edgeCloudInstanceId := *createResp.Id
285-
utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
285+
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
286+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
286287
"project_id": projectId,
287288
"instance_id": edgeCloudInstanceId,
288289
"region": region,
289290
})
291+
if resp.Diagnostics.HasError() {
292+
return
293+
}
290294

291295
waitResp, err := edgewait.CreateOrUpdateInstanceWaitHandler(ctx, i.client, projectId, region, edgeCloudInstanceId).WaitWithContext(ctx)
292296
if err != nil {
@@ -436,9 +440,12 @@ func (i *instanceResource) ImportState(ctx context.Context, req resource.ImportS
436440
)
437441
return
438442
}
439-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
440-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
441-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
443+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
444+
"project_id": idParts[0],
445+
"region": idParts[1],
446+
"instance_id": idParts[2],
447+
})
448+
tflog.Info(ctx, "Edge cloud Instance state imported")
442449
}
443450

444451
// mapFields maps the API response to the Terraform model.

stackit/internal/services/logs/instance/resource.go

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

1010
"github.com/hashicorp/terraform-plugin-framework/diag"
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/planmodifier"
@@ -18,7 +17,7 @@ import (
1817
"github.com/hashicorp/terraform-plugin-log/tflog"
1918
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
2019
logs "github.com/stackitcloud/stackit-sdk-go/services/logs/v1api"
21-
wait "github.com/stackitcloud/stackit-sdk-go/services/logs/v1api/wait"
20+
"github.com/stackitcloud/stackit-sdk-go/services/logs/v1api/wait"
2221
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
2322
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
2423
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/logs/utils"
@@ -249,6 +248,21 @@ func (r *logsInstanceResource) Create(ctx context.Context, req resource.CreateRe
249248

250249
ctx = core.LogResponse(ctx)
251250

251+
if createResp == nil || createResp.Id == "" {
252+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating Logs Instance", "Create API response: Incomplete response (id missing)")
253+
return
254+
}
255+
instanceId := createResp.Id
256+
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
257+
ctx = tfutils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
258+
"project_id": projectId,
259+
"region": region,
260+
"instance_id": instanceId,
261+
})
262+
if resp.Diagnostics.HasError() {
263+
return
264+
}
265+
252266
waitResp, err := wait.CreateLogsInstanceWaitHandler(ctx, r.client.DefaultAPI, projectId, regionId, createResp.Id).WaitWithContext(ctx)
253267
if err != nil {
254268
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating Logs Instance", fmt.Sprintf("Waiting for Logs Instance to become active: %v", err))
@@ -403,9 +417,12 @@ func (r *logsInstanceResource) ImportState(ctx context.Context, req resource.Imp
403417
core.LogAndAddError(ctx, &resp.Diagnostics, "Error importing Logs Instance", fmt.Sprintf("Invalid import ID %q: expected format is `project_id`,`region`,`instance_id`", req.ID))
404418
return
405419
}
406-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
407-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
408-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
420+
421+
ctx = tfutils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
422+
"project_id": idParts[0],
423+
"region": idParts[1],
424+
"instance_id": idParts[2],
425+
})
409426
tflog.Info(ctx, "Logs Instance state imported")
410427
}
411428

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package logs
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"regexp"
7+
"testing"
8+
9+
"github.com/google/uuid"
10+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
11+
logs "github.com/stackitcloud/stackit-sdk-go/services/logs/v1api"
12+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
13+
)
14+
15+
func TestLogsInstanceSavesIDsOnError(t *testing.T) {
16+
projectId := uuid.NewString()
17+
instanceId := uuid.NewString()
18+
const (
19+
region = "eu01"
20+
)
21+
s := testutil.NewMockServer(t)
22+
defer s.Server.Close()
23+
tfConfig := fmt.Sprintf(`
24+
provider "stackit" {
25+
default_region = "%s"
26+
logs_custom_endpoint = "%s"
27+
service_account_token = "mock-server-needs-no-auth"
28+
}
29+
resource "stackit_logs_instance" "logs" {
30+
project_id = "%s"
31+
display_name = "logs-instance-example"
32+
retention_days = 30
33+
}
34+
`, region, s.Server.URL, projectId)
35+
36+
resource.UnitTest(t, resource.TestCase{
37+
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
38+
Steps: []resource.TestStep{
39+
{
40+
PreConfig: func() {
41+
s.Reset(
42+
testutil.MockResponse{
43+
Description: "create instance",
44+
ToJsonBody: logs.LogsInstance{
45+
Id: instanceId,
46+
},
47+
},
48+
testutil.MockResponse{
49+
Description: "failing waiter",
50+
StatusCode: http.StatusInternalServerError,
51+
},
52+
)
53+
},
54+
Config: tfConfig,
55+
ExpectError: regexp.MustCompile("Error creating Logs Instance.*"),
56+
},
57+
{
58+
PreConfig: func() {
59+
s.Reset(
60+
testutil.MockResponse{
61+
Description: "refresh",
62+
Handler: func(w http.ResponseWriter, req *http.Request) {
63+
expected := fmt.Sprintf("/v1/projects/%s/regions/%s/instances/%s", projectId, region, instanceId)
64+
if req.URL.Path != expected {
65+
t.Errorf("expected request to %s, got %s", expected, req.URL.Path)
66+
}
67+
w.WriteHeader(http.StatusInternalServerError)
68+
},
69+
},
70+
testutil.MockResponse{Description: "delete", StatusCode: http.StatusAccepted},
71+
testutil.MockResponse{
72+
Description: "delete waiter",
73+
StatusCode: http.StatusNotFound,
74+
},
75+
)
76+
},
77+
RefreshState: true,
78+
ExpectError: regexp.MustCompile("Error reading logs instance.*"),
79+
},
80+
},
81+
})
82+
}

stackit/internal/services/mariadb/credential/resource.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
1616
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
1717

18-
"github.com/hashicorp/terraform-plugin-framework/path"
1918
"github.com/hashicorp/terraform-plugin-framework/resource"
2019
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
2120
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
@@ -190,7 +189,15 @@ func (r *credentialResource) Create(ctx context.Context, req resource.CreateRequ
190189
return
191190
}
192191
credentialId := *credentialsResp.Id
193-
ctx = tflog.SetField(ctx, "credential_id", credentialId)
192+
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
193+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
194+
"project_id": projectId,
195+
"instance_id": instanceId,
196+
"credential_id": credentialId,
197+
})
198+
if resp.Diagnostics.HasError() {
199+
return
200+
}
194201

195202
waitResp, err := wait.CreateCredentialsWaitHandler(ctx, r.client, projectId, instanceId, credentialId).WaitWithContext(ctx)
196203
if err != nil {
@@ -311,9 +318,11 @@ func (r *credentialResource) ImportState(ctx context.Context, req resource.Impor
311318
return
312319
}
313320

314-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
315-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...)
316-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("credential_id"), idParts[2])...)
321+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
322+
"project_id": idParts[0],
323+
"instance_id": idParts[1],
324+
"credential_id": idParts[2],
325+
})
317326
tflog.Info(ctx, "MariaDB credential state imported")
318327
}
319328

stackit/internal/services/mariadb/instance/resource.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
2020
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
2121

22-
"github.com/hashicorp/terraform-plugin-framework/path"
2322
"github.com/hashicorp/terraform-plugin-framework/resource"
2423
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
2524
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
@@ -319,7 +318,21 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
319318

320319
ctx = core.LogResponse(ctx)
321320

321+
if createResp == nil || createResp.InstanceId == nil {
322+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", "Create API response: Incomplete response (id missing)")
323+
return
324+
}
325+
322326
instanceId := *createResp.InstanceId
327+
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
328+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
329+
"project_id": projectId,
330+
"instance_id": instanceId,
331+
})
332+
if resp.Diagnostics.HasError() {
333+
return
334+
}
335+
323336
ctx = tflog.SetField(ctx, "instance_id", instanceId)
324337
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client, projectId, instanceId).WaitWithContext(ctx)
325338
if err != nil {
@@ -510,8 +523,10 @@ func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportS
510523
return
511524
}
512525

513-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
514-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...)
526+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
527+
"project_id": idParts[0],
528+
"instance_id": idParts[1],
529+
})
515530
tflog.Info(ctx, "MariaDB instance state imported")
516531
}
517532

0 commit comments

Comments
 (0)