github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/generate/validate.go (about)

     1  package generate
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  
     7  	"github.com/containers/common/pkg/sysinfo"
     8  	"github.com/containers/podman/v2/pkg/cgroups"
     9  	"github.com/containers/podman/v2/pkg/specgen"
    10  	"github.com/containers/podman/v2/utils"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  // Verify resource limits are sanely set when running on cgroup v1.
    15  func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error) {
    16  	warnings := []string{}
    17  
    18  	sysInfo := sysinfo.New(true)
    19  
    20  	if s.ResourceLimits == nil {
    21  		return warnings, nil
    22  	}
    23  
    24  	if s.ResourceLimits.Unified != nil {
    25  		return nil, errors.New("Cannot use --cgroup-conf without cgroup v2")
    26  	}
    27  
    28  	// Memory checks
    29  	if s.ResourceLimits.Memory != nil {
    30  		memory := s.ResourceLimits.Memory
    31  		if memory.Limit != nil && !sysInfo.MemoryLimit {
    32  			warnings = append(warnings, "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
    33  			memory.Limit = nil
    34  			memory.Swap = nil
    35  		}
    36  		if memory.Limit != nil && memory.Swap != nil && !sysInfo.SwapLimit {
    37  			warnings = append(warnings, "Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.")
    38  			memory.Swap = nil
    39  		}
    40  		if memory.Limit != nil && memory.Swap != nil && *memory.Swap < *memory.Limit {
    41  			return warnings, errors.New("minimum memoryswap limit should be larger than memory limit, see usage")
    42  		}
    43  		if memory.Limit == nil && memory.Swap != nil {
    44  			return warnings, errors.New("you should always set a memory limit when using a memoryswap limit, see usage")
    45  		}
    46  		if memory.Swappiness != nil {
    47  			if !sysInfo.MemorySwappiness {
    48  				warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded.")
    49  				memory.Swappiness = nil
    50  			} else {
    51  				if *memory.Swappiness < 0 || *memory.Swappiness > 100 {
    52  					return warnings, errors.Errorf("invalid value: %v, valid memory swappiness range is 0-100", *memory.Swappiness)
    53  				}
    54  			}
    55  		}
    56  		if memory.Reservation != nil && !sysInfo.MemoryReservation {
    57  			warnings = append(warnings, "Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.")
    58  			memory.Reservation = nil
    59  		}
    60  		if memory.Limit != nil && memory.Reservation != nil && *memory.Limit < *memory.Reservation {
    61  			return warnings, errors.New("minimum memory limit cannot be less than memory reservation limit, see usage")
    62  		}
    63  		if memory.Kernel != nil && !sysInfo.KernelMemory {
    64  			warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
    65  			memory.Kernel = nil
    66  		}
    67  		if memory.DisableOOMKiller != nil && *memory.DisableOOMKiller && !sysInfo.OomKillDisable {
    68  			warnings = append(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.")
    69  			memory.DisableOOMKiller = nil
    70  		}
    71  	}
    72  
    73  	// Pids checks
    74  	if s.ResourceLimits.Pids != nil {
    75  		pids := s.ResourceLimits.Pids
    76  		// TODO: Should this be 0, or checking that ResourceLimits.Pids
    77  		// is set at all?
    78  		if pids.Limit > 0 && !sysInfo.PidsLimit {
    79  			warnings = append(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.")
    80  			s.ResourceLimits.Pids = nil
    81  		}
    82  	}
    83  
    84  	// CPU Checks
    85  	if s.ResourceLimits.CPU != nil {
    86  		cpu := s.ResourceLimits.CPU
    87  		if cpu.Shares != nil && !sysInfo.CPUShares {
    88  			warnings = append(warnings, "Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.")
    89  			cpu.Shares = nil
    90  		}
    91  		if cpu.Period != nil && !sysInfo.CPUCfsPeriod {
    92  			warnings = append(warnings, "Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.")
    93  			cpu.Period = nil
    94  		}
    95  		if cpu.Period != nil && (*cpu.Period < 1000 || *cpu.Period > 1000000) {
    96  			return warnings, errors.New("CPU cfs period cannot be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)")
    97  		}
    98  		if cpu.Quota != nil && !sysInfo.CPUCfsQuota {
    99  			warnings = append(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.")
   100  			cpu.Quota = nil
   101  		}
   102  		if cpu.Quota != nil && *cpu.Quota < 1000 {
   103  			return warnings, errors.New("CPU cfs quota cannot be less than 1ms (i.e. 1000)")
   104  		}
   105  		if (cpu.Cpus != "" || cpu.Mems != "") && !sysInfo.Cpuset {
   106  			warnings = append(warnings, "Your kernel does not support cpuset or the cgroup is not mounted. CPUset discarded.")
   107  			cpu.Cpus = ""
   108  			cpu.Mems = ""
   109  		}
   110  
   111  		cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(cpu.Cpus)
   112  		if err != nil {
   113  			return warnings, errors.Errorf("invalid value %s for cpuset cpus", cpu.Cpus)
   114  		}
   115  		if !cpusAvailable {
   116  			return warnings, errors.Errorf("requested CPUs are not available - requested %s, available: %s", cpu.Cpus, sysInfo.Cpus)
   117  		}
   118  
   119  		memsAvailable, err := sysInfo.IsCpusetMemsAvailable(cpu.Mems)
   120  		if err != nil {
   121  			return warnings, errors.Errorf("invalid value %s for cpuset mems", cpu.Mems)
   122  		}
   123  		if !memsAvailable {
   124  			return warnings, errors.Errorf("requested memory nodes are not available - requested %s, available: %s", cpu.Mems, sysInfo.Mems)
   125  		}
   126  	}
   127  
   128  	// Blkio checks
   129  	if s.ResourceLimits.BlockIO != nil {
   130  		blkio := s.ResourceLimits.BlockIO
   131  		if blkio.Weight != nil && !sysInfo.BlkioWeight {
   132  			warnings = append(warnings, "Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.")
   133  			blkio.Weight = nil
   134  		}
   135  		if blkio.Weight != nil && (*blkio.Weight > 1000 || *blkio.Weight < 10) {
   136  			return warnings, errors.New("range of blkio weight is from 10 to 1000")
   137  		}
   138  		if len(blkio.WeightDevice) > 0 && !sysInfo.BlkioWeightDevice {
   139  			warnings = append(warnings, "Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.")
   140  			blkio.WeightDevice = nil
   141  		}
   142  		if len(blkio.ThrottleReadBpsDevice) > 0 && !sysInfo.BlkioReadBpsDevice {
   143  			warnings = append(warnings, "Your kernel does not support BPS Block I/O read limit or the cgroup is not mounted. Block I/O BPS read limit discarded")
   144  			blkio.ThrottleReadBpsDevice = nil
   145  		}
   146  		if len(blkio.ThrottleWriteBpsDevice) > 0 && !sysInfo.BlkioWriteBpsDevice {
   147  			warnings = append(warnings, "Your kernel does not support BPS Block I/O write limit or the cgroup is not mounted. Block I/O BPS write limit discarded.")
   148  			blkio.ThrottleWriteBpsDevice = nil
   149  		}
   150  		if len(blkio.ThrottleReadIOPSDevice) > 0 && !sysInfo.BlkioReadIOpsDevice {
   151  			warnings = append(warnings, "Your kernel does not support IOPS Block read limit or the cgroup is not mounted. Block I/O IOPS read limit discarded.")
   152  			blkio.ThrottleReadIOPSDevice = nil
   153  		}
   154  		if len(blkio.ThrottleWriteIOPSDevice) > 0 && !sysInfo.BlkioWriteIOpsDevice {
   155  			warnings = append(warnings, "Your kernel does not support IOPS Block I/O write limit or the cgroup is not mounted. Block I/O IOPS write limit discarded.")
   156  			blkio.ThrottleWriteIOPSDevice = nil
   157  		}
   158  	}
   159  
   160  	return warnings, nil
   161  }
   162  
   163  // Verify resource limits are sanely set when running on cgroup v2.
   164  func verifyContainerResourcesCgroupV2(s *specgen.SpecGenerator) ([]string, error) {
   165  	warnings := []string{}
   166  
   167  	if s.ResourceLimits == nil {
   168  		return warnings, nil
   169  	}
   170  
   171  	if s.ResourceLimits.Memory != nil && s.ResourceLimits.Memory.Swap != nil {
   172  		own, err := utils.GetOwnCgroup()
   173  		if err != nil {
   174  			return warnings, err
   175  		}
   176  		memoryMax := filepath.Join("/sys/fs/cgroup", own, "memory.max")
   177  		memorySwapMax := filepath.Join("/sys/fs/cgroup", own, "memory.swap.max")
   178  		_, errMemoryMax := os.Stat(memoryMax)
   179  		_, errMemorySwapMax := os.Stat(memorySwapMax)
   180  		// Differently than cgroup v1, the memory.*max files are not present in the
   181  		// root directory, so we cannot query directly that, so as best effort use
   182  		// the current cgroup.
   183  		// Check whether memory.max exists in the current cgroup and memory.swap.max
   184  		//  does not.  In this case we can be sure memory swap is not enabled.
   185  		// If both files don't exist, the memory controller might not be enabled
   186  		// for the current cgroup.
   187  		if errMemoryMax == nil && errMemorySwapMax != nil {
   188  			warnings = append(warnings, "Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.")
   189  			s.ResourceLimits.Memory.Swap = nil
   190  		}
   191  	}
   192  	return warnings, nil
   193  }
   194  
   195  // Verify resource limits are sanely set, removing any limits that are not
   196  // possible with the current cgroups config.
   197  func verifyContainerResources(s *specgen.SpecGenerator) ([]string, error) {
   198  	cgroup2, err := cgroups.IsCgroup2UnifiedMode()
   199  	if err != nil {
   200  		return []string{}, err
   201  	}
   202  	if cgroup2 {
   203  		return verifyContainerResourcesCgroupV2(s)
   204  	}
   205  	return verifyContainerResourcesCgroupV1(s)
   206  }