Skip to content

Commit f361273

Browse files
committed
feat(opensearch) instance + credential, save IDs immediately after create
STACKITTPR-388
1 parent 9764542 commit f361273

3 files changed

Lines changed: 173 additions & 10 deletions

File tree

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

Lines changed: 10 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,11 @@ 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+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
193+
"project_id": projectId,
194+
"instance_id": instanceId,
195+
"credential_id": credentialId,
196+
})
194197

195198
waitResp, err := wait.CreateCredentialsWaitHandler(ctx, r.client, projectId, instanceId, credentialId).WaitWithContext(ctx)
196199
if err != nil {
@@ -311,9 +314,11 @@ func (r *credentialResource) ImportState(ctx context.Context, req resource.Impor
311314
return
312315
}
313316

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])...)
317+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
318+
"project_id": idParts[0],
319+
"instance_id": idParts[1],
320+
"credential_id": idParts[2],
321+
})
317322
tflog.Info(ctx, "OpenSearch credential state imported")
318323
}
319324

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

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

23-
"github.com/hashicorp/terraform-plugin-framework/path"
2423
"github.com/hashicorp/terraform-plugin-framework/resource"
2524
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
2625
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
@@ -366,9 +365,16 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
366365
}
367366

368367
ctx = core.LogResponse(ctx)
369-
368+
if createResp.InstanceId == nil {
369+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", "API response did not include instance ID")
370+
}
370371
instanceId := *createResp.InstanceId
371-
ctx = tflog.SetField(ctx, "instance_id", instanceId)
372+
373+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
374+
"project_id": projectId,
375+
"instance_id": instanceId,
376+
})
377+
372378
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client, projectId, instanceId).WaitWithContext(ctx)
373379
if err != nil {
374380
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Instance creation waiting: %v", err))
@@ -557,8 +563,10 @@ func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportS
557563
return
558564
}
559565

560-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
561-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...)
566+
ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
567+
"project_id": idParts[0],
568+
"instance_id": idParts[1],
569+
})
562570
tflog.Info(ctx, "OpenSearch instance state imported")
563571
}
564572

stackit/internal/services/opensearch/opensearch_acc_test.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package opensearch_test
33
import (
44
"context"
55
"fmt"
6+
"net/http"
7+
"regexp"
68
"strings"
79
"testing"
810

11+
"github.com/google/uuid"
912
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
1013
"github.com/hashicorp/terraform-plugin-testing/terraform"
1114
"github.com/stackitcloud/stackit-sdk-go/core/config"
@@ -235,6 +238,153 @@ func TestAccOpenSearchResource(t *testing.T) {
235238
})
236239
}
237240

