Skip to content

Commit

Permalink
add create cmd (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
sunny0826 authored Feb 4, 2021
1 parent f1bfc73 commit 30aeb06
Show file tree
Hide file tree
Showing 10 changed files with 361 additions and 22 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ Usage:
Available Commands:
add Merge configuration file with $HOME/.kube/config
alias Generate alias for all contexts
clear Clear lapsed context, cluster and user
completion Generates bash/zsh completion scripts
create Create new KubeConfig(experiment)
delete Delete the specified context from the kubeconfig
help Help about any command
ls List kubeconfig
Expand Down
1 change: 1 addition & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func NewBaseCommand() *BaseCommand {
&ListCommand{}, // list command
&AliasCommand{}, // alias command
&ClearCommand{}, // clear command
&CreateCommand{}, // create command
)

return baseCmd
Expand Down
255 changes: 255 additions & 0 deletions cmd/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
package cmd

import (
"context"
"encoding/base64"
"fmt"
"os"

"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
coreV1 "k8s.io/api/core/v1"
rbacV1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

// ClearCommand clean command struct
type CreateCommand struct {
BaseCommand
}

type CreateOptions struct {
config *clientcmdapi.Config
clientSet *kubernetes.Clientset
role string
token string
contextName string
userName string
namespace string
}

// Init AliasCommand
func (ce *CreateCommand) Init() {
ce.command = &cobra.Command{
Use: "create",
Short: "Create new KubeConfig(experiment)",
Long: "Create new KubeConfig(experiment)",
RunE: func(cmd *cobra.Command, args []string) error {
return ce.runCreate(cmd, args)
},
Example: createExample(),
}
ce.command.DisableFlagsInUseLine = true
}

func (ce *CreateCommand) runCreate(cmd *cobra.Command, args []string) error {
config, err := clientcmd.LoadFromFile(cfgFile)
if err != nil {
return err
}
userName := PromptUI("user name", "")
co := CreateOptions{
config: config,
userName: userName,
}
err = co.chooseContext()
if err != nil {
return err
}
err = co.chooseNamespace()
if err != nil {
return err
}
err = co.createServiceAccounts()
if err != nil {
return err
}
err = co.selectClusterRole()
if err != nil {
return err
}
err = co.createRoleBinding()
if err != nil {
return err
}
err = co.getToken()
if err != nil {
return err
}
newConfig := co.putOutKubeConfig()
fileName := fmt.Sprintf("%s.kubeconfig", co.userName)
err = clientcmd.WriteToFile(*newConfig, fileName)
if err != nil {
return err
}
printString(os.Stdout, "kubeconfig: "+fileName+" create success\n")
return nil
}

func (co *CreateOptions) chooseContext() error {
var kubeItems []Needle
current := co.config.CurrentContext
for key, obj := range co.config.Contexts {
if key != current {
kubeItems = append(kubeItems, Needle{Name: key, Cluster: obj.Cluster, User: obj.AuthInfo})
} else {
kubeItems = append([]Needle{{Name: key, Cluster: obj.Cluster, User: obj.AuthInfo, Center: "(*)"}}, kubeItems...)
}
}
num := SelectUI(kubeItems, "Select Kube Context")
co.contextName = kubeItems[num].Name
co.config.CurrentContext = co.contextName
clientConfig := clientcmd.NewDefaultClientConfig(
*co.config,
&clientcmd.ConfigOverrides{},
)
c, _ := clientConfig.ClientConfig()
clientSet, err := kubernetes.NewForConfig(c)
if err != nil {
return err
}
co.clientSet = clientSet
return nil
}

func (co *CreateOptions) chooseNamespace() error {
var nss []Namespaces
ctx := context.TODO()
namespaceList, err := co.clientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
if err != nil {
return err
}
for _, specItem := range namespaceList.Items {
nss = append(nss, Namespaces{Name: specItem.Name, Default: false})
}
num := selectNamespace(nss)
co.namespace = nss[num].Name
return nil
}

func (co *CreateOptions) createServiceAccounts() error {
saName := co.userName
// TODO 判断 sa 是否存在
userServiceAccount, err := co.clientSet.CoreV1().ServiceAccounts(co.namespace).Get(context.TODO(), saName, metav1.GetOptions{})
if err != nil {
saObj := &coreV1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: saName,
},
}
userServiceAccount, err = co.clientSet.CoreV1().ServiceAccounts(co.namespace).Create(context.TODO(), saObj, metav1.CreateOptions{})
if err != nil {
return err
}
printString(os.Stdout, "ServiceAccount")
fmt.Printf(" : %s create success\n", userServiceAccount.Name)
} else {
printYellow(os.Stdout, "ServiceAccount")
fmt.Printf(" : %s already exists\n", userServiceAccount.Name)
}
return nil
}

