Skip to content

Commit eacc3a4

Browse files
committed
Add remote support for SSH and TLS docker contexts
1 parent cb04af6 commit eacc3a4

File tree

87 files changed

+5816
-124
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+5816
-124
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
diff a/cmd/minikube/cmd/config/profile_list.go b/cmd/minikube/cmd/config/profile_list.go (rejected hunks)
2+
@@ -40,8 +40,11 @@ import (
3+
"k8s.io/klog/v2"
4+
)
5+
6+
-var profileOutput string
7+
-var isLight bool
8+
+var (
9+
+ profileOutput string
10+
+ isLight bool
11+
+ isDetailed bool
12+
+)
13+
14+
var profileListCmd = &cobra.Command{
15+
Use: "list",
16+
@@ -130,7 +133,13 @@ func profileStatus(p *config.Profile, api libmachine.API) cluster.State {
17+
18+
func renderProfilesTable(ps [][]string) {
19+
table := tablewriter.NewWriter(os.Stdout)
20+
- table.SetHeader([]string{"Profile", "VM Driver", "Runtime", "IP", "Port", "Version", "Status", "Nodes", "Active Profile", "Active Kubecontext"})
21+
+ if isDetailed {
22+
+ table.SetHeader([]string{"Profile", "Driver", "Runtime", "IP", "Port", "Version",
23+
+ "Status", "Nodes", "Active Profile", "Active Kubecontext"})
24+
+ } else {
25+
+ table.SetHeader([]string{"Profile", "Driver", "Runtime", "IP", "Version", "Status",
26+
+ "Nodes", "Active Profile", "Active Kubecontext"})
27+
+ }
28+
table.SetAutoFormatHeaders(false)
29+
table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true})
30+
table.SetCenterSeparator("|")
31+
@@ -164,7 +173,13 @@ func profilesToTableData(profiles []*config.Profile) [][]string {
32+
if p.ActiveKubeContext {
33+
k = "*"
34+
}
35+
- data = append(data, []string{p.Name, p.Config.Driver, p.Config.KubernetesConfig.ContainerRuntime, cpIP, strconv.Itoa(cpPort), k8sVersion, p.Status, strconv.Itoa(len(p.Config.Nodes)), c, k})
36+
+ if isDetailed {
37+
+ data = append(data, []string{p.Name, p.Config.Driver, p.Config.KubernetesConfig.ContainerRuntime,
38+
+ cpIP, strconv.Itoa(cpPort), k8sVersion, p.Status, strconv.Itoa(len(p.Config.Nodes)), c, k})
39+
+ } else {
40+
+ data = append(data, []string{p.Name, p.Config.Driver, p.Config.KubernetesConfig.ContainerRuntime,
41+
+ cpIP, k8sVersion, p.Status, strconv.Itoa(len(p.Config.Nodes)), c, k})
42+
+ }
43+
}
44+
return data
45+
}
46+
@@ -213,5 +228,6 @@ func profilesOrDefault(profiles []*config.Profile) []*config.Profile {
47+
func init() {
48+
profileListCmd.Flags().StringVarP(&profileOutput, "output", "o", "table", "The output format. One of 'json', 'table'")
49+
profileListCmd.Flags().BoolVarP(&isLight, "light", "l", false, "If true, returns list of profiles faster by skipping validating the status of the cluster.")
50+
+ profileListCmd.Flags().BoolVarP(&isDetailed, "detailed", "d", false, "If true, returns a detailed list of profiles.")
51+
ProfileCmd.AddCommand(profileListCmd)
52+
}

cmd/minikube/cmd/service.go.rej

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
diff a/cmd/minikube/cmd/service.go b/cmd/minikube/cmd/service.go (rejected hunks)
2+
@@ -163,18 +163,20 @@ You may select another namespace by using 'minikube service {{.service}} -n <nam
3+
for _, svc := range noNodePortServices {
4+
noNodePortSvcNames = append(noNodePortSvcNames, fmt.Sprintf("%s/%s", svc.Namespace, svc.Name))
5+
}
6+
- if len(noNodePortServices) != 0 {
7+
+ if len(noNodePortServices) > 0 {
8+
out.WarningT("Services {{.svc_names}} have type \"ClusterIP\" not meant to be exposed, however for local development minikube allows you to access this !", out.V{"svc_names": noNodePortSvcNames})
9+
}
10+
11+
- if driver.NeedsPortForward(co.Config.Driver) && services != nil {
12+
- startKicServiceTunnel(services, cname, co.Config.Driver)
13+
+ if driver.NeedsPortForward(co.Config.Driver) {
14+
+ svcs := services
15+
+ if len(svcs) == 0 && len(noNodePortServices) > 0 {
16+
+ svcs = noNodePortServices
17+
+ }
18+
+ if len(svcs) > 0 {
19+
+ startKicServiceTunnel(svcs, cname, co.Config.Driver)
20+
+ }
21+
} else if !serviceURLMode {
22+
openURLs(data)
23+
- if len(noNodePortServices) != 0 {
24+
- startKicServiceTunnel(noNodePortServices, cname, co.Config.Driver)
25+
- }
26+
-
27+
}
28+
},
29+
}

cmd/minikube/cmd/ssh.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import (
2929
"k8s.io/minikube/pkg/minikube/node"
3030
"k8s.io/minikube/pkg/minikube/out"
3131
"k8s.io/minikube/pkg/minikube/reason"
32+
"k8s.io/minikube/pkg/drivers/kic/oci"
33+
"k8s.io/klog/v2"
3234
)
3335

3436
var nativeSSHClient bool
@@ -56,7 +58,16 @@ var sshCmd = &cobra.Command{
5658
}
5759
}
5860

