Merge pull request #10716 from cdoern/podFlags

Podman Pod Create --cpus and --cpuset-cpus flags
This commit is contained in:
OpenShift Merge Robot 2021-06-23 15:51:29 -04:00 committed by GitHub
commit 3f3feaa015
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 326 additions and 29 deletions

View file

@ -222,7 +222,6 @@ func createInit(c *cobra.Command) error {
} }
cliVals.Env = env cliVals.Env = env
} }
if c.Flag("cgroups").Changed && cliVals.CGroupsMode == "split" && registry.IsRemote() { if c.Flag("cgroups").Changed && cliVals.CGroupsMode == "split" && registry.IsRemote() {
return errors.Errorf("the option --cgroups=%q is not supported in remote mode", cliVals.CGroupsMode) return errors.Errorf("the option --cgroups=%q is not supported in remote mode", cliVals.CGroupsMode)
} }
@ -292,6 +291,8 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions
Net: netOpts, Net: netOpts,
CreateCommand: os.Args, CreateCommand: os.Args,
Hostname: s.ContainerBasicConfig.Hostname, Hostname: s.ContainerBasicConfig.Hostname,
Cpus: cliVals.CPUS,
CpusetCpus: cliVals.CPUSetCPUs,
} }
// Unset config values we passed to the pod to prevent them being used twice for the container and pod. // Unset config values we passed to the pod to prevent them being used twice for the container and pod.
s.ContainerBasicConfig.Hostname = "" s.ContainerBasicConfig.Hostname = ""

View file

