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 189 190 191 192 193 194 195 196 197
|
package validation
import (
"encoding/json"
"errors"
"fmt"
"net"
"net/url"
"os"
"strings"
"github.com/crc-org/crc/v2/pkg/crc/constants"
"github.com/crc-org/crc/v2/pkg/crc/image"
"github.com/crc-org/crc/v2/pkg/crc/logging"
"github.com/crc-org/crc/v2/pkg/crc/machine/bundle"
crcpreset "github.com/crc-org/crc/v2/pkg/crc/preset"
"github.com/docker/go-units"
"github.com/pbnjay/memory"
)
// ValidateCPUs checks if provided cpus count is valid
func ValidateCPUs(value int, preset crcpreset.Preset) error {
if value < constants.GetDefaultCPUs(preset) {
return fmt.Errorf("requires CPUs >= %d", constants.GetDefaultCPUs(preset))
}
return nil
}
// ValidateMemory checks if provided Memory count is valid
func ValidateMemory(value int, preset crcpreset.Preset) error {
if value < constants.GetDefaultMemory(preset) {
return fmt.Errorf("requires memory in MiB >= %d", constants.GetDefaultMemory(preset))
}
return ValidateEnoughMemory(value)
}
func ValidateDiskSize(value int) error {
if value < constants.DefaultDiskSize {
return fmt.Errorf("requires disk size in GiB >= %d", constants.DefaultDiskSize)
}
return nil
}
func ValidatePersistentVolumeSize(value int) error {
if value < constants.DefaultPersistentVolumeSize {
return fmt.Errorf("requires disk size in GiB >= %d", constants.DefaultPersistentVolumeSize)
}
return nil
}
// ValidateEnoughMemory checks if enough memory is installed on the host
func ValidateEnoughMemory(value int) error {
totalMemory := memory.TotalMemory()
logging.Debugf("Total memory of system is %d bytes", totalMemory)
valueBytes := value * 1024 * 1024
if totalMemory < uint64(valueBytes) {
return fmt.Errorf("only %s of memory found (%s required)",
units.HumanSize(float64(totalMemory)),
units.HumanSize(float64(valueBytes)))
}
return nil
}
// ValidateBundlePath checks if the provided bundle path exist
func ValidateBundlePath(bundlePath string, preset crcpreset.Preset) error {
logging.Debugf("Got bundle path: %s", bundlePath)
if err := ValidateURL(bundlePath); err != nil {
var urlError *url.Error
var invalidPathError *invalidPath
// If error occur due to invalid path, then return with a more meaningful error message
if errors.As(err, &invalidPathError) {
return fmt.Errorf("%s is invalid or missing, run 'crc setup' to download the default bundle", bundlePath)
}
// Some local paths (for example relative paths) can't be parsed/validated by `ValidateURL` and will be validated here
if errors.As(err, &urlError) {
if err1 := ValidatePath(bundlePath); err1 != nil {
return fmt.Errorf("%s is invalid or missing, run 'crc setup' to download the default bundle", bundlePath)
}
} else {
return err
}
}
userProvidedBundle := bundle.GetBundleNameFromURI(bundlePath)
bundleMismatchWarning(userProvidedBundle, preset)
return nil
}
func ValidateBundle(bundlePath string, preset crcpreset.Preset) error {
bundleName := bundle.GetBundleNameFromURI(bundlePath)
bundleMetadata, err := bundle.Get(bundleName)
if err != nil {
if bundlePath == constants.GetDefaultBundlePath(preset) {
return nil
}
return ValidateBundlePath(bundlePath, preset)
}
bundleMismatchWarning(bundleMetadata.GetBundleName(), preset)
/* 'bundle' is already unpacked in ~/.crc/cache */
return nil
}
func bundleMismatchWarning(userProvidedBundle string, preset crcpreset.Preset) {
userProvidedBundle = bundle.GetBundleNameWithExtension(userProvidedBundle)
if userProvidedBundle != constants.GetDefaultBundle(preset) {
// Should append underscore (_) here, as we don't want crc_libvirt_4.7.15.crcbundle
// to be detected as a custom bundle for crc_libvirt_4.7.1.crcbundle
usingCustomBundle := strings.HasPrefix(bundle.GetBundleNameWithoutExtension(userProvidedBundle),
fmt.Sprintf("%s_", bundle.GetBundleNameWithoutExtension(constants.GetDefaultBundle(preset))))
if usingCustomBundle {
logging.Warnf("Using custom bundle %s", userProvidedBundle)
} else {
logging.Warnf("Using %s bundle, but %s is expected for this release", userProvidedBundle, constants.GetDefaultBundle(preset))
}
}
}
// ValidateIPAddress checks if provided IP is valid
func ValidateIPAddress(ipAddress string) error {
ip := net.ParseIP(ipAddress).To4()
if ip == nil {
return fmt.Errorf("'%s' is not a valid IPv4 address", ipAddress)
}
return nil
}
func ValidateURL(uri string) error {
u, err := url.ParseRequestURI(uri)
if err != nil {
logging.Debugf("Failed to parse url: %v", err)
return err
}
switch {
// If uri string is without scheme then check if it is a valid absolute path.
// Relative paths will cause `ParseRequestURI` to error out, and will be handled in `ValidateBundlePath`
case !u.IsAbs():
return ValidatePath(uri)
// In case of windows where path started with C:\<path> the uri scheme would be `C`
// We are going to check if the uri scheme is a single letter and assume that it is windows drive path url
// and send it to ValidatePath
case len(u.Scheme) == 1:
return ValidatePath(uri)
case u.Scheme == "http", u.Scheme == "https":
return nil
case u.Scheme == "docker":
return image.ValidateURI(u)
default:
return fmt.Errorf("invalid %s format (only supported http, https or docker)", uri)
}
}
type invalidPath struct {
path string
}
func (e *invalidPath) Error() string {
return fmt.Sprintf("file '%s' does not exist", e.path)
}
// ValidatePath check if provide path is exist
func ValidatePath(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return &invalidPath{path: path}
}
return nil
}
type imagePullSecret struct {
Auths map[string]map[string]interface{} `json:"auths"`
}
// ImagePullSecret checks if the given string is a valid image pull secret and returns an error if not.
func ImagePullSecret(secret string) error {
if secret == "" {
return errors.New("empty pull secret")
}
var s imagePullSecret
err := json.Unmarshal([]byte(secret), &s)
if err != nil {
return fmt.Errorf("invalid pull secret: %v", err)
}
if len(s.Auths) == 0 {
return fmt.Errorf("invalid pull secret: missing 'auths' JSON-object field")
}
for d, a := range s.Auths {
_, authPresent := a["auth"]
_, credsStorePresent := a["credsStore"]
if !authPresent && !credsStorePresent {
return fmt.Errorf("invalid pull secret, '%q' JSON-object requires either 'auth' or 'credsStore' field", d)
}
}
return nil
}
|