Skip to content

Commit 0f5e3a2

Browse files
authored
fix(opensearch): add nil pointer checks for cmd outputs (#616)
relates to STACKITCLI-107
1 parent 84d7fb6 commit 0f5e3a2

File tree

12 files changed

+359
-35
lines changed

12 files changed

+359
-35
lines changed

internal/cmd/opensearch/credentials/create/create.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77

88
"github.com/goccy/go-yaml"
9+
"github.com/spf13/cobra"
910
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
1011
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
1112
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
@@ -15,8 +16,6 @@ import (
1516
"github.com/stackitcloud/stackit-cli/internal/pkg/services/opensearch/client"
1617
opensearchUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/opensearch/utils"
1718
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
18-
19-
"github.com/spf13/cobra"
2019
"github.com/stackitcloud/stackit-sdk-go/services/opensearch"
2120
)
2221

@@ -79,7 +78,7 @@ func NewCmd(p *print.Printer) *cobra.Command {
7978
return fmt.Errorf("create OpenSearch credentials: %w", err)
8079
}
8180

82-
return outputResult(p, model, instanceLabel, resp)
81+
return outputResult(p, model.OutputFormat, model.ShowPassword, instanceLabel, resp)
8382
},
8483
}
8584
configureFlags(cmd)
@@ -123,11 +122,15 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *opensearch.
123122
return req
124123
}
125124

