add DryRun() method for instruction common interface | add DryRun() method to all instructions | TODO: tests + install/delete

This commit is contained in:
Adrien Marquès 2018-11-14 20:11:07 +01:00
parent 2f95acb851
commit 1a7e1c2db0
11 changed files with 159 additions and 10 deletions

View File

@ -112,7 +112,13 @@ func NewReader(ctx *instruction.ExecutionContext, buildfile io.Reader) (*Reader,
}
// Execute the current buildfile instruction by instruction
func (r *Reader) Execute() error {
// if <dryRun> is set to TRUE, run on dry-run mode
func (r *Reader) Execute(_dryRun ...bool) error {
dryRun := true
if len(_dryRun) < 1 || !_dryRun[0] {
dryRun = false
}
// 1. update package list
// err := r.Context.PackageManager.Fetch()
@ -168,7 +174,7 @@ func (r *Reader) Execute() error {
// 3. launch [pre] (it set)
if pre != nil {
execSection(pre, *r.Context, preTable, refresh, wg)
execSection(pre, *r.Context, preTable, dryRun, refresh, wg)
time.Sleep(time.Second * 2)
}
@ -182,7 +188,7 @@ func (r *Reader) Execute() error {
if !ok {
continue
}
go execSection(sec, *r.Context, &table[i], refresh, wg)
go execSection(sec, *r.Context, &table[i], dryRun, refresh, wg)
}
wg.Wait()
@ -192,11 +198,17 @@ func (r *Reader) Execute() error {
return nil
}
func execSection(section *[]instruction.T, ctx instruction.ExecutionContext, tsec *tableSection, refresher chan<- bool, wg *sync.WaitGroup) {
func execSection(section *[]instruction.T, ctx instruction.ExecutionContext, tsec *tableSection, dryRun bool, refresher chan<- bool, wg *sync.WaitGroup) {
for i, inst := range *section {
tsec.instructions[i].start = time.Now()
_, err := inst.Exec(ctx)
var err error = nil
if dryRun {
_, err = inst.DryRun(ctx)
} else {
_, err = inst.Exec(ctx)
}
tsec.instructions[i].stop = time.Now()
tsec.instructions[i].stopped = true
tsec.instructions[i].err = err

View File

@ -39,3 +39,7 @@ func (d alias) Exec(ctx ExecutionContext) ([]byte, error) {
ctx.Alias[d.Name] = d.Value
return nil, nil
}
func (d alias) DryRun(ctx ExecutionContext) ([]byte, error) {
return d.Exec(ctx)
}

View File

@ -15,6 +15,8 @@ type T interface {
Build(string) error
// Exec the given instruction
Exec(ExecutionContext) ([]byte, error)
// DryRun checks the success of the given instruction without actually running it (non-destructive)
DryRun(ExecutionContext) ([]byte, error)
}
// ExecutionContext contains system-specific drivers to manage the host

View File

@ -45,3 +45,38 @@ func (d copy) Exec(ctx ExecutionContext) ([]byte, error) {
return nil, nil
}
func (d copy) DryRun(ctx ExecutionContext) ([]byte, error) {
// 1. fail if source file not found
if _, err := os.Stat(d.Src); os.IsNotExist(err) {
return nil, fmt.Errorf("cannot find file '%s'", d.Src)
}
// 2. if destination to create : try to create (then remove)
if _, err := os.Stat(d.Src); os.IsNotExist(err) {
file, err := os.OpenFile(d.Dst, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return nil, fmt.Errorf("cannot copy '%s' to '%s' | %s", d.Src, d.Dst, err)
}
file.Close()
if err := os.Remove(d.Dst); err != nil {
return nil, fmt.Errorf("cannot tmp file at '%s' | %s", d.Dst, err)
}
return nil, nil
}
// 3. if destination exists : check write permission
file, err := os.OpenFile(d.Dst, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
return nil, fmt.Errorf("cannot copy '%s' to '%s' | %s", d.Src, d.Dst, err)
}
file.Close()
return nil, nil
}

View File

@ -33,3 +33,7 @@ func (d delete) Exec(ctx ExecutionContext) ([]byte, error) {
return nil, nil
}
func (d delete) DryRun(ctx ExecutionContext) ([]byte, error) {
return nil, nil
}

View File

@ -33,3 +33,7 @@ func (d install) Exec(ctx ExecutionContext) ([]byte, error) {
return nil, nil
}
func (d install) DryRun(ctx ExecutionContext) ([]byte, error) {
return nil, nil
}

View File

@ -40,3 +40,21 @@ func (d run) Exec(ctx ExecutionContext) ([]byte, error) {
return nil, nil
}
func (d run) DryRun(ctx ExecutionContext) ([]byte, error) {
// 1. get file / alias
path := d.raw
if !strings.Contains(path, "/") {
if p, exists := ctx.Alias[path]; exists {
path = p
}
}
// 1. fail if file not found
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, fmt.Errorf("cannot find script '%s'", path)
}
return nil, nil
}

View File

@ -60,3 +60,17 @@ func (d service) Exec(ctx ExecutionContext) ([]byte, error) {
return nil, nil
}
func (d service) DryRun(ctx ExecutionContext) ([]byte, error) {
// fail if a service does not exist
for _, service := range d.Services {
if err := ctx.ServiceManager.Exec("status", service); err != nil {
return nil, fmt.Errorf("cannot find service '%s' | %s", service, err)
}
}
return nil, nil
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/xdrm-brackets/nix-amer/internal/cnf"
"os"
"path/filepath"
"strings"
)
@ -94,3 +95,57 @@ func (d set) Exec(ctx ExecutionContext) ([]byte, error) {
return nil, nil
}
func (d set) DryRun(ctx ExecutionContext) ([]byte, error) {
// 1. get file / alias
path := d.File
if !strings.Contains(path, "/") {
if p, exists := ctx.Alias[path]; exists {
path = p
}
}
// 2. fail if file not found
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, fmt.Errorf("cannot find file '%s'", path)
}
// 3. try to load format
format, err := cnf.Load(path)
if err != nil {
return nil, err
}
// 4. try to update value
if !format.Set(d.Path, d.Value) {
return nil, ErrCannotSet
}
// 5. fail on missing write permission
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, os.FileMode(0775))
if err != nil {
return nil, fmt.Errorf("cannot update '%s' | %s", path, err)
}
file.Close()
// 6. create non-destructive dry-run folder
dryRunFolder := "/tmp/dry-run"
if err := os.MkdirAll(dryRunFolder, os.FileMode(0777)); err != nil {
return nil, fmt.Errorf("cannot create dry-run folder | %s", err)
}
// 7. create updated file inside .dry-run
tmpout := filepath.Join(dryRunFolder, strings.Replace(path, "/", "-", -1))
file, err = os.Create(tmpout)
if err != nil {
return nil, fmt.Errorf("cannot create dry-run file '%s' | %s", tmpout, err)
}
defer file.Close()
if _, err = format.WriteTo(file); err != nil {
return nil, fmt.Errorf("cannot write dry-run file '%s' | %s", tmpout, err)
}
return nil, nil
}

View File

@ -7,7 +7,7 @@ import (
type systemd struct{ exec exec.Executor }
// available actions
var actions = []string{"enable", "disable", "start", "stop", "reload", "restart"}
var actions = []string{"enable", "disable", "status", "start", "stop", "reload", "restart"}
func (d *systemd) SetExecutor(_exec exec.Executor) {
d.exec = _exec

View File

@ -43,13 +43,10 @@ func main() {
fmt.Printf("%s\n", clifmt.Color(32, "valid"))
// stop here if dry run
if dryRun {
return
}
// 3. Execute
fmt.Printf("------\n")
err = instructions.Execute()
err = instructions.Execute(dryRun)
if err != nil {
fmt.Printf("%s\n", clifmt.Warn(err.Error()))
return
@ -58,4 +55,8 @@ func main() {
clifmt.Align("finished in")
fmt.Printf("%ss\n", clifmt.Color(32, fmt.Sprintf("%.2f", time.Now().Sub(start).Seconds())))
if dryRun {
fmt.Printf("\n%s %s\n", clifmt.Info("updated configurations are inside"), clifmt.Color(32, "/tmp/dry-run"))
}
}