Skip to content

Commit 8ab1d41

Browse files
committed
refactor(ufw):
- added update implementation
1 parent c09913a commit 8ab1d41

2 files changed

Lines changed: 174 additions & 1 deletion

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,173 @@
11
package update
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/spf13/cobra"
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
10+
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
14+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/ufw/client"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
16+
"github.com/stackitcloud/stackit-cli/internal/pkg/types"
17+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
18+
ufw "github.com/stackitcloud/stackit-sdk-go/services/ufw/v1api"
19+
"github.com/stackitcloud/stackit-sdk-go/services/ufw/v1api/wait"
20+
21+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
22+
)
23+
24+
const (
25+
instanceIdArg = "INSTANCE_ID"
26+
27+
sourceIpFlag = "sourceIp"
28+
directionFlag = "direction"
29+
descriptionFlag = "description"
30+
etherTypeFlag = "etherType"
31+
portRangeFlag = "portRange"
32+
protocolFlag = "protocol"
33+
)
34+
35+
type inputModel struct {
36+
*globalflags.GlobalFlagModel
37+
RuleRefId string
38+
39+
SourceIp *string
40+
Direction *string
41+
Description *string
42+
EtherType *string
43+
PortRange *string
44+
Protocol *string
45+
}
46+
47+
func NewCmd(params *types.CmdParams) *cobra.Command {
48+
cmd := &cobra.Command{
49+
Use: fmt.Sprintf("update %s", instanceIdArg),
50+
Short: "Updates an UFW rule instance",
51+
Long: "Updates a STACKIT Unified Firewall (UFW) rule instance.",
52+
Args: args.SingleArg(instanceIdArg, utils.ValidateUUID),
53+
Example: examples.Build(
54+
examples.NewExample(
55+
`Update a UFW rule instance with "1.1.1.1/32" as sourceIp for instance with id=ID`,
56+
"$ stackit ufw instance update ID --sourceIp 1.1.1.1/32"),
57+
),
58+
RunE: func(cmd *cobra.Command, args []string) error {
59+
ctx := context.Background()
60+
model, err := parseInput(params.Printer, cmd)
61+
if err != nil {
62+
return err
63+
}
64+
65+
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
66+
if err != nil {
67+
return err
68+
}
69+
70+
projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd)
71+
if err != nil {
72+
params.Printer.Debug(print.ErrorLevel, "get project name: %v", err)
73+
projectLabel = model.ProjectId
74+
}
75+
76+
prompt := fmt.Sprintf("Are you sure you want to update a UFW rule instance for project %q?", projectLabel)
77+
err = params.Printer.PromptForConfirmation(prompt)
78+
if err != nil {
79+
return err
80+
}
81+
82+
req := buildRequest(ctx, model, apiClient)
83+
84+
resp, err := req.Execute()
85+
if err != nil {
86+
return fmt.Errorf("update UFW rule instance: %w", err)
87+
}
88+
instanceId := resp.RefId
89+
90+
if !model.Async {
91+
err := spinner.Run(params.Printer, "Updating ufw rule instance", func() error {
92+
_, err = wait.UpdateRuleWaitHandler(ctx, apiClient.DefaultAPI, model.ProjectId, model.Region, *instanceId).WaitWithContext(ctx)
93+
return err
94+
})
95+
if err != nil {
96+
return fmt.Errorf("wait for UFW rule instance updating process: %w", err)
97+
}
98+
}
99+
100+
return outputResult(params.Printer, model.OutputFormat, model.Async, projectLabel, resp)
101+
},
102+
}
103+
configureFlags(cmd)
104+
return cmd
105+
}
106+
107+
func configureFlags(cmd *cobra.Command) {
108+
cmd.Flags().StringP(sourceIpFlag, "s", "", "The IP (CIDR) to which the rule applies (e.g. 192.168.0.1/32)")
109+
cmd.Flags().StringP(directionFlag, "d", "", "Direction (the direction of the traffic, typically ingress or egress, for security rules type)")
110+
cmd.Flags().StringP(descriptionFlag, "D", "", "Description")
111+
cmd.Flags().StringP(etherTypeFlag, "e", "", "Specifies the bound of the rule (for security rules type)")
112+
cmd.Flags().StringP(portRangeFlag, "r", "", "Port range (the Port range to which the rule applies, for security rules type)")
113+
cmd.Flags().StringP(protocolFlag, "o", "", "The network protocol (e.g. TCP, UDP, ICMP, for security rules type)")
114+
115+
err := flags.MarkFlagsRequired(cmd, sourceIpFlag)
116+
cobra.CheckErr(err)
117+
}
118+
119+
func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
120+
globalFlags := globalflags.Parse(p, cmd)
121+
if globalFlags.ProjectId == "" {
122+
return nil, &errors.ProjectIdError{}
123+
}
124+
125+
if globalFlags.Region == "" {
126+
return nil, &errors.RegionError{}
127+
}
128+
129+
model := inputModel{
130+
GlobalFlagModel: globalFlags,
131+
132+
SourceIp: flags.FlagToStringPointer(p, cmd, sourceIpFlag),
133+
Direction: flags.FlagToStringPointer(p, cmd, directionFlag),
134+
Description: flags.FlagToStringPointer(p, cmd, descriptionFlag),
135+
EtherType: flags.FlagToStringPointer(p, cmd, etherTypeFlag),
136+
PortRange: flags.FlagToStringPointer(p, cmd, portRangeFlag),
137+
Protocol: flags.FlagToStringPointer(p, cmd, protocolFlag),
138+
}
139+
140+
p.DebugInputModel(model)
141+
return &model, nil
142+
}
143+
144+
func buildRequest(ctx context.Context, model *inputModel, apiClient *ufw.APIClient) ufw.ApiUpdateRuleRequest {
145+
req := apiClient.DefaultAPI.UpdateRule(ctx, model.ProjectId, model.Region, model.RuleRefId)
146+
147+
// TODO - add logic for field checking: existing ACLs, correct product, type, instanceID maybe
148+
149+
req = req.UpdateRulePayload(ufw.UpdateRulePayload{
150+
SourceIP: *model.SourceIp,
151+
Direction: model.Direction,
152+
EtherType: model.EtherType,
153+
PortRange: model.PortRange,
154+
Protocol: model.Protocol,
155+
})
156+
157+
return req
158+
}
159+
160+
func outputResult(p *print.Printer, outputFormat string, async bool, projectLabel string, rule *ufw.UpdateRuleResponse) error {
161+
if rule == nil {
162+
return fmt.Errorf("response is nil")
163+
}
164+
165+
return p.OutputResult(outputFormat, rule, func() error {
166+
operationState := "Updated"
167+
if async {
168+
operationState = "Triggered updating process of"
169+
}
170+
p.Outputf("%s rule for project %q. Rule refID: %s\n", operationState, projectLabel, utils.PtrString(rule.RefId))
171+
return nil
172+
})
173+
}

‎internal/cmd/ufw/ufw.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/stackitcloud/stackit-cli/internal/cmd/ufw/rules/delete"
77
"github.com/stackitcloud/stackit-cli/internal/cmd/ufw/rules/describe"
88
"github.com/stackitcloud/stackit-cli/internal/cmd/ufw/rules/list"
9+
"github.com/stackitcloud/stackit-cli/internal/cmd/ufw/rules/update"
910
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
1011
"github.com/stackitcloud/stackit-cli/internal/pkg/types"
1112
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
@@ -27,6 +28,6 @@ func addSubcommands(cmd *cobra.Command, params *types.CmdParams) {
2728
cmd.AddCommand(list.NewCmd(params))
2829
cmd.AddCommand(describe.NewCmd(params))
2930
cmd.AddCommand(create.NewCmd(params))
30-
//cmd.AddCommand(update.NewCmd(params))
31+
cmd.AddCommand(update.NewCmd(params))
3132
cmd.AddCommand(delete.NewCmd(params))
3233
}

0 commit comments

Comments
 (0)