126-
func outputResult(p *print.Printer, model *inputModel, instanceLabel string, resp *opensearch.CredentialsResponse) error {
127-
if !model.ShowPassword {
125+
func outputResult(p *print.Printer, outputFormat string, showPassword bool, instanceLabel string, resp *opensearch.CredentialsResponse) error {
126+
if resp == nil || resp.Raw == nil || resp.Raw.Credentials == nil || resp.Uri == nil {
127+
return fmt.Errorf("response or response content is nil")
128+
}
129+
130+
if !showPassword {
128131
resp.Raw.Credentials.Password = utils.Ptr("hidden")
129132
}
130-
switch model.OutputFormat {
133+
switch outputFormat {
131134
case print.JSONOutputFormat:
132135
details, err := json.MarshalIndent(resp, "", " ")
133136
if err != nil {
@@ -151,7 +154,7 @@ func outputResult(p *print.Printer, model *inputModel, instanceLabel string, res
151154
if username := resp.Raw.Credentials.Username; username != nil && *username != "" {
152155
p.Outputf("Username: %s\n", *username)
153156
}
154-
if !model.ShowPassword {
157+
if !showPassword {
155158
p.Outputf("Password: <hidden>\n")
156159
} else {
157160
p.Outputf("Password: %s\n", utils.PtrString(resp.Raw.Credentials.Password))

internal/cmd/opensearch/credentials/create/create_test.go

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import (
44
"context"
55
"testing"
66

7-
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
8-
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
9-
107
"github.com/google/go-cmp/cmp"
118
"github.com/google/go-cmp/cmp/cmpopts"
129
"github.com/google/uuid"
10+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
1313
"github.com/stackitcloud/stackit-sdk-go/services/opensearch"
1414
)
1515

@@ -200,3 +200,83 @@ func TestBuildRequest(t *testing.T) {
200200
})
201201
}
202202
}
203+
204+
func TestOutputResult(t *testing.T) {
205+
type args struct {
206+
outputFormat string
207+
showPassword bool
208+
instanceLabel string
209+
resp *opensearch.CredentialsResponse
210+
}
211+
tests := []struct {
212+
name string
213+
args args
214+
wantErr bool
215+
}{
216+
{
217+
name: "empty",
218+
args: args{},
219+
wantErr: true,
220+
},
221+
{
222+
name: "set no raw in response",
223+
args: args{
224+
resp: &opensearch.CredentialsResponse{
225+
Uri: utils.Ptr("https://opensearch.example.com"),
226+
},
227+
},
228+
wantErr: true,
229+
},
230+
{
231+
name: "set empty raw in response",
232+
args: args{
233+
resp: &opensearch.CredentialsResponse{
234+
Raw: &opensearch.RawCredentials{},
235+
Uri: utils.Ptr("https://opensearch.example.com"),
236+
},
237+
},
238+
wantErr: true,
239+
},
240+
{
241+
name: "set raw but no uri in response",
242+
args: args{
243+
resp: &opensearch.CredentialsResponse{
244+
Raw: &opensearch.RawCredentials{
245+
Credentials: &opensearch.Credentials{},
246+
},
247+
},
248+
},
249+
wantErr: true,
250+
},
251+
{
252+
name: "set uri but no raw in response",
253+
args: args{
254+
resp: &opensearch.CredentialsResponse{
255+
Uri: utils.Ptr("https://opensearch.example.com"),
256+
},
257+
},
258+
wantErr: true,
259+
},
260+
{
261+
name: "set response correctly",
262+
args: args{
263+
resp: &opensearch.CredentialsResponse{
264+
Raw: &opensearch.RawCredentials{
265+
Credentials: &opensearch.Credentials{},
266+
},
267+
Uri: utils.Ptr("https://opensearch.example.com"),
268+
},
269+
},
270+
wantErr: false,
271+
},
272+
}
273+
p := print.NewPrinter()
274+
p.Cmd = NewCmd(p)
275+
for _, tt := range tests {
276+
t.Run(tt.name, func(t *testing.T) {
277+
if err := outputResult(p, tt.args.outputFormat, tt.args.showPassword, tt.args.instanceLabel, tt.args.resp); (err != nil) != tt.wantErr {
278+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
279+
}
280+
})
281+
}
282+
}

internal/cmd/opensearch/credentials/describe/describe.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *opensearch.
112112
}
113113

114114
func outputResult(p *print.Printer, outputFormat string, credentials *opensearch.CredentialsResponse) error {
115+
if credentials == nil {
116+
return fmt.Errorf("credentials is nil")
117+
}
118+
115119
switch outputFormat {
116120
case print.JSONOutputFormat:
117121
details, err := json.MarshalIndent(credentials, "", " ")

internal/cmd/opensearch/credentials/describe/describe_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,37 @@ func TestBuildRequest(t *testing.T) {
243243
})
244244
}
245245
}
246+
247+
func TestOutputResult(t *testing.T) {
248+
type args struct {
249+
outputFormat string
250+
credentials *opensearch.CredentialsResponse
251+
}
252+
tests := []struct {
253+
name string
254+
args args
255+
wantErr bool
256+
}{
257+
{
258+
name: "empty",
259+
args: args{},
260+
wantErr: true,
261+
},
262+
{
263+
name: "set empty credentials",
264+
args: args{
265+
credentials: &opensearch.CredentialsResponse{},
266+
},
267+
wantErr: false,
268+
},
269+
}
270+
p := print.NewPrinter()
271+
p.Cmd = NewCmd(p)
272+
for _, tt := range tests {
273+
t.Run(tt.name, func(t *testing.T) {
274+
if err := outputResult(p, tt.args.outputFormat, tt.args.credentials); (err != nil) != tt.wantErr {
275+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
276+
}
277+
})
278+
}
279+
}

internal/cmd/opensearch/credentials/list/list_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,44 @@ func TestBuildRequest(t *testing.T) {
207207
})
208208
}
209209
}
210+
211+
func TestOutputResult(t *testing.T) {
212+
type args struct {
213+
outputFormat string
214+
credentials []opensearch.CredentialsListItem
215+
}
216+
tests := []struct {
217+
name string
218+
args args
219+
wantErr bool
220+
}{
221+
{
222+
name: "empty",
223+
args: args{},
224+
wantErr: false,
225+
},
226+
{
227+
name: "set empty credentials slice",
228+
args: args{
229+
credentials: []opensearch.CredentialsListItem{},
230+
},
231+
wantErr: false,
232+
},
233+
{
234+
name: "set empty credential in credentials slice",
235+
args: args{
236+
credentials: []opensearch.CredentialsListItem{{}},
237+
},
238+
wantErr: false,
239+
},
240+
}
241+
p := print.NewPrinter()
242+
p.Cmd = NewCmd(p)
243+
for _, tt := range tests {
244+
t.Run(tt.name, func(t *testing.T) {
245+
if err := outputResult(p, tt.args.outputFormat, tt.args.credentials); (err != nil) != tt.wantErr {
246+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
247+
}
248+
})
249+
}
250+
}

internal/cmd/opensearch/instance/create/create.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func NewCmd(p *print.Printer) *cobra.Command {
127127
s.Stop()
128128
}
129129

130-
return outputResult(p, model, projectLabel, instanceId, resp)
130+
return outputResult(p, model.OutputFormat, model.Async, projectLabel, instanceId, resp)
131131
},
132132
}
133133
configureFlags(cmd)
@@ -256,8 +256,8 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient openSearchCl
256256
return req, nil
257257
}
258258

