Skip to content

Commit

Permalink
Merge pull request #25010 from robertgzr/play-cdi
Browse files Browse the repository at this point in the history
Add kube play support for CDI resource allocation
  • Loading branch information
openshift-merge-bot[bot] authored Jan 14, 2025
2 parents 9f1fee2 + 0d0a78c commit ec6b035
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 26 deletions.
110 changes: 84 additions & 26 deletions pkg/specgen/generate/kube/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"sigs.k8s.io/yaml"
cdiparser "tags.cncf.io/container-device-interface/pkg/parser"
)

func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions, publishAllPorts bool, podYAML *v1.PodTemplateSpec) (entities.PodCreateOptions, error) {
Expand Down Expand Up @@ -276,36 +277,14 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
// but apply to the containers with the prefixed name
s.SeccompProfilePath = opts.SeccompPaths.FindForContainer(opts.Container.Name)

s.ResourceLimits = &spec.LinuxResources{}
milliCPU := opts.Container.Resources.Limits.Cpu().MilliValue()
if milliCPU > 0 {
period, quota := util.CoresToPeriodAndQuota(float64(milliCPU) / 1000)
s.ResourceLimits.CPU = &spec.LinuxCPU{
Quota: &quota,
Period: &period,
}
}

limit, err := quantityToInt64(opts.Container.Resources.Limits.Memory())
err = setupContainerResources(s, opts.Container)
if err != nil {
return nil, fmt.Errorf("failed to set memory limit: %w", err)
return nil, fmt.Errorf("failed to configure container resources: %w", err)
}

memoryRes, err := quantityToInt64(opts.Container.Resources.Requests.Memory())
err = setupContainerDevices(s, opts.Container)
if err != nil {
return nil, fmt.Errorf("failed to set memory reservation: %w", err)
}

if limit > 0 || memoryRes > 0 {
s.ResourceLimits.Memory = &spec.LinuxMemory{}
}

if limit > 0 {
s.ResourceLimits.Memory.Limit = &limit
}

if memoryRes > 0 {
s.ResourceLimits.Memory.Reservation = &memoryRes
return nil, fmt.Errorf("failed to configure container devices: %w", err)
}

ulimitVal, ok := opts.Annotations[define.UlimitAnnotation]
Expand Down Expand Up @@ -840,6 +819,85 @@ func makeHealthCheck(inCmd string, interval int32, retries int32, timeout int32,
return &hc, nil
}

func setupContainerResources(s *specgen.SpecGenerator, containerYAML v1.Container) error {
s.ResourceLimits = &spec.LinuxResources{}
milliCPU := containerYAML.Resources.Limits.Cpu().MilliValue()
if milliCPU > 0 {
period, quota := util.CoresToPeriodAndQuota(float64(milliCPU) / 1000)
s.ResourceLimits.CPU = &spec.LinuxCPU{
Quota: &quota,
Period: &period,
}
}

limit, err := quantityToInt64(containerYAML.Resources.Limits.Memory())
if err != nil {
return fmt.Errorf("failed to set memory limit: %w", err)
}

memoryRes, err := quantityToInt64(containerYAML.Resources.Requests.Memory())
if err != nil {
return fmt.Errorf("failed to set memory reservation: %w", err)
}

if limit > 0 || memoryRes > 0 {
s.ResourceLimits.Memory = &spec.LinuxMemory{}
}

if limit > 0 {
s.ResourceLimits.Memory.Limit = &limit
}

if memoryRes > 0 {
s.ResourceLimits.Memory.Reservation = &memoryRes
}

return nil
}

const PodmanDeviceResourcePrefix = "io.podman/device"

func setupContainerDevices(s *specgen.SpecGenerator, containerYAML v1.Container) error {
s.Devices = make([]spec.LinuxDevice, 0)
// avoid duplicates
devices := make(map[string]bool, 0)

parse := func(device string) error {
vendor, class, name := cdiparser.ParseDevice(device)
if vendor == "" {
return nil
}

if err := cdiparser.ValidateDeviceName(name); err != nil {
// handle internal "fake" CDI
if vendor == "podman.io" && class == "device" {
device = name
} else {
return fmt.Errorf("not a qualified name %v: %w", device, err)
}
}

if _, ok := devices[device]; !ok {
devices[device] = true
s.Devices = append(s.Devices, spec.LinuxDevice{Path: device})
}
return nil
}

for key := range containerYAML.Resources.Requests {
if err := parse(key.String()); err != nil {
return err
}
}
for key := range containerYAML.Resources.Limits {
if err := parse(key.String()); err != nil {
return err
}
}

return nil
}

func setupSecurityContext(s *specgen.SpecGenerator, securityContext *v1.SecurityContext, podSecurityContext *v1.PodSecurityContext) {
if securityContext == nil {
securityContext = &v1.SecurityContext{}
Expand Down
65 changes: 65 additions & 0 deletions pkg/specgen/generate/kube/play_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/containers/podman/v5/pkg/k8s.io/apimachinery/pkg/util/intstr"
"github.com/containers/podman/v5/pkg/specgen"
"github.com/docker/docker/pkg/meminfo"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/yaml"
)
Expand Down Expand Up @@ -1400,3 +1401,67 @@ func TestTCPLivenessProbe(t *testing.T) {
})
}
}

func TestDeviceResource(t *testing.T) {
tests := []struct {
name string
specGenerator specgen.SpecGenerator
container v1.Container
succeed bool
devices []spec.LinuxDevice
}{
{
"ParseQualifiedCDI",
specgen.SpecGenerator{},
v1.Container{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"nvidia.com/gpu=0": resource.MustParse("1"),
},
},
},
true,
[]spec.LinuxDevice{
{Path: "nvidia.com/gpu=0"},
},
},
{
"ParsePodmanDeviceResource",
specgen.SpecGenerator{},
v1.Container{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"podman.io/device=/dev/kmsg": resource.MustParse("1"),
},
},
},
true,
[]spec.LinuxDevice{
{Path: "/dev/kmsg"},
},
},
{
"InvalidCDI",
specgen.SpecGenerator{},
v1.Container{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"foobar.net/class=///": resource.MustParse("1"),
},
},
},
false,
[]spec.LinuxDevice{},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := setupContainerDevices(&test.specGenerator, test.container)
assert.Equal(t, err == nil, test.succeed)
if err == nil {
assert.Equal(t, test.specGenerator.Devices, test.devices)
}
})
}
}

0 comments on commit ec6b035

Please sign in to comment.