diff --git a/cmd/modd/main.go b/cmd/modd/main.go index 08a14cd..c91b3c5 100644 --- a/cmd/modd/main.go +++ b/cmd/modd/main.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "strings" + "time" "github.com/cortesi/modd" "github.com/cortesi/modd/notify" @@ -45,6 +46,10 @@ var prep = kingpin.Flag("prep", "Run prep commands and exit"). Short('p'). Bool() +var maxRestartDelay = kingpin.Flag("maxrestart", "Max deamon restart delay"). + Default("8"). + Int() + var debug = kingpin.Flag("debug", "Debugging for modd development"). Default("false"). Bool() @@ -110,7 +115,11 @@ func main() { notifiers = append(notifiers, ¬ify.BeepNotifier{}) } - mr, err := modd.NewModRunner(*file, log, notifiers, !(*noconf)) + mr, err := modd.NewModRunner(log, notifiers, modd.ModRunnerArgs{ + ConfPath: *file, + ConfReload: !(*noconf), + MaxRestartDelay: time.Duration(*maxRestartDelay) * time.Second, + }) if err != nil { log.Shout("%s", err) return diff --git a/daemon.go b/daemon.go index 5f86c7f..b946889 100644 --- a/daemon.go +++ b/daemon.go @@ -17,14 +17,13 @@ const ( MinRestart = 500 * time.Millisecond // MulRestart is the exponential backoff multiplier applied when the daemon exits uncleanly MulRestart = 2 - // MaxRestart is the maximum amount of time between daemon restarts - MaxRestart = 8 * time.Second ) // A single daemon type daemon struct { - conf conf.Daemon - indir string + conf conf.Daemon + indir string + maxRestartDelay time.Duration ex *shell.Executor log termlog.Stream @@ -61,12 +60,12 @@ func (d *daemon) Run() { // If we exited cleanly, or the process ran for > MaxRestart, we reset // the delay timer - if time.Now().Sub(lastStart) > MaxRestart { + if time.Now().Sub(lastStart) > d.maxRestartDelay { delay = MinRestart } else { delay *= MulRestart - if delay > MaxRestart { - delay = MaxRestart + if delay > d.maxRestartDelay { + delay = d.maxRestartDelay } } } @@ -110,7 +109,7 @@ type DaemonPen struct { } // NewDaemonPen creates a new DaemonPen -func NewDaemonPen(block conf.Block, vars map[string]string, log termlog.TermLog) (*DaemonPen, error) { +func NewDaemonPen(block conf.Block, vars map[string]string, log termlog.TermLog, maxRestartDelay time.Duration) (*DaemonPen, error) { d := make([]*daemon, len(block.Daemons)) for i, dmn := range block.Daemons { vcmd := varcmd.VarCmd{Block: nil, Modified: nil, Vars: vars} @@ -134,10 +133,11 @@ func NewDaemonPen(block conf.Block, vars map[string]string, log termlog.TermLog) } d[i] = &daemon{ - conf: dmn, - log: log.Stream(niceHeader("daemon: ", dmn.Command)), - shell: sh, - indir: indir, + conf: dmn, + log: log.Stream(niceHeader("daemon: ", dmn.Command)), + shell: sh, + indir: indir, + maxRestartDelay: maxRestartDelay, } } return &DaemonPen{daemons: d}, nil @@ -171,10 +171,10 @@ type DaemonWorld struct { } // NewDaemonWorld creates a DaemonWorld -func NewDaemonWorld(cnf *conf.Config, log termlog.TermLog) (*DaemonWorld, error) { +func NewDaemonWorld(cnf *conf.Config, log termlog.TermLog, maxRestartDelay time.Duration) (*DaemonWorld, error) { daemonPens := make([]*DaemonPen, len(cnf.Blocks)) for i, b := range cnf.Blocks { - d, err := NewDaemonPen(b, cnf.GetVariables(), log) + d, err := NewDaemonPen(b, cnf.GetVariables(), log, maxRestartDelay) if err != nil { return nil, err } diff --git a/modd.go b/modd.go index ebc3063..b027d28 100644 --- a/modd.go +++ b/modd.go @@ -52,22 +52,31 @@ var CommonExcludes = []string{ "**/node_modules/**", } +// ModRunnerArgs arguments for modd command +type ModRunnerArgs struct { + ConfPath string + ConfReload bool + MaxRestartDelay time.Duration +} + // ModRunner coordinates running the modd command type ModRunner struct { - Log termlog.TermLog - Config *conf.Config - ConfPath string - ConfReload bool - Notifiers []notify.Notifier + Log termlog.TermLog + Config *conf.Config + ConfPath string + ConfReload bool + MaxRestartDelay time.Duration + Notifiers []notify.Notifier } // NewModRunner constructs a new ModRunner -func NewModRunner(confPath string, log termlog.TermLog, notifiers []notify.Notifier, confreload bool) (*ModRunner, error) { +func NewModRunner(log termlog.TermLog, notifiers []notify.Notifier, ma ModRunnerArgs) (*ModRunner, error) { mr := &ModRunner{ - Log: log, - ConfPath: confPath, - ConfReload: confreload, - Notifiers: notifiers, + Log: log, + ConfPath: ma.ConfPath, + ConfReload: ma.ConfReload, + MaxRestartDelay: ma.MaxRestartDelay, + Notifiers: notifiers, } err := mr.ReadConfig() if err != nil { @@ -166,7 +175,7 @@ func (mr *ModRunner) trigger(root string, mod *moddwatch.Mod, dworld *DaemonWorl // Gives control of chan to caller func (mr *ModRunner) runOnChan(modchan chan *moddwatch.Mod, readyCallback func()) error { - dworld, err := NewDaemonWorld(mr.Config, mr.Log) + dworld, err := NewDaemonWorld(mr.Config, mr.Log, mr.MaxRestartDelay) if err != nil { return err } diff --git a/test/maxrestartdelay.conf b/test/maxrestartdelay.conf new file mode 100644 index 0000000..3e6319d --- /dev/null +++ b/test/maxrestartdelay.conf @@ -0,0 +1,5 @@ +# used to test max restart delay +# run and in another shell do echo test > changeme etc +changeme { + daemon: echo starting; exec sleep 1000000 +} \ No newline at end of file