-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(cli): add --dry
option to pebble run
#214
Changes from 1 commit
8ecc565
901dcd4
f615dbf
782783f
73954cf
e973894
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -42,7 +42,7 @@ func (s *mgrsSuite) SetUpTest(c *C) { | |||||||||||||||
|
||||||||||||||||
s.dir = c.MkDir() | ||||||||||||||||
|
||||||||||||||||
o, err := overlord.New(s.dir, nil, nil) | ||||||||||||||||
o, err := overlord.New(s.dir, nil, nil, false) | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the reason why I'm not a fan of boolean args: the call site is not readable unless you know the function prototype by heart. I'm even less of a fan of providing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function signature is just asking for a refactor of its arguments to a struct:
Suggested change
But that's unrelated to this specific PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method has remained quite simple, with those couple of arguments, for several years now. But every few weeks we have a PR that tries to add more parameters to it, and if we had allowed these to go on we'd have a struct with 50 different options by now. Not everything that needs to be passed down a chain of objects needs to be in a constructor. In the case at hand, per the other comment, we should not do DryStart on the Init, but rather inside Start itself and only if it has not yet run externally. On the Daemon, we can call a DryStart on the overlord itself when we're doing a dry run. |
||||||||||||||||
c.Assert(err, IsNil) | ||||||||||||||||
s.o = o | ||||||||||||||||
} | ||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -74,7 +74,7 @@ type Overlord struct { | |
|
||
// New creates a new Overlord with all its state managers. | ||
// It can be provided with an optional restart.Handler. | ||
func New(pebbleDir string, restartHandler restart.Handler, serviceOutput io.Writer) (*Overlord, error) { | ||
func New(pebbleDir string, restartHandler restart.Handler, serviceOutput io.Writer, dry bool) (*Overlord, error) { | ||
o := &Overlord{ | ||
pebbleDir: pebbleDir, | ||
loopTomb: new(tomb.Tomb), | ||
|
@@ -89,10 +89,18 @@ func New(pebbleDir string, restartHandler restart.Handler, serviceOutput io.Writ | |
} | ||
statePath := filepath.Join(pebbleDir, ".pebble.state") | ||
|
||
backend := &overlordStateBackend{ | ||
path: statePath, | ||
ensureBefore: o.ensureBefore, | ||
var backend state.Backend | ||
if dry { | ||
backend = &noopStateBackend{ | ||
ensureBefore: o.ensureBefore, | ||
} | ||
} else { | ||
backend = &overlordStateBackend{ | ||
path: statePath, | ||
ensureBefore: o.ensureBefore, | ||
} | ||
} | ||
|
||
s, err := loadState(statePath, restartHandler, backend) | ||
if err != nil { | ||
return nil, err | ||
|
@@ -133,6 +141,11 @@ func New(pebbleDir string, restartHandler restart.Handler, serviceOutput io.Writ | |
// the shared task runner should be added last! | ||
o.stateEng.AddManager(o.runner) | ||
|
||
// Dry start all managers. | ||
if err := o.stateEng.DryStart(); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this only happen if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really; the dry start mechanism, as discussed on KO026, always runs in order to run initialization tasks that do not incur in unwanted side-effects. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, but it's a bit awkward to see this here at the end of Init. I think this shoud be run when starting, or on a specific There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue of running this when starting (e.g. at the top of The issue of requiring callers to call an I think running this at the bottom of |
||
return nil, err | ||
} | ||
|
||
return o, nil | ||
} | ||
|
||
|
@@ -318,15 +331,15 @@ func (o *Overlord) settle(timeout time.Duration, beforeCleanups func()) error { | |
if timeout > 0 && time.Since(t0) > timeout { | ||
err := fmt.Errorf("Settle is not converging") | ||
if len(errs) != 0 { | ||
return &ensureError{append(errs, err)} | ||
return &multiError{append(errs, err)} | ||
} | ||
return err | ||
} | ||
next := o.ensureTimerReset() | ||
err := o.stateEng.Ensure() | ||
switch ee := err.(type) { | ||
case nil: | ||
case *ensureError: | ||
case *multiError: | ||
anpep marked this conversation as resolved.
Show resolved
Hide resolved
|
||
errs = append(errs, ee.errs...) | ||
default: | ||
errs = append(errs, err) | ||
|
@@ -353,7 +366,7 @@ func (o *Overlord) settle(timeout time.Duration, beforeCleanups func()) error { | |
} | ||
} | ||
if len(errs) != 0 { | ||
return &ensureError{errs} | ||
return &multiError{errs} | ||
} | ||
return nil | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this go all the way to the sanityCheck below, right before Start?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've extensively discussed this with @flotter, and one of his (rightful) concerns where around system-wide side effects caused by e.g. listening on a port, since it's an "external" side effect (i.e. contributes to Pebble initialization by persisting some state externally, be it listening on a socket or calling
.Checkpoint()
on the state backend).The idea is for the validation to be contained in the
.DryStart()
methods, and alsosanityCheck()
is a no-op for now anyway.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, there's definitely a limit that is worth considering, so @flotter is right on track. The question here is the cost-benefit: what are we leaving out by following a bit further, and what are the issues that can happen. For example, if we just open the port, it means we've validated that the port may be opened at all. Given that benefit, what's the cost? Will any messages be handled, or maybe not because that's done further down?