github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/pkg/oci/utils.go (about) 1 // Copyright (c) 2017 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package oci 7 8 import ( 9 "context" 10 "errors" 11 "fmt" 12 "path/filepath" 13 "regexp" 14 goruntime "runtime" 15 "strconv" 16 "strings" 17 "syscall" 18 19 criContainerdAnnotations "github.com/containerd/cri-containerd/pkg/annotations" 20 crioAnnotations "github.com/cri-o/cri-o/pkg/annotations" 21 specs "github.com/opencontainers/runtime-spec/specs-go" 22 "github.com/sirupsen/logrus" 23 24 vc "github.com/kata-containers/runtime/virtcontainers" 25 "github.com/kata-containers/runtime/virtcontainers/device/config" 26 exp "github.com/kata-containers/runtime/virtcontainers/experimental" 27 vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" 28 dockershimAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations/dockershim" 29 "github.com/kata-containers/runtime/virtcontainers/types" 30 ) 31 32 type annotationContainerType struct { 33 annotation string 34 containerType vc.ContainerType 35 } 36 37 var ( 38 // ErrNoLinux is an error for missing Linux sections in the OCI configuration file. 39 ErrNoLinux = errors.New("missing Linux section") 40 41 // CRIContainerTypeKeyList lists all the CRI keys that could define 42 // the container type from annotations in the config.json. 43 CRIContainerTypeKeyList = []string{criContainerdAnnotations.ContainerType, crioAnnotations.ContainerType, dockershimAnnotations.ContainerTypeLabelKey} 44 45 // CRISandboxNameKeyList lists all the CRI keys that could define 46 // the sandbox ID (sandbox ID) from annotations in the config.json. 47 CRISandboxNameKeyList = []string{criContainerdAnnotations.SandboxID, crioAnnotations.SandboxID, dockershimAnnotations.SandboxIDLabelKey} 48 49 // CRIContainerTypeList lists all the maps from CRI ContainerTypes annotations 50 // to a virtcontainers ContainerType. 51 CRIContainerTypeList = []annotationContainerType{ 52 {crioAnnotations.ContainerTypeSandbox, vc.PodSandbox}, 53 {crioAnnotations.ContainerTypeContainer, vc.PodContainer}, 54 {criContainerdAnnotations.ContainerTypeSandbox, vc.PodSandbox}, 55 {criContainerdAnnotations.ContainerTypeContainer, vc.PodContainer}, 56 {dockershimAnnotations.ContainerTypeLabelSandbox, vc.PodSandbox}, 57 {dockershimAnnotations.ContainerTypeLabelContainer, vc.PodContainer}, 58 } 59 ) 60 61 const ( 62 // StateCreated represents a container that has been created and is 63 // ready to be run. 64 StateCreated = "created" 65 66 // StateRunning represents a container that's currently running. 67 StateRunning = "running" 68 69 // StateStopped represents a container that has been stopped. 70 StateStopped = "stopped" 71 72 // StatePaused represents a container that has been paused. 73 StatePaused = "paused" 74 ) 75 76 const KernelModulesSeparator = ";" 77 78 // FactoryConfig is a structure to set the VM factory configuration. 79 type FactoryConfig struct { 80 // Template enables VM templating support in VM factory. 81 Template bool 82 83 // TemplatePath specifies the path of template. 84 TemplatePath string 85 86 // VMCacheNumber specifies the the number of caches of VMCache. 87 VMCacheNumber uint 88 89 // VMCacheEndpoint specifies the endpoint of transport VM from the VM cache server to runtime. 90 VMCacheEndpoint string 91 } 92 93 // RuntimeConfig aggregates all runtime specific settings 94 type RuntimeConfig struct { 95 HypervisorType vc.HypervisorType 96 HypervisorConfig vc.HypervisorConfig 97 98 NetmonConfig vc.NetmonConfig 99 100 AgentType vc.AgentType 101 AgentConfig interface{} 102 103 ProxyType vc.ProxyType 104 ProxyConfig vc.ProxyConfig 105 106 ShimType vc.ShimType 107 ShimConfig interface{} 108 109 Console string 110 111 //Determines how the VM should be connected to the 112 //the container network interface 113 InterNetworkModel vc.NetInterworkingModel 114 FactoryConfig FactoryConfig 115 Debug bool 116 Trace bool 117 118 //Determines if seccomp should be applied inside guest 119 DisableGuestSeccomp bool 120 121 //Determines if create a netns for hypervisor process 122 DisableNewNetNs bool 123 124 //Determines kata processes are managed only in sandbox cgroup 125 SandboxCgroupOnly bool 126 127 //Determines if containers are allowed to join the pid namespace of the kata agent 128 EnableAgentPidNs bool 129 130 //Experimental features enabled 131 Experimental []exp.Feature 132 } 133 134 // AddKernelParam allows the addition of new kernel parameters to an existing 135 // hypervisor configuration stored inside the current runtime configuration. 136 func (config *RuntimeConfig) AddKernelParam(p vc.Param) error { 137 return config.HypervisorConfig.AddKernelParam(p) 138 } 139 140 var ociLog = logrus.WithFields(logrus.Fields{ 141 "source": "virtcontainers", 142 "subsystem": "oci", 143 }) 144 145 // SetLogger sets the logger for oci package. 146 func SetLogger(ctx context.Context, logger *logrus.Entry) { 147 fields := ociLog.Data 148 ociLog = logger.WithFields(fields) 149 } 150 151 func cmdEnvs(spec specs.Spec, envs []types.EnvVar) []types.EnvVar { 152 for _, env := range spec.Process.Env { 153 kv := strings.Split(env, "=") 154 if len(kv) < 2 { 155 continue 156 } 157 158 envs = append(envs, 159 types.EnvVar{ 160 Var: kv[0], 161 Value: kv[1], 162 }) 163 } 164 165 return envs 166 } 167 168 func newMount(m specs.Mount) vc.Mount { 169 readonly := false 170 for _, flag := range m.Options { 171 if flag == "ro" { 172 readonly = true 173 break 174 } 175 } 176 return vc.Mount{ 177 Source: m.Source, 178 Destination: m.Destination, 179 Type: m.Type, 180 Options: m.Options, 181 ReadOnly: readonly, 182 } 183 } 184 185 func containerMounts(spec specs.Spec) []vc.Mount { 186 ociMounts := spec.Mounts 187 188 if ociMounts == nil { 189 return []vc.Mount{} 190 } 191 192 var mnts []vc.Mount 193 for _, m := range ociMounts { 194 mnts = append(mnts, newMount(m)) 195 } 196 197 return mnts 198 } 199 200 func contains(strings []string, toFind string) bool { 201 for _, candidate := range strings { 202 if candidate == toFind { 203 return true 204 } 205 } 206 return false 207 } 208 209 func regexpContains(regexps []string, toMatch string) bool { 210 for _, candidate := range regexps { 211 if matched, _ := regexp.MatchString(candidate, toMatch); matched { 212 return true 213 } 214 } 215 return false 216 } 217 218 func checkPathIsInGlobs(globs []string, path string) bool { 219 for _, glob := range globs { 220 filenames, _ := filepath.Glob(glob) 221 for _, a := range filenames { 222 if path == a { 223 return true 224 } 225 } 226 } 227 return false 228 } 229 230 // Check if an annotation name either belongs to another prefix, matches regexp list 231 func checkAnnotationNameIsValid(list []string, name string, prefix string) bool { 232 if strings.HasPrefix(name, prefix) { 233 return regexpContains(list, strings.TrimPrefix(name, prefix)) 234 } 235 return true 236 } 237 238 func newLinuxDeviceInfo(d specs.LinuxDevice) (*config.DeviceInfo, error) { 239 allowedDeviceTypes := []string{"c", "b", "u", "p"} 240 241 if !contains(allowedDeviceTypes, d.Type) { 242 return nil, fmt.Errorf("Unexpected Device Type %s for device %s", d.Type, d.Path) 243 } 244 245 if d.Path == "" { 246 return nil, fmt.Errorf("Path cannot be empty for device") 247 } 248 249 deviceInfo := config.DeviceInfo{ 250 ContainerPath: d.Path, 251 DevType: d.Type, 252 Major: d.Major, 253 Minor: d.Minor, 254 } 255 if d.UID != nil { 256 deviceInfo.UID = *d.UID 257 } 258 259 if d.GID != nil { 260 deviceInfo.GID = *d.GID 261 } 262 263 if d.FileMode != nil { 264 deviceInfo.FileMode = *d.FileMode 265 } 266 267 return &deviceInfo, nil 268 } 269 270 func containerDeviceInfos(spec specs.Spec) ([]config.DeviceInfo, error) { 271 ociLinuxDevices := spec.Linux.Devices 272 273 if ociLinuxDevices == nil { 274 return []config.DeviceInfo{}, nil 275 } 276 277 var devices []config.DeviceInfo 278 for _, d := range ociLinuxDevices { 279 linuxDeviceInfo, err := newLinuxDeviceInfo(d) 280 if err != nil { 281 return []config.DeviceInfo{}, err 282 } 283 284 devices = append(devices, *linuxDeviceInfo) 285 } 286 287 return devices, nil 288 } 289 290 func networkConfig(ocispec specs.Spec, config RuntimeConfig) (vc.NetworkConfig, error) { 291 linux := ocispec.Linux 292 if linux == nil { 293 return vc.NetworkConfig{}, ErrNoLinux 294 } 295 296 var netConf vc.NetworkConfig 297 298 for _, n := range linux.Namespaces { 299 if n.Type != specs.NetworkNamespace { 300 continue 301 } 302 303 if n.Path != "" { 304 netConf.NetNSPath = n.Path 305 } 306 } 307 netConf.InterworkingModel = config.InterNetworkModel 308 netConf.DisableNewNetNs = config.DisableNewNetNs 309 310 netConf.NetmonConfig = vc.NetmonConfig{ 311 Path: config.NetmonConfig.Path, 312 Debug: config.NetmonConfig.Debug, 313 Enable: config.NetmonConfig.Enable, 314 } 315 316 return netConf, nil 317 } 318 319 // GetContainerType determines which type of container matches the annotations 320 // table provided. 321 func GetContainerType(annotations map[string]string) (vc.ContainerType, error) { 322 if containerType, ok := annotations[vcAnnotations.ContainerTypeKey]; ok { 323 return vc.ContainerType(containerType), nil 324 } 325 326 ociLog.Errorf("Annotations[%s] not found, cannot determine the container type", 327 vcAnnotations.ContainerTypeKey) 328 return vc.UnknownContainerType, fmt.Errorf("Could not find container type") 329 } 330 331 // ContainerType returns the type of container and if the container type was 332 // found from CRI servers annotations. 333 func ContainerType(spec specs.Spec) (vc.ContainerType, error) { 334 for _, key := range CRIContainerTypeKeyList { 335 containerTypeVal, ok := spec.Annotations[key] 336 if !ok { 337 continue 338 } 339 340 for _, t := range CRIContainerTypeList { 341 if t.annotation == containerTypeVal { 342 return t.containerType, nil 343 } 344 345 } 346 347 return vc.UnknownContainerType, fmt.Errorf("Unknown container type %s", containerTypeVal) 348 } 349 350 return vc.PodSandbox, nil 351 } 352 353 func GetSandboxConfigPath(annotations map[string]string) string { 354 return annotations[vcAnnotations.SandboxConfigPathKey] 355 } 356 357 // SandboxID determines the sandbox ID related to an OCI configuration. This function 358 // is expected to be called only when the container type is "PodContainer". 359 func SandboxID(spec specs.Spec) (string, error) { 360 for _, key := range CRISandboxNameKeyList { 361 sandboxID, ok := spec.Annotations[key] 362 if ok { 363 return sandboxID, nil 364 } 365 } 366 367 return "", fmt.Errorf("Could not find sandbox ID") 368 } 369 370 func addAnnotations(ocispec specs.Spec, config *vc.SandboxConfig, runtimeConfig RuntimeConfig) error { 371 for key := range ocispec.Annotations { 372 if !checkAnnotationNameIsValid(runtimeConfig.HypervisorConfig.EnableAnnotations, key, vcAnnotations.KataAnnotationHypervisorPrefix) { 373 return fmt.Errorf("annotation %v is not enabled", key) 374 } 375 } 376 err := addAssetAnnotations(ocispec, config) 377 if err != nil { 378 return err 379 } 380 if err := addHypervisorConfigOverrides(ocispec, config, runtimeConfig); err != nil { 381 return err 382 } 383 384 if err := addRuntimeConfigOverrides(ocispec, config, runtimeConfig); err != nil { 385 return err 386 } 387 388 if err := addAgentConfigOverrides(ocispec, config); err != nil { 389 return err 390 } 391 return nil 392 } 393 394 func addAssetAnnotations(ocispec specs.Spec, config *vc.SandboxConfig) error { 395 assetAnnotations, err := types.AssetAnnotations() 396 if err != nil { 397 return err 398 } 399 400 for _, a := range assetAnnotations { 401 value, ok := ocispec.Annotations[a] 402 if ok { 403 config.Annotations[a] = value 404 } 405 } 406 407 return nil 408 } 409 410 func addHypervisorConfigOverrides(ocispec specs.Spec, config *vc.SandboxConfig, runtimeConfig RuntimeConfig) error { 411 if err := addHypervisorCPUOverrides(ocispec, config); err != nil { 412 return err 413 } 414 415 if err := addHypervisorMemoryOverrides(ocispec, config, runtimeConfig); err != nil { 416 return err 417 } 418 419 if err := addHypervisorBlockOverrides(ocispec, config); err != nil { 420 return err 421 } 422 423 if err := addHypervisorVirtioFsOverrides(ocispec, config, runtimeConfig); err != nil { 424 return err 425 } 426 427 if err := addHypervisorPathOverrides(ocispec, config, runtimeConfig); err != nil { 428 return err 429 } 430 431 if value, ok := ocispec.Annotations[vcAnnotations.MachineType]; ok { 432 if value != "" { 433 config.HypervisorConfig.HypervisorMachineType = value 434 } 435 } 436 437 if value, ok := ocispec.Annotations[vcAnnotations.MachineAccelerators]; ok { 438 if value != "" { 439 config.HypervisorConfig.MachineAccelerators = value 440 } 441 } 442 443 if value, ok := ocispec.Annotations[vcAnnotations.CPUFeatures]; ok { 444 if value != "" { 445 config.HypervisorConfig.CPUFeatures = value 446 } 447 } 448 449 if value, ok := ocispec.Annotations[vcAnnotations.DisableVhostNet]; ok { 450 disableVhostNet, err := strconv.ParseBool(value) 451 if err != nil { 452 return fmt.Errorf("Error parsing annotation for disable_vhost_net: Please specify boolean value 'true|false'") 453 } 454 455 config.HypervisorConfig.DisableVhostNet = disableVhostNet 456 } 457 458 if value, ok := ocispec.Annotations[vcAnnotations.VhostUserStorePath]; ok { 459 if !checkPathIsInGlobs(runtimeConfig.HypervisorConfig.VhostUserStorePathList, value) { 460 return fmt.Errorf("vhost store path %v required from annotation is not valid", value) 461 } 462 config.HypervisorConfig.VhostUserStorePath = value 463 } 464 465 if value, ok := ocispec.Annotations[vcAnnotations.GuestHookPath]; ok { 466 if value != "" { 467 config.HypervisorConfig.GuestHookPath = value 468 } 469 } 470 471 if value, ok := ocispec.Annotations[vcAnnotations.UseVSock]; ok { 472 useVsock, err := strconv.ParseBool(value) 473 if err != nil { 474 return fmt.Errorf("Error parsing annotation for use_vsock: Please specify boolean value 'true|false'") 475 } 476 477 config.HypervisorConfig.UseVSock = useVsock 478 } 479 480 if value, ok := ocispec.Annotations[vcAnnotations.DisableImageNvdimm]; ok { 481 disableNvdimm, err := strconv.ParseBool(value) 482 if err != nil { 483 return fmt.Errorf("Error parsing annotation for use_nvdimm: Please specify boolean value 'true|false'") 484 } 485 486 config.HypervisorConfig.DisableImageNvdimm = disableNvdimm 487 } 488 489 if value, ok := ocispec.Annotations[vcAnnotations.HotplugVFIOOnRootBus]; ok { 490 hotplugVFIOOnRootBus, err := strconv.ParseBool(value) 491 if err != nil { 492 return fmt.Errorf("Error parsing annotation for hotplug_vfio_on_root_bus: Please specify boolean value 'true|false'") 493 } 494 495 config.HypervisorConfig.HotplugVFIOOnRootBus = hotplugVFIOOnRootBus 496 } 497 498 if value, ok := ocispec.Annotations[vcAnnotations.PCIeRootPort]; ok { 499 pcieRootPort, err := strconv.ParseUint(value, 10, 32) 500 if err != nil { 501 return fmt.Errorf("Error parsing annotation for pcie_root_port: %v, Please specify an integer greater than or equal to 0", err) 502 } 503 config.HypervisorConfig.PCIeRootPort = uint32(pcieRootPort) 504 } 505 506 if value, ok := ocispec.Annotations[vcAnnotations.EntropySource]; ok { 507 if value != "" { 508 config.HypervisorConfig.EntropySource = value 509 } 510 } 511 512 return nil 513 } 514 515 func addHypervisorPathOverrides(ocispec specs.Spec, config *vc.SandboxConfig, runtimeConfig RuntimeConfig) error { 516 if value, ok := ocispec.Annotations[vcAnnotations.HypervisorPath]; ok { 517 if !checkPathIsInGlobs(runtimeConfig.HypervisorConfig.HypervisorPathList, value) { 518 return fmt.Errorf("hypervisor %v required from annotation is not valid", value) 519 } 520 config.HypervisorConfig.HypervisorPath = value 521 } 522 523 if value, ok := ocispec.Annotations[vcAnnotations.JailerPath]; ok { 524 if !checkPathIsInGlobs(runtimeConfig.HypervisorConfig.JailerPathList, value) { 525 return fmt.Errorf("jailer %v required from annotation is not valid", value) 526 } 527 config.HypervisorConfig.JailerPath = value 528 } 529 530 if value, ok := ocispec.Annotations[vcAnnotations.CtlPath]; ok { 531 if !checkPathIsInGlobs(runtimeConfig.HypervisorConfig.HypervisorCtlPathList, value) { 532 return fmt.Errorf("hypervisor control %v required from annotation is not valid", value) 533 } 534 config.HypervisorConfig.HypervisorCtlPath = value 535 } 536 537 if value, ok := ocispec.Annotations[vcAnnotations.KernelParams]; ok { 538 if value != "" { 539 params := vc.DeserializeParams(strings.Fields(value)) 540 for _, param := range params { 541 if err := config.HypervisorConfig.AddKernelParam(param); err != nil { 542 return fmt.Errorf("Error adding kernel parameters in annotation kernel_params : %v", err) 543 } 544 } 545 } 546 } 547 return nil 548 } 549 550 func addHypervisorMemoryOverrides(ocispec specs.Spec, sbConfig *vc.SandboxConfig, runtimeConfig RuntimeConfig) error { 551 if value, ok := ocispec.Annotations[vcAnnotations.DefaultMemory]; ok { 552 memorySz, err := strconv.ParseUint(value, 10, 32) 553 if err != nil { 554 return fmt.Errorf("Error encountered parsing annotation for default_memory: %v", err) 555 } 556 557 sbConfig.HypervisorConfig.MemorySize = uint32(memorySz) 558 } 559 560 if value, ok := ocispec.Annotations[vcAnnotations.MemSlots]; ok { 561 mslots, err := strconv.ParseUint(value, 10, 32) 562 if err != nil { 563 return fmt.Errorf("Error parsing annotation for memory_slots: %v, please specify positive numeric value", err) 564 } 565 566 if mslots > 0 { 567 sbConfig.HypervisorConfig.MemSlots = uint32(mslots) 568 } 569 } 570 571 if value, ok := ocispec.Annotations[vcAnnotations.MemOffset]; ok { 572 moffset, err := strconv.ParseUint(value, 10, 32) 573 if err != nil { 574 return fmt.Errorf("Error parsing annotation for memory_offset: %v, please specify positive numeric value", err) 575 } 576 577 if moffset > 0 { 578 sbConfig.HypervisorConfig.MemOffset = uint32(moffset) 579 } 580 } 581 582 if value, ok := ocispec.Annotations[vcAnnotations.VirtioMem]; ok { 583 virtioMem, err := strconv.ParseBool(value) 584 if err != nil { 585 return fmt.Errorf("Error parsing annotation for enable_virtio_mem: Please specify boolean value 'true|false'") 586 } 587 588 sbConfig.HypervisorConfig.VirtioMem = virtioMem 589 } 590 591 if value, ok := ocispec.Annotations[vcAnnotations.MemPrealloc]; ok { 592 memPrealloc, err := strconv.ParseBool(value) 593 if err != nil { 594 return fmt.Errorf("Error parsing annotation for enable_mem_prealloc: Please specify boolean value 'true|false'") 595 } 596 597 sbConfig.HypervisorConfig.MemPrealloc = memPrealloc 598 } 599 600 if value, ok := ocispec.Annotations[vcAnnotations.EnableSwap]; ok { 601 enableSwap, err := strconv.ParseBool(value) 602 if err != nil { 603 return fmt.Errorf("Error parsing annotation for enable_swap: Please specify boolean value 'true|false'") 604 } 605 606 sbConfig.HypervisorConfig.Mlock = !enableSwap 607 } 608 609 if value, ok := ocispec.Annotations[vcAnnotations.FileBackedMemRootDir]; ok { 610 if !checkPathIsInGlobs(runtimeConfig.HypervisorConfig.FileBackedMemRootList, value) { 611 return fmt.Errorf("file_mem_backend value %v required from annotation is not valid", value) 612 } 613 sbConfig.HypervisorConfig.FileBackedMemRootDir = value 614 } 615 616 if value, ok := ocispec.Annotations[vcAnnotations.HugePages]; ok { 617 hugePages, err := strconv.ParseBool(value) 618 if err != nil { 619 return fmt.Errorf("Error parsing annotation for enable_hugepages: Please specify boolean value 'true|false'") 620 } 621 622 sbConfig.HypervisorConfig.HugePages = hugePages 623 } 624 625 if value, ok := ocispec.Annotations[vcAnnotations.IOMMU]; ok { 626 iommu, err := strconv.ParseBool(value) 627 if err != nil { 628 return fmt.Errorf("Error parsing annotation for iommu: Please specify boolean value 'true|false'") 629 } 630 631 sbConfig.HypervisorConfig.IOMMU = iommu 632 } 633 634 if value, ok := ocispec.Annotations[vcAnnotations.IOMMUPlatform]; ok { 635 deviceIOMMU, err := strconv.ParseBool(value) 636 if err != nil { 637 return fmt.Errorf("Error parsing annotation for enable_iommu_platform: Please specify boolean value 'true|false'") 638 } 639 640 sbConfig.HypervisorConfig.IOMMUPlatform = deviceIOMMU 641 } 642 return nil 643 } 644 645 func addHypervisorCPUOverrides(ocispec specs.Spec, sbConfig *vc.SandboxConfig) error { 646 if value, ok := ocispec.Annotations[vcAnnotations.DefaultVCPUs]; ok { 647 vcpus, err := strconv.ParseUint(value, 10, 32) 648 if err != nil { 649 return fmt.Errorf("Error encountered parsing annotation default_vcpus: %v, please specify numeric value", err) 650 } 651 652 numCPUs := goruntime.NumCPU() 653 654 if uint32(vcpus) > uint32(numCPUs) { 655 return fmt.Errorf("Number of cpus %d specified in annotation default_vcpus is greater than the number of CPUs %d on the system", vcpus, numCPUs) 656 } 657 658 sbConfig.HypervisorConfig.NumVCPUs = uint32(vcpus) 659 } 660 661 if value, ok := ocispec.Annotations[vcAnnotations.DefaultMaxVCPUs]; ok { 662 maxVCPUs, err := strconv.ParseUint(value, 10, 32) 663 if err != nil { 664 return fmt.Errorf("Error encountered parsing annotation for default_maxvcpus: %v, please specify positive numeric value", err) 665 } 666 667 numCPUs := goruntime.NumCPU() 668 max := uint32(maxVCPUs) 669 670 if max > uint32(numCPUs) { 671 return fmt.Errorf("Number of cpus %d in annotation default_maxvcpus is greater than the number of CPUs %d on the system", max, numCPUs) 672 } 673 674 if sbConfig.HypervisorType == vc.QemuHypervisor && max > vc.MaxQemuVCPUs() { 675 return fmt.Errorf("Number of cpus %d in annotation default_maxvcpus is greater than max no of CPUs %d supported for qemu", max, vc.MaxQemuVCPUs()) 676 } 677 678 sbConfig.HypervisorConfig.DefaultMaxVCPUs = max 679 } 680 681 return nil 682 } 683 684 func addHypervisorBlockOverrides(ocispec specs.Spec, sbConfig *vc.SandboxConfig) error { 685 if value, ok := ocispec.Annotations[vcAnnotations.BlockDeviceDriver]; ok { 686 supportedBlockDrivers := []string{config.VirtioSCSI, config.VirtioBlock, config.VirtioMmio, config.Nvdimm, config.VirtioBlockCCW} 687 688 valid := false 689 for _, b := range supportedBlockDrivers { 690 if b == value { 691 sbConfig.HypervisorConfig.BlockDeviceDriver = value 692 valid = true 693 } 694 } 695 696 if !valid { 697 return fmt.Errorf("Invalid hypervisor block storage driver %v specified in annotation (supported drivers: %v)", value, supportedBlockDrivers) 698 } 699 } 700 701 if value, ok := ocispec.Annotations[vcAnnotations.DisableBlockDeviceUse]; ok { 702 disableBlockDeviceUse, err := strconv.ParseBool(value) 703 if err != nil { 704 return fmt.Errorf("Error parsing annotation for disable_block_device_use: Please specify boolean value 'true|false'") 705 } 706 707 sbConfig.HypervisorConfig.DisableBlockDeviceUse = disableBlockDeviceUse 708 } 709 710 if value, ok := ocispec.Annotations[vcAnnotations.EnableIOThreads]; ok { 711 enableIOThreads, err := strconv.ParseBool(value) 712 if err != nil { 713 return fmt.Errorf("Error parsing annotation for enable_iothreads: Please specify boolean value 'true|false'") 714 } 715 716 sbConfig.HypervisorConfig.EnableIOThreads = enableIOThreads 717 } 718 719 if value, ok := ocispec.Annotations[vcAnnotations.BlockDeviceCacheSet]; ok { 720 blockDeviceCacheSet, err := strconv.ParseBool(value) 721 if err != nil { 722 return fmt.Errorf("Error parsing annotation for block_device_cache_set: Please specify boolean value 'true|false'") 723 } 724 725 sbConfig.HypervisorConfig.BlockDeviceCacheSet = blockDeviceCacheSet 726 } 727 728 if value, ok := ocispec.Annotations[vcAnnotations.BlockDeviceCacheDirect]; ok { 729 blockDeviceCacheDirect, err := strconv.ParseBool(value) 730 if err != nil { 731 return fmt.Errorf("Error parsing annotation for block_device_cache_direct: Please specify boolean value 'true|false'") 732 } 733 734 sbConfig.HypervisorConfig.BlockDeviceCacheDirect = blockDeviceCacheDirect 735 } 736 737 if value, ok := ocispec.Annotations[vcAnnotations.BlockDeviceCacheNoflush]; ok { 738 blockDeviceCacheNoflush, err := strconv.ParseBool(value) 739 if err != nil { 740 return fmt.Errorf("Error parsing annotation for block_device_cache_noflush: Please specify boolean value 'true|false'") 741 } 742 743 sbConfig.HypervisorConfig.BlockDeviceCacheNoflush = blockDeviceCacheNoflush 744 } 745 746 return nil 747 } 748 749 func addHypervisorVirtioFsOverrides(ocispec specs.Spec, sbConfig *vc.SandboxConfig, runtimeConfig RuntimeConfig) error { 750 if value, ok := ocispec.Annotations[vcAnnotations.SharedFS]; ok { 751 supportedSharedFS := []string{config.Virtio9P, config.VirtioFS} 752 valid := false 753 for _, fs := range supportedSharedFS { 754 if fs == value { 755 sbConfig.HypervisorConfig.SharedFS = value 756 valid = true 757 } 758 } 759 760 if !valid { 761 return fmt.Errorf("Invalid hypervisor shared file system %v specified for annotation shared_fs, (supported file systems: %v)", value, supportedSharedFS) 762 } 763 } 764 765 if value, ok := ocispec.Annotations[vcAnnotations.VirtioFSDaemon]; ok { 766 if !checkPathIsInGlobs(runtimeConfig.HypervisorConfig.VirtioFSDaemonList, value) { 767 return fmt.Errorf("virtiofs daemon %v required from annotation is not valid", value) 768 } 769 sbConfig.HypervisorConfig.VirtioFSDaemon = value 770 } 771 772 if sbConfig.HypervisorConfig.SharedFS == config.VirtioFS && sbConfig.HypervisorConfig.VirtioFSDaemon == "" { 773 return fmt.Errorf("cannot enable virtio-fs without daemon path") 774 } 775 776 if value, ok := ocispec.Annotations[vcAnnotations.VirtioFSCache]; ok { 777 sbConfig.HypervisorConfig.VirtioFSCache = value 778 } 779 780 if value, ok := ocispec.Annotations[vcAnnotations.VirtioFSCacheSize]; ok { 781 cacheSize, err := strconv.ParseUint(value, 10, 32) 782 if err != nil { 783 return fmt.Errorf("Error parsing annotation for virtio_fs_cache_size: %v, please specify positive numeric value", err) 784 } 785 786 sbConfig.HypervisorConfig.VirtioFSCacheSize = uint32(cacheSize) 787 } 788 789 if value, ok := ocispec.Annotations[vcAnnotations.Msize9p]; ok { 790 msize9p, err := strconv.ParseUint(value, 10, 32) 791 if err != nil || msize9p == 0 { 792 return fmt.Errorf("Error parsing annotation for msize_9p, please specify positive numeric value") 793 } 794 795 sbConfig.HypervisorConfig.Msize9p = uint32(msize9p) 796 } 797 798 return nil 799 } 800 801 func addRuntimeConfigOverrides(ocispec specs.Spec, sbConfig *vc.SandboxConfig, runtimeConfig RuntimeConfig) error { 802 if value, ok := ocispec.Annotations[vcAnnotations.DisableGuestSeccomp]; ok { 803 disableGuestSeccomp, err := strconv.ParseBool(value) 804 if err != nil { 805 return fmt.Errorf("Error parsing annotation for disable_guest_seccomp: Please specify boolean value 'true|false'") 806 } 807 808 sbConfig.DisableGuestSeccomp = disableGuestSeccomp 809 } 810 811 if value, ok := ocispec.Annotations[vcAnnotations.SandboxCgroupOnly]; ok { 812 sandboxCgroupOnly, err := strconv.ParseBool(value) 813 if err != nil { 814 return fmt.Errorf("Error parsing annotation for sandbox_cgroup_only: Please specify boolean value 'true|false'") 815 } 816 817 sbConfig.SandboxCgroupOnly = sandboxCgroupOnly 818 } 819 820 if value, ok := ocispec.Annotations[vcAnnotations.Experimental]; ok { 821 features := strings.Split(value, " ") 822 sbConfig.Experimental = []exp.Feature{} 823 824 for _, f := range features { 825 feature := exp.Get(f) 826 if feature == nil { 827 return fmt.Errorf("Unsupported experimental feature %s specified in annotation %v", f, vcAnnotations.Experimental) 828 } 829 sbConfig.Experimental = append(sbConfig.Experimental, *feature) 830 } 831 } 832 833 if value, ok := ocispec.Annotations[vcAnnotations.DisableNewNetNs]; ok { 834 disableNewNetNs, err := strconv.ParseBool(value) 835 if err != nil { 836 return fmt.Errorf("Error parsing annotation for experimental: Please specify boolean value 'true|false'") 837 } 838 sbConfig.NetworkConfig.DisableNewNetNs = disableNewNetNs 839 } 840 841 if value, ok := ocispec.Annotations[vcAnnotations.InterNetworkModel]; ok { 842 runtimeConfig := RuntimeConfig{} 843 if err := runtimeConfig.InterNetworkModel.SetModel(value); err != nil { 844 return fmt.Errorf("Unknown network model specified in annotation %s", vcAnnotations.InterNetworkModel) 845 } 846 847 sbConfig.NetworkConfig.InterworkingModel = runtimeConfig.InterNetworkModel 848 } 849 850 return nil 851 } 852 853 func addAgentConfigOverrides(ocispec specs.Spec, config *vc.SandboxConfig) error { 854 c, ok := config.AgentConfig.(vc.KataAgentConfig) 855 if !ok { 856 return nil 857 } 858 859 if value, ok := ocispec.Annotations[vcAnnotations.KernelModules]; ok { 860 modules := strings.Split(value, KernelModulesSeparator) 861 c.KernelModules = modules 862 config.AgentConfig = c 863 } 864 865 if value, ok := ocispec.Annotations[vcAnnotations.AgentTrace]; ok { 866 trace, err := strconv.ParseBool(value) 867 if err != nil { 868 return fmt.Errorf("Error parsing annotation for agent.trace: Please specify boolean value 'true|false'") 869 } 870 c.Trace = trace 871 } 872 873 if value, ok := ocispec.Annotations[vcAnnotations.AgentTraceMode]; ok { 874 c.TraceMode = value 875 } 876 877 if value, ok := ocispec.Annotations[vcAnnotations.AgentTraceType]; ok { 878 c.TraceType = value 879 } 880 881 if value, ok := ocispec.Annotations[vcAnnotations.AgentContainerPipeSize]; ok { 882 containerPipeSize, err := strconv.ParseUint(value, 10, 32) 883 if err != nil { 884 return fmt.Errorf("Error parsing annotation for %s: Please specify uint32 value", vcAnnotations.AgentContainerPipeSize) 885 } 886 c.ContainerPipeSize = uint32(containerPipeSize) 887 } 888 889 config.AgentConfig = c 890 891 return nil 892 } 893 894 // SandboxConfig converts an OCI compatible runtime configuration file 895 // to a virtcontainers sandbox configuration structure. 896 func SandboxConfig(ocispec specs.Spec, runtimeConfig RuntimeConfig, bundlePath, cid, console string, detach, systemdCgroup bool) (vc.SandboxConfig, error) { 897 containerConfig, err := ContainerConfig(ocispec, bundlePath, cid, console, detach) 898 if err != nil { 899 return vc.SandboxConfig{}, err 900 } 901 902 shmSize, err := getShmSize(containerConfig) 903 if err != nil { 904 return vc.SandboxConfig{}, err 905 } 906 907 networkConfig, err := networkConfig(ocispec, runtimeConfig) 908 if err != nil { 909 return vc.SandboxConfig{}, err 910 } 911 912 sandboxConfig := vc.SandboxConfig{ 913 ID: cid, 914 915 Hostname: ocispec.Hostname, 916 917 HypervisorType: runtimeConfig.HypervisorType, 918 HypervisorConfig: runtimeConfig.HypervisorConfig, 919 920 AgentType: runtimeConfig.AgentType, 921 AgentConfig: runtimeConfig.AgentConfig, 922 923 ProxyType: runtimeConfig.ProxyType, 924 ProxyConfig: runtimeConfig.ProxyConfig, 925 926 ShimType: runtimeConfig.ShimType, 927 ShimConfig: runtimeConfig.ShimConfig, 928 929 NetworkConfig: networkConfig, 930 931 Containers: []vc.ContainerConfig{containerConfig}, 932 933 Annotations: map[string]string{ 934 vcAnnotations.BundlePathKey: bundlePath, 935 }, 936 937 ShmSize: shmSize, 938 939 SystemdCgroup: systemdCgroup, 940 941 SandboxCgroupOnly: runtimeConfig.SandboxCgroupOnly, 942 943 EnableAgentPidNs: runtimeConfig.EnableAgentPidNs, 944 945 DisableGuestSeccomp: runtimeConfig.DisableGuestSeccomp, 946 947 // Q: Is this really necessary? @weizhang555 948 // Spec: &ocispec, 949 950 Experimental: runtimeConfig.Experimental, 951 } 952 953 if err := addAnnotations(ocispec, &sandboxConfig, runtimeConfig); err != nil { 954 return vc.SandboxConfig{}, err 955 } 956 957 return sandboxConfig, nil 958 } 959 960 // ContainerConfig converts an OCI compatible runtime configuration 961 // file to a virtcontainers container configuration structure. 962 func ContainerConfig(ocispec specs.Spec, bundlePath, cid, console string, detach bool) (vc.ContainerConfig, error) { 963 rootfs := vc.RootFs{Target: ocispec.Root.Path, Mounted: true} 964 if !filepath.IsAbs(rootfs.Target) { 965 rootfs.Target = filepath.Join(bundlePath, ocispec.Root.Path) 966 } 967 968 ociLog.Debugf("container rootfs: %s", rootfs.Target) 969 970 cmd := types.Cmd{ 971 Args: ocispec.Process.Args, 972 Envs: cmdEnvs(ocispec, []types.EnvVar{}), 973 WorkDir: ocispec.Process.Cwd, 974 User: strconv.FormatUint(uint64(ocispec.Process.User.UID), 10), 975 PrimaryGroup: strconv.FormatUint(uint64(ocispec.Process.User.GID), 10), 976 Interactive: ocispec.Process.Terminal, 977 Console: console, 978 Detach: detach, 979 NoNewPrivileges: ocispec.Process.NoNewPrivileges, 980 } 981 982 cmd.SupplementaryGroups = []string{} 983 for _, gid := range ocispec.Process.User.AdditionalGids { 984 cmd.SupplementaryGroups = append(cmd.SupplementaryGroups, strconv.FormatUint(uint64(gid), 10)) 985 } 986 987 deviceInfos, err := containerDeviceInfos(ocispec) 988 if err != nil { 989 return vc.ContainerConfig{}, err 990 } 991 992 if ocispec.Process != nil { 993 cmd.Capabilities = ocispec.Process.Capabilities 994 } 995 996 containerConfig := vc.ContainerConfig{ 997 ID: cid, 998 RootFs: rootfs, 999 ReadonlyRootfs: ocispec.Root.Readonly, 1000 Cmd: cmd, 1001 Annotations: map[string]string{ 1002 vcAnnotations.BundlePathKey: bundlePath, 1003 }, 1004 Mounts: containerMounts(ocispec), 1005 DeviceInfos: deviceInfos, 1006 Resources: *ocispec.Linux.Resources, 1007 1008 // This is a custom OCI spec modified at SetEphemeralStorageType() 1009 // to support ephemeral storage and k8s empty dir. 1010 CustomSpec: &ocispec, 1011 } 1012 1013 cType, err := ContainerType(ocispec) 1014 if err != nil { 1015 return vc.ContainerConfig{}, err 1016 } 1017 1018 containerConfig.Annotations[vcAnnotations.ContainerTypeKey] = string(cType) 1019 1020 return containerConfig, nil 1021 } 1022 1023 func getShmSize(c vc.ContainerConfig) (uint64, error) { 1024 var shmSize uint64 1025 1026 for _, m := range c.Mounts { 1027 if m.Destination != "/dev/shm" { 1028 continue 1029 } 1030 1031 shmSize = vc.DefaultShmSize 1032 1033 if m.Type == "bind" && m.Source != "/dev/shm" { 1034 var s syscall.Statfs_t 1035 1036 if err := syscall.Statfs(m.Source, &s); err != nil { 1037 return 0, err 1038 } 1039 shmSize = uint64(s.Bsize) * s.Blocks 1040 } 1041 break 1042 } 1043 1044 ociLog.Infof("shm-size detected: %d", shmSize) 1045 1046 return shmSize, nil 1047 } 1048 1049 // StatusToOCIState translates a virtcontainers container status into an OCI state. 1050 func StatusToOCIState(status vc.ContainerStatus) specs.State { 1051 return specs.State{ 1052 Version: specs.Version, 1053 ID: status.ID, 1054 Status: StateToOCIState(status.State.State), 1055 Pid: status.PID, 1056 Bundle: status.Annotations[vcAnnotations.BundlePathKey], 1057 Annotations: status.Annotations, 1058 } 1059 } 1060 1061 // StateToOCIState translates a virtcontainers container state into an OCI one. 1062 func StateToOCIState(state types.StateString) string { 1063 switch state { 1064 case types.StateReady: 1065 return StateCreated 1066 case types.StateRunning: 1067 return StateRunning 1068 case types.StateStopped: 1069 return StateStopped 1070 case types.StatePaused: 1071 return StatePaused 1072 default: 1073 return "" 1074 } 1075 } 1076 1077 // EnvVars converts an OCI process environment variables slice 1078 // into a virtcontainers EnvVar slice. 1079 func EnvVars(envs []string) ([]types.EnvVar, error) { 1080 var envVars []types.EnvVar 1081 1082 envDelimiter := "=" 1083 expectedEnvLen := 2 1084 1085 for _, env := range envs { 1086 envSlice := strings.SplitN(env, envDelimiter, expectedEnvLen) 1087 1088 if len(envSlice) < expectedEnvLen { 1089 return []types.EnvVar{}, fmt.Errorf("Wrong string format: %s, expecting only %v parameters separated with %q", 1090 env, expectedEnvLen, envDelimiter) 1091 } 1092 1093 if envSlice[0] == "" { 1094 return []types.EnvVar{}, fmt.Errorf("Environment variable cannot be empty") 1095 } 1096 1097 envSlice[1] = strings.Trim(envSlice[1], "' ") 1098 1099 envVar := types.EnvVar{ 1100 Var: envSlice[0], 1101 Value: envSlice[1], 1102 } 1103 1104 envVars = append(envVars, envVar) 1105 } 1106 1107 return envVars, nil 1108 } 1109 1110 // GetOCIConfig returns an OCI spec configuration from the annotation 1111 // stored into the container status. 1112 func GetOCIConfig(status vc.ContainerStatus) (specs.Spec, error) { 1113 if status.Spec == nil { 1114 return specs.Spec{}, fmt.Errorf("missing OCI spec for container") 1115 } 1116 1117 return *status.Spec, nil 1118 } 1119 1120 // IsCRIOContainerManager check if a Pod is created from CRI-O 1121 func IsCRIOContainerManager(spec *specs.Spec) bool { 1122 if val, ok := spec.Annotations[crioAnnotations.ContainerType]; ok { 1123 if val == crioAnnotations.ContainerTypeSandbox || val == crioAnnotations.ContainerTypeContainer { 1124 return true 1125 } 1126 } 1127 return false 1128 }