Skip to content

Commit

Permalink
feat: generate context name by specific attributes (#956)
Browse files Browse the repository at this point in the history
* feat: generate context name by specific attributes

Signed-off-by: chengzw <[email protected]>

* run make doc-gen to generate docs

* fix e2e test

Signed-off-by: chengzw <[email protected]>

* add context-name parameter

* add unit test

Signed-off-by: chengzw <[email protected]>

* update chinese docs

---------

Signed-off-by: chengzw <[email protected]>
  • Loading branch information
cr7258 committed May 23, 2024
1 parent da53bb2 commit 915c93b
Show file tree
Hide file tree
Showing 36 changed files with 403 additions and 125 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/e2e-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
echo "Running kubecm add..."
echo "********************************************************************************"
bin/kubecm add -cf 2nd-kind
bin/kubecm add -cf 3rd-kind --context-name 3rd
bin/kubecm add -cf 3rd-kind --context-prefix 3rd
echo "********************************************************************************"
echo "Running kubecm merge multiple kubeconfig..."
echo "********************************************************************************"
Expand All @@ -85,7 +85,7 @@ jobs:
echo "********************************************************************************"
echo "Running kubecm switch..."
echo "********************************************************************************"
bin/kubecm s 3rd
bin/kubecm s 3rd-kind-3rd-kind
echo "********************************************************************************"
echo "Running kubecm delete..."
echo "********************************************************************************"
Expand Down
94 changes: 66 additions & 28 deletions cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"reflect"
"strconv"
"strings"

"github.com/spf13/cobra"
"k8s.io/client-go/tools/clientcmd"
Expand Down Expand Up @@ -36,22 +37,34 @@ func (ac *AddCommand) Init() {
},
Example: addExample(),
}
ac.command.Flags().StringP("file", "f", "", "Path to merge kubeconfig files")
ac.command.Flags().String("context-name", "", "override context name when add kubeconfig context")
ac.command.PersistentFlags().BoolP("cover", "c", false, "Overwrite local kubeconfig files")
ac.command.PersistentFlags().Bool("select-context", false, "select the context to be added")
ac.command.Flags().StringP("file", "f", "", "path to merge kubeconfig files")
ac.command.Flags().String("context-prefix", "", "add a prefix before context name")
ac.command.Flags().String("context-name", "", "override context name when add kubeconfig context, when context-name is set, context-prefix and context-template parameters will be ignored")
ac.command.PersistentFlags().BoolP("cover", "c", false, "overwrite local kubeconfig files")
ac.command.Flags().Bool("select-context", false, "select the context to be added")
ac.command.Flags().StringSlice("context-template", []string{"context"}, "define the attributes used for composing the context name, available values: filename, user, cluster, context, namespace")
_ = ac.command.MarkFlagRequired("file")
ac.AddCommands(&DocsCommand{})
}

func (ac *AddCommand) runAdd(cmd *cobra.Command, args []string) error {
file, _ := ac.command.Flags().GetString("file")
cover, _ := ac.command.Flags().GetBool("cover")
contextPrefix, _ := ac.command.Flags().GetString("context-prefix")
contextName, _ := ac.command.Flags().GetString("context-name")
selectContext, _ := ac.command.Flags().GetBool("select-context")
contextTemplate, _ := ac.command.Flags().GetStringSlice("context-template")

var newConfig *clientcmdapi.Config
var err error

if contextName != "" {
contextTemplate = []string{}
contextPrefix = contextName
}
err := validateContextTemplate(contextTemplate)
if err != nil {
return err
}

if file == "-" {
// from stdin
Expand All @@ -75,15 +88,15 @@ func (ac *AddCommand) runAdd(cmd *cobra.Command, args []string) error {
}
}

err = AddToLocal(newConfig, file, contextName, cover, selectContext)
err = AddToLocal(newConfig, file, contextPrefix, cover, selectContext, contextTemplate)
if err != nil {
return err
}
return nil
}

