diff options
author | cdoern <cbdoer23@g.holycross.edu> | 2021-06-17 17:36:35 -0400 |
---|---|---|
committer | cdoern <cbdoer23@g.holycross.edu> | 2021-06-23 13:47:57 -0400 |
commit | bbd085ad1e3cf9c5b543c907ad7014ccf8a5cb34 (patch) | |
tree | 14dd2d240f32586a624f6a786d8127b8bfb95017 | |
parent | 510509bafcdd055b4a6819ed300db7292c6fb6b8 (diff) | |
download | podman-bbd085ad1e3cf9c5b543c907ad7014ccf8a5cb34.tar.gz podman-bbd085ad1e3cf9c5b543c907ad7014ccf8a5cb34.tar.bz2 podman-bbd085ad1e3cf9c5b543c907ad7014ccf8a5cb34.zip |
Podman Pod Create --cpus and --cpuset-cpus flags
Added logic and handling for two new Podman pod create Flags.
--cpus specifies the total number of cores on which the pod can execute, this
is a combination of the period and quota for the CPU.
--cpuset-cpus is a string value which determines of these available cores,
how many we will truly execute on.
Signed-off-by: cdoern <cbdoer23@g.holycross.edu>
-rw-r--r-- | cmd/podman/containers/create.go | 3 | ||||
-rw-r--r-- | cmd/podman/pods/create.go | 53 | ||||
-rw-r--r-- | docs/source/markdown/podman-pod-create.1.md | 16 | ||||
-rw-r--r-- | libpod/container_validate.go | 1 | ||||
-rw-r--r-- | libpod/define/pod_inspect.go | 12 | ||||
-rw-r--r-- | libpod/options.go | 42 | ||||
-rw-r--r-- | libpod/pod.go | 104 | ||||
-rw-r--r-- | libpod/pod_api.go | 6 | ||||
-rw-r--r-- | libpod/runtime_pod_infra_linux.go | 8 | ||||
-rw-r--r-- | pkg/domain/entities/pods.go | 41 | ||||
-rw-r--r-- | pkg/specgen/generate/container_create.go | 1 | ||||
-rw-r--r-- | pkg/specgen/generate/pod_create.go | 8 | ||||
-rw-r--r-- | pkg/specgen/podspecgen.go | 12 | ||||
-rw-r--r-- | pkg/specgen/specgen.go | 4 | ||||
-rw-r--r-- | test/e2e/pod_create_test.go | 44 |
15 files changed, 326 insertions, 29 deletions
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 68a17abd0..ecaef5c0a 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -225,7 +225,6 @@ func createInit(c *cobra.Command) error { } cliVals.Env = env } - 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) } @@ -316,6 +315,8 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions Net: netOpts, CreateCommand: os.Args, 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. s.ContainerBasicConfig.Hostname = "" diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 735dfa78c..03e3ffaa0 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -5,9 +5,13 @@ import ( "fmt" "io/ioutil" "os" + "runtime" + "sort" + "strconv" "strings" "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/parse" "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/specgen" "github.com/containers/podman/v3/pkg/util" + "github.com/docker/docker/pkg/parsers" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -55,6 +60,14 @@ func init() { 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" flags.StringVar(&createOptions.CGroupParent, cgroupParentflagName, "", "Set parent cgroup for the pod") _ = 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) if err != nil { return err diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md index 4b890a7af..653b0f6f1 100644 --- a/docs/source/markdown/podman-pod-create.1.md +++ b/docs/source/markdown/podman-pod-create.1.md @@ -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. +#### **--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* 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. diff --git a/libpod/container_validate.go b/libpod/container_validate.go index aae96ae85..6ff46f1b1 100644 --- a/libpod/container_validate.go +++ b/libpod/container_validate.go @@ -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) { return errors.Wrapf(define.ErrInvalidArg, "please set User explicitly via WithUser() instead of in OCI spec directly") } - return nil } diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go index 2fa91166f..67f075b3c 100644 --- a/libpod/define/pod_inspect.go +++ b/libpod/define/pod_inspect.go @@ -51,6 +51,12 @@ type InspectPodData struct { // Containers gives a brief summary of all containers in the pod and // their current status. 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 @@ -91,6 +97,12 @@ type InspectPodInfraConfig struct { Networks []string // NetworkOptions are additional options for each network 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. diff --git a/libpod/options.go b/libpod/options.go index f2468e41b..b12153512 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -20,6 +20,7 @@ import ( "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -559,7 +560,6 @@ func WithMaxLogSize(limit int64) CtrCreateOption { if ctr.valid { return define.ErrRuntimeFinalized } - ctr.config.LogSize = limit return nil @@ -867,7 +867,6 @@ func WithMountNSFrom(nsCtr *Container) CtrCreateOption { if err := checkDependencyContainer(nsCtr, ctr); err != nil { return err } - ctr.config.MountNsCtr = nsCtr.ID() return nil @@ -2359,3 +2358,42 @@ func WithVolatile() CtrCreateOption { 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: "a, + } + } else { + pod.config.InfraContainer.ResourceLimits = &specs.LinuxResources{} + pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{ + Period: &period, + Quota: "a, + } + } + 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 + } +} diff --git a/libpod/pod.go b/libpod/pod.go index dce2a0c1c..d7a9b15d9 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -1,12 +1,14 @@ package libpod import ( + "context" "net" "time" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/lock" "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -91,25 +93,26 @@ type podState struct { // Generally speaking, aside from those two exceptions, these options will set // the equivalent field in the container's configuration. type InfraContainerConfig struct { - ConmonPidFile string `json:"conmonPidFile"` - HasInfraContainer bool `json:"makeInfraContainer"` - NoNetwork bool `json:"noNetwork,omitempty"` - HostNetwork bool `json:"infraHostNetwork,omitempty"` - PortBindings []ocicni.PortMapping `json:"infraPortBindings"` - StaticIP net.IP `json:"staticIP,omitempty"` - StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"` - UseImageResolvConf bool `json:"useImageResolvConf,omitempty"` - DNSServer []string `json:"dnsServer,omitempty"` - DNSSearch []string `json:"dnsSearch,omitempty"` - DNSOption []string `json:"dnsOption,omitempty"` - UseImageHosts bool `json:"useImageHosts,omitempty"` - HostAdd []string `json:"hostsAdd,omitempty"` - Networks []string `json:"networks,omitempty"` - ExitCommand []string `json:"exitCommand,omitempty"` - InfraImage string `json:"infraImage,omitempty"` - InfraCommand []string `json:"infraCommand,omitempty"` - Slirp4netns bool `json:"slirp4netns,omitempty"` - NetworkOptions map[string][]string `json:"network_options,omitempty"` + ConmonPidFile string `json:"conmonPidFile"` + HasInfraContainer bool `json:"makeInfraContainer"` + NoNetwork bool `json:"noNetwork,omitempty"` + HostNetwork bool `json:"infraHostNetwork,omitempty"` + PortBindings []ocicni.PortMapping `json:"infraPortBindings"` + StaticIP net.IP `json:"staticIP,omitempty"` + StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"` + UseImageResolvConf bool `json:"useImageResolvConf,omitempty"` + DNSServer []string `json:"dnsServer,omitempty"` + DNSSearch []string `json:"dnsSearch,omitempty"` + DNSOption []string `json:"dnsOption,omitempty"` + UseImageHosts bool `json:"useImageHosts,omitempty"` + HostAdd []string `json:"hostsAdd,omitempty"` + Networks []string `json:"networks,omitempty"` + ExitCommand []string `json:"exitCommand,omitempty"` + InfraImage string `json:"infraImage,omitempty"` + InfraCommand []string `json:"infraCommand,omitempty"` + Slirp4netns bool `json:"slirp4netns,omitempty"` + NetworkOptions map[string][]string `json:"network_options,omitempty"` + ResourceLimits *specs.LinuxResources `json:"resource_limits,omitempty"` } // ID retrieves the pod's ID @@ -128,6 +131,45 @@ func (p *Pod) Namespace() string { 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 func (p *Pod) Labels() map[string]string { labels := make(map[string]string) @@ -208,7 +250,31 @@ func (p *Pod) CgroupPath() (string, error) { if err := p.updatePod(); err != nil { 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 } diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 14fe8276c..d8f5d15f8 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -538,6 +538,9 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC.String() infraConfig.NoManageResolvConf = p.config.InfraContainer.UseImageResolvConf 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 { infraConfig.DNSServer = make([]string, 0, len(p.config.InfraContainer.DNSServer)) @@ -581,6 +584,9 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { SharedNamespaces: sharesNS, NumContainers: uint(len(containers)), Containers: ctrs, + CPUSetCPUs: p.ResourceLim().CPU.Cpus, + CPUPeriod: p.CPUPeriod(), + CPUQuota: p.CPUQuota(), } return &inspectData, nil diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index c20153c8d..6b002f65a 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -146,7 +146,6 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm options = append(options, WithExitCommand(p.config.InfraContainer.ExitCommand)) } } - g.SetRootReadonly(true) 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 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) - 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) continue } - 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 { 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...) } @@ -211,7 +212,6 @@ func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container, if !r.valid { return nil, define.ErrRuntimeStopped } - imageName := p.config.InfraContainer.InfraImage if imageName == "" { imageName = r.config.Engine.InfraImage diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index 88055454f..35f940bca 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -7,6 +7,8 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/util" + "github.com/opencontainers/runtime-spec/specs-go" ) type PodKillOptions struct { @@ -116,13 +118,35 @@ type PodCreateOptions struct { Name string Net *NetOptions Share []string + Cpus float64 + CpusetCpus string } type PodCreateReport struct { 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 = "a + 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 s.Name = p.Name s.Hostname = p.Hostname @@ -156,6 +180,21 @@ func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) { // Cgroup 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 { diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index a0f5cc7e6..b569f8390 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -346,7 +346,6 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. options = append(options, libpod.WithLogDriver(s.LogConfiguration.Driver)) } } - // Security options if len(s.SelinuxOpts) > 0 { options = append(options, libpod.WithSecLabels(s.SelinuxOpts)) diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 07c56b799..023ebb41e 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -54,6 +54,14 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod if len(p.Name) > 0 { 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 { options = append(options, libpod.WithPodHostname(p.Hostname)) } diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 7d771f5bb..000a787ea 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -2,6 +2,8 @@ package specgen import ( "net" + + spec "github.com/opencontainers/runtime-spec/specs-go" ) // PodBasicConfig contains basic configuration options for pods. @@ -155,6 +157,16 @@ type PodSpecGenerator struct { PodBasicConfig PodNetworkConfig 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 diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 2815bdebb..c5cc726d7 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -470,6 +470,10 @@ type ContainerResourceConfig struct { // that are used to configure cgroup v2. // Optional. 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 diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index a70d9f13f..e437c98e6 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -5,9 +5,12 @@ import ( "io/ioutil" "os" "path/filepath" + "strconv" "strings" + "github.com/containers/common/pkg/sysinfo" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/util" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -515,4 +518,45 @@ ENTRYPOINT ["sleep","99999"] 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)) + }) }) |