...

Source file src/github.com/Microsoft/hcsshim/internal/oci/uvm.go

Documentation: github.com/Microsoft/hcsshim/internal/oci

     1  //go:build windows
     2  
     3  package oci
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"strconv"
     9  
    10  	runhcsopts "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
    11  	"github.com/Microsoft/hcsshim/internal/log"
    12  	"github.com/Microsoft/hcsshim/internal/uvm"
    13  	"github.com/Microsoft/hcsshim/pkg/annotations"
    14  	"github.com/opencontainers/runtime-spec/specs-go"
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  // UVM specific annotation parsing
    19  
    20  // ParseAnnotationsCPUCount searches `s.Annotations` for the CPU annotation. If
    21  // not found searches `s` for the Windows CPU section. If neither are found
    22  // returns `def`.
    23  func ParseAnnotationsCPUCount(ctx context.Context, s *specs.Spec, annotation string, def int32) int32 {
    24  	if m := parseAnnotationsUint64(ctx, s.Annotations, annotation, 0); m != 0 {
    25  		return int32(m)
    26  	}
    27  	if s.Windows != nil &&
    28  		s.Windows.Resources != nil &&
    29  		s.Windows.Resources.CPU != nil &&
    30  		s.Windows.Resources.CPU.Count != nil &&
    31  		*s.Windows.Resources.CPU.Count > 0 {
    32  		return int32(*s.Windows.Resources.CPU.Count)
    33  	}
    34  	return def
    35  }
    36  
    37  // ParseAnnotationsCPULimit searches `s.Annotations` for the CPU annotation. If
    38  // not found searches `s` for the Windows CPU section. If neither are found
    39  // returns `def`.
    40  func ParseAnnotationsCPULimit(ctx context.Context, s *specs.Spec, annotation string, def int32) int32 {
    41  	if m := parseAnnotationsUint64(ctx, s.Annotations, annotation, 0); m != 0 {
    42  		return int32(m)
    43  	}
    44  	if s.Windows != nil &&
    45  		s.Windows.Resources != nil &&
    46  		s.Windows.Resources.CPU != nil &&
    47  		s.Windows.Resources.CPU.Maximum != nil &&
    48  		*s.Windows.Resources.CPU.Maximum > 0 {
    49  		return int32(*s.Windows.Resources.CPU.Maximum)
    50  	}
    51  	return def
    52  }
    53  
    54  // ParseAnnotationsCPUWeight searches `s.Annotations` for the CPU annotation. If
    55  // not found searches `s` for the Windows CPU section. If neither are found
    56  // returns `def`.
    57  func ParseAnnotationsCPUWeight(ctx context.Context, s *specs.Spec, annotation string, def int32) int32 {
    58  	if m := parseAnnotationsUint64(ctx, s.Annotations, annotation, 0); m != 0 {
    59  		return int32(m)
    60  	}
    61  	if s.Windows != nil &&
    62  		s.Windows.Resources != nil &&
    63  		s.Windows.Resources.CPU != nil &&
    64  		s.Windows.Resources.CPU.Shares != nil &&
    65  		*s.Windows.Resources.CPU.Shares > 0 {
    66  		return int32(*s.Windows.Resources.CPU.Shares)
    67  	}
    68  	return def
    69  }
    70  
    71  // ParseAnnotationsStorageIops searches `s.Annotations` for the `Iops`
    72  // annotation. If not found searches `s` for the Windows Storage section. If
    73  // neither are found returns `def`.
    74  func ParseAnnotationsStorageIops(ctx context.Context, s *specs.Spec, annotation string, def int32) int32 {
    75  	if m := parseAnnotationsUint64(ctx, s.Annotations, annotation, 0); m != 0 {
    76  		return int32(m)
    77  	}
    78  	if s.Windows != nil &&
    79  		s.Windows.Resources != nil &&
    80  		s.Windows.Resources.Storage != nil &&
    81  		s.Windows.Resources.Storage.Iops != nil &&
    82  		*s.Windows.Resources.Storage.Iops > 0 {
    83  		return int32(*s.Windows.Resources.Storage.Iops)
    84  	}
    85  	return def
    86  }
    87  
    88  // ParseAnnotationsStorageBps searches `s.Annotations` for the `Bps` annotation.
    89  // If not found searches `s` for the Windows Storage section. If neither are
    90  // found returns `def`.
    91  func ParseAnnotationsStorageBps(ctx context.Context, s *specs.Spec, annotation string, def int32) int32 {
    92  	if m := parseAnnotationsUint64(ctx, s.Annotations, annotation, 0); m != 0 {
    93  		return int32(m)
    94  	}
    95  	if s.Windows != nil &&
    96  		s.Windows.Resources != nil &&
    97  		s.Windows.Resources.Storage != nil &&
    98  		s.Windows.Resources.Storage.Bps != nil &&
    99  		*s.Windows.Resources.Storage.Bps > 0 {
   100  		return int32(*s.Windows.Resources.Storage.Bps)
   101  	}
   102  	return def
   103  }
   104  
   105  // ParseAnnotationsMemory searches `s.Annotations` for the memory annotation. If
   106  // not found searches `s` for the Windows memory section. If neither are found
   107  // returns `def`.
   108  //
   109  // Note: The returned value is in `MB`.
   110  func ParseAnnotationsMemory(ctx context.Context, s *specs.Spec, annotation string, def uint64) uint64 {
   111  	if m := parseAnnotationsUint64(ctx, s.Annotations, annotation, 0); m != 0 {
   112  		return m
   113  	}
   114  	if s.Windows != nil &&
   115  		s.Windows.Resources != nil &&
   116  		s.Windows.Resources.Memory != nil &&
   117  		s.Windows.Resources.Memory.Limit != nil &&
   118  		*s.Windows.Resources.Memory.Limit > 0 {
   119  		return (*s.Windows.Resources.Memory.Limit / 1024 / 1024)
   120  	}
   121  	return def
   122  }
   123  
   124  // parseAnnotationsPreferredRootFSType searches `a` for `key` and verifies that the
   125  // value is in the set of allowed values. If `key` is not found returns `def`.
   126  func parseAnnotationsPreferredRootFSType(ctx context.Context, a map[string]string, key string, def uvm.PreferredRootFSType) uvm.PreferredRootFSType {
   127  	if v, ok := a[key]; ok {
   128  		switch v {
   129  		case "initrd":
   130  			return uvm.PreferredRootFSTypeInitRd
   131  		case "vhd":
   132  			return uvm.PreferredRootFSTypeVHD
   133  		default:
   134  			log.G(ctx).WithFields(logrus.Fields{
   135  				"annotation": key,
   136  				"value":      v,
   137  			}).Warn("annotation value must be 'initrd' or 'vhd'")
   138  		}
   139  	}
   140  	return def
   141  }
   142  
   143  // handleAnnotationKernelDirectBoot handles parsing annotationKernelDirectBoot and setting
   144  // implied annotations from the result.
   145  func handleAnnotationKernelDirectBoot(ctx context.Context, a map[string]string, lopts *uvm.OptionsLCOW) {
   146  	lopts.KernelDirect = ParseAnnotationsBool(ctx, a, annotations.KernelDirectBoot, lopts.KernelDirect)
   147  	if !lopts.KernelDirect {
   148  		lopts.KernelFile = uvm.KernelFile
   149  	}
   150  }
   151  
   152  // handleAnnotationPreferredRootFSType handles parsing annotationPreferredRootFSType and setting
   153  // implied annotations from the result
   154  func handleAnnotationPreferredRootFSType(ctx context.Context, a map[string]string, lopts *uvm.OptionsLCOW) {
   155  	lopts.PreferredRootFSType = parseAnnotationsPreferredRootFSType(ctx, a, annotations.PreferredRootFSType, lopts.PreferredRootFSType)
   156  	switch lopts.PreferredRootFSType {
   157  	case uvm.PreferredRootFSTypeInitRd:
   158  		lopts.RootFSFile = uvm.InitrdFile
   159  	case uvm.PreferredRootFSTypeVHD:
   160  		lopts.RootFSFile = uvm.VhdFile
   161  	}
   162  }
   163  
   164  // handleAnnotationFullyPhysicallyBacked handles parsing annotationFullyPhysicallyBacked and setting
   165  // implied annotations from the result. For both LCOW and WCOW options.
   166  func handleAnnotationFullyPhysicallyBacked(ctx context.Context, a map[string]string, opts interface{}) {
   167  	switch options := opts.(type) {
   168  	case *uvm.OptionsLCOW:
   169  		options.FullyPhysicallyBacked = ParseAnnotationsBool(ctx, a, annotations.FullyPhysicallyBacked, options.FullyPhysicallyBacked)
   170  		if options.FullyPhysicallyBacked {
   171  			options.AllowOvercommit = false
   172  			options.PreferredRootFSType = uvm.PreferredRootFSTypeInitRd
   173  			options.RootFSFile = uvm.InitrdFile
   174  			options.VPMemDeviceCount = 0
   175  		}
   176  	case *uvm.OptionsWCOW:
   177  		options.FullyPhysicallyBacked = ParseAnnotationsBool(ctx, a, annotations.FullyPhysicallyBacked, options.FullyPhysicallyBacked)
   178  		if options.FullyPhysicallyBacked {
   179  			options.AllowOvercommit = false
   180  		}
   181  	}
   182  }
   183  
   184  // handleSecurityPolicy handles parsing SecurityPolicy and NoSecurityHardware and setting
   185  // implied options from the results. Both LCOW only, not WCOW
   186  func handleSecurityPolicy(ctx context.Context, a map[string]string, lopts *uvm.OptionsLCOW) {
   187  	lopts.SecurityPolicy = parseAnnotationsString(a, annotations.SecurityPolicy, lopts.SecurityPolicy)
   188  	// allow actual isolated boot etc to be ignored if we have no hardware. Required for dev
   189  	// this is not a security issue as the attestation will fail without a genuine report
   190  	noSecurityHardware := ParseAnnotationsBool(ctx, a, annotations.NoSecurityHardware, false)
   191  
   192  	// if there is a security policy (and SNP) we currently boot in a way that doesn't support any boot options
   193  	// this might change if the building of the vmgs file were to be done on demand but that is likely
   194  	// much slower and noy very useful. We do respect the filename of the vmgs file so if it is necessary to
   195  	// have different options then multiple files could be used.
   196  	if len(lopts.SecurityPolicy) > 0 && !noSecurityHardware {
   197  		// VPMem not supported by the enlightened kernel for SNP so set count to zero.
   198  		lopts.VPMemDeviceCount = 0
   199  		// set the default GuestState filename.
   200  		lopts.GuestStateFile = uvm.GuestStateFile
   201  		lopts.KernelBootOptions = ""
   202  		lopts.PreferredRootFSType = uvm.PreferredRootFSTypeNA
   203  		lopts.AllowOvercommit = false
   204  		lopts.SecurityPolicyEnabled = true
   205  	}
   206  
   207  	if len(lopts.SecurityPolicy) > 0 {
   208  		// will only be false if explicitly set false by the annotation. We will otherwise default to true when there is a security policy
   209  		lopts.EnableScratchEncryption = ParseAnnotationsBool(ctx, a, annotations.EncryptedScratchDisk, true)
   210  	}
   211  }
   212  
   213  // sets options common to both WCOW and LCOW from annotations
   214  func specToUVMCreateOptionsCommon(ctx context.Context, opts *uvm.Options, s *specs.Spec) {
   215  	opts.MemorySizeInMB = ParseAnnotationsMemory(ctx, s, annotations.MemorySizeInMB, opts.MemorySizeInMB)
   216  	opts.LowMMIOGapInMB = parseAnnotationsUint64(ctx, s.Annotations, annotations.MemoryLowMMIOGapInMB, opts.LowMMIOGapInMB)
   217  	opts.HighMMIOBaseInMB = parseAnnotationsUint64(ctx, s.Annotations, annotations.MemoryHighMMIOBaseInMB, opts.HighMMIOBaseInMB)
   218  	opts.HighMMIOGapInMB = parseAnnotationsUint64(ctx, s.Annotations, annotations.MemoryHighMMIOGapInMB, opts.HighMMIOGapInMB)
   219  	opts.AllowOvercommit = ParseAnnotationsBool(ctx, s.Annotations, annotations.AllowOvercommit, opts.AllowOvercommit)
   220  	opts.EnableDeferredCommit = ParseAnnotationsBool(ctx, s.Annotations, annotations.EnableDeferredCommit, opts.EnableDeferredCommit)
   221  	opts.ProcessorCount = ParseAnnotationsCPUCount(ctx, s, annotations.ProcessorCount, opts.ProcessorCount)
   222  	opts.ProcessorLimit = ParseAnnotationsCPULimit(ctx, s, annotations.ProcessorLimit, opts.ProcessorLimit)
   223  	opts.ProcessorWeight = ParseAnnotationsCPUWeight(ctx, s, annotations.ProcessorWeight, opts.ProcessorWeight)
   224  	opts.StorageQoSBandwidthMaximum = ParseAnnotationsStorageBps(ctx, s, annotations.StorageQoSBandwidthMaximum, opts.StorageQoSBandwidthMaximum)
   225  	opts.StorageQoSIopsMaximum = ParseAnnotationsStorageIops(ctx, s, annotations.StorageQoSIopsMaximum, opts.StorageQoSIopsMaximum)
   226  	opts.CPUGroupID = parseAnnotationsString(s.Annotations, annotations.CPUGroupID, opts.CPUGroupID)
   227  	opts.NetworkConfigProxy = parseAnnotationsString(s.Annotations, annotations.NetworkConfigProxy, opts.NetworkConfigProxy)
   228  	opts.ProcessDumpLocation = parseAnnotationsString(s.Annotations, annotations.ContainerProcessDumpLocation, opts.ProcessDumpLocation)
   229  	opts.NoWritableFileShares = ParseAnnotationsBool(ctx, s.Annotations, annotations.DisableWritableFileShares, opts.NoWritableFileShares)
   230  	opts.DumpDirectoryPath = parseAnnotationsString(s.Annotations, annotations.DumpDirectoryPath, opts.DumpDirectoryPath)
   231  }
   232  
   233  // SpecToUVMCreateOpts parses `s` and returns either `*uvm.OptionsLCOW` or
   234  // `*uvm.OptionsWCOW`.
   235  func SpecToUVMCreateOpts(ctx context.Context, s *specs.Spec, id, owner string) (interface{}, error) {
   236  	if !IsIsolated(s) {
   237  		return nil, errors.New("cannot create UVM opts for non-isolated spec")
   238  	}
   239  	if IsLCOW(s) {
   240  		lopts := uvm.NewDefaultOptionsLCOW(id, owner)
   241  		specToUVMCreateOptionsCommon(ctx, lopts.Options, s)
   242  
   243  		/*
   244  			WARNING!!!!!!!!!!
   245  
   246  			When adding an option here which must match some security policy by default, make sure that the correct default (ie matches
   247  			a default security policy) is applied in handleSecurityPolicy. Inadvertantly adding an "option" which defaults to false but MUST be
   248  			true for a default security	policy to work will force the annotation to have be set by the team that owns the box. That will
   249  			be practically difficult and we	might not find out until a little late in the process.
   250  		*/
   251  
   252  		lopts.EnableColdDiscardHint = ParseAnnotationsBool(ctx, s.Annotations, annotations.EnableColdDiscardHint, lopts.EnableColdDiscardHint)
   253  		lopts.VPMemDeviceCount = parseAnnotationsUint32(ctx, s.Annotations, annotations.VPMemCount, lopts.VPMemDeviceCount)
   254  		lopts.VPMemSizeBytes = parseAnnotationsUint64(ctx, s.Annotations, annotations.VPMemSize, lopts.VPMemSizeBytes)
   255  		lopts.VPMemNoMultiMapping = ParseAnnotationsBool(ctx, s.Annotations, annotations.VPMemNoMultiMapping, lopts.VPMemNoMultiMapping)
   256  		lopts.VPCIEnabled = ParseAnnotationsBool(ctx, s.Annotations, annotations.VPCIEnabled, lopts.VPCIEnabled)
   257  		lopts.BootFilesPath = parseAnnotationsString(s.Annotations, annotations.BootFilesRootPath, lopts.BootFilesPath)
   258  		lopts.EnableScratchEncryption = ParseAnnotationsBool(ctx, s.Annotations, annotations.EncryptedScratchDisk, lopts.EnableScratchEncryption)
   259  		lopts.SecurityPolicy = parseAnnotationsString(s.Annotations, annotations.SecurityPolicy, lopts.SecurityPolicy)
   260  		lopts.SecurityPolicyEnforcer = parseAnnotationsString(s.Annotations, annotations.SecurityPolicyEnforcer, lopts.SecurityPolicyEnforcer)
   261  		lopts.UVMReferenceInfoFile = parseAnnotationsString(s.Annotations, annotations.UVMReferenceInfoFile, lopts.UVMReferenceInfoFile)
   262  		lopts.KernelBootOptions = parseAnnotationsString(s.Annotations, annotations.KernelBootOptions, lopts.KernelBootOptions)
   263  		lopts.DisableTimeSyncService = ParseAnnotationsBool(ctx, s.Annotations, annotations.DisableLCOWTimeSyncService, lopts.DisableTimeSyncService)
   264  		handleAnnotationPreferredRootFSType(ctx, s.Annotations, lopts)
   265  		handleAnnotationKernelDirectBoot(ctx, s.Annotations, lopts)
   266  
   267  		// parsing of FullyPhysicallyBacked needs to go after handling kernel direct boot and
   268  		// preferred rootfs type since it may overwrite settings created by those
   269  		handleAnnotationFullyPhysicallyBacked(ctx, s.Annotations, lopts)
   270  
   271  		// SecurityPolicy is very sensitive to other settings and will silently change those that are incompatible.
   272  		// Eg VMPem device count, overridden kernel option cannot be respected.
   273  		handleSecurityPolicy(ctx, s.Annotations, lopts)
   274  
   275  		// override the default GuestState filename if specified
   276  		lopts.GuestStateFile = parseAnnotationsString(s.Annotations, annotations.GuestStateFile, lopts.GuestStateFile)
   277  		return lopts, nil
   278  	} else if IsWCOW(s) {
   279  		wopts := uvm.NewDefaultOptionsWCOW(id, owner)
   280  		specToUVMCreateOptionsCommon(ctx, wopts.Options, s)
   281  
   282  		wopts.DisableCompartmentNamespace = ParseAnnotationsBool(ctx, s.Annotations, annotations.DisableCompartmentNamespace, wopts.DisableCompartmentNamespace)
   283  		wopts.NoDirectMap = ParseAnnotationsBool(ctx, s.Annotations, annotations.VSMBNoDirectMap, wopts.NoDirectMap)
   284  		wopts.NoInheritHostTimezone = ParseAnnotationsBool(ctx, s.Annotations, annotations.NoInheritHostTimezone, wopts.NoInheritHostTimezone)
   285  		handleAnnotationFullyPhysicallyBacked(ctx, s.Annotations, wopts)
   286  		return wopts, nil
   287  	}
   288  	return nil, errors.New("cannot create UVM opts spec is not LCOW or WCOW")
   289  }
   290  
   291  // UpdateSpecFromOptions sets extra annotations on the OCI spec based on the
   292  // `opts` struct.
   293  func UpdateSpecFromOptions(s specs.Spec, opts *runhcsopts.Options) specs.Spec {
   294  	if opts == nil {
   295  		return s
   296  	}
   297  
   298  	if _, ok := s.Annotations[annotations.BootFilesRootPath]; !ok && opts.BootFilesRootPath != "" {
   299  		s.Annotations[annotations.BootFilesRootPath] = opts.BootFilesRootPath
   300  	}
   301  
   302  	if _, ok := s.Annotations[annotations.ProcessorCount]; !ok && opts.VmProcessorCount != 0 {
   303  		s.Annotations[annotations.ProcessorCount] = strconv.FormatInt(int64(opts.VmProcessorCount), 10)
   304  	}
   305  
   306  	if _, ok := s.Annotations[annotations.MemorySizeInMB]; !ok && opts.VmMemorySizeInMb != 0 {
   307  		s.Annotations[annotations.MemorySizeInMB] = strconv.FormatInt(int64(opts.VmMemorySizeInMb), 10)
   308  	}
   309  
   310  	if _, ok := s.Annotations[annotations.GPUVHDPath]; !ok && opts.GPUVHDPath != "" {
   311  		s.Annotations[annotations.GPUVHDPath] = opts.GPUVHDPath
   312  	}
   313  
   314  	if _, ok := s.Annotations[annotations.NetworkConfigProxy]; !ok && opts.NCProxyAddr != "" {
   315  		s.Annotations[annotations.NetworkConfigProxy] = opts.NCProxyAddr
   316  	}
   317  
   318  	for key, value := range opts.DefaultContainerAnnotations {
   319  		// Make sure not to override any annotations which are set explicitly
   320  		if _, ok := s.Annotations[key]; !ok {
   321  			s.Annotations[key] = value
   322  		}
   323  	}
   324  
   325  	return s
   326  }
   327  

View as plain text