func (co *CreateOptions) selectClusterRole() error {
clusterRoleList := []string{
"view", "edit", "admin", "cluster-admin", "custom",
}
templates := &promptui.SelectTemplates{
Label: "{{ . }}",
Active: "\U0001F63C {{ . | red }}",
Inactive: " {{ . | cyan }}",
Selected: "\U0001F638 Select:{{ . | green }}",
}
prompt := promptui.Select{
Label: "please select the cluster role of the user:",
Items: clusterRoleList,
Templates: templates,
Size: 4,
}
i, _, err := prompt.Run()
if err != nil {
return err
}
if clusterRoleList[i] == "custom" {
customClusterRole := PromptUI("custom cluster role", "")
fmt.Println(customClusterRole)
_, err := co.clientSet.RbacV1().ClusterRoles().Get(context.TODO(), customClusterRole, metav1.GetOptions{})
if err != nil {
return err
}
co.role = customClusterRole
} else {
co.role = clusterRoleList[i]
}
return nil
}

func (co *CreateOptions) createRoleBinding() error {
co.role = "view"
rb := &rbacV1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", co.userName, co.role),
Namespace: co.namespace,
},
Subjects: []rbacV1.Subject{
{
Kind: "ServiceAccount",
Name: co.userName,
Namespace: co.namespace,
},
},
RoleRef: rbacV1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: co.role,
},
}
newRoleBinding, err := co.clientSet.RbacV1().RoleBindings(co.namespace).Create(context.TODO(), rb, metav1.CreateOptions{})
if err != nil {
return err
}
printString(os.Stdout, "RoleBinding")
fmt.Printf(" : %s create success\n", newRoleBinding.Name)
return nil
}

func (co *CreateOptions) getToken() error {
sa, err := co.clientSet.CoreV1().ServiceAccounts(co.namespace).Get(context.TODO(), co.userName, metav1.GetOptions{})
if err != nil {
return err
}
secretName := sa.Secrets[0].Name
secretToken, _ := co.clientSet.CoreV1().Secrets(co.namespace).Get(context.TODO(), secretName, metav1.GetOptions{})
sEnc := base64.StdEncoding.EncodeToString(secretToken.Data["token"])
sDec, err := base64.StdEncoding.DecodeString(sEnc)
if err != nil {
return err
}
co.token = string(sDec)
return nil
}

func (co *CreateOptions) putOutKubeConfig() *clientcmdapi.Config {
coContext := co.config.Contexts[co.contextName]
coCluster := co.config.Clusters[coContext.Cluster]
coAuthInfo := clientcmdapi.NewAuthInfo()
coAuthInfo.Token = co.token
coContext.AuthInfo = co.userName

newConfig := clientcmdapi.NewConfig()
newConfig.Clusters[coContext.Cluster] = coCluster
newConfig.AuthInfos[coContext.AuthInfo] = coAuthInfo
newConfig.Contexts[co.userName] = coContext
newConfig.CurrentContext = co.userName
return newConfig
}