259-
func outputResult(p *print.Printer, model *inputModel, projectLabel, instanceId string, resp *opensearch.CreateInstanceResponse) error {
260-
switch model.OutputFormat {
259+
func outputResult(p *print.Printer, outputFormat string, async bool, projectLabel, instanceId string, resp *opensearch.CreateInstanceResponse) error {
260+
switch outputFormat {
261261
case print.JSONOutputFormat:
262262
details, err := json.MarshalIndent(resp, "", " ")
263263
if err != nil {
@@ -276,7 +276,7 @@ func outputResult(p *print.Printer, model *inputModel, projectLabel, instanceId
276276
return nil
277277
default:
278278
operationState := "Created"
279-
if model.Async {
279+
if async {
280280
operationState = "Triggered creation of"
281281
}
282282
p.Outputf("%s instance for project %q. Instance ID: %s\n", operationState, projectLabel, instanceId)

internal/cmd/opensearch/instance/create/create_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,3 +488,40 @@ func TestBuildRequest(t *testing.T) {
488488
})
489489
}
490490
}
491+
492+
func TestOutputResult(t *testing.T) {
493+
type args struct {
494+
outputFormat string
495+
async bool
496+
projectLabel string
497+
instanceId string
498+
resp *opensearch.CreateInstanceResponse
499+
}
500+
tests := []struct {
501+
name string
502+
args args
503+
wantErr bool
504+
}{
505+
{
506+
name: "empty",
507+
args: args{},
508+
wantErr: false,
509+
},
510+
{
511+
name: "set empty response",
512+
args: args{
513+
resp: &opensearch.CreateInstanceResponse{},
514+
},
515+
wantErr: false,
516+
},
517+
}
518+
p := print.NewPrinter()
519+
p.Cmd = NewCmd(p)
520+
for _, tt := range tests {
521+
t.Run(tt.name, func(t *testing.T) {
522+
if err := outputResult(p, tt.args.outputFormat, tt.args.async, tt.args.projectLabel, tt.args.instanceId, tt.args.resp); (err != nil) != tt.wantErr {
523+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
524+
}
525+
})
526+
}
527+
}

internal/cmd/opensearch/instance/describe/describe.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *opensearch.
100100
}
101101

102102
func outputResult(p *print.Printer, outputFormat string, instance *opensearch.Instance) error {
103+
if instance == nil {
104+
return fmt.Errorf("instance is nil")
105+
}
106+
103107
switch outputFormat {
104108
case print.JSONOutputFormat:
105109
details, err := json.MarshalIndent(instance, "", " ")
@@ -123,18 +127,22 @@ func outputResult(p *print.Printer, outputFormat string, instance *opensearch.In
123127
table.AddSeparator()
124128
table.AddRow("NAME", utils.PtrString(instance.Name))
125129
table.AddSeparator()
126-
table.AddRow("LAST OPERATION TYPE", utils.PtrString(instance.LastOperation.Type))
127-
table.AddSeparator()
128-
table.AddRow("LAST OPERATION STATE", utils.PtrString(instance.LastOperation.State))
129-
table.AddSeparator()
130+
if instance.LastOperation != nil {
131+
table.AddRow("LAST OPERATION TYPE", utils.PtrString(instance.LastOperation.Type))
132+
table.AddSeparator()
133+
table.AddRow("LAST OPERATION STATE", utils.PtrString(instance.LastOperation.State))
134+
table.AddSeparator()
135+
}
130136
table.AddRow("PLAN ID", utils.PtrString(instance.PlanId))
131137
// Only show ACL if it's present and not empty
132-
acl := (*instance.Parameters)[aclParameterKey]
133-
aclStr, ok := acl.(string)
134-
if ok {
135-
if aclStr != "" {
136-
table.AddSeparator()
137-
table.AddRow("ACL", aclStr)
138+
if instance.Parameters != nil {
139+
acl := (*instance.Parameters)[aclParameterKey]
140+
aclStr, ok := acl.(string)
141+
if ok {
142+
if aclStr != "" {
143+
table.AddSeparator()
144+
table.AddRow("ACL", aclStr)
145+
}
138146
}
139147
}
140148
err := table.Display(p)

internal/cmd/opensearch/instance/describe/describe_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,37 @@ func TestBuildRequest(t *testing.T) {
216216
})
217217
}
218218
}
219+
220+
func TestOutputResult(t *testing.T) {
221+
type args struct {
222+
outputFormat string
223+
instance *opensearch.Instance
224+
}
225+
tests := []struct {
226+
name string
227+
args args
228+
wantErr bool
229+
}{
230+
{
231+
name: "empty",
232+
args: args{},
233+
wantErr: true,
234+
},
235+
{
236+
name: "set empty instance",
237+
args: args{
238+
instance: &opensearch.Instance{},
239+
},
240+
wantErr: false,
241+
},
242+
}
243+
p := print.NewPrinter()
244+
p.Cmd = NewCmd(p)
245+
for _, tt := range tests {
246+
t.Run(tt.name, func(t *testing.T) {
247+
if err := outputResult(p, tt.args.outputFormat, tt.args.instance); (err != nil) != tt.wantErr {
248+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
249+
}
250+
})
251+
}
252+
}

0 commit comments

Comments
 (0)