Skip to content

Commit ad7fb39

Browse files
committed
support kubernetes ha cluster topology in minikube
1 parent 79e4bb8 commit ad7fb39

File tree

97 files changed

+2215
-1187
lines changed

Some content is hidden

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

97 files changed

+2215
-1187
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ _testmain.go
2525
*.exe
2626
*.test
2727
*.prof
28+
*.pprof
2829

2930
/deploy/iso/minikube-iso/board/minikube/x86_64/rootfs-overlay/usr/bin/auto-pause
3031
/deploy/iso/minikube-iso/board/minikube/aarch64/rootfs-overlay/usr/bin/auto-pause

cmd/minikube/cmd/config/profile_list.go

+79-36
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func printProfilesTable() {
8383
}
8484

8585
if len(validProfiles) == 0 {
86-
exit.Message(reason.UsageNoProfileRunning, "No minikube profile was found. ")
86+
exit.Message(reason.UsageNoProfileRunning, "No minikube profile was found.")
8787
}
8888

8989
updateProfilesStatus(validProfiles)
@@ -111,45 +111,82 @@ func updateProfilesStatus(profiles []*config.Profile) {
111111
}
112112

113113
func profileStatus(p *config.Profile, api libmachine.API) string {
114-
cp, err := config.PrimaryControlPlane(p.Config)
115-
if err != nil {
116-
exit.Error(reason.GuestCpConfig, "error getting primary control plane", err)
114+
cps := config.ControlPlanes(*p.Config)
115+
if len(cps) == 0 {
116+
exit.Message(reason.GuestCpConfig, "No control-plane nodes found.")
117117
}
118118

119-
host, err := machine.LoadHost(api, config.MachineName(*p.Config, cp))
120-
if err != nil {
121-
klog.Warningf("error loading profiles: %v", err)
122-
return "Unknown"
123-
}
119+
status := "Unknown"
120+
healthyCPs := 0
121+
for _, cp := range cps {
122+
machineName := config.MachineName(*p.Config, cp)
124123

125-
// The machine isn't running, no need to check inside
126-
s, err := host.Driver.GetState()
127-
if err != nil {
128-
klog.Warningf("error getting host state: %v", err)
129-
return "Unknown"
130-
}
131-
if s != state.Running {
132-
return s.String()
133-
}
124+
ms, err := machine.Status(api, machineName)
125+
if err != nil {
126+
klog.Warningf("error loading profile (will continue): machine status for %s: %v", machineName, err)
127+
continue
128+
}
129+
if ms != state.Running.String() {
130+
klog.Warningf("error loading profile (will continue): machine %s is not running: %q", machineName, ms)
131+
status = ms
132+
continue
133+
}
134134

135-
cr, err := machine.CommandRunner(host)
136-
if err != nil {
137-
klog.Warningf("error loading profiles: %v", err)
138-
return "Unknown"
139-
}
135+
host, err := machine.LoadHost(api, machineName)
136+
if err != nil {
137+
klog.Warningf("error loading profile (will continue): load host for %s: %v", machineName, err)
138+
continue
139+
}
140140

141-
hostname, _, port, err := driver.ControlPlaneEndpoint(p.Config, &cp, host.DriverName)
142-
if err != nil {
143-
klog.Warningf("error loading profiles: %v", err)
144-
return "Unknown"
141+
hs, err := host.Driver.GetState()
142+
if err != nil {
143+
klog.Warningf("error loading profile (will continue): host state for %s: %v", machineName, err)
144+
continue
145+
}
146+
if hs != state.Running {
147+
klog.Warningf("error loading profile (will continue): host %s is not running: %q", machineName, hs)
148+
status = hs.String()
149+
continue
150+
}
151+
152+
cr, err := machine.CommandRunner(host)
153+
if err != nil {
154+
klog.Warningf("error loading profile (will continue): command runner for %s: %v", machineName, err)
155+
continue
156+
}
157+
158+
hostname, _, port, err := driver.ControlPlaneEndpoint(p.Config, &cp, host.DriverName)
159+
if err != nil {
160+
klog.Warningf("error loading profile (will continue): control-plane endpoint for %s: %v", machineName, err)
161+
continue
162+
}
163+
164+
as, err := kverify.APIServerStatus(cr, hostname, port)
165+
if err != nil {
166+
klog.Warningf("error loading profile (will continue): apiserver status for %s: %v", machineName, err)
167+
continue
168+
}
169+
if as != state.Running {
170+
klog.Warningf("error loading profile (will continue): apiserver %s is not running: %q", machineName, hs)
171+
status = as.String()
172+
continue
173+
}
174+
175+
status = state.Running.String()
176+
healthyCPs++
145177
}
146178

147-
status, err := kverify.APIServerStatus(cr, hostname, port)
148-
if err != nil {
149-
klog.Warningf("error getting apiserver status for %s: %v", p.Name, err)
150-
return "Unknown"
179+
if config.HA(*p.Config) {
180+
switch {
181+
case healthyCPs < 2:
182+
return state.Stopped.String()
183+
case healthyCPs == 2:
184+
return "Degraded"
185+
default:
186+
return "HAppy"
187+
}
151188
}
152-
return status.String()
189+
return status
153190
}
154191

155192
func renderProfilesTable(ps [][]string) {
@@ -166,9 +203,15 @@ func profilesToTableData(profiles []*config.Profile) [][]string {
166203
var data [][]string
167204
currentProfile := ClusterFlagValue()
168205
for _, p := range profiles {
169-
cp, err := config.PrimaryControlPlane(p.Config)
170-
if err != nil {
171-
exit.Error(reason.GuestCpConfig, "error getting primary control plane", err)
206+
cpIP := p.Config.KubernetesConfig.APIServerHAVIP
207+
cpPort := p.Config.APIServerPort
208+
if !config.HA(*p.Config) {
209+
cp, err := config.ControlPlane(*p.Config)
210+
if err != nil {
211+
exit.Error(reason.GuestCpConfig, "error getting control-plane node", err)
212+
}
213+
cpIP = cp.IP
214+
cpPort = cp.Port
172215
}
173216

174217
k8sVersion := p.Config.KubernetesConfig.KubernetesVersion
@@ -179,7 +222,7 @@ func profilesToTableData(profiles []*config.Profile) [][]string {
179222
if p.Name == currentProfile {
180223
c = "*"
181224
}
182-
data = append(data, []string{p.Name, p.Config.Driver, p.Config.KubernetesConfig.ContainerRuntime, cp.IP, strconv.Itoa(cp.Port), k8sVersion, p.Status, strconv.Itoa(len(p.Config.Nodes)), c})
225+
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})
183226
}
184227
return data
185228
}

cmd/minikube/cmd/cp.go

+2-5
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Example Command : "minikube cp a.txt /home/docker/b.txt" +
7171
runner = remoteCommandRunner(&co, dst.node)
7272
} else if src.node == "" {
7373
// if node name not explicitly specified in both of source and target,
74-
// consider target is controlpanel node for backward compatibility.
74+
// consider target is control-plane node for backward compatibility.
7575
runner = co.CP.Runner
7676
} else {
7777
runner = command.NewExecRunner(false)
@@ -84,9 +84,6 @@ Example Command : "minikube cp a.txt /home/docker/b.txt" +
8484
},
8585
}
8686

87-
func init() {
88-
}
89-
9087
// setDstFileNameFromSrc sets the src filename as dst filename
9188
// when the dst file name is not provided and ends with a `/`.
9289
// Otherwise this function is a no-op and returns the passed dst.
@@ -211,7 +208,7 @@ func validateArgs(src, dst *remotePath) {
211208
}
212209

213210
// if node name not explicitly specified in both of source and target,
214-
// consider target node is controlpanel for backward compatibility.
211+
// consider target node is control-plane for backward compatibility.
215212
if src.node == "" && dst.node == "" && !strings.HasPrefix(dst.path, "/") {
216213
exit.Message(reason.Usage, `Target <remote file path> must be an absolute Path. Relative Path is not allowed (example: "minikube:/home/docker/copied.txt")`)
217214
}

cmd/minikube/cmd/docker-env.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,7 @@ func mustRestartDockerd(name string, runner command.Runner) {
228228
// will need to wait for apisever container to come up, this usually takes 5 seconds
229229
// verifying apisever using kverify would add code complexity for a rare case.
230230
klog.Warningf("waiting to ensure apisever container is up...")
231-
startTime := time.Now()
232-
if err = waitForAPIServerProcess(runner, startTime, time.Second*30); err != nil {
231+
if err = waitForAPIServerProcess(runner, time.Now(), time.Second*30); err != nil {
233232
klog.Warningf("apiserver container isn't up, error: %v", err)
234233
}
235234
}

cmd/minikube/cmd/logs.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func shouldSilentFail() bool {
138138

139139
api, cc := mustload.Partial(ClusterFlagValue())
140140

141-
cp, err := config.PrimaryControlPlane(cc)
141+
cp, err := config.ControlPlane(*cc)
142142
if err != nil {
143143
return false
144144
}

cmd/minikube/cmd/node_add.go

+28-16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package cmd
1919
import (
2020
"github.com/spf13/cobra"
2121
"github.com/spf13/viper"
22+
2223
"k8s.io/minikube/pkg/minikube/cni"
2324
"k8s.io/minikube/pkg/minikube/config"
2425
"k8s.io/minikube/pkg/minikube/driver"
@@ -32,8 +33,9 @@ import (
3233
)
3334

3435
var (
35-
cp bool
36-
worker bool
36+
cpNode bool
37+
workerNode bool
38+
deleteNodeOnFailure bool
3739
)
3840

3941
var nodeAddCmd = &cobra.Command{
@@ -48,20 +50,31 @@ var nodeAddCmd = &cobra.Command{
4850
out.FailureT("none driver does not support multi-node clusters")
4951
}
5052

51-
name := node.Name(len(cc.Nodes) + 1)
53+
if cpNode && !config.HA(*cc) {
54+
out.FailureT("Adding a control-plane node to a non-HA cluster is not currently supported. Please first delete the cluster and use 'minikube start --ha' to create new one.")
55+
}
56+
57+
roles := []string{}
58+
if workerNode {
59+
roles = append(roles, "worker")
60+
}
61+
if cpNode {
62+
roles = append(roles, "control-plane")
63+
}
5264

53-
// for now control-plane feature is not supported
54-
if cp {
55-
out.Step(style.Unsupported, "Adding a control-plane node is not yet supported, setting control-plane flag to false")
56-
cp = false
65+
// calculate appropriate new node name with id following the last existing one
66+
lastID, err := node.ID(cc.Nodes[len(cc.Nodes)-1].Name)
67+
if err != nil {
68+
lastID = len(cc.Nodes)
69+
out.ErrLn("determining last node index (will assume %d): %v", lastID, err)
5770
}
71+
name := node.Name(lastID + 1)
5872

59-
out.Step(style.Happy, "Adding node {{.name}} to cluster {{.cluster}}", out.V{"name": name, "cluster": cc.Name})
60-
// TODO: Deal with parameters better. Ideally we should be able to acceot any node-specific minikube start params here.
73+
out.Step(style.Happy, "Adding node {{.name}} to cluster {{.cluster}} as {{.roles}}", out.V{"name": name, "cluster": cc.Name, "roles": roles})
6174
n := config.Node{
6275
Name: name,
63-
Worker: worker,
64-
ControlPlane: cp,
76+
Worker: workerNode,
77+
ControlPlane: cpNode,
6578
KubernetesVersion: cc.KubernetesConfig.KubernetesVersion,
6679
}
6780

@@ -77,7 +90,7 @@ var nodeAddCmd = &cobra.Command{
7790
}
7891

7992
register.Reg.SetStep(register.InitialSetup)
80-
if err := node.Add(cc, n, false); err != nil {
93+
if err := node.Add(cc, n, deleteNodeOnFailure); err != nil {
8194
_, err := maybeDeleteAndRetry(cmd, *cc, n, nil, err)
8295
if err != nil {
8396
exit.Error(reason.GuestNodeAdd, "failed to add node", err)
@@ -93,10 +106,9 @@ var nodeAddCmd = &cobra.Command{
93106
}
94107

95108
func init() {
96-
// TODO(https://github.com/kubernetes/minikube/issues/7366): We should figure out which minikube start flags to actually import
97-
nodeAddCmd.Flags().BoolVar(&cp, "control-plane", false, "This flag is currently unsupported.")
98-
nodeAddCmd.Flags().BoolVar(&worker, "worker", true, "If true, the added node will be marked for work. Defaults to true.")
99-
nodeAddCmd.Flags().Bool(deleteOnFailure, false, "If set, delete the current cluster if start fails and try again. Defaults to false.")
109+
nodeAddCmd.Flags().BoolVar(&cpNode, "control-plane", false, "If set, added node will become a control-plane. Defaults to false. Currently only supported for existing HA clusters.")
110+
nodeAddCmd.Flags().BoolVar(&workerNode, "worker", true, "If set, added node will be available as worker. Defaults to true.")
111+
nodeAddCmd.Flags().BoolVar(&deleteNodeOnFailure, "delete-on-failure", false, "If set, delete the current cluster if start fails and try again. Defaults to false.")
100112

101113
nodeCmd.AddCommand(nodeAddCmd)
102114
}

cmd/minikube/cmd/node_start.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ var nodeStartCmd = &cobra.Command{
5656
}
5757

5858
register.Reg.SetStep(register.InitialSetup)
59-
r, p, m, h, err := node.Provision(cc, n, n.ControlPlane, viper.GetBool(deleteOnFailure))
59+
r, p, m, h, err := node.Provision(cc, n, viper.GetBool(deleteOnFailure))
6060
if err != nil {
6161
exit.Error(reason.GuestNodeProvision, "provisioning host for node", err)
6262
}
@@ -71,10 +71,8 @@ var nodeStartCmd = &cobra.Command{
7171
ExistingAddons: cc.Addons,
7272
}
7373

74-
_, err = node.Start(s, n.ControlPlane)
75-
if err != nil {
76-
_, err := maybeDeleteAndRetry(cmd, *cc, *n, nil, err)
77-
if err != nil {
74+
if _, err = node.Start(s); err != nil {
75+
if _, err := maybeDeleteAndRetry(cmd, *cc, *n, nil, err); err != nil {
7876
node.ExitIfFatal(err, false)
7977
exit.Error(reason.GuestNodeStart, "failed to start node", err)
8078
}

0 commit comments

Comments
 (0)