From d9bf3f1290c04e04492ea4699e9825a9c59b6cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20=C4=8Cerm=C3=A1k?= Date: Wed, 11 Jan 2023 16:56:28 +0100 Subject: [PATCH] Limit replica count to 1 when deploying from kubernetes YAML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes: https://github.com/containers/podman/issues/16765 Signed-off-by: Dan Čermák --- pkg/domain/infra/abi/play.go | 29 ++++--- test/e2e/play_kube_test.go | 144 ++++++++++++++++++----------------- 2 files changed, 90 insertions(+), 83 deletions(-) diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 7a351d2417..5d54143c90 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -333,7 +333,6 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM deploymentName string podSpec v1.PodTemplateSpec numReplicas int32 - i int32 report entities.PlayKubeReport ) @@ -345,20 +344,19 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM if deploymentYAML.Spec.Replicas != nil { numReplicas = *deploymentYAML.Spec.Replicas } + if numReplicas > 1 { + logrus.Warnf("Limiting replica count to 1, more than one replica is not supported by Podman") + } podSpec = deploymentYAML.Spec.Template - // create "replicas" number of pods - var notifyProxies []*notifyproxy.NotifyProxy - for i = 0; i < numReplicas; i++ { - podName := fmt.Sprintf("%s-pod-%d", deploymentName, i) - podReport, proxies, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex, deploymentYAML.Annotations, configMaps, serviceContainer) - if err != nil { - return nil, notifyProxies, fmt.Errorf("encountered while bringing up pod %s: %w", podName, err) - } - report.Pods = append(report.Pods, podReport.Pods...) - notifyProxies = append(notifyProxies, proxies...) + podName := fmt.Sprintf("%s-pod", deploymentName) + podReport, proxies, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex, deploymentYAML.Annotations, configMaps, serviceContainer) + if err != nil { + return nil, nil, fmt.Errorf("encountered while bringing up pod %s: %w", podName, err) } - return &report, notifyProxies, nil + report.Pods = podReport.Pods + + return &report, proxies, nil } func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int, annotations map[string]string, configMaps []v1.ConfigMap, serviceContainer *libpod.Container) (*entities.PlayKubeReport, []*notifyproxy.NotifyProxy, error) { @@ -1255,10 +1253,11 @@ func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, opt if deploymentYAML.Spec.Replicas != nil { numReplicas = *deploymentYAML.Spec.Replicas } - for i := 0; i < int(numReplicas); i++ { - podName := fmt.Sprintf("%s-pod-%d", deploymentName, i) - podNames = append(podNames, podName) + if numReplicas > 1 { + logrus.Warnf("Limiting replica count to 1, more than one replica is not supported by Podman") } + podName := fmt.Sprintf("%s-pod", deploymentName) + podNames = append(podNames, podName) case "PersistentVolumeClaim": var pvcYAML v1.PersistentVolumeClaim if err := yaml.Unmarshal(document, &pvcYAML); err != nil { diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index a3cef42656..6d30a1eed8 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -1267,20 +1267,14 @@ func withReplicas(replicas int32) deploymentOption { } } -// getPodNamesInDeployment returns list of Pod objects -// with just their name set, so that it can be passed around +// getPodNameInDeployment returns the Pod object +// with just its name set, so that it can be passed around // and into getCtrNameInPod for ease of testing -func getPodNamesInDeployment(d *Deployment) []Pod { - var pods []Pod - var i int32 +func getPodNameInDeployment(d *Deployment) Pod { + p := Pod{} + p.Name = fmt.Sprintf("%s-pod", d.Name) - for i = 0; i < d.Replicas; i++ { - p := Pod{} - p.Name = fmt.Sprintf("%s-pod-%d", d.Name, i) - pods = append(pods, p) - } - - return pods + return p } // Ctr describes the options a kube yaml can be configured at container level @@ -1982,7 +1976,7 @@ var _ = Describe("Podman play kube", func() { kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - inspect := podmanTest.Podman([]string{"inspect", "liveness-probe-pod-0-alpine", "--format", "'{{ .Config.Healthcheck }}'"}) + inspect := podmanTest.Podman([]string{"inspect", "liveness-probe-pod-alpine", "--format", "'{{ .Config.Healthcheck }}'"}) inspect.WaitWithDefaultTimeout() healthcheckcmd := inspect.OutputToString() // check if CMD-SHELL based equivalent health check is added to container @@ -1998,14 +1992,14 @@ var _ = Describe("Podman play kube", func() { Expect(kube).Should(Exit(0)) time.Sleep(2 * time.Second) - hc := podmanTest.Podman([]string{"healthcheck", "run", "liveness-unhealthy-probe-pod-0-alpine"}) + hc := podmanTest.Podman([]string{"healthcheck", "run", "liveness-unhealthy-probe-pod-alpine"}) hc.WaitWithDefaultTimeout() hcoutput := hc.OutputToString() Expect(hcoutput).To(ContainSubstring(define.HealthCheckUnhealthy)) }) It("podman play kube support container startup probe", func() { - ctrName := "startup-healthy-probe-pod-0-alpine" + ctrName := "startup-healthy-probe-pod-alpine" err := writeYaml(startupProbePodYaml, kubeYaml) Expect(err).ToNot(HaveOccurred()) @@ -2873,8 +2867,8 @@ spec: kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - podNames := getPodNamesInDeployment(deployment) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0]), "--format", "'{{ .Config.Entrypoint }}'"}) + podName := getPodNameInDeployment(deployment) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podName), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) // yaml's command should override the image's Entrypoint @@ -2882,8 +2876,7 @@ spec: }) It("podman play kube deployment more than 1 replica test correct command", func() { - var i, numReplicas int32 - numReplicas = 5 + var numReplicas int32 = 5 deployment := getDeployment(withReplicas(numReplicas)) err := generateKubeYaml("deployment", deployment, kubeYaml) Expect(err).ToNot(HaveOccurred()) @@ -2892,13 +2885,13 @@ spec: kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - podNames := getPodNamesInDeployment(deployment) - for i = 0; i < numReplicas; i++ { - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "'{{ .Config.Entrypoint }}'"}) - inspect.WaitWithDefaultTimeout() - Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) - } + podName := getPodNameInDeployment(deployment) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podName), "--format", "'{{ .Config.Entrypoint }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) + }) It("podman play kube --ip and --mac-address", func() { @@ -2928,20 +2921,18 @@ spec: kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - podNames := getPodNamesInDeployment(deployment) - for i = 0; i < numReplicas; i++ { - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "{{ .NetworkSettings.Networks." + net + ".IPAddress }}"}) - inspect.WaitWithDefaultTimeout() - Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(Equal(ips[i])) - } + podName := getPodNameInDeployment(deployment) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podName), "--format", "{{ .NetworkSettings.Networks." + net + ".IPAddress }}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(Equal(ips[i])) + + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podName), "--format", "{{ .NetworkSettings.Networks." + net + ".MacAddress }}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(Equal(macs[i])) - for i = 0; i < numReplicas; i++ { - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "{{ .NetworkSettings.Networks." + net + ".MacAddress }}"}) - inspect.WaitWithDefaultTimeout() - Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(Equal(macs[i])) - } }) It("podman play kube with multiple networks", func() { @@ -3350,12 +3341,11 @@ VOLUME %s`, ALPINE, hostPathDir+"/") Expect(kube).Should(Exit(0)) correctLabels := expectedLabelKey + ":" + expectedLabelValue - for _, pod := range getPodNamesInDeployment(deployment) { - inspect := podmanTest.Podman([]string{"pod", "inspect", pod.Name, "--format", "'{{ .Labels }}'"}) - inspect.WaitWithDefaultTimeout() - Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(correctLabels)) - } + pod := getPodNameInDeployment(deployment) + inspect := podmanTest.Podman([]string{"pod", "inspect", pod.Name, "--format", "'{{ .Labels }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(correctLabels)) }) It("podman play kube allows setting resource limits", func() { @@ -3388,19 +3378,18 @@ VOLUME %s`, ALPINE, hostPathDir+"/") kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - for _, pod := range getPodNamesInDeployment(deployment) { - pod := pod // copy into local scope - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&pod), "--format", ` + pod := getPodNameInDeployment(deployment) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&pod), "--format", ` CpuPeriod: {{ .HostConfig.CpuPeriod }} CpuQuota: {{ .HostConfig.CpuQuota }} Memory: {{ .HostConfig.Memory }} MemoryReservation: {{ .HostConfig.MemoryReservation }}`}) - inspect.WaitWithDefaultTimeout() - Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(fmt.Sprintf("%s: %d", "CpuQuota", expectedCPUQuota))) - Expect(inspect.OutputToString()).To(ContainSubstring("MemoryReservation: " + expectedMemoryRequest)) - Expect(inspect.OutputToString()).To(ContainSubstring("Memory: " + expectedMemoryLimit)) - } + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(fmt.Sprintf("%s: %d", "CpuQuota", expectedCPUQuota))) + Expect(inspect.OutputToString()).To(ContainSubstring("MemoryReservation: " + expectedMemoryRequest)) + Expect(inspect.OutputToString()).To(ContainSubstring("Memory: " + expectedMemoryLimit)) + }) It("podman play kube allows setting resource limits with --cpus 1", func() { @@ -3423,18 +3412,17 @@ MemoryReservation: {{ .HostConfig.MemoryReservation }}`}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - for _, pod := range getPodNamesInDeployment(deployment) { - pod := pod // copy into local scope - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&pod), "--format", `{{ .HostConfig.CpuPeriod }}:{{ .HostConfig.CpuQuota }}`}) + pod := getPodNameInDeployment(deployment) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&pod), "--format", `{{ .HostConfig.CpuPeriod }}:{{ .HostConfig.CpuQuota }}`}) - inspect.WaitWithDefaultTimeout() - Expect(inspect).Should(Exit(0)) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) - parts := strings.Split(strings.Trim(inspect.OutputToString(), "\n"), ":") - Expect(parts).To(HaveLen(2)) + parts := strings.Split(strings.Trim(inspect.OutputToString(), "\n"), ":") + Expect(parts).To(HaveLen(2)) + + Expect(parts[0]).To(Equal(parts[1])) - Expect(parts[0]).To(Equal(parts[1])) - } }) It("podman play kube reports invalid image name", func() { @@ -3659,12 +3647,12 @@ spec: Expect(inspectVolume).Should(Exit(0)) Expect(inspectVolume.OutputToString()).To(ContainSubstring(volName)) - inspectPod := podmanTest.Podman([]string{"inspect", podName + "-pod-0", "--format", "'{{ .State }}'"}) + inspectPod := podmanTest.Podman([]string{"inspect", podName + "-pod", "--format", "'{{ .State }}'"}) inspectPod.WaitWithDefaultTimeout() Expect(inspectPod).Should(Exit(0)) Expect(inspectPod.OutputToString()).To(ContainSubstring(`Running`)) - inspectMounts := podmanTest.Podman([]string{"inspect", podName + "-pod-0-" + ctrName, "--format", "{{ (index .Mounts 0).Type }}:{{ (index .Mounts 0).Name }}"}) + inspectMounts := podmanTest.Podman([]string{"inspect", podName + "-pod-" + ctrName, "--format", "{{ (index .Mounts 0).Type }}:{{ (index .Mounts 0).Name }}"}) inspectMounts.WaitWithDefaultTimeout() Expect(inspectMounts).Should(Exit(0)) @@ -3692,7 +3680,7 @@ spec: for i := 0; i < 2; i++ { podName := fmt.Sprintf("testPod%d", i) deploymentName := fmt.Sprintf("testDeploy%d", i) - deploymentPodName := fmt.Sprintf("%s-pod-0", deploymentName) + deploymentPodName := fmt.Sprintf("%s-pod", deploymentName) podNames = append(podNames, podName) podNames = append(podNames, deploymentPodName) @@ -3831,7 +3819,7 @@ invalid kube kind kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - podName := getPodNamesInDeployment(deployment)[0].Name + podName := getPodNameInDeployment(deployment).Name inspect := podmanTest.Podman([]string{"inspect", podName + "-" + ctr01Name, "--format", "'{{.Config.Labels}}'"}) inspect.WaitWithDefaultTimeout() @@ -4247,7 +4235,7 @@ ENV OPENJ9_JAVA_OPTIONS=%q kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - inspect := podmanTest.Podman([]string{"inspect", fmt.Sprintf("%s-%s-%s", deployment.Name, "pod-0", defaultCtrName), "--format", "'{{ .Config }}'"}) + inspect := podmanTest.Podman([]string{"inspect", fmt.Sprintf("%s-%s-%s", deployment.Name, "pod", defaultCtrName), "--format", "'{{ .Config }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`)) @@ -4724,6 +4712,7 @@ cgroups="disabled"`), 0644) }) It("podman kube --quiet with error", func() { + SkipIfNotRootless("We need to create an error trying to bind to port 80") yaml := ` apiVersion: apps/v1 kind: Deployment @@ -4742,6 +4731,7 @@ spec: image: quay.io/libpod/alpine_nginx:latest ports: - containerPort: 1234 + hostPort: 80 ` err = writeYaml(yaml, kubeYaml) @@ -4989,4 +4979,22 @@ spec: testEchoServerUDP(":19012") testEchoServerTCP(":19011") }) + + It("podman play kube with replicas limits the count to 1 and emits a warning", func() { + deployment := getDeployment(withReplicas(10)) + err := generateKubeYaml("deployment", deployment, kubeYaml) + Expect(err).ToNot(HaveOccurred()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // warnings are only propagated to local clients + if !IsRemote() { + Expect(kube.ErrorToString()).Should(ContainSubstring("Limiting replica count to 1, more than one replica is not supported by Podman")) + } + + Expect(strings.Count(kube.OutputToString(), "Pod:")).To(Equal(1)) + Expect(strings.Count(kube.OutputToString(), "Container:")).To(Equal(1)) + }) })