From 936e30f68be0a13f1452aaa65993debeee162c85 Mon Sep 17 00:00:00 2001
From: Urvashi Mohnani <umohnani@redhat.com>
Date: Tue, 17 Oct 2023 11:27:24 -0400
Subject: [PATCH] Use node hostname in kube play when hostNetwork=true

When the hostNetwork option is set to true in the k8s yaml,
set the pod's hostname to the name of the machine/node as is
done in k8s. Also set the utsns to host.

Signed-off-by: Urvashi Mohnani <umohnani@redhat.com>
---
 pkg/domain/entities/pods.go       |  7 +++
 pkg/domain/infra/abi/play.go      |  2 +
 pkg/specgen/generate/kube/kube.go | 11 +++++
 test/e2e/play_kube_test.go        | 81 +++++++++++++++++++++++++++++++
 4 files changed, 101 insertions(+)

diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index c6bdc0f553..0baff93a66 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -146,6 +146,7 @@ type PodCreateOptions struct {
 	VolumesFrom        []string          `json:"volumes_from,omitempty"`
 	SecurityOpt        []string          `json:"security_opt,omitempty"`
 	Sysctl             []string          `json:"sysctl,omitempty"`
+	Uts                string            `json:"uts,omitempty"`
 }
 
 // PodLogsOptions describes the options to extract pod logs.
@@ -362,6 +363,12 @@ func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.Pod
 		return nil, err
 	}
 	s.Ipc = out
+
+	out, err = specgen.ParseNamespace(p.Uts)
+	if err != nil {
+		return nil, err
+	}
+	s.UtsNs = out
 	s.Hostname = p.Hostname
 	s.ExitPolicy = p.ExitPolicy
 	s.Labels = p.Labels
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 6d9e67a377..0bfb12d1e8 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -789,6 +789,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
 			SecretsManager:     secretsManager,
 			UserNSIsHost:       p.Userns.IsHost(),
 			Volumes:            volumes,
+			UtsNSIsHost:        p.UtsNs.IsHost(),
 		}
 		specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
 		if err != nil {
@@ -858,6 +859,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
 			SecretsManager:     secretsManager,
 			UserNSIsHost:       p.Userns.IsHost(),
 			Volumes:            volumes,
+			UtsNSIsHost:        p.UtsNs.IsHost(),
 		}
 
 		if podYAML.Spec.TerminationGracePeriodSeconds != nil {
diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go
index bff697979e..a9b742ebe2 100644
--- a/pkg/specgen/generate/kube/kube.go
+++ b/pkg/specgen/generate/kube/kube.go
@@ -69,6 +69,12 @@ func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions,
 	}
 	if podYAML.Spec.HostNetwork {
 		p.Net.Network = specgen.Namespace{NSMode: "host"}
+		nodeHostName, err := os.Hostname()
+		if err != nil {
+			return p, err
+		}
+		p.Hostname = nodeHostName
+		p.Uts = "host"
 	}
 	if podYAML.Spec.HostAliases != nil {
 		if p.Net.NoHosts {
@@ -156,6 +162,8 @@ type CtrSpecGenOptions struct {
 	UserNSIsHost bool
 	// PidNSIsHost tells the container to use the host pidns
 	PidNSIsHost bool
+	// UtsNSIsHost tells the container to use the host utsns
+	UtsNSIsHost bool
 	// SecretManager to access the secrets
 	SecretsManager *secrets.SecretsManager
 	// LogDriver which should be used for the container
@@ -563,6 +571,9 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
 	if opts.IpcNSIsHost {
 		s.IpcNS.NSMode = specgen.Host
 	}
+	if opts.UtsNSIsHost {
+		s.UtsNS.NSMode = specgen.Host
+	}
 
 	// Add labels that come from kube
 	if len(s.Labels) == 0 {
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index a8102f650e..38139e159c 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -6256,4 +6256,85 @@ EXPOSE 2004-2005/tcp`, CITEST_IMAGE)
 		Expect(inspect).Should(ExitCleanly())
 		Expect(inspect.OutputToString()).To(Equal("20"))
 	})
+
+	It("hostname should be node name when hostNetwork=true", func() {
+		netYaml := `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  hostNetwork: true
+  hostname: blah
+  containers:
+    - name: alpine
+      image: alpine
+      command:
+        - sleep
+        - "100"
+`
+
+		err := writeYaml(netYaml, kubeYaml)
+		Expect(err).ToNot(HaveOccurred())
+		kube := podmanTest.Podman([]string{"kube", "play", kubeYaml})
+		kube.WaitWithDefaultTimeout()
+		Expect(kube).Should(ExitCleanly())
+
+		// Get the name of the host
+		hostname, err := os.Hostname()
+		Expect(err).ToNot(HaveOccurred())
+
+		exec := podmanTest.Podman([]string{"exec", "test-pod-alpine", "hostname"})
+		exec.WaitWithDefaultTimeout()
+		Expect(exec).Should(ExitCleanly())
+		Expect(exec.OutputToString()).To(Equal(hostname))
+
+		// Check that the UTS namespace is set to host also
+		hostUts := SystemExec("ls", []string{"-l", "/proc/self/ns/uts"})
+		Expect(hostUts).Should(ExitCleanly())
+		arr := strings.Split(hostUts.OutputToString(), " ")
+		exec = podmanTest.Podman([]string{"exec", "test-pod-alpine", "ls", "-l", "/proc/self/ns/uts"})
+		exec.WaitWithDefaultTimeout()
+		Expect(exec).Should(ExitCleanly())
+		execArr := strings.Split(exec.OutputToString(), " ")
+		Expect(execArr[len(execArr)-1]).To(ContainSubstring(arr[len(arr)-1]))
+	})
+
+	It("hostname should be pod name when hostNetwork=false", func() {
+		netYaml := `
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+    - name: alpine
+      image: alpine
+      command:
+        - sleep
+        - "100"
+`
+
+		err := writeYaml(netYaml, kubeYaml)
+		Expect(err).ToNot(HaveOccurred())
+		kube := podmanTest.Podman([]string{"kube", "play", kubeYaml})
+		kube.WaitWithDefaultTimeout()
+		Expect(kube).Should(ExitCleanly())
+
+		exec := podmanTest.Podman([]string{"exec", "test-pod-alpine", "hostname"})
+		exec.WaitWithDefaultTimeout()
+		Expect(exec).Should(ExitCleanly())
+		Expect(exec.OutputToString()).To(Equal("test-pod"))
+
+		// Check that the UTS namespace is set to host also
+		hostUts := SystemExec("ls", []string{"-l", "/proc/self/ns/uts"})
+		Expect(hostUts).Should(ExitCleanly())
+		arr := strings.Split(hostUts.OutputToString(), " ")
+		exec = podmanTest.Podman([]string{"exec", "test-pod-alpine", "ls", "-l", "/proc/self/ns/uts"})
+		exec.WaitWithDefaultTimeout()
+		Expect(exec).Should(ExitCleanly())
+		execArr := strings.Split(exec.OutputToString(), " ")
+		Expect(execArr[len(execArr)-1]).To(Not(ContainSubstring(arr[len(arr)-1])))
+	})
+
 })