Merge pull request #261 from crosbymichael/hooks
Implement hooks in libcontainer code base
This commit is contained in:
commit
7d122ff30e
|
@ -1,5 +1,11 @@
|
||||||
package configs
|
package configs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
type Rlimit struct {
|
type Rlimit struct {
|
||||||
Type int `json:"type"`
|
Type int `json:"type"`
|
||||||
Hard uint64 `json:"hard"`
|
Hard uint64 `json:"hard"`
|
||||||
|
@ -159,4 +165,76 @@ type Config struct {
|
||||||
// A number of rules are given, each having an action to be taken if a syscall matches it.
|
// 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.
|
// A default action to be taken if no rules match is also given.
|
||||||
Seccomp *Seccomp `json:"seccomp"`
|
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"`
|
||||||
|
Root string `json:"root"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FuncHook) Run(s HookState) error {
|
||||||
|
return f.run(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,3 @@ type Mount struct {
|
||||||
// Optional Command to be run after Source is mounted.
|
// Optional Command to be run after Source is mounted.
|
||||||
PostmountCmds []Command `json:"postmount_cmds"`
|
PostmountCmds []Command `json:"postmount_cmds"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Command struct {
|
|
||||||
Path string `json:"path"`
|
|
||||||
Args []string `json:"args"`
|
|
||||||
Env []string `json:"env"`
|
|
||||||
Dir string `json:"dir"`
|
|
||||||
}
|
|
||||||
|
|
|
@ -185,6 +185,7 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
|
||||||
parentPipe: parentPipe,
|
parentPipe: parentPipe,
|
||||||
manager: c.cgroupManager,
|
manager: c.cgroupManager,
|
||||||
config: c.newInitConfig(p),
|
config: c.newInitConfig(p),
|
||||||
|
container: c,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,6 +248,17 @@ func (c *linuxContainer) Destroy() error {
|
||||||
err = rerr
|
err = rerr
|
||||||
}
|
}
|
||||||
c.initProcess = nil
|
c.initProcess = nil
|
||||||
|
if c.config.Hooks != nil {
|
||||||
|
s := configs.HookState{
|
||||||
|
ID: c.id,
|
||||||
|
Root: c.config.Rootfs,
|
||||||
|
}
|
||||||
|
for _, hook := range c.config.Hooks.Poststop {
|
||||||
|
if err := hook.Run(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -932,3 +932,65 @@ func TestOomScoreAdj(t *testing.T) {
|
||||||
t.Fatalf("Expected oom_score_adj %d; got %q", config.OomScoreAdj, outputOomScoreAdj)
|
t.Fatalf("Expected oom_score_adj %d; got %q", config.OomScoreAdj, outputOomScoreAdj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHook(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
root, err := newTestRoot()
|
||||||
|
ok(t, err)
|
||||||
|
defer os.RemoveAll(root)
|
||||||
|
|
||||||
|
rootfs, err := newRootfs()
|
||||||
|
ok(t, err)
|
||||||
|
defer remove(rootfs)
|
||||||
|
|
||||||
|
config := newTemplateConfig(rootfs)
|
||||||
|
config.Hooks = &configs.Hooks{
|
||||||
|
Prestart: []configs.Hook{
|
||||||
|
configs.NewFunctionHook(func(s configs.HookState) error {
|
||||||
|
f, err := os.Create(filepath.Join(s.Root, "test"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return f.Close()
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Poststop: []configs.Hook{
|
||||||
|
configs.NewFunctionHook(func(s configs.HookState) error {
|
||||||
|
return os.RemoveAll(filepath.Join(s.Root, "test"))
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
container, err := factory.Create("test", config)
|
||||||
|
ok(t, err)
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
pconfig := libcontainer.Process{
|
||||||
|
Args: []string{"sh", "-c", "ls /test"},
|
||||||
|
Env: standardEnvironment,
|
||||||
|
Stdin: nil,
|
||||||
|
Stdout: &stdout,
|
||||||
|
}
|
||||||
|
err = container.Start(&pconfig)
|
||||||
|
ok(t, err)
|
||||||
|
|
||||||
|
// Wait for process
|
||||||
|
waitProcess(&pconfig, t)
|
||||||
|
|
||||||
|
outputLs := string(stdout.Bytes())
|
||||||
|
|
||||||
|
// Check that the ls output has the expected file touched by the prestart hook
|
||||||
|
if !strings.Contains(outputLs, "/test") {
|
||||||
|
container.Destroy()
|
||||||
|
t.Fatalf("ls output doesn't have the expected file: %s", outputLs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := container.Destroy(); err != nil {
|
||||||
|
t.Fatalf("container destory %s", err)
|
||||||
|
}
|
||||||
|
fi, err := os.Stat(filepath.Join(rootfs, "test"))
|
||||||
|
if err == nil || !os.IsNotExist(err) {
|
||||||
|
t.Fatalf("expected file to not exist, got %s", fi.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
"github.com/opencontainers/runc/libcontainer/system"
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -200,6 +201,18 @@ func (p *initProcess) start() (err error) {
|
||||||
p.manager.Destroy()
|
p.manager.Destroy()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
if p.config.Config.Hooks != nil {
|
||||||
|
s := configs.HookState{
|
||||||
|
ID: p.container.id,
|
||||||
|
Pid: p.pid(),
|
||||||
|
Root: p.config.Config.Rootfs,
|
||||||
|
}
|
||||||
|
for _, hook := range p.config.Config.Hooks.Prestart {
|
||||||
|
if err := hook.Run(s); err != nil {
|
||||||
|
return newSystemError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := p.createNetworkInterfaces(); err != nil {
|
if err := p.createNetworkInterfaces(); err != nil {
|
||||||
return newSystemError(err)
|
return newSystemError(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue