Skip to content

Commit b452eb5

Browse files
authored
feat(objectstorage): store ids after provisioning (#1282)
relates to STACKITTPR-386
1 parent 566c377 commit b452eb5

4 files changed

Lines changed: 139 additions & 13 deletions

File tree

stackit/internal/services/objectstorage/bucket/resource.go

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

19-
"github.com/hashicorp/terraform-plugin-framework/path"
2019
"github.com/hashicorp/terraform-plugin-framework/resource"
2120
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
2221
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
@@ -205,6 +204,16 @@ func (r *bucketResource) Create(ctx context.Context, req resource.CreateRequest,
205204

206205
ctx = core.LogResponse(ctx)
207206

207+
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
208+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
209+
"project_id": projectId,
210+
"region": region,
211+
"name": bucketName,
212+
})
213+
if resp.Diagnostics.HasError() {
214+
return
215+
}
216+
208217
waitResp, err := wait.CreateBucketWaitHandler(ctx, r.client, projectId, region, bucketName).WaitWithContext(ctx)
209218
if err != nil {
210219
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating bucket", fmt.Sprintf("Bucket creation waiting: %v", err))
@@ -333,9 +342,12 @@ func (r *bucketResource) ImportState(ctx context.Context, req resource.ImportSta
333342
return
334343
}
335344

336-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
337-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
338-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), idParts[2])...)
345+
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
346+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
347+
"project_id": idParts[0],
348+
"region": idParts[1],
349+
"name": idParts[2],
350+
})
339351
tflog.Info(ctx, "ObjectStorage bucket state imported")
340352
}
341353

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,16 @@ func (r *credentialResource) Create(ctx context.Context, req resource.CreateRequ
286286
return
287287
}
288288
credentialId := *credentialResp.KeyId
289-
ctx = tflog.SetField(ctx, "credential_id", credentialId)
289+
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
290+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
291+
"project_id": projectId,
292+
"region": region,
293+
"credentials_group_id": credentialsGroupId,
294+
"credential_id": credentialId,
295+
})
296+
if resp.Diagnostics.HasError() {
297+
return
298+
}
290299

291300
// Map response body to schema
292301
err = mapFields(credentialResp, &model, region)
@@ -445,10 +454,12 @@ func (r *credentialResource) ImportState(ctx context.Context, req resource.Impor
445454
return
446455
}
447456

448-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
449-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
450-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("credentials_group_id"), idParts[2])...)
451-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("credential_id"), idParts[3])...)
457+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
458+
"project_id": idParts[0],
459+
"region": idParts[1],
460+
"credentials_group_id": idParts[2],
461+
"credential_id": idParts[3],
462+
})
452463
tflog.Info(ctx, "ObjectStorage credential state imported")
453464
}
454465

stackit/internal/services/objectstorage/credentialsgroup/resource.go

Lines changed: 20 additions & 4 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"
@@ -207,6 +206,21 @@ func (r *credentialsGroupResource) Create(ctx context.Context, req resource.Crea
207206

208207
ctx = core.LogResponse(ctx)
209208

209+
if got == nil || got.CredentialsGroup == nil || got.CredentialsGroup.CredentialsGroupId == nil {
210+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credentials group", "Got empty credential group id id")
211+
return
212+
}
213+
credentialsGroupId := *got.CredentialsGroup.CredentialsGroupId
214+
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
215+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
216+
"project_id": projectId,
217+
"region": region,
218+
"credentials_group_id": credentialsGroupId,
219+
})
220+
if resp.Diagnostics.HasError() {
221+
return
222+
}
223+
210224
// Map response body to schema
211225
err = mapFields(got, &model, region)
212226
if err != nil {
@@ -312,9 +326,11 @@ func (r *credentialsGroupResource) ImportState(ctx context.Context, req resource
312326
return
313327
}
314328

315-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
316-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
317-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("credentials_group_id"), idParts[2])...)
329+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
330+
"project_id": idParts[0],
331+
"region": idParts[1],
332+
"credentials_group_id": idParts[2],
333+
})
318334
tflog.Info(ctx, "ObjectStorage credentials group state imported")
319335
}
320336

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package objectstorage
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+
"github.com/stackitcloud/stackit-sdk-go/core/utils"
12+
"github.com/stackitcloud/stackit-sdk-go/services/objectstorage"
13+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
14+
)
15+
16+
func TestObjectStorageBucketSavesIDsOnError(t *testing.T) {
17+
projectId := uuid.NewString()
18+
const (
19+
name = "bucket-name"
20+
region = "eu01"
21+
)
22+
s := testutil.NewMockServer(t)
23+
defer s.Server.Close()
24+
tfConfig := fmt.Sprintf(`
25+
provider "stackit" {
26+
default_region = "%s"
27+
objectstorage_custom_endpoint = "%s"
28+
service_account_token = "mock-server-needs-no-auth"
29+
}
30+
resource "stackit_objectstorage_bucket" "instance" {
31+
project_id = "%s"
32+
name = "%s"
33+
}
34+
`, region, s.Server.URL, projectId, name)
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: "project enable",
44+
ToJsonBody: objectstorage.ProjectStatus{
45+
Project: utils.Ptr(projectId),
46+
Scope: utils.Ptr(objectstorage.PROJECTSCOPE_PUBLIC),
47+
},
48+
},
49+
testutil.MockResponse{
50+
Description: "create bucket",
51+
ToJsonBody: objectstorage.Bucket{
52+
Name: utils.Ptr(name),
53+
Region: utils.Ptr(region),
54+
},
55+
},
56+
testutil.MockResponse{
57+
Description: "failing waiter",
58+
StatusCode: http.StatusInternalServerError,
59+
},
60+
)
61+
},
62+
Config: tfConfig,
63+
ExpectError: regexp.MustCompile("Error creating bucket.*"),
64+
},
65+
{
66+
PreConfig: func() {
67+
s.Reset(
68+
testutil.MockResponse{
69+
Description: "refresh",
70+
Handler: func(w http.ResponseWriter, req *http.Request) {
71+
expected := fmt.Sprintf("/v2/project/%s/regions/%s/bucket/%s", projectId, region, name)
72+
if req.URL.Path != expected {
73+
t.Errorf("expected request to %s, got %s", expected, req.URL.Path)
74+
}
75+
w.WriteHeader(http.StatusInternalServerError)
76+
},
77+
},
78+
testutil.MockResponse{Description: "delete", StatusCode: http.StatusAccepted},
79+
testutil.MockResponse{Description: "delete waiter", StatusCode: http.StatusNotFound},
80+
)
81+
},
82+
RefreshState: true,
83+
ExpectError: regexp.MustCompile("Error reading bucket*"),
84+
},
85+
},
86+
})
87+
}

0 commit comments

Comments
 (0)