mirror of
https://github.com/ahmetb/kubectx.git
synced 2026-03-16 02:42:15 +00:00
Compare commits
1 Commits
master
...
abalkan/is
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d56196b9c2 |
@@ -33,6 +33,9 @@ Switched to context "minikube".
|
|||||||
$ kubectx -
|
$ kubectx -
|
||||||
Switched to context "oregon".
|
Switched to context "oregon".
|
||||||
|
|
||||||
|
# start an "isolated shell" that only has a single context
|
||||||
|
$ kubectx -s minikube
|
||||||
|
|
||||||
# rename context
|
# rename context
|
||||||
$ kubectx dublin=gke_ahmetb_europe-west1-b_dublin
|
$ kubectx dublin=gke_ahmetb_europe-west1-b_dublin
|
||||||
Context "gke_ahmetb_europe-west1-b_dublin" renamed to "dublin".
|
Context "gke_ahmetb_europe-west1-b_dublin" renamed to "dublin".
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ import (
|
|||||||
type CurrentOp struct{}
|
type CurrentOp struct{}
|
||||||
|
|
||||||
func (_op CurrentOp) Run(stdout, _ io.Writer) error {
|
func (_op CurrentOp) Run(stdout, _ io.Writer) error {
|
||||||
|
if err := checkIsolatedMode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||||
defer kc.Close()
|
defer kc.Close()
|
||||||
if err := kc.Parse(); err != nil {
|
if err := kc.Parse(); err != nil {
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ type DeleteOp struct {
|
|||||||
|
|
||||||
// deleteContexts deletes context entries one by one.
|
// deleteContexts deletes context entries one by one.
|
||||||
func (op DeleteOp) Run(_, stderr io.Writer) error {
|
func (op DeleteOp) Run(_, stderr io.Writer) error {
|
||||||
|
if err := checkIsolatedMode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for _, ctx := range op.Contexts {
|
for _, ctx := range op.Contexts {
|
||||||
// TODO inefficiency here. we open/write/close the same file many times.
|
// TODO inefficiency here. we open/write/close the same file many times.
|
||||||
deletedName, wasActiveContext, err := deleteContext(ctx)
|
deletedName, wasActiveContext, err := deleteContext(ctx)
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ func parseArgs(argv []string) Op {
|
|||||||
return ListOp{}
|
return ListOp{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if argv[0] == "--shell" || argv[0] == "-s" {
|
||||||
|
if len(argv) != 2 {
|
||||||
|
return UnsupportedOp{Err: fmt.Errorf("'%s' requires exactly one context name argument", argv[0])}
|
||||||
|
}
|
||||||
|
return ShellOp{Target: argv[1]}
|
||||||
|
}
|
||||||
|
|
||||||
if argv[0] == "-d" {
|
if argv[0] == "-d" {
|
||||||
if len(argv) == 1 {
|
if len(argv) == 1 {
|
||||||
if cmdutil.IsInteractiveMode(os.Stdout) {
|
if cmdutil.IsInteractiveMode(os.Stdout) {
|
||||||
|
|||||||
@@ -72,6 +72,18 @@ func Test_parseArgs_new(t *testing.T) {
|
|||||||
{name: "rename context with old=current",
|
{name: "rename context with old=current",
|
||||||
args: []string{"a=."},
|
args: []string{"a=."},
|
||||||
want: RenameOp{"a", "."}},
|
want: RenameOp{"a", "."}},
|
||||||
|
{name: "shell shorthand",
|
||||||
|
args: []string{"-s", "prod"},
|
||||||
|
want: ShellOp{Target: "prod"}},
|
||||||
|
{name: "shell long form",
|
||||||
|
args: []string{"--shell", "prod"},
|
||||||
|
want: ShellOp{Target: "prod"}},
|
||||||
|
{name: "shell without context name",
|
||||||
|
args: []string{"-s"},
|
||||||
|
want: UnsupportedOp{Err: fmt.Errorf("'-s' requires exactly one context name argument")}},
|
||||||
|
{name: "shell with too many args",
|
||||||
|
args: []string{"--shell", "a", "b"},
|
||||||
|
want: UnsupportedOp{Err: fmt.Errorf("'--shell' requires exactly one context name argument")}},
|
||||||
{name: "unrecognized flag",
|
{name: "unrecognized flag",
|
||||||
args: []string{"-x"},
|
args: []string{"-x"},
|
||||||
want: UnsupportedOp{Err: fmt.Errorf("unsupported option '-x'")}},
|
want: UnsupportedOp{Err: fmt.Errorf("unsupported option '-x'")}},
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ type InteractiveDeleteOp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
|
func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
|
||||||
|
if err := checkIsolatedMode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// parse kubeconfig just to see if it can be loaded
|
// parse kubeconfig just to see if it can be loaded
|
||||||
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||||
if err := kc.Parse(); err != nil {
|
if err := kc.Parse(); err != nil {
|
||||||
@@ -77,6 +80,9 @@ func (op InteractiveSwitchOp) Run(_, stderr io.Writer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (op InteractiveDeleteOp) Run(_, stderr io.Writer) error {
|
func (op InteractiveDeleteOp) Run(_, stderr io.Writer) error {
|
||||||
|
if err := checkIsolatedMode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// parse kubeconfig just to see if it can be loaded
|
// parse kubeconfig just to see if it can be loaded
|
||||||
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||||
if err := kc.Parse(); err != nil {
|
if err := kc.Parse(); err != nil {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ func printUsage(out io.Writer) error {
|
|||||||
%PROG% -d <NAME> [<NAME...>] : delete context <NAME> ('.' for current-context)
|
%PROG% -d <NAME> [<NAME...>] : delete context <NAME> ('.' for current-context)
|
||||||
%SPAC% (this command won't delete the user/cluster entry
|
%SPAC% (this command won't delete the user/cluster entry
|
||||||
%SPAC% referenced by the context entry)
|
%SPAC% referenced by the context entry)
|
||||||
|
%PROG% -s, --shell <NAME> : start a shell scoped to context <NAME>
|
||||||
%PROG% -h,--help : show this message
|
%PROG% -h,--help : show this message
|
||||||
%PROG% -V,--version : show version`
|
%PROG% -V,--version : show version`
|
||||||
help = strings.ReplaceAll(help, "%PROG%", selfName())
|
help = strings.ReplaceAll(help, "%PROG%", selfName())
|
||||||
|
|||||||
24
cmd/kubectx/isolated_shell_guard.go
Normal file
24
cmd/kubectx/isolated_shell_guard.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ahmetb/kubectx/internal/env"
|
||||||
|
"github.com/ahmetb/kubectx/internal/kubeconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkIsolatedMode() error {
|
||||||
|
if os.Getenv(env.EnvIsolatedShell) != "1" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||||
|
defer kc.Close()
|
||||||
|
if err := kc.Parse(); err != nil {
|
||||||
|
return fmt.Errorf("you are in a locked single-context shell, use 'exit' to leave")
|
||||||
|
}
|
||||||
|
|
||||||
|
cur := kc.GetCurrentContext()
|
||||||
|
return fmt.Errorf("you are in a locked single-context shell (\"%s\"), use 'exit' to leave", cur)
|
||||||
|
}
|
||||||
@@ -30,6 +30,9 @@ import (
|
|||||||
type ListOp struct{}
|
type ListOp struct{}
|
||||||
|
|
||||||
func (_ ListOp) Run(stdout, stderr io.Writer) error {
|
func (_ ListOp) Run(stdout, stderr io.Writer) error {
|
||||||
|
if err := checkIsolatedMode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||||
defer kc.Close()
|
defer kc.Close()
|
||||||
if err := kc.Parse(); err != nil {
|
if err := kc.Parse(); err != nil {
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ func parseRenameSyntax(v string) (string, string, bool) {
|
|||||||
// to the "new" value. If the old refers to the current-context,
|
// to the "new" value. If the old refers to the current-context,
|
||||||
// current-context preference is also updated.
|
// current-context preference is also updated.
|
||||||
func (op RenameOp) Run(_, stderr io.Writer) error {
|
func (op RenameOp) Run(_, stderr io.Writer) error {
|
||||||
|
if err := checkIsolatedMode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||||
defer kc.Close()
|
defer kc.Close()
|
||||||
if err := kc.Parse(); err != nil {
|
if err := kc.Parse(); err != nil {
|
||||||
|
|||||||
135
cmd/kubectx/shell.go
Normal file
135
cmd/kubectx/shell.go
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/ahmetb/kubectx/internal/env"
|
||||||
|
"github.com/ahmetb/kubectx/internal/kubeconfig"
|
||||||
|
"github.com/ahmetb/kubectx/internal/printer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ShellOp indicates intention to start a scoped sub-shell for a context.
|
||||||
|
type ShellOp struct {
|
||||||
|
Target string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op ShellOp) Run(_, stderr io.Writer) error {
|
||||||
|
if err := checkIsolatedMode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
kubectlPath, err := resolveKubectl()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify context exists and get current context for exit message
|
||||||
|
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||||
|
defer kc.Close()
|
||||||
|
if err := kc.Parse(); err != nil {
|
||||||
|
return errors.Wrap(err, "kubeconfig error")
|
||||||
|
}
|
||||||
|
if !kc.ContextExists(op.Target) {
|
||||||
|
return fmt.Errorf("no context exists with the name: \"%s\"", op.Target)
|
||||||
|
}
|
||||||
|
previousCtx := kc.GetCurrentContext()
|
||||||
|
|
||||||
|
// Extract minimal kubeconfig using kubectl
|
||||||
|
data, err := extractMinimalKubeconfig(kubectlPath, op.Target)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to extract kubeconfig for context")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to temp file
|
||||||
|
tmpFile, err := os.CreateTemp("", "kubectx-shell-*.yaml")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to create temp kubeconfig file")
|
||||||
|
}
|
||||||
|
tmpPath := tmpFile.Name()
|
||||||
|
defer os.Remove(tmpPath)
|
||||||
|
|
||||||
|
if _, err := tmpFile.Write(data); err != nil {
|
||||||
|
tmpFile.Close()
|
||||||
|
return errors.Wrap(err, "failed to write temp kubeconfig")
|
||||||
|
}
|
||||||
|
tmpFile.Close()
|
||||||
|
|
||||||
|
// Print entry message
|
||||||
|
badgeColor := color.New(color.BgRed, color.FgWhite, color.Bold)
|
||||||
|
printer.EnableOrDisableColor(badgeColor)
|
||||||
|
fmt.Fprintf(stderr, "%s kubectl context is %s in this shell — type 'exit' to leave.\n",
|
||||||
|
badgeColor.Sprint("[ISOLATED SHELL]"), printer.WarningColor.Sprint(op.Target))
|
||||||
|
|
||||||
|
// Detect and start shell
|
||||||
|
shellBin := detectShell()
|
||||||
|
cmd := exec.Command(shellBin)
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Env = append(os.Environ(),
|
||||||
|
"KUBECONFIG="+tmpPath,
|
||||||
|
env.EnvIsolatedShell+"=1",
|
||||||
|
)
|
||||||
|
|
||||||
|
_ = cmd.Run()
|
||||||
|
|
||||||
|
// Print exit message
|
||||||
|
fmt.Fprintf(stderr, "%s kubectl context is now %s.\n",
|
||||||
|
badgeColor.Sprint("[ISOLATED SHELL EXITED]"), printer.WarningColor.Sprint(previousCtx))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveKubectl() (string, error) {
|
||||||
|
if v := os.Getenv("KUBECTL"); v != "" {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
path, err := exec.LookPath("kubectl")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("kubectl is required for --shell but was not found in PATH")
|
||||||
|
}
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractMinimalKubeconfig(kubectlPath, contextName string) ([]byte, error) {
|
||||||
|
cmd := exec.Command(kubectlPath, "config", "view", "--minify", "--flatten",
|
||||||
|
"--context", contextName)
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
data, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("kubectl config view failed: %w", err)
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectShell() string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// cmd.exe always sets the PROMPT env var, so if it is present
|
||||||
|
// we can reliably assume we are running inside cmd.exe.
|
||||||
|
if os.Getenv("PROMPT") != "" {
|
||||||
|
return "cmd.exe"
|
||||||
|
}
|
||||||
|
// Otherwise assume PowerShell. PSModulePath is always set on
|
||||||
|
// Windows regardless of the shell, so it cannot be used as a
|
||||||
|
// discriminator; however the absence of PROMPT is a strong
|
||||||
|
// enough signal that we are in a PowerShell session.
|
||||||
|
if pwsh, err := exec.LookPath("pwsh"); err == nil {
|
||||||
|
return pwsh
|
||||||
|
}
|
||||||
|
if powershell, err := exec.LookPath("powershell"); err == nil {
|
||||||
|
return powershell
|
||||||
|
}
|
||||||
|
return "cmd.exe"
|
||||||
|
}
|
||||||
|
if v := os.Getenv("SHELL"); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return "/bin/sh"
|
||||||
|
}
|
||||||
131
cmd/kubectx/shell_test.go
Normal file
131
cmd/kubectx/shell_test.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ahmetb/kubectx/internal/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_detectShell_unix(t *testing.T) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("skipping unix shell detection test on windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
shellEnv string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "SHELL env set",
|
||||||
|
shellEnv: "/bin/zsh",
|
||||||
|
want: "/bin/zsh",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SHELL env empty, falls back to /bin/sh",
|
||||||
|
shellEnv: "",
|
||||||
|
want: "/bin/sh",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
orig := os.Getenv("SHELL")
|
||||||
|
defer os.Setenv("SHELL", orig)
|
||||||
|
|
||||||
|
os.Setenv("SHELL", tt.shellEnv)
|
||||||
|
if tt.shellEnv == "" {
|
||||||
|
os.Unsetenv("SHELL")
|
||||||
|
}
|
||||||
|
|
||||||
|
got := detectShell()
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("detectShell() = %q, want %q", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ShellOp_blockedWhenNested(t *testing.T) {
|
||||||
|
// Simulate being inside an isolated shell
|
||||||
|
orig := os.Getenv(env.EnvIsolatedShell)
|
||||||
|
defer os.Setenv(env.EnvIsolatedShell, orig)
|
||||||
|
os.Setenv(env.EnvIsolatedShell, "1")
|
||||||
|
|
||||||
|
op := ShellOp{Target: "some-context"}
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
err := op.Run(&stdout, &stderr)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error when running ShellOp inside isolated shell, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
want := "locked single-context shell to"
|
||||||
|
if !bytes.Contains([]byte(err.Error()), []byte(want)) {
|
||||||
|
// The error may not contain the context name if kubeconfig is not available,
|
||||||
|
// but it should still be blocked
|
||||||
|
want2 := "locked single-context shell"
|
||||||
|
if !bytes.Contains([]byte(err.Error()), []byte(want2)) {
|
||||||
|
t.Errorf("error message %q does not contain %q", err.Error(), want2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_resolveKubectl_envVar(t *testing.T) {
|
||||||
|
orig := os.Getenv("KUBECTL")
|
||||||
|
defer os.Setenv("KUBECTL", orig)
|
||||||
|
|
||||||
|
os.Setenv("KUBECTL", "/custom/path/kubectl")
|
||||||
|
got, err := resolveKubectl()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if got != "/custom/path/kubectl" {
|
||||||
|
t.Errorf("resolveKubectl() = %q, want %q", got, "/custom/path/kubectl")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_resolveKubectl_inPath(t *testing.T) {
|
||||||
|
orig := os.Getenv("KUBECTL")
|
||||||
|
defer os.Setenv("KUBECTL", orig)
|
||||||
|
os.Unsetenv("KUBECTL")
|
||||||
|
|
||||||
|
// kubectl should be findable in PATH on most dev machines
|
||||||
|
got, err := resolveKubectl()
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("kubectl not in PATH, skipping")
|
||||||
|
}
|
||||||
|
if got == "" {
|
||||||
|
t.Error("resolveKubectl() returned empty string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_checkIsolatedMode_notSet(t *testing.T) {
|
||||||
|
orig := os.Getenv(env.EnvIsolatedShell)
|
||||||
|
defer os.Setenv(env.EnvIsolatedShell, orig)
|
||||||
|
os.Unsetenv(env.EnvIsolatedShell)
|
||||||
|
|
||||||
|
err := checkIsolatedMode()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected nil error when not in isolated mode, got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_checkIsolatedMode_set(t *testing.T) {
|
||||||
|
orig := os.Getenv(env.EnvIsolatedShell)
|
||||||
|
defer os.Setenv(env.EnvIsolatedShell, orig)
|
||||||
|
os.Setenv(env.EnvIsolatedShell, "1")
|
||||||
|
|
||||||
|
err := checkIsolatedMode()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error when in isolated mode, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
want := "locked single-context shell"
|
||||||
|
if !bytes.Contains([]byte(err.Error()), []byte(want)) {
|
||||||
|
t.Errorf("error message %q does not contain %q", err.Error(), want)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,9 @@ type SwitchOp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (op SwitchOp) Run(_, stderr io.Writer) error {
|
func (op SwitchOp) Run(_, stderr io.Writer) error {
|
||||||
|
if err := checkIsolatedMode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var newCtx string
|
var newCtx string
|
||||||
var err error
|
var err error
|
||||||
if op.Target == "-" {
|
if op.Target == "-" {
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ import (
|
|||||||
type UnsetOp struct{}
|
type UnsetOp struct{}
|
||||||
|
|
||||||
func (_ UnsetOp) Run(_, stderr io.Writer) error {
|
func (_ UnsetOp) Run(_, stderr io.Writer) error {
|
||||||
|
if err := checkIsolatedMode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
kc := new(kubeconfig.Kubeconfig).WithLoader(kubeconfig.DefaultLoader)
|
||||||
defer kc.Close()
|
defer kc.Close()
|
||||||
if err := kc.Parse(); err != nil {
|
if err := kc.Parse(); err != nil {
|
||||||
|
|||||||
2
internal/env/constants.go
vendored
2
internal/env/constants.go
vendored
@@ -29,4 +29,6 @@ const (
|
|||||||
|
|
||||||
// EnvDebug describes the internal environment variable for more verbose logging.
|
// EnvDebug describes the internal environment variable for more verbose logging.
|
||||||
EnvDebug = `DEBUG`
|
EnvDebug = `DEBUG`
|
||||||
|
|
||||||
|
EnvIsolatedShell = "KUBECTX_ISOLATED_SHELL"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user