1 package configs_test
2
3 import (
4 "encoding/json"
5 "fmt"
6 "os"
7 "reflect"
8 "testing"
9 "time"
10
11 "github.com/opencontainers/runc/libcontainer/configs"
12 "github.com/opencontainers/runtime-spec/specs-go"
13 )
14
15 func TestUnmarshalHooks(t *testing.T) {
16 timeout := time.Second
17
18 hookCmd := configs.NewCommandHook(configs.Command{
19 Path: "/var/vcap/hooks/hook",
20 Args: []string{"--pid=123"},
21 Env: []string{"FOO=BAR"},
22 Dir: "/var/vcap",
23 Timeout: &timeout,
24 })
25
26 hookJson, err := json.Marshal(hookCmd)
27 if err != nil {
28 t.Fatal(err)
29 }
30
31 for _, hookName := range configs.HookNameList {
32 hooks := configs.Hooks{}
33 err = hooks.UnmarshalJSON([]byte(fmt.Sprintf(`{"%s" :[%s]}`, hookName, hookJson)))
34 if err != nil {
35 t.Fatal(err)
36 }
37
38 if !reflect.DeepEqual(hooks[hookName], configs.HookList{hookCmd}) {
39 t.Errorf("Expected %s to equal %+v but it was %+v", hookName, hookCmd, hooks[hookName])
40 }
41 }
42 }
43
44 func TestUnmarshalHooksWithInvalidData(t *testing.T) {
45 hook := configs.Hooks{}
46 err := hook.UnmarshalJSON([]byte(`{invalid-json}`))
47 if err == nil {
48 t.Error("Expected error to occur but it was nil")
49 }
50 }
51
52 func TestMarshalHooks(t *testing.T) {
53 timeout := time.Second
54
55 hookCmd := configs.NewCommandHook(configs.Command{
56 Path: "/var/vcap/hooks/hook",
57 Args: []string{"--pid=123"},
58 Env: []string{"FOO=BAR"},
59 Dir: "/var/vcap",
60 Timeout: &timeout,
61 })
62
63 hook := configs.Hooks{
64 configs.Prestart: configs.HookList{hookCmd},
65 configs.CreateRuntime: configs.HookList{hookCmd},
66 configs.CreateContainer: configs.HookList{hookCmd},
67 configs.StartContainer: configs.HookList{hookCmd},
68 configs.Poststart: configs.HookList{hookCmd},
69 configs.Poststop: configs.HookList{hookCmd},
70 }
71 hooks, err := hook.MarshalJSON()
72 if err != nil {
73 t.Fatal(err)
74 }
75
76
77 hookCmdJson := `[{"path":"/var/vcap/hooks/hook","args":["--pid=123"],"env":["FOO=BAR"],"dir":"/var/vcap","timeout":1000000000}]`
78 h := fmt.Sprintf(`{"createContainer":%[1]s,"createRuntime":%[1]s,"poststart":%[1]s,"poststop":%[1]s,"prestart":%[1]s,"startContainer":%[1]s}`, hookCmdJson)
79 if string(hooks) != h {
80 t.Errorf("Expected hooks %s to equal %s", string(hooks), h)
81 }
82 }
83
84 func TestMarshalUnmarshalHooks(t *testing.T) {
85 timeout := time.Second
86
87 hookCmd := configs.NewCommandHook(configs.Command{
88 Path: "/var/vcap/hooks/hook",
89 Args: []string{"--pid=123"},
90 Env: []string{"FOO=BAR"},
91 Dir: "/var/vcap",
92 Timeout: &timeout,
93 })
94
95 hook := configs.Hooks{
96 configs.Prestart: configs.HookList{hookCmd},
97 configs.CreateRuntime: configs.HookList{hookCmd},
98 configs.CreateContainer: configs.HookList{hookCmd},
99 configs.StartContainer: configs.HookList{hookCmd},
100 configs.Poststart: configs.HookList{hookCmd},
101 configs.Poststop: configs.HookList{hookCmd},
102 }
103 hooks, err := hook.MarshalJSON()
104 if err != nil {
105 t.Fatal(err)
106 }
107
108 umMhook := configs.Hooks{}
109 err = umMhook.UnmarshalJSON(hooks)
110 if err != nil {
111 t.Fatal(err)
112 }
113 if !reflect.DeepEqual(umMhook, hook) {
114 t.Errorf("Expected hooks to be equal after mashaling -> unmarshaling them: %+v, %+v", umMhook, hook)
115 }
116 }
117
118 func TestMarshalHooksWithUnexpectedType(t *testing.T) {
119 fHook := configs.NewFunctionHook(func(*specs.State) error {
120 return nil
121 })
122 hook := configs.Hooks{
123 configs.CreateRuntime: configs.HookList{fHook},
124 }
125 hooks, err := hook.MarshalJSON()
126 if err != nil {
127 t.Fatal(err)
128 }
129
130 h := `{"createContainer":null,"createRuntime":null,"poststart":null,"poststop":null,"prestart":null,"startContainer":null}`
131 if string(hooks) != h {
132 t.Errorf("Expected hooks %s to equal %s", string(hooks), h)
133 }
134 }
135
136 func TestFuncHookRun(t *testing.T) {
137 state := &specs.State{
138 Version: "1",
139 ID: "1",
140 Status: "created",
141 Pid: 1,
142 Bundle: "/bundle",
143 }
144
145 fHook := configs.NewFunctionHook(func(s *specs.State) error {
146 if !reflect.DeepEqual(state, s) {
147 return fmt.Errorf("expected state %+v to equal %+v", state, s)
148 }
149 return nil
150 })
151
152 err := fHook.Run(state)
153 if err != nil {
154 t.Fatal(err)
155 }
156 }
157
158 func TestCommandHookRun(t *testing.T) {
159 state := &specs.State{
160 Version: "1",
161 ID: "1",
162 Status: "created",
163 Pid: 1,
164 Bundle: "/bundle",
165 }
166
167 stateJson, err := json.Marshal(state)
168 if err != nil {
169 t.Fatal(err)
170 }
171
172 verifyCommandTemplate := `#!/bin/sh
173 if [ "$1" != "testarg" ]; then
174 echo "Bad value for $1. Expected 'testarg', found '$1'"
175 exit 1
176 fi
177 if [ -z "$FOO" ] || [ "$FOO" != BAR ]; then
178 echo "Bad value for FOO. Expected 'BAR', found '$FOO'"
179 exit 1
180 fi
181 expectedJson=%q
182 read JSON
183 if [ "$JSON" != "$expectedJson" ]; then
184 echo "Bad JSON received. Expected '$expectedJson', found '$JSON'"
185 exit 1
186 fi
187 exit 0
188 `
189 verifyCommand := fmt.Sprintf(verifyCommandTemplate, stateJson)
190 filename := "/tmp/runc-hooktest.sh"
191 os.Remove(filename)
192 if err := os.WriteFile(filename, []byte(verifyCommand), 0o700); err != nil {
193 t.Fatalf("Failed to create tmp file: %v", err)
194 }
195 defer os.Remove(filename)
196
197 cmdHook := configs.NewCommandHook(configs.Command{
198 Path: filename,
199 Args: []string{filename, "testarg"},
200 Env: []string{"FOO=BAR"},
201 Dir: "/",
202 })
203
204 if err := cmdHook.Run(state); err != nil {
205 t.Errorf(fmt.Sprintf("Expected error to not occur but it was %+v", err))
206 }
207 }
208
209 func TestCommandHookRunTimeout(t *testing.T) {
210 state := &specs.State{
211 Version: "1",
212 ID: "1",
213 Status: "created",
214 Pid: 1,
215 Bundle: "/bundle",
216 }
217 timeout := 100 * time.Millisecond
218
219 cmdHook := configs.NewCommandHook(configs.Command{
220 Path: "/bin/sleep",
221 Args: []string{"/bin/sleep", "1"},
222 Timeout: &timeout,
223 })
224
225 if err := cmdHook.Run(state); err == nil {
226 t.Error("Expected error to occur but it was nil")
227 }
228 }
229
View as plain text