Skip to content

Commit 5cfc72a

Browse files
committed
feat(sfs) print detailed error message on validation-error
1 parent 3b2e92d commit 5cfc72a

7 files changed

Lines changed: 145 additions & 10 deletions

File tree

stackit/internal/services/sfs/export-policy/resource.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func (r *exportPolicyResource) Create(ctx context.Context, req resource.CreateRe
276276

277277
createResp, err := r.client.CreateShareExportPolicy(ctx, projectId, region).CreateShareExportPolicyPayload(*payload).Execute()
278278
if err != nil {
279-
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating export policy", fmt.Sprintf("Calling API: %v", err))
279+
sfsUtils.LogAndAddError(ctx, &resp.Diagnostics, "Error creating export policy", "Calling API", err)
280280
return
281281
}
282282

@@ -402,7 +402,7 @@ func (r *exportPolicyResource) Update(ctx context.Context, req resource.UpdateRe
402402

403403
_, err = r.client.UpdateShareExportPolicy(ctx, projectId, region, exportPolicyId).UpdateShareExportPolicyPayload(*payload).Execute()
404404
if err != nil {
405-
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating export policy", fmt.Sprintf("Calling API to update export policy: %v", err))
405+
sfsUtils.LogAndAddError(ctx, &resp.Diagnostics, "Error updating export policy", "Calling API to update export policy", err)
406406
return
407407
}
408408

stackit/internal/services/sfs/resourcepool/resource.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ func (r *resourcePoolResource) Create(ctx context.Context, req resource.CreateRe
232232
CreateResourcePoolPayload(*payload).
233233
Execute()
234234
if err != nil {
235-
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating resource pool", fmt.Sprintf("Calling API: %v", err))
235+
sfsUtils.LogAndAddError(ctx, &resp.Diagnostics, "Error creating resource pool", "Calling API", err)
236236
return
237237
}
238238

@@ -380,7 +380,7 @@ func (r *resourcePoolResource) Update(ctx context.Context, req resource.UpdateRe
380380
return
381381
}
382382
}
383-
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating resource pool", fmt.Sprintf("Calling API: %v", err))
383+
sfsUtils.LogAndAddError(ctx, &resp.Diagnostics, "Error updating resource pool", "Calling API", err)
384384
return
385385
}
386386

stackit/internal/services/sfs/sfs_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,48 @@ resource "stackit_sfs_share" "example" {
162162
},
163163
})
164164
}
165+
166+
func TestLogAndErrorPrintsValidationMessage(t *testing.T) {
167+
projectId := uuid.NewString()
168+
const region = "eu01"
169+
s := testutil.NewMockServer(t)
170+
defer s.Server.Close()
171+
tfConfig := fmt.Sprintf(`
172+
provider "stackit" {
173+
default_region = "%s"
174+
sfs_custom_endpoint = "%s"
175+
service_account_token = "mock-server-needs-no-auth"
176+
enable_beta_resources = true
177+
}
178+
resource "stackit_sfs_resource_pool" "resourcepool" {
179+
project_id = "%s"
180+
name = "sfs-instance"
181+
availability_zone = "eu01-m"
182+
performance_class = "Standard"
183+
size_gigabytes = 512
184+
ip_acl = ["192.168.2.0/24"]
185+
}
186+
`, region, s.Server.URL, projectId)
187+
s.Reset(testutil.MockResponse{
188+
StatusCode: http.StatusBadRequest,
189+
ToJsonBody: sfs.ValidationError{
190+
Type: utils.Ptr("storage.stackit.cloud/validation-error"),
191+
Title: utils.Ptr("Validation Failed"),
192+
Fields: &[]sfs.ValidationErrorField{
193+
{
194+
Field: utils.Ptr("ip"),
195+
Reason: utils.Ptr("999.999.999 is not a valid ip address"),
196+
},
197+
},
198+
},
199+
})
200+
resource.UnitTest(t, resource.TestCase{
201+
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
202+
Steps: []resource.TestStep{
203+
{
204+
Config: tfConfig,
205+
ExpectError: regexp.MustCompile(".*Calling API: Validation Failed\n\nField: ip \\| Reason: 999.999.999 is not a valid ip address"),
206+
},
207+
},
208+
})
209+
}

stackit/internal/services/sfs/share/resource.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ func (r *shareResource) Create(ctx context.Context, req resource.CreateRequest,
230230
CreateSharePayload(payload).
231231
Execute()
232232
if err != nil {
233-
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating share", fmt.Sprintf("Calling API: %v", err))
233+
sfsUtils.LogAndAddError(ctx, &resp.Diagnostics, "Error creating share", "Calling API", err)
234234
return
235235
}
236236

@@ -382,7 +382,7 @@ func (r *shareResource) Update(ctx context.Context, req resource.UpdateRequest,
382382
return
383383
}
384384
}
385-
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating share", fmt.Sprintf("Calling API: %v", err))
385+
sfsUtils.LogAndAddError(ctx, &resp.Diagnostics, "Error updating share", "Calling API", err)
386386
return
387387
}
388388

