diff --git a/libpod/define/annotations.go b/libpod/define/annotations.go index 50bc52571a..a70f83f785 100644 --- a/libpod/define/annotations.go +++ b/libpod/define/annotations.go @@ -140,6 +140,10 @@ const ( // of the init container. InitContainerType = "io.podman.annotations.init.container.type" + // UlimitAnnotation is used by kube play when playing a kube yaml to specify the ulimits + // of the container + UlimitAnnotation = "io.podman.annotations.ulimit" + // MaxKubeAnnotation is the max length of annotations allowed by Kubernetes. MaxKubeAnnotation = 63 ) diff --git a/libpod/kube.go b/libpod/kube.go index 240ab4e322..b5ff66fd44 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -531,6 +531,24 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, } } + if ctr.config.Spec.Process != nil { + var ulimitArr []string + defaultUlimits := util.DefaultContainerConfig().Ulimits() + for _, ulimit := range ctr.config.Spec.Process.Rlimits { + finalUlimit := strings.ToLower(strings.ReplaceAll(ulimit.Type, "RLIMIT_", "")) + "=" + strconv.Itoa(int(ulimit.Soft)) + ":" + strconv.Itoa(int(ulimit.Hard)) + // compare ulimit with default list so we don't add it twice + if cutil.StringInSlice(finalUlimit, defaultUlimits) { + continue + } + + ulimitArr = append(ulimitArr, finalUlimit) + } + + if len(ulimitArr) > 0 { + kubeAnnotations[define.UlimitAnnotation] = strings.Join(ulimitArr, ",") + } + } + if !ctr.HostNetwork() { hostNetwork = false } diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 4a3d6648e8..09fe5fc7c6 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -273,6 +273,18 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener s.ResourceLimits.Memory.Reservation = &memoryRes } + ulimitVal, ok := opts.Annotations[define.UlimitAnnotation] + if ok { + ulimits := strings.Split(ulimitVal, ",") + for _, ul := range ulimits { + parsed, err := units.ParseUlimit(ul) + if err != nil { + return nil, err + } + s.Rlimits = append(s.Rlimits, spec.POSIXRlimit{Type: parsed.Name, Soft: uint64(parsed.Soft), Hard: uint64(parsed.Hard)}) + } + } + // TODO: We don't understand why specgen does not take of this, but // integration tests clearly pointed out that it was required. imageData, err := opts.Image.Inspect(ctx, nil) diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 0f14b2985c..00b68ee551 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -72,7 +72,6 @@ var _ = Describe("Podman kube generate", func() { Expect(pod.Spec.Containers[0].SecurityContext).To(BeNil()) Expect(pod.Spec.Containers[0].Env).To(BeNil()) Expect(pod).To(HaveField("Name", "top-pod")) - Expect(pod.Annotations).To(HaveLen(0)) numContainers := 0 for range pod.Spec.Containers { @@ -1330,4 +1329,40 @@ USER test1` Expect(pod.Spec.Volumes[0].Secret).To(BeNil()) }) + + It("podman generate & play kube with --ulimit set", func() { + ctrName := "ulimit-ctr" + ctrNameInKubePod := ctrName + "-pod-" + ctrName + session1 := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, "--ulimit", "nofile=1231:3123", ALPINE, "sleep", "1000"}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + + outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml") + kube := podmanTest.Podman([]string{"kube", "generate", ctrName, "-f", outputFile}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + b, err := os.ReadFile(outputFile) + Expect(err).ShouldNot(HaveOccurred()) + pod := new(v1.Pod) + err = yaml.Unmarshal(b, pod) + Expect(err).ToNot(HaveOccurred()) + Expect(pod.Annotations).To(HaveKey(define.UlimitAnnotation)) + Expect(pod.Annotations[define.UlimitAnnotation]).To(ContainSubstring("nofile=1231:3123")) + + rm := podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-f", ctrName}) + rm.WaitWithDefaultTimeout() + Expect(rm).Should(Exit(0)) + + play := podmanTest.Podman([]string{"kube", "play", outputFile}) + play.WaitWithDefaultTimeout() + Expect(play).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", ctrNameInKubePod}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("RLIMIT_NOFILE")) + Expect(inspect.OutputToString()).To(ContainSubstring("1231")) + Expect(inspect.OutputToString()).To(ContainSubstring("3123")) + }) })