Skip to content

Commit 5a7f5c4

Browse files
committed
feat(vpn): onboard gateway
relates to STACKITCLI-353
1 parent f2883ea commit 5a7f5c4

9 files changed

Lines changed: 155 additions & 0 deletions

File tree

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ require (
3737
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.7
3838
github.com/stackitcloud/stackit-sdk-go/services/ske v1.11.0
3939
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.3
40+
github.com/stackitcloud/stackit-sdk-go/services/vpn v0.9.0
4041
github.com/zalando/go-keyring v0.2.6
4142
golang.org/x/mod v0.34.0
4243
golang.org/x/oauth2 v0.35.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,8 @@ github.com/stackitcloud/stackit-sdk-go/services/ske v1.11.0 h1:QoKyQPe8FqDqJLNgE
656656
github.com/stackitcloud/stackit-sdk-go/services/ske v1.11.0/go.mod h1:KhVYCR58wETqdI7Quwhe3OR3BhB2T/b7DzaMsfDnr8g=
657657
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.3 h1:AQrcr+qeIuZob+3TT2q1L4WOPtpsu5SEpkTnOUHDqfE=
658658
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.3/go.mod h1:8BBGC69WFXWWmKgzSjgE4HvsI7pEgO0RN2cASwuPJ18=
659+
github.com/stackitcloud/stackit-sdk-go/services/vpn v0.9.0 h1:ZqZ0Wbkyz1rnclTTvnAKNalo0oSNWq9pyS4lO/athaQ=
660+
github.com/stackitcloud/stackit-sdk-go/services/vpn v0.9.0/go.mod h1:toIjQk1dhxdUFVyCWJJja0w/0nFpDid8MWX0ukQfvfo=
659661
github.com/stbenjam/no-sprintf-host-port v0.3.1 h1:AyX7+dxI4IdLBPtDbsGAyqiTSLpCP9hWRrXQDU4Cm/g=
660662
github.com/stbenjam/no-sprintf-host-port v0.3.1/go.mod h1:ODbZesTCHMVKthBHskvUUexdcNHAQRXk9NpSsL8p/HQ=
661663
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

internal/cmd/config/set/set.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const (
5353
logsCustomEndpointFlag = "logs-custom-endpoint"
5454
sfsCustomEndpointFlag = "sfs-custom-endpoint"
5555
cdnCustomEndpointFlag = "cdn-custom-endpoint"
56+
vpnCustomEndpointFlag = "vpn-custom-endpoint"
5657
)
5758

5859
type inputModel struct {
@@ -172,6 +173,7 @@ func configureFlags(cmd *cobra.Command) {
172173
cmd.Flags().String(logsCustomEndpointFlag, "", "Logs API base URL, used in calls to this API")
173174
cmd.Flags().String(sfsCustomEndpointFlag, "", "SFS API base URL, used in calls to this API")
174175
cmd.Flags().String(cdnCustomEndpointFlag, "", "CDN API base URL, used in calls to this API")
176+
cmd.Flags().String(vpnCustomEndpointFlag, "", "VPN API base URL, used in calls to this API")
175177

176178
err := viper.BindPFlag(config.SessionTimeLimitKey, cmd.Flags().Lookup(sessionTimeLimitFlag))
177179
cobra.CheckErr(err)
@@ -240,6 +242,8 @@ func configureFlags(cmd *cobra.Command) {
240242
cobra.CheckErr(err)
241243
err = viper.BindPFlag(config.CDNCustomEndpointKey, cmd.Flags().Lookup(cdnCustomEndpointFlag))
242244
cobra.CheckErr(err)
245+
err = viper.BindPFlag(config.VpnCustomEndpointKey, cmd.Flags().Lookup(vpnCustomEndpointFlag))
246+
cobra.CheckErr(err)
243247
}
244248

245249
func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) {

internal/cmd/config/unset/unset.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const (
5858
intakeCustomEndpointFlag = "intake-custom-endpoint"
5959
logsCustomEndpointFlag = "logs-custom-endpoint"
6060
cdnCustomEndpointFlag = "cdn-custom-endpoint"
61+
vpnCustomEndpointFlag = "vpn-custom-endpoint"
6162
)
6263

6364
type inputModel struct {
@@ -102,6 +103,7 @@ type inputModel struct {
102103
IntakeCustomEndpoint bool
103104
LogsCustomEndpoint bool
104105
CDNCustomEndpoint bool
106+
VpnCustomEndpoint bool
105107
}
106108

107109
func NewCmd(params *types.CmdParams) *cobra.Command {
@@ -243,6 +245,9 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
243245
if model.CDNCustomEndpoint {
244246
viper.Set(config.CDNCustomEndpointKey, "")
245247
}
248+
if model.VpnCustomEndpoint {
249+
viper.Set(config.VpnCustomEndpointKey, "")
250+
}
246251

247252
err := config.Write()
248253
if err != nil {
@@ -297,6 +302,7 @@ func configureFlags(cmd *cobra.Command) {
297302
cmd.Flags().Bool(logsCustomEndpointFlag, false, "Logs API base URL. If unset, uses the default base URL")
298303
cmd.Flags().Bool(sfsCustomEndpointFlag, false, "SFS API base URL. If unset, uses the default base URL")
299304
cmd.Flags().Bool(cdnCustomEndpointFlag, false, "Custom CDN endpoint URL. If unset, uses the default base URL")
305+
cmd.Flags().Bool(vpnCustomEndpointFlag, false, "VPN API base URL. If unset, uses the default base URL")
300306
}
301307

302308
func parseInput(p *print.Printer, cmd *cobra.Command) *inputModel {
@@ -342,6 +348,7 @@ func parseInput(p *print.Printer, cmd *cobra.Command) *inputModel {
342348
IntakeCustomEndpoint: flags.FlagToBoolValue(p, cmd, intakeCustomEndpointFlag),
343349
LogsCustomEndpoint: flags.FlagToBoolValue(p, cmd, logsCustomEndpointFlag),
344350
CDNCustomEndpoint: flags.FlagToBoolValue(p, cmd, cdnCustomEndpointFlag),
351+
VpnCustomEndpoint: flags.FlagToBoolValue(p, cmd, vpnCustomEndpointFlag),
345352
}
346353

347354
p.DebugInputModel(model)

internal/cmd/config/unset/unset_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ func fixtureFlagValues(mods ...func(flagValues map[string]bool)) map[string]bool
4848
intakeCustomEndpointFlag: true,
4949
logsCustomEndpointFlag: true,
5050
cdnCustomEndpointFlag: true,
51+
vpnCustomEndpointFlag: true,
5152
}
5253
for _, mod := range mods {
5354
mod(flagValues)
@@ -94,6 +95,7 @@ func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
9495
IntakeCustomEndpoint: true,
9596
LogsCustomEndpoint: true,
9697
CDNCustomEndpoint: true,
98+
VpnCustomEndpoint: true,
9799
}
98100
for _, mod := range mods {
99101
mod(model)
@@ -156,6 +158,7 @@ func TestParseInput(t *testing.T) {
156158
model.IntakeCustomEndpoint = false
157159
model.LogsCustomEndpoint = false
158160
model.CDNCustomEndpoint = false
161+
model.VpnCustomEndpoint = false
159162
}),
160163
},
161164
{
@@ -358,6 +361,16 @@ func TestParseInput(t *testing.T) {
358361
model.CDNCustomEndpoint = false
359362
}),
360363
},
364+
{
365+
description: "vpn custom endpoint empty",
366+
flagValues: fixtureFlagValues(func(flagValues map[string]bool) {
367+
flagValues[vpnCustomEndpointFlag] = false
368+
}),
369+
isValid: true,
370+
expectedModel: fixtureInputModel(func(model *inputModel) {
371+
model.VpnCustomEndpoint = false
372+
}),
373+
},
361374
}
362375
for _, tt := range tests {
363376
t.Run(tt.description, func(t *testing.T) {

internal/pkg/config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const (
5454
CDNCustomEndpointKey = "cdn_custom_endpoint"
5555
IntakeCustomEndpointKey = "intake_custom_endpoint"
5656
LogsCustomEndpointKey = "logs_custom_endpoint"
57+
VpnCustomEndpointKey = "vpn_custom_endpoint"
5758

5859
ProjectNameKey = "project_name"
5960
DefaultProfileName = "default"
@@ -121,6 +122,7 @@ var ConfigKeys = []string{
121122
ServiceEnablementCustomEndpointKey,
122123
SfsCustomEndpointKey,
123124
TokenCustomEndpointKey,
125+
VpnCustomEndpointKey,
124126
}
125127

126128
var defaultConfigFolderPath string
@@ -212,6 +214,7 @@ func setConfigDefaults() {
212214
viper.SetDefault(AlbCustomEndpoint, "")
213215
viper.SetDefault(LogsCustomEndpointKey, "")
214216
viper.SetDefault(CDNCustomEndpointKey, "")
217+
viper.SetDefault(VpnCustomEndpointKey, "")
215218
}
216219

217220
func getConfigFilePath(configFolder string) string {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package client
2+
3+
import (
4+
vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1api"
5+
6+
"github.com/stackitcloud/stackit-cli/internal/pkg/config"
7+
genericclient "github.com/stackitcloud/stackit-cli/internal/pkg/generic-client"
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
9+
10+
"github.com/spf13/viper"
11+
)
12+
13+
func ConfigureClient(p *print.Printer, cliVersion string) (*vpn.APIClient, error) {
14+
return genericclient.ConfigureClientGeneric(p, cliVersion, viper.GetString(config.VpnCustomEndpointKey), false, genericclient.CreateApiClient[*vpn.APIClient](vpn.NewAPIClient))
15+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package utils
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1api"
8+
)
9+
10+
func GetGatewayName(ctx context.Context, client vpn.DefaultAPI, projectId, regionString, gatewayId string) (string, error) {
11+
region := vpn.Region(regionString)
12+
if !region.IsValid() {
13+
return "", fmt.Errorf("region %q not found", region)
14+
}
15+
resp, err := client.GetGateway(ctx, projectId, region, gatewayId).Execute()
16+
if err != nil {
17+
return "", fmt.Errorf("get gateway: %w", err)
18+
}
19+
if resp != nil {
20+
return resp.DisplayName, nil
21+
}
22+
return "", nil
23+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package utils
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/google/uuid"
9+
vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1api"
10+
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
12+
)
13+
14+
const (
15+
testGatewayName = "test-gateway-01"
16+
testRegion = "eu01"
17+
)
18+
19+
var (
20+
testProjectId = uuid.NewString()
21+
testGatewayId = uuid.NewString()
22+
)
23+
24+
type mockSettings struct {
25+
getGatewayFails bool
26+
getGatewayResp *vpn.GatewayResponse
27+
}
28+
29+
func newAPIMock(settings *mockSettings) vpn.DefaultAPI {
30+
return &vpn.DefaultAPIServiceMock{
31+
GetGatewayExecuteMock: utils.Ptr(func(_ vpn.ApiGetGatewayRequest) (*vpn.GatewayResponse, error) {
32+
if settings.getGatewayFails {
33+
return nil, fmt.Errorf("could not get gateway details")
34+
}
35+
36+
return settings.getGatewayResp, nil
37+
}),
38+
}
39+
}
40+
41+
func TestGetGatewayName(t *testing.T) {
42+
tests := []struct {
43+
description string
44+
getGatewayResp *vpn.GatewayResponse
45+
getGatewayFails bool
46+
isValid bool
47+
expectedOutput string
48+
}{
49+
{
50+
description: "base",
51+
getGatewayResp: &vpn.GatewayResponse{
52+
DisplayName: testGatewayName,
53+
},
54+
isValid: true,
55+
expectedOutput: testGatewayName,
56+
},
57+
{
58+
description: "get gateway fails",
59+
getGatewayFails: true,
60+
isValid: false,
61+
},
62+
}
63+
64+
for _, tt := range tests {
65+
t.Run(tt.description, func(t *testing.T) {
66+
client := newAPIMock(&mockSettings{
67+
getGatewayFails: tt.getGatewayFails,
68+
getGatewayResp: tt.getGatewayResp,
69+
})
70+
71+
output, err := GetGatewayName(context.Background(), client, testProjectId, testRegion, testGatewayId)
72+
73+
if tt.isValid && err != nil {
74+
t.Errorf("failed on valid input")
75+
}
76+
if !tt.isValid && err == nil {
77+
t.Errorf("did not fail on invalid input")
78+
}
79+
if !tt.isValid {
80+
return
81+
}
82+
if output != tt.expectedOutput {
83+
t.Errorf("expected output to be %s, got %s", tt.expectedOutput, output)
84+
}
85+
})
86+
}
87+
}

0 commit comments

Comments
 (0)