@@ -88,6 +88,17 @@ func RunHostPreflights(c *cli.Context) error {
88
88
if err != nil {
89
89
return fmt .Errorf ("unable to read host preflights: %w" , err )
90
90
}
91
+
92
+ chpfs , err := preflights .GetClusterHostPreflights (c .Context )
93
+ if err != nil {
94
+ return fmt .Errorf ("unable to get cluster host preflights: %w" , err )
95
+ }
96
+
97
+ for _ , h := range chpfs {
98
+ hpf .Collectors = append (hpf .Collectors , h .Spec .Collectors ... )
99
+ hpf .Analyzers = append (hpf .Analyzers , h .Spec .Analyzers ... )
100
+ }
101
+
91
102
return runHostPreflights (c , hpf )
92
103
}
93
104
@@ -96,25 +107,61 @@ func runHostPreflights(c *cli.Context, hpf *v1beta2.HostPreflightSpec) error {
96
107
return nil
97
108
}
98
109
pb := spinner .Start ()
99
- pb .Infof ("Running host preflights on node" )
110
+ if c .Bool ("skip-host-preflights" ) {
111
+ pb .Infof ("Skipping host preflights" )
112
+ pb .Close ()
113
+ return nil
114
+ }
115
+ pb .Infof ("Running host preflights" )
100
116
output , err := preflights .Run (c .Context , hpf )
101
117
if err != nil {
102
118
pb .CloseWithError ()
103
119
return fmt .Errorf ("host preflights failed: %w" , err )
104
120
}
121
+
122
+ err = output .SaveToDisk ()
123
+ if err != nil {
124
+ pb .CloseWithError ()
125
+ return fmt .Errorf ("failed to save preflights output: %w" , err )
126
+ }
127
+
105
128
if output .HasFail () {
129
+ s := "failures"
130
+ if len (output .Fail ) == 1 {
131
+ s = "failure"
132
+ }
133
+ msg := fmt .Sprintf ("Host preflights have %d %s" , len (output .Fail ), s )
134
+ if output .HasWarn () {
135
+ s = "warnings"
136
+ if len (output .Warn ) == 1 {
137
+ s = "warning"
138
+ }
139
+ msg += fmt .Sprintf (" and %d %s" , len (output .Warn ), s )
140
+ }
141
+
142
+ pb .Errorf (msg )
106
143
pb .CloseWithError ()
107
- output .PrintTable ()
144
+ output .PrintTableWithoutInfo ()
108
145
return fmt .Errorf ("preflights haven't passed on the host" )
109
146
}
110
- if ! output .HasWarn () || c .Bool ("no-prompt" ) {
147
+ if ! output .HasWarn () {
148
+ pb .Close ()
149
+ return nil
150
+ }
151
+ if c .Bool ("no-prompt" ) {
152
+ // We have warnings but we are not in interactive mode
153
+ // so we just print the warnings and continue
111
154
pb .Close ()
112
- output .PrintTable ()
155
+ output .PrintTableWithoutInfo ()
113
156
return nil
114
157
}
158
+ s := "warnings"
159
+ if len (output .Warn ) == 1 {
160
+ s = "warning"
161
+ }
162
+ pb .Warnf ("Host preflights have %d %s" , len (output .Warn ), s )
115
163
pb .CloseWithError ()
116
- output .PrintTable ()
117
- logrus .Infof ("Host preflights have warnings" )
164
+ output .PrintTableWithoutInfo ()
118
165
if ! prompts .New ().Confirm ("Do you want to continue ?" , false ) {
119
166
return fmt .Errorf ("user aborted" )
120
167
}
@@ -136,7 +183,7 @@ func isAlreadyInstalled() (bool, error) {
136
183
}
137
184
}
138
185
139
- func checkLicenseMatches (c * cli. Context ) error {
186
+ func checkLicenseMatches (licenseFile string ) error {
140
187
rel , err := release .GetChannelRelease ()
141
188
if err != nil {
142
189
return fmt .Errorf ("failed to get release from binary: %w" , err ) // this should only be if the release is malformed
@@ -146,20 +193,20 @@ func checkLicenseMatches(c *cli.Context) error {
146
193
// 1. no release and no license, which is OK
147
194
// 2. no license and a release, which is not OK
148
195
// 3. a license and no release, which is not OK
149
- if rel == nil && c . String ( "license" ) == "" {
196
+ if rel == nil && licenseFile == "" {
150
197
// no license and no release, this is OK
151
198
return nil
152
- } else if rel == nil && c . String ( "license" ) != "" {
199
+ } else if rel == nil && licenseFile != "" {
153
200
// license is present but no release, this means we would install without vendor charts and k0s overrides
154
201
return fmt .Errorf ("a license was provided but no release was found in binary, please rerun without the license flag" )
155
- } else if rel != nil && c . String ( "license" ) == "" {
202
+ } else if rel != nil && licenseFile == "" {
156
203
// release is present but no license, this is not OK
157
204
return fmt .Errorf ("no license was provided for %s and one is required, please rerun with '--license <path to license file>'" , rel .AppSlug )
158
205
}
159
206
160
- license , err := helpers .ParseLicense (c . String ( "license" ) )
207
+ license , err := helpers .ParseLicense (licenseFile )
161
208
if err != nil {
162
- return fmt .Errorf ("unable to parse the license file at %q, please ensure it is not corrupt: %w" , c . String ( "license" ) , err )
209
+ return fmt .Errorf ("unable to parse the license file at %q, please ensure it is not corrupt: %w" , licenseFile , err )
163
210
}
164
211
165
212
// Check if the license matches the application version data
@@ -173,6 +220,21 @@ func checkLicenseMatches(c *cli.Context) error {
173
220
return fmt .Errorf ("license channel %s (%s) does not match binary channel %s, please provide the correct license" , license .Spec .ChannelID , license .Spec .ChannelName , rel .ChannelID )
174
221
}
175
222
223
+ if license .Spec .Entitlements ["expires_at" ].Value .StrVal != "" {
224
+ // read the expiration date, and check it against the current date
225
+ expiration , err := time .Parse (time .RFC3339 , license .Spec .Entitlements ["expires_at" ].Value .StrVal )
226
+ if err != nil {
227
+ return fmt .Errorf ("unable to parse expiration date: %w" , err )
228
+ }
229
+ if time .Now ().After (expiration ) {
230
+ return fmt .Errorf ("license expired on %s, please provide a valid license" , expiration )
231
+ }
232
+ }
233
+
234
+ if ! license .Spec .IsEmbeddedClusterDownloadEnabled {
235
+ return fmt .Errorf ("license does not have embedded cluster enabled, please provide a valid license" )
236
+ }
237
+
176
238
return nil
177
239
}
178
240
@@ -375,7 +437,7 @@ func waitForK0s() error {
375
437
}
376
438
377
439
// runOutro calls Outro() in all enabled addons by means of Applier.
378
- func runOutro (c * cli.Context ) error {
440
+ func runOutro (c * cli.Context , adminConsolePwd string ) error {
379
441
os .Setenv ("KUBECONFIG" , defaults .PathToKubeConfig ())
380
442
opts := []addons.Option {}
381
443
@@ -398,12 +460,33 @@ func runOutro(c *cli.Context) error {
398
460
if ab := c .String ("airgap-bundle" ); ab != "" {
399
461
opts = append (opts , addons .WithAirgapBundle (ab ))
400
462
}
463
+ opts = append (opts , addons .WithAdminConsolePassword (adminConsolePwd ))
401
464
if c .String ("http-proxy" ) != "" || c .String ("https-proxy" ) != "" || c .String ("no-proxy" ) != "" {
402
465
opts = append (opts , addons .WithProxyFromArgs (c .String ("http-proxy" ), c .String ("https-proxy" ), c .String ("no-proxy" )))
403
466
}
404
467
return addons .NewApplier (opts ... ).Outro (c .Context )
405
468
}
406
469
470
+ func askAdminConsolePassword (c * cli.Context ) (string , error ) {
471
+ defaultPass := "password"
472
+ if c .Bool ("no-prompt" ) {
473
+ logrus .Infof ("Admin Console password set to: %s" , defaultPass )
474
+ return defaultPass , nil
475
+ }
476
+ maxTries := 3
477
+ for i := 0 ; i < maxTries ; i ++ {
478
+ promptA := prompts .New ().Password ("Enter an Admin Console password:" )
479
+ promptB := prompts .New ().Password ("Confirm password:" )
480
+
481
+ if promptA == promptB {
482
+ // TODO: Should we add extra password validation here? e.g length, complexity etc
483
+ return promptA , nil
484
+ }
485
+ logrus .Info ("Passwords don't match, please try again." )
486
+ }
487
+ return "" , fmt .Errorf ("unable to set Admin Console password after %d tries" , maxTries )
488
+ }
489
+
407
490
// installCommands executes the "install" command. This will ensure that a k0s.yaml file exists
408
491
// and then run `k0s install` to apply the cluster. Once this is finished then a "kubeconfig"
409
492
// file is created. Resulting kubeconfig is stored in the configuration dir.
@@ -461,6 +544,11 @@ var installCommand = &cli.Command{
461
544
Usage : "Use the system proxy settings for the install operation. These variables are currently only passed through to Velero and the Admin Console." ,
462
545
Hidden : true ,
463
546
},
547
+ & cli.BoolFlag {
548
+ Name : "skip-host-preflights" ,
549
+ Usage : "Skip host preflight checks. This is not recommended unless you are sure your system is compatible." ,
550
+ Value : false ,
551
+ },
464
552
},
465
553
Action : func (c * cli.Context ) error {
466
554
logrus .Debugf ("checking if %s is already installed" , binName )
@@ -479,7 +567,7 @@ var installCommand = &cli.Command{
479
567
return fmt .Errorf ("unable to configure network manager: %w" , err )
480
568
}
481
569
logrus .Debugf ("checking license matches" )
482
- if err := checkLicenseMatches (c ); err != nil {
570
+ if err := checkLicenseMatches (c . String ( "license" ) ); err != nil {
483
571
metricErr := fmt .Errorf ("unable to check license: %w" , err )
484
572
metrics .ReportApplyFinished (c , metricErr )
485
573
return err // do not return the metricErr, as we want the user to see the error message without a prefix
@@ -495,6 +583,11 @@ var installCommand = &cli.Command{
495
583
metrics .ReportApplyFinished (c , err )
496
584
return err
497
585
}
586
+ adminConsolePwd , err := askAdminConsolePassword (c )
587
+ if err != nil {
588
+ metrics .ReportApplyFinished (c , err )
589
+ return err
590
+ }
498
591
logrus .Debugf ("running host preflights" )
499
592
if err := RunHostPreflights (c ); err != nil {
500
593
err := fmt .Errorf ("unable to finish preflight checks: %w" , err )
@@ -534,7 +627,7 @@ var installCommand = &cli.Command{
534
627
return err
535
628
}
536
629
logrus .Debugf ("running outro" )
537
- if err := runOutro (c ); err != nil {
630
+ if err := runOutro (c , adminConsolePwd ); err != nil {
538
631
metrics .ReportApplyFinished (c , err )
539
632
return err
540
633
}
0 commit comments