// AddToLocal add kubeConfig to local
func AddToLocal(newConfig *clientcmdapi.Config, path, newName string, cover bool, selectContext bool) error {
func AddToLocal(newConfig *clientcmdapi.Config, path, contextPrefix string, cover bool, selectContext bool, contextTemplate []string) error {
oldConfig, err := clientcmd.LoadFromFile(cfgFile)
if err != nil {
return err
Expand All @@ -93,7 +106,7 @@ func AddToLocal(newConfig *clientcmdapi.Config, path, newName string, cover bool
fileName: getFileName(path),
}
// merge context loop
outConfig, err := kco.handleContexts(oldConfig, newName, selectContext)
outConfig, err := kco.handleContexts(oldConfig, contextPrefix, selectContext, contextTemplate)
if err != nil {
return err
}
Expand Down Expand Up @@ -121,21 +134,22 @@ func AddToLocal(newConfig *clientcmdapi.Config, path, newName string, cover bool
return nil
}

func (kc *KubeConfigOption) handleContexts(oldConfig *clientcmdapi.Config, contextName string, selectContext bool) (*clientcmdapi.Config, error) {
func (kc *KubeConfigOption) handleContexts(oldConfig *clientcmdapi.Config, contextPrefix string, selectContext bool, contextTemplate []string) (*clientcmdapi.Config, error) {
newConfig := clientcmdapi.NewConfig()
var index int
var newName string
generatedName := make(map[string]int)

for name, ctx := range kc.config.Contexts {
if len(kc.config.Contexts) > 1 {
if contextName == "" {
newName = fmt.Sprintf("%s-%s", kc.fileName, HashSufString(name))
} else {
newName = fmt.Sprintf("%s-%d", contextName, index)
}
} else if contextName == "" {
newName = name
} else {
newName = contextName
newName = kc.generateContextName(name, ctx, contextTemplate)
if contextPrefix != "" {
newName = strings.TrimSuffix(fmt.Sprintf("%s-%s", contextPrefix, newName), "-")
}

// prevent generate duplicate context name
// for example: set --context-template user,cluster, the context1 and context2 have the same user and cluster
generatedName[newName]++
if generatedName[newName] > 1 {
newName = fmt.Sprintf("%s-%d", newName, generatedName[newName])
}

if selectContext {
Expand All @@ -159,12 +173,32 @@ func (kc *KubeConfigOption) handleContexts(oldConfig *clientcmdapi.Config, conte
itemConfig := kc.handleContext(oldConfig, newName, ctx)
newConfig = appendConfig(newConfig, itemConfig)
fmt.Printf("Add Context: %s \n", newName)
index++
}
outConfig := appendConfig(oldConfig, newConfig)
return outConfig, nil
}

func (kc *KubeConfigOption) generateContextName(name string, ctx *clientcmdapi.Context, contextTemplate []string) string {
valueMap := map[string]string{
Filename: kc.fileName,
Context: name,
User: ctx.AuthInfo,
Cluster: ctx.Cluster,
Namespace: ctx.Namespace,
}

var contextValues []string
for _, value := range contextTemplate {
if v, ok := valueMap[value]; ok {
if v != "" {
contextValues = append(contextValues, v)
}
}
}

return strings.Join(contextValues, "-")
}

func checkContextName(name string, oldConfig *clientcmdapi.Config) bool {
if _, ok := oldConfig.Contexts[name]; ok {
return true
Expand Down Expand Up @@ -223,15 +257,19 @@ func (kc *KubeConfigOption) handleContext(oldConfig *clientcmdapi.Config,

func addExample() string {
return `
Note: If -c is set and more than one context is added to the kubeconfig file, the following will occur:
- If --context-name is set, the context will be generated as <context-name-0>, <context-name-1> ...
- If --context-name is not set, it will be generated as <file-name-{hash}> where {hash} is the MD5 hash of the file name.
# Merge test.yaml with $HOME/.kube/config
kubecm add -f test.yaml
# Merge test.yaml with $HOME/.kube/config and rename context name
kubecm add -cf test.yaml --context-name test
# Merge test.yaml with $HOME/.kube/config and add a prefix before context name
kubecm add -cf test.yaml --context-prefix test
# Merge test.yaml with $HOME/.kube/config and define the attributes used for composing the context name
kubecm add -f test.yaml --context-template user,cluster
# Merge test.yaml with $HOME/.kube/config, define the attributes used for composing the context name and add a prefix before context name
kubecm add -f test.yaml --context-template user,cluster --context-prefix demo
# Merge test.yaml with $HOME/.kube/config and override context name, it's useful if there is only one context in the kubeconfig file
kubecm add -f test.yaml --context-name test
# Merge test.yaml with $HOME/.kube/config and select the context to be added in interactive mode
kubecm add -f test.yaml --select-context
# Add kubeconfig from stdin
cat /etc/kubernetes/admin.conf | kubecm add -f -
cat /etc/kubernetes/admin.conf | kubecm add -f -
`
}
Loading

0 comments on commit 915c93b

Please sign in to comment.