-
Notifications
You must be signed in to change notification settings - Fork 2.7k
pkg/hooks: Version the hook structure and add 1.0.0 hooks #686
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
Conversation
Can one of the admins verify this patch?
|
bot, add author to whitelist |
cb463e4
to
1fef007
Compare
pkg/hooks/hooks.go
Outdated
case "poststop": | ||
config.Hooks.Poststop = append(config.Hooks.Poststop, hook.Hook) | ||
default: | ||
panic(fmt.Sprintf("unknown stage %q", stage)) |
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 be a return err. Panic would bring cri-o down.
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.
Agree, should probably have an ErrUnknownStage and return errors.Wrapf(ErrUnknownStage, "invalid stage %q", stage)
or similar
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 be a return err. Panic would bring cri-o down.
This should be impossible, because we validate stages after reading them off the disk. The Manager
hook information is private, so I'm not aware of a way to get around the post-read validation and inject a buggy hook. So I don't think the panic
is an issue, but I'll push an update anyway. There could always be bugs I've missed ;).
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.
Agree, should probably have an ErrUnknownStage and
return errors.Wrapf(ErrUnknownStage, "invalid stage %q", stage)
or similar
Somehow I missed this comment earlier. I've updated this line since to return an error, but have not defined an ErrUnknownStage
. Do you expect callers to want to switch on that vs. other error conditions? I'm having trouble imagining when you'd want different handling than for, say, invalid-regexp errors.
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.
If we don't see this being hit often at all (and given your earlier comment on validation elsewhere, I'm content to believe that), there's no real point to having a specific error -- if we didn't have such validation, I think it would be helpful to have an identifier for a potentially common error
} | ||
reader, ok := Readers[ver.Version] | ||
if !ok { | ||
return nil, fmt.Errorf("unrecognized hook version: %q", ver.Version) |
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.
Do existing hooks have a version? I don't think so, we would need to warn and set the version to 1.0.
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.
Do existing hooks have a version? I don't think so, we would need to warn and set the version to 1.0.
I'm retroactively declaring them to be 0.1.0, see the initial comment in this PR, these docs, and this code.
a6d1c12
to
7227d93
Compare
LGTM, needs gofmt ... remember to sign all the commits ... |
also agree on the error handling |
libpod/container_internal.go
Outdated
} | ||
} | ||
return nil | ||
hasBindMounts := false // FIXME: what do we want here? https://github.com/projectatomic/libpod/pull/155#pullrequestreview-116002429 |
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 think this can probably be len(c.config.Spec.Mounts) > 0
... It's not 1:1 with actual bind mounts, but it's the closest we can get inside libpod
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 think this can probably be
len(c.config.Spec.Mounts) > 0
... It's not 1:1 with actual bind mounts, but it's the closest we can get inside libpod
We can always adjust the libpod API to be more precise. However, you're suggesting we just keep the current logic, so I expect we should punt any libpod API changes to follow-up work if we address this issue at all.
Can you recommend wording for the the docs explaining the difference, so we document the behavior to avoid surprising users?
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.
On further examination, we do add several mounts unconditionally to the spec by default, so my change compiles into an unconditional true
, avoiding the actual intent of the hook...
Given this, I think it's safest right now to keep your unconditional false
and document that hasBindMounts hooks are not currently implemented. We could add a boolean to config indicating that user-added bind mounts are present for the purpose of informing the hooks at a later date.
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.
Given this, I think it's safest right now to keep your unconditional
false
and document thathasBindMounts
hooks are not currently implemented.
Done and pushed as 51fb6c48.
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.
oci-umount relies on this. So it would be better if you unconditionally return true, until it is fixed. I would rather always run the hook and take the penalty rather then face problems with leaked mount points. But we need to fix this quickly.
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.
But we need to fix this quickly.
It's probably better if @mheon or someone else familiar with the config flow files a separate PR to update libpod to handle this correctly. I'll rebase this on top once that lands.
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.
@wking Sure, should be simple enough. I'll see about getting one in on Monday.
25a1627
to
51fb6c4
Compare
Should it be an error to call
I can use Should we keep the error fatal in |
@wking I think that if the default hooks directory does not exist, it should not be fatal. But if a directory was explicitly set and does not exist, that should be an error. I'd add a check to make sure the path we give to |
51fb6c4
to
acc35ed
Compare
Ok, so:
Do you have a position on “migrate away from the current default hook directory and require callers who need hooks to set the default directory explicitly”? I'm in weakly favor of that, but for now I'll assume you want to keep the existing default.
Do we need to bother there? A check at that point would be racy vs. checking at invocation time. If we want “directory was explicitly set and does not exist” to be fatal (and we seem to agree that we do), then I'd rather enforce that check in the
I think we want to keep this fatal because of the above race. I've pushed acc35edc0 with one potential implementation for the above criteria that makes the failed-race behavior too strict. |
@wking I like enforcing restrictions in I think the checks you added seem reasonable, though they should probably be moved into |
I've kicked this around a bit, but can't get an API I'm comfortable with. We can't just have You could do something like: func WithHooksDir(hooksDir string, dirNotExistFatal bool) RuntimeOption to make the returned error conditional, but then there's no way for the caller to know if the directory was successfully added or not (e.g. if it wanted to pass a warning up the call chain). Are we really settled on the runtime, err := libpod.NewRuntime()
…
err = runtime.SetHooksDir(c.GlobalString("hooks-dir-path"))
if err != nil && (c.GlobalIsSet("hooks-dir-path") || !os.IsNotExist(err)) {
return nil, err
}
err = runtime.Finalize()
return runtime, err callers would have much more flexibility about handling setter errors.
You'd still have the race I have in acc35edc0f, where the directory is On the other hand, the
I'm currently very happy with manager, err := hooks.New(ctx, []string{c.runtime.config.HooksDir})
if err != nil {
if c.runtime.config.HooksNewErrorFatal { // or something that only softens the directory-does-not-exist case
return err
}
logrus.Warn(err)
} |
I'm fine with the error from Your second suggestion, passing down an error, sounds fine, though I think we'd be better off just passing through an error from |
9586dc7
to
b22924c
Compare
b22924c
to
859cb3c
Compare
859cb3c
to
9394b2f
Compare
A func WithHooksDir(hooksDir string, dirNotExistFatal bool) RuntimeOption That allows us to avoid the |
If a .json file existed when we called ioutil.ReadDir but that file has been removed by the time we get around to calling Read on it, silently ignore the file. Iterating through all the files in the directory shouldn't take particularly long, so this is an unlikely corner case. And when it happens, silently ignoring the file gives the same outcome as you'd have gotten if the parallel remove had happened slightly earlier before the ioutil.ReadDir call. Signed-off-by: W. Trevor King <[email protected]> Closes: #686 Approved by: mheon
And add an argument to WithHooksDir to set it. If the hook dir doesn't exist, the new hooks package considers that a fatal error. When a podman caller sets --hooks-dir-path=/some/typoed/directory, a fatal error is more helpful than silently not loading any hooks. However, callers who call podman without setting --hooks-dir-path may not need hooks at all. We don't want to pester those callers with not-exist errors. With this commit, we: * Assume the caller knows what they're doing if they set --hooks-dir-path and set HooksDirNotExistFatal. * If the caller does not explicitly set --hooks-dir-path, assume they won't mind if the hook directory is missing and set HooksDirNotExistFatal false. We also considered checking for the directory's existence in the code calling WithHooksDir or from within WithHooksDir, but checks there would race with the underlying ioutil.ReadDir in the hooks package. By pushing the warn/error decision down into libpod's implementation, we avoid a racy "do we expect this to work once libpod gets to it?" pre-check. I've also added a check to error if WithHooksDir is called with an empty-string argument, because we haven't defined the semantics of that (is it clearing a previous value? Is it effectively the same as the current directory?). I agree with Matthew that a separate WithNoHooks, or a *string argument to WithHooks, or some such would be a better API for clearing previous values [1]. But for now, I'm just erroring out to fail early for callers who might otherwise be surprised that libpod ignores empty-string HooksDir. [1]: #686 (comment) Signed-off-by: W. Trevor King <[email protected]> Closes: #686 Approved by: mheon
We also considered ordering with sort.Strings, but Matthew rejected that because it uses a byte-by-byte UTF-8 comparison [1] which would fail many language-specific conventions [2]. There's some more discussion of the localeToLanguage mapping in [3]. Currently language.Parse does not handle either 'C' or 'POSIX', returning: und, language: tag is not well-formed for both. [1]: #686 (comment) [2]: https://en.wikipedia.org/wiki/Alphabetical_order#Language-specific_conventions [3]: golang/go#25340 Signed-off-by: W. Trevor King <[email protected]> Closes: #686 Approved by: mheon
Following the vndr docs [1]: $ go get -u github.com/LK4D4/vndr $ vndr golang.org/x/text $ git add -A vendor/golang.org/x/text The targeted 'git add' was because we seem to have versioned some test files (e.g. vendor/github.com/varlink/go/varlink/varlink_test.go in 8493dba (Initial varlink implementation, 2018-03-26, #627). I don't know why, possibly an old vndr version? But either way, I'm punting that particular issue to a separate branch. [1]: https://github.com/LK4D4/vndr/blob/1fc68ee0c852556a9ed53cbde16247033f104111/README.md Signed-off-by: W. Trevor King <[email protected]> Closes: #686 Approved by: mheon
☀️ Test successful - status-papr |
We've had this functionality since 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, containers#686), but didn't have any user-facing docs for it. Signed-off-by: W. Trevor King <[email protected]>
We've had this functionality since 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, #686), but didn't have any user-facing docs for it. Signed-off-by: W. Trevor King <[email protected]> Closes: #811 Approved by: mheon
The continue here is from 5676597 (hooks/read: Ignore IsNotExist for JSON files in ReadDir, 2018-04-27, containers#686), where it was intended to silently ignore missing JSON files. However, the old logic was also silently ignoring not-exist errors from the os.Stat(hook.Hook.Path) from 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, containers#686). This commit adjusts the check so JSON not-exist errors continue to be silently ignored while hook executable not-exist errors become fatal. Signed-off-by: W. Trevor King <[email protected]>
This typo from 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, containers#686) was causing any 'annotations' entries in hook JSON to be silently ignored. Signed-off-by: W. Trevor King <[email protected]>
The typo is a copy/paste error from 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, containers#686). Signed-off-by: W. Trevor King <[email protected]>
Reported by Gary Edwards [1]. Both typos are originally from 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, containers#686). [1]: containers#884 (comment) Signed-off-by: W. Trevor King <[email protected]>
We've had logrus logging in the monitor code since it landed in 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, containers#686). This commit adds similar logging to the initial hook.New() and Manager.Hooks() calls to make it easier to see if those are working as expected. Signed-off-by: W. Trevor King <[email protected]>
The continue here is from 5676597 (hooks/read: Ignore IsNotExist for JSON files in ReadDir, 2018-04-27, #686), where it was intended to silently ignore missing JSON files. However, the old logic was also silently ignoring not-exist errors from the os.Stat(hook.Hook.Path) from 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, #686). This commit adjusts the check so JSON not-exist errors continue to be silently ignored while hook executable not-exist errors become fatal. Signed-off-by: W. Trevor King <[email protected]> Closes: #887 Approved by: rhatdan
This typo from 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, #686) was causing any 'annotations' entries in hook JSON to be silently ignored. Signed-off-by: W. Trevor King <[email protected]> Closes: #887 Approved by: rhatdan
The typo is a copy/paste error from 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, #686). Signed-off-by: W. Trevor King <[email protected]> Closes: #887 Approved by: rhatdan
Reported by Gary Edwards [1]. Both typos are originally from 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, #686). [1]: #884 (comment) Signed-off-by: W. Trevor King <[email protected]> Closes: #887 Approved by: rhatdan
We've had logrus logging in the monitor code since it landed in 68eb128 (pkg/hooks: Version the hook structure and add 1.0.0 hooks, 2018-04-27, #686). This commit adds similar logging to the initial hook.New() and Manager.Hooks() calls to make it easier to see if those are working as expected. Signed-off-by: W. Trevor King <[email protected]> Closes: #887 Approved by: rhatdan
This shifts the matching logic out of
libpod/container_internal
and into thehook
package, where we can reuse it after vendoring into CRI-O (as @rhatdan suggested here). It also adds unit tests with almost-complete coverage. Nowlibpod
is even more isolated from thehook
internals, which makes it fairly straightforward to bump the hook config file to 1.0.0. I've dubbed the old format 0.1.0, although it doesn't specify an explicit version. Motivation for some of my changes with 1.0.0:Add an explicit
version
field. This will make any future JSON structure migrations more straightforward by avoiding the need for version-guessing heuristics.Collect the matching properties in a new
When
sub-structure. This makes the rootHook
structure easier to understand, because you don't have to read over all the matching properties when wrapping your head aroundHook
.Replace the old
hook
andarguments
with a direct embedding of the runtime-spec's hook structure. This provides access to additional upstream properties (args[0]
,env
, andtimeout
) and avoids the complication of a libpod-specific analog structure.Add a
when.always
property. You can usually accomplish this effect in another way (e.g.when.commands = [".*"]
), but having a boolean explicitly for this use-case makes for easier reading and writing.Replace the previous
annotations
array with anannotations
map. The 0.1.0 approach matched only the values regardless of key, and that seems unreliable.Replace
cmds
withwhen.commands
, because while there are a few ways to abbreviate “commands”, there's only one way to write it out in full ;). This gives folks one less thing to remember when writing hook JSON.I've also added doc-compat support for the various pluralizations of the 0.1.0 properties. Previously, the docs and code were not in agreement (e.g. doc
stages
vs. codestage
). More on this particular facet in cri-o/cri-o#1235.And I've updated the docs to point out that the annotations being matched are the OCI config annotations. This differs from CRI-O, where the annotations used are the Kubernetes-supplied annotations. For example,
io.kubernetes.cri-o.Volumes
is part of CRI-O's runtime config annotations, but not part of the Kubernetes-supplied annotations CRI-O uses for matching hooks.The
Monitor
method supports the CRI-O use-case. podman doesn't need it directly, but CRI-O will need it when we vendor this package there.The WIP in the subject is because I'm not sure what the has-bind-mounts logic should look like, although I think the current
len(c.config.Spec.Mounts) > 0
is not quite what we want.