Skip to content

Commit

Permalink
(feat): Move config validation and modification outside of config
Browse files Browse the repository at this point in the history
  • Loading branch information
lasith-kg committed Nov 22, 2023
1 parent ba705f0 commit 3b59d1c
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 74 deletions.
31 changes: 21 additions & 10 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ func main() {
lds := service.NewLinuxDeviceService(rc)
uos := &service.UnixOwnerService{}

// Config
c, err := config.New(os.Args)
// Config + Flags
c, f, err := config.Parse(os.Args)
checkError(err)

// Service + Config Consumers
Expand All @@ -31,22 +31,33 @@ func main() {
ub := backend.NewLinuxOwnerBackend(uos)
ae := action.NewActionExecutor(action.DefaultDelay, rc, c)

// Modify Config
bm := config.NewBatchModifier([]config.Modifier{
config.NewOverridesModifier(f),
})
checkError(bm.Modify(c))

// Validate Config
bv := config.NewBatchValidator([]config.Validator{
config.NewFileSystemValidator(),
config.NewModeValidator(),
config.NewMountPointValidator(),
config.NewPermissionsValidator(),
config.NewOwnerValidator(uos),
})
checkError(bv.Validate(c))

// Layers
layers := []layer.Layer{
layers := layer.NewLayerExecutor([]layer.Layer{
layer.NewFormatDeviceLayer(db, ae, c),
layer.NewLabelDeviceLayer(db, ae, c),
layer.NewUnmountDeviceLayer(db, fb, ae, c),
layer.NewCreateDirectoryLayer(db, fb, ae, c),
layer.NewMountDeviceLayer(db, fb, ae, c),
layer.NewChangeOwnerLayer(ub, fb, ae, c),
layer.NewChangePermissionsLayer(fb, ae, c),
}
for _, layer := range layers {
checkError(layer.Refresh())
checkError(layer.Modify())
checkError(layer.Refresh())
checkError(layer.Validate())
}
})
checkError(layers.Execute())
}

func checkError(err error) {
Expand Down
2 changes: 1 addition & 1 deletion configs/ubuntu.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
globals:
defaults:
mode: healthcheck
devices:
/dev/vdb:
Expand Down
25 changes: 8 additions & 17 deletions internal/action/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ type ActionExecutor struct {
Delay time.Duration
RunnerCache *utils.RunnerCache
Config *config.Config
MaxAttempts int
}

func NewActionExecutor(delay time.Duration, rc *utils.RunnerCache, c *config.Config) *ActionExecutor {
return &ActionExecutor{
Delay: delay,
RunnerCache: rc,
Config: c,
MaxAttempts: 3,
}
}

Expand All @@ -49,7 +47,7 @@ func (ae *ActionExecutor) ExecuteAction(action Action) error {
return fmt.Errorf("🔴 %s: Healthcheck mode enabled. %s", name, action.Refuse())
}
if mode == config.Prompt && !ae.ShouldProceed(action) {
return fmt.Errorf("🔴 %s. Rejected or exceeded the maximum attempts", action.Refuse())
return fmt.Errorf("🔴 Action rejected. %s", action.Refuse())
}

err = action.Execute(ae.RunnerCache)
Expand All @@ -74,22 +72,15 @@ func (ae *ActionExecutor) ExecuteActions(actions []Action) error {
}

func (ae *ActionExecutor) ShouldProceed(action Action) bool {
for attempt := 1; attempt <= ae.MaxAttempts; attempt++ {
prompt := action.Prompt()
prompt := action.Prompt()

fmt.Printf("🟣 %s? (y/n): ", prompt)
var response string
fmt.Scanln(&response)
fmt.Printf("🟣 %s? (y/n): ", prompt)
var response string
fmt.Scanln(&response)

response = strings.ToLower(response)
if response == "y" || response == "yes" {
return true
} else if response == "n" || response == "no" {
return false
}

log.Printf("🟡 Invalid response. Please enter 'y' or 'n'. Attempts left: %d\n", ae.MaxAttempts-attempt)
response = strings.ToLower(response)
if response == "y" || response == "yes" {
return true
}

return false
}
4 changes: 2 additions & 2 deletions internal/action/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ func (a *MountDeviceAction) GetDeviceName() string {
}

func (a *MountDeviceAction) Refuse() string {
return fmt.Sprintf("Refused to mount %s to %s (%s)", a.Source, a.Target, a.Options)
return fmt.Sprintf("Refused to mount %s to %s", a.Source, a.Target)
}

func (a *MountDeviceAction) Success() string {
return fmt.Sprintf("Successfully mounted %s to %s (%s)", a.Source, a.Target, a.Options)
return fmt.Sprintf("Successfully mounted %s to %s", a.Source, a.Target)
}

type UnmountDeviceAction struct {
Expand Down
49 changes: 12 additions & 37 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ type Device struct {
Mode Mode `yaml:"mode"`
}

type Globals struct {
type Defaults struct {
Mode Mode `yaml:"mode"`
}

Expand All @@ -62,61 +62,36 @@ type Overrides struct {
}

type Config struct {
Globals Globals `yaml:"globals"`
Defaults Defaults `yaml:"defaults"`
Overrides Overrides `yaml:"overrides"`
Devices map[string]Device `yaml:"devices"`
Overrides Overrides
}

func New(args []string) (*Config, error) {
func Parse(args []string) (*Config, *Flag, error) {
// Generate config path
flag, err := parseFlags(args[0], args[1:])
if err != nil {
fmt.Fprint(os.Stderr, err)
return nil, fmt.Errorf("🔴 Failed to parse provided flags")
return nil, nil, fmt.Errorf("🔴 Failed to parse provided flags")
}

// Create config structure
c := &Config{}
config := &Config{}

// Load config file into memory
file, err := os.ReadFile(flag.Config)
if err != nil {
return nil, err
return nil, nil, err
}

// Unmarshal YAML file from memory into struct
err = yaml.UnmarshalStrict(file, c)
err = yaml.UnmarshalStrict(file, config)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return nil, fmt.Errorf("🔴 %s: Failed to ingest malformed config", flag.Config)
return nil, nil, fmt.Errorf("🔴 %s: Failed to ingest malformed config", flag.Config)
}

// Modfiy config
modifiers := []Modifier{
NewOverridesModifier(flag),
}
for _, modifier := range modifiers {
err = modifier.Modify(c)
if err != nil {
return nil, err
}
}

// Validate config
validators := []Validator{
NewFileSystemValidator(),
NewModeValidator(),
NewMountPointValidator(),
NewPermissionsValidator(),
}
for _, validator := range validators {
err = validator.Validate(c)
if err != nil {
return nil, err
}
}

return c, nil
return config, flag, nil
}

func parseFlags(program string, args []string) (*Flag, error) {
Expand Down Expand Up @@ -150,8 +125,8 @@ func (c *Config) GetMode(name string) (Mode, error) {
if c.Overrides.Mode != Empty {
return c.Overrides.Mode, nil
}
if cd.Mode == Empty && c.Globals.Mode != Empty {
return c.Globals.Mode, nil
if cd.Mode == Empty && c.Defaults.Mode != Empty {
return c.Defaults.Mode, nil
}
if cd.Mode != Empty {
return cd.Mode, nil
Expand Down
24 changes: 23 additions & 1 deletion internal/config/modifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@ type Modifier interface {
Modify(config *Config) error
}

type BatchModifier struct {
Modifiers []Modifier
}

func NewBatchModifier(modifiers []Modifier) *BatchModifier {
return &BatchModifier{
Modifiers: modifiers,
}
}

func (bv *BatchModifier) Modify(c *Config) error {
for _, modifier := range bv.Modifiers {
err := modifier.Modify(c)
if err != nil {
return err
}
}
return nil
}

type OverridesModifier struct {
Flag *Flag
}
Expand All @@ -19,6 +39,8 @@ func (om *OverridesModifier) Modify(config *Config) error {
if err != nil {
return err
}
config.Overrides.Mode = mode
if mode != Empty {
config.Overrides.Mode = mode
}
return nil
}
51 changes: 50 additions & 1 deletion internal/config/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,33 @@ import (
"path"

"github.com/reecetech/vds/internal/model"
"github.com/reecetech/vds/internal/service"
)

type Validator interface {
Validate(c *Config) error
}

type BatchValidator struct {
Validators []Validator
}

func NewBatchValidator(validators []Validator) *BatchValidator {
return &BatchValidator{
Validators: validators,
}
}

func (bv *BatchValidator) Validate(c *Config) error {
for _, validator := range bv.Validators {
err := validator.Validate(c)
if err != nil {
return err
}
}
return nil
}

type FileSystemValidator struct{}

func NewFileSystemValidator() *FileSystemValidator {
Expand All @@ -37,7 +58,7 @@ func NewModeValidator() *ModeValidator {
}

func (fsv *ModeValidator) Validate(c *Config) error {
mode := string(c.Globals.Mode)
mode := string(c.Defaults.Mode)
_, err := ParseMode(mode)
if err != nil {
return fmt.Errorf("🔴 %s is not a supported global mode", mode)
Expand Down Expand Up @@ -95,3 +116,31 @@ func (apv *PermissionsValidator) Validate(c *Config) error {
}
return nil
}

type OwnerValidator struct {
OwnerService service.OwnerService
}

func NewOwnerValidator(ons service.OwnerService) *OwnerValidator {
return &OwnerValidator{
OwnerService: ons,
}
}

func (ov *OwnerValidator) Validate(c *Config) error {
for _, device := range c.Devices {
if device.User != "" {
_, err := ov.OwnerService.GetUser(device.User)
if err != nil {
return err
}
}
if device.Group != "" {
_, err := ov.OwnerService.GetGroup(device.Group)
if err != nil {
return err
}
}
}
return nil
}
40 changes: 36 additions & 4 deletions internal/layer/layer.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,39 @@
package layer

type Layer interface {
Refresh() (error)
Modify() (error)
Validate() (error)
}
Refresh() error
Modify() error
Validate() error
}

type LayerExecutor struct {
Layers []Layer
}

func NewLayerExecutor(layers []Layer) *LayerExecutor {
return &LayerExecutor{
Layers: layers,
}
}

func (le *LayerExecutor) Execute() error {
for _, layer := range le.Layers {
err := layer.Refresh()
if err != nil {
return err
}
err = layer.Modify()
if err != nil {
return err
}
err = layer.Refresh()
if err != nil {
return err
}
err = layer.Validate()
if err != nil {
return err
}
}
return nil
}
2 changes: 1 addition & 1 deletion internal/layer/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (fdl *ChangePermissionsLayer) Validate() error {
return fmt.Errorf("🔴 %s: Failed ownership validation checks. %s is either not a directory or does not exist", name, cd.MountPoint)
}

if !d.Permissions.Equals(cd.Permissions) {
if d.Permissions.NotEquals(cd.Permissions) {
return fmt.Errorf("🔴 %s: Failed permissions validation checks. %s Permissions Expected=%s, Actual=%s", name, cd.MountPoint, cd.Permissions, d.Permissions)
}

Expand Down
4 changes: 4 additions & 0 deletions internal/model/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@ func (pa Permissions) Equals(pb Permissions) bool {
}
return fma == fmb
}

func (p Permissions) NotEquals(pb Permissions) bool {
return !p.Equals(pb)
}
Binary file modified main
Binary file not shown.

0 comments on commit 3b59d1c

Please sign in to comment.