-
Notifications
You must be signed in to change notification settings - Fork 134
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
Add ability to render the template config files. #271
Conversation
This is useful in AutoPilot applications like MySQL that need to rewrite the containerpilot.conf file and force a SIGHUP.
The unit tests now cast the runnable to core.App in order to pass.
As I noted in that MySQL thread, we are intentionally not rendering the entire ContainerPilot configuration. Do you have another example use case? |
Ask, and a use case appears! #272 Is this use case something we could do with this PR? |
Cool, @BobDickinson says that this patch would address #272. Let me know what joyent needs from my side to merge this PR. |
config/render.go
Outdated
} | ||
|
||
// NewRenderApp creates a new Config Rendering application | ||
func NewRenderApp(configFlag string, renderFlag string) (*RenderApp, error) { |
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 would much rather see a Render
method on Config
than have this logic in a new interface around App
. I see why you've created the new interface to begin with but it should delegate all the behavior to Config
. The approach here ends up duplicating a bunch of code and that's going to be a source of bugs (like for example in this case we've left out all the template filter functions). Instead we should be calling ParseConfig
to get a fully-parsed configuration that is identical to the one that the main application uses, and then making a Render
call to that to dump it.
I suspect we should have this Render
method dump a string (i.e. should have a signature like func (c *Config) Render() string
) and then have the caller decide what to do with the string.
I'm also wondering whether we need to have a new interface on App
at all. For the version
flag we just did this right in the LoadApp
and exited early: https://github.com/joyent/containerpilot/pull/271/files#diff-639ed475181e0558956e92333cd5e9c0R81
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.
Oh actually maybe an even better signature would be func (c *Config) Render(io.Writer)
, so that the caller can pass in an arbitrary Writer
for us to stream bytes into.
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.
Starting with easy one, I will change the code to look more like the "-version", i.e. get rid of the "app" function. In that case, I just need to embed more of the logic in app.go.
On the issue of rendering the "config file" vs. "config struct": What I had in mind while writing the code is that after containerpilot -render
is called you have a config file that can be used as input to containerpilot -config
. For that to work correctly (I think) I only need to call 'ApplyTemplate' because all the additional work of creating the 'Config struct' will happen once containerpilot
gets its SIGHUP and 'ParseConfig' gets called. The main idea here is to intentionally not create the struct and instead just render the template and obtain a valid JSON back.
I went through the ParseConfig file again and I did not see the "template filter functions" you talked about. Can you be more specific where they are located?
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 went through the ParseConfig file again and I did not see the "template filter functions" you talked about. Can you be more specific where they are located?
I was talking about these but on second look you actually are passing thru those. So the approach you have after 06e0dc6 is fine but let's factor out the repeated code into functions
core/app.go
Outdated
var versionFlag bool | ||
|
||
if !flag.Parsed() { | ||
flag.StringVar(&configFlag, "config", "", | ||
"JSON config or file:// path to JSON config file.") | ||
flag.StringVar(&renderFlag, "render", "", | ||
"- for stdout or file:// path where to save redenred JSON config file.") |
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 recognize that it would make the parsing harder, but maybe would it more more ergonomic to have the argument to -render
be optional? So one could do:
$ containerpilot -render | whatever
$ containerpilot -render -config file://containerpilot.json | whatever
$ containerpilot -render file://myfile
$ containerpilot -render file://myfile -config file://containerpilot.json
(I'd like to eliminate the file://
prefix requirement here too, but that'd break backwards compatibility with the existing config flag parsing so we're stuck with it until 3.x.)
Also, we've got a typo on the help string here.
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 have no problems with the proposed syntax. The only difficulty I found, is that the "flag" module does not look like it cleanly supports what you are asking, I am new to Go so I could be wrong here.
My implementation to what you are asking would be the following:
...
flag.StringVar(&renderFlag, "render", "/dev/null", ...)
...
if renderFlag != "/dev/null" {
// run the rendering code path...
}
The issue is that if the user runs containerpilot -render /dev/null
we will actually end up executing the regular containerpilot application.
If that is still okay I will make the change.
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.
Sorry, 'flag' did not work as I thought it would. I am not sure how to implement what you asked without bigger surgery.
core/app_test.go
Outdated
@@ -58,10 +58,11 @@ func TestValidConfigParse(t *testing.T) { | |||
|
|||
os.Setenv("TEST", "HELLO") | |||
os.Args = []string{"this", "-config", testJSON, "/testdata/test.sh", "valid1", "--debug"} | |||
app, err := LoadApp() | |||
runnable, err := LoadApp() |
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.
We'll definitely need tests for this new feature. Some things to test:
- passing the
-render
flag with valid arguments, invalid arguments, and without arguments - that the rendered output is as expected, including that all templating is being applied correctly
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.
Completely agree, I was waiting for feedback before I spent more time on it.
Well, @sodre you definitely beat me to it. Your suggestion, and once the other autopilotpattern/mysql#77 PR was closed I was going to do this. I guess great minds think alike? :-) Definitely in favour. |
@tgross,
I have that implemented in my local repo, if you like it, I can push it forward. When ContainerPilot moves to 3.0, I would suggest using golang's FlagSet and use a subcommand "template" instead of a flag, e.g. |
- containerpilot -template [-config file://] [-out file://]
Sure, let's go with that.
Absolutely agreed on that. |
core/app_test.go
Outdated
} | ||
|
||
func TestRenderConfigFileStdout(t *testing.T) { | ||
// Because of the "exit(0)" in LoadApp, we need to use this testing pattern |
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.
This is a code smell that we haven't factored out the functionality enough.
Just FYI I'm going to cut a patch release (2.6.1) for some minor bug fixes, but this is getting into good enough shape that I think it'll be a good basis for the next minor version release (2.7.0). |
That sounds good. Let me know what other things you would like changed. |
I did a quick refactor to cover my comments here so once Travis gives us the thumbs up I think this is ready to merge. |
(looking for a thumbs-up emoji to put on the "@tgross approved these changes", but no way to add a reaction to that....) |
This is useful in AutoPilot applications like MySQL that need to rewrite the containerpilot.conf file and force a SIGHUP. In particular, we could correctly address autopilotpattern/mysql#77.
You can use the code like this:
$CONTAINERPILOT
to stdoutcontainerpilot -render -
$CONTAINERPILOT
to filecontainerpilot -render file:///etc/cp.conf
containerpilot -config file:///etc/cp.tmpl -render -
containerpilot -config file:///etc/cp.tmpl -render file:///etc/cp.conf