Skip to content

Commit 458d051

Browse files
committed
add update command
1 parent 754cab4 commit 458d051

3 files changed

Lines changed: 487 additions & 2 deletions

File tree

internal/cmd/beta/vpn/gateway/gateway.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/vpn/gateway/delete"
66
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/vpn/gateway/describe"
77
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/vpn/gateway/list"
8+
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/vpn/gateway/update"
89
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
910
"github.com/stackitcloud/stackit-cli/internal/pkg/types"
1011
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
@@ -25,8 +26,9 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
2526
}
2627

2728
func addSubcommands(cmd *cobra.Command, params *types.CmdParams) {
28-
cmd.AddCommand(list.NewCmd(params))
29-
cmd.AddCommand(describe.NewCmd(params))
3029
cmd.AddCommand(create.NewCmd(params))
3130
cmd.AddCommand(delete.NewCmd(params))
31+
cmd.AddCommand(describe.NewCmd(params))
32+
cmd.AddCommand(list.NewCmd(params))
33+
cmd.AddCommand(update.NewCmd(params))
3234
}
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
package update
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/spf13/cobra"
8+
vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1api"
9+
"github.com/stackitcloud/stackit-sdk-go/services/vpn/v1api/wait"
10+
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
14+
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
16+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
17+
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
18+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/vpn/client"
19+
vpnUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/vpn/utils"
20+
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
21+
"github.com/stackitcloud/stackit-cli/internal/pkg/types"
22+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
23+
)
24+
25+
const (
26+
gatewayIdArg = "GATEWAY_ID"
27+
28+
availabilityZoneTunnel1Flag = "availability-zone-tunnel-1"
29+
availabilityZoneTunnel2Flag = "availability-zone-tunnel-2"
30+
bgpLocalAsnFlag = "bgp-local-asn"
31+
bgpOverrideAdvertisedRoutesFlag = "bgp-override-advertised-routes"
32+
nameFlag = "name"
33+
labelsFlag = "labels"
34+
planIdFlag = "plan-id"
35+
routingTypeFlag = "routing-type"
36+
)
37+
38+
type inputModel struct {
39+
*globalflags.GlobalFlagModel
40+
GatewayId string
41+
AvailabilityZone vpn.UpdateGatewayPayloadAvailabilityZones
42+
Bgp *vpn.BGPGatewayConfig
43+
Name string
44+
Labels *map[string]string
45+
PlanId string
46+
RoutingType vpn.RoutingType
47+
}
48+
49+
func NewCmd(params *types.CmdParams) *cobra.Command {
50+
cmd := &cobra.Command{
51+
Use: fmt.Sprintf("update %s", gatewayIdArg),
52+
Short: "Updates a vpn gateway",
53+
Long: "Updates a vpn gateway.",
54+
Args: args.SingleArg(gatewayIdArg, utils.ValidateUUID),
55+
Example: examples.Build(
56+
examples.NewExample(
57+
`Update vpn gateway with ID "xxx"`,
58+
"$ stackit beta vpn gateway update xxx",
59+
),
60+
),
61+
RunE: func(cmd *cobra.Command, inputArgs []string) error {
62+
ctx := context.Background()
63+
model, err := parseInput(params.Printer, cmd, inputArgs)
64+
if err != nil {
65+
return fmt.Errorf("unable to parse input: %w", err)
66+
}
67+
68+
// Configure API client
69+
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
70+
if err != nil {
71+
return err
72+
}
73+
74+
gatewayLabel, err := vpnUtils.GetGatewayName(ctx, apiClient.DefaultAPI, model.ProjectId, model.Region, model.GatewayId)
75+
if err != nil {
76+
params.Printer.Debug(print.ErrorLevel, "get gateway name: %v", err)
77+
gatewayLabel = model.GatewayId
78+
} else if gatewayLabel == "" {
79+
gatewayLabel = model.GatewayId
80+
}
81+
82+
projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd)
83+
if err != nil || projectLabel == "" {
84+
projectLabel = model.ProjectId
85+
}
86+
87+
prompt := fmt.Sprintf("Are you sure you want to update vpn gateway %q for the project %q?", gatewayLabel, projectLabel)
88+
err = params.Printer.PromptForConfirmation(prompt)
89+
if err != nil {
90+
return err
91+
}
92+
93+
// Call API
94+
req := buildRequest(ctx, model, apiClient)
95+
resp, err := req.Execute()
96+
if err != nil {
97+
return fmt.Errorf("update vpn gateway: %w", err)
98+
}
99+
100+
// Wait for async operation, if async mode not enabled
101+
if !model.Async {
102+
err := spinner.Run(params.Printer, "Updating gateway", func() error {
103+
_, err = wait.UpdateGatewayWaitHandler(ctx, apiClient.DefaultAPI, model.ProjectId, vpn.Region(model.Region), model.GatewayId).WaitWithContext(ctx)
104+
return err
105+
})
106+
if err != nil {
107+
return fmt.Errorf("waiting for gateway update: %w", err)
108+
}
109+
}
110+
111+
return outputResult(params.Printer, model.OutputFormat, model.Async, projectLabel, resp)
112+
},
113+
}
114+
configureFlags(cmd)
115+
return cmd
116+
}
117+
118+
func configureFlags(cmd *cobra.Command) {
119+
cmd.Flags().String(availabilityZoneTunnel1Flag, "", "Availability Zone of Tunnel 1")
120+
cmd.Flags().String(availabilityZoneTunnel2Flag, "", "Availability Zone of Tunnel 2")
121+
cmd.Flags().Int64(bgpLocalAsnFlag, 0, "ASN for private use (reserved by IANA), both 16Bit and 32Bit ranges are valid (RFC 6996)")
122+
cmd.Flags().StringArray(bgpOverrideAdvertisedRoutesFlag, nil, "A list of IPv4 Prefixes to advertise via BGP")
123+
cmd.Flags().String(nameFlag, "", "Gateway name")
124+
cmd.Flags().StringToString(labelsFlag, nil, "Labels in key=value format, separated by commas")
125+
cmd.Flags().String(planIdFlag, "", "Plan ID")
126+
cmd.Flags().String(routingTypeFlag, "", "Routing Type: \"POLICY_BASED\", \"ROUTE_BASED\" or \"BGP_ROUTE_BASED\"")
127+
128+
err := flags.MarkFlagsRequired(cmd, availabilityZoneTunnel1Flag, availabilityZoneTunnel2Flag, nameFlag, planIdFlag, routingTypeFlag)
129+
cobra.CheckErr(err)
130+
}
131+
132+
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
133+
gatewayId := inputArgs[0]
134+
globalFlags := globalflags.Parse(p, cmd)
135+
if globalFlags.ProjectId == "" {
136+
return nil, &errors.ProjectIdError{}
137+
}
138+
139+
bgpLocalAsn := flags.FlagToInt64Pointer(p, cmd, bgpLocalAsnFlag)
140+
if bgpLocalAsn != nil {
141+
if *bgpLocalAsn < 0 {
142+
return nil, &errors.FlagValidationError{
143+
Flag: bgpLocalAsnFlag,
144+
Details: "must be a positive integer",
145+
}
146+
}
147+
}
148+
bgpOverrideAdvertisedRoutes := flags.FlagToStringArrayValue(p, cmd, bgpOverrideAdvertisedRoutesFlag)
149+
150+
var bgp *vpn.BGPGatewayConfig
151+
if bgpLocalAsn != nil || bgpOverrideAdvertisedRoutes != nil {
152+
bgp = &vpn.BGPGatewayConfig{
153+
LocalAsn: bgpLocalAsn,
154+
OverrideAdvertisedRoutes: flags.FlagToStringArrayValue(p, cmd, bgpOverrideAdvertisedRoutesFlag),
155+
}
156+
}
157+
158+
model := inputModel{
159+
GatewayId: gatewayId,
160+
GlobalFlagModel: globalFlags,
161+
AvailabilityZone: vpn.UpdateGatewayPayloadAvailabilityZones{
162+
Tunnel1: flags.FlagToStringValue(p, cmd, availabilityZoneTunnel1Flag),
163+
Tunnel2: flags.FlagToStringValue(p, cmd, availabilityZoneTunnel2Flag),
164+
},
165+
Bgp: bgp,
166+
Name: flags.FlagToStringValue(p, cmd, nameFlag),
167+
Labels: flags.FlagToStringToStringPointer(p, cmd, labelsFlag),
168+
PlanId: flags.FlagToStringValue(p, cmd, planIdFlag),
169+
RoutingType: vpn.RoutingType(flags.FlagToStringValue(p, cmd, routingTypeFlag)),
170+
}
171+
172+
p.DebugInputModel(model)
173+
return &model, nil
174+
}
175+
176+
func buildRequest(ctx context.Context, model *inputModel, apiClient *vpn.APIClient) vpn.ApiUpdateGatewayRequest {
177+
req := apiClient.DefaultAPI.UpdateGateway(ctx, model.ProjectId, vpn.Region(model.Region), model.GatewayId)
178+
req = req.UpdateGatewayPayload(vpn.UpdateGatewayPayload{
179+
AvailabilityZones: model.AvailabilityZone,
180+
Bgp: model.Bgp,
181+
DisplayName: model.Name,
182+
Labels: model.Labels,
183+
PlanId: model.PlanId,
184+
RoutingType: model.RoutingType,
185+
})
186+
return req
187+
}
188+
189+
func outputResult(p *print.Printer, outputFormat string, async bool, projectLabel string, item *vpn.GatewayResponse) error {
190+
return p.OutputResult(outputFormat, item, func() error {
191+
if item == nil {
192+
p.Outputln("vpn gateway response is empty")
193+
return nil
194+
}
195+
196+
operation := "Updated"
197+
if async {
198+
operation = "Triggered update of"
199+
}
200+
p.Outputf(
201+
"%s vpn gateway %q in project %q.\n",
202+
operation,
203+
item.DisplayName,
204+
projectLabel,
205+
)
206+
return nil
207+
})
208+
}

0 commit comments

Comments
 (0)