Skip to content

Commit 7a47f16

Browse files
authored
Merge pull request #10844 from afbjorklund/kubectl-ssh
Allow running kubectl over the ssh connection
2 parents afe31b1 + 15e422d commit 7a47f16

File tree

4 files changed

+88
-18
lines changed

4 files changed

+88
-18
lines changed

cmd/minikube/cmd/generate-docs.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import (
2727
"k8s.io/minikube/pkg/minikube/style"
2828
)
2929

30-
var path string
30+
var docsPath string
3131

3232
// generateDocs represents the generate-docs command
3333
var generateDocs = &cobra.Command{
@@ -38,20 +38,20 @@ var generateDocs = &cobra.Command{
3838
Hidden: true,
3939
Run: func(cmd *cobra.Command, args []string) {
4040
// if directory does not exist
41-
docsPath, err := os.Stat(path)
42-
if err != nil || !docsPath.IsDir() {
41+
st, err := os.Stat(docsPath)
42+
if err != nil || !st.IsDir() {
4343
exit.Message(reason.Usage, "Unable to generate the documentation. Please ensure that the path specified is a directory, exists & you have permission to write to it.")
4444
}
4545

4646
// generate docs
47-
if err := generate.Docs(RootCmd, path); err != nil {
47+
if err := generate.Docs(RootCmd, docsPath); err != nil {
4848
exit.Error(reason.InternalGenerateDocs, "Unable to generate docs", err)
4949
}
50-
out.Step(style.Documentation, "Docs have been saved at - {{.path}}", out.V{"path": path})
50+
out.Step(style.Documentation, "Docs have been saved at - {{.path}}", out.V{"path": docsPath})
5151
},
5252
}
5353

5454
func init() {
55-
generateDocs.Flags().StringVar(&path, "path", "", "The path on the file system where the docs in markdown need to be saved")
55+
generateDocs.Flags().StringVar(&docsPath, "path", "", "The path on the file system where the docs in markdown need to be saved")
5656
RootCmd.AddCommand(generateDocs)
5757
}

cmd/minikube/cmd/kubectl.go

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,22 @@ import (
2020
"fmt"
2121
"os"
2222
"os/exec"
23+
"path"
2324
"syscall"
2425

2526
"github.com/spf13/cobra"
2627
"k8s.io/klog/v2"
2728
"k8s.io/minikube/pkg/minikube/config"
2829
"k8s.io/minikube/pkg/minikube/constants"
30+
"k8s.io/minikube/pkg/minikube/machine"
31+
"k8s.io/minikube/pkg/minikube/mustload"
2932
"k8s.io/minikube/pkg/minikube/node"
3033
"k8s.io/minikube/pkg/minikube/out"
34+
"k8s.io/minikube/pkg/minikube/vmpath"
35+
)
36+
37+
var (
38+
useSSH bool
3139
)
3240

3341
// kubectlCmd represents the kubectl command
@@ -36,9 +44,13 @@ var kubectlCmd = &cobra.Command{
3644
Short: "Run a kubectl binary matching the cluster version",
3745
Long: `Run the Kubernetes client, download it if necessary. Remember -- after kubectl!
3846
39-
Examples:
40-
minikube kubectl -- --help
41-
minikube kubectl -- get pods --namespace kube-system`,
47+
This will run the Kubernetes client (kubectl) with the same version as the cluster
48+
49+
Normally it will download a binary matching the host operating system and architecture,
50+
but optionally you can also run it directly on the control plane over the ssh connection.
51+
This can be useful if you cannot run kubectl locally for some reason, like unsupported
52+
host. Please be aware that when using --ssh all paths will apply to the remote machine.`,
53+
Example: "minikube kubectl -- --help\nminikube kubectl -- get pods --namespace kube-system",
4254
Run: func(cmd *cobra.Command, args []string) {
4355
cc, err := config.Load(ClusterFlagValue())
4456

@@ -47,8 +59,31 @@ minikube kubectl -- get pods --namespace kube-system`,
4759
version = cc.KubernetesConfig.KubernetesVersion
4860
}
4961

50-
cluster := []string{"--cluster", ClusterFlagValue()}
51-
args = append(cluster, args...)
62+
cname := ClusterFlagValue()
63+
64+
if useSSH {
65+
co := mustload.Running(cname)
66+
n := co.CP.Node
67+
68+
kc := []string{"sudo"}
69+
kc = append(kc, kubectlPath(*co.Config))
70+
kc = append(kc, "--kubeconfig")
71+
kc = append(kc, kubeconfigPath(*co.Config))
72+
args = append(kc, args...)
73+
74+
klog.Infof("Running SSH %v", args)
75+
err := machine.CreateSSHShell(co.API, *co.Config, *n, args, false)
76+
if err != nil {
77+
fmt.Fprintf(os.Stderr, "Error running kubectl: %v", err)
78+
os.Exit(1)
79+
}
80+
return
81+
}
82+
83+
if len(args) > 1 && args[0] != "--help" {
84+
cluster := []string{"--cluster", cname}
85+
args = append(cluster, args...)
86+
}
5287

5388
c, err := KubectlCommand(version, args...)
5489
if err != nil {
@@ -66,14 +101,24 @@ minikube kubectl -- get pods --namespace kube-system`,
66101
waitStatus := exitError.Sys().(syscall.WaitStatus)
67102
rc = waitStatus.ExitStatus()
68103
} else {
69-
fmt.Fprintf(os.Stderr, "Error running %s: %v\n", path, err)
104+
fmt.Fprintf(os.Stderr, "Error running %s: %v\n", c.Path, err)
70105
rc = 1
71106
}
72107
os.Exit(rc)
73108
}
74109
},
75110
}
76111

112+
// kubectlPath returns the path to kubectl
113+
func kubectlPath(cfg config.ClusterConfig) string {
114+
return path.Join(vmpath.GuestPersistentDir, "binaries", cfg.KubernetesConfig.KubernetesVersion, "kubectl")
115+
}
116+
117+
// kubeconfigPath returns the path to kubeconfig
118+
func kubeconfigPath(cfg config.ClusterConfig) string {
119+
return "/etc/kubernetes/admin.conf"
120+
}
121+
77122
// KubectlCommand will return kubectl command with a version matching the cluster
78123
func KubectlCommand(version string, args ...string) (*exec.Cmd, error) {
79124
if version == "" {
@@ -87,3 +132,7 @@ func KubectlCommand(version string, args ...string) (*exec.Cmd, error) {
87132

88133
return exec.Command(path, args...), nil
89134
}
135+
136+
func init() {
137+
kubectlCmd.Flags().BoolVar(&useSSH, "ssh", false, "Use SSH for running kubernetes client on the node")
138+
}

cmd/minikube/main.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ func main() {
6161
bridgeLogMessages()
6262
defer klog.Flush()
6363

64-
setFlags()
64+
// Don't parse flags when running as kubectl
65+
_, callingCmd := filepath.Split(os.Args[0])
66+
parse := callingCmd != "kubectl"
67+
setFlags(parse)
6568

6669
s := stacklog.MustStartFromEnv("STACKLOG_PATH")
6770
defer s.Stop()
@@ -124,14 +127,16 @@ func (lb machineLogBridge) Write(b []byte) (n int, err error) {
124127
}
125128

126129
// setFlags sets the flags
127-
func setFlags() {
130+
func setFlags(parse bool) {
128131
// parse flags beyond subcommand - get aroung go flag 'limitations':
129132
// "Flag parsing stops just before the first non-flag argument" (ref: https://pkg.go.dev/flag#hdr-Command_line_flag_syntax)
130133
pflag.CommandLine.ParseErrorsWhitelist.UnknownFlags = true
131134
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
132135
// avoid 'pflag: help requested' error, as help will be defined later by cobra cmd.Execute()
133136
pflag.BoolP("help", "h", false, "")
134-
pflag.Parse()
137+
if parse {
138+
pflag.Parse()
139+
}
135140

136141
// set default flag value for logtostderr and alsologtostderr but don't override user's preferences
137142
if !pflag.CommandLine.Changed("logtostderr") {

site/content/en/docs/commands/kubectl.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,30 @@ Run a kubectl binary matching the cluster version
1313

1414
Run the Kubernetes client, download it if necessary. Remember -- after kubectl!
1515

16-
Examples:
17-
minikube kubectl -- --help
18-
minikube kubectl -- get pods --namespace kube-system
16+
This will run the Kubernetes client (kubectl) with the same version as the cluster
17+
18+
Normally it will download a binary matching the host operating system and architecture,
19+
but optionally you can also run it directly on the control plane over the ssh connection.
20+
This can be useful if you cannot run kubectl locally for some reason, like unsupported
21+
host. Please be aware that when using --ssh all paths will apply to the remote machine.
1922

2023
```shell
2124
minikube kubectl [flags]
2225
```
2326

27+
### Examples
28+
29+
```
30+
minikube kubectl -- --help
31+
minikube kubectl -- get pods --namespace kube-system
32+
```
33+
34+
### Options
35+
36+
```
37+
--ssh Use SSH for running kubernetes client on the node
38+
```
39+
2440
### Options inherited from parent commands
2541

2642
```

0 commit comments

Comments
 (0)