From 05567f2c944992ead055c6e221bc44373ff6f587 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 10 Sep 2015 17:57:31 -0700 Subject: [PATCH] Implement hooks in libcontainer Signed-off-by: Michael Crosby --- libcontainer/configs/config.go | 73 +++++++++++++++++++++++++++++++++ libcontainer/configs/mount.go | 7 ---- libcontainer/container_linux.go | 10 +++++ libcontainer/process_linux.go | 12 ++++++ 4 files changed, 95 insertions(+), 7 deletions(-) diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index 1179f63cf..7c42ee39b 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -1,5 +1,11 @@ package configs +import ( + "bytes" + "encoding/json" + "os/exec" +) + type Rlimit struct { Type int `json:"type"` Hard uint64 `json:"hard"` @@ -159,4 +165,71 @@ type Config struct { // A number of rules are given, each having an action to be taken if a syscall matches it. // A default action to be taken if no rules match is also given. Seccomp *Seccomp `json:"seccomp"` + + // Hooks are a collection of actions to perform at various container lifecycle events. + // Hooks are not able to be marshaled to json but they are also not needed to. + Hooks *Hooks `json:"-"` +} + +type Hooks struct { + // Prestart commands are executed after the container namespaces are created, + // but before the user supplied command is executed from init. + Prestart []Hook + + // PostStop commands are executed after the container init process exits. + Poststop []Hook +} + +// HookState is the payload provided to a hook on execution. +type HookState struct { + ID string `json:"id"` + Pid int `json:"pid"` +} + +type Hook interface { + // Run executes the hook with the provided state. + Run(*HookState) error +} + +// NewFunctionHooks will call the provided function when the hook is run. +func NewFunctionHook(f func(*HookState) error) *FuncHook { + return &FuncHook{ + Run: f, + } +} + +type FuncHook struct { + Run func(*HookState) error +} + +type Command struct { + Path string `json:"path"` + Args []string `json:"args"` + Env []string `json:"env"` + Dir string `json:"dir"` +} + +// NewCommandHooks will execute the provided command when the hook is run. +func NewCommandHook(cmd Command) *CommandHook { + return &CommandHook{ + Command: cmd, + } +} + +type CommandHook struct { + Command +} + +func (c *Command) Run(s *HookState) error { + b, err := json.Marshal(s) + if err != nil { + return err + } + cmd := exec.Cmd{ + Path: c.Path, + Args: c.Args, + Env: c.Env, + Stdin: bytes.NewReader(b), + } + return cmd.Run() } diff --git a/libcontainer/configs/mount.go b/libcontainer/configs/mount.go index 5a69f815e..50668f04f 100644 --- a/libcontainer/configs/mount.go +++ b/libcontainer/configs/mount.go @@ -25,10 +25,3 @@ type Mount struct { // Optional Command to be run after Source is mounted. PostmountCmds []Command `json:"postmount_cmds"` } - -type Command struct { - Path string `json:"path"` - Args []string `json:"args"` - Env []string `json:"env"` - Dir string `json:"dir"` -} diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index 9210ec6a3..481b81c95 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -247,6 +247,16 @@ func (c *linuxContainer) Destroy() error { err = rerr } c.initProcess = nil + if c.config.Hooks != nil { + s := configs.HookState{ + ID: c.id, + } + for _, hook := range c.config.Hooks.Poststop { + if err := hook.Run(&s); err != nil { + return err + } + } + } return err } diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index 0fe06e8a6..9b5b562fc 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -13,6 +13,7 @@ import ( "syscall" "github.com/opencontainers/runc/libcontainer/cgroups" + "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/system" ) @@ -200,6 +201,17 @@ func (p *initProcess) start() (err error) { p.manager.Destroy() } }() + if p.config.Config.Hooks != nil { + s := configs.HookState{ + ID: p.container.id, + Pid: p.pid(), + } + for _, hook := range p.config.Config.Hooks.Prestart { + if err := hook.Run(&s); err != nil { + return newSystemError(err) + } + } + } if err := p.createNetworkInterfaces(); err != nil { return newSystemError(err) }