Skip to content

Commit

Permalink
Merge pull request #7 from bvankampen/kubeconfig-update
Browse files Browse the repository at this point in the history
Kubeconfig with multiple contexts / add symlink feature
  • Loading branch information
bvankampen authored Oct 12, 2024
2 parents bf8ed0e + afcdbfe commit 2489a30
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 32 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- go 1.22

### Usage:

```
NAME:
cluster - Select kubeconfig
Expand All @@ -15,7 +16,7 @@ USAGE:
ks [global options] command [command options] [arguments...]
VERSION:
2.0 (929de80)
1.2 (bf8ed0e)
COMMANDS:
help, h Shows a list of commands or help for one command
Expand All @@ -25,9 +26,13 @@ GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
```

### Build:

`make`

### Install:

`make install`

### Config:
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.2
1.3
3 changes: 2 additions & 1 deletion internal/config/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ var defaultConf = AppConfig{
KubeconfigFile: "config",
ExtraKubeconfigDirs: []string{"~/Downloads"},
ShowKubeConfig: true,
CreateLink: true,
}

type AppConfig struct {
KubeconfigDir string `yaml:"kubeconfigDir"`
KubeconfigFile string `yaml:"kubeconfigFile"`
ExtraKubeconfigDirs []string `yaml:"extraKubeconfigDirs"`
ShowKubeConfig bool `yaml:"showKubeconfig"`
CreateLink bool `yaml:"createLink"`
}

func LoadAppConfig() *AppConfig {
Expand Down Expand Up @@ -53,6 +55,5 @@ func LoadAppConfig() *AppConfig {
logrus.Errorf("Unable to load configfile: %v", err)
}
}

return &appconfig
}
50 changes: 40 additions & 10 deletions internal/kubeconfig/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func loadKubeConfigsFromDirectory(dir string) []api.Config {
return apiConfigs
}

