Skip to content

Commit af89fbf

Browse files
committed
feat: ske kubeconfig create merge kubeconfig into the default kubeconfig file
Signed-off-by: Javier Vela <[email protected]>
1 parent 674d387 commit af89fbf

File tree

5 files changed

+207
-38
lines changed

5 files changed

+207
-38
lines changed

docs/stackit_ske_kubeconfig.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ stackit ske kubeconfig [flags]
3030
### SEE ALSO
3131

3232
* [stackit ske](./stackit_ske.md) - Provides functionality for SKE
33-
* [stackit ske kubeconfig create](./stackit_ske_kubeconfig_create.md) - Creates a kubeconfig for an SKE cluster
33+
* [stackit ske kubeconfig create](./stackit_ske_kubeconfig_create.md) - Creates or update a kubeconfig for an SKE cluster
3434
* [stackit ske kubeconfig login](./stackit_ske_kubeconfig_login.md) - Login plugin for kubernetes clients
3535

docs/stackit_ske_kubeconfig_create.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
## stackit ske kubeconfig create
22

3-
Creates a kubeconfig for an SKE cluster
3+
Creates or update a kubeconfig for an SKE cluster
44

55
### Synopsis
66

7-
Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster.
7+
Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster, if the config exits in the kubeconfig file the information will be updated.
88

9-
By default the kubeconfig is created in the .kube folder, in the user's home directory. The kubeconfig file will be overwritten if it already exists.
9+
By default, the kubeconfig information of the SKE cluster is merged into the default kubeconfig file of the current user. If the kubeconfig file doesn't exist, a new one will be created.
1010
You can override this behavior by specifying a custom filepath with the --filepath flag.
11+
1112
An expiration time can be set for the kubeconfig. The expiration time is set in seconds(s), minutes(m), hours(h), days(d) or months(M). Default is 1h.
13+
1214
Note that the format is <value><unit>, e.g. 30d for 30 days and you can't combine units.
1315

