Skip to content

Commit

Permalink
remove config file (#19)
Browse files Browse the repository at this point in the history
* feat: global refacto to remove the config file and add opts to sync

* update the doc

* bump the cli version
  • Loading branch information
MqllR committed Mar 15, 2022
1 parent 62d0330 commit 601fd6d
Show file tree
Hide file tree
Showing 18 changed files with 189 additions and 324 deletions.
52 changes: 19 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,32 @@
**kubenv** is a tool to ease the management of multiple kubernetes cluster.

Features:
- Merge different kubeconfig file in different path into a single big kubeconfig
- Merge different kubeconfig files from different path into a single big kubeconfig
- Switch between kubernetes context
- Execute a command using either a single or mutliple contexts

## Install

Grap the latest release in github or build by yourself:
Grabe the latest release in github release or build it by yourself:

```
go get https://github.com/mqllr/kubenv
```

Define your configuration file as [kubenv-example.yaml](https://github.com/MqllR/kubenv/blob/master/example/kubenv_example.yaml) and export the environment variable:
## Usage

```
export KUBENV_CONFIG=/path/to/my/config.yaml
```
Get started by picking up your kubeconfig files:

## Configuration

### k8sConfigs

The k8sConfigs section define the sync mode. 2 modes available: `local` for local files and `exec` for command execution. The exec mode will capture the command output.

```yaml
dev:
sync:
mode: local
path: /tmp/k8senv/dev/config
kind:
sync:
mode: exec
command:
- bash
- -c
- |
kind export -q kubeconfig --kubeconfig /tmp/test && cat /tmp/test
```
kubenv sync -a=false -m=exec --command="cat /path/to/kubeconfig"
▸ Start the synchronization of kubeconfig file into /home/john/.kube/config ...
```

## Usage
Get started by picking up your kubeconfig files:
Which is equivalent to:

```
kubenv sync
▸ Start the synchronization of kubeconfig file into /home/mql/.kube/config ...
Sync kubeconfig foo ✔
Sync kubeconfig bar ✔
kubenv sync -a=false -m=local --path="/path/to/kubeconfig"
▸ Start the synchronization of kubeconfig file into /home/john/.kube/config ...
```

Then you can change your default context by using the `use-context`
Expand All @@ -67,6 +45,14 @@ or execute a command against a single or multiple contexts:
kubenv with-context
```

The tool uses https://github.com/AlecAivazis/survey to navigate between contexts. If you prefer to use j/k to go down or up, press `esc`:
The CLI uses https://github.com/AlecAivazis/survey to navigate between contexts. If you prefer to use j/k to go down or up, press `esc`:

> The user can also press esc to toggle the ability cycle through the options with the j and k keys to do down and up respectively.
## Bonus

Want the CLI to be part of kubectl?

```
ln -s /home/john/go/bin/kubenv /usr/local/bin/kubectl-env
```
3 changes: 1 addition & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
- [ ] Backup / restore kubeconfig when running the sync command
- [ ] Switch between namespaces
- [x] Run command across mutliple clusters
- [ ] Read the kubenv config through http(s)

## Sync

- [ ] Get kubeconfig files using http(s)
- [ ] Get kubeconfig with glob
- [ ] Get kubeconfig with glob pattern
- [ ] Add arguments to append a kubeconfig file to an existing config

## Prompt
Expand Down
50 changes: 7 additions & 43 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,12 @@ package cmd
import (
goflag "flag"
"fmt"
"os"

"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/klog"

"github.com/mqllr/kubenv/pkg/config"
)

var (
// Used for flags.
cfgFile string

rootCmd = &cobra.Command{
Use: "kubenv",
Short: "A tool to manage multiple Kube cluster",
Expand All @@ -37,49 +30,20 @@ func Execute() error {
}

func init() {
cobra.OnInitialize(initConfig)

klog.InitFlags(nil)
rootCmd.PersistentFlags().AddGoFlagSet(goflag.CommandLine)

rootCmd.Flags().SortFlags = false
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/kubenv.yaml)")

rootCmd.AddCommand(NewVersionCmd())
rootCmd.AddCommand(versionCmd())

// root cmd
rootCmd.AddCommand(showCmd)
rootCmd.AddCommand(syncCmd)
rootCmd.AddCommand(useContextCmd)
rootCmd.AddCommand(withContextCmd)
rootCmd.AddCommand(showCmd())
rootCmd.AddCommand(syncCommand())
rootCmd.AddCommand(useContextCmd())
rootCmd.AddCommand(withContextCmd())

// show cmd
showCmd.AddCommand(showClusterCmd)
showCmd.AddCommand(showUserCmd)
}

func initConfig() {
viper.SetEnvPrefix("kubenv")
err := viper.BindEnv("config")
if err != nil {
klog.Fatalf("Error when binding the config key %s", err)
}

if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else if viper.GetString("config") != "" {
viper.SetConfigFile(viper.GetString("config"))
} else {
viper.SetConfigName("kubenv")
viper.SetConfigType("yaml")
viper.AddConfigPath("$HOME/")
viper.AddConfigPath(".")
}

viper.SetDefault("kubeConfig", os.Getenv("HOME")+"/.kube/config")

err = config.LoadConfig()
if err != nil {
klog.Fatal(err)
}
showCmd().AddCommand(showClusterCmd())
showCmd().AddCommand(showUserCmd())
}
10 changes: 6 additions & 4 deletions cmd/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package cmd

import "github.com/spf13/cobra"

var showCmd = &cobra.Command{
Use: "show",
Short: "show different information",
Aliases: []string{"sh"},
func showCmd() *cobra.Command {
return &cobra.Command{
Use: "show",
Short: "show different information",
Aliases: []string{"sh"},
}
}
18 changes: 11 additions & 7 deletions cmd/show_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ import (
"k8s.io/klog"
)

var showClusterCmd = &cobra.Command{
Use: "cluster",
Short: "Print out the current context's cluster",
Run: func(cmd *cobra.Command, args []string) {
showCluster(args)
},
func showClusterCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "cluster",
Short: "Print out the current context's cluster",
Run: func(cmd *cobra.Command, args []string) {
showCluster(args)
},
}

return cmd
}

func showCluster(args []string) {
kubeconfig, err := k8s.NewKubeConfigFromFile(config.Conf.KubeConfig)
kubeconfig, err := k8s.NewKubeConfigFromFile(config.GetKubeConfig())
if err != nil {
klog.Fatalf("Cannot load the kubeconfig file: %s", err)
}
Expand Down
17 changes: 10 additions & 7 deletions cmd/show_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ import (
"k8s.io/klog"
)

var showUserCmd = &cobra.Command{
Use: "user",
Short: "Print out the current context's user",
Run: func(cmd *cobra.Command, args []string) {
showUser(args)
},
func showUserCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "user",
Short: "Print out the current context's user",
Run: func(cmd *cobra.Command, args []string) {
showUser(args)
},
}
return cmd
}

func showUser(args []string) {
kubeconfig, err := k8s.NewKubeConfigFromFile(config.Conf.KubeConfig)
kubeconfig, err := k8s.NewKubeConfigFromFile(config.GetKubeConfig())
if err != nil {
klog.Fatalf("Cannot load the kubeconfig file: %s", err)
}
Expand Down
80 changes: 55 additions & 25 deletions cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,77 @@ import (

"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"k8s.io/klog"

"github.com/mqllr/kubenv/pkg/config"
"github.com/mqllr/kubenv/pkg/k8s"
k8ssync "github.com/mqllr/kubenv/pkg/sync"
"github.com/mqllr/kubenv/pkg/sync"
)

var syncCmd = &cobra.Command{
Use: "sync",
Short: "Synchronize the kubernetes config files",
Run: func(cmd *cobra.Command, args []string) {
sync(args)
},
}
func syncCommand() *cobra.Command {
opts := sync.SyncOptions{}

func sync(args []string) {
fmt.Printf("%v Start the synchronization of kubeconfig file into %s ...\n", promptui.IconSelect, config.Conf.KubeConfig)
cmd := &cobra.Command{
Use: "sync",
Short: "Synchronize the kubernetes config files",
PreRunE: func(cmd *cobra.Command, args []string) error {
return validateFlags(&opts)
},
RunE: func(cmd *cobra.Command, args []string) error {
return runSync(&opts)
},
}

fullConfig := k8s.NewKubeConfig()
f := cmd.Flags()
f.BoolVarP(&opts.AppendTo, "append", "a", true, "Append the new kubeconfig files to ~/.kube/config")
f.StringVarP(&opts.Mode, "mode", "m", "local", "Mode to read a kubeconfig file. Either local, exec or glob")
f.StringVar(&opts.Path, "path", "", "A path to the local kubeconfig file")
f.StringVar(&opts.Command, "command", "", "A command to execute to retrieve the kubeconfig file")

for name, conf := range config.Conf.K8SConfigs {
fmt.Printf("Sync kubeconfig %s", name)
return cmd
}

s, err := k8ssync.NewService(*conf.Sync)
if err != nil {
fmt.Printf(" %v\n", promptui.IconBad)
klog.V(2).Infof("Cannot sync: %s", err)
continue
func validateFlags(opts *sync.SyncOptions) error {
existInSyncMode := func(mode string) bool {
for _, m := range config.SyncMode {
if mode == m {
return true
}
}
return false
}

if !existInSyncMode(opts.Mode) {
return fmt.Errorf("Mode %s not supported", opts.Mode)
}

return nil
}

func runSync(opts *sync.SyncOptions) error {
fmt.Printf("%v Start to synchronize the kubeconfig file into %s ...\n", promptui.IconSelect, config.GetKubeConfig())

err = s.AppendKubeConfig(fullConfig)
baseKubeConfig := k8s.NewKubeConfig()
var err error

if opts.AppendTo {
baseKubeConfig, err = k8s.NewKubeConfigFromFile(config.GetKubeConfig())
if err != nil {
fmt.Printf(" %v\n", promptui.IconBad)
klog.V(2).Infof("Error when getting the config back: %s", err)
} else {
fmt.Printf(" %v\n", promptui.IconGood)
return fmt.Errorf("Something went wrong: %s", err)
}
}

err := fullConfig.WriteFile(config.Conf.KubeConfig)
svc := sync.NewService(opts)
kubeconfig, err := svc.GetKubeConfig()
if err != nil {
return fmt.Errorf("Cannot retrieve the kubeconfig: %s", err)
}

baseKubeConfig.Append(kubeconfig)

err = baseKubeConfig.WriteFile(config.GetKubeConfig())
if err != nil {
fmt.Printf("%v Failed to write the kubeconfig file: %s", promptui.IconBad, err)
}

return nil
}
Loading

0 comments on commit 601fd6d

Please sign in to comment.