github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/pkg/compatoci/utils.go (about)

     1  // Copyright (c) 2017 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package compatoci
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"path/filepath"
    13  
    14  	specs "github.com/opencontainers/runtime-spec/specs-go"
    15  	"github.com/sirupsen/logrus"
    16  
    17  	vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations"
    18  )
    19  
    20  var ociLog = logrus.WithFields(logrus.Fields{
    21  	"source":    "virtcontainers",
    22  	"subsystem": "compatoci",
    23  })
    24  
    25  // compatOCIProcess is a structure inheriting from specs.Process defined
    26  // in runtime-spec/specs-go package. The goal is to be compatible with
    27  // both v1.0.0-rc4 and v1.0.0-rc5 since the latter introduced a change
    28  // about the type of the Capabilities field.
    29  // Refer to: https://github.com/opencontainers/runtime-spec/commit/37391fb
    30  type compatOCIProcess struct {
    31  	specs.Process
    32  	Capabilities interface{} `json:"capabilities,omitempty" platform:"linux"` //nolint:govet
    33  }
    34  
    35  // compatOCISpec is a structure inheriting from specs.Spec defined
    36  // in runtime-spec/specs-go package. It relies on the compatOCIProcess
    37  // structure declared above, in order to be compatible with both
    38  // v1.0.0-rc4 and v1.0.0-rc5.
    39  // Refer to: https://github.com/opencontainers/runtime-spec/commit/37391fb
    40  type compatOCISpec struct {
    41  	specs.Spec
    42  	Process *compatOCIProcess `json:"process,omitempty"` //nolint:govet
    43  }
    44  
    45  func containerCapabilities(s compatOCISpec) (specs.LinuxCapabilities, error) {
    46  	capabilities := s.Process.Capabilities
    47  	var c specs.LinuxCapabilities
    48  
    49  	// In spec v1.0.0-rc4, capabilities was a list of strings. This was changed
    50  	// to an object with v1.0.0-rc5.
    51  	// Check for the interface type to support both the versions.
    52  	switch caps := capabilities.(type) {
    53  	case map[string]interface{}:
    54  		for key, value := range caps {
    55  			switch val := value.(type) {
    56  			case []interface{}:
    57  				var list []string
    58  
    59  				for _, str := range val {
    60  					list = append(list, str.(string))
    61  				}
    62  
    63  				switch key {
    64  				case "bounding":
    65  					c.Bounding = list
    66  				case "effective":
    67  					c.Effective = list
    68  				case "inheritable":
    69  					c.Inheritable = list
    70  				case "ambient":
    71  					c.Ambient = list
    72  				case "permitted":
    73  					c.Permitted = list
    74  				}
    75  
    76  			default:
    77  				return c, fmt.Errorf("Unexpected format for capabilities: %v", caps)
    78  			}
    79  		}
    80  	case []interface{}:
    81  		var list []string
    82  		for _, str := range caps {
    83  			list = append(list, str.(string))
    84  		}
    85  
    86  		c = specs.LinuxCapabilities{
    87  			Bounding:    list,
    88  			Effective:   list,
    89  			Inheritable: list,
    90  			Ambient:     list,
    91  			Permitted:   list,
    92  		}
    93  	case nil:
    94  		ociLog.Debug("Empty capabilities have been passed")
    95  		return c, nil
    96  	default:
    97  		return c, fmt.Errorf("Unexpected format for capabilities: %v", caps)
    98  	}
    99  
   100  	return c, nil
   101  }
   102  
   103  // SetLogger sets up a logger for this pkg
   104  func SetLogger(logger *logrus.Entry) {
   105  	fields := ociLog.Data
   106  
   107  	ociLog = logger.WithFields(fields)
   108  }
   109  
   110  // ContainerCapabilities return a LinuxCapabilities for virtcontainer
   111  func ContainerCapabilities(s compatOCISpec) (specs.LinuxCapabilities, error) {
   112  	if s.Process == nil {
   113  		return specs.LinuxCapabilities{}, fmt.Errorf("ContainerCapabilities, Process is nil")
   114  	}
   115  	return containerCapabilities(s)
   116  }
   117  
   118  // getConfigPath returns the full config path from the bundle
   119  // path provided.
   120  func getConfigPath(bundlePath string) string {
   121  	return filepath.Join(bundlePath, "config.json")
   122  }
   123  
   124  // ParseConfigJSON unmarshals the config.json file.
   125  func ParseConfigJSON(bundlePath string) (specs.Spec, error) {
   126  	configPath := getConfigPath(bundlePath)
   127  	ociLog.Debugf("converting %s", configPath)
   128  
   129  	configByte, err := ioutil.ReadFile(configPath)
   130  	if err != nil {
   131  		return specs.Spec{}, err
   132  	}
   133  
   134  	var compSpec compatOCISpec
   135  	if err := json.Unmarshal(configByte, &compSpec); err != nil {
   136  		return specs.Spec{}, err
   137  	}
   138  
   139  	caps, err := ContainerCapabilities(compSpec)
   140  	if err != nil {
   141  		return specs.Spec{}, err
   142  	}
   143  
   144  	compSpec.Spec.Process = &compSpec.Process.Process
   145  	compSpec.Spec.Process.Capabilities = &caps
   146  
   147  	return compSpec.Spec, nil
   148  }
   149  
   150  func GetContainerSpec(annotations map[string]string) (specs.Spec, error) {
   151  	if bundlePath, ok := annotations[vcAnnotations.BundlePathKey]; ok {
   152  		return ParseConfigJSON(bundlePath)
   153  	}
   154  
   155  	ociLog.Errorf("Annotations[%s] not found, cannot find container spec",
   156  		vcAnnotations.BundlePathKey)
   157  	return specs.Spec{}, fmt.Errorf("Could not find container spec")
   158  }