Skip to content

Commit ee4ff8a

Browse files
committed
feature: implement create command
1 parent cb07c4f commit ee4ff8a

File tree

2 files changed

+408
-4
lines changed

2 files changed

+408
-4
lines changed
Lines changed: 127 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,154 @@
11
package create
22

33
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
48
"github.com/spf13/cobra"
59
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
10+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
611
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
714
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
16+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client"
17+
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
818
)
919

20+
type inputModel struct {
21+
*globalflags.GlobalFlagModel
22+
Labels map[string]any
23+
Description string
24+
Name string
25+
Stateful bool
26+
}
27+
1028
func NewCmd(p *print.Printer) *cobra.Command {
1129
cmd := &cobra.Command{
1230
Use: "create",
1331
Short: "create security groups",
1432
Long: "create security groups",
1533
Args: args.NoArgs,
1634
Example: examples.Build(
17-
examples.NewExample(`example 1`, `foo bar baz`),
18-
examples.NewExample(`example 2`, `foo bar baz`),
35+
examples.NewExample(`create a named group`, `$ stackit beta security-group create --name my-new-group`),
36+
examples.NewExample(`create a named group with labels`, `$ stackit beta security-group create --name my-new-group --labels label1=value1,label2=value2`),
1937
),
2038
RunE: func(cmd *cobra.Command, args []string) error {
2139
return executeCreate(cmd, p, args)
2240
},
2341
}
24-
cmd.Flags().String("dummy", "foo", "fooify")
42+
43+
configureFlags(cmd)
2544
return cmd
2645
}
2746

28-
func executeCreate(cmd *cobra.Command, p *print.Printer, args []string) error {
47+
func configureFlags(cmd *cobra.Command) {
48+
cmd.Flags().String("name", "", "the name of the security group")
49+
cmd.Flags().String("description", "", "an optional description of the security group. Must be <= 127 chars")
50+
cmd.Flags().Bool("stateful", false, "create a stateful or a stateless security group")
51+
cmd.Flags().StringSlice("labels", nil, "a list of labels in the form <key>=<value>")
52+
53+
if err := flags.MarkFlagsRequired(cmd, "name"); err != nil {
54+
cobra.CheckErr(err)
55+
}
56+
}
57+
58+
func executeCreate(cmd *cobra.Command, p *print.Printer, _ []string) error {
2959
p.Info("executing create command")
60+
ctx := context.Background()
61+
model, err := parseInput(p, cmd)
62+
if err != nil {
63+
return err
64+
}
65+
66+
// Configure API client
67+
apiClient, err := client.ConfigureClient(p)
68+
if err != nil {
69+
return err
70+
}
71+
72+
projectLabel, err := projectname.GetProjectName(ctx, p, cmd)
73+
if err != nil {
74+
p.Debug(print.ErrorLevel, "get project name: %v", err)
75+
projectLabel = model.ProjectId
76+
}
77+
78+
// Call API
79+
req := buildRequest(ctx, model, apiClient)
80+
_, err = req.Execute()
81+
if err != nil {
82+
return fmt.Errorf("create security group: %w", err)
83+
}
84+
85+
operationState := "Enabled"
86+
if model.Async {
87+
operationState = "Triggered enablement of"
88+
}
89+
p.Info("%s security group %q for %q\n", operationState, model.Name, projectLabel)
3090
return nil
3191
}
92+
93+
func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
94+
globalFlags := globalflags.Parse(p, cmd)
95+
if globalFlags.ProjectId == "" {
96+
return nil, &errors.ProjectIdError{}
97+
}
98+
99+
labels := make(map[string]any)
100+
for _, label := range flags.FlagToStringSliceValue(p, cmd, "labels") {
101+
parts := strings.Split(label, "=")
102+
if len(parts) != 2 {
103+
return nil, &errors.ArgValidationError{
104+
Arg: "labels",
105+
Details: "invalid label declaration. Must be in the form <key>=<value>",
106+
}
107+
}
108+
labels[parts[0]] = parts[1]
109+
110+
}
111+
description := flags.FlagToStringValue(p, cmd, "description")
112+
if len(description) >= 128 {
113+
return nil, &errors.ArgValidationError{
114+
Arg: "invalid description",
115+
Details: "description exceeds 127 characters in length",
116+
}
117+
}
118+
model := inputModel{
119+
GlobalFlagModel: globalFlags,
120+
Name: flags.FlagToStringValue(p, cmd, "name"),
121+
122+
Labels: labels,
123+
Description: description,
124+
Stateful: flags.FlagToBoolValue(p, cmd, "stateful"),
125+
}
126+
127+
if p.IsVerbosityDebug() {
128+
modelStr, err := print.BuildDebugStrFromInputModel(model)
129+
if err != nil {
130+
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
131+
} else {
132+
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
133+
}
134+
}
135+
136+
return &model, nil
137+
}
138+
139+
func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiCreateSecurityGroupRequest {
140+
request := apiClient.CreateSecurityGroup(ctx, model.ProjectId)
141+
payload := iaas.NewCreateSecurityGroupPayload(&model.Name)
142+
payload.Description = &model.Description
143+
if model.Labels != nil {
144+
// this check assure that we don't end up with a pointer to nil
145+
// which is a thing in go!
146+
payload.Labels = &model.Labels
147+
}
148+
payload.Name = &model.Name
149+
payload.Stateful = &model.Stateful
150+
request = request.CreateSecurityGroupPayload(*payload)
151+
152+
return request
153+
154+
}

0 commit comments

Comments
 (0)