diff --git a/configs/ubuntu.yml b/configs/ubuntu.yml index 7fe445b..6ef2b2e 100644 --- a/configs/ubuntu.yml +++ b/configs/ubuntu.yml @@ -1,6 +1,6 @@ devices: /dev/vdb: - fs: xfs + fs: ext4 label: external-vol mountPoint: /mnt/app group: ubuntu diff --git a/internal/backend/resize.go b/internal/backend/resize.go index 8cd2f8c..33721c9 100644 --- a/internal/backend/resize.go +++ b/internal/backend/resize.go @@ -15,12 +15,9 @@ type BlockDeviceMetrics struct { } func (bdm *BlockDeviceMetrics) ShouldResize(threshold float64) bool { - // Handle edge case for dividing by zero - if bdm.BlockDeviceSize == 0 { - return false - } - utilisation := (float64(bdm.FileSystemSize) / float64(bdm.BlockDeviceSize)) * 100 - return utilisation < threshold + // Minimum File System Size (mfss) + mfss := float64(bdm.BlockDeviceSize) * (threshold / 100) + return float64(bdm.FileSystemSize) < mfss } type DeviceResizeBackend interface { diff --git a/internal/config/config.go b/internal/config/config.go index 5b35870..db576f6 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -11,13 +11,7 @@ import ( ) const ( - // DefaultResizeThreshold is the default threshold for triggering a filesystem resize. - // It is expressed as a percentage of the total filesystem size. If the used space - // is below this threshold, a resize operation will be initiated. The default value is - // set to 99.999% to account for occasional overhead that reduces the effective storage - // that can be used by the filesystem - DefaultResizeThreshold = 99.999 - DefaultMountOptions = model.MountOptions("defaults") + DefaultMountOptions = model.MountOptions("defaults") ) type Flag struct { @@ -183,7 +177,7 @@ func (c *Config) GetResizeFs(name string) bool { func (c *Config) GetResizeThreshold(name string) float64 { cd, found := c.Devices[name] if !found { - return DefaultResizeThreshold + return 0 } if c.overrides.ResizeThreshold != 0 { return c.overrides.ResizeThreshold @@ -191,8 +185,5 @@ func (c *Config) GetResizeThreshold(name string) float64 { if cd.ResizeThreshold != 0 { return cd.ResizeThreshold } - if c.Defaults.ResizeThreshold != 0 { - return c.Defaults.ResizeThreshold - } - return DefaultResizeThreshold + return c.Defaults.ResizeThreshold } diff --git a/internal/config/validator.go b/internal/config/validator.go index 5e391f3..b8913ed 100644 --- a/internal/config/validator.go +++ b/internal/config/validator.go @@ -43,7 +43,7 @@ func (fsv *FileSystemValidator) Validate(c *Config) error { for name, device := range c.Devices { fs, err := model.ParseFileSystem(string(device.Fs)) if err != nil { - return fmt.Errorf("🔴 %s: %s is not a supported file system", name, fs) + return fmt.Errorf("🔴 %s: '%s' is not a supported file system", name, fs) } if fs == model.Unformatted { return fmt.Errorf("🔴 %s: Must provide a supported file system", name) @@ -62,13 +62,13 @@ func (fsv *ModeValidator) Validate(c *Config) error { mode := string(c.Defaults.Mode) _, err := model.ParseMode(mode) if err != nil { - return fmt.Errorf("🔴 %s is not a supported global mode", mode) + return fmt.Errorf("🔴 '%s' (defaults) is not a global mode", mode) } mode = string(c.overrides.Mode) _, err = model.ParseMode(mode) if err != nil { - return fmt.Errorf("🔴 %s is not a supported overrides mode", mode) + return fmt.Errorf("🔴 '%s' (-mode) is not a supported mode", mode) } for name, device := range c.Devices { @@ -111,11 +111,11 @@ func NewMountOptionsValidator() *MountOptionsValidator { func (mov *MountOptionsValidator) Validate(c *Config) error { mo := string(c.Defaults.MountOptions) if err := mov.validate(mo); err != nil { - return fmt.Errorf("🔴 '%s' is not a supported global mode as %s", mo, err) + return fmt.Errorf("🔴 '%s' (defaults) is not a supported mode as %s", mo, err) } mo = string(c.overrides.MountOptions) if err := mov.validate(mo); err != nil { - return fmt.Errorf("🔴 '%s' is not a supported overrides mode as %s", mo, err) + return fmt.Errorf("🔴 '%s' (-mount-options) is not a supported mode as %s", mo, err) } for name, device := range c.Devices { mo := string(device.MountOptions) @@ -172,14 +172,14 @@ func NewResizeThresholdValidator() *ResizeThresholdValidator { func (rtv *ResizeThresholdValidator) Validate(c *Config) error { if c.Defaults.ResizeThreshold < 0 || c.Defaults.ResizeThreshold > 100 { - return fmt.Errorf("🔴 '%g' (default) must be a floating point between 0 and 100", c.Defaults.ResizeThreshold) + return fmt.Errorf("🔴 '%g' (default) must be a floating point between (inclusive) 0 and 100", c.Defaults.ResizeThreshold) } if c.overrides.ResizeThreshold < 0 || c.overrides.ResizeThreshold > 100 { - return fmt.Errorf("🔴 '%g' (overrides) must be a floating point between 0 and 100", c.overrides.ResizeThreshold) + return fmt.Errorf("🔴 '%g' (-resize-threshold) must be a floating point between (inclusive) 0 and 100", c.overrides.ResizeThreshold) } for name, device := range c.Devices { if device.ResizeThreshold < 0 || device.ResizeThreshold > 100 { - return fmt.Errorf("🔴 %s: '%g' must be a floating point between 0 and 100", name, device.ResizeThreshold) + return fmt.Errorf("🔴 %s: '%g' must be a floating point between (inclusive) 0 and 100", name, device.ResizeThreshold) } } return nil diff --git a/internal/layer/resize.go b/internal/layer/resize.go index 023c3f6..889153c 100644 --- a/internal/layer/resize.go +++ b/internal/layer/resize.go @@ -1,6 +1,8 @@ package layer import ( + "fmt" + "github.com/reecetech/ebs-bootstrap/internal/action" "github.com/reecetech/ebs-bootstrap/internal/backend" "github.com/reecetech/ebs-bootstrap/internal/config" @@ -35,7 +37,11 @@ func (fdl *ResizeDeviceLayer) Modify(c *config.Config) ([]action.Action, error) return nil, err } mode := c.GetMode(name) - if rt := c.GetResizeThreshold(name); metrics.ShouldResize(rt) { + rt := c.GetResizeThreshold(name) + // If the resize threshold is set to 0, let us ensure that there is always + // an attempt to resize the block device. For the currently supported file systems + // xfs and ext4, the commands to resize the block device are idempotent + if rt == 0 || metrics.ShouldResize(rt) { a, err := fdl.deviceResizeBackend.Resize(bd) if err != nil { return nil, err @@ -48,6 +54,21 @@ func (fdl *ResizeDeviceLayer) Modify(c *config.Config) ([]action.Action, error) } func (fdl *ResizeDeviceLayer) Validate(c *config.Config) error { + for name := range c.Devices { + if !c.GetResizeFs(name) { + continue + } + metrics, err := fdl.deviceResizeBackend.GetBlockDeviceMetrics(name) + if err != nil { + return err + } + rt := c.GetResizeThreshold(name) + // If the resize threshold is 0, then the device would always be resized + // Therefore, lets ignore that case from our validation checks + if rt > 0 && metrics.ShouldResize(rt) { + return fmt.Errorf("🔴 %s: Failed to resize file system. File System=%d Block Device=%d (bytes)", name, metrics.FileSystemSize, metrics.BlockDeviceSize) + } + } return nil }