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  }