diff --git a/libpod/kube.go b/libpod/kube.go index 023a1f03f3b..7d9c6e0f8d6 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -20,6 +20,7 @@ import ( "github.com/sirupsen/logrus" "go.podman.io/common/libnetwork/types" "go.podman.io/common/pkg/config" + "go.podman.io/image/v5/manifest" "go.podman.io/podman/v6/libpod/define" "go.podman.io/podman/v6/pkg/domain/entities" "go.podman.io/podman/v6/pkg/env" @@ -1073,9 +1074,45 @@ func containerToV1Container(ctx context.Context, c *Container, getService bool) } dns.Options = dnsOptions } + + if hc := c.config.HealthCheckConfig; hc != nil { + kubeContainer.LivenessProbe = healthConfigToProbe(hc) + } + return kubeContainer, kubeVolumes, &dns, annotations, nil } +// healthConfigToProbe converts a container's Schema2HealthConfig into a +// Kubernetes Probe for use as a LivenessProbe in generated kube YAML. +func healthConfigToProbe(hc *manifest.Schema2HealthConfig) *v1.Probe { + // Test[0] is the type: NONE, CMD, or CMD-SHELL. NONE means disabled. + if hc == nil || len(hc.Test) == 0 || hc.Test[0] == define.HealthConfigTestNone { + return nil + } + + var cmd []string + switch hc.Test[0] { + case define.HealthConfigTestCmd: + cmd = hc.Test[1:] + case define.HealthConfigTestCmdShell: + cmd = append([]string{"/bin/sh", "-c"}, hc.Test[1:]...) + default: + return nil + } + + probe := &v1.Probe{ + Handler: v1.Handler{ + Exec: &v1.ExecAction{Command: cmd}, + }, + InitialDelaySeconds: int32(hc.StartPeriod.Seconds()), + TimeoutSeconds: int32(hc.Timeout.Seconds()), + PeriodSeconds: int32(hc.Interval.Seconds()), + FailureThreshold: int32(hc.Retries), + } + + return probe +} + // portMappingToContainerPort takes a portmapping and converts // it to a v1.ContainerPort format for kube output func portMappingToContainerPort(portMappings []types.PortMapping, getService bool) ([]v1.ContainerPort, error) { diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index ff413a89284..eb0634cba88 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -892,15 +892,16 @@ var _ = Describe("Podman kube generate", func() { pod := new(v1.Pod) err = yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).ToNot(HaveOccurred()) - Expect(pod.Spec.Containers[0].VolumeMounts).To(ContainElements(v1.VolumeMount{ - Name: pvcName, - MountPath: etcTargetPath, - SubPath: etcSubPath, - }, v1.VolumeMount{ - Name: pvcName, - MountPath: varTargetPath, - SubPath: varSubPath, - }), + Expect(pod.Spec.Containers[0].VolumeMounts).To( + ContainElements(v1.VolumeMount{ + Name: pvcName, + MountPath: etcTargetPath, + SubPath: etcSubPath, + }, v1.VolumeMount{ + Name: pvcName, + MountPath: varTargetPath, + SubPath: varSubPath, + }), ) }) @@ -2001,4 +2002,47 @@ EXPOSE 2004-2005/tcp`, CITEST_IMAGE) kube.WaitWithDefaultTimeout() Expect(kube).Should(ExitWithError(125, "k8s DaemonSets can only have restartPolicy set to Always")) }) + + It("on container with healthcheck exports LivenessProbe", func() { + testCases := []struct { + name string + healthCmd string + healthCmdExpect []string + }{ + {"test-hc-ctr-1", "CMD /bin/true", []string{"/bin/true"}}, + {"test-hc-ctr-2", "CMD-SHELL /bin/true", []string{"/bin/sh", "-c", "/bin/true"}}, + } + + for _, ctr := range testCases { + session := podmanTest.Podman([]string{ + "create", "--name", ctr.name, + "--health-cmd", ctr.healthCmd, + "--health-interval", "10s", + "--health-timeout", "5s", + "--health-retries", "3", + "--health-start-period", "2s", + CITEST_IMAGE, "top", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + + kube := podmanTest.Podman([]string{"kube", "generate", ctr.name}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(ExitCleanly()) + + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).ToNot(HaveOccurred()) + Expect(pod.Spec.Containers).To(HaveLen(1)) + + probe := pod.Spec.Containers[0].LivenessProbe + Expect(probe).ToNot(BeNil(), "LivenessProbe should be set when container has a healthcheck") + Expect(probe.Exec).ToNot(BeNil()) + Expect(probe.Exec.Command).To(Equal([]string{"/bin/sh", "-c", "/bin/true"})) + Expect(probe.PeriodSeconds).To(Equal(int32(10))) + Expect(probe.TimeoutSeconds).To(Equal(int32(5))) + Expect(probe.FailureThreshold).To(Equal(int32(3))) + Expect(probe.InitialDelaySeconds).To(Equal(int32(2))) + } + }) })