Skip to content

Commit

Permalink
(feat): Refactor resize backend
Browse files Browse the repository at this point in the history
  • Loading branch information
lasith-kg committed Dec 26, 2023
1 parent 149e254 commit 76ed45e
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 140 deletions.
4 changes: 2 additions & 2 deletions cmd/ebs-bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func main() {
db := backend.NewLinuxDeviceBackend(lds, fssf)
fb := backend.NewLinuxFileBackend(ufs)
ub := backend.NewLinuxOwnerBackend(uos)
drb := backend.NewLinuxDeviceResizeBackend(lds, fssf)
dmb := backend.NewLinuxDeviceMetricsBackend(lds, fssf)
dae := action.NewDefaultActionExecutor(c)

// Modify Config
Expand Down Expand Up @@ -66,7 +66,7 @@ func main() {
layer.NewLabelDeviceLayer(db),
layer.NewCreateDirectoryLayer(db, fb),
layer.NewMountDeviceLayer(db, fb),
layer.NewResizeDeviceLayer(drb),
layer.NewResizeDeviceLayer(db, dmb),
layer.NewChangeOwnerLayer(ub, fb),
layer.NewChangePermissionsLayer(fb),
}
Expand Down
20 changes: 20 additions & 0 deletions internal/backend/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
type DeviceBackend interface {
GetBlockDevice(device string) (*model.BlockDevice, error)
Label(bd *model.BlockDevice, label string) ([]action.Action, error)
Resize(bd *model.BlockDevice) (action.Action, error)
Format(bd *model.BlockDevice, fileSystem model.FileSystem) (action.Action, error)
Mount(bd *model.BlockDevice, target string, options model.MountOptions) action.Action
Remount(bd *model.BlockDevice, target string, options model.MountOptions) action.Action
Expand Down Expand Up @@ -62,6 +63,25 @@ func (db *LinuxDeviceBackend) Label(bd *model.BlockDevice, label string) ([]acti
return append(actions, a), nil
}

func (db *LinuxDeviceBackend) Resize(bd *model.BlockDevice) (action.Action, error) {
fss, err := db.fileSystemServiceFactory.Select(bd.FileSystem)
if err != nil {
return nil, err
}
target := bd.Name
if fss.DoesResizeRequireMount() {
if bd.MountPoint == "" {
return nil, fmt.Errorf("🔴 %s: To resize the %s file system, device must be mounted", fss.GetFileSystem(), bd.Name)
}
target = bd.MountPoint
}
return action.NewResizeDeviceAction(
bd.Name,
target,
fss,
), nil
}

func (db *LinuxDeviceBackend) Format(bd *model.BlockDevice, fileSystem model.FileSystem) (action.Action, error) {
fss, err := db.fileSystemServiceFactory.Select(fileSystem)
if err != nil {
Expand Down
78 changes: 78 additions & 0 deletions internal/backend/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package backend

import (
"fmt"

"github.com/reecetech/ebs-bootstrap/internal/config"
"github.com/reecetech/ebs-bootstrap/internal/model"
"github.com/reecetech/ebs-bootstrap/internal/service"
)

type DeviceMetricsBackend interface {
GetBlockDeviceMetrics(name string) (*model.BlockDeviceMetrics, error)
From(config *config.Config) error
}

type LinuxDeviceMetricsBackend struct {
blockDeviceMetrics map[string]*model.BlockDeviceMetrics
deviceService service.DeviceService
fileSystemServiceFactory service.FileSystemServiceFactory
}

func NewLinuxDeviceMetricsBackend(ds service.DeviceService, fssf service.FileSystemServiceFactory) *LinuxDeviceMetricsBackend {
return &LinuxDeviceMetricsBackend{
blockDeviceMetrics: map[string]*model.BlockDeviceMetrics{},
deviceService: ds,
fileSystemServiceFactory: fssf,
}
}

func (dmb *LinuxDeviceMetricsBackend) GetBlockDeviceMetrics(name string) (*model.BlockDeviceMetrics, error) {
metrics, exists := dmb.blockDeviceMetrics[name]
if !exists {
return nil, fmt.Errorf("🔴 %s: Could not find block device metrics", name)
}
return metrics, nil
}

func (dmb *LinuxDeviceMetricsBackend) From(config *config.Config) error {
// Clear in memory representation of metrics and devices
for k := range dmb.blockDeviceMetrics {
delete(dmb.blockDeviceMetrics, k)
}

for name := range config.Devices {
bd, err := dmb.deviceService.GetBlockDevice(name)
if err != nil {
return err
}
// Can not fetch file system metrics from a device with
// no file system. Therefore, we exit with error if this is the case
// The reason we exit early, rather than continuing is because we want
// to simplify the data view of the Device Resize Backend as much as possible
// With this check, we ensure that all devices in the resize backend have a
// valid file system
if bd.FileSystem == model.Unformatted {
return fmt.Errorf("🔴 %s: Can not resize a device with no file system", bd.Name)
}
fs, err := dmb.fileSystemServiceFactory.Select(bd.FileSystem)
if err != nil {
return err
}
// Block Device Size
bss, err := dmb.deviceService.GetSize(bd.Name)
if err != nil {
return err
}
// File System Size
fss, err := fs.GetSize(bd.Name)
if err != nil {
return err
}
dmb.blockDeviceMetrics[bd.Name] = &model.BlockDeviceMetrics{
BlockDeviceSize: bss,
FileSystemSize: fss,
}
}
return nil
}
125 changes: 0 additions & 125 deletions internal/backend/resize.go

This file was deleted.

32 changes: 19 additions & 13 deletions internal/layer/resize.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@ import (
)

type ResizeDeviceLayer struct {
deviceResizeBackend backend.DeviceResizeBackend
deviceBackend backend.DeviceBackend
deviceMetricsBackend backend.DeviceMetricsBackend
}

func NewResizeDeviceLayer(dmb backend.DeviceResizeBackend) *ResizeDeviceLayer {
func NewResizeDeviceLayer(db backend.DeviceBackend, dmb backend.DeviceMetricsBackend) *ResizeDeviceLayer {
return &ResizeDeviceLayer{
deviceResizeBackend: dmb,
deviceBackend: db,
deviceMetricsBackend: dmb,
}
}

func (fdl *ResizeDeviceLayer) From(c *config.Config) error {
return fdl.deviceResizeBackend.From(c)
err := fdl.deviceBackend.From(c)
if err != nil {
return err
}
return fdl.deviceMetricsBackend.From(c)
}

func (fdl *ResizeDeviceLayer) Modify(c *config.Config) ([]action.Action, error) {
Expand All @@ -28,21 +34,21 @@ func (fdl *ResizeDeviceLayer) Modify(c *config.Config) ([]action.Action, error)
if !c.GetResizeFs(name) {
continue
}
bd, err := fdl.deviceResizeBackend.GetBlockDevice(name)
bd, err := fdl.deviceBackend.GetBlockDevice(name)
if err != nil {
return nil, err
}
metrics, err := fdl.deviceResizeBackend.GetBlockDeviceMetrics(name)
metrics, err := fdl.deviceMetricsBackend.GetBlockDeviceMetrics(name)
if err != nil {
return nil, err
}
mode := c.GetMode(name)
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.WithinThreshold(rt) {
a, err := fdl.deviceResizeBackend.Resize(bd)
// If the resize threshold is set to 0, always 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.deviceBackend.Resize(bd)
if err != nil {
return nil, err
}
Expand All @@ -58,14 +64,14 @@ func (fdl *ResizeDeviceLayer) Validate(c *config.Config) error {
if !c.GetResizeFs(name) {
continue
}
metrics, err := fdl.deviceResizeBackend.GetBlockDeviceMetrics(name)
metrics, err := fdl.deviceMetricsBackend.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.WithinThreshold(rt) {
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)
}
}
Expand Down
11 changes: 11 additions & 0 deletions internal/model/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,14 @@ func (mop MountOptions) Remount() MountOptions {
}
return MountOptions(strings.Join(mops, ","))
}

type BlockDeviceMetrics struct {
FileSystemSize uint64
BlockDeviceSize uint64
}

func (bdm *BlockDeviceMetrics) ShouldResize(threshold float64) bool {
// Minimum File System Size (mfss)
mfss := float64(bdm.BlockDeviceSize) * (threshold / 100)
return float64(bdm.FileSystemSize) < mfss
}

0 comments on commit 76ed45e

Please sign in to comment.