github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/hooks/exec/runtimeconfigfilter_test.go (about) 1 package exec 2 3 import ( 4 "context" 5 "encoding/json" 6 "os" 7 "testing" 8 "time" 9 10 spec "github.com/opencontainers/runtime-spec/specs-go" 11 "github.com/pkg/errors" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 func pointerInt(value int) *int { 16 return &value 17 } 18 19 func pointerUInt32(value uint32) *uint32 { 20 return &value 21 } 22 23 func pointerFileMode(value os.FileMode) *os.FileMode { 24 return &value 25 } 26 27 func TestRuntimeConfigFilter(t *testing.T) { 28 unexpectedEndOfJSONInput := json.Unmarshal([]byte("{\n"), nil) //nolint 29 30 for _, tt := range []struct { 31 name string 32 contextTimeout time.Duration 33 hooks []spec.Hook 34 input *spec.Spec 35 expected *spec.Spec 36 expectedHookError string 37 expectedRunError error 38 }{ 39 { 40 name: "no-op", 41 hooks: []spec.Hook{ 42 { 43 Path: path, 44 Args: []string{"sh", "-c", "cat"}, 45 }, 46 }, 47 input: &spec.Spec{ 48 Version: "1.0.0", 49 Root: &spec.Root{ 50 Path: "rootfs", 51 }, 52 }, 53 expected: &spec.Spec{ 54 Version: "1.0.0", 55 Root: &spec.Root{ 56 Path: "rootfs", 57 }, 58 }, 59 }, 60 { 61 name: "device injection", 62 hooks: []spec.Hook{ 63 { 64 Path: path, 65 Args: []string{"sh", "-c", `sed 's|\("gid":0}\)|\1,{"path": "/dev/sda","type":"b","major":8,"minor":0,"fileMode":384,"uid":0,"gid":0}|'`}, 66 }, 67 }, 68 input: &spec.Spec{ 69 Version: "1.0.0", 70 Root: &spec.Root{ 71 Path: "rootfs", 72 }, 73 Linux: &spec.Linux{ 74 Devices: []spec.LinuxDevice{ 75 { 76 Path: "/dev/fuse", 77 Type: "c", 78 Major: 10, 79 Minor: 229, 80 FileMode: pointerFileMode(0600), 81 UID: pointerUInt32(0), 82 GID: pointerUInt32(0), 83 }, 84 }, 85 }, 86 }, 87 expected: &spec.Spec{ 88 Version: "1.0.0", 89 Root: &spec.Root{ 90 Path: "rootfs", 91 }, 92 Linux: &spec.Linux{ 93 Devices: []spec.LinuxDevice{ 94 { 95 Path: "/dev/fuse", 96 Type: "c", 97 Major: 10, 98 Minor: 229, 99 FileMode: pointerFileMode(0600), 100 UID: pointerUInt32(0), 101 GID: pointerUInt32(0), 102 }, 103 { 104 Path: "/dev/sda", 105 Type: "b", 106 Major: 8, 107 Minor: 0, 108 FileMode: pointerFileMode(0600), 109 UID: pointerUInt32(0), 110 GID: pointerUInt32(0), 111 }, 112 }, 113 }, 114 }, 115 }, 116 { 117 name: "chaining", 118 hooks: []spec.Hook{ 119 { 120 Path: path, 121 Args: []string{"sh", "-c", `sed 's|\("gid":0}\)|\1,{"path": "/dev/sda","type":"b","major":8,"minor":0,"fileMode":384,"uid":0,"gid":0}|'`}, 122 }, 123 { 124 Path: path, 125 Args: []string{"sh", "-c", `sed 's|/dev/sda|/dev/sdb|'`}, 126 }, 127 }, 128 input: &spec.Spec{ 129 Version: "1.0.0", 130 Root: &spec.Root{ 131 Path: "rootfs", 132 }, 133 Linux: &spec.Linux{ 134 Devices: []spec.LinuxDevice{ 135 { 136 Path: "/dev/fuse", 137 Type: "c", 138 Major: 10, 139 Minor: 229, 140 FileMode: pointerFileMode(0600), 141 UID: pointerUInt32(0), 142 GID: pointerUInt32(0), 143 }, 144 }, 145 }, 146 }, 147 expected: &spec.Spec{ 148 Version: "1.0.0", 149 Root: &spec.Root{ 150 Path: "rootfs", 151 }, 152 Linux: &spec.Linux{ 153 Devices: []spec.LinuxDevice{ 154 { 155 Path: "/dev/fuse", 156 Type: "c", 157 Major: 10, 158 Minor: 229, 159 FileMode: pointerFileMode(0600), 160 UID: pointerUInt32(0), 161 GID: pointerUInt32(0), 162 }, 163 { 164 Path: "/dev/sdb", 165 Type: "b", 166 Major: 8, 167 Minor: 0, 168 FileMode: pointerFileMode(0600), 169 UID: pointerUInt32(0), 170 GID: pointerUInt32(0), 171 }, 172 }, 173 }, 174 }, 175 }, 176 { 177 name: "context timeout", 178 contextTimeout: time.Duration(1) * time.Second, 179 hooks: []spec.Hook{ 180 { 181 Path: path, 182 Args: []string{"sh", "-c", "sleep 2"}, 183 }, 184 }, 185 input: &spec.Spec{ 186 Version: "1.0.0", 187 Root: &spec.Root{ 188 Path: "rootfs", 189 }, 190 }, 191 expected: &spec.Spec{ 192 Version: "1.0.0", 193 Root: &spec.Root{ 194 Path: "rootfs", 195 }, 196 }, 197 expectedHookError: "^executing \\[sh -c sleep 2]: signal: killed$", 198 expectedRunError: context.DeadlineExceeded, 199 }, 200 { 201 name: "hook timeout", 202 hooks: []spec.Hook{ 203 { 204 Path: path, 205 Args: []string{"sh", "-c", "sleep 2"}, 206 Timeout: pointerInt(1), 207 }, 208 }, 209 input: &spec.Spec{ 210 Version: "1.0.0", 211 Root: &spec.Root{ 212 Path: "rootfs", 213 }, 214 }, 215 expected: &spec.Spec{ 216 Version: "1.0.0", 217 Root: &spec.Root{ 218 Path: "rootfs", 219 }, 220 }, 221 expectedHookError: "^executing \\[sh -c sleep 2]: signal: killed$", 222 expectedRunError: context.DeadlineExceeded, 223 }, 224 { 225 name: "invalid JSON", 226 hooks: []spec.Hook{ 227 { 228 Path: path, 229 Args: []string{"sh", "-c", "echo '{'"}, 230 }, 231 }, 232 input: &spec.Spec{ 233 Version: "1.0.0", 234 Root: &spec.Root{ 235 Path: "rootfs", 236 }, 237 }, 238 expected: &spec.Spec{ 239 Version: "1.0.0", 240 Root: &spec.Root{ 241 Path: "rootfs", 242 }, 243 }, 244 expectedRunError: unexpectedEndOfJSONInput, 245 }, 246 } { 247 test := tt 248 t.Run(test.name, func(t *testing.T) { 249 ctx := context.Background() 250 if test.contextTimeout > 0 { 251 var cancel context.CancelFunc 252 ctx, cancel = context.WithTimeout(ctx, test.contextTimeout) 253 defer cancel() 254 } 255 hookErr, err := RuntimeConfigFilter(ctx, test.hooks, test.input, DefaultPostKillTimeout) 256 assert.Equal(t, test.expectedRunError, errors.Cause(err)) 257 if test.expectedHookError == "" { 258 if hookErr != nil { 259 t.Fatal(hookErr) 260 } 261 } else { 262 assert.Regexp(t, test.expectedHookError, hookErr.Error()) 263 } 264 assert.Equal(t, test.expected, test.input) 265 }) 266 } 267 }