@ -5,9 +5,13 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"runtime"
"sort"
"strconv"
"strings" "strings"
"github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/sysinfo"
"github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/parse"
"github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/registry"
@ -16,6 +20,7 @@ import (
"github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util" "github.com/containers/podman/v3/pkg/util"
"github.com/docker/docker/pkg/parsers"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -55,6 +60,14 @@ func init() {
common.DefineNetFlags(createCommand) common.DefineNetFlags(createCommand)
cpusetflagName := "cpuset-cpus"
flags.StringVar(&createOptions.CpusetCpus, cpusetflagName, "", "CPUs in which to allow execution")
_ = createCommand.RegisterFlagCompletionFunc(cpusetflagName, completion.AutocompleteDefault)
cpusflagName := "cpus"
flags.Float64Var(&createOptions.Cpus, cpusflagName, 0.000, "set amount of CPUs for the pod")
_ = createCommand.RegisterFlagCompletionFunc(cpusflagName, completion.AutocompleteDefault)
cgroupParentflagName := "cgroup-parent" cgroupParentflagName := "cgroup-parent"
flags.StringVar(&createOptions.CGroupParent, cgroupParentflagName, "", "Set parent cgroup for the pod") flags.StringVar(&createOptions.CGroupParent, cgroupParentflagName, "", "Set parent cgroup for the pod")
_ = createCommand.RegisterFlagCompletionFunc(cgroupParentflagName, completion.AutocompleteDefault) _ = createCommand.RegisterFlagCompletionFunc(cgroupParentflagName, completion.AutocompleteDefault)
@ -185,6 +198,46 @@ func create(cmd *cobra.Command, args []string) error {
} }
} }
numCPU := sysinfo.NumCPU()
if numCPU == 0 {
numCPU = runtime.NumCPU()
}
if createOptions.Cpus > float64(numCPU) {
createOptions.Cpus = float64(numCPU)
}
copy := createOptions.CpusetCpus
cpuSet := createOptions.Cpus
if cpuSet == 0 {
cpuSet = float64(sysinfo.NumCPU())
}
ret, err := parsers.ParseUintList(copy)
copy = ""
if err != nil {
errors.Wrapf(err, "could not parse list")
}
var vals []int
for ind, val := range ret {
if val {
vals = append(vals, ind)
}
}
sort.Ints(vals)
for ind, core := range vals {
if core > int(cpuSet) {
if copy == "" {
copy = "0-" + strconv.Itoa(int(cpuSet))
createOptions.CpusetCpus = copy
break
} else {
createOptions.CpusetCpus = copy
break
}
} else if ind != 0 {
copy += "," + strconv.Itoa(core)
} else {
copy = "" + strconv.Itoa(core)
}
}
response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions) response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions)
if err != nil { if err != nil {
return err return err

View file

@ -23,6 +23,22 @@ Add a host to the /etc/hosts file shared between all containers in the pod.
Path to cgroups under which the cgroup for the pod will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist. Path to cgroups under which the cgroup for the pod will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.
#### **--cpus**=*amount*
Set the total number of CPUs delegated to the pod. Default is 0.000 which indicates that there is no limit on computation power.
#### **--cpuset-cpus**=*amount*
Limit the CPUs to support execution. First CPU is numbered 0. Unlike --cpus this is of type string and parsed as a list of numbers
Format is 0-3,0,1
Examples of the List Format:
0-4,9 # bits 0, 1, 2, 3, 4, and 9 set
0-2,7,12-14 # bits 0, 1, 2, 7, 12, 13, and 14 set
#### **--dns**=*ipaddr* #### **--dns**=*ipaddr*
Set custom DNS servers in the /etc/resolv.conf file that will be shared between all containers in the pod. A special option, "none" is allowed which disables creation of /etc/resolv.conf for the pod. Set custom DNS servers in the /etc/resolv.conf file that will be shared between all containers in the pod. A special option, "none" is allowed which disables creation of /etc/resolv.conf for the pod.

View file

@ -131,6 +131,5 @@ func (c *Container) validate() error {
if c.config.User == "" && (c.config.Spec.Process.User.UID != 0 || c.config.Spec.Process.User.GID != 0) { if c.config.User == "" && (c.config.Spec.Process.User.UID != 0 || c.config.Spec.Process.User.GID != 0) {
return errors.Wrapf(define.ErrInvalidArg, "please set User explicitly via WithUser() instead of in OCI spec directly") return errors.Wrapf(define.ErrInvalidArg, "please set User explicitly via WithUser() instead of in OCI spec directly")
} }
return nil return nil
} }

View file

@ -51,6 +51,12 @@ type InspectPodData struct {
// Containers gives a brief summary of all containers in the pod and // Containers gives a brief summary of all containers in the pod and
// their current status. // their current status.
Containers []InspectPodContainerInfo `json:"Containers,omitempty"` Containers []InspectPodContainerInfo `json:"Containers,omitempty"`
// CPUPeriod contains the CPU period of the pod
CPUPeriod uint64 `json:"cpu_period,omitempty"`
// CPUQuota contains the CPU quota of the pod
CPUQuota int64 `json:"cpu_quota,omitempty"`
// CPUSetCPUs contains linux specific CPU data for the pod
CPUSetCPUs string `json:"cpuset_cpus,omitempty"`
} }
// InspectPodInfraConfig contains the configuration of the pod's infra // InspectPodInfraConfig contains the configuration of the pod's infra
@ -91,6 +97,12 @@ type InspectPodInfraConfig struct {
Networks []string Networks []string
// NetworkOptions are additional options for each network // NetworkOptions are additional options for each network
NetworkOptions map[string][]string NetworkOptions map[string][]string
// CPUPeriod contains the CPU period of the pod
CPUPeriod uint64 `json:"cpu_period,omitempty"`
// CPUQuota contains the CPU quota of the pod
CPUQuota int64 `json:"cpu_quota,omitempty"`
// CPUSetCPUs contains linux specific CPU data for the container
CPUSetCPUs string `json:"cpuset_cpus,omitempty"`
} }
// InspectPodContainerInfo contains information on a container in a pod. // InspectPodContainerInfo contains information on a container in a pod.

View file

@ -20,6 +20,7 @@ import (
"github.com/containers/storage" "github.com/containers/storage"
"github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/idtools"
"github.com/cri-o/ocicni/pkg/ocicni" "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -559,7 +560,6 @@ func WithMaxLogSize(limit int64) CtrCreateOption {
if ctr.valid { if ctr.valid {
return define.ErrRuntimeFinalized return define.ErrRuntimeFinalized
} }
ctr.config.LogSize = limit ctr.config.LogSize = limit
return nil return nil
@ -867,7 +867,6 @@ func WithMountNSFrom(nsCtr *Container) CtrCreateOption {
if err := checkDependencyContainer(nsCtr, ctr); err != nil { if err := checkDependencyContainer(nsCtr, ctr); err != nil {
return err return err
} }
ctr.config.MountNsCtr = nsCtr.ID() ctr.config.MountNsCtr = nsCtr.ID()
return nil return nil
@ -2359,3 +2358,42 @@ func WithVolatile() CtrCreateOption {
return nil return nil
} }
} }
// WithPodCPUPAQ takes the given cpu period and quota and inserts them in the proper place.
func WithPodCPUPAQ(period uint64, quota int64) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
if pod.CPUPeriod() != 0 && pod.CPUQuota() != 0 {
pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{
Period: &period,
Quota: &quota,
}
} else {
pod.config.InfraContainer.ResourceLimits = &specs.LinuxResources{}
pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{
Period: &period,
Quota: &quota,
}
}
return nil
}
}
// WithPodCPUSetCPUS computes and sets the Cpus linux resource string which determines the amount of cores, from those available, we are allowed to execute on
func WithPodCPUSetCPUs(inp string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
if pod.ResourceLim().CPU.Period != nil {
pod.config.InfraContainer.ResourceLimits.CPU.Cpus = inp
} else {
pod.config.InfraContainer.ResourceLimits = &specs.LinuxResources{}
pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{}
pod.config.InfraContainer.ResourceLimits.CPU.Cpus = inp
}
return nil
}
}

