From d2dd708de134cfe0617821142557b3e55b0bdf64 Mon Sep 17 00:00:00 2001 From: Samuel Karp Date: Thu, 24 Aug 2023 16:04:50 -0700 Subject: [PATCH] Add support for rlimits Signed-off-by: Samuel Karp --- README.md | 2 ++ pkg/adaptation/adaptation_suite_test.go | 20 +++++++++-- pkg/adaptation/api.go | 1 + pkg/adaptation/result.go | 36 +++++++++++++++++++ pkg/api/adjustment.go | 15 ++++++++ pkg/runtime-tools/generate/generate.go | 17 +++++++++ .../generate/generate_suite_test.go | 35 ++++++++++++++++++ 7 files changed, 124 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 55a61885..bbc467a5 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,7 @@ The following pieces of container metadata are available to plugins in NRI: - environment variables - mounts - OCI hooks + - rlimits - linux - namespace IDs - devices @@ -212,6 +213,7 @@ container parameters: - mounts - environment variables - OCI hooks + - rlimits - linux - devices - resources diff --git a/pkg/adaptation/adaptation_suite_test.go b/pkg/adaptation/adaptation_suite_test.go index b741fe9a..fca19497 100644 --- a/pkg/adaptation/adaptation_suite_test.go +++ b/pkg/adaptation/adaptation_suite_test.go @@ -26,10 +26,11 @@ import ( "sigs.k8s.io/yaml" - nri "github.com/containerd/nri/pkg/adaptation" - "github.com/containerd/nri/pkg/api" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + + nri "github.com/containerd/nri/pkg/adaptation" + "github.com/containerd/nri/pkg/api" ) var _ = Describe("Configuration", func() { @@ -484,6 +485,9 @@ var _ = Describe("Plugin container creation adjustments", func() { } a.AddDevice(dev) + case "rlimit": + a.AddRlimit("nofile", 456, 123) + case "resources/cpu": a.SetLinuxCPUShares(123) a.SetLinuxCPUQuota(456) @@ -623,6 +627,11 @@ var _ = Describe("Plugin container creation adjustments", func() { }, }, ), + Entry("adjust rlimits", "rlimit", + &api.ContainerAdjustment{ + Rlimits: []*api.POSIXRlimit{{Type: "nofile", Soft: 123, Hard: 456}}, + }, + ), Entry("adjust CPU resources", "resources/cpu", &api.ContainerAdjustment{ Linux: &api.LinuxContainerAdjustment{ @@ -1847,6 +1856,7 @@ func stripAdjustment(a *api.ContainerAdjustment) *api.ContainerAdjustment { stripMounts(a) stripEnv(a) stripHooks(a) + stripRlimits(a) stripLinuxAdjustment(a) return a } @@ -1885,6 +1895,12 @@ func stripHooks(a *api.ContainerAdjustment) { } } +func stripRlimits(a *api.ContainerAdjustment) { + if len(a.Rlimits) == 0 { + a.Rlimits = nil + } +} + func stripLinuxAdjustment(a *api.ContainerAdjustment) { if a.Linux == nil { return diff --git a/pkg/adaptation/api.go b/pkg/adaptation/api.go index bc8ada06..49240598 100644 --- a/pkg/adaptation/api.go +++ b/pkg/adaptation/api.go @@ -81,6 +81,7 @@ type ( HugepageLimit = api.HugepageLimit Hooks = api.Hooks Hook = api.Hook + POSIXRlimit = api.POSIXRlimit EventMask = api.EventMask ) diff --git a/pkg/adaptation/result.go b/pkg/adaptation/result.go index a6188ef5..80f15084 100644 --- a/pkg/adaptation/result.go +++ b/pkg/adaptation/result.go @@ -56,6 +56,9 @@ func collectCreateContainerResult(request *CreateContainerRequest) *result { if request.Container.Hooks == nil { request.Container.Hooks = &Hooks{} } + if request.Container.Rlimits == nil { + request.Container.Rlimits = []*POSIXRlimit{} + } if request.Container.Linux == nil { request.Container.Linux = &LinuxContainer{} } @@ -85,6 +88,7 @@ func collectCreateContainerResult(request *CreateContainerRequest) *result { Mounts: []*Mount{}, Env: []*KeyValue{}, Hooks: &Hooks{}, + Rlimits: []*POSIXRlimit{}, Linux: &LinuxContainerAdjustment{ Devices: []*LinuxDevice{}, Resources: &LinuxResources{ @@ -210,6 +214,9 @@ func (r *result) adjust(rpl *ContainerAdjustment, plugin string) error { return err } } + if err := r.adjustRlimits(rpl.Rlimits, plugin); err != nil { + return err + } return nil } @@ -659,6 +666,19 @@ func (r *result) adjustCgroupsPath(path, plugin string) error { return nil } +func (r *result) adjustRlimits(rlimits []*POSIXRlimit, plugin string) error { + create, id, adjust := r.request.create, r.request.create.Container.Id, r.reply.adjust + for _, l := range rlimits { + if err := r.owners.claimRlimits(id, l.Type, plugin); err != nil { + return err + } + + create.Container.Rlimits = append(create.Container.Rlimits, l) + adjust.Rlimits = append(adjust.Rlimits, l) + } + return nil +} + func (r *result) updateResources(reply, u *ContainerUpdate, plugin string) error { if u.Linux == nil || u.Linux.Resources == nil { return nil @@ -873,6 +893,7 @@ type owners struct { rdtClass string unified map[string]string cgroupsPath string + rlimits map[string]string } func (ro resultOwners) ownersFor(id string) *owners { @@ -980,6 +1001,10 @@ func (ro resultOwners) claimCgroupsPath(id, plugin string) error { return ro.ownersFor(id).claimCgroupsPath(plugin) } +func (ro resultOwners) claimRlimits(id, typ, plugin string) error { + return ro.ownersFor(id).claimRlimit(typ, plugin) +} + func (o *owners) claimAnnotation(key, plugin string) error { if o.annotations == nil { o.annotations = make(map[string]string) @@ -1183,6 +1208,17 @@ func (o *owners) claimUnified(key, plugin string) error { return nil } +func (o *owners) claimRlimit(typ, plugin string) error { + if o.rlimits == nil { + o.rlimits = make(map[string]string) + } + if other, taken := o.rlimits[typ]; taken { + return conflict(plugin, other, "rlimit", typ) + } + o.rlimits[typ] = plugin + return nil +} + func (o *owners) claimCgroupsPath(plugin string) error { if other := o.cgroupsPath; other != "" { return conflict(plugin, other, "cgroups path") diff --git a/pkg/api/adjustment.go b/pkg/api/adjustment.go index c44b4aa5..0f1fcd36 100644 --- a/pkg/api/adjustment.go +++ b/pkg/api/adjustment.go @@ -103,6 +103,15 @@ func (a *ContainerAdjustment) AddHooks(h *Hooks) { } } +func (a *ContainerAdjustment) AddRlimit(typ string, hard, soft uint64) { + a.initRlimits() + a.Rlimits = append(a.Rlimits, &POSIXRlimit{ + Type: typ, + Hard: hard, + Soft: soft, + }) +} + // AddDevice records the addition of the given device to a container. func (a *ContainerAdjustment) AddDevice(d *LinuxDevice) { a.initLinux() @@ -260,6 +269,12 @@ func (a *ContainerAdjustment) initHooks() { } } +func (a *ContainerAdjustment) initRlimits() { + if a.Rlimits == nil { + a.Rlimits = []*POSIXRlimit{} + } +} + func (a *ContainerAdjustment) initLinux() { if a.Linux == nil { a.Linux = &LinuxContainerAdjustment{} diff --git a/pkg/runtime-tools/generate/generate.go b/pkg/runtime-tools/generate/generate.go index 847383eb..ad8c20e2 100644 --- a/pkg/runtime-tools/generate/generate.go +++ b/pkg/runtime-tools/generate/generate.go @@ -119,6 +119,9 @@ func (g *Generator) Adjust(adjust *nri.ContainerAdjustment) error { if err := g.AdjustMounts(adjust.GetMounts()); err != nil { return err } + if err := g.AdjustRlimits(adjust.GetRlimits()); err != nil { + return err + } return nil } @@ -320,6 +323,20 @@ func (g *Generator) AdjustDevices(devices []*nri.LinuxDevice) { } } +func (g *Generator) AdjustRlimits(rlimits []*nri.POSIXRlimit) error { + for _, l := range rlimits { + if l == nil { + continue + } + g.Config.Process.Rlimits = append(g.Config.Process.Rlimits, rspec.POSIXRlimit{ + Type: l.Type, + Hard: l.Hard, + Soft: l.Soft, + }) + } + return nil +} + // AdjustMounts adjusts the mounts in the OCI Spec. func (g *Generator) AdjustMounts(mounts []*nri.Mount) error { if len(mounts) == 0 { diff --git a/pkg/runtime-tools/generate/generate_suite_test.go b/pkg/runtime-tools/generate/generate_suite_test.go index 7e302b04..589b8309 100644 --- a/pkg/runtime-tools/generate/generate_suite_test.go +++ b/pkg/runtime-tools/generate/generate_suite_test.go @@ -69,6 +69,28 @@ var _ = Describe("Adjustment", func() { }) }) + When("has rlimits", func() { + It("adjusts Spec correctly", func() { + var ( + spec = makeSpec() + adjust = &api.ContainerAdjustment{ + Rlimits: []*api.POSIXRlimit{{ + Type: "nofile", + Hard: 456, + Soft: 123, + }}, + } + ) + + rg := &rgen.Generator{Config: spec} + xg := xgen.SpecGenerator(rg) + + Expect(xg).ToNot(BeNil()) + Expect(xg.Adjust(adjust)).To(Succeed()) + Expect(spec).To(Equal(makeSpec(withRlimit("nofile", 456, 123)))) + }) + }) + When("has memory limit", func() { It("adjusts Spec correctly", func() { var ( @@ -381,6 +403,19 @@ func withMounts(mounts []rspec.Mount) specOption { } } +func withRlimit(typ string, hard, soft uint64) specOption { + return func(spec *rspec.Spec) { + if spec.Process == nil { + return + } + spec.Process.Rlimits = append(spec.Process.Rlimits, rspec.POSIXRlimit{ + Type: typ, + Hard: hard, + Soft: soft, + }) + } +} + func makeSpec(options ...specOption) *rspec.Spec { spec := &rspec.Spec{ Process: &rspec.Process{},