Compare commits

..

7 Commits

Author SHA1 Message Date
Ahmet Alp Balkan
cd1849d8df query namespace exists with GET Namespace
More efficient ns switches with kubens by querying only the namespace (instead
of listing all namespaces).

Signed-off-by: Ahmet Alp Balkan <ahmetb@google.com>
2020-05-31 15:23:02 -07:00
Ahmet Alp Balkan
0141ee19d2 handle ctxs with ':' in the name for windows (#235)
For windows, if the ctx name for kubens state file
contains a colon ':' (EKS), we replace it with __ (both while reading/writing).

It doesn't break existing users (as it didn't work in the first place).

Other characters remain unhandled, hoping it won't happen again.

Signed-off-by: Ahmet Alp Balkan <ahmetb@google.com>
2020-05-31 14:24:01 -07:00
Jason Harmon
401188fefd Fix color output on Windows (#220) 2020-05-15 15:26:00 -07:00
Ahmet Alp Balkan
01bd237baa Release for arm64 (armv8) and armhf (armv6), armv7 (#217) 2020-05-04 14:56:20 -07:00
Ahmet Alp Balkan
956d5953c2 kubens: fix interactive switch messages
Fixes #209.

Signed-off-by: Ahmet Alp Balkan <ahmetb@google.com>
2020-04-30 07:36:21 -07:00
Ahmet Alp Balkan
4425628f91 Remove changelog
Signed-off-by: Ahmet Alp Balkan <ahmetb@google.com>
2020-04-30 07:36:21 -07:00
Curro Rodriguez
f2021bb08b changed help in kubens, return had a typo (#210) 2020-04-30 07:34:06 -07:00
9 changed files with 109 additions and 36 deletions

View File

@@ -15,6 +15,9 @@ builds:
- windows
goarch:
- amd64
- arm
- arm64
goarm: [6, 7]
- id: kubens
main: ./cmd/kubens
binary: kubens
@@ -26,9 +29,18 @@ builds:
- windows
goarch:
- amd64
- arm
- arm64
goarm: [6, 7]
archives:
- id: kubectx-archive
name_template: "kubectx_{{ .Tag }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
name_template: |-
kubectx_{{ .Tag }}_{{ .Os }}_{{ .Arch -}}
{{- with .Arm -}}
{{- if (eq . "6") -}}hf
{{- else -}}v{{- . -}}
{{- end -}}
{{- end -}}
builds:
- kubectx
replacements:
@@ -39,7 +51,13 @@ archives:
format: zip
files: ["LICENSE"]
- id: kubens-archive
name_template: "kubens_{{ .Tag }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
name_template: |-
kubens_{{ .Tag }}_{{ .Os }}_{{ .Arch -}}
{{- with .Arm -}}
{{- if (eq . "6") -}}hf
{{- else -}}v{{- . -}}
{{- end -}}
{{- end -}}
builds:
- kubens
replacements:
@@ -49,12 +67,6 @@ archives:
- goos: windows
format: zip
files: ["LICENSE"]
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
checksum:
name_template: "checksums.txt"
algorithm: sha256

View File

@@ -8,6 +8,7 @@ import (
"github.com/ahmetb/kubectx/internal/cmdutil"
"github.com/ahmetb/kubectx/internal/env"
"github.com/ahmetb/kubectx/internal/printer"
"github.com/fatih/color"
)
type Op interface {
@@ -15,15 +16,15 @@ type Op interface {
}
func main() {
cmdutil.PrintDeprecatedEnvWarnings(os.Stderr, os.Environ())
cmdutil.PrintDeprecatedEnvWarnings(color.Error, os.Environ())
op := parseArgs(os.Args[1:])
if err := op.Run(os.Stdout, os.Stderr); err != nil {
printer.Error(os.Stderr, err.Error())
if err := op.Run(color.Output, color.Error); err != nil {
printer.Error(color.Error, err.Error())
if _, ok := os.LookupEnv(env.EnvDebug); ok {
// print stack trace in verbose mode
fmt.Fprintf(os.Stderr, "[DEBUG] error: %+v\n", err)
fmt.Fprintf(color.Error, "[DEBUG] error: %+v\n", err)
}
defer os.Exit(1)
}

View File

@@ -53,8 +53,8 @@ func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
}
name, err := switchNamespace(kc, choice)
if err != nil {
return errors.Wrap(err, "failed to switch context")
return errors.Wrap(err, "failed to switch namespace")
}
printer.Success(stderr, "Switched to context %s.", printer.SuccessColor.Sprint(name))
printer.Success(stderr, "Active namespace is %q.", printer.SuccessColor.Sprint(name))
return nil
}

View File

@@ -40,5 +40,5 @@ func selfName() string {
if strings.HasPrefix(me, pluginPrefix) {
return "kubectl " + strings.TrimPrefix(me, pluginPrefix)
}
return "kubectx"
return "kubens"
}

View File

@@ -54,15 +54,7 @@ func queryNamespaces(kc *kubeconfig.Kubeconfig) ([]string, error) {
return []string{"ns1", "ns2"}, nil
}
b, err := kc.Bytes()
if err != nil {
return nil, errors.Wrap(err, "failed to convert in-memory kubeconfig to yaml")
}
cfg, err := clientcmd.RESTConfigFromKubeConfig(b)
if err != nil {
return nil, errors.Wrap(err, "failed to initialize config")
}
clientset, err := kubernetes.NewForConfig(cfg)
clientset, err := newKubernetesClientSet(kc)
if err != nil {
return nil, errors.Wrap(err, "failed to initialize k8s REST client")
}
@@ -87,3 +79,15 @@ func queryNamespaces(kc *kubeconfig.Kubeconfig) ([]string, error) {
}
return out, nil
}
func newKubernetesClientSet(kc *kubeconfig.Kubeconfig) (*kubernetes.Clientset, error) {
b, err := kc.Bytes()
if err != nil {
return nil, errors.Wrap(err, "failed to convert in-memory kubeconfig to yaml")
}
cfg, err := clientcmd.RESTConfigFromKubeConfig(b)
if err != nil {
return nil, errors.Wrap(err, "failed to initialize config")
}
return kubernetes.NewForConfig(cfg)
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/ahmetb/kubectx/internal/cmdutil"
"github.com/ahmetb/kubectx/internal/env"
"github.com/ahmetb/kubectx/internal/printer"
"github.com/fatih/color"
)
type Op interface {
@@ -15,14 +16,14 @@ type Op interface {
}
func main() {
cmdutil.PrintDeprecatedEnvWarnings(os.Stderr, os.Environ())
cmdutil.PrintDeprecatedEnvWarnings(color.Error, os.Environ())
op := parseArgs(os.Args[1:])
if err := op.Run(os.Stdout, os.Stderr); err != nil {
printer.Error(os.Stderr, err.Error())
if err := op.Run(color.Output, color.Error); err != nil {
printer.Error(color.Error, err.Error())
if _, ok := os.LookupEnv(env.EnvDebug); ok {
// print stack trace in verbose mode
fmt.Fprintf(os.Stderr, "[DEBUG] error: %+v\n", err)
fmt.Fprintf(color.Error, "[DEBUG] error: %+v\n", err)
}
defer os.Exit(1)
}

View File

@@ -5,6 +5,8 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/ahmetb/kubectx/internal/cmdutil"
)
@@ -18,7 +20,14 @@ type NSFile struct {
func NewNSFile(ctx string) NSFile { return NSFile{dir: defaultDir, ctx: ctx} }
func (f NSFile) path() string { return filepath.Join(f.dir, f.ctx) }
func (f NSFile) path() string {
fn := f.ctx
if isWindows() {
// bug 230: eks clusters contain ':' in ctx name, not a valid file name for win32
fn = strings.ReplaceAll(fn, ":", "__")
}
return filepath.Join(f.dir, fn)
}
// Load reads the previous namespace setting, or returns empty if not exists.
func (f NSFile) Load() (string, error) {
@@ -40,3 +49,11 @@ func (f NSFile) Save(value string) error {
}
return ioutil.WriteFile(f.path(), []byte(value), 0644)
}
// isWindows determines if the process is running on windows OS.
func isWindows() bool {
if os.Getenv("_FORCE_GOOS") == "windows" { // for testing
return true
}
return runtime.GOOS == "windows"
}

View File

@@ -3,7 +3,11 @@ package main
import (
"io/ioutil"
"os"
"runtime"
"strings"
"testing"
"github.com/ahmetb/kubectx/internal/testutil"
)
func TestNSFile(t *testing.T) {
@@ -36,3 +40,28 @@ func TestNSFile(t *testing.T) {
t.Fatalf("Load()=%q; expected=%q", v, expected)
}
}
func TestNSFile_path_windows(t *testing.T) {
defer testutil.WithEnvVar("_FORCE_GOOS", "windows")()
fp := NewNSFile("a:b:c").path()
if expected := "a__b__c"; !strings.HasSuffix(fp, expected) {
t.Fatalf("file did not have expected ending %q: %s", expected, fp)
}
}
func Test_isWindows(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("won't test this case on windows")
}
got := isWindows()
if got {
t.Fatalf("isWindows() returned true for %s", runtime.GOOS)
}
defer testutil.WithEnvVar("_FORCE_GOOS", "windows")()
if !isWindows() {
t.Fatalf("isWindows() failed to detect windows with env override.")
}
}

View File

@@ -2,8 +2,11 @@ package main
import (
"io"
"os"
"github.com/pkg/errors"
errors2 "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/ahmetb/kubectx/internal/cmdutil"
"github.com/ahmetb/kubectx/internal/kubeconfig"
@@ -75,14 +78,20 @@ func switchNamespace(kc *kubeconfig.Kubeconfig, ns string) (string, error) {
}
func namespaceExists(kc *kubeconfig.Kubeconfig, ns string) (bool, error) {
nses, err := queryNamespaces(kc)
// for tests
if os.Getenv("_MOCK_NAMESPACES") != "" {
return ns == "ns1" || ns == "ns2", nil
}
clientset, err := newKubernetesClientSet(kc)
if err != nil {
return false, err
return false, errors.Wrap(err, "failed to initialize k8s REST client")
}
for _, v := range nses {
if v == ns {
return true, nil
}
namespace, err := clientset.CoreV1().Namespaces().Get(ns, metav1.GetOptions{})
if errors2.IsNotFound(err) {
return false, nil
}
return false, nil
return namespace != nil, errors.Wrapf(err, "failed to query "+
"namespace %q from k8s API", ns)
}