Skip to content

Commit 527bbcd

Browse files
Merge pull request #7389 from sharifelgamal/driver-fallback
fallback to alternate drivers on failure
2 parents 534b355 + 322b5d5 commit 527bbcd

File tree

8 files changed

+280
-136
lines changed

8 files changed

+280
-136
lines changed

cmd/minikube/cmd/node_add.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/spf13/cobra"
2121
"k8s.io/minikube/pkg/minikube/config"
2222
"k8s.io/minikube/pkg/minikube/driver"
23+
"k8s.io/minikube/pkg/minikube/exit"
2324
"k8s.io/minikube/pkg/minikube/mustload"
2425
"k8s.io/minikube/pkg/minikube/node"
2526
"k8s.io/minikube/pkg/minikube/out"
@@ -54,7 +55,10 @@ var nodeAddCmd = &cobra.Command{
5455
}
5556

5657
if err := node.Add(cc, n); err != nil {
57-
maybeDeleteAndRetry(*cc, n, nil, err)
58+
_, err := maybeDeleteAndRetry(*cc, n, nil, err)
59+
if err != nil {
60+
exit.WithError("failed to add node", err)
61+
}
5862
}
5963

6064
out.T(out.Ready, "Successfully added {{.name}} to {{.cluster}}!", out.V{"name": name, "cluster": cc.Name})

cmd/minikube/cmd/node_start.go

+20-2
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,27 @@ var nodeStartCmd = &cobra.Command{
4949
exit.WithError("retrieving node", err)
5050
}
5151

52-
_, err = node.Start(*cc, *n, nil, false)
52+
r, p, m, h, err := node.Provision(cc, n, false)
5353
if err != nil {
54-
maybeDeleteAndRetry(*cc, *n, nil, err)
54+
exit.WithError("provisioning host for node", err)
55+
}
56+
57+
s := node.Starter{
58+
Runner: r,
59+
PreExists: p,
60+
MachineAPI: m,
61+
Host: h,
62+
Cfg: cc,
63+
Node: n,
64+
ExistingAddons: nil,
65+
}
66+
67+
_, err = node.Start(s, false)
68+
if err != nil {
69+
_, err := maybeDeleteAndRetry(*cc, *n, nil, err)
70+
if err != nil {
71+
exit.WithError("failed to start node", err)
72+
}
5573
}
5674
},
5775
}

cmd/minikube/cmd/start.go

+107-27
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,59 @@ func runStart(cmd *cobra.Command, args []string) {
150150
}
151151

