gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/pkg/katautils/create_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 "context" 11 "encoding/json" 12 "errors" 13 "fmt" 14 "io/ioutil" 15 "os" 16 "path" 17 "path/filepath" 18 "strings" 19 "syscall" 20 "testing" 21 22 ktu "github.com/kata-containers/runtime/pkg/katatestutils" 23 vc "github.com/kata-containers/runtime/virtcontainers" 24 "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" 25 "github.com/kata-containers/runtime/virtcontainers/pkg/oci" 26 "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" 27 "github.com/opencontainers/runtime-spec/specs-go" 28 "github.com/stretchr/testify/assert" 29 ) 30 31 const ( 32 testConsole = "/dev/pts/999" 33 testContainerTypeAnnotation = "io.kubernetes.cri-o.ContainerType" 34 testSandboxIDAnnotation = "io.kubernetes.cri-o.SandboxID" 35 testContainerTypeContainer = "container" 36 ) 37 38 var ( 39 testBundleDir = "" 40 41 // testingImpl is a concrete mock RVC implementation used for testing 42 testingImpl = &vcmock.VCMock{} 43 44 tc ktu.TestConstraint 45 ) 46 47 func init() { 48 tc = ktu.NewTestConstraint(false) 49 } 50 51 func writeOCIConfigFile(spec specs.Spec, configPath string) error { 52 if configPath == "" { 53 return errors.New("BUG: need config file path") 54 } 55 56 bytes, err := json.MarshalIndent(spec, "", "\t") 57 if err != nil { 58 return err 59 } 60 61 return ioutil.WriteFile(configPath, bytes, testFileMode) 62 } 63 64 // Create an OCI bundle in the specified directory. 65 // 66 // Note that the directory will be created, but it's parent is expected to exist. 67 // 68 // This function works by copying the already-created test bundle. Ideally, 69 // the bundle would be recreated for each test, but createRootfs() uses 70 // docker which on some systems is too slow, resulting in the tests timing 71 // out. 72 func makeOCIBundle(bundleDir string) error { 73 from := testBundleDir 74 to := bundleDir 75 76 // only the basename of bundleDir needs to exist as bundleDir 77 // will get created by cp(1). 78 base := filepath.Dir(bundleDir) 79 80 for _, dir := range []string{from, base} { 81 if !FileExists(dir) { 82 return fmt.Errorf("BUG: directory %v should exist", dir) 83 } 84 } 85 86 output, err := RunCommandFull([]string{"cp", "-a", from, to}, true) 87 if err != nil { 88 return fmt.Errorf("failed to copy test OCI bundle from %v to %v: %v (output: %v)", from, to, err, output) 89 } 90 91 return nil 92 } 93 94 // newTestRuntimeConfig creates a new RuntimeConfig 95 func newTestRuntimeConfig(dir, consolePath string, create bool) (oci.RuntimeConfig, error) { 96 if dir == "" { 97 return oci.RuntimeConfig{}, errors.New("BUG: need directory") 98 } 99 100 hypervisorConfig, err := newTestHypervisorConfig(dir, create) 101 if err != nil { 102 return oci.RuntimeConfig{}, err 103 } 104 105 return oci.RuntimeConfig{ 106 HypervisorType: vc.QemuHypervisor, 107 HypervisorConfig: hypervisorConfig, 108 AgentType: vc.KataContainersAgent, 109 ProxyType: vc.KataProxyType, 110 ShimType: vc.KataShimType, 111 Console: consolePath, 112 }, nil 113 } 114 115 // newTestHypervisorConfig creaets a new virtcontainers 116 // HypervisorConfig, ensuring that the required resources are also 117 // created. 118 // 119 // Note: no parameter validation in case caller wishes to create an invalid 120 // object. 121 func newTestHypervisorConfig(dir string, create bool) (vc.HypervisorConfig, error) { 122 kernelPath := path.Join(dir, "kernel") 123 imagePath := path.Join(dir, "image") 124 hypervisorPath := path.Join(dir, "hypervisor") 125 126 if create { 127 for _, file := range []string{kernelPath, imagePath, hypervisorPath} { 128 err := createEmptyFile(file) 129 if err != nil { 130 return vc.HypervisorConfig{}, err 131 } 132 } 133 } 134 135 return vc.HypervisorConfig{ 136 KernelPath: kernelPath, 137 ImagePath: imagePath, 138 HypervisorPath: hypervisorPath, 139 HypervisorMachineType: "pc-lite", 140 }, nil 141 } 142 143 // return the value of the *last* param with the specified key 144 func findLastParam(key string, params []vc.Param) (string, error) { 145 if key == "" { 146 return "", errors.New("ERROR: need non-nil key") 147 } 148 149 l := len(params) 150 if l == 0 { 151 return "", errors.New("ERROR: no params") 152 } 153 154 for i := l - 1; i >= 0; i-- { 155 p := params[i] 156 157 if key == p.Key { 158 return p.Value, nil 159 } 160 } 161 162 return "", fmt.Errorf("no param called %q found", name) 163 } 164 165 func TestSetEphemeralStorageType(t *testing.T) { 166 if tc.NotValid(ktu.NeedRoot()) { 167 t.Skip(ktu.TestDisabledNeedRoot) 168 } 169 170 assert := assert.New(t) 171 172 dir, err := ioutil.TempDir(testDir, "foo") 173 if err != nil { 174 t.Fatal(err) 175 } 176 defer os.RemoveAll(dir) 177 178 ephePath := filepath.Join(dir, vc.K8sEmptyDir, "tmp-volume") 179 err = os.MkdirAll(ephePath, testDirMode) 180 assert.Nil(err) 181 182 err = syscall.Mount("tmpfs", ephePath, "tmpfs", 0, "") 183 assert.Nil(err) 184 defer syscall.Unmount(ephePath, 0) 185 186 ociSpec := specs.Spec{} 187 var ociMounts []specs.Mount 188 mount := specs.Mount{ 189 Source: ephePath, 190 } 191 192 ociMounts = append(ociMounts, mount) 193 ociSpec.Mounts = ociMounts 194 ociSpec = SetEphemeralStorageType(ociSpec) 195 196 mountType := ociSpec.Mounts[0].Type 197 assert.Equal(mountType, "ephemeral", 198 "Unexpected mount type, got %s expected ephemeral", mountType) 199 } 200 201 func TestSetKernelParams(t *testing.T) { 202 assert := assert.New(t) 203 204 config := oci.RuntimeConfig{} 205 206 assert.Empty(config.HypervisorConfig.KernelParams) 207 208 err := SetKernelParams(&config) 209 assert.NoError(err) 210 211 config.HypervisorConfig.BlockDeviceDriver = "virtio-scsi" 212 err = SetKernelParams(&config) 213 assert.NoError(err) 214 215 if needSystemd(config.HypervisorConfig) { 216 assert.NotEmpty(config.HypervisorConfig.KernelParams) 217 } 218 } 219 220 func TestSetKernelParamsUserOptionTakesPriority(t *testing.T) { 221 assert := assert.New(t) 222 223 initName := "init" 224 initValue := "/sbin/myinit" 225 226 ipName := "ip" 227 ipValue := "127.0.0.1" 228 229 params := []vc.Param{ 230 {Key: initName, Value: initValue}, 231 {Key: ipName, Value: ipValue}, 232 } 233 234 hypervisorConfig := vc.HypervisorConfig{ 235 KernelParams: params, 236 } 237 238 // Config containing user-specified kernel parameters 239 config := oci.RuntimeConfig{ 240 HypervisorConfig: hypervisorConfig, 241 } 242 243 assert.NotEmpty(config.HypervisorConfig.KernelParams) 244 245 err := SetKernelParams(&config) 246 assert.NoError(err) 247 248 kernelParams := config.HypervisorConfig.KernelParams 249 250 init, err := findLastParam(initName, kernelParams) 251 assert.NoError(err) 252 assert.Equal(initValue, init) 253 254 ip, err := findLastParam(ipName, kernelParams) 255 assert.NoError(err) 256 assert.Equal(ipValue, ip) 257 258 } 259 260 func TestCreateSandboxConfigFail(t *testing.T) { 261 assert := assert.New(t) 262 263 path, err := ioutil.TempDir("", "containers-mapping") 264 assert.NoError(err) 265 defer os.RemoveAll(path) 266 ctrsMapTreePath = path 267 268 tmpdir, err := ioutil.TempDir("", "") 269 assert.NoError(err) 270 defer os.RemoveAll(tmpdir) 271 272 runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) 273 assert.NoError(err) 274 275 bundlePath := filepath.Join(tmpdir, "bundle") 276 277 err = makeOCIBundle(bundlePath) 278 assert.NoError(err) 279 280 ociConfigFile := filepath.Join(bundlePath, "config.json") 281 assert.True(FileExists(ociConfigFile)) 282 283 spec, err := compatoci.ParseConfigJSON(bundlePath) 284 assert.NoError(err) 285 286 quota := int64(0) 287 limit := int64(0) 288 289 spec.Linux.Resources.Memory = &specs.LinuxMemory{ 290 Limit: &limit, 291 } 292 293 spec.Linux.Resources.CPU = &specs.LinuxCPU{ 294 // specify an invalid value 295 Quota: "a, 296 } 297 298 rootFs := vc.RootFs{Mounted: true} 299 300 _, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true, false) 301 assert.Error(err) 302 } 303 304 func TestCreateSandboxFail(t *testing.T) { 305 if tc.NotValid(ktu.NeedRoot()) { 306 t.Skip(ktu.TestDisabledNeedRoot) 307 } 308 309 assert := assert.New(t) 310 311 path, err := ioutil.TempDir("", "containers-mapping") 312 assert.NoError(err) 313 defer os.RemoveAll(path) 314 ctrsMapTreePath = path 315 316 tmpdir, err := ioutil.TempDir("", "") 317 assert.NoError(err) 318 defer os.RemoveAll(tmpdir) 319 320 runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) 321 assert.NoError(err) 322 323 bundlePath := filepath.Join(tmpdir, "bundle") 324 325 err = makeOCIBundle(bundlePath) 326 assert.NoError(err) 327 328 ociConfigFile := filepath.Join(bundlePath, "config.json") 329 assert.True(FileExists(ociConfigFile)) 330 331 spec, err := compatoci.ParseConfigJSON(bundlePath) 332 assert.NoError(err) 333 334 rootFs := vc.RootFs{Mounted: true} 335 336 _, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true, false) 337 assert.Error(err) 338 assert.True(vcmock.IsMockError(err)) 339 } 340 341 func TestCheckForFips(t *testing.T) { 342 assert := assert.New(t) 343 344 path, err := ioutil.TempDir("", "") 345 assert.NoError(err) 346 defer os.RemoveAll(path) 347 348 val := procFIPS 349 procFIPS = filepath.Join(path, "fips-enabled") 350 defer func() { 351 procFIPS = val 352 }() 353 354 err = ioutil.WriteFile(procFIPS, []byte("1"), 0644) 355 assert.NoError(err) 356 357 hconfig := vc.HypervisorConfig{ 358 KernelParams: []vc.Param{ 359 {Key: "init", Value: "/sys/init"}, 360 }, 361 } 362 config := vc.SandboxConfig{ 363 HypervisorConfig: hconfig, 364 } 365 assert.NoError(checkForFIPS(&config)) 366 367 params := config.HypervisorConfig.KernelParams 368 assert.Equal(len(params), 2) 369 assert.Equal(params[1].Key, "fips") 370 assert.Equal(params[1].Value, "1") 371 372 config.HypervisorConfig = hconfig 373 err = ioutil.WriteFile(procFIPS, []byte("unexpected contents"), 0644) 374 assert.NoError(err) 375 assert.NoError(checkForFIPS(&config)) 376 assert.Equal(config.HypervisorConfig, hconfig) 377 378 assert.NoError(os.Remove(procFIPS)) 379 assert.NoError(checkForFIPS(&config)) 380 assert.Equal(config.HypervisorConfig, hconfig) 381 } 382 383 func TestCreateContainerContainerConfigFail(t *testing.T) { 384 assert := assert.New(t) 385 386 path, err := ioutil.TempDir("", "containers-mapping") 387 assert.NoError(err) 388 defer os.RemoveAll(path) 389 ctrsMapTreePath = path 390 391 tmpdir, err := ioutil.TempDir("", "") 392 assert.NoError(err) 393 defer os.RemoveAll(tmpdir) 394 395 bundlePath := filepath.Join(tmpdir, "bundle") 396 397 err = makeOCIBundle(bundlePath) 398 assert.NoError(err) 399 400 ociConfigFile := filepath.Join(bundlePath, "config.json") 401 assert.True(FileExists(ociConfigFile)) 402 403 spec, err := compatoci.ParseConfigJSON(bundlePath) 404 assert.NoError(err) 405 406 // Set invalid container type 407 containerType := "你好,世界" 408 spec.Annotations = make(map[string]string) 409 spec.Annotations[testContainerTypeAnnotation] = containerType 410 411 // rewrite file 412 err = writeOCIConfigFile(spec, ociConfigFile) 413 assert.NoError(err) 414 415 rootFs := vc.RootFs{Mounted: true} 416 417 for _, disableOutput := range []bool{true, false} { 418 _, err = CreateContainer(context.Background(), testingImpl, nil, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false) 419 assert.Error(err) 420 assert.False(vcmock.IsMockError(err)) 421 assert.True(strings.Contains(err.Error(), containerType)) 422 os.RemoveAll(path) 423 } 424 } 425 426 func TestCreateContainerFail(t *testing.T) { 427 assert := assert.New(t) 428 429 path, err := ioutil.TempDir("", "containers-mapping") 430 assert.NoError(err) 431 defer os.RemoveAll(path) 432 ctrsMapTreePath = path 433 434 tmpdir, err := ioutil.TempDir("", "") 435 assert.NoError(err) 436 defer os.RemoveAll(tmpdir) 437 438 bundlePath := filepath.Join(tmpdir, "bundle") 439 440 err = makeOCIBundle(bundlePath) 441 assert.NoError(err) 442 443 ociConfigFile := filepath.Join(bundlePath, "config.json") 444 assert.True(FileExists(ociConfigFile)) 445 446 spec, err := compatoci.ParseConfigJSON(bundlePath) 447 assert.NoError(err) 448 449 // set expected container type and sandboxID 450 spec.Annotations = make(map[string]string) 451 spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer 452 spec.Annotations[testSandboxIDAnnotation] = testSandboxID 453 454 // rewrite file 455 err = writeOCIConfigFile(spec, ociConfigFile) 456 assert.NoError(err) 457 458 rootFs := vc.RootFs{Mounted: true} 459 460 for _, disableOutput := range []bool{true, false} { 461 _, err = CreateContainer(context.Background(), testingImpl, nil, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false) 462 assert.Error(err) 463 assert.True(vcmock.IsMockError(err)) 464 os.RemoveAll(path) 465 } 466 } 467 468 func TestCreateContainer(t *testing.T) { 469 assert := assert.New(t) 470 471 path, err := ioutil.TempDir("", "containers-mapping") 472 assert.NoError(err) 473 defer os.RemoveAll(path) 474 ctrsMapTreePath = path 475 476 testingImpl.CreateContainerFunc = func(ctx context.Context, sandboxID string, containerConfig vc.ContainerConfig) (vc.VCSandbox, vc.VCContainer, error) { 477 return &vcmock.Sandbox{}, &vcmock.Container{}, nil 478 } 479 480 defer func() { 481 testingImpl.CreateContainerFunc = nil 482 }() 483 484 tmpdir, err := ioutil.TempDir("", "") 485 assert.NoError(err) 486 defer os.RemoveAll(tmpdir) 487 488 bundlePath := filepath.Join(tmpdir, "bundle") 489 490 err = makeOCIBundle(bundlePath) 491 assert.NoError(err) 492 493 ociConfigFile := filepath.Join(bundlePath, "config.json") 494 assert.True(FileExists(ociConfigFile)) 495 496 spec, err := compatoci.ParseConfigJSON(bundlePath) 497 assert.NoError(err) 498 499 // set expected container type and sandboxID 500 spec.Annotations = make(map[string]string) 501 spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer 502 spec.Annotations[testSandboxIDAnnotation] = testSandboxID 503 504 // rewrite file 505 err = writeOCIConfigFile(spec, ociConfigFile) 506 assert.NoError(err) 507 508 rootFs := vc.RootFs{Mounted: true} 509 510 for _, disableOutput := range []bool{true, false} { 511 _, err = CreateContainer(context.Background(), testingImpl, nil, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false) 512 assert.NoError(err) 513 os.RemoveAll(path) 514 } 515 }