1416
```
@@ -18,19 +20,19 @@ stackit ske kubeconfig create CLUSTER_NAME [flags]
1820
### Examples
1921

2022
```
21-
Create a kubeconfig for the SKE cluster with name "my-cluster"
23+
Create a kubeconfig for the SKE cluster with name "my-cluster. If the config exits in the kubeconfig file the information will be updated."
2224
$ stackit ske kubeconfig create my-cluster
2325
2426
Get a login kubeconfig for the SKE cluster with name "my-cluster". This kubeconfig does not contain any credentials and instead obtains valid credentials via the `stackit ske kubeconfig login` command.
2527
$ stackit ske kubeconfig create my-cluster --login
2628
27-
Create a kubeconfig for the SKE cluster with name "my-cluster" and set the expiration time to 30 days
29+
Create o kubeconfig for the SKE cluster with name "my-cluster" and set the expiration time to 30 days. If the config exits in the kubeconfig file the information will be updated.
2830
$ stackit ske kubeconfig create my-cluster --expiration 30d
2931
30-
Create a kubeconfig for the SKE cluster with name "my-cluster" and set the expiration time to 2 months
32+
Create or update a kubeconfig for the SKE cluster with name "my-cluster" and set the expiration time to 2 months. If the config exits in the kubeconfig file the information will be updated.
3133
$ stackit ske kubeconfig create my-cluster --expiration 2M
3234
33-
Create a kubeconfig for the SKE cluster with name "my-cluster" in a custom filepath
35+
Create or update a kubeconfig for the SKE cluster with name "my-cluster" in a custom filepath. If the config exits in the kubeconfig file the information will be updated.
3436
$ stackit ske kubeconfig create my-cluster --filepath /path/to/config
3537
3638
Get a kubeconfig for the SKE cluster with name "my-cluster" without writing it to a file and format the output as json

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

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,30 +40,30 @@ type inputModel struct {
4040
func NewCmd(p *print.Printer) *cobra.Command {
4141
cmd := &cobra.Command{
4242
Use: fmt.Sprintf("create %s", clusterNameArg),
43-
Short: "Creates a kubeconfig for an SKE cluster",
43+
Short: "Creates or update a kubeconfig for an SKE cluster",
4444
Long: fmt.Sprintf("%s\n\n%s\n%s\n%s\n%s",
45-
"Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster.",
46-
"By default the kubeconfig is created in the .kube folder, in the user's home directory. The kubeconfig file will be overwritten if it already exists.",
47-
"You can override this behavior by specifying a custom filepath with the --filepath flag.",
48-
"An expiration time can be set for the kubeconfig. The expiration time is set in seconds(s), minutes(m), hours(h), days(d) or months(M). Default is 1h.",
45+
"Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster, if the config exits in the kubeconfig file the information will be updated.",
46+
"By default, the kubeconfig information of the SKE cluster is merged into the default kubeconfig file of the current user. If the kubeconfig file doesn't exist, a new one will be created.",
47+
"You can override this behavior by specifying a custom filepath with the --filepath flag.\n",
48+
"An expiration time can be set for the kubeconfig. The expiration time is set in seconds(s), minutes(m), hours(h), days(d) or months(M). Default is 1h.\n",
4949
"Note that the format is <value><unit>, e.g. 30d for 30 days and you can't combine units."),
5050
Args: args.SingleArg(clusterNameArg, nil),
5151
Example: examples.Build(
5252
examples.NewExample(
53-
`Create a kubeconfig for the SKE cluster with name "my-cluster"`,
53+
`Create a kubeconfig for the SKE cluster with name "my-cluster. If the config exits in the kubeconfig file the information will be updated."`,
5454
"$ stackit ske kubeconfig create my-cluster"),
5555
examples.NewExample(
5656
`Get a login kubeconfig for the SKE cluster with name "my-cluster". `+
5757
"This kubeconfig does not contain any credentials and instead obtains valid credentials via the `stackit ske kubeconfig login` command.",
5858
"$ stackit ske kubeconfig create my-cluster --login"),
5959
examples.NewExample(
60-
`Create a kubeconfig for the SKE cluster with name "my-cluster" and set the expiration time to 30 days`,
60+
`Create o kubeconfig for the SKE cluster with name "my-cluster" and set the expiration time to 30 days. If the config exits in the kubeconfig file the information will be updated.`,
6161
"$ stackit ske kubeconfig create my-cluster --expiration 30d"),
6262
examples.NewExample(
63-
`Create a kubeconfig for the SKE cluster with name "my-cluster" and set the expiration time to 2 months`,
63+
`Create or update a kubeconfig for the SKE cluster with name "my-cluster" and set the expiration time to 2 months. If the config exits in the kubeconfig file the information will be updated.`,
6464
"$ stackit ske kubeconfig create my-cluster --expiration 2M"),
6565
examples.NewExample(
66-
`Create a kubeconfig for the SKE cluster with name "my-cluster" in a custom filepath`,
66+
`Create or update a kubeconfig for the SKE cluster with name "my-cluster" in a custom filepath. If the config exits in the kubeconfig file the information will be updated.`,
6767
"$ stackit ske kubeconfig create my-cluster --filepath /path/to/config"),
6868
examples.NewExample(
6969
`Get a kubeconfig for the SKE cluster with name "my-cluster" without writing it to a file and format the output as json`,
@@ -83,7 +83,7 @@ func NewCmd(p *print.Printer) *cobra.Command {
8383
}
8484

8585
if !model.AssumeYes && !model.DisableWriting {
86-
prompt := fmt.Sprintf("Are you sure you want to create a kubeconfig for SKE cluster %q? This will OVERWRITE your current kubeconfig file, if it exists.", model.ClusterName)
86+
prompt := fmt.Sprintf("Are you sure you want to update your kubeconfig for SKE cluster %q? This will update your kubeconfig file. \n If it the kubeconfig file doesn´t exists, it will create a new one.", model.ClusterName)
8787
err = p.PromptForConfirmation(prompt)
8888
if err != nil {
8989
return err
@@ -137,10 +137,11 @@ func NewCmd(p *print.Printer) *cobra.Command {
137137
}
138138

139139
if !model.DisableWriting {
140-
err = skeUtils.WriteConfigFile(kubeconfigPath, kubeconfig)
140+
err = skeUtils.MergeKubeConfig(kubeconfigPath, kubeconfig)
141141
if err != nil {
142142
return fmt.Errorf("write kubeconfig file: %w", err)
143143
}
144+
p.Outputf("\nSet kubectl context to %s with: kubectl config use-context %s\n", model.ClusterName, model.ClusterName)
144145
}
145146

146147
return outputResult(p, model, kubeconfigPath, respKubeconfig, respLogin)
@@ -260,7 +261,7 @@ func outputResult(p *print.Printer, model *inputModel, kubeconfigPath string, re
260261
if respKubeconfig != nil {
261262
expiration = fmt.Sprintf(", with expiration date %v (UTC)", *respKubeconfig.ExpirationTimestamp)
262263
}
263-
p.Outputf("Created kubeconfig file for cluster %s in %q%s\n", model.ClusterName, kubeconfigPath, expiration)
264+
p.Outputf("Updated kubeconfig file for cluster %s in %q%s\n", model.ClusterName, kubeconfigPath, expiration)
264265

265266
return nil
266267
}

internal/pkg/services/ske/utils/utils.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"strconv"
99

1010
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
11+
"k8s.io/client-go/tools/clientcmd"
12+
1113
"github.com/stackitcloud/stackit-sdk-go/services/ske"
1214
"golang.org/x/mod/semver"
1315
)
@@ -228,6 +230,45 @@ func ConvertToSeconds(timeStr string) (*string, error) {
228230
return utils.Ptr(strconv.FormatUint(result, 10)), nil
229231
}
230232

233+
// Merge new Kubeconfig into existing Kubeconfig. If it doesn´t exits, creates a new one
234+
func MergeKubeConfig(pathDestionationKubeConfig, contentNewKubeConfig string) error {
235+
if contentNewKubeConfig == "" {
236+
return fmt.Errorf("no data to merge. the new kubeconfig is empty")
237+
}
238+
239+
newConfig, err := clientcmd.Load([]byte(contentNewKubeConfig))
240+
if err != nil {
241+
return fmt.Errorf("error loading new kubeconfig: %w", err)
242+
}
243+
244+
// if the destionation kubeconfig does not exist, create a new one
245+
if _, err := os.Stat(pathDestionationKubeConfig); os.IsNotExist(err) {
246+
return WriteConfigFile(pathDestionationKubeConfig, contentNewKubeConfig)
247+
}
248+
249+
existingConfig, err := clientcmd.LoadFromFile(pathDestionationKubeConfig)
250+
if err != nil {
251+
return fmt.Errorf("error loading existing kubeconfig: %w", err)
252+
}
253+
254+
for name, authInfo := range newConfig.AuthInfos {
255+
existingConfig.AuthInfos[name] = authInfo
256+
}
257+
for name, context := range newConfig.Contexts {
258+
existingConfig.Contexts[name] = context
259+
}
260+
for name, cluster := range newConfig.Clusters {
261+
existingConfig.Clusters[name] = cluster
262+
}
263+
264+
err = clientcmd.WriteToFile(*existingConfig, pathDestionationKubeConfig)
265+
if err != nil {
266+
return fmt.Errorf("error writing merged kubeconfig: %w", err)
267+
}
268+
269+
return nil
270+
}
271+
231272
// WriteConfigFile writes the given data to the given path.
232273
// The directory is created if it does not exist.
233274
func WriteConfigFile(configPath, data string) error {

0 commit comments

Comments
 (0)