59-
err = machine.CreateSSHShell(co.API, *co.Config, *n, args, nativeSSHClient)
61+
// For remote Docker contexts, use docker exec instead of SSH
62+
klog.Warningf("SSH Command: Driver=%s, IsRemoteDockerContext=%v", co.Config.Driver, oci.IsRemoteDockerContext())
63+
if co.Config.Driver == driver.Docker && oci.IsRemoteDockerContext() {
64+
klog.Warningf("Using CreateSSHTerminal for remote Docker context")
65+
err = oci.CreateSSHTerminal(config.MachineName(*co.Config, *n), args)
66+
} else {
67+
klog.Warningf("Using standard SSH shell")
68+
err = machine.CreateSSHShell(co.API, *co.Config, *n, args, nativeSSHClient)
69+
}
70+
6071
if err != nil {
6172
// This is typically due to a non-zero exit code, so no need for flourish.
6273
out.ErrLn("ssh: %v", err)

cmd/minikube/cmd/start.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ import (
4141
"github.com/google/go-containerregistry/pkg/name"
4242
"github.com/google/go-containerregistry/pkg/v1/remote"
4343
"github.com/pkg/errors"
44-
"github.com/shirou/gopsutil/v3/cpu"
45-
gopshost "github.com/shirou/gopsutil/v3/host"
44+
"github.com/shirou/gopsutil/v4/cpu"
45+
gopshost "github.com/shirou/gopsutil/v4/host"
4646
"github.com/spf13/cobra"
4747
"github.com/spf13/viper"
4848
"golang.org/x/text/cases"
@@ -113,10 +113,11 @@ func init() {
113113

114114
// startCmd represents the start command
115115
var startCmd = &cobra.Command{
116-
Use: "start",
117-
Short: "Starts a local Kubernetes cluster",
118-
Long: "Starts a local Kubernetes cluster",
119-
Run: runStart,
116+
Use: "start",
117+
Aliases: []string{"create"},
118+
Short: "Starts a local Kubernetes cluster",
119+
Long: "Starts a local Kubernetes cluster",
120+
Run: runStart,
120121
}
121122

122123
// platform generates a user-readable platform message
@@ -135,7 +136,12 @@ func platform() string {
135136

136137
vsys, vrole, err := gopshost.Virtualization()
137138
if err != nil {
138-
klog.Warningf("gopshost.Virtualization returned error: %v", err)
139+
// Only log if it's a real error, not just "not implemented yet"
140+
if !strings.Contains(err.Error(), "not implemented yet") {
141+
klog.Warningf("gopshost.Virtualization returned error: %v", err)
142+
} else {
143+
klog.V(3).Infof("Virtualization detection not implemented for this platform (harmless)")
144+
}
139145
} else {
140146
klog.Infof("virtualization: %s %s", vsys, vrole)
141147
}
@@ -731,6 +737,14 @@ func selectDriver(existing *config.ClusterConfig) (registry.DriverState, []regis
731737
return ds, nil, true
732738
}
733739

740+
// Check for remote Docker context and auto-select docker driver
741+
if oci.IsRemoteDockerContext() {
742+
ds := driver.Status("docker")
743+
out.Step(style.Sparkle, `Detected a remote Docker context, using the {{.driver}} driver`, out.V{"driver": ds.String()})
744+
out.Infof("For remote Docker connections, you may need to run 'minikube tunnel-ssh' for API server access")
745+
return ds, nil, true
746+
}
747+
734748
choices := driver.Choices(viper.GetBool("vm"))
735749
pick, alts, rejects := driver.Suggest(choices)
736750
if pick.Name == "" {

cmd/minikube/cmd/start.go.rej

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
diff a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go (rejected hunks)
2+
@@ -1098,8 +1112,8 @@ func suggestMemoryAllocation(sysLimit, containerLimit, nodes int) int {
3+
return mem
4+
}
5+
6+
- const fallback = 2200
7+
- maximum := 6000
8+
+ const fallback = 3072
9+
+ maximum := 6144
10+
11+
if sysLimit > 0 && fallback > sysLimit {
12+
return sysLimit

cmd/minikube/cmd/start_flags.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424

2525
"github.com/blang/semver/v4"
2626
"github.com/pkg/errors"
27-
"github.com/shirou/gopsutil/v3/cpu"
27+
"github.com/shirou/gopsutil/v4/cpu"
2828
"github.com/spf13/cobra"
2929
"github.com/spf13/viper"
3030
"k8s.io/klog/v2"

cmd/minikube/cmd/start_flags.go.rej

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
diff a/cmd/minikube/cmd/start_flags.go b/cmd/minikube/cmd/start_flags.go (rejected hunks)
2+
@@ -61,8 +61,8 @@ const (
3+
hostOnlyCIDR = "host-only-cidr"
4+
containerRuntime = "container-runtime"
5+
criSocket = "cri-socket"
6+
- networkPlugin = "network-plugin"
7+
- enableDefaultCNI = "enable-default-cni"
8+
+ networkPlugin = "network-plugin" // deprecated, use --cni instead
9+
+ enableDefaultCNI = "enable-default-cni" // deprecated, use --cni=bridge instead
10+
cniFlag = "cni"
11+
hypervVirtualSwitch = "hyperv-virtual-switch"
12+
hypervUseExternalSwitch = "hyperv-use-external-switch"
13+
@@ -163,7 +163,7 @@ func initMinikubeFlags() {
14+
startCmd.Flags().Bool(dryRun, false, "dry-run mode. Validates configuration, but does not mutate system state")
15+
16+
startCmd.Flags().String(cpus, "2", fmt.Sprintf("Number of CPUs allocated to Kubernetes. Use %q to use the maximum number of CPUs. Use %q to not specify a limit (Docker/Podman only)", constants.MaxResources, constants.NoLimit))
17+
- startCmd.Flags().String(memory, "", fmt.Sprintf("Amount of RAM to allocate to Kubernetes (format: <number>[<unit>], where unit = b, k, m or g). Use %q to use the maximum amount of memory. Use %q to not specify a limit (Docker/Podman only)", constants.MaxResources, constants.NoLimit))
18+
+ startCmd.Flags().StringP(memory, "m", "", fmt.Sprintf("Amount of RAM to allocate to Kubernetes (format: <number>[<unit>], where unit = b, k, m or g). Use %q to use the maximum amount of memory. Use %q to not specify a limit (Docker/Podman only)", constants.MaxResources, constants.NoLimit))
19+
startCmd.Flags().String(humanReadableDiskSize, defaultDiskSize, "Disk size allocated to the minikube VM (format: <number>[<unit>], where unit = b, k, m or g).")
20+
startCmd.Flags().Bool(downloadOnly, false, "If true, only download and cache files for later use - don't install or start anything.")
21+
startCmd.Flags().Bool(cacheImages, true, "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --driver=none.")
22+
@@ -182,7 +182,7 @@ func initMinikubeFlags() {
23+
startCmd.Flags().Uint16(mountPortFlag, defaultMountPort, mountPortDescription)
24+
startCmd.Flags().String(mountTypeFlag, defaultMountType, mountTypeDescription)
25+
startCmd.Flags().String(mountUID, defaultMountUID, mountUIDDescription)
26+
- startCmd.Flags().StringSlice(config.AddonListFlag, nil, "Enable addons. see `minikube addons list` for a list of valid addon names.")
27+
+ startCmd.Flags().StringSlice(config.AddonListFlag, nil, "Enable one or more addons, in a comma-separated format. See `minikube addons list` for a list of valid addon names.")
28+
startCmd.Flags().String(criSocket, "", "The cri socket path to be used.")
29+
startCmd.Flags().String(networkPlugin, "", "DEPRECATED: Replaced by --cni")
30+
startCmd.Flags().Bool(enableDefaultCNI, false, "DEPRECATED: Replaced by --cni=bridge")
31+
@@ -232,6 +232,20 @@ func initKubernetesFlags() {
32+
func initDriverFlags() {
33+
startCmd.Flags().StringP("driver", "d", "", fmt.Sprintf("Driver is one of: %v (defaults to auto-detect)", driver.DisplaySupportedDrivers()))
34+
startCmd.Flags().String("vm-driver", "", "DEPRECATED, use `driver` instead.")
35+
+ // Hide the deprecated vm-driver flag from help text
36+
+ if err := startCmd.Flags().MarkHidden("vm-driver"); err != nil {
37+
+ klog.Warningf("Failed to hide vm-driver flag: %v\n", err)
38+
+ }
39+
+ // Hide the deprecated flag from help text so new users dont use it (still will be processed)
40+
+ if err := startCmd.Flags().MarkHidden(enableDefaultCNI); err != nil {
41+
+ klog.Warningf("Failed to hide %s flag: %v\n", enableDefaultCNI, err)
42+
+ }
43+
+
44+
+ // Hide the deprecated flag from help text so new users dont use it (still will be processed)
45+
+ if err := startCmd.Flags().MarkHidden(networkPlugin); err != nil {
46+
+ klog.Warningf("Failed to hide %s flag: %v\n", networkPlugin, err)
47+
+ }
48+
+
49+
startCmd.Flags().Bool(disableDriverMounts, false, "Disables the filesystem mounts provided by the hypervisors")
50+
startCmd.Flags().Bool("vm", false, "Filter to use only VM Drivers")
51+

cmd/minikube/cmd/start_test.go.rej

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
diff a/cmd/minikube/cmd/start_test.go b/cmd/minikube/cmd/start_test.go (rejected hunks)
2+
@@ -277,26 +277,26 @@ func TestSuggestMemoryAllocation(t *testing.T) {
3+
nodes int
4+
want int
5+
}{
6+
- {"128GB sys", 128000, 0, 1, 6000},
7+
- {"64GB sys", 64000, 0, 1, 6000},
8+
- {"32GB sys", 32768, 0, 1, 6000},
9+
+ {"128GB sys", 128000, 0, 1, 6144},
10+
+ {"64GB sys", 64000, 0, 1, 6144},
11+
+ {"32GB sys", 32768, 0, 1, 6144},
12+
{"16GB sys", 16384, 0, 1, 4000},
13+
{"odd sys", 14567, 0, 1, 3600},
14+
- {"4GB sys", 4096, 0, 1, 2200},
15+
+ {"4GB sys", 4096, 0, 1, 3072},
16+
{"2GB sys", 2048, 0, 1, 2048},
17+
- {"Unable to poll sys", 0, 0, 1, 2200},
18+
+ {"Unable to poll sys", 0, 0, 1, 3072},
19+
{"128GB sys, 16GB container", 128000, 16384, 1, 16336},
20+
{"64GB sys, 16GB container", 64000, 16384, 1, 16000},
21+
{"16GB sys, 4GB container", 16384, 4096, 1, 4000},
22+
{"4GB sys, 3.5GB container", 16384, 3500, 1, 3452},
23+
{"16GB sys, 2GB container", 16384, 2048, 1, 2048},
24+
{"16GB sys, unable to poll container", 16384, 0, 1, 4000},
25+
- {"128GB sys 2 nodes", 128000, 0, 2, 6000},
26+
- {"8GB sys 3 nodes", 8192, 0, 3, 2200},
27+
- {"16GB sys 2 nodes", 16384, 0, 2, 2200},
28+
+ {"128GB sys 2 nodes", 128000, 0, 2, 6144},
29+
+ {"8GB sys 3 nodes", 8192, 0, 3, 3072},
30+
+ {"16GB sys 2 nodes", 16384, 0, 2, 3072},
31+
{"32GB sys 2 nodes", 32768, 0, 2, 4050},
32+
- {"odd sys 2 nodes", 14567, 0, 2, 2200},
33+
- {"4GB sys 2 nodes", 4096, 0, 2, 2200},
34+
+ {"odd sys 2 nodes", 14567, 0, 2, 3072},
35+
+ {"4GB sys 2 nodes", 4096, 0, 2, 3072},
36+
{"2GB sys 3 nodes", 2048, 0, 3, 2048},
37+
}
38+
for _, test := range tests {

cmd/minikube/cmd/tunnel-ssh.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cmd
18+
19+
import (
20+
"fmt"
21+
"os"
22+
"os/signal"
23+
"strconv"
24+
"syscall"
25+
26+
"github.com/spf13/cobra"
27+
"k8s.io/klog/v2"
28+
"k8s.io/minikube/pkg/drivers/kic/oci"
29+
"k8s.io/minikube/pkg/minikube/exit"
30+
"k8s.io/minikube/pkg/minikube/kubeconfig"
31+
"k8s.io/minikube/pkg/minikube/mustload"
32+
"k8s.io/minikube/pkg/minikube/out"
33+
"k8s.io/minikube/pkg/minikube/reason"
34+
"k8s.io/minikube/pkg/minikube/style"
35+
)
36+
37+
// tunnelSSHCmd represents the tunnel-ssh command for persistent SSH tunneling
38+
var tunnelSSHCmd = &cobra.Command{
39+
Use: "tunnel-ssh",
40+
Short: "Create persistent SSH tunnel for remote Docker contexts",
41+
Long: `Creates and maintains an SSH tunnel for API server access when using
42+
remote Docker contexts. The tunnel runs in the foreground and can be stopped with Ctrl+C.`,
43+
Run: func(_ *cobra.Command, _ []string) {
44+
// Check if we're using a remote SSH Docker context
45+
if !oci.IsRemoteDockerContext() {
46+
out.Styled(style.Meh, "No remote Docker context detected - tunnel not needed")
47+
return
48+
}
49+
50+
if !oci.IsSSHDockerContext() {
51+
out.ErrT(style.Sad, "SSH tunnel only supported for SSH-based Docker contexts")
52+
exit.Error(reason.Usage, "unsupported context type", fmt.Errorf("not an SSH context"))
53+
}
54+
55+
cname := ClusterFlagValue()
56+
co := mustload.Running(cname)
57+
58+
out.Step(style.Launch, "Starting SSH tunnel for API server access...")
59+
klog.Infof("Setting up persistent SSH tunnel for cluster %s", cname)
60+
61+
// Set up SSH tunnel for API server access
62+
tunnelEndpoint, cleanup, err := oci.SetupAPIServerTunnel(co.CP.Port)
63+
if err != nil {
64+
exit.Error(reason.HostKubeconfigUpdate, "setting up SSH tunnel", err)
65+
}
66+
67+
if tunnelEndpoint == "" {
68+
out.Styled(style.Meh, "No tunnel needed for this context")
69+
return
70+
}
71+
72+
// Parse tunnel endpoint to get port
73+
var tunnelPort int
74+
if len(tunnelEndpoint) > 19 { // len("https://localhost:")
75+
portStr := tunnelEndpoint[19:] // Skip "https://localhost:"
76+
if port, parseErr := strconv.Atoi(portStr); parseErr == nil {
77+
tunnelPort = port
78+
}
79+
}
80+
81+
// Update kubeconfig to use the tunnel
82+
updated, err := kubeconfig.UpdateEndpoint(cname, "localhost", tunnelPort, kubeconfig.PathFromEnv(), kubeconfig.NewExtension())
83+
if err != nil {
84+
cleanup()
85+
exit.Error(reason.HostKubeconfigUpdate, "updating kubeconfig", err)
86+
}
87+
88+
if updated {
89+
out.Step(style.Celebrate, `"{{.context}}" context updated to use SSH tunnel {{.endpoint}}`,
90+
out.V{"context": cname, "endpoint": tunnelEndpoint})
91+
}
92+
93+
out.Step(style.Running, "SSH tunnel active on {{.endpoint}} ({{.original}} -> localhost:{{.port}})",
94+
out.V{
95+
"endpoint": tunnelEndpoint,
96+
"original": fmt.Sprintf("%s:%d", co.CP.Hostname, co.CP.Port),
97+
"port": tunnelPort,
98+
})
99+
out.Styled(style.Tip, "Press Ctrl+C to stop the tunnel")
100+
101+
// Set up signal handling for graceful shutdown
102+
c := make(chan os.Signal, 1)
103+
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
104+
105+
// Wait for interrupt signal
106+
<-c
107+
out.Step(style.Shutdown, "Stopping SSH tunnel...")
108+
cleanup()
109+
out.Styled(style.Celebrate, "SSH tunnel stopped")
110+
},
111+
}
112+
113+
func init() {
114+
RootCmd.AddCommand(tunnelSSHCmd)
115+
}

0 commit comments

Comments
 (0)