diff options
-rw-r--r-- | libpod/kube.go | 53 | ||||
-rw-r--r-- | pkg/domain/infra/abi/generate.go | 21 | ||||
-rw-r--r-- | test/e2e/generate_kube_test.go | 103 |
3 files changed, 163 insertions, 14 deletions
diff --git a/libpod/kube.go b/libpod/kube.go index 3cb0489b3..8c09a6bb5 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -353,6 +353,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po podInitCtrs := []v1.Container{} podAnnotations := make(map[string]string) dnsInfo := v1.PodDNSConfig{} + var hostname string // 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 @@ -368,6 +369,14 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po podAnnotations[k] = TruncateKubeAnnotation(v) } isInit := ctr.IsInitCtr() + // Since hostname is only set at pod level, set the hostname to the hostname of the first container we encounter + if hostname == "" { + // Only set the hostname if it is not set to the truncated container ID, which we do by default if no + // hostname is specified for the container + if !strings.Contains(ctr.ID(), ctr.Hostname()) { + hostname = ctr.Hostname() + } + } ctr, volumes, _, annotations, err := containerToV1Container(ctx, ctr) if err != nil { @@ -377,17 +386,21 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po podAnnotations[define.BindMountPrefix+k] = TruncateKubeAnnotation(v) } // Since port bindings for the pod are handled by the - // infra container, wipe them here. - ctr.Ports = nil - - // We add the original port declarations from the libpod infra container - // to the first kubernetes container description because otherwise we loose - // the original container/port bindings. - // Add the port configuration to the first regular container or the first - // init container if only init containers have been created in the pod. - if first && len(ports) > 0 && (!isInit || len(containers) == 2) { - ctr.Ports = ports - first = false + // infra container, wipe them here only if we are sharing the net namespace + // If the network namespace is not being shared in the pod, then containers + // can have their own network configurations + if p.SharesNet() { + ctr.Ports = nil + + // We add the original port declarations from the libpod infra container + // to the first kubernetes container description because otherwise we loose + // the original container/port bindings. + // Add the port configuration to the first regular container or the first + // init container if only init containers have been created in the pod. + if first && len(ports) > 0 && (!isInit || len(containers) == 2) { + ctr.Ports = ports + first = false + } } if isInit { podInitCtrs = append(podInitCtrs, ctr) @@ -430,10 +443,11 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po podContainers, podVolumes, &dnsInfo, - hostNetwork), nil + hostNetwork, + hostname), nil } -func newPodObject(podName string, annotations map[string]string, initCtrs, containers []v1.Container, volumes []v1.Volume, dnsOptions *v1.PodDNSConfig, hostNetwork bool) *v1.Pod { +func newPodObject(podName string, annotations map[string]string, initCtrs, containers []v1.Container, volumes []v1.Volume, dnsOptions *v1.PodDNSConfig, hostNetwork bool, hostname string) *v1.Pod { tm := v12.TypeMeta{ Kind: "Pod", APIVersion: "v1", @@ -454,6 +468,7 @@ func newPodObject(podName string, annotations map[string]string, initCtrs, conta } ps := v1.PodSpec{ Containers: containers, + Hostname: hostname, HostNetwork: hostNetwork, InitContainers: initCtrs, Volumes: volumes, @@ -479,6 +494,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, podDNS := v1.PodDNSConfig{} kubeAnnotations := make(map[string]string) ctrNames := make([]string, 0, len(ctrs)) + var hostname string for _, ctr := range ctrs { ctrNames = append(ctrNames, removeUnderscores(ctr.Name())) for k, v := range ctr.config.Spec.Annotations { @@ -491,6 +507,14 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, } isInit := ctr.IsInitCtr() + // Since hostname is only set at pod level, set the hostname to the hostname of the first container we encounter + if hostname == "" { + // Only set the hostname if it is not set to the truncated container ID, which we do by default if no + // hostname is specified for the container + if !strings.Contains(ctr.ID(), ctr.Hostname()) { + hostname = ctr.Hostname() + } + } if !ctr.HostNetwork() { hostNetwork = false @@ -555,7 +579,8 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, kubeCtrs, kubeVolumes, &podDNS, - hostNetwork), nil + hostNetwork, + hostname), nil } // containerToV1Container converts information we know about a libpod container diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go index 5c1047b74..31885ce54 100644 --- a/pkg/domain/infra/abi/generate.go +++ b/pkg/domain/infra/abi/generate.go @@ -50,6 +50,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, content [][]byte ) + defaultKubeNS := true // Lookup for podman objects. for _, nameOrID := range nameOrIDs { // Let's assume it's a container, so get the container. @@ -75,6 +76,17 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, return nil, err } } else { + // Get the pod config to see if the user has modified the default + // namespace sharing values as this might affect the pods when run + // in a k8s cluster + podConfig, err := pod.Config() + if err != nil { + return nil, err + } + if !(podConfig.UsePodIPC && podConfig.UsePodNet && podConfig.UsePodUTS) { + defaultKubeNS = false + } + pods = append(pods, pod) continue } @@ -94,6 +106,15 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, return nil, fmt.Errorf("name or ID %q not found", nameOrID) } + if !defaultKubeNS { + warning := ` +# NOTE: The namespace sharing for a pod has been modified by the user and is not the same as the +# default settings for kubernetes. This can lead to unexpected behavior when running the generated +# kube yaml in a kubernetes cluster. +` + content = append(content, []byte(warning)) + } + // Generate kube persistent volume claims from volumes. if len(vols) >= 1 { pvs, err := getKubePVCs(vols) diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 2ca774a03..845aa60ce 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -328,6 +328,109 @@ var _ = Describe("Podman generate kube", func() { Expect(pod.Spec.HostAliases[1]).To(HaveField("IP", testIP)) }) + It("podman generate kube with network sharing", func() { + // Expect error with default sharing options as Net namespace is shared + podName := "testPod" + podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName}) + podSession.WaitWithDefaultTimeout() + Expect(podSession).Should(Exit(0)) + + ctrSession := podmanTest.Podman([]string{"create", "--name", "testCtr", "--pod", podName, "-p", "9000:8000", ALPINE, "top"}) + ctrSession.WaitWithDefaultTimeout() + Expect(ctrSession).Should(Exit(125)) + + // Ports without Net sharing should work with ports being set for each container in the generated kube yaml + podName = "testNet" + podSession = podmanTest.Podman([]string{"pod", "create", "--name", podName, "--share", "ipc"}) + podSession.WaitWithDefaultTimeout() + Expect(podSession).Should(Exit(0)) + + ctr1Name := "ctr1" + ctr1Session := podmanTest.Podman([]string{"create", "--name", ctr1Name, "--pod", podName, "-p", "9000:8000", ALPINE, "top"}) + ctr1Session.WaitWithDefaultTimeout() + Expect(ctr1Session).Should(Exit(0)) + + ctr2Name := "ctr2" + ctr2Session := podmanTest.Podman([]string{"create", "--name", ctr2Name, "--pod", podName, "-p", "6000:5000", ALPINE, "top"}) + ctr2Session.WaitWithDefaultTimeout() + Expect(ctr2Session).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).To(BeNil()) + Expect(pod.Spec.Containers).To(HaveLen(2)) + Expect(pod.Spec.Containers[0].Ports[0].ContainerPort).To(Equal(int32(8000))) + Expect(pod.Spec.Containers[1].Ports[0].ContainerPort).To(Equal(int32(5000))) + Expect(pod.Spec.Containers[0].Ports[0].HostPort).To(Equal(int32(9000))) + Expect(pod.Spec.Containers[1].Ports[0].HostPort).To(Equal(int32(6000))) + }) + + It("podman generate kube with and without hostname", func() { + // Expect error with default sharing options as UTS namespace is shared + podName := "testPod" + podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName}) + podSession.WaitWithDefaultTimeout() + Expect(podSession).Should(Exit(0)) + + ctrSession := podmanTest.Podman([]string{"create", "--name", "testCtr", "--pod", podName, "--hostname", "test-hostname", ALPINE, "top"}) + ctrSession.WaitWithDefaultTimeout() + Expect(ctrSession).Should(Exit(125)) + + // Hostname without uts sharing should work, but generated kube yaml will have pod hostname + // set to the hostname of the first container + podName = "testHostname" + podSession = podmanTest.Podman([]string{"pod", "create", "--name", podName, "--share", "ipc"}) + podSession.WaitWithDefaultTimeout() + Expect(podSession).Should(Exit(0)) + + ctr1Name := "ctr1" + ctr1HostName := "ctr1-hostname" + ctr1Session := podmanTest.Podman([]string{"create", "--name", ctr1Name, "--pod", podName, "--hostname", ctr1HostName, ALPINE, "top"}) + ctr1Session.WaitWithDefaultTimeout() + Expect(ctr1Session).Should(Exit(0)) + + ctr2Name := "ctr2" + ctr2Session := podmanTest.Podman([]string{"create", "--name", ctr2Name, "--pod", podName, ALPINE, "top"}) + ctr2Session.WaitWithDefaultTimeout() + Expect(ctr2Session).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).To(BeNil()) + Expect(pod.Spec.Containers).To(HaveLen(2)) + Expect(pod.Spec.Hostname).To(Equal(ctr1HostName)) + + // No hostname + + podName = "testNoHostname" + podSession = podmanTest.Podman([]string{"pod", "create", "--name", podName, "--share", "ipc"}) + podSession.WaitWithDefaultTimeout() + Expect(podSession).Should(Exit(0)) + + ctr3Name := "ctr3" + ctr3Session := podmanTest.Podman([]string{"create", "--name", ctr3Name, "--pod", podName, ALPINE, "top"}) + ctr3Session.WaitWithDefaultTimeout() + Expect(ctr3Session).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).To(BeNil()) + Expect(pod.Spec.Containers).To(HaveLen(1)) + Expect(pod.Spec.Hostname).To(BeEmpty()) + }) + It("podman generate service kube on pod", func() { session := podmanTest.Podman([]string{"create", "--pod", "new:test-pod", "-p", "4000:4000/udp", ALPINE, "ls"}) session.WaitWithDefaultTimeout() |