241+
func TestOpensearchInstanceSavesIDsOnError(t *testing.T) {
242+
var (
243+
projectId = uuid.NewString()
244+
instanceId = uuid.NewString()
245+
)
246+
const (
247+
name = "opensearch-instance-test"
248+
version = "version"
249+
planName = "plan-name"
250+
)
251+
s := testutil.NewMockServer(t)
252+
defer s.Server.Close()
253+
tfConfig := fmt.Sprintf(`
254+
provider "stackit" {
255+
opensearch_custom_endpoint = "%s"
256+
service_account_token = "mock-server-needs-no-auth"
257+
}
258+
259+
resource "stackit_opensearch_instance" "instance" {
260+
project_id = "%s"
261+
name = "%s"
262+
version = "%s"
263+
plan_name = "%s"
264+
}
265+
`, s.Server.URL, projectId, name, version, planName)
266+
267+
resource.UnitTest(t, resource.TestCase{
268+
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
269+
Steps: []resource.TestStep{
270+
{
271+
PreConfig: func() {
272+
s.Reset(
273+
testutil.MockResponse{
274+
Description: "offerings",
275+
ToJsonBody: &opensearch.ListOfferingsResponse{
276+
Offerings: &[]opensearch.Offering{
277+
{
278+
Name: utils.Ptr("offering-name"),
279+
Version: utils.Ptr(version),
280+
Plans: &[]opensearch.Plan{
281+
{
282+
Id: utils.Ptr("plan-id"),
283+
Name: utils.Ptr(planName),
284+
},
285+
},
286+
},
287+
},
288+
},
289+
},
290+
testutil.MockResponse{
291+
Description: "create instance",
292+
ToJsonBody: &opensearch.CreateInstanceResponse{
293+
InstanceId: utils.Ptr(instanceId),
294+
},
295+
},
296+
testutil.MockResponse{Description: "failing waiter", StatusCode: http.StatusInternalServerError},
297+
)
298+
},
299+
Config: tfConfig,
300+
ExpectError: regexp.MustCompile("Error creating instance.*"),
301+
},
302+
{
303+
PreConfig: func() {
304+
s.Reset(
305+
testutil.MockResponse{
306+
Description: "refresh",
307+
Handler: func(w http.ResponseWriter, req *http.Request) {
308+
expected := fmt.Sprintf("/v1/projects/%s/instances/%s", projectId, instanceId)
309+
if req.URL.Path != expected {
310+
t.Errorf(fmt.Sprintf("unexpected URL path: got %s, want %s", req.URL.Path, expected), http.StatusBadRequest)
311+
}
312+
w.WriteHeader(http.StatusInternalServerError)
313+
},
314+
},
315+
testutil.MockResponse{Description: "delete"},
316+
testutil.MockResponse{Description: "delete waiter", StatusCode: http.StatusGone},
317+
)
318+
},
319+
RefreshState: true,
320+
ExpectError: regexp.MustCompile("Error reading instance.*"),
321+
},
322+
},
323+
})
324+
}
325+
326+
func TestOpensearchCredentialSavesIDsOnError(t *testing.T) {
327+
var (
328+
projectId = uuid.NewString()
329+
instanceId = uuid.NewString()
330+
credentialId = uuid.NewString()
331+
)
332+
s := testutil.NewMockServer(t)
333+
defer s.Server.Close()
334+
tfConfig := fmt.Sprintf(`
335+
provider "stackit" {
336+
opensearch_custom_endpoint = "%s"
337+
service_account_token = "mock-server-needs-no-auth"
338+
}
339+
340+
resource "stackit_opensearch_credential" "credential" {
341+
project_id = "%s"
342+
instance_id = "%s"
343+
}
344+
`, s.Server.URL, projectId, instanceId)
345+
346+
resource.UnitTest(t, resource.TestCase{
347+
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
348+
Steps: []resource.TestStep{
349+
{
350+
PreConfig: func() {
351+
s.Reset(
352+
testutil.MockResponse{
353+
Description: "create credential",
354+
ToJsonBody: &opensearch.CredentialsResponse{
355+
Id: utils.Ptr(credentialId),
356+
},
357+
},
358+
testutil.MockResponse{Description: "create waiter", StatusCode: http.StatusInternalServerError},
359+
)
360+
},
361+
Config: tfConfig,
362+
ExpectError: regexp.MustCompile("Error creating credential.*"),
363+
},
364+
{
365+
PreConfig: func() {
366+
s.Reset(
367+
testutil.MockResponse{
368+
Description: "refresh",
369+
Handler: func(w http.ResponseWriter, req *http.Request) {
370+
expected := fmt.Sprintf("/v1/projects/%s/instances/%s/credentials/%s", projectId, instanceId, credentialId)
371+
if req.URL.Path != expected {
372+
t.Errorf(fmt.Sprintf("unexpected URL path: got %s, want %s", req.URL.Path, expected), http.StatusBadRequest)
373+
}
374+
w.WriteHeader(http.StatusInternalServerError)
375+
},
376+
},
377+
testutil.MockResponse{Description: "delete"},
378+
testutil.MockResponse{Description: "delete waiter", StatusCode: http.StatusGone},
379+
)
380+
},
381+
RefreshState: true,
382+
ExpectError: regexp.MustCompile("Error reading credential.*"),
383+
},
384+
},
385+
})
386+
}
387+
238388
func testAccCheckOpenSearchDestroy(s *terraform.State) error {
239389
ctx := context.Background()
240390
var client *opensearch.APIClient

0 commit comments

Comments
 (0)