diff --git a/cmd/switcher/context.go b/cmd/switcher/context.go index 85fd1cfa0..bec01c237 100644 --- a/cmd/switcher/context.go +++ b/cmd/switcher/context.go @@ -43,8 +43,8 @@ var ( return err } - kubeconfigPath, contextName, err := history.SetPreviousContext(stores, config, stateDirectory, noIndex) - reportNewContext(kubeconfigPath, contextName) + kubeconfigPath, contextName, storeName, sourcePath, err := history.SetPreviousContext(stores, config, stateDirectory, noIndex) + reportNewContext(kubeconfigPath, contextName, storeName, sourcePath) return err }, } @@ -63,8 +63,8 @@ var ( return err } - kubeconfigPath, contextName, err := history.SetLastContext(stores, config, stateDirectory, noIndex) - reportNewContext(kubeconfigPath, contextName) + kubeconfigPath, contextName, storeName, sourcePath, err := history.SetLastContext(stores, config, stateDirectory, noIndex) + reportNewContext(kubeconfigPath, contextName, storeName, sourcePath) return err }, } @@ -126,8 +126,8 @@ var ( return err } - kubeconfigPath, contextName, err := set_context.SetContext(args[0], stores, config, stateDirectory, noIndex, true) - reportNewContext(kubeconfigPath, contextName) + kubeconfigPath, contextName, storeName, sourcePath, err := set_context.SetContext(args[0], stores, config, stateDirectory, noIndex, true) + reportNewContext(kubeconfigPath, contextName, storeName, sourcePath) return err }, SilenceUsage: true, @@ -257,13 +257,22 @@ func setFlagsForContextCommands(command *cobra.Command) { "show preview of the selected kubeconfig. Possibly makes sense to disable when using vault as the kubeconfig store to prevent excessive requests against the API.") } -func reportNewContext(kubeconfigPath *string, contextName *string) { +func reportNewContext(kubeconfigPath, contextName, storeName, sourcePath *string) { if kubeconfigPath == nil || contextName == nil { return } + storeField := "" + if storeName != nil { + storeField = *storeName + } + sourceField := "" + if sourcePath != nil { + sourceField = *sourcePath + } + // print kubeconfig path and context name to std.out // captured by calling script setting KUBECONFIG environment variable // prefixed with "__ " to distinguish kubeconfig path output from other responses (e.g., errors, list of context, ...) - fmt.Printf("__ %s,%s", *kubeconfigPath, *contextName) + fmt.Printf("__ %s,%s,%s,%s", *kubeconfigPath, *contextName, storeField, sourceField) } diff --git a/cmd/switcher/history.go b/cmd/switcher/history.go index 09733a6e3..9f01bf148 100644 --- a/cmd/switcher/history.go +++ b/cmd/switcher/history.go @@ -34,8 +34,8 @@ var ( return err } - kubeconfigPath, contextName, err := history.SwitchToHistory(stores, config, stateDirectory, noIndex) - reportNewContext(kubeconfigPath, contextName) + kubeconfigPath, contextName, storeName, sourcePath, err := history.SwitchToHistory(stores, config, stateDirectory, noIndex) + reportNewContext(kubeconfigPath, contextName, storeName, sourcePath) return err }, } diff --git a/cmd/switcher/init.go b/cmd/switcher/init.go index 32ab95d09..73836cd19 100644 --- a/cmd/switcher/init.go +++ b/cmd/switcher/init.go @@ -71,6 +71,15 @@ function switch(){ KUBECONFIG_PATH="${remainder%%,*}"; remainder="${remainder#*,}" SELECTED_CONTEXT="${remainder%%,*}"; remainder="${remainder#*,}" + # Parse optional store name and source path (fields 3 and 4) + if [[ "$remainder" == *,* ]]; then + KUBESWITCH_STORE="${remainder%%,*}"; remainder="${remainder#*,}" + KUBESWITCH_PATH="$remainder" + else + KUBESWITCH_STORE="" + KUBESWITCH_PATH="" + fi + if [ -z ${KUBECONFIG_PATH+x} ]; then # KUBECONFIG_PATH is not set printf "%s\n" "$RESPONSE" @@ -91,6 +100,8 @@ function switch(){ fi export KUBECONFIG="$KUBECONFIG_PATH" + export KUBESWITCH_STORE + export KUBESWITCH_PATH printf "switched to context %s\n" "$SELECTED_CONTEXT" }` @@ -147,6 +158,18 @@ function kubeswitch return end + # Parse optional store name and source path + if set -q split_info[3] + set -gx KUBESWITCH_STORE $split_info[3] + else + set -gx KUBESWITCH_STORE "" + end + if set -q split_info[4] + set -gx KUBESWITCH_PATH $split_info[4] + else + set -gx KUBESWITCH_PATH "" + end + if test ! -e "$KUBECONFIG_PATH" echo "ERROR: \"$KUBECONFIG_PATH\" does not exist" return 1 @@ -220,6 +243,17 @@ function kubeswitch { Write-Output $KUBECONFIG_PATH $SELECTED_CONTEXT = $remainder.split(",")[1] + if ($remainder.split(",").Count -ge 3) { + $env:KUBESWITCH_STORE = $remainder.split(",")[2] + } else { + $env:KUBESWITCH_STORE = "" + } + if ($remainder.split(",").Count -ge 4) { + $env:KUBESWITCH_PATH = $remainder.split(",")[3] + } else { + $env:KUBESWITCH_PATH = "" + } + if (-not $KUBECONFIG_PATH) { Write-Output $RESPONSE return diff --git a/cmd/switcher/switcher.go b/cmd/switcher/switcher.go index 056f831a6..1d07628d7 100644 --- a/cmd/switcher/switcher.go +++ b/cmd/switcher/switcher.go @@ -98,29 +98,33 @@ var ( return currentContextCmd.RunE(cmd, args) } + // Handle special cases first if len(args) > 0 { switch args[0] { case "-": return previousContextCmd.RunE(cmd, args[1:]) case ".": return lastContextCmd.RunE(cmd, args[1:]) - default: - return setContextCmd.RunE(cmd, args) } } + // Common path: initialize once, call Switcher with args[0] or "" stores, config, err := initialize() if err != nil { return err } - // config file setting overwrites the command line default (--showPreview true) if showPreview && config.ShowPreview != nil && !*config.ShowPreview { showPreview = false } - kubeconfigPath, contextName, err := pkg.Switcher(stores, config, stateDirectory, noIndex, showPreview) - reportNewContext(kubeconfigPath, contextName) + desiredContext := "" + if len(args) > 0 { + desiredContext = args[0] + } + + kubeconfigPath, contextName, storeName, sourcePath, err := pkg.Switcher(stores, config, stateDirectory, noIndex, showPreview, desiredContext) + reportNewContext(kubeconfigPath, contextName, storeName, sourcePath) return err }, SilenceUsage: true, diff --git a/pkg/main.go b/pkg/main.go index bcc0300d7..8cb0c6551 100644 --- a/pkg/main.go +++ b/pkg/main.go @@ -20,6 +20,7 @@ import ( "sync" "time" + storeutil "github.com/danielfoehrkn/kubeswitch/pkg/store" historyutil "github.com/danielfoehrkn/kubeswitch/pkg/subcommands/history/util" "github.com/hashicorp/go-multierror" "github.com/ktr0731/go-fuzzyfinder" @@ -64,10 +65,26 @@ var ( logger = logrus.New() ) -func Switcher(stores []storetypes.KubeconfigStore, config *types.Config, stateDir string, noIndex, showPreview bool) (*string, *string, error) { +// waitForSearchResults waits for at least one search result or timeout +func waitForSearchResults(timeout time.Duration) { + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + allKubeconfigContextNamesLock.RLock() + count := len(allKubeconfigContextNames) + allKubeconfigContextNamesLock.RUnlock() + if count > 0 { + // Give more time for additional results + time.Sleep(300 * time.Millisecond) + return + } + time.Sleep(50 * time.Millisecond) + } +} + +func Switcher(stores []storetypes.KubeconfigStore, config *types.Config, stateDir string, noIndex, showPreview bool, desiredContext string) (*string, *string, *string, *string, error) { c, err := DoSearch(stores, config, stateDir, noIndex) if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } // here we asynchronously read from the result channel until the wait group is done (call wg.Done for all stores) @@ -115,13 +132,124 @@ func Switcher(stores []storetypes.KubeconfigStore, config *types.Config, stateDi defer logSearchErrors() + // If a desired context was provided, handle exact/partial matching + if desiredContext != "" { + // Wait for search results to populate + waitForSearchResults(10 * time.Second) + + // Take a snapshot of current contexts and aliases + allKubeconfigContextNamesLock.RLock() + contextsCopy := make([]string, len(allKubeconfigContextNames)) + copy(contextsCopy, allKubeconfigContextNames) + allKubeconfigContextNamesLock.RUnlock() + + aliasToContextLock.RLock() + aliasesCopy := make(map[string]string, len(aliasToContext)) + for k, v := range aliasToContext { + aliasesCopy[k] = v + } + aliasToContextLock.RUnlock() + + // Check for exact match (case-sensitive) in contexts or aliases + exactMatch := "" + for _, name := range contextsCopy { + if name == desiredContext { + exactMatch = name + break + } + } + if exactMatch == "" { + for alias := range aliasesCopy { + if alias == desiredContext { + exactMatch = alias + break + } + } + } + + // If exact match, switch immediately without showing picker + if exactMatch != "" { + kubeconfigPath := readFromContextToPathMapping(exactMatch) + storeID := readFromPathToStoreID(kubeconfigPath) + store := kindToStore[storeID] + tags := readFromPathToTagsMapping(kubeconfigPath) + + kubeconfigData, err := store.GetKubeconfigForPath(kubeconfigPath, tags) + if err != nil { + return nil, nil, nil, nil, err + } + + kubeconfig, err := kubeconfigutil.NewKubeconfig(kubeconfigData) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("failed to parse selected kubeconfig: %v", err) + } + + selectedContext := exactMatch + contextForHistory := selectedContext + + if len(store.GetContextPrefix(kubeconfigPath)) > 0 && strings.HasPrefix(selectedContext, store.GetContextPrefix(kubeconfigPath)) { + selectedContext = strings.TrimPrefix(selectedContext, fmt.Sprintf("%s/", store.GetContextPrefix(kubeconfigPath))) + } + + if err := kubeconfig.SetContext(selectedContext, aliasutil.GetContextForAlias(selectedContext, aliasToContext), store.GetContextPrefix(kubeconfigPath)); err != nil { + return nil, nil, nil, nil, err + } + + if err := kubeconfig.SetKubeswitchContext(contextForHistory); err != nil { + return nil, nil, nil, nil, err + } + + tempKubeconfigPath, err := kubeconfig.WriteKubeconfigFile() + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("failed to write temporary kubeconfig file: %v", err) + } + + ns, err := kubeconfig.NamespaceOfContext(kubeconfig.GetCurrentContext()) + if err != nil { + logger.Warnf("failed to get namespace: %v", err) + } else if err := historyutil.AppendToHistory(contextForHistory, ns); err != nil { + logger.Warnf("failed to append to history: %v", err) + } + + storeName := storeutil.DeriveStoreName(store, kubeconfigPath) + return &tempKubeconfigPath, &selectedContext, &storeName, &kubeconfigPath, nil + } + + // No exact match - filter by substring (case-insensitive) + lowerDesired := strings.ToLower(desiredContext) + var partialMatches []string + seen := make(map[string]bool) + + for _, name := range contextsCopy { + if strings.Contains(strings.ToLower(name), lowerDesired) { + partialMatches = append(partialMatches, name) + seen[name] = true + } + } + + for alias := range aliasesCopy { + if strings.Contains(strings.ToLower(alias), lowerDesired) && !seen[alias] { + partialMatches = append(partialMatches, alias) + } + } + + if len(partialMatches) == 0 { + return nil, nil, nil, nil, fmt.Errorf("no contexts matching %q", desiredContext) + } + + // Replace global list with filtered matches for picker + allKubeconfigContextNamesLock.Lock() + allKubeconfigContextNames = partialMatches + allKubeconfigContextNamesLock.Unlock() + } + kubeconfigPath, selectedContext, err := showFuzzySearch(kindToStore, showPreview) if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } if len(kubeconfigPath) == 0 { - return nil, nil, nil + return nil, nil, nil, nil, nil } // map back kubeconfig path to the store kind @@ -136,12 +264,12 @@ func Switcher(stores []storetypes.KubeconfigStore, config *types.Config, stateDi // use the store to get the kubeconfig for the selected kubeconfig path kubeconfigData, err := store.GetKubeconfigForPath(kubeconfigPath, tags) if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } kubeconfig, err := kubeconfigutil.NewKubeconfig(kubeconfigData) if err != nil { - return nil, nil, fmt.Errorf("failed to parse selected kubeconfig. Please check if this file is a valid kubeconfig: %v", err) + return nil, nil, nil, nil, fmt.Errorf("failed to parse selected kubeconfig. Please check if this file is a valid kubeconfig: %v", err) } // save the original selected context for the history @@ -154,17 +282,17 @@ func Switcher(stores []storetypes.KubeconfigStore, config *types.Config, stateDi } if err := kubeconfig.SetContext(selectedContext, aliasutil.GetContextForAlias(selectedContext, aliasToContext), store.GetContextPrefix(kubeconfigPath)); err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } if err := kubeconfig.SetKubeswitchContext(contextForHistory); err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } // write a temporary kubeconfig file and return the path tempKubeconfigPath, err := kubeconfig.WriteKubeconfigFile() if err != nil { - return nil, nil, fmt.Errorf("failed to write temporary kubeconfig file: %v", err) + return nil, nil, nil, nil, fmt.Errorf("failed to write temporary kubeconfig file: %v", err) } // get namespace for current context @@ -175,7 +303,8 @@ func Switcher(stores []storetypes.KubeconfigStore, config *types.Config, stateDi logger.Warnf("failed to append context to history file: %v", err) } - return &tempKubeconfigPath, &selectedContext, nil + storeName := storeutil.DeriveStoreName(store, kubeconfigPath) + return &tempKubeconfigPath, &selectedContext, &storeName, &kubeconfigPath, nil } // writeIndex tries to write the Index file for the kubeconfig store diff --git a/pkg/store/store_name.go b/pkg/store/store_name.go new file mode 100644 index 000000000..b578ec194 --- /dev/null +++ b/pkg/store/store_name.go @@ -0,0 +1,21 @@ +package store + +import ( + "path/filepath" + + storetypes "github.com/danielfoehrkn/kubeswitch/pkg/store/types" + "github.com/danielfoehrkn/kubeswitch/types" +) + +// DeriveStoreName returns a human-readable name for the store that provided +// the kubeconfig at the given path. Priority: store ID > parent directory +// (filesystem) > store kind. +func DeriveStoreName(s storetypes.KubeconfigStore, path string) string { + if cfg := s.GetStoreConfig(); cfg.ID != nil && *cfg.ID != "" { + return *cfg.ID + } + if s.GetKind() == types.StoreKindFilesystem { + return filepath.Base(filepath.Dir(path)) + } + return string(s.GetKind()) +} diff --git a/pkg/store/store_name_test.go b/pkg/store/store_name_test.go new file mode 100644 index 000000000..bc0bb770b --- /dev/null +++ b/pkg/store/store_name_test.go @@ -0,0 +1,76 @@ +package store + +import ( + "testing" + + storetypes "github.com/danielfoehrkn/kubeswitch/pkg/store/types" + "github.com/danielfoehrkn/kubeswitch/types" +) + +func TestDeriveStoreName(t *testing.T) { + tests := []struct { + name string + store storetypes.KubeconfigStore + path string + expected string + }{ + { + name: "uses store ID when set", + store: &fakeStore{id: strPtr("my-store"), kind: "filesystem"}, + path: "/home/user/.kube/production/config", + expected: "my-store", + }, + { + name: "filesystem falls back to parent directory name when ID nil", + store: &fakeStore{id: nil, kind: "filesystem"}, + path: "/home/user/.kube/production/config", + expected: "production", + }, + { + name: "filesystem falls back to parent directory name when ID empty", + store: &fakeStore{id: strPtr(""), kind: "filesystem"}, + path: "/home/user/.kube/production/config", + expected: "production", + }, + { + name: "non-filesystem falls back to store kind", + store: &fakeStore{id: nil, kind: "vault"}, + path: "vault/secret/path", + expected: "vault", + }, + { + name: "nested filesystem path without ID", + store: &fakeStore{id: nil, kind: "filesystem"}, + path: "/home/user/.kube/team/project/config", + expected: "project", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := DeriveStoreName(tt.store, tt.path) + if got != tt.expected { + t.Errorf("DeriveStoreName() = %q, want %q", got, tt.expected) + } + }) + } +} + +type fakeStore struct { + storetypes.KubeconfigStore + id *string + kind string +} + +func (f *fakeStore) GetKind() types.StoreKind { + return types.StoreKind(f.kind) +} + +func strPtr(s string) *string { return &s } + +func (f *fakeStore) GetStoreConfig() types.KubeconfigStore { + return types.KubeconfigStore{ + ID: f.id, + Kind: types.StoreKind(f.kind), + } +} diff --git a/pkg/subcommands/exec/exec.go b/pkg/subcommands/exec/exec.go index 22d91ae85..32feeae7d 100644 --- a/pkg/subcommands/exec/exec.go +++ b/pkg/subcommands/exec/exec.go @@ -53,7 +53,7 @@ func ExecuteCommand(pattern string, command []string, stores []storetypes.Kubeco } for _, context := range contexts { - tmpKubeconfigFile, _, err := setcontext.SetContext(context, stores, config, stateDir, noIndex, false) + tmpKubeconfigFile, _, _, _, err := setcontext.SetContext(context, stores, config, stateDir, noIndex, false) if err != nil { return err } diff --git a/pkg/subcommands/history/history.go b/pkg/subcommands/history/history.go index 6937a6974..ed4d84dde 100644 --- a/pkg/subcommands/history/history.go +++ b/pkg/subcommands/history/history.go @@ -30,10 +30,10 @@ import ( var logger = logrus.New() -func SwitchToHistory(stores []storetypes.KubeconfigStore, config *types.Config, stateDir string, noIndex bool) (*string, *string, error) { +func SwitchToHistory(stores []storetypes.KubeconfigStore, config *types.Config, stateDir string, noIndex bool) (*string, *string, *string, *string, error) { history, err := util.ReadHistory() if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } historyLength := len(history) @@ -84,32 +84,32 @@ func SwitchToHistory(stores []storetypes.KubeconfigStore, config *types.Config, }) if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } context, ns, err := util.ParseHistoryEntry(history[idx]) if err != nil { - return nil, nil, fmt.Errorf("failed to set namespace: %v", err) + return nil, nil, nil, nil, fmt.Errorf("failed to set namespace: %v", err) } // TODO: only switch context if the current context is not already set // requires to first check if a kubeconfig is already set (setcontext always creates a new file) // do not append to history as the old namespace will be added (only add history after changing the namespace) - tmpKubeconfigFile, _, err := setcontext.SetContext(*context, stores, config, stateDir, noIndex, false) + tmpKubeconfigFile, _, storeName, sourcePath, err := setcontext.SetContext(*context, stores, config, stateDir, noIndex, false) if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } // old history entry that does not include a namespace if ns == nil { - return tmpKubeconfigFile, nil, nil + return tmpKubeconfigFile, nil, storeName, sourcePath, nil } if err := setNamespace(*ns, *tmpKubeconfigFile); err != nil { - return tmpKubeconfigFile, nil, err + return tmpKubeconfigFile, nil, storeName, sourcePath, err } - return tmpKubeconfigFile, context, util.AppendToHistory(*context, *ns) + return tmpKubeconfigFile, context, storeName, sourcePath, util.AppendToHistory(*context, *ns) } func setNamespace(ns string, tmpKubeconfigFile string) error { @@ -131,14 +131,14 @@ func setNamespace(ns string, tmpKubeconfigFile string) error { // SetPreviousContext sets the previously used context from the history (position 1) // does not add a history entry -func SetPreviousContext(stores []storetypes.KubeconfigStore, config *types.Config, stateDir string, noIndex bool) (*string, *string, error) { +func SetPreviousContext(stores []storetypes.KubeconfigStore, config *types.Config, stateDir string, noIndex bool) (*string, *string, *string, *string, error) { history, err := util.ReadHistory() if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } if len(history) == 0 { - return nil, nil, nil + return nil, nil, nil, nil, nil } var position int @@ -150,48 +150,48 @@ func SetPreviousContext(stores []storetypes.KubeconfigStore, config *types.Confi context, ns, err := util.ParseHistoryEntry(history[position]) if err != nil { - return nil, nil, fmt.Errorf("failed to set previous context: %v", err) + return nil, nil, nil, nil, fmt.Errorf("failed to set previous context: %v", err) } - tmpKubeconfigFile, _, err := setcontext.SetContext(*context, stores, config, stateDir, noIndex, false) + tmpKubeconfigFile, _, storeName, sourcePath, err := setcontext.SetContext(*context, stores, config, stateDir, noIndex, false) if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } // old history entry that does not include a namespace if ns == nil { - return tmpKubeconfigFile, nil, nil + return tmpKubeconfigFile, nil, storeName, sourcePath, nil } - return tmpKubeconfigFile, context, setNamespace(*ns, *tmpKubeconfigFile) + return tmpKubeconfigFile, context, storeName, sourcePath, setNamespace(*ns, *tmpKubeconfigFile) } // SetLastContext sets the last used context from the history (position 0) // does not add a history entry -func SetLastContext(stores []storetypes.KubeconfigStore, config *types.Config, stateDir string, noIndex bool) (*string, *string, error) { +func SetLastContext(stores []storetypes.KubeconfigStore, config *types.Config, stateDir string, noIndex bool) (*string, *string, *string, *string, error) { history, err := util.ReadHistory() if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } if len(history) == 0 { - return nil, nil, nil + return nil, nil, nil, nil, nil } context, ns, err := util.ParseHistoryEntry(history[0]) if err != nil { - return nil, nil, fmt.Errorf("failed to set previous context: %v", err) + return nil, nil, nil, nil, fmt.Errorf("failed to set previous context: %v", err) } - tmpKubeconfigFile, _, err := setcontext.SetContext(*context, stores, config, stateDir, noIndex, false) + tmpKubeconfigFile, _, storeName, sourcePath, err := setcontext.SetContext(*context, stores, config, stateDir, noIndex, false) if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } // old history entry that does not include a namespace if ns == nil { - return tmpKubeconfigFile, nil, nil + return tmpKubeconfigFile, nil, storeName, sourcePath, nil } - return tmpKubeconfigFile, context, setNamespace(*ns, *tmpKubeconfigFile) + return tmpKubeconfigFile, context, storeName, sourcePath, setNamespace(*ns, *tmpKubeconfigFile) } diff --git a/pkg/subcommands/set-context/set_context.go b/pkg/subcommands/set-context/set_context.go index 8dfd8aef0..6e16054c7 100644 --- a/pkg/subcommands/set-context/set_context.go +++ b/pkg/subcommands/set-context/set_context.go @@ -22,6 +22,7 @@ import ( "github.com/sirupsen/logrus" "github.com/danielfoehrkn/kubeswitch/pkg" + storeutil "github.com/danielfoehrkn/kubeswitch/pkg/store" storetypes "github.com/danielfoehrkn/kubeswitch/pkg/store/types" historyutil "github.com/danielfoehrkn/kubeswitch/pkg/subcommands/history/util" kubeconfigutil "github.com/danielfoehrkn/kubeswitch/pkg/util/kubectx_copied" @@ -30,10 +31,10 @@ import ( var logger = logrus.New() -func SetContext(desiredContext string, stores []storetypes.KubeconfigStore, config *types.Config, stateDir string, noIndex bool, appendToHistory bool) (*string, *string, error) { +func SetContext(desiredContext string, stores []storetypes.KubeconfigStore, config *types.Config, stateDir string, noIndex bool, appendToHistory bool) (*string, *string, *string, *string, error) { c, err := pkg.DoSearch(stores, config, stateDir, noIndex) if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } var mError *multierror.Error @@ -61,12 +62,12 @@ func SetContext(desiredContext string, stores []storetypes.KubeconfigStore, conf if desiredContext == discoveredContext.Name || matchesContextWithoutPrefix || desiredContext == discoveredContext.Alias { kubeconfigData, err := kubeconfigStore.GetKubeconfigForPath(discoveredContext.Path, discoveredContext.Tags) if err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } kubeconfig, err := kubeconfigutil.NewKubeconfig(kubeconfigData) if err != nil { - return nil, nil, fmt.Errorf("failed to parse kubeconfig: %v", err) + return nil, nil, nil, nil, fmt.Errorf("failed to parse kubeconfig: %v", err) } originalContextBeforeAlias := "" @@ -75,36 +76,38 @@ func SetContext(desiredContext string, stores []storetypes.KubeconfigStore, conf } if err := kubeconfig.SetContext(contextWithoutPrefix, originalContextBeforeAlias, kubeconfigStore.GetContextPrefix(discoveredContext.Path)); err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } if err := kubeconfig.SetKubeswitchContext(desiredContext); err != nil { - return nil, nil, err + return nil, nil, nil, nil, err } tempKubeconfigPath, err := kubeconfig.WriteKubeconfigFile() if err != nil { - return nil, nil, fmt.Errorf("failed to write temporary kubeconfig file: %v", err) + return nil, nil, nil, nil, fmt.Errorf("failed to write temporary kubeconfig file: %v", err) } if appendToHistory { // get namespace for current context ns, err := kubeconfig.NamespaceOfContext(kubeconfig.GetCurrentContext()) if err != nil { - return nil, nil, fmt.Errorf("failed to get namespace of current context: %v", err) + return nil, nil, nil, nil, fmt.Errorf("failed to get namespace of current context: %v", err) } if err := historyutil.AppendToHistory(desiredContext, ns); err != nil { logger.Warnf("failed to append context to history file: %v", err) } } - return &tempKubeconfigPath, &desiredContext, nil + storeName := storeutil.DeriveStoreName(kubeconfigStore, discoveredContext.Path) + sourcePath := discoveredContext.Path + return &tempKubeconfigPath, &desiredContext, &storeName, &sourcePath, nil } } if mError != nil { - return nil, nil, fmt.Errorf("context with name %q not found. Possibly due to errors: %v", desiredContext, mError.Error()) + return nil, nil, nil, nil, fmt.Errorf("context with name %q not found. Possibly due to errors: %v", desiredContext, mError.Error()) } - return nil, nil, fmt.Errorf("context with name %q not found", desiredContext) + return nil, nil, nil, nil, fmt.Errorf("context with name %q not found", desiredContext) }