1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
package preflight
import (
"fmt"
crcConfig "github.com/crc-org/crc/v2/pkg/crc/config"
"github.com/crc-org/crc/v2/pkg/crc/errors"
"github.com/crc-org/crc/v2/pkg/crc/logging"
)
type Flags uint32
const (
// Indicates a PreflightCheck should only be run as part of "crc setup"
SetupOnly Flags = 1 << iota
NoFix
CleanUpOnly
StartUpOnly
)
type CheckFunc func() error
type FixFunc func() error
type CleanUpFunc func() error
type Check struct {
configKeySuffix string
checkDescription string
check CheckFunc
fixDescription string
fix FixFunc
flags Flags
cleanupDescription string
cleanup CleanUpFunc
labels labels
}
func (check *Check) getSkipConfigName() string {
if check.configKeySuffix == "" {
return ""
}
return "skip-" + check.configKeySuffix
}
func (check *Check) shouldSkip(config crcConfig.Storage) bool {
if check.configKeySuffix == "" {
return false
}
return config.Get(check.getSkipConfigName()).AsBool()
}
func (check *Check) doCheck(config crcConfig.Storage) error {
if check.checkDescription == "" {
panic(fmt.Sprintf("Should not happen, empty description for check '%s'", check.configKeySuffix))
}
logging.Infof("%s", check.checkDescription)
if check.shouldSkip(config) {
logging.Warn("Skipping above check...")
return nil
}
err := check.check()
if err != nil {
logging.Debug(err.Error())
}
return err
}
func (check *Check) doFix() error {
if check.fixDescription == "" {
panic(fmt.Sprintf("Should not happen, empty description for fix '%s'", check.configKeySuffix))
}
if check.flags&NoFix == NoFix {
return fmt.Errorf(check.fixDescription)
}
logging.Infof("%s", check.fixDescription)
return check.fix()
}
func (check *Check) doCleanUp() error {
if check.cleanupDescription == "" {
panic(fmt.Sprintf("Should not happen, empty description for cleanup '%s'", check.configKeySuffix))
}
logging.Infof("%s", check.cleanupDescription)
return check.cleanup()
}
func doPreflightChecks(config crcConfig.Storage, checks []Check) error {
for _, check := range checks {
if check.flags&SetupOnly == SetupOnly || check.flags&CleanUpOnly == CleanUpOnly {
continue
}
if err := check.doCheck(config); err != nil {
return err
}
}
return nil
}
func doFixPreflightChecks(config crcConfig.Storage, checks []Check, checkOnly bool) error {
for _, check := range checks {
if check.flags&CleanUpOnly == CleanUpOnly || check.flags&StartUpOnly == StartUpOnly {
continue
}
err := check.doCheck(config)
if err == nil {
continue
} else if checkOnly {
return err
}
if err = check.doFix(); err != nil {
return err
}
}
return nil
}
func doCleanUpPreflightChecks(checks []Check) error {
var mErr errors.MultiError
// Do the cleanup in reverse order to avoid any dependency during cleanup
for i := len(checks) - 1; i >= 0; i-- {
check := checks[i]
if check.cleanup == nil {
continue
}
err := check.doCleanUp()
if err != nil {
// If an error occurs in a cleanup function
// we log/collect it and move to the next
logging.Debug(err)
mErr.Collect(err)
}
}
if len(mErr.Errors) == 0 {
return nil
}
return mErr
}
func doRegisterSettings(cfg crcConfig.Schema, checks []Check) {
for _, check := range checks {
if check.configKeySuffix != "" {
cfg.AddSetting(check.getSkipConfigName(), false, crcConfig.ValidateBool, crcConfig.SuccessfullyApplied,
"Skip preflight check (true/false, default: false)")
}
}
}
func getPreflightChecksHelper(config crcConfig.Storage) []Check {
experimentalFeatures := config.Get(crcConfig.ExperimentalFeatures).AsBool()
mode := crcConfig.GetNetworkMode(config)
bundlePath := config.Get(crcConfig.Bundle).AsString()
preset := crcConfig.GetPreset(config)
enableBundleQuayFallback := config.Get(crcConfig.EnableBundleQuayFallback).AsBool()
logging.Infof("Using bundle path %s", bundlePath)
return getPreflightChecks(experimentalFeatures, mode, bundlePath, preset, enableBundleQuayFallback)
}
// StartPreflightChecks performs the preflight checks before starting the cluster
func StartPreflightChecks(config crcConfig.Storage) error {
if err := doPreflightChecks(config, getPreflightChecksHelper(config)); err != nil {
return &errors.PreflightError{Err: err}
}
return nil
}
// SetupHost performs the prerequisite checks and setups the host to run the cluster
func SetupHost(config crcConfig.Storage, checkOnly bool) error {
return doFixPreflightChecks(config, getPreflightChecksHelper(config), checkOnly)
}
func RegisterSettings(config crcConfig.Schema) {
doRegisterSettings(config, getAllPreflightChecks())
}
func CleanUpHost() error {
// A user can use setup with experiment flag
// and not use cleanup with same flag, to avoid
// any extra step/confusion we are just adding the checks
// which are behind the experiment flag. This way cleanup
// perform action in a sane way.
return doCleanUpPreflightChecks(getAllPreflightChecks())
}
|