diff --git a/internal/buildfile/reader.go b/internal/buildfile/reader.go index cb09559..09eec2f 100644 --- a/internal/buildfile/reader.go +++ b/internal/buildfile/reader.go @@ -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 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 diff --git a/internal/instruction/alias.go b/internal/instruction/alias.go index 2a50d06..6ae8186 100644 --- a/internal/instruction/alias.go +++ b/internal/instruction/alias.go @@ -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) +} diff --git a/internal/instruction/common.go b/internal/instruction/common.go index f01c3b7..e07cff5 100644 --- a/internal/instruction/common.go +++ b/internal/instruction/common.go @@ -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 diff --git a/internal/instruction/copy.go b/internal/instruction/copy.go index 2cfc52b..5b419da 100644 --- a/internal/instruction/copy.go +++ b/internal/instruction/copy.go @@ -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 + +} diff --git a/internal/instruction/delete.go b/internal/instruction/delete.go index fe4dfdf..6300093 100644 --- a/internal/instruction/delete.go +++ b/internal/instruction/delete.go @@ -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 +} diff --git a/internal/instruction/install.go b/internal/instruction/install.go index 958e54a..959742f 100644 --- a/internal/instruction/install.go +++ b/internal/instruction/install.go @@ -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 +} diff --git a/internal/instruction/run.go b/internal/instruction/run.go index 22c7dc0..e162637 100644 --- a/internal/instruction/run.go +++ b/internal/instruction/run.go @@ -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 + +} diff --git a/internal/instruction/service.go b/internal/instruction/service.go index 76d6305..f73a343 100644 --- a/internal/instruction/service.go +++ b/internal/instruction/service.go @@ -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 + +} diff --git a/internal/instruction/set.go b/internal/instruction/set.go index 56aa329..f882974 100644 --- a/internal/instruction/set.go +++ b/internal/instruction/set.go @@ -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 + +} diff --git a/internal/ser/systemd.go b/internal/ser/systemd.go index 230aacd..59fbe2b 100644 --- a/internal/ser/systemd.go +++ b/internal/ser/systemd.go @@ -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 diff --git a/main.go b/main.go index cb6cddd..f44cc1d 100644 --- a/main.go +++ b/main.go @@ -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")) + } + }