This repository has been archived by the owner on May 3, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
385 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,22 @@ | ||
# kubectx | ||
|
||
Faster/Smarter alternative to famous [kubectx](https://github.com/ahmetb/kubectx). <br> | ||
Uses client-go. | ||
Simply 5x-10x faster alternative to famous [kubectx](https://github.com/ahmetb/kubectx). Uses client-go. | ||
|
||
### Install | ||
``` | ||
# kubens | ||
go get -u github.com/aca/kubectx/cmd/kubens | ||
# kubectx | ||
TODO | ||
go get -u github.com/aca/kubectx/cmd/kubectx | ||
``` | ||
|
||
### Benchmarks | ||
|
||
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative | | ||
|:---|---:|---:|---:|---:| | ||
| `/usr/bin/kubectx minikube` | 98.9 ± 12.1 | 84.8 | 138.6 | 5.35 ± 0.89 | | ||
| `/home/rok/bin/kubectx minikube` | 18.5 ± 2.1 | 15.6 | 27.1 | 1.00 | | ||
|
||
|
||
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative | | ||
|:---|---:|---:|---:|---:| | ||
| `/usr/bin/kubens kube-system` | 258.6 ± 20.7 | 237.9 | 300.9 | 5.73 ± 1.78 | | ||
| `/home/rok/bin/kubens kube-system` | 45.1 ± 13.6 | 32.3 | 120.0 | 1.00 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#!/bin/sh | ||
|
||
kubectx=/usr/bin/kubectx | ||
go_kubectx="$GOPATH/bin/kubectx" | ||
|
||
kubens=/usr/bin/kubens | ||
go_kubens="$GOPATH/bin/kubens" | ||
|
||
bench_kubectx() { | ||
echo "COMMAND: kubectx $@" | ||
hyperfine "$kubectx $@" "$go_kubectx $@" --export-markdown bench_kubectx.md 2>/dev/null | ||
printf "\n\n" | ||
} | ||
|
||
bench_kubens() { | ||
echo "COMMAND: kubens $@" | ||
hyperfine "$kubens $@" "$go_kubens $@" --export-markdown bench_kubens.md 2>/dev/null | ||
printf "\n\n" | ||
} | ||
|
||
bench_kubectx minikube | ||
bench_kubens kube-system | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
|
||
"github.com/aca/kubectx/config" | ||
"github.com/aca/kubectx/fzfutil" | ||
"github.com/spf13/pflag" | ||
|
||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api" | ||
|
||
"k8s.io/client-go/tools/clientcmd" | ||
) | ||
|
||
var cfg *config.Config | ||
var rules *clientcmd.ClientConfigLoadingRules | ||
var clientcmdCfg *clientcmdapi.Config | ||
|
||
func main() { | ||
var err error | ||
log.SetFlags(log.LstdFlags | log.Lshortfile) | ||
cfg, err = config.ReadCfg() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
fs := pflag.NewFlagSet("kubectx", pflag.ContinueOnError) | ||
current := fs.BoolP("current", "c", false, "show the current context") | ||
help := fs.BoolP("help", "h", false, "help") | ||
fs.Parse(os.Args[1:]) | ||
|
||
if *help { | ||
fmt.Printf(`USAGE: | ||
kubectx : switch context(interactive) | ||
kubectx <QUERY> : switch context which closest match with QUERY(if failed to match, interactive mode) | ||
kubectx - : switch to previous context (~/.config/kubectx/config.json) | ||
kubectx -c, --current : show current context name | ||
kubectx -h,--help : help | ||
`) | ||
return | ||
} | ||
|
||
rules = clientcmd.NewDefaultClientConfigLoadingRules() | ||
b, err := ioutil.ReadFile(rules.GetDefaultFilename()) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
clientcmdCfg, err = clientcmd.Load(b) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
var query string | ||
|
||
if *current { | ||
fmt.Println(clientcmdCfg.CurrentContext) | ||
return | ||
} | ||
|
||
if len(os.Args) > 1 && os.Args[1] == "-" { | ||
query = cfg.LastContext | ||
} else if len(os.Args) > 1 { | ||
query = os.Args[1] | ||
} | ||
|
||
err = kubectx(query) | ||
if err != nil { | ||
fmt.Printf("Err: %s", err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func kubectx(query string) error { | ||
var err error | ||
|
||
_, ok := clientcmdCfg.Contexts[query] | ||
if ok { | ||
return modifyConfig(query) | ||
} | ||
|
||
listNS := func(in io.WriteCloser) { | ||
for key, _ := range clientcmdCfg.Contexts { | ||
fmt.Fprintln(in, key) | ||
} | ||
} | ||
|
||
fzfopt := []string{"--select-1", "--query", query} | ||
|
||
result, err := fzfutil.FZF(listNS, fzfopt...) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if len(result) == 0 { | ||
return errors.New("no context selected") | ||
} | ||
|
||
return modifyConfig(result[0]) | ||
} | ||
|
||
func modifyConfig(to string) (err error) { | ||
curCtx := clientcmdCfg.CurrentContext | ||
defer func() { | ||
if err == nil { | ||
if curCtx != to { | ||
cfg.LastContext = curCtx | ||
config.WriteCfg(cfg) | ||
} | ||
fmt.Printf("Switched to context \"%s\"\n", to) | ||
} | ||
}() | ||
clientcmdCfg.CurrentContext = to | ||
return clientcmd.ModifyConfig(rules, *clientcmdCfg, false) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,132 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/aca/kubectx/config" | ||
"github.com/aca/kubectx/fzfutil" | ||
"github.com/spf13/pflag" | ||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api" | ||
|
||
"k8s.io/client-go/kubernetes" | ||
"k8s.io/client-go/tools/clientcmd" | ||
) | ||
|
||
var cfg *config.Config | ||
var rules *clientcmd.ClientConfigLoadingRules | ||
var clientcmdCfg *clientcmdapi.Config | ||
var kubeCfgBytes []byte | ||
|
||
func main() { | ||
var err error | ||
log.SetFlags(log.LstdFlags | log.Lshortfile) | ||
cfg, err = config.ReadCfg() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
fs := pflag.NewFlagSet("kubectx", pflag.ContinueOnError) | ||
current := fs.BoolP("current", "c", false, "show the current context") | ||
help := fs.BoolP("help", "h", false, "help") | ||
fs.Parse(os.Args[1:]) | ||
|
||
if *help { | ||
fmt.Printf(`USAGE: | ||
kubens : switch namespace(interactive) | ||
kubens <QUERY> : switch namespace which closest match with QUERY(if failed to match, interactive mode) | ||
kubens - : switch to previous namespace (~/.config/kubectx/config.json) | ||
kubens -c, --current : show current namespace name | ||
kubens -h,--help : help | ||
`) | ||
return | ||
} | ||
|
||
rules = clientcmd.NewDefaultClientConfigLoadingRules() | ||
kubeCfgBytes, err = ioutil.ReadFile(rules.GetDefaultFilename()) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
clientcmdCfg, err = clientcmd.Load(kubeCfgBytes) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
var query string | ||
if len(os.Args) > 1 { | ||
query = strings.Join(os.Args[1:], "") | ||
|
||
if *current { | ||
fmt.Println(clientcmdCfg.Contexts[clientcmdCfg.CurrentContext].Namespace) | ||
return | ||
} | ||
|
||
if len(os.Args) > 1 && os.Args[1] == "-" { | ||
query = cfg.LastNamespace | ||
} else if len(os.Args) > 1 { | ||
query = os.Args[1] | ||
} | ||
|
||
err := kubens(query) | ||
err = kubens(query) | ||
if err != nil { | ||
fmt.Printf("Err: %s", err) | ||
log.Printf("Err: %s", err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
// kubens fuzzy finds & switches namespaces | ||
func kubens(query string) error { | ||
rules := clientcmd.NewDefaultClientConfigLoadingRules() | ||
|
||
kubeconfig, err := ioutil.ReadFile(rules.GetDefaultFilename()) | ||
func kubens(query string) (err error) { | ||
restConfig, err := clientcmd.RESTConfigFromKubeConfig(kubeCfgBytes) | ||
if err != nil { | ||
return err | ||
} | ||
restConfig, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) | ||
restConfig.Timeout = time.Second * 5 | ||
|
||
clientset, err := kubernetes.NewForConfig(restConfig) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fzfopt := []string{"--select-1", "--query", query} | ||
namespaces, err := clientset.CoreV1().Namespaces().List(metav1.ListOptions{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
output, err := fzfutil.FZF(func(in io.WriteCloser) { | ||
namespaces, err := clientset.CoreV1().Namespaces().List(metav1.ListOptions{}) | ||
if err != nil { | ||
panic(err) | ||
} | ||
fzfopt := []string{"--select-1", "--query", query} | ||
|
||
inputChan := make(chan string, 100) | ||
result, err := fzfutil.FZF(func(in io.WriteCloser) { | ||
for _, item := range namespaces.Items { | ||
fmt.Fprintln(in, item.ObjectMeta.Name) | ||
} | ||
close(inputChan) | ||
|
||
}, fzfopt...) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
if len(output) == 0 { | ||
return fmt.Errorf("namespace not selected") | ||
if len(result) == 0 { | ||
return errors.New("no namespace selected") | ||
} | ||
|
||
clientcmdConfig, err := rules.Load() | ||
if err != nil { | ||
return err | ||
} | ||
return modifyConfig(result[0]) | ||
} | ||
|
||
clientcmdConfig.Contexts[clientcmdConfig.CurrentContext].Namespace = output[0] | ||
return clientcmd.ModifyConfig(rules, *clientcmdConfig, false) | ||
func modifyConfig(to string) (err error) { | ||
curNS := clientcmdCfg.Contexts[clientcmdCfg.CurrentContext].Namespace | ||
defer func() { | ||
if err == nil { | ||
if curNS != to { | ||
cfg.LastNamespace = curNS | ||
config.WriteCfg(cfg) | ||
} | ||
fmt.Printf("Switched to namespace \"%s\"\n", to) | ||
} | ||
}() | ||
clientcmdCfg.Contexts[clientcmdCfg.CurrentContext].Namespace = to | ||
return clientcmd.ModifyConfig(rules, *clientcmdCfg, false) | ||
} |
Oops, something went wrong.