diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 3240eb9449..409018086d 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -23,6 +23,10 @@ import ( "github.com/opencontainers/runtime-spec/specs-go" ) +const ( + rlimitPrefix = "rlimit_" +) + func getCPULimits(c *entities.ContainerCreateOptions) *specs.LinuxCPU { cpu := &specs.LinuxCPU{} hasLimits := false @@ -236,11 +240,15 @@ func setNamespaces(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) func GenRlimits(ulimits []string) ([]specs.POSIXRlimit, error) { rlimits := make([]specs.POSIXRlimit, 0, len(ulimits)) // Rlimits/Ulimits - for _, u := range ulimits { - if u == "host" { + for _, ulimit := range ulimits { + if ulimit == "host" { rlimits = nil break } + // `ulimitNameMapping` from go-units uses lowercase and names + // without prefixes, e.g. `RLIMIT_NOFILE` should be converted to `nofile`. + // https://github.com/containers/podman/issues/9803 + u := strings.TrimPrefix(strings.ToLower(ulimit), rlimitPrefix) ul, err := units.ParseUlimit(u) if err != nil { return nil, fmt.Errorf("ulimit option %q requires name=SOFT:HARD, failed to be parsed: %w", u, err) diff --git a/pkg/specgenutil/specgenutil_test.go b/pkg/specgenutil/specgenutil_test.go index fb2743f170..6abe5ff777 100644 --- a/pkg/specgenutil/specgenutil_test.go +++ b/pkg/specgenutil/specgenutil_test.go @@ -4,6 +4,8 @@ package specgenutil import ( + "fmt" + "strings" "testing" "github.com/containers/common/pkg/machine" @@ -154,3 +156,64 @@ func TestParseLinuxResourcesDeviceAccess(t *testing.T) { _, err = parseLinuxResourcesDeviceAccess("a *:-3 r") assert.NotNil(t, err, "err is not nil") } + +func TestGenRlimits(t *testing.T) { + testLimits := map[string]string{ + "core": "1:2", + "cpu": "1:2", + "data": "1:2", + "fsize": "1:2", + "locks": "1:2", + "memlock": "1:2", + "msgqueue": "1:2", + "nice": "1:2", + "nofile": "1:2", + "nproc": "1:2", + "rss": "1:2", + "rtprio": "1:2", + "rttime": "1:2", + "sigpending": "1:2", + "stack": "1:2", + } + + lowerCaseLimits := make([]string, 0, len(testLimits)) + upperCaseLimitsWithPrefix := make([]string, 0, len(testLimits)) + for name, limit := range testLimits { + lowerCaseLimits = append(lowerCaseLimits, fmt.Sprintf("%s=%s", name, limit)) + upperCaseLimitsWithPrefix = append(upperCaseLimitsWithPrefix, strings.ToUpper(fmt.Sprintf("%s%s=%s", rlimitPrefix, name, limit))) + } + + parsedLimits, err := GenRlimits(lowerCaseLimits) + assert.NoError(t, err, "err is nil") + + for _, limit := range parsedLimits { + val, ok := testLimits[limit.Type] + assert.True(t, ok, "%s matched", limit.Type) + assert.Equal(t, val, fmt.Sprintf("%d:%d", limit.Soft, limit.Hard), "soft and hard limits are equal") + } + + parsedLimits, err = GenRlimits(upperCaseLimitsWithPrefix) + assert.NoError(t, err, "err is nil") + + for _, limit := range parsedLimits { + val, ok := testLimits[limit.Type] + assert.True(t, ok, "%s matched", limit.Type) + assert.Equal(t, val, fmt.Sprintf("%d:%d", limit.Soft, limit.Hard), "soft and hard limits are equal") + } + + // go-utils module has no tests for the parser. + _, err = GenRlimits([]string{"foo=1:1"}) + assert.Error(t, err, "err is not nil") + + _, err = GenRlimits([]string{"RLIMIT_FOO=1:1"}) + assert.Error(t, err, "err is not nil") + + _, err = GenRlimits([]string{"nofile"}) + assert.Error(t, err, "err is not nil") + + _, err = GenRlimits([]string{"nofile=bar"}) + assert.Error(t, err, "err is not nil") + + _, err = GenRlimits([]string{"nofile=bar:buzz"}) + assert.Error(t, err, "err is not nil") +}