Skip to content

Commit

Permalink
Limit replica count to 1 when deploying from kubernetes YAML
Browse files Browse the repository at this point in the history
This fixes: containers#16765

Signed-off-by: Dan Čermák <dcermak@suse.com>
  • Loading branch information
dcermak committed Jan 12, 2023
1 parent 4bbe2ee commit c529e80
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 83 deletions.
29 changes: 14 additions & 15 deletions pkg/domain/infra/abi/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,6 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
deploymentName string
podSpec v1.PodTemplateSpec
numReplicas int32
i int32
report entities.PlayKubeReport
)

Expand All @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down
141 changes: 73 additions & 68 deletions test/e2e/play_kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

for i = 0; i < d.Replicas; i++ {
p := Pod{}
p.Name = fmt.Sprintf("%s-pod-%d", d.Name, i)
pods = append(pods, p)
}
func getPodNameInDeployment(d *Deployment) Pod {
p := Pod{}
p.Name = fmt.Sprintf("%s-pod", d.Name)

return pods
return p
}

// Ctr describes the options a kube yaml can be configured at container level
Expand Down Expand Up @@ -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
Expand All @@ -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())

Expand Down Expand Up @@ -2873,17 +2867,16 @@ 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
Expect(inspect.OutputToString()).To(ContainSubstring(strings.Join(defaultCtrCmd, " ")))
})

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())
Expand All @@ -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() {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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() {
Expand All @@ -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() {
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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`))
Expand Down Expand Up @@ -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
Expand All @@ -4742,6 +4731,7 @@ spec:
image: quay.io/libpod/alpine_nginx:latest
ports:
- containerPort: 1234
hostPort: 80
`

err = writeYaml(yaml, kubeYaml)
Expand Down Expand Up @@ -4989,4 +4979,19 @@ 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))

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))
})
})

0 comments on commit c529e80

Please sign in to comment.