Skip to content

Commit 27e06c0

Browse files
committed
delete command
1 parent 6d7009a commit 27e06c0

3 files changed

Lines changed: 296 additions & 0 deletions

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package delete
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/globalflags"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
16+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/vpn/client"
17+
vpnUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/vpn/utils"
18+
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
19+
"github.com/stackitcloud/stackit-cli/internal/pkg/types"
20+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
21+
)
22+
23+
const (
24+
gatewayIdArg = "GATEWAY_ID"
25+
)
26+
27+
type inputModel struct {
28+
*globalflags.GlobalFlagModel
29+
GatewayId string
30+
}
31+
32+
func NewCmd(params *types.CmdParams) *cobra.Command {
33+
cmd := &cobra.Command{
34+
Use: fmt.Sprintf("delete %s", gatewayIdArg),
35+
Short: "Deletes a vpn gateway",
36+
Long: "Deletes a vpn gateway.",
37+
Args: args.SingleArg(gatewayIdArg, utils.ValidateUUID),
38+
Example: examples.Build(
39+
examples.NewExample(
40+
`Delete a vpn gateway with the ID "xxx"`,
41+
"$ stackit beta vpn gateway delete xxx",
42+
),
43+
),
44+
RunE: func(cmd *cobra.Command, inputArgs []string) error {
45+
ctx := context.Background()
46+
model, err := parseInput(params.Printer, cmd, inputArgs)
47+
if err != nil {
48+
return fmt.Errorf("unable to parse input: %w", err)
49+
}
50+
51+
// Configure API client
52+
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
53+
if err != nil {
54+
return err
55+
}
56+
57+
gatewayLabel, err := vpnUtils.GetGatewayName(ctx, apiClient.DefaultAPI, model.ProjectId, model.Region, model.GatewayId)
58+
if err != nil {
59+
params.Printer.Debug(print.ErrorLevel, "get gateway name: %v", err)
60+
gatewayLabel = model.GatewayId
61+
} else if gatewayLabel == "" {
62+
gatewayLabel = model.GatewayId
63+
}
64+
65+
prompt := fmt.Sprintf("Are you sure you want to delete the vpn gateway %q? (This cannot be undone)", gatewayLabel)
66+
err = params.Printer.PromptForConfirmation(prompt)
67+
if err != nil {
68+
return err
69+
}
70+
71+
// Call API
72+
req := buildRequest(ctx, model, apiClient)
73+
err = req.Execute()
74+
if err != nil {
75+
return fmt.Errorf("delete vpn gateway: %w", err)
76+
}
77+
78+
// Wait for async operation, if async mode not enabled
79+
if !model.Async {
80+
err := spinner.Run(params.Printer, "Deleting gateway", func() error {
81+
_, err = wait.DeleteGatewayWaitHandler(ctx, apiClient.DefaultAPI, model.ProjectId, vpn.Region(model.Region), model.GatewayId).WaitWithContext(ctx)
82+
return err
83+
})
84+
if err != nil {
85+
return fmt.Errorf("waiting for gateway deletion: %w", err)
86+
}
87+
}
88+
89+
operation := "Deleted"
90+
if model.Async {
91+
operation = "Triggered deletion of"
92+
}
93+
94+
params.Printer.Outputf("%s gateway %q\n", operation, gatewayLabel)
95+
return nil
96+
},
97+
}
98+
return cmd
99+
}
100+
101+
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
102+
gatewayId := inputArgs[0]
103+
104+
globalFlags := globalflags.Parse(p, cmd)
105+
if globalFlags.ProjectId == "" {
106+
return nil, &errors.ProjectIdError{}
107+
}
108+
109+
model := inputModel{
110+
GlobalFlagModel: globalFlags,
111+
GatewayId: gatewayId,
112+
}
113+
114+
p.DebugInputModel(model)
115+
return &model, nil
116+
}
117+
118+
func buildRequest(ctx context.Context, model *inputModel, apiClient *vpn.APIClient) vpn.ApiDeleteGatewayRequest {
119+
return apiClient.DefaultAPI.DeleteGateway(ctx, model.ProjectId, vpn.Region(model.Region), model.GatewayId)
120+
}
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package delete
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/google/go-cmp/cmp"
8+
"github.com/google/go-cmp/cmp/cmpopts"
9+
"github.com/google/uuid"
10+
vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1api"
11+
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/testutils"
14+
)
15+
16+
var projectIdFlag = globalflags.ProjectIdFlag
17+
var regionFlag = globalflags.RegionFlag
18+
19+
type testCtxKey struct{}
20+
21+
var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo")
22+
var testClient = &vpn.APIClient{DefaultAPI: &vpn.DefaultAPIService{}}
23+
24+
var testProjectId = uuid.NewString()
25+
var testRegion = "eu01"
26+
var testGatewayId = uuid.NewString()
27+
28+
func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
29+
flagValues := map[string]string{
30+
projectIdFlag: testProjectId,
31+
regionFlag: testRegion,
32+
}
33+
for _, mod := range mods {
34+
mod(flagValues)
35+
}
36+
return flagValues
37+
}
38+
39+
func fixtureArgValues(mods ...func(argValues []string)) []string {
40+
argValues := []string{
41+
testGatewayId,
42+
}
43+
for _, mod := range mods {
44+
mod(argValues)
45+
}
46+
return argValues
47+
}
48+
49+
func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
50+
model := &inputModel{
51+
GlobalFlagModel: &globalflags.GlobalFlagModel{
52+
ProjectId: testProjectId,
53+
Verbosity: globalflags.VerbosityDefault,
54+
Region: testRegion,
55+
},
56+
GatewayId: testGatewayId,
57+
}
58+
for _, mod := range mods {
59+
mod(model)
60+
}
61+
return model
62+
}
63+
64+
func fixtureRequest(mods ...func(request *vpn.ApiDeleteGatewayRequest)) vpn.ApiDeleteGatewayRequest {
65+
request := testClient.DefaultAPI.DeleteGateway(testCtx, testProjectId, vpn.Region(testRegion), testGatewayId)
66+
for _, mod := range mods {
67+
mod(&request)
68+
}
69+
return request
70+
}
71+
72+
func TestParseInput(t *testing.T) {
73+
tests := []struct {
74+
description string
75+
argValues []string
76+
flagValues map[string]string
77+
isValid bool
78+
expectedModel *inputModel
79+
}{
80+
{
81+
description: "base",
82+
argValues: fixtureArgValues(),
83+
flagValues: fixtureFlagValues(),
84+
isValid: true,
85+
expectedModel: fixtureInputModel(),
86+
},
87+
{
88+
description: "no values",
89+
argValues: []string{},
90+
flagValues: map[string]string{},
91+
isValid: false,
92+
},
93+
{
94+
description: "no arg values",
95+
argValues: []string{},
96+
flagValues: fixtureFlagValues(),
97+
isValid: false,
98+
},
99+
{
100+
description: "no flag values",
101+
argValues: fixtureArgValues(),
102+
flagValues: map[string]string{},
103+
isValid: false,
104+
},
105+
{
106+
description: "project id missing",
107+
argValues: fixtureArgValues(),
108+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
109+
delete(flagValues, projectIdFlag)
110+
}),
111+
isValid: false,
112+
},
113+
{
114+
description: "project id invalid 1",
115+
argValues: fixtureArgValues(),
116+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
117+
flagValues[projectIdFlag] = ""
118+
}),
119+
isValid: false,
120+
},
121+
{
122+
description: "project id invalid 2",
123+
argValues: fixtureArgValues(),
124+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
125+
flagValues[projectIdFlag] = "invalid-uuid"
126+
}),
127+
isValid: false,
128+
},
129+
{
130+
description: "gateway id invalid 1",
131+
argValues: []string{""},
132+
flagValues: fixtureFlagValues(),
133+
isValid: false,
134+
},
135+
{
136+
description: "gateway id invalid 2",
137+
argValues: []string{"invalid-uuid"},
138+
flagValues: fixtureFlagValues(),
139+
isValid: false,
140+
},
141+
}
142+
for _, tt := range tests {
143+
t.Run(tt.description, func(t *testing.T) {
144+
testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid)
145+
})
146+
}
147+
}
148+
149+
func TestBuildRequest(t *testing.T) {
150+
tests := []struct {
151+
description string
152+
model *inputModel
153+
expectedRequest vpn.ApiDeleteGatewayRequest
154+
}{
155+
{
156+
description: "base",
157+
model: fixtureInputModel(),
158+
expectedRequest: fixtureRequest(),
159+
},
160+
}
161+
for _, tt := range tests {
162+
t.Run(tt.description, func(t *testing.T) {
163+
request := buildRequest(testCtx, tt.model, testClient)
164+
165+
diff := cmp.Diff(request, tt.expectedRequest,
166+
cmp.AllowUnexported(tt.expectedRequest, vpn.DefaultAPIService{}),
167+
cmpopts.EquateComparable(testCtx),
168+
)
169+
if diff != "" {
170+
t.Fatalf("Data does not match: %s", diff)
171+
}
172+
})
173+
}
174+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package gateway
22

33
import (
4+
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/vpn/gateway/delete"
45
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/vpn/gateway/describe"
56
"github.com/stackitcloud/stackit-cli/internal/cmd/beta/vpn/gateway/list"
67
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
@@ -25,4 +26,5 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
2526
func addSubcommands(cmd *cobra.Command, params *types.CmdParams) {
2627
cmd.AddCommand(list.NewCmd(params))
2728
cmd.AddCommand(describe.NewCmd(params))
29+
cmd.AddCommand(delete.NewCmd(params))
2830
}

0 commit comments

Comments
 (0)