From 4501128a89360d9aa306dcbb9d7fa94e6fba1668 Mon Sep 17 00:00:00 2001 From: Nick Santos Date: Mon, 31 Aug 2020 13:11:45 -0400 Subject: [PATCH] cli: add a --context flag for setting kube context. Fixes https://github.com/tilt-dev/tilt/issues/3296 (#3734) --- internal/cli/ci.go | 1 + internal/cli/doctor.go | 1 + internal/cli/down.go | 1 + internal/cli/flags.go | 11 +++++ internal/cli/tiltfile_result.go | 1 + internal/cli/up.go | 1 + internal/cli/wire.go | 3 +- internal/cli/wire_gen.go | 78 ++++++++++++++++++++------------- internal/engine/wire.go | 1 + internal/engine/wire_gen.go | 6 ++- internal/k8s/client.go | 7 ++- internal/k8s/env.go | 20 +++++++-- 12 files changed, 91 insertions(+), 40 deletions(-) diff --git a/internal/cli/ci.go b/internal/cli/ci.go index be8f31692d..f9247c8a27 100644 --- a/internal/cli/ci.go +++ b/internal/cli/ci.go @@ -46,6 +46,7 @@ While Tilt is running, you can view the UI at %s:%d addStartServerFlags(cmd) addDevServerFlags(cmd) addTiltfileFlag(cmd, &c.fileName) + addKubeContextFlag(cmd) cmd.Flags().BoolVar(&logActionsFlag, "logactions", false, "log all actions and state changes") cmd.Flags().Lookup("logactions").Hidden = true diff --git a/internal/cli/doctor.go b/internal/cli/doctor.go index dbad654b4c..67f6563610 100644 --- a/internal/cli/doctor.go +++ b/internal/cli/doctor.go @@ -25,6 +25,7 @@ func (c *doctorCmd) register() *cobra.Command { Use: "doctor", Short: "Print diagnostic information about the Tilt environment, for filing bug reports", } + addKubeContextFlag(cmd) return cmd } diff --git a/internal/cli/down.go b/internal/cli/down.go index 8b67fccad2..e0fa6f30fb 100644 --- a/internal/cli/down.go +++ b/internal/cli/down.go @@ -52,6 +52,7 @@ In that case, see https://tilt.dev/user_config.html and/or comments in your Tilt } addTiltfileFlag(cmd, &c.fileName) + addKubeContextFlag(cmd) cmd.Flags().BoolVar(&c.deleteNamespaces, "delete-namespaces", false, "delete namespaces defined in the Tiltfile (by default, don't)") return cmd diff --git a/internal/cli/flags.go b/internal/cli/flags.go index 5fd82b2ecd..e5ae93059c 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -3,6 +3,7 @@ package cli import ( "github.com/spf13/cobra" + "github.com/tilt-dev/tilt/internal/k8s" "github.com/tilt-dev/tilt/internal/tiltfile" ) @@ -13,6 +14,10 @@ func addTiltfileFlag(cmd *cobra.Command, s *string) { cmd.Flags().StringVarP(s, "file", "f", tiltfile.FileName, "Path to Tiltfile") } +func addKubeContextFlag(cmd *cobra.Command) { + cmd.Flags().StringVar(&kubeContextOverride, "context", "", "Kubernetes context override. Equivalent to kubectl --context") +} + // For commands that talk to the web server. func addConnectServerFlags(cmd *cobra.Command) { cmd.Flags().IntVar(&webPort, "port", DefaultWebPort, "Port for the Tilt HTTP server. Only necessary if you started Tilt with --port.") @@ -29,3 +34,9 @@ func addDevServerFlags(cmd *cobra.Command) { cmd.Flags().IntVar(&webDevPort, "webdev-port", DefaultWebDevPort, "Port for the Tilt Dev Webpack server. Only applies when using --web-mode=local") cmd.Flags().Var(&webModeFlag, "web-mode", "Values: local, prod. Controls whether to use prod assets or a local dev server. (If flag not specified: if Tilt was built from source, it will use a local asset server; otherwise, prod assets.)") } + +var kubeContextOverride string + +func ProvideKubeContextOverride() k8s.KubeContextOverride { + return k8s.KubeContextOverride(kubeContextOverride) +} diff --git a/internal/cli/tiltfile_result.go b/internal/cli/tiltfile_result.go index c757be6dbf..79ef17f23d 100644 --- a/internal/cli/tiltfile_result.go +++ b/internal/cli/tiltfile_result.go @@ -63,6 +63,7 @@ Run with -v | --verbose to print Tiltfile execution logs on stderr, regardless o } addTiltfileFlag(cmd, &c.fileName) + addKubeContextFlag(cmd) cmd.Flags().BoolVarP(&c.builtinTimings, "builtin-timings", "b", false, "If true, print timing data for Tiltfile builtin calls instead of Tiltfile result JSON") cmd.Flags().DurationVar(&c.durThreshold, "dur-threshold", 0, "Only compatible with Builtin Timings mode. Should be a Go duration string. If passed, only print information about builtin calls lasting this duration and longer.") diff --git a/internal/cli/up.go b/internal/cli/up.go index 62fe946680..649e9fdc5a 100644 --- a/internal/cli/up.go +++ b/internal/cli/up.go @@ -89,6 +89,7 @@ local resources--i.e. those using serve_cmd--are terminated when you exit Tilt. addStartServerFlags(cmd) addDevServerFlags(cmd) addTiltfileFlag(cmd, &c.fileName) + addKubeContextFlag(cmd) cmd.Flags().Lookup("logactions").Hidden = true cmd.Flags().StringVar(&c.outputSnapshotOnExit, "output-snapshot-on-exit", "", "If specified, Tilt will dump a snapshot of its state to the specified path when it exits") diff --git a/internal/cli/wire.go b/internal/cli/wire.go index 5e85b38fc0..f156486ded 100644 --- a/internal/cli/wire.go +++ b/internal/cli/wire.go @@ -60,7 +60,8 @@ var K8sWireSet = wire.NewSet( k8s.ProvideContainerRuntime, k8s.ProvideServerVersion, k8s.ProvideK8sClient, - k8s.ProvideOwnerFetcher) + k8s.ProvideOwnerFetcher, + ProvideKubeContextOverride) var BaseWireSet = wire.NewSet( K8sWireSet, diff --git a/internal/cli/wire_gen.go b/internal/cli/wire_gen.go index 9472456da0..ce77349767 100644 --- a/internal/cli/wire_gen.go +++ b/internal/cli/wire_gen.go @@ -58,8 +58,9 @@ import ( // Injectors from wire.go: func wireTiltfileResult(ctx context.Context, analytics2 *analytics.TiltAnalytics, subcommand model.TiltSubcommand) (cmdTiltfileResultDeps, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return cmdTiltfileResultDeps{}, err } @@ -96,8 +97,9 @@ var ( ) func wireDockerPrune(ctx context.Context, analytics2 *analytics.TiltAnalytics, subcommand model.TiltSubcommand) (dpDeps, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return dpDeps{}, err } @@ -154,8 +156,9 @@ func wireCmdUp(ctx context.Context, analytics3 *analytics.TiltAnalytics, cmdTags openInput := _wireOpenInputValue openURL := _wireOpenURLValue terminalPrompt := prompt.NewTerminalPrompt(analytics3, openInput, openURL, stdout, modelWebHost, webURL) - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return CmdUpDeps{}, err } @@ -310,8 +313,9 @@ func wireCmdCI(ctx context.Context, analytics3 *analytics.TiltAnalytics, subcomm openInput := _wireOpenInputValue openURL := _wireOpenURLValue terminalPrompt := prompt.NewTerminalPrompt(analytics3, openInput, openURL, stdout, modelWebHost, webURL) - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return CmdCIDeps{}, err } @@ -445,8 +449,9 @@ var ( ) func wireKubeContext(ctx context.Context) (k8s.KubeContext, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return "", err } @@ -458,8 +463,9 @@ func wireKubeContext(ctx context.Context) (k8s.KubeContext, error) { } func wireKubeConfig(ctx context.Context) (*api.Config, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return nil, err } @@ -467,8 +473,9 @@ func wireKubeConfig(ctx context.Context) (*api.Config, error) { } func wireEnv(ctx context.Context) (k8s.Env, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return "", err } @@ -477,14 +484,16 @@ func wireEnv(ctx context.Context) (k8s.Env, error) { } func wireNamespace(ctx context.Context) (k8s.Namespace, error) { - clientConfig := k8s.ProvideClientConfig() + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) namespace := k8s.ProvideConfigNamespace(clientConfig) return namespace, nil } func wireClusterName(ctx context.Context) (k8s.ClusterName, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return "", err } @@ -493,8 +502,9 @@ func wireClusterName(ctx context.Context) (k8s.ClusterName, error) { } func wireRuntime(ctx context.Context) (container.Runtime, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return "", err } @@ -516,8 +526,9 @@ func wireRuntime(ctx context.Context) (container.Runtime, error) { } func wireK8sClient(ctx context.Context) (k8s.Client, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return nil, err } @@ -538,7 +549,8 @@ func wireK8sClient(ctx context.Context) (k8s.Client, error) { } func wireK8sVersion(ctx context.Context) (*version2.Info, error) { - clientConfig := k8s.ProvideClientConfig() + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) restConfigOrError := k8s.ProvideRESTConfig(clientConfig) clientsetOrError := k8s.ProvideClientset(restConfigOrError) info, err := k8s.ProvideServerVersion(clientsetOrError) @@ -549,8 +561,9 @@ func wireK8sVersion(ctx context.Context) (*version2.Info, error) { } func wireDockerClusterClient(ctx context.Context) (docker.ClusterClient, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return nil, err } @@ -579,8 +592,9 @@ func wireDockerClusterClient(ctx context.Context) (docker.ClusterClient, error) } func wireDockerLocalClient(ctx context.Context) (docker.LocalClient, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return nil, err } @@ -605,8 +619,9 @@ func wireDockerLocalClient(ctx context.Context) (docker.LocalClient, error) { } func wireDownDeps(ctx context.Context, tiltAnalytics *analytics.TiltAnalytics, subcommand model.TiltSubcommand) (DownDeps, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return DownDeps{}, err } @@ -652,8 +667,9 @@ func wireLogsDeps(ctx context.Context, tiltAnalytics *analytics.TiltAnalytics, s } func wireDumpImageDeployRefDeps(ctx context.Context) (DumpImageDeployRefDeps, error) { - clientConfig := k8s.ProvideClientConfig() - apiConfig, err := k8s.ProvideKubeConfig(clientConfig) + k8sKubeContextOverride := ProvideKubeContextOverride() + clientConfig := k8s.ProvideClientConfig(k8sKubeContextOverride) + apiConfig, err := k8s.ProvideKubeConfig(clientConfig, k8sKubeContextOverride) if err != nil { return DumpImageDeployRefDeps{}, err } @@ -691,7 +707,7 @@ func wireDumpImageDeployRefDeps(ctx context.Context) (DumpImageDeployRefDeps, er // wire.go: -var K8sWireSet = wire.NewSet(k8s.ProvideEnv, k8s.ProvideClusterName, k8s.ProvideKubeContext, k8s.ProvideKubeConfig, k8s.ProvideClientConfig, k8s.ProvideClientset, k8s.ProvideRESTConfig, k8s.ProvidePortForwardClient, k8s.ProvideConfigNamespace, k8s.ProvideKubectlRunner, k8s.ProvideContainerRuntime, k8s.ProvideServerVersion, k8s.ProvideK8sClient, k8s.ProvideOwnerFetcher) +var K8sWireSet = wire.NewSet(k8s.ProvideEnv, k8s.ProvideClusterName, k8s.ProvideKubeContext, k8s.ProvideKubeConfig, k8s.ProvideClientConfig, k8s.ProvideClientset, k8s.ProvideRESTConfig, k8s.ProvidePortForwardClient, k8s.ProvideConfigNamespace, k8s.ProvideKubectlRunner, k8s.ProvideContainerRuntime, k8s.ProvideServerVersion, k8s.ProvideK8sClient, k8s.ProvideOwnerFetcher, ProvideKubeContextOverride) var BaseWireSet = wire.NewSet( K8sWireSet, tiltfile.WireSet, provideKubectlLogLevel, docker.SwitchWireSet, dockercompose.NewDockerComposeClient, clockwork.NewRealClock, engine.DeployerWireSet, runtimelog.NewPodLogManager, portforward.NewController, engine.NewBuildController, local.ProvideExecer, local.NewController, k8swatch.NewPodWatcher, k8swatch.NewServiceWatcher, k8swatch.NewEventWatchManager, configs.NewConfigsController, telemetry.NewController, dcwatch.NewEventWatcher, runtimelog.NewDockerComposeLogManager, engine.NewProfilerManager, cloud.WireSet, cloudurl.ProvideAddress, k8srollout.NewPodMonitor, telemetry.NewStartTracker, exit.NewController, provideClock, hud.WireSet, prompt.WireSet, provideLogActions, store.NewStore, wire.Bind(new(store.RStore), new(*store.Store)), dockerprune.NewDockerPruner, provideTiltInfo, engine.ProvideSubscribers, engine.NewUpper, analytics2.NewAnalyticsUpdater, analytics2.ProvideAnalyticsReporter, provideUpdateModeFlag, fswatch.NewGitManager, fswatch.NewWatchManager, fswatch.ProvideFsWatcherMaker, fswatch.ProvideTimerMaker, provideWebVersion, diff --git a/internal/engine/wire.go b/internal/engine/wire.go index 47860cddfa..e3f0934e06 100644 --- a/internal/engine/wire.go +++ b/internal/engine/wire.go @@ -123,6 +123,7 @@ func provideDockerComposeBuildAndDeployer( // EnvNone ensures that we get an exploding k8s client. wire.Value(k8s.Env(k8s.EnvNone)), + wire.Value(k8s.KubeContextOverride("")), k8s.ProvideClientConfig, k8s.ProvideConfigNamespace, k8s.ProvideKubeContext, diff --git a/internal/engine/wire_gen.go b/internal/engine/wire_gen.go index 12e8a144b2..c1fcc7e781 100644 --- a/internal/engine/wire_gen.go +++ b/internal/engine/wire_gen.go @@ -102,12 +102,13 @@ func provideDockerComposeBuildAndDeployer(ctx context.Context, dcCli dockercompo execCustomBuilder := build.NewExecCustomBuilder(dCli, clock) updateModeFlag := _wireBuildcontrolUpdateModeFlagValue env := _wireEnvValue - clientConfig := k8s.ProvideClientConfig() + kubeContextOverride := _wireKubeContextOverrideValue + clientConfig := k8s.ProvideClientConfig(kubeContextOverride) restConfigOrError := k8s.ProvideRESTConfig(clientConfig) clientsetOrError := k8s.ProvideClientset(restConfigOrError) portForwardClient := k8s.ProvidePortForwardClient(restConfigOrError, clientsetOrError) namespace := k8s.ProvideConfigNamespace(clientConfig) - config, err := k8s.ProvideKubeConfig(clientConfig) + config, err := k8s.ProvideKubeConfig(clientConfig, kubeContextOverride) if err != nil { return nil, err } @@ -132,6 +133,7 @@ func provideDockerComposeBuildAndDeployer(ctx context.Context, dcCli dockercompo var ( _wireBuildcontrolUpdateModeFlagValue = buildcontrol.UpdateModeFlag(buildcontrol.UpdateModeAuto) _wireEnvValue = k8s.Env(k8s.EnvNone) + _wireKubeContextOverrideValue = k8s.KubeContextOverride("") ) // wire.go: diff --git a/internal/k8s/client.go b/internal/k8s/client.go index dd8af22116..663036d24a 100644 --- a/internal/k8s/client.go +++ b/internal/k8s/client.go @@ -40,6 +40,7 @@ type PodID string type NodeID string type ServiceName string type KubeContext string +type KubeContextOverride string // NOTE(nick): This isn't right. DefaultNamespace is a function of your kubectl context. const DefaultNamespace = Namespace("default") @@ -474,11 +475,13 @@ func ProvideClientset(cfg RESTConfigOrError) ClientsetOrError { return ClientsetOrError{Clientset: clientset, Error: err} } -func ProvideClientConfig() clientcmd.ClientConfig { +func ProvideClientConfig(contextOverride KubeContextOverride) clientcmd.ClientConfig { rules := clientcmd.NewDefaultClientConfigLoadingRules() rules.DefaultClientConfig = &clientcmd.DefaultClientConfig - overrides := &clientcmd.ConfigOverrides{} + overrides := &clientcmd.ConfigOverrides{ + CurrentContext: string(contextOverride), + } return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( rules, overrides) diff --git a/internal/k8s/env.go b/internal/k8s/env.go index c1cc8d88cd..c310131089 100644 --- a/internal/k8s/env.go +++ b/internal/k8s/env.go @@ -46,14 +46,26 @@ func ProvideKubeContext(config *api.Config) (KubeContext, error) { return KubeContext(config.CurrentContext), nil } -func ProvideKubeConfig(clientLoader clientcmd.ClientConfig) (*api.Config, error) { - access := clientLoader.ConfigAccess() - config, err := access.GetStartingConfig() +func ProvideKubeConfig(clientLoader clientcmd.ClientConfig, contextOverride KubeContextOverride) (*api.Config, error) { + config, err := clientLoader.RawConfig() if err != nil { return nil, errors.Wrap(err, "Loading Kubernetes current-context") } - return config, nil + // NOTE(nick): The RawConfig() accessor doesn't handle overrides. + // The other accessors do. So we do what ClientConfig does internally, and + // apply the overrides ourselves. + if contextOverride != "" { + config.CurrentContext = string(contextOverride) + + // If the user explicitly passed an override, validate it. + err := clientcmd.ConfirmUsable(config, string(contextOverride)) + if err != nil { + return nil, errors.Wrap(err, "Overriding Kubernetes context") + } + } + + return &config, nil } func ProvideClusterName(ctx context.Context, config *api.Config) ClusterName {