Skip to content

Commit

Permalink
(feat): Format, label, resize and mount devices through configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
lasith-kg committed Dec 25, 2023
1 parent 2ea24ac commit 26eca08
Show file tree
Hide file tree
Showing 52 changed files with 3,127 additions and 1,737 deletions.
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# syntax=docker/dockerfile:1
FROM golang:1.21

# Build a static binary
ENV CGO_ENABLED=0

# Set destination for COPY
WORKDIR /app

Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
# Specific Architecture
./build/docker.sh --architecture arm64
ls -la
... ebs-bootstrap_linux-arm64
... ebs-bootstrap-linux-aarch64

# All Architectures
./build/docker.sh
ls -la
... ebs-bootstrap_linux-arm64
... ebs-bootstrap_linux-x86_64
... ebs-bootstrap-linux-aarch64
... ebs-bootstrap-linux-x86_64
```

## Recommended Setup

### `systemd`
Expand Down
276 changes: 276 additions & 0 deletions assets/uml.drawio

Large diffs are not rendered by default.

Binary file added assets/uml.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
102 changes: 74 additions & 28 deletions cmd/ebs-bootstrap.go
Original file line number Diff line number Diff line change
@@ -1,44 +1,90 @@
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)
// Services
erf := utils.NewExecRunnerFactory()
ufs := service.NewUnixFileService()
lds := service.NewLinuxDeviceService(erf)
uos := service.NewUnixOwnerService()
ans := service.NewAwsNitroNVMeService()
fssf := service.NewLinuxFileSystemServiceFactory(erf)

// Warnings
warnings(uos)

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

// Service + Config Consumers
db := backend.NewLinuxDeviceBackend(lds, fssf)
fb := backend.NewLinuxFileBackend(ufs)
ub := backend.NewLinuxOwnerBackend(uos)
drb := backend.NewLinuxDeviceResizeBackend(lds, fssf)
dae := action.NewDefaultActionExecutor(c)

// Modify Config
modifiers := []config.Modifier{
config.NewAwsNVMeDriverModifier(ans, lds),
}
for _, m := range modifiers {
checkError(m.Modify(c))
}
config, err := config.New(os.Args, dt, fs)

// Validate Config
validators := []config.Validator{
config.NewFileSystemValidator(),
config.NewModeValidator(),
config.NewResizeThresholdValidator(),
config.NewDeviceValidator(lds),
config.NewMountPointValidator(),
config.NewMountOptionsValidator(),
config.NewOwnerValidator(uos),
}
for _, v := range validators {
checkError(v.Validate(c))
}

// Layers
le := layer.NewExponentialBackoffLayerExecutor(c, dae)
layers := []layer.Layer{
layer.NewFormatDeviceLayer(db),
layer.NewLabelDeviceLayer(db),
layer.NewCreateDirectoryLayer(db, fb),
layer.NewMountDeviceLayer(db, fb),
layer.NewResizeDeviceLayer(drb),
layer.NewChangeOwnerLayer(ub, fb),
layer.NewChangePermissionsLayer(fb),
}
checkError(le.Execute(layers))
}

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)
}
func warnings(us service.OwnerService) {
cu, err := us.GetCurrentUser()
if err != nil {
return
}
if cu.Uid != 0 {
log.Println("🚧 Not running as root user. Operations that query and modify block devices will likely be restricted")
}
}
11 changes: 0 additions & 11 deletions configs/ebs-bootstrap.yml

This file was deleted.

9 changes: 9 additions & 0 deletions configs/ubuntu.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
devices:
/dev/vdb:
fs: ext4
label: external-vol
mountPoint: /mnt/app
group: ubuntu
user: ubuntu
permissions: 0644
resizeFs: true
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
module ebs-bootstrap
module github.com/reecetech/ebs-bootstrap

go 1.21

require gopkg.in/yaml.v2 v2.4.0

require github.com/google/go-cmp v0.6.0 // indirect
require (
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
86 changes: 86 additions & 0 deletions internal/action/action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package action

import (
"fmt"
"log"
"strings"

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

type Action interface {
Execute() error
Success() string
Prompt() string
Refuse() string
GetMode() model.Mode
SetMode(mode model.Mode) Action
}

type ActionExecutor interface {
Execute(actions []Action) error
}

type DefaultActionExecutor struct {
config *config.Config
read func(buffer *string) error
}

func NewDefaultActionExecutor(c *config.Config) *DefaultActionExecutor {
return &DefaultActionExecutor{
config: c,
read: func(buffer *string) error {
_, err := fmt.Scanln(buffer)
return err
},
}
}

func (dae *DefaultActionExecutor) Execute(actions []Action) error {
for _, a := range actions {
err := dae.execute(a)
if err != nil {
return err
}
}
return nil
}

func (dae *DefaultActionExecutor) execute(action Action) error {
switch action.GetMode() {
case model.Force:
break
case model.Prompt:
if !dae.shouldProceed(action) {
return fmt.Errorf("🔴 Action rejected. %s", action.Refuse())
}
case model.Healthcheck:
return fmt.Errorf("🔴 Healthcheck mode enabled. %s", action.Refuse())
default:
return fmt.Errorf("🔴 Unexpected mode detected. %s", action.Refuse())
}

if err := action.Execute(); err != nil {
return err
}
log.Printf("⭐ %s", action.Success())
return nil
}

func (dae *DefaultActionExecutor) shouldProceed(action Action) bool {
prompt := action.Prompt()

fmt.Printf("🟣 %s? (y/n): ", prompt)
var response string
err := dae.read(&response)
if err != nil {
return false
}

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

0 comments on commit 26eca08

Please sign in to comment.