Tests the new Hook

Signed-off-by: Renaud Gaubert <rgaubert@nvidia.com>
This commit is contained in:
Renaud Gaubert 2020-05-09 01:05:12 -07:00
parent ccdd75760c
commit 2f7bdf9d3b
5 changed files with 156 additions and 72 deletions

View File

@ -239,10 +239,6 @@ const (
Poststop = "poststop"
)
var (
HookNameList = []HookName{Prestart, CreateRuntime, CreateContainer, StartContainer, Poststart, Poststop}
)
// TODO move this to runtime-spec
// See: https://github.com/opencontainers/runtime-spec/pull/1046
const (

View File

@ -8,6 +8,10 @@ import (
"testing"
)
var (
HookNameList = []HookName{Prestart, CreateRuntime, CreateContainer, StartContainer, Poststart, Poststop}
)
func loadConfig(name string) (*Config, error) {
f, err := os.Open(filepath.Join("../sample_configs", name))
if err != nil {

View File

@ -15,27 +15,29 @@ import (
func TestUnmarshalHooks(t *testing.T) {
timeout := time.Second
prestartCmd := configs.NewCommandHook(configs.Command{
Path: "/var/vcap/hooks/prestart",
hookCmd := configs.NewCommandHook(configs.Command{
Path: "/var/vcap/hooks/hook",
Args: []string{"--pid=123"},
Env: []string{"FOO=BAR"},
Dir: "/var/vcap",
Timeout: &timeout,
})
prestart, err := json.Marshal(prestartCmd.Command)
hookJson, err := json.Marshal(hookCmd)
if err != nil {
t.Fatal(err)
}
hook := configs.Hooks{}
err = hook.UnmarshalJSON([]byte(fmt.Sprintf(`{"Prestart" :[%s]}`, prestart)))
if err != nil {
t.Fatal(err)
}
for _, hookName := range configs.HookNameList {
hooks := configs.Hooks{}
err = hooks.UnmarshalJSON([]byte(fmt.Sprintf(`{"%s" :[%s]}`, hookName, hookJson)))
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(hook.Prestart[0], prestartCmd) {
t.Errorf("Expected prestart to equal %+v but it was %+v",
prestartCmd, hook.Prestart[0])
if !reflect.DeepEqual(hooks[hookName], hookCmd) {
t.Errorf("Expected %s to equal %+v but it was %+v", hookName, hookCmd, hooks[hookName])
}
}
}
@ -50,8 +52,8 @@ func TestUnmarshalHooksWithInvalidData(t *testing.T) {
func TestMarshalHooks(t *testing.T) {
timeout := time.Second
prestartCmd := configs.NewCommandHook(configs.Command{
Path: "/var/vcap/hooks/prestart",
hookCmd := configs.NewCommandHook(configs.Command{
Path: "/var/vcap/hooks/hook",
Args: []string{"--pid=123"},
Env: []string{"FOO=BAR"},
Dir: "/var/vcap",
@ -59,14 +61,21 @@ func TestMarshalHooks(t *testing.T) {
})
hook := configs.Hooks{
Prestart: []configs.Hook{prestartCmd},
configs.Prestart: configs.HookList{hookCmd},
configs.CreateRuntime: configs.HookList{hookCmd},
configs.CreateContainer: configs.HookList{hookCmd},
configs.StartContainer: configs.HookList{hookCmd},
configs.Poststart: configs.HookList{hookCmd},
configs.Poststop: configs.HookList{hookCmd},
}
hooks, err := hook.MarshalJSON()
if err != nil {
t.Fatal(err)
}
h := `{"poststart":null,"poststop":null,"prestart":[{"path":"/var/vcap/hooks/prestart","args":["--pid=123"],"env":["FOO=BAR"],"dir":"/var/vcap","timeout":1000000000}]}`
// Note Marshal seems to output fields in alphabetical order
hookCmdJson := `[{"path":"/var/vcap/hooks/hook","args":["--pid=123"],"env":["FOO=BAR"],"dir":"/var/vcap","timeout":1000000000}]`
h := fmt.Sprintf(`{"createContainer":%[1]s,"createRuntime":%[1]s,"poststart":%[1]s,"poststop":%[1]s,"prestart":%[1]s,"startContainer":%[1]s}`, hookCmdJson)
if string(hooks) != h {
t.Errorf("Expected hooks %s to equal %s", string(hooks), h)
}
@ -75,8 +84,8 @@ func TestMarshalHooks(t *testing.T) {
func TestMarshalUnmarshalHooks(t *testing.T) {
timeout := time.Second
prestart := configs.NewCommandHook(configs.Command{
Path: "/var/vcap/hooks/prestart",
hookCmd := configs.NewCommandHook(configs.Command{
Path: "/var/vcap/hooks/hook",
Args: []string{"--pid=123"},
Env: []string{"FOO=BAR"},
Dir: "/var/vcap",
@ -84,7 +93,12 @@ func TestMarshalUnmarshalHooks(t *testing.T) {
})
hook := configs.Hooks{
Prestart: []configs.Hook{prestart},
configs.Prestart: configs.HookList{hookCmd},
configs.CreateRuntime: configs.HookList{hookCmd},
configs.CreateContainer: configs.HookList{hookCmd},
configs.StartContainer: configs.HookList{hookCmd},
configs.Poststart: configs.HookList{hookCmd},
configs.Poststop: configs.HookList{hookCmd},
}
hooks, err := hook.MarshalJSON()
if err != nil {
@ -96,8 +110,8 @@ func TestMarshalUnmarshalHooks(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(umMhook.Prestart[0], prestart) {
t.Errorf("Expected hooks to be equal after mashaling -> unmarshaling them: %+v, %+v", umMhook.Prestart[0], prestart)
if !reflect.DeepEqual(umMhook, hook) {
t.Errorf("Expected hooks to be equal after mashaling -> unmarshaling them: %+v, %+v", umMhook, hook)
}
}
@ -106,14 +120,14 @@ func TestMarshalHooksWithUnexpectedType(t *testing.T) {
return nil
})
hook := configs.Hooks{
Prestart: []configs.Hook{fHook},
configs.CreateRuntime: configs.HookList{fHook},
}
hooks, err := hook.MarshalJSON()
if err != nil {
t.Fatal(err)
}
h := `{"poststart":null,"poststop":null,"prestart":null}`
h := `{"createContainer":null,"createRuntime":null,"poststart":null,"poststop":null,"prestart":null,"startContainer":null}`
if string(hooks) != h {
t.Errorf("Expected hooks %s to equal %s", string(hooks), h)
}

View File

@ -1118,39 +1118,67 @@ func TestHook(t *testing.T) {
}
return config.Rootfs, nil
}
createFileFromBundle := func(filename, bundle string) error {
root, err := getRootfsFromBundle(bundle)
if err != nil {
return err
}
config.Hooks = &configs.Hooks{
Prestart: []configs.Hook{
f, err := os.Create(filepath.Join(root, filename))
if err != nil {
return err
}
return f.Close()
}
// Note FunctionHooks can't be serialized to json this means they won't be passed down to the container
// For CreateContainer and StartContainer which run in the container namespace, this means we need to pass Command Hooks.
hookFiles := map[configs.HookName]string{
configs.Prestart: "prestart",
configs.CreateRuntime: "createRuntime",
configs.CreateContainer: "createContainer",
configs.StartContainer: "startContainer",
configs.Poststart: "poststart",
}
config.Hooks = configs.Hooks{
configs.Prestart: configs.HookList{
configs.NewFunctionHook(func(s *specs.State) error {
if s.Bundle != expectedBundle {
t.Fatalf("Expected prestart hook bundlePath '%s'; got '%s'", expectedBundle, s.Bundle)
}
root, err := getRootfsFromBundle(s.Bundle)
if err != nil {
return err
}
f, err := os.Create(filepath.Join(root, "test"))
if err != nil {
return err
}
return f.Close()
return createFileFromBundle(hookFiles[configs.Prestart], s.Bundle)
}),
},
Poststart: []configs.Hook{
configs.CreateRuntime: configs.HookList{
configs.NewFunctionHook(func(s *specs.State) error {
if s.Bundle != expectedBundle {
t.Fatalf("Expected createRuntime hook bundlePath '%s'; got '%s'", expectedBundle, s.Bundle)
}
return createFileFromBundle(hookFiles[configs.CreateRuntime], s.Bundle)
}),
},
configs.CreateContainer: configs.HookList{
configs.NewCommandHook(configs.Command{
Path: "/bin/bash",
Args: []string{"/bin/bash", "-c", fmt.Sprintf("touch ./%s", hookFiles[configs.CreateContainer])},
}),
},
configs.StartContainer: configs.HookList{
configs.NewCommandHook(configs.Command{
Path: "/bin/sh",
Args: []string{"/bin/sh", "-c", fmt.Sprintf("touch /%s", hookFiles[configs.StartContainer])},
}),
},
configs.Poststart: configs.HookList{
configs.NewFunctionHook(func(s *specs.State) error {
if s.Bundle != expectedBundle {
t.Fatalf("Expected poststart hook bundlePath '%s'; got '%s'", expectedBundle, s.Bundle)
}
root, err := getRootfsFromBundle(s.Bundle)
if err != nil {
return err
}
return ioutil.WriteFile(filepath.Join(root, "test"), []byte("hello world"), 0755)
return createFileFromBundle(hookFiles[configs.Poststart], s.Bundle)
}),
},
Poststop: []configs.Hook{
configs.Poststop: configs.HookList{
configs.NewFunctionHook(func(s *specs.State) error {
if s.Bundle != expectedBundle {
t.Fatalf("Expected poststop hook bundlePath '%s'; got '%s'", expectedBundle, s.Bundle)
@ -1160,7 +1188,13 @@ func TestHook(t *testing.T) {
if err != nil {
return err
}
return os.RemoveAll(filepath.Join(root, "test"))
for _, hook := range hookFiles {
if err = os.RemoveAll(filepath.Join(root, hook)); err != nil {
return err
}
}
return nil
}),
},
}
@ -1173,10 +1207,16 @@ func TestHook(t *testing.T) {
container, err := newContainerWithName("test", config)
ok(t, err)
// e.g: 'ls /prestart ...'
cmd := "ls "
for _, hook := range hookFiles {
cmd += "/" + hook + " "
}
var stdout bytes.Buffer
pconfig := libcontainer.Process{
Cwd: "/",
Args: []string{"sh", "-c", "ls /test"},
Args: []string{"sh", "-c", cmd},
Env: standardEnvironment,
Stdin: nil,
Stdout: &stdout,
@ -1188,30 +1228,15 @@ func TestHook(t *testing.T) {
// 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)
}
// Check that the file is written by the poststart hook
testFilePath := filepath.Join(rootfs, "test")
contents, err := ioutil.ReadFile(testFilePath)
if err != nil {
t.Fatalf("cannot read file '%s': %s", testFilePath, err)
}
if string(contents) != "hello world" {
t.Fatalf("Expected test file to contain 'hello world'; got '%s'", string(contents))
}
if err := container.Destroy(); err != nil {
t.Fatalf("container destroy %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())
for _, hook := range []string{"prestart", "createRuntime", "poststart"} {
fi, err := os.Stat(filepath.Join(rootfs, hook))
if err == nil || !os.IsNotExist(err) {
t.Fatalf("expected file '%s to not exists, but it does", fi.Name())
}
}
}

View File

@ -42,6 +42,33 @@ func TestCreateHooks(t *testing.T) {
Args: []string{"--some", "thing"},
},
},
CreateRuntime: []specs.Hook{
{
Path: "/some/hook/path",
},
{
Path: "/some/hook2/path",
Args: []string{"--some", "thing"},
},
},
CreateContainer: []specs.Hook{
{
Path: "/some/hook/path",
},
{
Path: "/some/hook2/path",
Args: []string{"--some", "thing"},
},
},
StartContainer: []specs.Hook{
{
Path: "/some/hook/path",
},
{
Path: "/some/hook2/path",
Args: []string{"--some", "thing"},
},
},
Poststart: []specs.Hook{
{
Path: "/some/hook/path",
@ -77,19 +104,37 @@ func TestCreateHooks(t *testing.T) {
conf := &configs.Config{}
createHooks(rspec, conf)
prestart := conf.Hooks.Prestart
prestart := conf.Hooks[configs.Prestart]
if len(prestart) != 2 {
t.Error("Expected 2 Prestart hooks")
}
poststart := conf.Hooks.Poststart
createRuntime := conf.Hooks[configs.CreateRuntime]
if len(createRuntime) != 2 {
t.Error("Expected 2 createRuntime hooks")
}
createContainer := conf.Hooks[configs.CreateContainer]
if len(createContainer) != 2 {
t.Error("Expected 2 createContainer hooks")
}
startContainer := conf.Hooks[configs.StartContainer]
if len(startContainer) != 2 {
t.Error("Expected 2 startContainer hooks")
}
poststart := conf.Hooks[configs.Poststart]
if len(poststart) != 3 {
t.Error("Expected 3 Poststart hooks")
}
poststop := conf.Hooks.Poststop
poststop := conf.Hooks[configs.Poststop]
if len(poststop) != 4 {
t.Error("Expected 4 Poststop hooks")