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