152152
validateSpecifiedDriver(existing)
153-
ds := selectDriver(existing)
153+
ds, alts, specified := selectDriver(existing)
154+
starter, err := provisionWithDriver(cmd, ds, existing)
155+
if err != nil {
156+
if specified {
157+
// If the user specified a driver, don't fallback to anything else
158+
exit.WithError("error provisioning host", err)
159+
} else {
160+
success := false
161+
// Walk down the rest of the options
162+
for _, alt := range alts {
163+
out.WarningT("Startup with {{.old_driver}} driver failed, trying with alternate driver {{.new_driver}}: {{.error}}", out.V{"old_driver": ds.Name, "new_driver": alt.Name, "error": err})
164+
ds = alt
165+
// Delete the existing cluster and try again with the next driver on the list
166+
profile, err := config.LoadProfile(ClusterFlagValue())
167+
if err != nil {
168+
glog.Warningf("%s profile does not exist, trying anyways.", ClusterFlagValue())
169+
}
170+
171+
err = deleteProfile(profile)
172+
if err != nil {
173+
out.WarningT("Failed to delete cluster {{.name}}, proceeding with retry anyway.", out.V{"name": ClusterFlagValue()})
174+
}
175+
starter, err = provisionWithDriver(cmd, ds, existing)
176+
if err != nil {
177+
continue
178+
} else {
179+
// Success!
180+
success = true
181+
break
182+
}
183+
}
184+
if !success {
185+
exit.WithError("error provisioning host", err)
186+
}
187+
}
188+
}
189+
190+
kubeconfig, err := startWithDriver(starter, existing)
191+
if err != nil {
192+
exit.WithError("failed to start node", err)
193+
}
194+
195+
if err := showKubectlInfo(kubeconfig, starter.Node.KubernetesVersion, starter.Cfg.Name); err != nil {
196+
glog.Errorf("kubectl info: %v", err)
197+
}
198+
199+
}
200+
201+
func provisionWithDriver(cmd *cobra.Command, ds registry.DriverState, existing *config.ClusterConfig) (node.Starter, error) {
154202
driverName := ds.Name
155203
glog.Infof("selected driver: %s", driverName)
156204
validateDriver(ds, existing)
157-
err = autoSetDriverOptions(cmd, driverName)
205+
err := autoSetDriverOptions(cmd, driverName)
158206
if err != nil {
159207
glog.Errorf("Error autoSetOptions : %v", err)
160208
}
@@ -170,19 +218,19 @@ func runStart(cmd *cobra.Command, args []string) {
170218
k8sVersion := getKubernetesVersion(existing)
171219
cc, n, err := generateClusterConfig(cmd, existing, k8sVersion, driverName)
172220
if err != nil {
173-
exit.WithError("Failed to generate config", err)
221+
return node.Starter{}, errors.Wrap(err, "Failed to generate config")
174222
}
175223

176224
// This is about as far as we can go without overwriting config files
177225
if viper.GetBool(dryRun) {
178226
out.T(out.DryRun, `dry-run validation complete!`)
179-
return
227+
os.Exit(0)
180228
}
181229

182230
if driver.IsVM(driverName) {
183231
url, err := download.ISO(viper.GetStringSlice(isoURL), cmd.Flags().Changed(isoURL))
184232
if err != nil {
185-
exit.WithError("Failed to cache ISO", err)
233+
return node.Starter{}, errors.Wrap(err, "Failed to cache ISO")
186234
}
187235
cc.MinikubeISO = url
188236
}
@@ -201,17 +249,37 @@ func runStart(cmd *cobra.Command, args []string) {
201249
}
202250
}
203251

204-
kubeconfig, err := node.Start(cc, n, existingAddons, true)
252+
mRunner, preExists, mAPI, host, err := node.Provision(&cc, &n, true)
205253
if err != nil {
206-
kubeconfig = maybeDeleteAndRetry(cc, n, existingAddons, err)
254+
return node.Starter{}, err
255+
}
256+
257+
return node.Starter{
258+
Runner: mRunner,
259+
PreExists: preExists,
260+
MachineAPI: mAPI,
261+
Host: host,
262+
ExistingAddons: existingAddons,
263+
Cfg: &cc,
264+
Node: &n,
265+
}, nil
266+
}
267+
268+
func startWithDriver(starter node.Starter, existing *config.ClusterConfig) (*kubeconfig.Settings, error) {
269+
kubeconfig, err := node.Start(starter, true)
270+
if err != nil {
271+
kubeconfig, err = maybeDeleteAndRetry(*starter.Cfg, *starter.Node, starter.ExistingAddons, err)
272+
if err != nil {
273+
return nil, err
274+
}
207275
}
208276

209277
numNodes := viper.GetInt(nodes)
210278
if numNodes == 1 && existing != nil {
211279
numNodes = len(existing.Nodes)
212280
}
213281
if numNodes > 1 {
214-
if driver.BareMetal(driverName) {
282+
if driver.BareMetal(starter.Cfg.Driver) {
215283
exit.WithCodeT(exit.Config, "The none driver is not compatible with multi-node clusters.")
216284
} else {
217285
for i := 1; i < numNodes; i++ {
@@ -220,20 +288,18 @@ func runStart(cmd *cobra.Command, args []string) {
220288
Name: nodeName,
221289
Worker: true,
222290
ControlPlane: false,
223-
KubernetesVersion: cc.KubernetesConfig.KubernetesVersion,
291+
KubernetesVersion: starter.Cfg.KubernetesConfig.KubernetesVersion,
224292
}
225293
out.Ln("") // extra newline for clarity on the command line
226-
err := node.Add(&cc, n)
294+
err := node.Add(starter.Cfg, n)
227295
if err != nil {
228-
exit.WithError("adding node", err)
296+
return nil, errors.Wrap(err, "adding node")
229297
}
230298
}
231299
}
232300
}
233301

234-
if err := showKubectlInfo(kubeconfig, cc.KubernetesConfig.KubernetesVersion, cc.Name); err != nil {
235-
glog.Errorf("kubectl info: %v", err)
236-
}
302+
return kubeconfig, nil
237303
}
238304

239305
func updateDriver(driverName string) {
@@ -303,7 +369,7 @@ func showKubectlInfo(kcs *kubeconfig.Settings, k8sVersion string, machineName st
303369
return nil
304370
}
305371

306-
func maybeDeleteAndRetry(cc config.ClusterConfig, n config.Node, existingAddons map[string]bool, originalErr error) *kubeconfig.Settings {
372+
func maybeDeleteAndRetry(cc config.ClusterConfig, n config.Node, existingAddons map[string]bool, originalErr error) (*kubeconfig.Settings, error) {
307373
if viper.GetBool(deleteOnFailure) {
308374
out.WarningT("Node {{.name}} failed to start, deleting and trying again.", out.V{"name": n.Name})
309375
// Start failed, delete the cluster and try again
@@ -318,21 +384,35 @@ func maybeDeleteAndRetry(cc config.ClusterConfig, n config.Node, existingAddons
318384
}
319385

320386
var kubeconfig *kubeconfig.Settings
321-
for _, v := range cc.Nodes {
322-
k, err := node.Start(cc, v, existingAddons, v.ControlPlane)
323-
if v.ControlPlane {
387+
for _, n := range cc.Nodes {
388+
r, p, m, h, err := node.Provision(&cc, &n, n.ControlPlane)
389+
s := node.Starter{
390+
Runner: r,
391+
PreExists: p,
392+
MachineAPI: m,
393+
Host: h,
394+
Cfg: &cc,
395+
Node: &n,
396+
ExistingAddons: existingAddons,
397+
}
398+
if err != nil {
399+
// Ok we failed again, let's bail
400+
return nil, err
401+
}
402+
403+
k, err := node.Start(s, n.ControlPlane)
404+
if n.ControlPlane {
324405
kubeconfig = k
325406
}
326407
if err != nil {
327408
// Ok we failed again, let's bail
328-
exit.WithError("Start failed after cluster deletion", err)
409+
return nil, err
329410
}
330411
}
331-
return kubeconfig
412+
return kubeconfig, nil
332413
}
333414
// Don't delete the cluster unless they ask
334-
exit.WithError("startup failed", originalErr)
335-
return nil
415+
return nil, errors.Wrap(originalErr, "startup failed")
336416
}
337417

338418
func kubectlVersion(path string) (string, error) {
@@ -360,7 +440,7 @@ func kubectlVersion(path string) (string, error) {
360440
return cv.ClientVersion.GitVersion, nil
361441
}
362442

363-
func selectDriver(existing *config.ClusterConfig) registry.DriverState {
443+
func selectDriver(existing *config.ClusterConfig) (registry.DriverState, []registry.DriverState, bool) {
364444
// Technically unrelated, but important to perform before detection
365445
driver.SetLibvirtURI(viper.GetString(kvmQemuURI))
366446

@@ -369,7 +449,7 @@ func selectDriver(existing *config.ClusterConfig) registry.DriverState {
369449
old := hostDriver(existing)
370450
ds := driver.Status(old)
371451
out.T(out.Sparkle, `Using the {{.driver}} driver based on existing profile`, out.V{"driver": ds.String()})
372-
return ds
452+
return ds, nil, true
373453
}
374454

375455
// Default to looking at the new driver parameter
@@ -389,7 +469,7 @@ func selectDriver(existing *config.ClusterConfig) registry.DriverState {
389469
exit.WithCodeT(exit.Unavailable, "The driver '{{.driver}}' is not supported on {{.os}}", out.V{"driver": d, "os": runtime.GOOS})
390470
}
391471
out.T(out.Sparkle, `Using the {{.driver}} driver based on user configuration`, out.V{"driver": ds.String()})
392-
return ds
472+
return ds, nil, true
393473
}
394474

395475
// Fallback to old driver parameter
@@ -399,7 +479,7 @@ func selectDriver(existing *config.ClusterConfig) registry.DriverState {
399479
exit.WithCodeT(exit.Unavailable, "The driver '{{.driver}}' is not supported on {{.os}}", out.V{"driver": d, "os": runtime.GOOS})
400480
}
401481
out.T(out.Sparkle, `Using the {{.driver}} driver based on user configuration`, out.V{"driver": ds.String()})
402-
return ds
482+
return ds, nil, true
403483
}
404484

405485
choices := driver.Choices(viper.GetBool("vm"))
@@ -422,7 +502,7 @@ func selectDriver(existing *config.ClusterConfig) registry.DriverState {
422502
} else {
423503
out.T(out.Sparkle, `Automatically selected the {{.driver}} driver`, out.V{"driver": pick.String()})
424504
}
425-
return pick
505+
return pick, alts, false
426506
}
427507

428508
// hostDriver returns the actual driver used by a libmachine host, which can differ from our config

pkg/minikube/exit/exit.go

+1-32
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,13 @@ limitations under the License.
1818
package exit
1919

2020
import (
21-
"fmt"
2221
"os"
2322
"runtime"
2423
"runtime/debug"
2524

2625
"github.com/golang/glog"
2726
"k8s.io/minikube/pkg/minikube/out"
2827
"k8s.io/minikube/pkg/minikube/problem"
29-
"k8s.io/minikube/pkg/minikube/translate"
3028
)
3129

3230
// Exit codes based on sysexits(3)
@@ -41,9 +39,6 @@ const (
4139
IO = 74 // IO represents an I/O error
4240
Config = 78 // Config represents an unconfigured or misconfigured state
4341
Permissions = 77 // Permissions represents a permissions error
44-
45-
// MaxLogEntries controls the number of log entries to show for each source
46-
MaxLogEntries = 3
4742
)
4843

4944
// UsageT outputs a templated usage error and exits with error code 64
@@ -65,7 +60,7 @@ func WithError(msg string, err error) {
6560
if p != nil {
6661
WithProblem(msg, err, p)
6762
}
68-
displayError(msg, err)
63+
out.DisplayError(msg, err)
6964
os.Exit(Software)
7065
}
7166

@@ -81,29 +76,3 @@ func WithProblem(msg string, err error, p *problem.Problem) {
8176
}
8277
os.Exit(Config)
8378
}
84-
85-
// WithLogEntries outputs an error along with any important log entries, and exits.
86-
func WithLogEntries(msg string, err error, entries map[string][]string) {
87-
displayError(msg, err)
88-
89-
for name, lines := range entries {
90-
out.FailureT("Problems detected in {{.entry}}:", out.V{"entry": name})
91-
if len(lines) > MaxLogEntries {
92-
lines = lines[:MaxLogEntries]
93-
}
94-
for _, l := range lines {
95-
out.T(out.LogEntry, l)
96-
}
97-
}
98-
os.Exit(Software)
99-
}
100-
101-
func displayError(msg string, err error) {
102-
// use Warning because Error will display a duplicate message to stderr
103-
glog.Warningf(fmt.Sprintf("%s: %v", msg, err))
104-
out.ErrT(out.Empty, "")
105-
out.FatalT("{{.msg}}: {{.err}}", out.V{"msg": translate.T(msg), "err": err})
106-
out.ErrT(out.Empty, "")
107-
out.ErrT(out.Sad, "minikube is exiting due to an error. If the above message is not useful, open an issue:")
108-
out.ErrT(out.URL, "https://github.com/kubernetes/minikube/issues/new/choose")
109-
}

pkg/minikube/node/cache.go

-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ func handleDownloadOnly(cacheGroup, kicGroup *errgroup.Group, k8sVersion string)
8080
}
8181
out.T(out.Check, "Download complete!")
8282
os.Exit(0)
83-
8483
}
8584

8685
// CacheKubectlBinary caches the kubectl binary

pkg/minikube/node/node.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,21 @@ func Add(cc *config.ClusterConfig, n config.Node) error {
3939
return errors.Wrap(err, "save node")
4040
}
4141

42-
_, err := Start(*cc, n, nil, false)
42+
r, p, m, h, err := Provision(cc, &n, false)
43+
if err != nil {
44+
return err
45+
}
46+
s := Starter{
47+
Runner: r,
48+
PreExists: p,
49+
MachineAPI: m,
50+
Host: h,
51+
Cfg: cc,
52+
Node: &n,
53+
ExistingAddons: nil,
54+
}
55+
56+
_, err = Start(s, false)
4357
return err
4458
}
4559

0 commit comments

Comments
 (0)