github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/pkg/katautils/utils_test.go (about) 1 // Copyright (c) 2018 Intel Corporation 2 // Copyright (c) 2018 HyperHQ Inc. 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 // 6 7 package katautils 8 9 import ( 10 "errors" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "path" 16 "path/filepath" 17 "strings" 18 "syscall" 19 "testing" 20 21 "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" 22 "github.com/stretchr/testify/assert" 23 ) 24 25 const ( 26 testDirMode = os.FileMode(0750) 27 testFileMode = os.FileMode(0640) 28 29 // small docker image used to create root filesystems from 30 testDockerImage = "busybox" 31 32 testSandboxID = "99999999-9999-9999-99999999999999999" 33 testContainerID = "1" 34 testBundle = "bundle" 35 specConfig = "config.json" 36 ) 37 38 var ( 39 testDir = "" 40 ) 41 42 func init() { 43 var err error 44 45 fmt.Printf("INFO: creating test directory\n") 46 testDir, err = ioutil.TempDir("", fmt.Sprintf("%s-", name)) 47 if err != nil { 48 panic(fmt.Sprintf("ERROR: failed to create test directory: %v", err)) 49 } 50 51 fmt.Printf("INFO: test directory is %v\n", testDir) 52 53 testBundleDir = filepath.Join(testDir, testBundle) 54 err = os.MkdirAll(testBundleDir, testDirMode) 55 if err != nil { 56 panic(fmt.Sprintf("ERROR: failed to create bundle directory %v: %v", testBundleDir, err)) 57 } 58 59 fmt.Printf("INFO: creating OCI bundle in %v for tests to use\n", testBundleDir) 60 err = realMakeOCIBundle(testBundleDir) 61 if err != nil { 62 panic(fmt.Sprintf("ERROR: failed to create OCI bundle: %v", err)) 63 } 64 } 65 66 // createOCIConfig creates an OCI configuration (spec) file in 67 // the bundle directory specified (which must exist). 68 func createOCIConfig(bundleDir string) error { 69 if bundleDir == "" { 70 return errors.New("BUG: Need bundle directory") 71 } 72 73 if !FileExists(bundleDir) { 74 return fmt.Errorf("BUG: Bundle directory %s does not exist", bundleDir) 75 } 76 77 var configCmd string 78 79 // Search for a suitable version of runc to use to generate 80 // the OCI config file. 81 for _, cmd := range []string{"docker-runc", "runc"} { 82 fullPath, err := exec.LookPath(cmd) 83 if err == nil { 84 configCmd = fullPath 85 break 86 } 87 } 88 89 if configCmd == "" { 90 return errors.New("Cannot find command to generate OCI config file") 91 } 92 93 _, err := RunCommand([]string{configCmd, "spec", "--bundle", bundleDir}) 94 if err != nil { 95 return err 96 } 97 98 specFile := filepath.Join(bundleDir, specConfig) 99 if !FileExists(specFile) { 100 return fmt.Errorf("generated OCI config file does not exist: %v", specFile) 101 } 102 103 return nil 104 } 105 106 // realMakeOCIBundle will create an OCI bundle (including the "config.json" 107 // config file) in the directory specified (which must already exist). 108 // 109 // XXX: Note that tests should *NOT* call this function - they should 110 // XXX: instead call makeOCIBundle(). 111 func realMakeOCIBundle(bundleDir string) error { 112 if bundleDir == "" { 113 return errors.New("BUG: Need bundle directory") 114 } 115 116 if !FileExists(bundleDir) { 117 return fmt.Errorf("BUG: Bundle directory %v does not exist", bundleDir) 118 } 119 120 err := createOCIConfig(bundleDir) 121 if err != nil { 122 return err 123 } 124 125 // Note the unusual parameter (a directory, not the config 126 // file to parse!) 127 spec, err := compatoci.ParseConfigJSON(bundleDir) 128 if err != nil { 129 return err 130 } 131 132 // Determine the rootfs directory name the OCI config refers to 133 ociRootPath := spec.Root.Path 134 135 rootfsDir := filepath.Join(bundleDir, ociRootPath) 136 137 if strings.HasPrefix(ociRootPath, "/") { 138 return fmt.Errorf("Cannot handle absolute rootfs as bundle must be unique to each test") 139 } 140 141 err = createRootfs(rootfsDir) 142 if err != nil { 143 return err 144 } 145 146 return nil 147 } 148 149 // createRootfs creates a minimal root filesystem below the specified 150 // directory. 151 func createRootfs(dir string) error { 152 var ( 153 output string 154 err error 155 ) 156 157 ctrEngine := CtrEngine{} 158 for _, name := range DockerLikeCtrEngines { 159 fmt.Printf("INFO: checking for container engine: %s\n", name) 160 161 output, err = ctrEngine.Init(name) 162 if err == nil { 163 break 164 } 165 } 166 167 if ctrEngine.Name == "" { 168 panic(fmt.Sprintf("ERROR: Docker-like container engine not accessible to current user: %v (error %v)", 169 output, err)) 170 } 171 172 err = os.MkdirAll(dir, testDirMode) 173 if err != nil { 174 return err 175 } 176 177 container, err := ctrEngine.Create(testDockerImage) 178 if err != nil { 179 return err 180 } 181 182 err = ctrEngine.GetRootfs(container, dir) 183 if err != nil { 184 return err 185 } 186 187 // Clean up 188 _, err = ctrEngine.Rm(container) 189 if err != nil { 190 return err 191 } 192 193 return nil 194 } 195 196 func createFile(file, contents string) error { 197 return ioutil.WriteFile(file, []byte(contents), testFileMode) 198 } 199 200 func createEmptyFile(path string) (err error) { 201 return ioutil.WriteFile(path, []byte(""), testFileMode) 202 } 203 204 func TestUtilsResolvePathEmptyPath(t *testing.T) { 205 _, err := ResolvePath("") 206 assert.Error(t, err) 207 } 208 209 func TestUtilsResolvePathValidPath(t *testing.T) { 210 dir, err := ioutil.TempDir("", "") 211 if err != nil { 212 t.Fatal(err) 213 } 214 defer os.RemoveAll(dir) 215 216 target := path.Join(dir, "target") 217 linkDir := path.Join(dir, "a/b/c") 218 linkFile := path.Join(linkDir, "link") 219 220 err = createEmptyFile(target) 221 assert.NoError(t, err) 222 223 absolute, err := filepath.Abs(target) 224 assert.NoError(t, err) 225 226 resolvedTarget, err := filepath.EvalSymlinks(absolute) 227 assert.NoError(t, err) 228 229 err = os.MkdirAll(linkDir, testDirMode) 230 assert.NoError(t, err) 231 232 err = syscall.Symlink(target, linkFile) 233 assert.NoError(t, err) 234 235 resolvedLink, err := ResolvePath(linkFile) 236 assert.NoError(t, err) 237 238 assert.Equal(t, resolvedTarget, resolvedLink) 239 } 240 241 func TestUtilsResolvePathENOENT(t *testing.T) { 242 dir, err := ioutil.TempDir("", "") 243 if err != nil { 244 t.Fatal(err) 245 } 246 247 target := path.Join(dir, "target") 248 linkDir := path.Join(dir, "a/b/c") 249 linkFile := path.Join(linkDir, "link") 250 251 err = createEmptyFile(target) 252 assert.NoError(t, err) 253 254 err = os.MkdirAll(linkDir, testDirMode) 255 assert.NoError(t, err) 256 257 err = syscall.Symlink(target, linkFile) 258 assert.NoError(t, err) 259 260 cwd, err := os.Getwd() 261 assert.NoError(t, err) 262 defer os.Chdir(cwd) 263 264 err = os.Chdir(dir) 265 assert.NoError(t, err) 266 267 err = os.RemoveAll(dir) 268 assert.NoError(t, err) 269 270 _, err = ResolvePath(filepath.Base(linkFile)) 271 assert.Error(t, err) 272 } 273 274 func TestFileSize(t *testing.T) { 275 assert := assert.New(t) 276 277 dir, err := ioutil.TempDir(testDir, "") 278 if err != nil { 279 t.Fatal(err) 280 } 281 defer os.RemoveAll(dir) 282 283 file := filepath.Join(dir, "foo") 284 285 // ENOENT 286 _, err = fileSize(file) 287 assert.Error(err) 288 289 err = createEmptyFile(file) 290 assert.NoError(err) 291 292 // zero size 293 size, err := fileSize(file) 294 assert.NoError(err) 295 assert.Equal(size, int64(0)) 296 297 msg := "hello" 298 msgLen := len(msg) 299 300 err = WriteFile(file, msg, testFileMode) 301 assert.NoError(err) 302 303 size, err = fileSize(file) 304 assert.NoError(err) 305 assert.Equal(size, int64(msgLen)) 306 } 307 308 func TestWriteFileErrWriteFail(t *testing.T) { 309 assert := assert.New(t) 310 311 err := WriteFile("", "", 0000) 312 assert.Error(err) 313 } 314 315 func TestWriteFileErrNoPath(t *testing.T) { 316 assert := assert.New(t) 317 318 dir, err := ioutil.TempDir(testDir, "") 319 assert.NoError(err) 320 defer os.RemoveAll(dir) 321 322 // attempt to write a file over an existing directory 323 err = WriteFile(dir, "", 0000) 324 assert.Error(err) 325 } 326 327 func TestGetFileContents(t *testing.T) { 328 type testData struct { 329 contents string 330 } 331 332 data := []testData{ 333 {""}, 334 {" "}, 335 {"\n"}, 336 {"\n\n"}, 337 {"\n\n\n"}, 338 {"foo"}, 339 {"foo\nbar"}, 340 {"processor : 0\nvendor_id : GenuineIntel\n"}, 341 } 342 343 dir, err := ioutil.TempDir(testDir, "") 344 if err != nil { 345 t.Fatal(err) 346 } 347 defer os.RemoveAll(dir) 348 349 file := filepath.Join(dir, "foo") 350 351 // file doesn't exist 352 _, err = GetFileContents(file) 353 assert.Error(t, err) 354 355 for _, d := range data { 356 // create the file 357 err = ioutil.WriteFile(file, []byte(d.contents), testFileMode) 358 if err != nil { 359 t.Fatal(err) 360 } 361 defer os.Remove(file) 362 363 contents, err := GetFileContents(file) 364 assert.NoError(t, err) 365 assert.Equal(t, contents, d.contents) 366 } 367 }