View file

@ -1,12 +1,14 @@
package libpod package libpod
import ( import (
"context"
"net" "net"
"time" "time"
"github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/lock" "github.com/containers/podman/v3/libpod/lock"
"github.com/cri-o/ocicni/pkg/ocicni" "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -91,25 +93,26 @@ type podState struct {
// Generally speaking, aside from those two exceptions, these options will set // Generally speaking, aside from those two exceptions, these options will set
// the equivalent field in the container's configuration. // the equivalent field in the container's configuration.
type InfraContainerConfig struct { type InfraContainerConfig struct {
ConmonPidFile string `json:"conmonPidFile"` ConmonPidFile string `json:"conmonPidFile"`
HasInfraContainer bool `json:"makeInfraContainer"` HasInfraContainer bool `json:"makeInfraContainer"`
NoNetwork bool `json:"noNetwork,omitempty"` NoNetwork bool `json:"noNetwork,omitempty"`
HostNetwork bool `json:"infraHostNetwork,omitempty"` HostNetwork bool `json:"infraHostNetwork,omitempty"`
PortBindings []ocicni.PortMapping `json:"infraPortBindings"` PortBindings []ocicni.PortMapping `json:"infraPortBindings"`
StaticIP net.IP `json:"staticIP,omitempty"` StaticIP net.IP `json:"staticIP,omitempty"`
StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"` StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"`
UseImageResolvConf bool `json:"useImageResolvConf,omitempty"` UseImageResolvConf bool `json:"useImageResolvConf,omitempty"`
DNSServer []string `json:"dnsServer,omitempty"` DNSServer []string `json:"dnsServer,omitempty"`
DNSSearch []string `json:"dnsSearch,omitempty"` DNSSearch []string `json:"dnsSearch,omitempty"`
DNSOption []string `json:"dnsOption,omitempty"` DNSOption []string `json:"dnsOption,omitempty"`
UseImageHosts bool `json:"useImageHosts,omitempty"` UseImageHosts bool `json:"useImageHosts,omitempty"`
HostAdd []string `json:"hostsAdd,omitempty"` HostAdd []string `json:"hostsAdd,omitempty"`
Networks []string `json:"networks,omitempty"` Networks []string `json:"networks,omitempty"`
ExitCommand []string `json:"exitCommand,omitempty"` ExitCommand []string `json:"exitCommand,omitempty"`
InfraImage string `json:"infraImage,omitempty"` InfraImage string `json:"infraImage,omitempty"`
InfraCommand []string `json:"infraCommand,omitempty"` InfraCommand []string `json:"infraCommand,omitempty"`
Slirp4netns bool `json:"slirp4netns,omitempty"` Slirp4netns bool `json:"slirp4netns,omitempty"`
NetworkOptions map[string][]string `json:"network_options,omitempty"` NetworkOptions map[string][]string `json:"network_options,omitempty"`
ResourceLimits *specs.LinuxResources `json:"resource_limits,omitempty"`
} }
// ID retrieves the pod's ID // ID retrieves the pod's ID
@ -128,6 +131,45 @@ func (p *Pod) Namespace() string {
return p.config.Namespace return p.config.Namespace
} }
// ResourceLim returns the cpuset resource limits for the pod
func (p *Pod) ResourceLim() *specs.LinuxResources {
resCopy := &specs.LinuxResources{}
if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil {
return nil
}
if resCopy != nil && resCopy.CPU != nil {
return resCopy
}
empty := &specs.LinuxResources{
CPU: &specs.LinuxCPU{},
}
return empty
}
// CPUPeriod returns the pod CPU period
func (p *Pod) CPUPeriod() uint64 {
resCopy := &specs.LinuxResources{}
if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil {
return 0
}
if resCopy != nil && resCopy.CPU != nil && resCopy.CPU.Period != nil {
return *resCopy.CPU.Period
}
return 0
}
// CPUQuota returns the pod CPU quota
func (p *Pod) CPUQuota() int64 {
resCopy := &specs.LinuxResources{}
if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil {
return 0
}
if resCopy != nil && resCopy.CPU != nil && resCopy.CPU.Quota != nil {
return *resCopy.CPU.Quota
}
return 0
}
// Labels returns the pod's labels // Labels returns the pod's labels
func (p *Pod) Labels() map[string]string { func (p *Pod) Labels() map[string]string {
labels := make(map[string]string) labels := make(map[string]string)
@ -208,7 +250,31 @@ func (p *Pod) CgroupPath() (string, error) {
if err := p.updatePod(); err != nil { if err := p.updatePod(); err != nil {
return "", err return "", err
} }
if p.state.CgroupPath != "" {
return p.state.CgroupPath, nil
}
if !p.HasInfraContainer() {
return "", errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container")
}
id := p.state.InfraContainerID
if id != "" {
ctr, err := p.runtime.state.Container(id)
if err != nil {
return "", errors.Wrapf(err, "could not get infra")
}
if ctr != nil {
ctr.Start(context.Background(), false)
cgroupPath, err := ctr.CGroupPath()
if err != nil {
return "", errors.Wrapf(err, "could not get container cgroup")
}
p.state.CgroupPath = cgroupPath
p.save()
return cgroupPath, nil
}
}
return p.state.CgroupPath, nil return p.state.CgroupPath, nil
} }

View file

@ -538,6 +538,9 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC.String() infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC.String()
infraConfig.NoManageResolvConf = p.config.InfraContainer.UseImageResolvConf infraConfig.NoManageResolvConf = p.config.InfraContainer.UseImageResolvConf
infraConfig.NoManageHosts = p.config.InfraContainer.UseImageHosts infraConfig.NoManageHosts = p.config.InfraContainer.UseImageHosts
infraConfig.CPUPeriod = p.CPUPeriod()
infraConfig.CPUQuota = p.CPUQuota()
infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus
if len(p.config.InfraContainer.DNSServer) > 0 { if len(p.config.InfraContainer.DNSServer) > 0 {
infraConfig.DNSServer = make([]string, 0, len(p.config.InfraContainer.DNSServer)) infraConfig.DNSServer = make([]string, 0, len(p.config.InfraContainer.DNSServer))
@ -581,6 +584,9 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
SharedNamespaces: sharesNS, SharedNamespaces: sharesNS,
NumContainers: uint(len(containers)), NumContainers: uint(len(containers)),
Containers: ctrs, Containers: ctrs,
CPUSetCPUs: p.ResourceLim().CPU.Cpus,
CPUPeriod: p.CPUPeriod(),
CPUQuota: p.CPUQuota(),
} }
return &inspectData, nil return &inspectData, nil

View file

@ -146,7 +146,6 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
options = append(options, WithExitCommand(p.config.InfraContainer.ExitCommand)) options = append(options, WithExitCommand(p.config.InfraContainer.ExitCommand))
} }
} }
g.SetRootReadonly(true) g.SetRootReadonly(true)
g.SetProcessArgs(infraCtrCommand) g.SetProcessArgs(infraCtrCommand)
@ -173,7 +172,6 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
// Ignore mqueue sysctls if not sharing IPC // Ignore mqueue sysctls if not sharing IPC
if !p.config.UsePodIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") { if !p.config.UsePodIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") {
logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace for pod is unused", sysctlKey, sysctlVal) logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace for pod is unused", sysctlKey, sysctlVal)
continue continue
} }
@ -188,7 +186,6 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace for pod is unused", sysctlKey, sysctlVal) logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace for pod is unused", sysctlKey, sysctlVal)
continue continue
} }
g.AddLinuxSysctl(sysctlKey, sysctlVal) g.AddLinuxSysctl(sysctlKey, sysctlVal)
} }
@ -200,7 +197,11 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
if len(p.config.InfraContainer.ConmonPidFile) > 0 { if len(p.config.InfraContainer.ConmonPidFile) > 0 {
options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile)) options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile))
} }
newRes := new(spec.LinuxResources)
newRes.CPU = new(spec.LinuxCPU)
newRes.CPU = p.ResourceLim().CPU
g.Config.Linux.Resources.CPU = newRes.CPU
return r.newContainer(ctx, g.Config, options...) return r.newContainer(ctx, g.Config, options...)
} }
@ -211,7 +212,6 @@ func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container,
if !r.valid { if !r.valid {
return nil, define.ErrRuntimeStopped return nil, define.ErrRuntimeStopped
} }
imageName := p.config.InfraContainer.InfraImage imageName := p.config.InfraContainer.InfraImage
if imageName == "" { if imageName == "" {
imageName = r.config.Engine.InfraImage imageName = r.config.Engine.InfraImage

View file

@ -7,6 +7,8 @@ import (
"github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
"github.com/opencontainers/runtime-spec/specs-go"
) )
type PodKillOptions struct { type PodKillOptions struct {
@ -116,13 +118,35 @@ type PodCreateOptions struct {
Name string Name string
Net *NetOptions Net *NetOptions
Share []string Share []string
Cpus float64
CpusetCpus string
} }
type PodCreateReport struct { type PodCreateReport struct {
Id string //nolint Id string //nolint
} }
func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) { func (p *PodCreateOptions) CPULimits() *specs.LinuxCPU {
cpu := &specs.LinuxCPU{}
hasLimits := false
if p.Cpus != 0 {
period, quota := util.CoresToPeriodAndQuota(p.Cpus)
cpu.Period = &period
cpu.Quota = &quota
hasLimits = true
}
if p.CpusetCpus != "" {
cpu.Cpus = p.CpusetCpus
hasLimits = true
}
if !hasLimits {
return cpu
}
return cpu
}
func (p *PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) error {
// Basic Config // Basic Config
s.Name = p.Name s.Name = p.Name
s.Hostname = p.Hostname s.Hostname = p.Hostname
@ -156,6 +180,21 @@ func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) {
// Cgroup // Cgroup
s.CgroupParent = p.CGroupParent s.CgroupParent = p.CGroupParent
// Resource config
cpuDat := p.CPULimits()
if s.ResourceLimits == nil {
s.ResourceLimits = &specs.LinuxResources{}
s.ResourceLimits.CPU = &specs.LinuxCPU{}
}
if cpuDat != nil {
s.ResourceLimits.CPU = cpuDat
if p.Cpus != 0 {
s.CPUPeriod = *cpuDat.Period
s.CPUQuota = *cpuDat.Quota
}
}
return nil
} }
type PodPruneOptions struct { type PodPruneOptions struct {

View file

@ -347,7 +347,6 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
options = append(options, libpod.WithLogDriver(s.LogConfiguration.Driver)) options = append(options, libpod.WithLogDriver(s.LogConfiguration.Driver))
} }
} }
// Security options // Security options
if len(s.SelinuxOpts) > 0 { if len(s.SelinuxOpts) > 0 {
options = append(options, libpod.WithSecLabels(s.SelinuxOpts)) options = append(options, libpod.WithSecLabels(s.SelinuxOpts))

View file

@ -54,6 +54,14 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod
if len(p.Name) > 0 { if len(p.Name) > 0 {
options = append(options, libpod.WithPodName(p.Name)) options = append(options, libpod.WithPodName(p.Name))
} }
if p.ResourceLimits != nil && p.ResourceLimits.CPU != nil && p.ResourceLimits.CPU.Period != nil && p.ResourceLimits.CPU.Quota != nil {
if *p.ResourceLimits.CPU.Period != 0 || *p.ResourceLimits.CPU.Quota != 0 {
options = append(options, libpod.WithPodCPUPAQ((*p.ResourceLimits.CPU.Period), (*p.ResourceLimits.CPU.Quota)))
}
}
if p.ResourceLimits != nil && p.ResourceLimits.CPU != nil && p.ResourceLimits.CPU.Cpus != "" {
options = append(options, libpod.WithPodCPUSetCPUs(p.ResourceLimits.CPU.Cpus))
}
if len(p.Hostname) > 0 { if len(p.Hostname) > 0 {
options = append(options, libpod.WithPodHostname(p.Hostname)) options = append(options, libpod.WithPodHostname(p.Hostname))
} }

View file

@ -2,6 +2,8 @@ package specgen
import ( import (
"net" "net"
spec "github.com/opencontainers/runtime-spec/specs-go"
) )
// PodBasicConfig contains basic configuration options for pods. // PodBasicConfig contains basic configuration options for pods.
@ -155,6 +157,16 @@ type PodSpecGenerator struct {
PodBasicConfig PodBasicConfig
PodNetworkConfig PodNetworkConfig
PodCgroupConfig PodCgroupConfig
PodResourceConfig
}
type PodResourceConfig struct {
// ResourceLimits contains linux specific CPU data for the pod
ResourceLimits *spec.LinuxResources `json:"resource_limits,omitempty"`
// CPU period of the cpuset, determined by --cpus
CPUPeriod uint64 `json:"cpu_period,omitempty"`
// CPU quota of the cpuset, determined by --cpus
CPUQuota int64 `json:"cpu_quota,omitempty"`
} }
// NewPodSpecGenerator creates a new pod spec // NewPodSpecGenerator creates a new pod spec

View file

@ -470,6 +470,10 @@ type ContainerResourceConfig struct {
// that are used to configure cgroup v2. // that are used to configure cgroup v2.
// Optional. // Optional.
CgroupConf map[string]string `json:"unified,omitempty"` CgroupConf map[string]string `json:"unified,omitempty"`
// CPU period of the cpuset, determined by --cpus
CPUPeriod uint64 `json:"cpu_period,omitempty"`
// CPU quota of the cpuset, determined by --cpus
CPUQuota int64 `json:"cpu_quota,omitempty"`
} }
// ContainerHealthCheckConfig describes a container healthcheck with attributes // ContainerHealthCheckConfig describes a container healthcheck with attributes

View file

@ -5,9 +5,12 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"github.com/containers/common/pkg/sysinfo"
"github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/util"
. "github.com/containers/podman/v3/test/utils" . "github.com/containers/podman/v3/test/utils"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@ -515,4 +518,45 @@ ENTRYPOINT ["sleep","99999"]
Expect(create.ExitCode()).To(BeZero()) Expect(create.ExitCode()).To(BeZero())
}) })
It("podman pod create --cpus", func() {
podName := "testPod"
numCPU := float64(sysinfo.NumCPU())
period, quota := util.CoresToPeriodAndQuota(numCPU)
numCPUStr := strconv.Itoa(int(numCPU))
podCreate := podmanTest.Podman([]string{"pod", "create", "--cpus", numCPUStr, "--name", podName})
podCreate.WaitWithDefaultTimeout()
Expect(podCreate.ExitCode()).To(Equal(0))
contCreate := podmanTest.Podman([]string{"container", "create", "--pod", podName, "alpine"})
contCreate.WaitWithDefaultTimeout()
Expect(podCreate.ExitCode()).To(Equal(0))
podInspect := podmanTest.Podman([]string{"pod", "inspect", podName})
podInspect.WaitWithDefaultTimeout()
Expect(podInspect.ExitCode()).To(Equal(0))
podJSON := podInspect.InspectPodToJSON()
Expect(podJSON.CPUPeriod).To(Equal(period))
Expect(podJSON.CPUQuota).To(Equal(quota))
})
It("podman pod create --cpuset-cpus", func() {
podName := "testPod"
ctrName := "testCtr"
numCPU := float64(sysinfo.NumCPU())
numCPUStr := strconv.Itoa(int(numCPU))
in := "0-" + numCPUStr
podCreate := podmanTest.Podman([]string{"pod", "create", "--cpuset-cpus", in, "--name", podName})
podCreate.WaitWithDefaultTimeout()
Expect(podCreate.ExitCode()).To(Equal(0))
contCreate := podmanTest.Podman([]string{"container", "create", "--name", ctrName, "--pod", podName, "alpine"})
contCreate.WaitWithDefaultTimeout()
Expect(podCreate.ExitCode()).To(Equal(0))
podInspect := podmanTest.Podman([]string{"pod", "inspect", podName})
podInspect.WaitWithDefaultTimeout()
Expect(podInspect.ExitCode()).To(Equal(0))
podJSON := podInspect.InspectPodToJSON()
Expect(podJSON.CPUSetCPUs).To(Equal(in))
})
}) })