From 368707bc9cba126169fd1a7aac14d79f68a5ae84 Mon Sep 17 00:00:00 2001 From: Christophe Fergeau Date: Thu, 6 Aug 2020 18:32:25 +0200 Subject: [PATCH] Add resize command boilerplate This adds a --disk-size command line argument to `crc start`, as well as a 'disk-size' config option. The size is specified in GiB. Upon next crc start, the size of the VM disk will be automatically resized. This relies on machine driver support, the UpdateConfigRaw implementation must be able to resize the disk image. Once that is done, `xfs_growfs` is called on VM startup in order to grow the size of the VM partition. --- cmd/crc/cmd/config/config.go | 2 ++ cmd/crc/cmd/start.go | 5 +++++ pkg/crc/config/validations.go | 13 +++++++++++++ pkg/crc/constants/constants.go | 7 ++++--- pkg/crc/machine/driver.go | 12 ++++++++++++ pkg/crc/machine/machine.go | 18 ++++++++++++++++++ pkg/crc/machine/types.go | 5 +++-- pkg/crc/validation/validation.go | 11 +++++++++++ 8 files changed, 68 insertions(+), 5 deletions(-) diff --git a/cmd/crc/cmd/config/config.go b/cmd/crc/cmd/config/config.go index 41ded0624b..5c79e4c99b 100644 --- a/cmd/crc/cmd/config/config.go +++ b/cmd/crc/cmd/config/config.go @@ -13,6 +13,7 @@ const ( Bundle = "bundle" CPUs = "cpus" Memory = "memory" + DiskSize = "disk-size" NameServer = "nameserver" PullSecretFile = "pull-secret-file" DisableUpdateCheck = "disable-update-check" @@ -28,6 +29,7 @@ func RegisterSettings(cfg *config.Config) { cfg.AddSetting(Bundle, constants.DefaultBundlePath, config.ValidateBundle, config.SuccessfullyApplied) cfg.AddSetting(CPUs, constants.DefaultCPUs, config.ValidateCPUs, config.RequiresRestartMsg) cfg.AddSetting(Memory, constants.DefaultMemory, config.ValidateMemory, config.RequiresRestartMsg) + cfg.AddSetting(DiskSize, 0, config.ValidateDiskSize, config.RequiresRestartMsg) cfg.AddSetting(NameServer, "", config.ValidateIPAddress, config.SuccessfullyApplied) cfg.AddSetting(PullSecretFile, "", config.ValidatePath, config.SuccessfullyApplied) cfg.AddSetting(DisableUpdateCheck, false, config.ValidateBool, config.SuccessfullyApplied) diff --git a/cmd/crc/cmd/start.go b/cmd/crc/cmd/start.go index 293eec5348..24f93a7e62 100644 --- a/cmd/crc/cmd/start.go +++ b/cmd/crc/cmd/start.go @@ -31,6 +31,7 @@ func init() { flagSet.StringP(cmdConfig.PullSecretFile, "p", "", fmt.Sprintf("File path of image pull secret (download from %s)", constants.CrcLandingPageURL)) flagSet.IntP(cmdConfig.CPUs, "c", constants.DefaultCPUs, "Number of CPU cores to allocate to the OpenShift cluster") flagSet.IntP(cmdConfig.Memory, "m", constants.DefaultMemory, "MiB of memory to allocate to the OpenShift cluster") + flagSet.UintP(cmdConfig.DiskSize, "d", 0, "Total size in GiB of the disk used by the CodeReady Container instance") flagSet.StringP(cmdConfig.NameServer, "n", "", "IPv4 address of nameserver to use for the OpenShift cluster") flagSet.Bool(cmdConfig.DisableUpdateCheck, false, "Don't check for update") @@ -65,6 +66,7 @@ func runStart(arguments []string) (*machine.StartResult, error) { startConfig := machine.StartConfig{ BundlePath: config.Get(cmdConfig.Bundle).AsString(), Memory: config.Get(cmdConfig.Memory).AsInt(), + DiskSize: config.Get(cmdConfig.DiskSize).AsInt(), CPUs: config.Get(cmdConfig.CPUs).AsInt(), NameServer: config.Get(cmdConfig.NameServer).AsString(), PullSecret: &cluster.PullSecret{ @@ -161,6 +163,9 @@ func validateStartFlags() error { if err := validation.ValidateCPUs(config.Get(cmdConfig.CPUs).AsInt()); err != nil { return err } + if err := validation.ValidateDiskSize(config.Get(cmdConfig.DiskSize).AsInt()); err != nil { + return err + } if err := validation.ValidateBundle(config.Get(cmdConfig.Bundle).AsString()); err != nil { return err } diff --git a/pkg/crc/config/validations.go b/pkg/crc/config/validations.go index 6b68351204..634c21fe1a 100644 --- a/pkg/crc/config/validations.go +++ b/pkg/crc/config/validations.go @@ -19,6 +19,19 @@ func ValidateBool(value interface{}) (bool, string) { return false, "must be true or false" } +// ValidateDiskSize checks if provided disk size is valid in the config +func ValidateDiskSize(value interface{}) (bool, string) { + diskSize, err := cast.ToIntE(value) + if err != nil { + return false, fmt.Sprintf("could not convert '%s' to integer", value) + } + if err := validation.ValidateDiskSize(diskSize); err != nil { + return false, err.Error() + } + + return true, "" +} + // ValidateCPUs checks if provided cpus count is valid in the config func ValidateCPUs(value interface{}) (bool, string) { v, err := cast.ToIntE(value) diff --git a/pkg/crc/constants/constants.go b/pkg/crc/constants/constants.go index 646b28e6be..8575438dc3 100644 --- a/pkg/crc/constants/constants.go +++ b/pkg/crc/constants/constants.go @@ -11,9 +11,10 @@ import ( ) const ( - DefaultName = "crc" - DefaultCPUs = 4 - DefaultMemory = 9216 + DefaultName = "crc" + DefaultCPUs = 4 + DefaultMemory = 9216 + DefaultDiskSize = 31 DefaultSSHUser = "core" DefaultSSHPort = 22 diff --git a/pkg/crc/machine/driver.go b/pkg/crc/machine/driver.go index bad0b850a0..7348986071 100644 --- a/pkg/crc/machine/driver.go +++ b/pkg/crc/machine/driver.go @@ -32,3 +32,15 @@ func setVcpus(host *host.Host, vcpus int) error { return updateDriverValue(host, vcpuSetter) } + +func convertGiBToBytes(gib int) uint64 { + return uint64(gib) * 1024 * 1024 * 1024 +} + +func setDiskSize(host *host.Host, diskSizeGiB int) error { + diskSizeSetter := func(driver *libmachine.VMDriver) { + driver.DiskCapacity = convertGiBToBytes(diskSizeGiB) + } + + return updateDriverValue(host, diskSizeSetter) +} diff --git a/pkg/crc/machine/machine.go b/pkg/crc/machine/machine.go index 211e33049a..329d9f1aef 100644 --- a/pkg/crc/machine/machine.go +++ b/pkg/crc/machine/machine.go @@ -128,6 +128,19 @@ func (client *client) updateVMConfig(startConfig StartConfig, api libmachine.API return err } + /* Disk size */ + if startConfig.DiskSize != 0 { + if err := setDiskSize(host, startConfig.DiskSize); err != nil { + logging.Debugf("Failed to update CRC disk configuration: %v", err) + if err != drivers.ErrNotImplemented { + return err + } + } + if err := api.Save(host); err != nil { + return err + } + } + return nil } @@ -262,6 +275,11 @@ func (client *client) Start(startConfig StartConfig) (*StartResult, error) { return nil, errors.Wrap(err, "Error updating public key") } + // Trigger disk resize, this will be a no-op if no disk size change is needed + if _, err = sshRunner.Run("sudo xfs_growfs / >/dev/null"); err != nil { + return nil, errors.Wrap(err, "Error updating filesystem size") + } + // Start network time synchronization if `CRC_DEBUG_ENABLE_STOP_NTP` is not set if stopNtp, _ := strconv.ParseBool(os.Getenv("CRC_DEBUG_ENABLE_STOP_NTP")); !stopNtp { logging.Info("Starting network time synchronization in CodeReady Containers VM") diff --git a/pkg/crc/machine/types.go b/pkg/crc/machine/types.go index 34ef45b12e..dfec555df1 100644 --- a/pkg/crc/machine/types.go +++ b/pkg/crc/machine/types.go @@ -11,8 +11,9 @@ type StartConfig struct { BundlePath string // Hypervisor - Memory int - CPUs int + Memory int // Memory size in MiB + CPUs int + DiskSize int // Disk size in GiB // Nameserver NameServer string diff --git a/pkg/crc/validation/validation.go b/pkg/crc/validation/validation.go index b8746e6696..e392a24976 100644 --- a/pkg/crc/validation/validation.go +++ b/pkg/crc/validation/validation.go @@ -31,6 +31,17 @@ func ValidateMemory(value int) error { return ValidateEnoughMemory(value) } +func ValidateDiskSize(value int) error { + /* If no disk size was set on the command line or in the preferences, we'll validate '0' */ + if value == 0 { + return nil + } + if value < constants.DefaultDiskSize { + return fmt.Errorf("requires disk size in GiB >= %d", constants.DefaultDiskSize) + } + return nil +} + // ValidateEnoughMemory checks if enough memory is installed on the host func ValidateEnoughMemory(value int) error { totalMemory := memory.TotalMemory()