@@ -150,11 +150,59 @@ func runStart(cmd *cobra.Command, args []string) {
150
150
}
151
151
152
152
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 ) {
154
202
driverName := ds .Name
155
203
glog .Infof ("selected driver: %s" , driverName )
156
204
validateDriver (ds , existing )
157
- err = autoSetDriverOptions (cmd , driverName )
205
+ err : = autoSetDriverOptions (cmd , driverName )
158
206
if err != nil {
159
207
glog .Errorf ("Error autoSetOptions : %v" , err )
160
208
}
@@ -170,19 +218,19 @@ func runStart(cmd *cobra.Command, args []string) {
170
218
k8sVersion := getKubernetesVersion (existing )
171
219
cc , n , err := generateClusterConfig (cmd , existing , k8sVersion , driverName )
172
220
if err != nil {
173
- exit . WithError ( "Failed to generate config" , err )
221
+ return node. Starter {}, errors . Wrap ( err , "Failed to generate config" )
174
222
}
175
223
176
224
// This is about as far as we can go without overwriting config files
177
225
if viper .GetBool (dryRun ) {
178
226
out .T (out .DryRun , `dry-run validation complete!` )
179
- return
227
+ os . Exit ( 0 )
180
228
}
181
229
182
230
if driver .IsVM (driverName ) {
183
231
url , err := download .ISO (viper .GetStringSlice (isoURL ), cmd .Flags ().Changed (isoURL ))
184
232
if err != nil {
185
- exit . WithError ( "Failed to cache ISO" , err )
233
+ return node. Starter {}, errors . Wrap ( err , "Failed to cache ISO" )
186
234
}
187
235
cc .MinikubeISO = url
188
236
}
@@ -201,17 +249,37 @@ func runStart(cmd *cobra.Command, args []string) {
201
249
}
202
250
}
203
251
204
- kubeconfig , err := node .Start ( cc , n , existingAddons , true )
252
+ mRunner , preExists , mAPI , host , err := node .Provision ( & cc , & n , true )
205
253
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
+ }
207
275
}
208
276
209
277
numNodes := viper .GetInt (nodes )
210
278
if numNodes == 1 && existing != nil {
211
279
numNodes = len (existing .Nodes )
212
280
}
213
281
if numNodes > 1 {
214
- if driver .BareMetal (driverName ) {
282
+ if driver .BareMetal (starter . Cfg . Driver ) {
215
283
exit .WithCodeT (exit .Config , "The none driver is not compatible with multi-node clusters." )
216
284
} else {
217
285
for i := 1 ; i < numNodes ; i ++ {
@@ -220,20 +288,18 @@ func runStart(cmd *cobra.Command, args []string) {
220
288
Name : nodeName ,
221
289
Worker : true ,
222
290
ControlPlane : false ,
223
- KubernetesVersion : cc .KubernetesConfig .KubernetesVersion ,
291
+ KubernetesVersion : starter . Cfg .KubernetesConfig .KubernetesVersion ,
224
292
}
225
293
out .Ln ("" ) // extra newline for clarity on the command line
226
- err := node .Add (& cc , n )
294
+ err := node .Add (starter . Cfg , n )
227
295
if err != nil {
228
- exit . WithError ( "adding node" , err )
296
+ return nil , errors . Wrap ( err , "adding node" )
229
297
}
230
298
}
231
299
}
232
300
}
233
301
234
- if err := showKubectlInfo (kubeconfig , cc .KubernetesConfig .KubernetesVersion , cc .Name ); err != nil {
235
- glog .Errorf ("kubectl info: %v" , err )
236
- }
302
+ return kubeconfig , nil
237
303
}
238
304
239
305
func updateDriver (driverName string ) {
@@ -303,7 +369,7 @@ func showKubectlInfo(kcs *kubeconfig.Settings, k8sVersion string, machineName st
303
369
return nil
304
370
}
305
371
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 ) {
307
373
if viper .GetBool (deleteOnFailure ) {
308
374
out .WarningT ("Node {{.name}} failed to start, deleting and trying again." , out.V {"name" : n .Name })
309
375
// Start failed, delete the cluster and try again
@@ -318,21 +384,35 @@ func maybeDeleteAndRetry(cc config.ClusterConfig, n config.Node, existingAddons
318
384
}
319
385
320
386
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 {
324
405
kubeconfig = k
325
406
}
326
407
if err != nil {
327
408
// Ok we failed again, let's bail
328
- exit . WithError ( "Start failed after cluster deletion" , err )
409
+ return nil , err
329
410
}
330
411
}
331
- return kubeconfig
412
+ return kubeconfig , nil
332
413
}
333
414
// 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" )
336
416
}
337
417
338
418
func kubectlVersion (path string ) (string , error ) {
@@ -360,7 +440,7 @@ func kubectlVersion(path string) (string, error) {
360
440
return cv .ClientVersion .GitVersion , nil
361
441
}
362
442
363
- func selectDriver (existing * config.ClusterConfig ) registry.DriverState {
443
+ func selectDriver (existing * config.ClusterConfig ) ( registry.DriverState , []registry. DriverState , bool ) {
364
444
// Technically unrelated, but important to perform before detection
365
445
driver .SetLibvirtURI (viper .GetString (kvmQemuURI ))
366
446
@@ -369,7 +449,7 @@ func selectDriver(existing *config.ClusterConfig) registry.DriverState {
369
449
old := hostDriver (existing )
370
450
ds := driver .Status (old )
371
451
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
373
453
}
374
454
375
455
// Default to looking at the new driver parameter
@@ -389,7 +469,7 @@ func selectDriver(existing *config.ClusterConfig) registry.DriverState {
389
469
exit .WithCodeT (exit .Unavailable , "The driver '{{.driver}}' is not supported on {{.os}}" , out.V {"driver" : d , "os" : runtime .GOOS })
390
470
}
391
471
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
393
473
}
394
474
395
475
// Fallback to old driver parameter
@@ -399,7 +479,7 @@ func selectDriver(existing *config.ClusterConfig) registry.DriverState {
399
479
exit .WithCodeT (exit .Unavailable , "The driver '{{.driver}}' is not supported on {{.os}}" , out.V {"driver" : d , "os" : runtime .GOOS })
400
480
}
401
481
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
403
483
}
404
484
405
485
choices := driver .Choices (viper .GetBool ("vm" ))
@@ -422,7 +502,7 @@ func selectDriver(existing *config.ClusterConfig) registry.DriverState {
422
502
} else {
423
503
out .T (out .Sparkle , `Automatically selected the {{.driver}} driver` , out.V {"driver" : pick .String ()})
424
504
}
425
- return pick
505
+ return pick , alts , false
426
506
}
427
507
428
508
// hostDriver returns the actual driver used by a libmachine host, which can differ from our config
0 commit comments