-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
43 changed files
with
2,358 additions
and
1,754 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,69 @@ | ||
package main | ||
|
||
import ( | ||
"os" | ||
"log" | ||
"ebs-bootstrap/internal/config" | ||
"ebs-bootstrap/internal/service" | ||
"ebs-bootstrap/internal/utils" | ||
"ebs-bootstrap/internal/state" | ||
"os" | ||
|
||
"github.com/reecetech/ebs-bootstrap/internal/action" | ||
"github.com/reecetech/ebs-bootstrap/internal/backend" | ||
"github.com/reecetech/ebs-bootstrap/internal/config" | ||
"github.com/reecetech/ebs-bootstrap/internal/layer" | ||
"github.com/reecetech/ebs-bootstrap/internal/service" | ||
"github.com/reecetech/ebs-bootstrap/internal/utils" | ||
) | ||
|
||
func main() { | ||
// Disable Timetamp | ||
log.SetFlags(0) | ||
e := utils.NewExecRunner() | ||
ds := &service.LinuxDeviceService{Runner: e} | ||
ns := &service.AwsNVMeService{} | ||
fs := &service.UnixFileService{} | ||
dts := &service.EbsDeviceTranslator{DeviceService: ds, NVMeService: ns} | ||
|
||
dt, err := dts.GetTranslator() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
config, err := config.New(os.Args, dt, fs) | ||
// Services | ||
rc := utils.NewRunnerCache() | ||
ufs := service.NewUnixFileService() | ||
lds := service.NewLinuxDeviceService(rc) | ||
uos := service.NewUnixOwnerService() | ||
ans := service.NewAwsNitroNVMeService() | ||
|
||
// Config + Flags | ||
c, f, err := config.Parse(os.Args) | ||
checkError(err) | ||
|
||
// Service + Config Consumers | ||
db := backend.NewLinuxDeviceBackend(lds) | ||
fb := backend.NewLinuxFileBackend(ufs) | ||
ub := backend.NewLinuxOwnerBackend(uos) | ||
ae := action.NewActionExecutor(rc, c) | ||
|
||
// Modify Config | ||
bm := config.NewBatchModifier([]config.Modifier{ | ||
config.NewOverridesModifier(f), | ||
config.NewAwsNVMeDriverModifier(ans, lds), | ||
}) | ||
checkError(bm.Modify(c)) | ||
|
||
// Validate Config | ||
bv := config.NewBatchValidator([]config.Validator{ | ||
config.NewDeviceValidator(lds), | ||
config.NewFileSystemValidator(), | ||
config.NewModeValidator(), | ||
config.NewMountPointValidator(), | ||
config.NewPermissionsValidator(), | ||
config.NewOwnerValidator(uos), | ||
}) | ||
checkError(bv.Validate(c)) | ||
// Layers | ||
layers := layer.NewLayerExecutor([]layer.Layer{ | ||
layer.NewFormatDeviceLayer(db), | ||
layer.NewLabelDeviceLayer(db), | ||
layer.NewUnmountDeviceLayer(db, fb), | ||
layer.NewCreateDirectoryLayer(db, fb), | ||
layer.NewMountDeviceLayer(db, fb), | ||
layer.NewChangeOwnerLayer(ub, fb), | ||
layer.NewChangePermissionsLayer(fb), | ||
}) | ||
checkError(layers.Execute(c, ae)) | ||
} | ||
|
||
func checkError(err error) { | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
for name, device := range config.Devices { | ||
d, err := state.NewDevice(name, ds, fs) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
err = d.Diff(config) | ||
if err == nil { | ||
log.Printf("🟢 %s: No changes detected", name) | ||
continue | ||
} | ||
if device.Mode == "healthcheck" { | ||
log.Fatal(err) | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
defaults: | ||
mode: healthcheck | ||
devices: | ||
/dev/vdb: | ||
fs: ext4 | ||
label: lasith-rules | ||
mountPoint: /ifmx/dev/james | ||
mountOptions: defaults | ||
group: ubuntu | ||
user: ubuntu | ||
permissions: 644 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
module ebs-bootstrap | ||
module "github.com/reecetech/ebs-bootstrap" | ||
|
||
go 1.21 | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package action | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"strings" | ||
"time" | ||
|
||
"github.com/reecetech/ebs-bootstrap/internal/config" | ||
"github.com/reecetech/ebs-bootstrap/internal/utils" | ||
) | ||
|
||
const ( | ||
// Device operations like mounting and formatting are | ||
// delegeated to respective C-based tools like `mount` and `mkfs`. | ||
// From experience, we need to introduce a slight delay to ensure | ||
// that the file-system is eventually consistent with any changes | ||
// that were performed | ||
DefaultDeviceActionDelay = 100 * time.Millisecond | ||
// File changes like os.Chown and os.Chmod are performed | ||
// natively through golang standard libraries. Since these standard | ||
// libraries are making direct syscalls, changes are reflected almost | ||
// immidiately on the file-system. Therefore a delay is not required | ||
// for actions that peform file changes | ||
DefaultFileActionDelay = 0 | ||
) | ||
|
||
type Action interface { | ||
Execute(rc *utils.RunnerCache) error | ||
GetdeviceName() string | ||
Success() string | ||
Prompt() string | ||
Refuse() string | ||
IsTrusted() bool | ||
GetDelay() time.Duration | ||
} | ||
|
||
type ActionExecutor struct { | ||
runnerCache *utils.RunnerCache | ||
config *config.Config | ||
} | ||
|
||
func NewActionExecutor(rc *utils.RunnerCache, c *config.Config) *ActionExecutor { | ||
return &ActionExecutor{ | ||
runnerCache: rc, | ||
config: c, | ||
} | ||
} | ||
|
||
func (ae *ActionExecutor) ExecuteAction(action Action) error { | ||
name := action.GetdeviceName() | ||
mode, err := ae.config.GetMode(name) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
switch mode { | ||
case config.Prompt: | ||
if !ae.ShouldProceed(action) { | ||
return fmt.Errorf("🔴 Action rejected. %s", action.Refuse()) | ||
} | ||
case config.Healtcheck: | ||
// Special handling for trusted actions when device is under | ||
// healthcheck mode | ||
if action.IsTrusted() { | ||
if ae.config.GetSkipTrustedActions() { | ||
log.Printf("🙅 Skipped trusted action. %s", action.Refuse()) | ||
return nil | ||
} | ||
break | ||
} | ||
return fmt.Errorf("🔴%s: Healthcheck mode enabled. %s", name, action.Refuse()) | ||
} | ||
return ae.executeAction(action) | ||
} | ||
|
||
func (ae *ActionExecutor) executeAction(action Action) error { | ||
if err := action.Execute(ae.runnerCache); err != nil { | ||
return err | ||
} | ||
log.Printf("⭐ %s: %s", action.GetdeviceName(), action.Success()) | ||
// Add a delay because from experience, the operating system needs some time | ||
// to catch up to device changes performed through C tools like e2label and mkfs.ext4 | ||
time.Sleep(action.GetDelay()) | ||
return nil | ||
} | ||
|
||
func (ae *ActionExecutor) ExecuteActions(actions []Action) error { | ||
for _, action := range actions { | ||
err := ae.ExecuteAction(action) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (ae *ActionExecutor) ShouldProceed(action Action) bool { | ||
prompt := action.Prompt() | ||
|
||
fmt.Printf("🟣 %s? (y/n): ", prompt) | ||
var response string | ||
fmt.Scanln(&response) | ||
|
||
response = strings.ToLower(response) | ||
if response == "y" || response == "yes" { | ||
return true | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package action | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"time" | ||
|
||
"github.com/reecetech/ebs-bootstrap/internal/model" | ||
"github.com/reecetech/ebs-bootstrap/internal/utils" | ||
) | ||
|
||
const ( | ||
DefaultDirectoryPermissions = os.FileMode(0755) | ||
) | ||
|
||
type CreateDirectoryAction struct { | ||
deviceName string | ||
path string | ||
} | ||
|
||
func NewCreateDirectoryAction(dn string, p string) *CreateDirectoryAction { | ||
return &CreateDirectoryAction{ | ||
deviceName: dn, | ||
path: p, | ||
} | ||
} | ||
|
||
func (a *CreateDirectoryAction) Execute(rc *utils.RunnerCache) error { | ||
return os.MkdirAll(a.path, DefaultDirectoryPermissions) | ||
} | ||
|
||
func (a *CreateDirectoryAction) Prompt() string { | ||
return fmt.Sprintf("Would you like to recursively create directory %s", a.path) | ||
} | ||
|
||
func (a *CreateDirectoryAction) GetdeviceName() string { | ||
return a.deviceName | ||
} | ||
|
||
func (a *CreateDirectoryAction) Refuse() string { | ||
return fmt.Sprintf("Refused to create directory %s", a.path) | ||
} | ||
|
||
func (a *CreateDirectoryAction) Success() string { | ||
return fmt.Sprintf("Successfully created directory %s", a.path) | ||
} | ||
|
||
func (a *CreateDirectoryAction) IsTrusted() bool { | ||
return false | ||
} | ||
|
||
func (a *CreateDirectoryAction) GetDelay() time.Duration { | ||
return DefaultFileActionDelay | ||
} | ||
|
||
type ChangeOwnerAction struct { | ||
deviceName string | ||
path string | ||
uid int | ||
gid int | ||
} | ||
|
||
func NewChangeOwnerAction(dn string, p string, uid int, gid int) *ChangeOwnerAction { | ||
return &ChangeOwnerAction{ | ||
deviceName: dn, | ||
path: p, | ||
uid: uid, | ||
gid: gid, | ||
} | ||
} | ||
|
||
func (a *ChangeOwnerAction) Execute(rc *utils.RunnerCache) error { | ||
return os.Chown(a.path, a.uid, a.gid) | ||
} | ||
|
||
func (a *ChangeOwnerAction) Prompt() string { | ||
return fmt.Sprintf("Would you like to change ownership (%d:%d) of %s", a.uid, a.gid, a.path) | ||
} | ||
|
||
func (a *ChangeOwnerAction) GetdeviceName() string { | ||
return a.deviceName | ||
} | ||
|
||
func (a *ChangeOwnerAction) Refuse() string { | ||
return fmt.Sprintf("Refused to to change ownership (%d:%d) of %s", a.uid, a.gid, a.path) | ||
} | ||
|
||
func (a *ChangeOwnerAction) Success() string { | ||
return fmt.Sprintf("Successfully changed ownership (%d:%d) of %s", a.uid, a.gid, a.path) | ||
} | ||
|
||
func (a *ChangeOwnerAction) IsTrusted() bool { | ||
return false | ||
} | ||
|
||
func (a *ChangeOwnerAction) GetDelay() time.Duration { | ||
return DefaultFileActionDelay | ||
} | ||
|
||
type ChangePermissionsAction struct { | ||
deviceName string | ||
path string | ||
perms model.Permissions | ||
} | ||
|
||
func NewChangePermissions(dn string, p string, perms model.Permissions) *ChangePermissionsAction { | ||
return &ChangePermissionsAction{ | ||
deviceName: dn, | ||
path: p, | ||
perms: perms, | ||
} | ||
} | ||
|
||
func (a *ChangePermissionsAction) Execute(rc *utils.RunnerCache) error { | ||
mode, err := a.perms.ToFileMode() | ||
if err != nil { | ||
return err | ||
} | ||
return os.Chmod(a.path, mode) | ||
} | ||
|
||
func (a *ChangePermissionsAction) Prompt() string { | ||
return fmt.Sprintf("Would you like to change permissions of %s to %s", a.path, a.perms) | ||
} | ||
|
||
func (a *ChangePermissionsAction) GetdeviceName() string { | ||
return a.deviceName | ||
} | ||
|
||
func (a *ChangePermissionsAction) Refuse() string { | ||
return fmt.Sprintf("Refused to to change permissions of %s to %s", a.path, a.perms) | ||
} | ||
|
||
func (a *ChangePermissionsAction) Success() string { | ||
return fmt.Sprintf("Successfully change permissions of %s to %s", a.path, a.perms) | ||
} | ||
|
||
func (a *ChangePermissionsAction) IsTrusted() bool { | ||
return false | ||
} | ||
|
||
func (a *ChangePermissionsAction) GetDelay() time.Duration { | ||
return DefaultFileActionDelay | ||
} |
Oops, something went wrong.