summaryrefslogtreecommitdiff
path: root/pkg/hooks/exec/exec.go
diff options
context:
space:
mode:
authorW. Trevor King <wking@tremily.us>2018-05-30 16:53:10 -0700
committerAtomic Bot <atomic-devel@projectatomic.io>2018-05-31 21:45:27 +0000
commit81d6f082f36940f72c8a1961ac1150d7e63e415f (patch)
treebde859f752a3ebf478daa3cea4d147d0b416dff6 /pkg/hooks/exec/exec.go
parent824ea4da338891aff122f08f8f97b4d08b1a1e95 (diff)
downloadpodman-81d6f082f36940f72c8a1961ac1150d7e63e415f.tar.gz
podman-81d6f082f36940f72c8a1961ac1150d7e63e415f.tar.bz2
podman-81d6f082f36940f72c8a1961ac1150d7e63e415f.zip
pkg/hooks/exec: Add a new package for local hook execution
This wraps os/exec to: * Clear the environment when the hook doesn't set 'env'. The runtime spec has [1]: > * env (array of strings, OPTIONAL) with the same semantics as IEEE > Std 1003.1-2008's environ. And running execle or similar with NULL env results in an empty environment: $ cat test.c #include <unistd.h> int main() { return execle("/usr/bin/env", "env", NULL, NULL); } $ cc -o test test.c $ ./test ...no output... Go's Cmd.Env, on the other hand, has [2]: > If Env is nil, the new process uses the current process's environment. This commit works around that by setting []string{} in those cases to avoid leaking the runtime environment into the hooks. * Roll the 'timeout' value (if set) into the passed context. There's no need for two separate ways to cancel hook execution. * Add a configurable timeout on abandoning a post-kill wait. The waiting goroutine will continue and eventually reap the process, but this avoids blocking the Run() call when that takes inordinately long (for example, if a GPU cleanup hook is stuck in I/O sleep [3]). The 'env' output format is specified in POSIX [4]. [1]: https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#posix-platform-hooks [2]: https://golang.org/pkg/os/exec/#Cmd [3]: https://github.com/projectatomic/libpod/pull/857#discussion_r192191002 [4]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html Signed-off-by: W. Trevor King <wking@tremily.us> Closes: #857 Approved by: mheon
Diffstat (limited to 'pkg/hooks/exec/exec.go')
-rw-r--r--pkg/hooks/exec/exec.go62
1 files changed, 62 insertions, 0 deletions
diff --git a/pkg/hooks/exec/exec.go b/pkg/hooks/exec/exec.go
new file mode 100644
index 000000000..94469b1d2
--- /dev/null
+++ b/pkg/hooks/exec/exec.go
@@ -0,0 +1,62 @@
+// Package exec provides utilities for executing Open Container Initative runtime hooks.
+package exec
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ osexec "os/exec"
+ "time"
+
+ rspec "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// DefaultPostKillTimeout is the recommended default post-kill timeout.
+const DefaultPostKillTimeout = time.Duration(10) * time.Second
+
+// Run executes the hook and waits for it to complete or for the
+// context or hook-specified timeout to expire.
+func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer, stderr io.Writer, postKillTimeout time.Duration) (hookErr, err error) {
+ cmd := osexec.Cmd{
+ Path: hook.Path,
+ Args: hook.Args,
+ Env: hook.Env,
+ Stdin: bytes.NewReader(state),
+ Stdout: stdout,
+ Stderr: stderr,
+ }
+ if cmd.Env == nil {
+ cmd.Env = []string{}
+ }
+
+ if hook.Timeout != nil {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithTimeout(ctx, time.Duration(*hook.Timeout)*time.Second)
+ defer cancel()
+ }
+
+ err = cmd.Start()
+ if err != nil {
+ return err, err
+ }
+ exit := make(chan error, 1)
+ go func() {
+ exit <- cmd.Wait()
+ }()
+
+ select {
+ case err = <-exit:
+ return err, err
+ case <-ctx.Done():
+ cmd.Process.Kill()
+ timer := time.NewTimer(postKillTimeout)
+ defer timer.Stop()
+ select {
+ case <-timer.C:
+ err = fmt.Errorf("failed to reap process within %s of the kill signal", postKillTimeout)
+ case err = <-exit:
+ }
+ return err, ctx.Err()
+ }
+}