Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ports and hostname correctly in kube yaml #14181

Merged
merged 1 commit into from
Jul 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 39 additions & 14 deletions libpod/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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)
Expand Down Expand Up @@ -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",
Expand All @@ -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,
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions pkg/domain/infra/abi/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,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.
Expand All @@ -76,6 +77,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
}
Expand All @@ -95,6 +107,15 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string,
return nil, errors.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)
Expand Down
103 changes: 103 additions & 0 deletions test/e2e/generate_kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down