Skip to content

Commit c0cb4eb

Browse files
committed
feat: ske kubeconfig create merge kubeconfig into the default kubeconfig file
Signed-off-by: Javier Vela <fjvela@gmail.com>
1 parent a3a0754 commit c0cb4eb

File tree

5 files changed

+209
-41
lines changed

5 files changed

+209
-41
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: 12 additions & 10 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,29 +20,29 @@ 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
36-
Get a kubeconfig for the SKE cluster with name "my-cluster" without writing it to a file.
37-
38+
Get a kubeconfig for the SKE cluster with name "my-cluster" without writing it to a file and format the output as json
39+
$ stackit ske kubeconfig create my-cluster --disable-writing --output-format json
3840
```
3941

4042
### Options
4143

4244
```
43-
--disable-writing Disable writing to the kubeconfig file.
45+
--disable-writing Disable the writing of kubeconfig. Set the output format to json or yaml using the --output-format flag to display the kubeconfig.
4446
-e, --expiration string 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
4547
--filepath string Path to create the kubeconfig file. By default, the kubeconfig is created as 'config' in the .kube folder, in the user's home directory.
4648
-h, --help Help for "stackit ske kubeconfig create"

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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strconv"
1010

1111
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
12+
"k8s.io/client-go/tools/clientcmd"
1213

1314
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
1415
"github.com/stackitcloud/stackit-sdk-go/services/ske"
@@ -246,6 +247,45 @@ func ConvertToSeconds(timeStr string) (*string, error) {
246247
return utils.Ptr(strconv.FormatUint(result, 10)), nil
247248
}
248249

250+
// Merge new Kubeconfig into existing Kubeconfig. If it doesn´t exits, creates a new one
251+
func MergeKubeConfig(pathDestionationKubeConfig, contentNewKubeConfig string) error {
252+
if contentNewKubeConfig == "" {
253+
return fmt.Errorf("no data to merge. the new kubeconfig is empty")
254+
}
255+
256+
newConfig, err := clientcmd.Load([]byte(contentNewKubeConfig))
257+
if err != nil {
258+
return fmt.Errorf("error loading new kubeconfig: %w", err)
259+
}
260+
261+
// if the destionation kubeconfig does not exist, create a new one
262+
if _, err := os.Stat(pathDestionationKubeConfig); os.IsNotExist(err) {
263+
return WriteConfigFile(pathDestionationKubeConfig, contentNewKubeConfig)
264+
}
265+
266+
existingConfig, err := clientcmd.LoadFromFile(pathDestionationKubeConfig)
267+
if err != nil {
268+
return fmt.Errorf("error loading existing kubeconfig: %w", err)
269+
}
270+
271+
for name, authInfo := range newConfig.AuthInfos {
272+
existingConfig.AuthInfos[name] = authInfo
273+
}
274+
for name, context := range newConfig.Contexts {
275+
existingConfig.Contexts[name] = context
276+
}
277+
for name, cluster := range newConfig.Clusters {
278+
existingConfig.Clusters[name] = cluster
279+
}
280+
281+
err = clientcmd.WriteToFile(*existingConfig, pathDestionationKubeConfig)
282+
if err != nil {
283+
return fmt.Errorf("error writing merged kubeconfig: %w", err)
284+
}
285+
286+
return nil
287+
}
288+
249289
// WriteConfigFile writes the given data to the given path.
250290
// The directory is created if it does not exist.
251291
func WriteConfigFile(configPath, data string) error {

0 commit comments

Comments
 (0)