Skip to content

Commit bc6157f

Browse files
author
Kai Kummerer
committed
Extend kubeconfig create command with flag to retrieve login kubeconfig
1 parent 851c802 commit bc6157f

4 files changed

Lines changed: 82 additions & 25 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ require (
2323
github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.8.0
2424
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.7.0
2525
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.4.0
26-
github.com/stackitcloud/stackit-sdk-go/services/ske v0.14.0
26+
github.com/stackitcloud/stackit-sdk-go/services/ske v0.15.0
2727
github.com/zalando/go-keyring v0.2.4
2828
golang.org/x/mod v0.17.0
2929
golang.org/x/oauth2 v0.20.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.7.0 h1:1Ho+M4D
144144
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.7.0/go.mod h1:LX0Mcyr7/QP77zf7e05fHCJO38RMuTxr7nEDUDZ3oPQ=
145145
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.4.0 h1:JB1O0E9+L50ZaO36uz7azurvUuB5JdX5s2ZXuIdb9t8=
146146
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.4.0/go.mod h1:Ni9RBJvcaXRIrDIuQBpJcuQvCQSj27crQSyc+WM4p0c=
147-
github.com/stackitcloud/stackit-sdk-go/services/ske v0.14.0 h1:GH67aTvjXiXC2XmYhgmqNXfG13JHKB3wsk5JlTErsjg=
148-
github.com/stackitcloud/stackit-sdk-go/services/ske v0.14.0/go.mod h1:0fFs4R7kg+gU7FNAIzzFvlCZJz6gyZ8CFhbK3eSrAwQ=
147+
github.com/stackitcloud/stackit-sdk-go/services/ske v0.15.0 h1:7iTzdiglvJmKMaHlr4JUPvNOmA730rAniry74cnZ8zI=
148+
github.com/stackitcloud/stackit-sdk-go/services/ske v0.15.0/go.mod h1:0fFs4R7kg+gU7FNAIzzFvlCZJz6gyZ8CFhbK3eSrAwQ=
149149
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
150150
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
151151
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=

internal/cmd/ske/kubeconfig/create/create.go

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
const (
2222
clusterNameArg = "CLUSTER_NAME"
2323

24+
loginFlag = "login"
2425
expirationFlag = "expiration"
2526
filepathFlag = "filepath"
2627
)
@@ -30,6 +31,7 @@ type inputModel struct {
3031
ClusterName string
3132
Filepath *string
3233
ExpirationTime *string
34+
Login bool
3335
}
3436

3537
func NewCmd(p *print.Printer) *cobra.Command {
@@ -47,6 +49,10 @@ func NewCmd(p *print.Printer) *cobra.Command {
4749
examples.NewExample(
4850
`Create a kubeconfig for the SKE cluster with name "my-cluster"`,
4951
"$ stackit ske kubeconfig create my-cluster"),
52+
examples.NewExample(
53+
`Create a login kubeconfig for the SKE cluster with name "my-cluster".`,
54+
"This kubeconfig does not contain any credentials and instead obtains valid credentials via the STACKIT CLI",
55+
"$ stackit ske kubeconfig create my-cluster --login"),
5056
examples.NewExample(
5157
`Create a kubeconfig for the SKE cluster with name "my-cluster" and set the expiration time to 30 days`,
5258
"$ stackit ske kubeconfig create my-cluster --expiration 30d"),
@@ -79,20 +85,41 @@ func NewCmd(p *print.Printer) *cobra.Command {
7985
}
8086

8187
// Call API
82-
req, err := buildRequest(ctx, model, apiClient)
83-
if err != nil {
84-
return fmt.Errorf("build kubeconfig create request: %w", err)
85-
}
86-
resp, err := req.Execute()
87-
if err != nil {
88-
return fmt.Errorf("create kubeconfig for SKE cluster: %w", err)
88+
var (
89+
kubeconfig string
90+
respKubeconfig *ske.Kubeconfig
91+
respLogin *ske.V1LoginKubeconfig
92+
)
93+
94+
if !model.Login {
95+
req, err := buildRequestCreate(ctx, model, apiClient)
96+
if err != nil {
97+
return fmt.Errorf("build kubeconfig create request: %w", err)
98+
}
99+
respKubeconfig, err = req.Execute()
100+
if err != nil {
101+
return fmt.Errorf("create kubeconfig for SKE cluster: %w", err)
102+
}
103+
if respKubeconfig.Kubeconfig == nil {
104+
return fmt.Errorf("no kubeconfig returned from the API")
105+
}
106+
kubeconfig = *respKubeconfig.Kubeconfig
107+
} else {
108+
req, err := buildRequestLogin(ctx, model, apiClient)
109+
if err != nil {
110+
return fmt.Errorf("build login kubeconfig create request: %w", err)
111+
}
112+
respLogin, err = req.Execute()
113+
if err != nil {
114+
return fmt.Errorf("create login kubeconfig for SKE cluster: %w", err)
115+
}
116+
if respLogin.Kubeconfig == nil {
117+
return fmt.Errorf("no login kubeconfig returned from the API")
118+
}
119+
kubeconfig = *respLogin.Kubeconfig
89120
}
90121

91122
// Create the config file
92-
if resp.Kubeconfig == nil {
93-
return fmt.Errorf("no kubeconfig returned from the API")
94-
}
95-
96123
var kubeconfigPath string
97124
if model.Filepath == nil {
98125
kubeconfigPath, err = skeUtils.GetDefaultKubeconfigPath()
@@ -103,21 +130,26 @@ func NewCmd(p *print.Printer) *cobra.Command {
103130
kubeconfigPath = *model.Filepath
104131
}
105132

106-
err = skeUtils.WriteConfigFile(kubeconfigPath, *resp.Kubeconfig)
107-
if err != nil {
108-
return fmt.Errorf("write kubeconfig file: %w", err)
133+
if model.OutputFormat != print.JSONOutputFormat {
134+
err = skeUtils.WriteConfigFile(kubeconfigPath, kubeconfig)
135+
if err != nil {
136+
return fmt.Errorf("write kubeconfig file: %w", err)
137+
}
109138
}
110139

111-
return outputResult(p, model, kubeconfigPath, resp)
140+
return outputResult(p, model, kubeconfigPath, respKubeconfig, respLogin)
112141
},
113142
}
114143
configureFlags(cmd)
115144
return cmd
116145
}
117146

118147
func configureFlags(cmd *cobra.Command) {
148+
cmd.Flags().BoolP(loginFlag, "l", false, "Create a login kubeconfig that obtains valid credentials via the STACKIT CLI. This flag is mutually exclusive with the `expiration` flag.")
119149
cmd.Flags().StringP(expirationFlag, "e", "", "Expiration time for the kubeconfig in seconds(s), minutes(m), hours(h), days(d) or months(M). Example: 30d. By default, expiration time is 1h")
120150
cmd.Flags().String(filepathFlag, "", "Path to create the kubeconfig file. By default, the kubeconfig is created as 'config' in the .kube folder, in the user's home directory.")
151+
152+
cmd.MarkFlagsMutuallyExclusive(loginFlag, expirationFlag)
121153
}
122154

123155
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
@@ -146,6 +178,7 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu
146178
ClusterName: clusterName,
147179
Filepath: flags.FlagToStringPointer(p, cmd, filepathFlag),
148180
ExpirationTime: expTime,
181+
Login: flags.FlagToBoolValue(p, cmd, loginFlag),
149182
}
150183

151184
if p.IsVerbosityDebug() {
@@ -160,7 +193,7 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu
160193
return &model, nil
161194
}
162195

163-
func buildRequest(ctx context.Context, model *inputModel, apiClient *ske.APIClient) (ske.ApiCreateKubeconfigRequest, error) {
196+
func buildRequestCreate(ctx context.Context, model *inputModel, apiClient *ske.APIClient) (ske.ApiCreateKubeconfigRequest, error) {
164197
req := apiClient.CreateKubeconfig(ctx, model.ProjectId, model.ClusterName)
165198

166199
payload := ske.CreateKubeconfigPayload{}
@@ -172,18 +205,32 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *ske.APIClie
172205
return req.CreateKubeconfigPayload(payload), nil
173206
}
174207

175-
func outputResult(p *print.Printer, model *inputModel, kubeconfigPath string, resp *ske.Kubeconfig) error {
208+
func buildRequestLogin(ctx context.Context, model *inputModel, apiClient *ske.APIClient) (ske.ApiGetLoginKubeconfigRequest, error) {
209+
return apiClient.GetLoginKubeconfig(ctx, model.ProjectId, model.ClusterName), nil
210+
}
211+
212+
func outputResult(p *print.Printer, model *inputModel, kubeconfigPath string, respKubeconfig *ske.Kubeconfig, respLogin *ske.V1LoginKubeconfig) error {
176213
switch model.OutputFormat {
177214
case print.JSONOutputFormat:
178-
details, err := json.MarshalIndent(resp, "", " ")
215+
var err error
216+
var details []byte
217+
if respKubeconfig != nil {
218+
details, err = json.MarshalIndent(respKubeconfig, "", " ")
219+
} else if respLogin != nil {
220+
details, err = json.MarshalIndent(respLogin, "", " ")
221+
}
179222
if err != nil {
180223
return fmt.Errorf("marshal SKE Kubeconfig: %w", err)
181224
}
182225
p.Outputln(string(details))
183226

184227
return nil
185228
default:
186-
p.Outputf("Created kubeconfig file for cluster %s in %q, with expiration date %v (UTC)\n", model.ClusterName, kubeconfigPath, *resp.ExpirationTimestamp)
229+
var expiration string
230+
if respKubeconfig != nil {
231+
expiration = fmt.Sprintf(", with expiration date %v (UTC)", *respKubeconfig.ExpirationTimestamp)
232+
}
233+
p.Outputf("Created kubeconfig file for cluster %s in %q%s\n", model.ClusterName, kubeconfigPath, expiration)
187234

188235
return nil
189236
}

internal/cmd/ske/kubeconfig/create/create_test.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,17 @@ func TestParseInput(t *testing.T) {
9292
model.ExpirationTime = utils.Ptr("2592000")
9393
}),
9494
},
95-
95+
{
96+
description: "login",
97+
argValues: fixtureArgValues(),
98+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
99+
flagValues["login"] = "true"
100+
}),
101+
isValid: true,
102+
expectedModel: fixtureInputModel(func(model *inputModel) {
103+
model.Login = true
104+
}),
105+
},
96106
{
97107
description: "custom filepath",
98108
argValues: fixtureArgValues(),
@@ -202,7 +212,7 @@ func TestParseInput(t *testing.T) {
202212
}
203213
}
204214

205-
func TestBuildRequest(t *testing.T) {
215+
func TestBuildRequestCreate(t *testing.T) {
206216
tests := []struct {
207217
description string
208218
model *inputModel
@@ -225,7 +235,7 @@ func TestBuildRequest(t *testing.T) {
225235

226236
for _, tt := range tests {
227237
t.Run(tt.description, func(t *testing.T) {
228-
request, _ := buildRequest(testCtx, tt.model, testClient)
238+
request, _ := buildRequestCreate(testCtx, tt.model, testClient)
229239

230240
diff := cmp.Diff(request, tt.expectedRequest,
231241
cmp.AllowUnexported(tt.expectedRequest),

0 commit comments

Comments
 (0)