From 52ed7fce2a1d0a34d24f717be843ed591167cbf7 Mon Sep 17 00:00:00 2001 From: Urvashi Mohnani Date: Thu, 3 Aug 2023 10:07:09 -0400 Subject: [PATCH] Add infra-name annotations to kube gen/play Add io.podman.annotations.infra.name annotation to kube play so users can set the name of the infra container created. When a pod is created with --infra-name set, the generated kube yaml will have an infraName annotation set that will be used when playing the generated yaml with podman. Signed-off-by: Urvashi Mohnani --- .../source/markdown/podman-kube-generate.1.md | 2 + docs/source/markdown/podman-kube-play.1.md.in | 2 + libpod/define/annotations.go | 4 + libpod/kube.go | 11 ++- pkg/bindings/kube/types.go | 2 +- pkg/domain/infra/abi/play.go | 5 ++ test/e2e/generate_kube_test.go | 42 ++++++++++ test/e2e/play_kube_test.go | 76 +++++++++++++++++++ 8 files changed, 141 insertions(+), 3 deletions(-) diff --git a/docs/source/markdown/podman-kube-generate.1.md b/docs/source/markdown/podman-kube-generate.1.md index 2de05cfa9e..916e8695e3 100644 --- a/docs/source/markdown/podman-kube-generate.1.md +++ b/docs/source/markdown/podman-kube-generate.1.md @@ -28,6 +28,8 @@ Once completed, the correct permissions are in place to access the volume when t Note that the generated Kubernetes YAML file can be used to re-run the deployment via podman-play-kube(1). +Note that if the pod being generated was created with the **--infra-name** flag set, then the generated kube yaml will have the **io.podman.annotations.infra.name** set where the value is the name of the infra container set by the user. + ## OPTIONS #### **--filename**, **-f**=*filename* diff --git a/docs/source/markdown/podman-kube-play.1.md.in b/docs/source/markdown/podman-kube-play.1.md.in index ab29fb93a0..2b957c479f 100644 --- a/docs/source/markdown/podman-kube-play.1.md.in +++ b/docs/source/markdown/podman-kube-play.1.md.in @@ -45,6 +45,8 @@ Note: The command `podman play kube` is an alias of `podman kube play`, and perf Note: The command `podman kube down` can be used to stop and remove pods or containers based on the same Kubernetes YAML used by `podman kube play` to create them. +Note: To customize the name of the infra container created during `podman kube play`, use the **io.podman.annotations.infra.name** annotation in the pod definition. This annotation is automatically set when generating a kube yaml from a pod that was created with the `--infra-name` flag set. + `Kubernetes PersistentVolumeClaims` A Kubernetes PersistentVolumeClaim represents a Podman named volume. Only the PersistentVolumeClaim name is required by Podman to create a volume. Kubernetes annotations can be used to make use of the available options for Podman volumes. diff --git a/libpod/define/annotations.go b/libpod/define/annotations.go index 72b5b18aa2..29796533bd 100644 --- a/libpod/define/annotations.go +++ b/libpod/define/annotations.go @@ -145,6 +145,10 @@ const ( // of the init container. InitContainerType = "io.podman.annotations.init.container.type" + // InfraNameAnnotation is used by generate and play kube when the infra container is set by the user during + // pod creation + InfraNameAnnotation = "io.podman.annotations.infra.name" + // UlimitAnnotation is used by kube play when playing a kube yaml to specify the ulimits // of the container UlimitAnnotation = "io.podman.annotations.ulimit" diff --git a/libpod/kube.go b/libpod/kube.go index 1842a9dbe3..072b98f421 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -64,6 +64,7 @@ func (p *Pod) GenerateForKube(ctx context.Context, getService, useLongAnnotation extraHost := make([]v1.HostAlias, 0) hostNetwork := false hostUsers := true + infraName := "" if p.HasInfraContainer() { infraContainer, err := p.getInfraContainer() if err != nil { @@ -90,8 +91,9 @@ func (p *Pod) GenerateForKube(ctx context.Context, getService, useLongAnnotation } hostNetwork = infraContainer.NetworkMode() == string(namespaces.NetworkMode(specgen.Host)) hostUsers = infraContainer.IDMappings().HostUIDMapping && infraContainer.IDMappings().HostGIDMapping + infraName = infraContainer.config.Name } - pod, err := p.podWithContainers(ctx, allContainers, ports, hostNetwork, hostUsers, getService, useLongAnnotations, podmanOnly) + pod, err := p.podWithContainers(ctx, allContainers, ports, hostNetwork, hostUsers, getService, useLongAnnotations, podmanOnly, infraName) if err != nil { return nil, servicePorts, err } @@ -426,7 +428,7 @@ func containersToServicePorts(containers []v1.Container) ([]v1.ServicePort, erro return sps, nil } -func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, ports []v1.ContainerPort, hostNetwork, hostUsers, getService, useLongAnnotations, podmanOnly bool) (*v1.Pod, error) { +func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, ports []v1.ContainerPort, hostNetwork, hostUsers, getService, useLongAnnotations, podmanOnly bool, infraName string) (*v1.Pod, error) { deDupPodVolumes := make(map[string]*v1.Volume) first := true podContainers := make([]v1.Container, 0, len(containers)) @@ -512,6 +514,11 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po dnsInfo.Options = options } } + // If the infraName is not the podID-infra, that means the user set another infra name using + // --infra-name during pod creation + if infraName != "" && infraName != p.ID()[:12]+"-infra" { + podAnnotations[define.InfraNameAnnotation] = truncateKubeAnnotation(infraName, useLongAnnotations) + } } } podVolumes := []v1.Volume{} diff --git a/pkg/bindings/kube/types.go b/pkg/bindings/kube/types.go index 9bb368e8d3..79cddfbf8c 100644 --- a/pkg/bindings/kube/types.go +++ b/pkg/bindings/kube/types.go @@ -53,7 +53,7 @@ type PlayOptions struct { Force *bool // PublishPorts - configure how to expose ports configured inside the K8S YAML file PublishPorts []string - // // Wait - indicates whether to return after having created the pods + // Wait - indicates whether to return after having created the pods Wait *bool ServiceContainer *bool } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index a11653dee3..52d3123156 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -664,6 +664,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY podSpec.PodSpecGen.InfraContainerSpec = specgen.NewSpecGenerator(infraImage, false) podSpec.PodSpecGen.InfraContainerSpec.NetworkOptions = p.NetworkOptions podSpec.PodSpecGen.InfraContainerSpec.SdNotifyMode = define.SdNotifyModeIgnore + // If the infraNameAnnotation is set in the yaml, use that as the infra container name + // If not, fall back to the default infra container name + if v, ok := podYAML.Annotations[define.InfraNameAnnotation]; ok { + podSpec.PodSpecGen.InfraContainerSpec.Name = v + } err = specgenutil.FillOutSpecGen(podSpec.PodSpecGen.InfraContainerSpec, &infraOptions, []string{}) if err != nil { diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 5f6729cc81..49468438de 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -1838,4 +1838,46 @@ EXPOSE 2004-2005/tcp`, ALPINE) Expect(err).ToNot(HaveOccurred()) Expect(pod.Annotations).To(HaveKeyWithValue(define.InspectAnnotationPublishAll+"/"+ctr, define.InspectResponseTrue)) }) + + It("podman generate kube on pod with --infra-name set", func() { + infraName := "infra-ctr" + podName := "test-pod" + podSession := podmanTest.Podman([]string{"pod", "create", "--infra-name", infraName, podName}) + podSession.WaitWithDefaultTimeout() + Expect(podSession).Should(Exit(0)) + + session := podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", podName}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).ToNot(HaveOccurred()) + Expect(pod.Annotations).To(HaveKeyWithValue(define.InfraNameAnnotation, infraName)) + }) + + It("podman generate kube on pod without --infra-name set", 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, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", podName}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // There should be no infra name annotation set if the --infra-name flag wasn't set during pod creation + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).ToNot(HaveOccurred()) + Expect(pod.Annotations).To(BeEmpty()) + }) }) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 0cd46df0ec..f6a13cd1bf 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -5879,4 +5879,80 @@ EXPOSE 2004-2005/tcp`, ALPINE) Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal(defaultUmask)) }) + + // podman play with infra name annotation + It("podman play kube test with infra name annotation set", func() { + infraName := "infra-ctr" + podName := "mypod" + outputFile := filepath.Join(podmanTest.TempDir, "pod.yaml") + pod := podmanTest.Podman([]string{"pod", "create", "--infra-name", infraName, podName}) + pod.WaitWithDefaultTimeout() + Expect(pod).Should(Exit(0)) + + ctr := podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr).Should(Exit(0)) + + // Generate kube yaml and it should have the infra name annotation set + gen := podmanTest.Podman([]string{"kube", "generate", "-f", outputFile, podName}) + gen.WaitWithDefaultTimeout() + Expect(gen).Should(Exit(0)) + // Remove the pod so it can be recreated via kube play + rm := podmanTest.Podman([]string{"pod", "rm", "-f", podName}) + rm.WaitWithDefaultTimeout() + Expect(rm).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"kube", "play", outputFile}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Expect the number of containers created to be 2, infra, and regular container + numOfCtrs := podmanTest.NumberOfContainers() + Expect(numOfCtrs).To(Equal(2)) + + ps := podmanTest.Podman([]string{"ps", "--format", "{{.Names}}"}) + ps.WaitWithDefaultTimeout() + Expect(ps).Should(Exit(0)) + Expect(ps.OutputToString()).To(ContainSubstring(infraName)) + }) + + // podman play with default infra name + It("podman play kube test with default infra name", func() { + podName := "mypod" + outputFile := filepath.Join(podmanTest.TempDir, "pod.yaml") + pod := podmanTest.Podman([]string{"pod", "create", podName}) + pod.WaitWithDefaultTimeout() + Expect(pod).Should(Exit(0)) + + ctr := podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr).Should(Exit(0)) + + // Generate kube yaml and it should have the infra name annotation set + gen := podmanTest.Podman([]string{"kube", "generate", "-f", outputFile, podName}) + gen.WaitWithDefaultTimeout() + Expect(gen).Should(Exit(0)) + // Remove the pod so it can be recreated via kube play + rm := podmanTest.Podman([]string{"pod", "rm", "-f", podName}) + rm.WaitWithDefaultTimeout() + Expect(rm).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"play", "kube", outputFile}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Expect the number of containers created to be 2, infra, and regular container + numOfCtrs := podmanTest.NumberOfContainers() + Expect(numOfCtrs).To(Equal(2)) + + podPs := podmanTest.Podman([]string{"pod", "ps", "-q"}) + podPs.WaitWithDefaultTimeout() + Expect(podPs).Should(Exit(0)) + podID := podPs.OutputToString() + + ps := podmanTest.Podman([]string{"ps", "--format", "{{.Names}}"}) + ps.WaitWithDefaultTimeout() + Expect(ps).Should(Exit(0)) + Expect(ps.OutputToString()).To(ContainSubstring(podID[:12] + "-infra")) + }) })