func LoadKubeConfigs(appconfig *config.AppConfig) ([]api.Config, api.Config) {
func LoadKubeConfigs(appconfig config.AppConfig) ([]api.Config, api.Config) {
apiConfigs := loadKubeConfigsFromDirectory(appconfig.KubeconfigDir)
for _, dir := range appconfig.ExtraKubeconfigDirs {
apiConfigs = append(apiConfigs, loadKubeConfigsFromDirectory(dir)...)
Expand Down Expand Up @@ -85,17 +85,42 @@ func checkMark(path string) bool {
return false
}

func SaveKubeConfig(config *api.Config, context string, dir string, file string, do_check_mark bool) error {
func SaveKubeConfig(config *api.Config, context string, dir string, file string, doCheckMark bool, createLink bool, isMove bool) error {
dir, _ = homedir.Expand(dir)
path := filepath.Join(dir, file)
config.CurrentContext = context
if !checkMark(path) && do_check_mark {
return errors.New("Kubeconfig (" + path + ") is not managed by rs. Remove/rename this file first.")
}
err := clientcmd.WriteToFile(*config, path)
markKubeConfig(path)
if err != nil {
return errors.New("Unable to write " + path + " Error: " + err.Error())
if createLink {
kubeConfigLocation := config.Contexts[context].LocationOfOrigin
err := clientcmd.WriteToFile(*config, kubeConfigLocation)
if err != nil {
return errors.New("Unable to write " + path + " Error: " + err.Error())
}
_, err = os.Stat(path)
if err == nil {
fileInfo, _ := os.Lstat(path)
if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
os.Remove(path)
} else {
return errors.New("File: " + path + " is not a symlink, please remove/rename this file first.")
}
}
if isMove {
// is kubeconfig is moved, then use new location instead of original location
kubeConfigLocation = filepath.Join(dir, filepath.Base(kubeConfigLocation))
}
err = os.Symlink(kubeConfigLocation, path)
if err != nil {
return errors.New("Unable to create Symlink " + kubeConfigLocation + "->" + path + "Error: " + err.Error())
}
} else {
if !checkMark(path) && doCheckMark {
return errors.New("Kubeconfig (" + path + ") is not managed by rs. Remove/rename this file first.")
}
err := clientcmd.WriteToFile(*config, path)
if err != nil {
return errors.New("Unable to write " + path + " Error: " + err.Error())
}
markKubeConfig(path)
}
return nil
}
Expand All @@ -112,7 +137,12 @@ func MoveKubeConfig(config *api.Config, context string, kubeConfigDir string) er
return nil
}

func DeleteKubeConfig(config *api.Config, context string) error {
func DeleteKubeConfig(config *api.Config, context string, dir string, file string, createLink bool) error {
if createLink {
dir, _ = homedir.Expand(dir)
path := filepath.Join(dir, file)
os.Remove(path)
}
originalKubeConfigLocation := config.Contexts[context].LocationOfOrigin
os.Remove(originalKubeConfigLocation)
return nil
Expand Down
15 changes: 7 additions & 8 deletions internal/selector/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package selector

import (
"github.com/bvankampen/kubeconfig-selector/internal/config"
"github.com/bvankampen/kubeconfig-selector/internal/kubeconfig"
"github.com/bvankampen/kubeconfig-selector/internal/ui"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
Expand All @@ -11,20 +10,20 @@ import (

func New(ctx cli.Context) (*Selector, error) {
appconfig := config.LoadAppConfig()
kubeconfigs, activeconfig := kubeconfig.LoadKubeConfigs(appconfig)
// kubeconfigs, activeconfig := kubeconfig.LoadKubeConfigs(appconfig)

return &Selector{
ctx: &ctx,
appConfig: *appconfig,
kubeConfigs: kubeconfigs,
activeConfig: activeconfig,
debug: ctx.GlobalBool("debug"),
ctx: &ctx,
appConfig: *appconfig,
// kubeConfigs: kubeconfigs,
// activeConfig: activeconfig,
debug: ctx.GlobalBool("debug"),
}, nil
}

func (s *Selector) Run() error {
var ui ui.UI
ui.Init(s.ctx, s.appConfig, s.kubeConfigs, s.activeConfig, s.debug)
ui.Init(s.ctx, s.appConfig, s.debug)
err := ui.Run()
if err != nil {
logrus.Panicf("Error: %v", err)
Expand Down
55 changes: 48 additions & 7 deletions internal/ui/app_main.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,47 @@ func (ui *UI) createList() int {
return currentIndex
}

func (ui *UI) redrawList() {
index := 0
ui.list.Clear()
for _, config := range ui.kubeConfigs {
for name, configContext := range config.Contexts {
kubeDir, _ := homedir.Expand(ui.appConfig.KubeconfigDir)

var star rune
star = 0
if !strings.HasPrefix(configContext.LocationOfOrigin, kubeDir) {
star = '*'
}

ui.list.AddItem(name, "", star, nil)

if ui.activeConfig.CurrentContext != "" {
activeConfigContext := ui.activeConfig.Contexts[ui.activeConfig.CurrentContext]
activeConfigCluster := activeConfigContext.Cluster
activeConfigServer := ui.activeConfig.Clusters[activeConfigContext.Cluster].Server

if configContext.Cluster == activeConfigCluster &&
config.Clusters[configContext.Cluster].Server == activeConfigServer &&
name == ui.activeConfig.CurrentContext {
}
}

index++
}
}
}

func (ui *UI) selectKubeConfig(index int) {
name, config, _ := ui.getConfigByIndex(index)
err := kubeconfig.SaveKubeConfig(
config.DeepCopy(),
name,
ui.appConfig.KubeconfigDir,
ui.appConfig.KubeconfigFile,
true)
true,
ui.appConfig.CreateLink,
false)
if err != nil {
ui.ErrorMessage(err.Error())
} else {
Expand All @@ -84,11 +117,12 @@ func (ui *UI) deleteConfigByIndex(index int) {
}

func (ui *UI) getConfigByIndex(index int) (string, api.Config, *api.Context) {
config := ui.kubeConfigs[index]
for name, context := range config.Contexts {
currentName, _ := ui.list.GetItemText(index)
if name == currentName {
return name, config, context
contextName, _ := ui.list.GetItemText(index)
for _, config := range ui.kubeConfigs {
for name, context := range config.Contexts {
if name == contextName {
return name, config, context
}
}
}
return "", api.Config{}, &api.Context{}
Expand Down Expand Up @@ -136,7 +170,6 @@ func (ui *UI) createViews(redraw bool) {
ui.debugView.SetBorder(true).SetTitle("Debug")
ui.views.AddItem(ui.debugView, 10, 3, false)
}

}

func (ui *UI) printDebug(debugMessage string) {
Expand All @@ -157,7 +190,11 @@ func (ui *UI) createAppMain() {

func (ui *UI) redrawAppMain() {
ui.createViews(true)
}

func (ui *UI) redrawLists() {
ui.ReloadKubeConfigs()
ui.redrawList()
}

func (ui *UI) moveKubeConfig() {
Expand All @@ -168,6 +205,8 @@ func (ui *UI) moveKubeConfig() {
name,
ui.appConfig.KubeconfigDir,
ui.appConfig.KubeconfigFile,
true,
ui.appConfig.CreateLink,
true)
if err != nil {
ui.ErrorMessage(err.Error())
Expand All @@ -194,6 +233,8 @@ func (ui *UI) renameKubeConfigContext(index int, config api.Config, contextName
newContextName,
kubeConfigPath,
kubeConfigFilename,
false,
ui.appConfig.CreateLink,
false)
if err != nil {
ui.ErrorMessage(err.Error())
Expand Down
11 changes: 9 additions & 2 deletions internal/ui/delete_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,16 @@ func (ui *UI) deleteCurrentItem() {
deleteMessage.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
switch buttonLabel {
case "Yes":
kubeconfig.DeleteKubeConfig(config.DeepCopy(), name)
kubeconfig.DeleteKubeConfig(
config.DeepCopy(),
name,
ui.appConfig.KubeconfigDir,
ui.appConfig.KubeconfigFile,
ui.appConfig.CreateLink,
)
ui.deleteConfigByIndex(index)
ui.list.RemoveItem(index)
ui.redrawLists()
// ui.list.RemoveItem(index)
}
ui.pages.
HidePage("delete").
Expand Down
3 changes: 3 additions & 0 deletions internal/ui/help_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ func (ui *UI) getHelpText() string {
" [yellow](*):[white] File not in " + ui.appConfig.KubeconfigDir + "\n" +
" [yellow]?:[white] Help \n" +
" \n [green]Changelog: \n\n" +
" [red]Version 1.3:[white] \n" +
" - fixed problems with kubeconfigs with multiple contexts \n" +
" - add option to make symlink to kubeconfig instead of copy \"createLink\" config value (boolean) \n" +
" [red]Version 1.2:[white] \n" +
" - Rename a context of a kubeconfig \n" +
" - Big rewrite and refactor of application internals \n" +
Expand Down
11 changes: 9 additions & 2 deletions internal/ui/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package ui

import (
"github.com/bvankampen/kubeconfig-selector/internal/config"
"github.com/bvankampen/kubeconfig-selector/internal/kubeconfig"
"github.com/rivo/tview"
"github.com/urfave/cli"
"k8s.io/client-go/tools/clientcmd/api"
)

func (ui *UI) Init(ctx *cli.Context, appConfig config.AppConfig, kubeConfigs []api.Config, activeConfig api.Config, debug bool) {
func (ui *UI) Init(ctx *cli.Context, appConfig config.AppConfig, debug bool) {
kubeConfigs, activeConfig := kubeconfig.LoadKubeConfigs(appConfig)
ui.ctx = ctx
ui.debug = debug
ui.app = tview.NewApplication()
Expand All @@ -21,6 +22,12 @@ func (ui *UI) Init(ctx *cli.Context, appConfig config.AppConfig, kubeConfigs []a
ui.createAppMain()
}

func (ui *UI) ReloadKubeConfigs() {
kubeConfigs, activeConfig := kubeconfig.LoadKubeConfigs(ui.appConfig)
ui.kubeConfigs = kubeConfigs
ui.activeConfig = activeConfig
}

func (ui *UI) Run() error {
ui.app.SetRoot(ui.pages, true)
err := ui.app.Run()
Expand Down

0 comments on commit 2489a30

Please sign in to comment.