func createExample() string {
return `
# Create new KubeConfig(experiment)
kubecm create
`
}
1 change: 1 addition & 0 deletions docs/en-us/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Available Commands:
alias Generate alias for all contexts
clear Clear lapsed context, cluster and user
completion Generates bash/zsh completion scripts
create Create new KubeConfig(experiment)
delete Delete the specified context from the kubeconfig
help Help about any command
ls List kubeconfig
Expand Down
23 changes: 12 additions & 11 deletions docs/en-us/_sidebar.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
* [Install](/en-us/install.md)
* [Interactive operation](/en-us/interactive.md)
* CLI References
* [kubecm add](/en-us/cli/kubecm_add.md)
* [kubecm alias](/en-us/cli/kubecm_alias.md)
* [kubecm completion](/en-us/cli/kubecm_completion.md)
* [kubecm delete](/en-us/cli/kubecm_delete.md)
* [kubecm ls](/en-us/cli/kubecm_ls.md)
* [kubecm merge](/en-us/cli/kubecm_merge.md)
* [kubecm namespace](/en-us/cli/kubecm_namespace.md)
* [kubecm rename](/en-us/cli/kubecm_rename.md)
* [kubecm switch](/en-us/cli/kubecm_switch.md)
* [kubecm clear](/en-us/cli/kubecm_clear.md)
* [kubecm version](/en-us/cli/kubecm_version.md)
* [add](/en-us/cli/kubecm_add.md)
* [create(experiment)](/en-us/cli/kubecm_create.md)
* [alias](/en-us/cli/kubecm_alias.md)
* [completion](/en-us/cli/kubecm_completion.md)
* [delete](/en-us/cli/kubecm_delete.md)
* [ls](/en-us/cli/kubecm_ls.md)
* [merge](/en-us/cli/kubecm_merge.md)
* [namespace](/en-us/cli/kubecm_namespace.md)
* [rename](/en-us/cli/kubecm_rename.md)
* [switch](/en-us/cli/kubecm_switch.md)
* [clear](/en-us/cli/kubecm_clear.md)
* [version](/en-us/cli/kubecm_version.md)
* [Contribute](/en-us/contribute.md)
38 changes: 38 additions & 0 deletions docs/en-us/cli/kubecm_create.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## kubecm create

Create new KubeConfig(experiment)

### Synopsis

Create new KubeConfig(experiment)

```
kubecm create
```

### Examples

```
# Create new KubeConfig(experiment)
kubecm create
```

### Options

```
-h, --help help for create
```

### Options inherited from parent commands

```
--config string path of kubeconfig (default "/Users/saybot/.kube/config")
```

### SEE ALSO

* [kubecm](../../../tmp/cli/kubecm.md) - KubeConfig Manager.

###### Auto generated by spf13/cobra on 4-Feb-2021
1 change: 1 addition & 0 deletions docs/zh-cn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Available Commands:
alias Generate alias for all contexts
clear Clear lapsed context, cluster and user
completion Generates bash/zsh completion scripts
create Create new KubeConfig(experiment)
delete Delete the specified context from the kubeconfig
help Help about any command
ls List kubeconfig
Expand Down
23 changes: 12 additions & 11 deletions docs/zh-cn/_sidebar.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
* [安装](/zh-cn/install.md)
* [交互式操作](/zh-cn/interactive.md)
* CLI 参考
* [kubecm add](/zh-cn/cli/kubecm_add.md)
* [kubecm alias](/zh-cn/cli/kubecm_alias.md)
* [kubecm completion](/zh-cn/cli/kubecm_completion.md)
* [kubecm delete](/zh-cn/cli/kubecm_delete.md)
* [kubecm ls](/zh-cn/cli/kubecm_ls.md)
* [kubecm merge](/zh-cn/cli/kubecm_merge.md)
* [kubecm namespace](/zh-cn/cli/kubecm_namespace.md)
* [kubecm rename](/zh-cn/cli/kubecm_rename.md)
* [kubecm switch](/zh-cn/cli/kubecm_switch.md)
* [kubecm clear](/en-us/cli/kubecm_clear.md)
* [kubecm version](/zh-cn/cli/kubecm_version.md)
* [add](/zh-cn/cli/kubecm_add.md)
* [create(实验性)](/zh-cn/cli/kubecm_create.md)
* [alias](/zh-cn/cli/kubecm_alias.md)
* [completion](/zh-cn/cli/kubecm_completion.md)
* [delete](/zh-cn/cli/kubecm_delete.md)
* [ls](/zh-cn/cli/kubecm_ls.md)
* [merge](/zh-cn/cli/kubecm_merge.md)
* [namespace](/zh-cn/cli/kubecm_namespace.md)
* [rename](/zh-cn/cli/kubecm_rename.md)
* [switch](/zh-cn/cli/kubecm_switch.md)
* [clear](/en-us/cli/kubecm_clear.md)
* [version](/zh-cn/cli/kubecm_version.md)
* [贡献](/zh-cn/contribute.md)
Loading

0 comments on commit 30aeb06

Please sign in to comment.