From d9a85466a0a1bf5cb99901d2bd79b928a126731f Mon Sep 17 00:00:00 2001 From: Urvashi Mohnani Date: Sun, 10 Sep 2023 16:37:07 -0400 Subject: [PATCH] Add support for kube TerminationGracePeriodSeconds Add support to kube play to support the TerminationGracePeriodSeconds fiels by sending the value of that to podman's stopTimeout. Add support to kube generate to generate TerminationGracePeriodSeconds if stopTimeout is set for a container (will ignore podman's default). Signed-off-by: Urvashi Mohnani --- docs/kubernetes_support.md | 2 +- libpod/kube.go | 36 +++++++++++++++++++++++++------ pkg/domain/infra/abi/play.go | 4 ++++ pkg/specgen/generate/kube/kube.go | 8 +++++++ test/e2e/generate_kube_test.go | 22 +++++++++++++++++++ test/e2e/play_kube_test.go | 23 ++++++++++++++++++++ 6 files changed, 88 insertions(+), 7 deletions(-) diff --git a/docs/kubernetes_support.md b/docs/kubernetes_support.md index fa427621ec..7a7a2409ff 100644 --- a/docs/kubernetes_support.md +++ b/docs/kubernetes_support.md @@ -33,7 +33,7 @@ Note: **N/A** means that the option cannot be supported in a single-node Podman | topologySpreadConstraints\.labelSelector | N/A | | topologySpreadConstraints\.minDomains | N/A | | restartPolicy | ✅ | -| terminationGracePeriod | no | +| terminationGracePeriodSeconds | ✅ | | activeDeadlineSeconds | no | | readinessGates\.conditionType | no | | hostname | ✅ | diff --git a/libpod/kube.go b/libpod/kube.go index 15def203b7..816322146f 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -435,7 +435,10 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po podInitCtrs := []v1.Container{} podAnnotations := make(map[string]string) dnsInfo := v1.PodDNSConfig{} - var hostname string + var ( + hostname string + stopTimeout *uint + ) // Let's sort the containers in order of created time // This will ensure that the init containers are defined in the correct order in the kube yaml @@ -463,6 +466,12 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po } } + // Pick the first container that has a stop-timeout set and use that value + // Ignore podman's default + if ctr.config.StopTimeout != util.DefaultContainerConfig().Engine.StopTimeout && stopTimeout == nil { + stopTimeout = &ctr.config.StopTimeout + } + ctr, volumes, _, annotations, err := containerToV1Container(ctx, ctr, getService) if err != nil { return nil, err @@ -536,10 +545,11 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po &dnsInfo, hostNetwork, hostUsers, - hostname), nil + hostname, + stopTimeout), nil } -func newPodObject(podName string, annotations map[string]string, initCtrs, containers []v1.Container, volumes []v1.Volume, dnsOptions *v1.PodDNSConfig, hostNetwork, hostUsers bool, hostname string) *v1.Pod { +func newPodObject(podName string, annotations map[string]string, initCtrs, containers []v1.Container, volumes []v1.Volume, dnsOptions *v1.PodDNSConfig, hostNetwork, hostUsers bool, hostname string, stopTimeout *uint) *v1.Pod { tm := v12.TypeMeta{ Kind: "Pod", APIVersion: "v1", @@ -571,6 +581,10 @@ func newPodObject(podName string, annotations map[string]string, initCtrs, conta if dnsOptions != nil && (len(dnsOptions.Nameservers)+len(dnsOptions.Searches)+len(dnsOptions.Options) > 0) { ps.DNSConfig = dnsOptions } + if stopTimeout != nil { + terminationGracePeriod := int64(*stopTimeout) + ps.TerminationGracePeriodSeconds = &terminationGracePeriod + } p := v1.Pod{ TypeMeta: tm, ObjectMeta: om, @@ -590,8 +604,11 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic podDNS := v1.PodDNSConfig{} kubeAnnotations := make(map[string]string) ctrNames := make([]string, 0, len(ctrs)) - var hostname string - var restartPolicy *string + var ( + hostname string + restartPolicy *string + stopTimeout *uint + ) for _, ctr := range ctrs { ctrNames = append(ctrNames, removeUnderscores(ctr.Name())) for k, v := range ctr.config.Spec.Annotations { @@ -616,6 +633,12 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic } } + // Pick the first container that has a stop-timeout set and use that value + // Ignore podman's default + if ctr.config.StopTimeout != util.DefaultContainerConfig().Engine.StopTimeout && stopTimeout == nil { + stopTimeout = &ctr.config.StopTimeout + } + // Use the restart policy of the first non-init container if !isInit && restartPolicy == nil { restartPolicy = &ctr.config.RestartPolicy @@ -707,7 +730,8 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic &podDNS, hostNetwork, hostUsers, - hostname) + hostname, + stopTimeout) // Set the pod's restart policy policy := "" diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 0881922d22..f4ee8eb650 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -855,6 +855,10 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY Volumes: volumes, } + if podYAML.Spec.TerminationGracePeriodSeconds != nil { + specgenOpts.TerminationGracePeriodSeconds = podYAML.Spec.TerminationGracePeriodSeconds + } + specGen, err := kube.ToSpecGen(ctx, &specgenOpts) if err != nil { return nil, nil, err diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 9eb97d9897..2f85ccb20c 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -168,6 +168,8 @@ type CtrSpecGenOptions struct { InitContainerType string // PodSecurityContext is the security context specified for the pod PodSecurityContext *v1.PodSecurityContext + // TerminationGracePeriodSeconds is the grace period given to a container to stop before being forcefully killed + TerminationGracePeriodSeconds *int64 } func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) { @@ -584,6 +586,12 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener s.Labels[systemdDefine.EnvVariable] = unit } + // Set the stopTimeout if terminationGracePeriodSeconds is set in the kube yaml + if opts.TerminationGracePeriodSeconds != nil { + timeout := uint(*opts.TerminationGracePeriodSeconds) + s.StopTimeout = &timeout + } + return s, nil } diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 49468438de..da0907c79e 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -52,6 +52,7 @@ 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.Spec.TerminationGracePeriodSeconds).To(BeNil()) numContainers := 0 for range pod.Spec.Containers { @@ -1880,4 +1881,25 @@ EXPOSE 2004-2005/tcp`, ALPINE) Expect(err).ToNot(HaveOccurred()) Expect(pod.Annotations).To(BeEmpty()) }) + + It("podman generate kube on pod with --stop-timeout set for ctr", func() { + podName := "test-pod" + podSession := podmanTest.Podman([]string{"pod", "create", podName}) + podSession.WaitWithDefaultTimeout() + Expect(podSession).Should(Exit(0)) + + session := podmanTest.Podman([]string{"create", "--pod", podName, "--stop-timeout", "20", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", podName}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // TerminationGracePeriodSeconds should be set to 20 + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).ToNot(HaveOccurred()) + Expect(int(*pod.Spec.TerminationGracePeriodSeconds)).To(Equal(20)) + }) }) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index a6f0496a3d..f87a0b5218 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -5991,4 +5991,27 @@ EXPOSE 2004-2005/tcp`, ALPINE) Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=bar`)) }) + + It("podman kube play with TerminationGracePeriodSeconds set", func() { + ctrName := "ctr" + ctrNameInPod := "ctr-pod-ctr" + outputFile := filepath.Join(podmanTest.TempDir, "pod.yaml") + + create := podmanTest.Podman([]string{"create", "--restart", "never", "--stop-timeout", "20", "--name", ctrName, ALPINE}) + create.WaitWithDefaultTimeout() + Expect(create).Should(Exit(0)) + + generate := podmanTest.Podman([]string{"kube", "generate", "-f", outputFile, ctrName}) + generate.WaitWithDefaultTimeout() + Expect(generate).Should(Exit(0)) + + play := podmanTest.Podman([]string{"kube", "play", outputFile}) + play.WaitWithDefaultTimeout() + Expect(play).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", ctrNameInPod, "-f", "{{ .Config.StopTimeout }}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(Equal("20")) + }) })