stackit/internal/services/sfs/utils/util.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package utils
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
7+
"strings"
68

9+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
710
"github.com/stackitcloud/stackit-sdk-go/services/sfs"
811

912
"github.com/hashicorp/terraform-plugin-framework/diag"
@@ -28,3 +31,40 @@ func ConfigureClient(ctx context.Context, providerData *core.ProviderData, diags
2831

2932
return apiClient
3033
}
34+
35+
func DescribeValidationError(err sfs.ValidationError) string {
36+
var sb strings.Builder
37+
if err.Title != nil {
38+
sb.WriteString(*err.Title)
39+
sb.WriteRune('\n')
40+
}
41+
if fields := err.Fields; fields != nil {
42+
for _, field := range *fields {
43+
sb.WriteRune('\n')
44+
sb.WriteString("Field: ")
45+
if field.Field != nil {
46+
sb.WriteString(*field.Field)
47+
}
48+
sb.WriteString(" | Reason: ")
49+
if field.Reason != nil {
50+
sb.WriteString(*field.Reason)
51+
}
52+
}
53+
}
54+
return sb.String()
55+
}
56+
57+
func LogAndAddError(ctx context.Context, diags *diag.Diagnostics, summary, detail string, err error) {
58+
if err == nil {
59+
return
60+
}
61+
message := err.Error()
62+
var oapiErr *oapierror.GenericOpenAPIError
63+
if errors.As(err, &oapiErr) {
64+
errModel := oapiErr.Model
65+
if validationErr, ok := errModel.(sfs.ValidationError); ok {
66+
message = DescribeValidationError(validationErr)
67+
}
68+
}
69+
core.LogAndAddError(ctx, diags, summary, fmt.Sprintf("%s: %v", detail, message))
70+
}

stackit/internal/services/sfs/utils/util_test.go

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

9+
"github.com/google/go-cmp/cmp"
910
"github.com/hashicorp/terraform-plugin-framework/diag"
1011
sdkClients "github.com/stackitcloud/stackit-sdk-go/core/clients"
1112
"github.com/stackitcloud/stackit-sdk-go/core/config"
13+
utils2 "github.com/stackitcloud/stackit-sdk-go/core/utils"
1214
"github.com/stackitcloud/stackit-sdk-go/services/sfs"
1315
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
1416
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
@@ -91,3 +93,50 @@ func TestConfigureClient(t *testing.T) {
9193
})
9294
}
9395
}
96+
97+
func TestDescribeValidationError(t *testing.T) {
98+
tests := []struct {
99+
name string
100+
err sfs.ValidationError
101+
want string
102+
}{
103+
{
104+
name: "just title",
105+
err: sfs.ValidationError{
106+
Title: utils2.Ptr("nice title"),
107+
},
108+
want: `nice title
109+
`,
110+
},
111+
{
112+
name: "with fields",
113+
err: sfs.ValidationError{
114+
Title: utils2.Ptr("nice title"),
115+
Fields: &[]sfs.ValidationErrorField{
116+
{
117+
Field: utils2.Ptr("field-a"),
118+
Reason: utils2.Ptr("reason-a"),
119+
},
120+
{
121+
Reason: utils2.Ptr("reason-b"),
122+
},
123+
{
124+
Field: utils2.Ptr("field-c"),
125+
},
126+
},
127+
},
128+
want: `nice title
129+
130+
Field: field-a | Reason: reason-a
131+
Field: | Reason: reason-b
132+
Field: field-c | Reason: `,
133+
},
134+
}
135+
136+
for _, tt := range tests {
137+
got := DescribeValidationError(tt.err)
138+
if d := cmp.Diff(got, tt.want); d != "" {
139+
t.Errorf("DescribeValidationError() = got diff: %s", d)
140+
}
141+
}
142+
}

stackit/internal/testutil/mockserver.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,19 @@ func (m *MockServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
5353
next.Handler(w, r)
5454
return
5555
}
56+
status := next.StatusCode
57+
if status == 0 {
58+
status = http.StatusOK
59+
}
5660
if next.ToJsonBody != nil {
5761
bs, err := json.Marshal(next.ToJsonBody)
5862
if err != nil {
5963
m.t.Fatalf("Error marshaling response body: %v", err)
6064
}
6165
w.Header().Set("content-type", "application/json")
66+
w.WriteHeader(status)
6267
w.Write(bs) //nolint:errcheck //test will fail when this happens
6368
}
64-
status := next.StatusCode
65-
if status == 0 {
66-
status = http.StatusOK
67-
}
6869
w.WriteHeader(status)
6970
}
7071

0 commit comments

Comments
 (0)