From 089d4fbfb88f243f0066d5d72c8709d6fd491547 Mon Sep 17 00:00:00 2001 From: Lantao Liu Date: Thu, 7 Feb 2019 18:30:15 -0800 Subject: [PATCH] Set /etc/hostname. Signed-off-by: Lantao Liu --- ...tname_env_test.go => pod_hostname_test.go} | 15 ++-- pkg/server/container_create.go | 11 ++- pkg/server/container_create_test.go | 19 +++++ pkg/server/helpers.go | 7 ++ pkg/server/sandbox_run.go | 19 ++++- pkg/server/sandbox_run_test.go | 77 ++++++++++++++++++- 6 files changed, 137 insertions(+), 11 deletions(-) rename integration/{pod_hostname_env_test.go => pod_hostname_test.go} (89%) diff --git a/integration/pod_hostname_env_test.go b/integration/pod_hostname_test.go similarity index 89% rename from integration/pod_hostname_env_test.go rename to integration/pod_hostname_test.go index a317e3cd5..1c01494b7 100644 --- a/integration/pod_hostname_env_test.go +++ b/integration/pod_hostname_test.go @@ -28,7 +28,7 @@ import ( runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" ) -func TestPodHostnameEnv(t *testing.T) { +func TestPodHostname(t *testing.T) { hostname, err := os.Hostname() require.NoError(t, err) for name, test := range map[string]struct { @@ -56,13 +56,13 @@ func TestPodHostnameEnv(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - testPodLogDir, err := ioutil.TempDir("/tmp", "hostname-env") + testPodLogDir, err := ioutil.TempDir("/tmp", "hostname") require.NoError(t, err) defer os.RemoveAll(testPodLogDir) opts := append(test.opts, WithPodLogDirectory(testPodLogDir)) t.Log("Create a sandbox with hostname") - sbConfig := PodSandboxConfig("sandbox", "hostname-env", opts...) + sbConfig := PodSandboxConfig("sandbox", "hostname", opts...) sb, err := runtimeService.RunPodSandbox(sbConfig, *runtimeHandler) require.NoError(t, err) defer func() { @@ -85,7 +85,8 @@ func TestPodHostnameEnv(t *testing.T) { cnConfig := ContainerConfig( containerName, testImage, - WithCommand("env"), + WithCommand("sh", "-c", + "echo -n /etc/hostname= && cat /etc/hostname && env"), WithLogPath(containerName), ) cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig) @@ -106,10 +107,14 @@ func TestPodHostnameEnv(t *testing.T) { return false, nil }, time.Second, 30*time.Second)) - t.Log("Search hostname env in container log") content, err := ioutil.ReadFile(filepath.Join(testPodLogDir, containerName)) assert.NoError(t, err) + + t.Log("Search hostname env in container log") assert.Contains(t, string(content), "HOSTNAME="+test.expectedHostname) + + t.Log("Search /etc/hostname content in container log") + assert.Contains(t, string(content), "/etc/hostname="+test.expectedHostname) }) } } diff --git a/pkg/server/container_create.go b/pkg/server/container_create.go index 1d3960550..ade4ed26a 100644 --- a/pkg/server/container_create.go +++ b/pkg/server/container_create.go @@ -339,8 +339,7 @@ func (c *criService) generateContainerSpec(id string, sandboxID string, sandboxP // Add HOSTNAME env. hostname := sandboxConfig.GetHostname() - if sandboxConfig.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE && - hostname == "" { + if sandboxConfig.GetHostname() == "" { hostname, err = c.os.Hostname() if err != nil { return nil, err @@ -483,6 +482,14 @@ func (c *criService) generateVolumeMounts(containerRootDir string, criMounts []* func (c *criService) generateContainerMounts(sandboxID string, config *runtime.ContainerConfig) []*runtime.Mount { var mounts []*runtime.Mount securityContext := config.GetLinux().GetSecurityContext() + if !isInCRIMounts(etcHostname, config.GetMounts()) { + mounts = append(mounts, &runtime.Mount{ + ContainerPath: etcHostname, + HostPath: c.getSandboxHostname(sandboxID), + Readonly: securityContext.GetReadonlyRootfs(), + }) + } + if !isInCRIMounts(etcHosts, config.GetMounts()) { mounts = append(mounts, &runtime.Mount{ ContainerPath: etcHosts, diff --git a/pkg/server/container_create_test.go b/pkg/server/container_create_test.go index c769ddbce..6828c0e3d 100644 --- a/pkg/server/container_create_test.go +++ b/pkg/server/container_create_test.go @@ -534,6 +534,11 @@ func TestGenerateContainerMounts(t *testing.T) { ReadonlyRootfs: true, }, expectedMounts: []*runtime.Mount{ + { + ContainerPath: "/etc/hostname", + HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hostname"), + Readonly: true, + }, { ContainerPath: "/etc/hosts", HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hosts"), @@ -554,6 +559,11 @@ func TestGenerateContainerMounts(t *testing.T) { "should setup rw mount when rootfs is read-write": { securityContext: &runtime.LinuxContainerSecurityContext{}, expectedMounts: []*runtime.Mount{ + { + ContainerPath: "/etc/hostname", + HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hostname"), + Readonly: false, + }, { ContainerPath: "/etc/hosts", HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hosts"), @@ -576,6 +586,11 @@ func TestGenerateContainerMounts(t *testing.T) { NamespaceOptions: &runtime.NamespaceOption{Ipc: runtime.NamespaceMode_NODE}, }, expectedMounts: []*runtime.Mount{ + { + ContainerPath: "/etc/hostname", + HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hostname"), + Readonly: false, + }, { ContainerPath: "/etc/hosts", HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hosts"), @@ -595,6 +610,10 @@ func TestGenerateContainerMounts(t *testing.T) { }, "should skip container mounts if already mounted by CRI": { criMounts: []*runtime.Mount{ + { + ContainerPath: "/etc/hostname", + HostPath: "/test-etc-hostname", + }, { ContainerPath: "/etc/hosts", HostPath: "/test-etc-host", diff --git a/pkg/server/helpers.go b/pkg/server/helpers.go index b44be5a2a..b00c905b9 100644 --- a/pkg/server/helpers.go +++ b/pkg/server/helpers.go @@ -99,6 +99,8 @@ const ( devShm = "/dev/shm" // etcHosts is the default path of /etc/hosts file. etcHosts = "/etc/hosts" + // etcHostname is the default path of /etc/hostname file. + etcHostname = "/etc/hostname" // resolvConfPath is the abs path of resolv.conf on host or container. resolvConfPath = "/etc/resolv.conf" // hostnameEnv is the key for HOSTNAME env. @@ -199,6 +201,11 @@ func (c *criService) getVolatileContainerRootDir(id string) string { return filepath.Join(c.config.StateDir, containersDir, id) } +// getSandboxHostname returns the hostname file path inside the sandbox root directory. +func (c *criService) getSandboxHostname(id string) string { + return filepath.Join(c.getSandboxRootDir(id), "hostname") +} + // getSandboxHosts returns the hosts file path inside the sandbox root directory. func (c *criService) getSandboxHosts(id string) string { return filepath.Join(c.getSandboxRootDir(id), "hosts") diff --git a/pkg/server/sandbox_run.go b/pkg/server/sandbox_run.go index f844a0107..aad88a46e 100644 --- a/pkg/server/sandbox_run.go +++ b/pkg/server/sandbox_run.go @@ -233,7 +233,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox } }() - // Setup sandbox /dev/shm, /etc/hosts and /etc/resolv.conf. + // Setup sandbox /dev/shm, /etc/hosts, /etc/resolv.conf and /etc/hostname. if err = c.setupSandboxFiles(id, config); err != nil { return nil, errors.Wrapf(err, "failed to setup sandbox files") } @@ -456,9 +456,22 @@ func (c *criService) generateSandboxContainerSpec(id string, config *runtime.Pod return g.Config, nil } -// setupSandboxFiles sets up necessary sandbox files including /dev/shm, /etc/hosts -// and /etc/resolv.conf. +// setupSandboxFiles sets up necessary sandbox files including /dev/shm, /etc/hosts, +// /etc/resolv.conf and /etc/hostname. func (c *criService) setupSandboxFiles(id string, config *runtime.PodSandboxConfig) error { + sandboxEtcHostname := c.getSandboxHostname(id) + hostname := config.GetHostname() + if hostname == "" { + var err error + hostname, err = c.os.Hostname() + if err != nil { + return errors.Wrap(err, "failed to get hostname") + } + } + if err := c.os.WriteFile(sandboxEtcHostname, []byte(hostname+"\n"), 0644); err != nil { + return errors.Wrapf(err, "failed to write hostname to %q", sandboxEtcHostname) + } + // TODO(random-liu): Consider whether we should maintain /etc/hosts and /etc/resolv.conf in kubelet. sandboxEtcHosts := c.getSandboxHosts(id) if err := c.os.CopyFile(etcHosts, sandboxEtcHosts, 0644); err != nil { diff --git a/pkg/server/sandbox_run_test.go b/pkg/server/sandbox_run_test.go index 2c9d4a056..2c6a273be 100644 --- a/pkg/server/sandbox_run_test.go +++ b/pkg/server/sandbox_run_test.go @@ -180,15 +180,30 @@ func TestGenerateSandboxContainerSpec(t *testing.T) { } func TestSetupSandboxFiles(t *testing.T) { - const testID = "test-id" + const ( + testID = "test-id" + realhostname = "test-real-hostname" + ) for desc, test := range map[string]struct { dnsConfig *runtime.DNSConfig + hostname string ipcMode runtime.NamespaceMode expectedCalls []ostesting.CalledDetail }{ "should check host /dev/shm existence when ipc mode is NODE": { ipcMode: runtime.NamespaceMode_NODE, expectedCalls: []ostesting.CalledDetail{ + { + Name: "Hostname", + }, + { + Name: "WriteFile", + Arguments: []interface{}{ + filepath.Join(testRootDir, sandboxesDir, testID, "hostname"), + []byte(realhostname + "\n"), + os.FileMode(0644), + }, + }, { Name: "CopyFile", Arguments: []interface{}{ @@ -219,6 +234,17 @@ func TestSetupSandboxFiles(t *testing.T) { }, ipcMode: runtime.NamespaceMode_NODE, expectedCalls: []ostesting.CalledDetail{ + { + Name: "Hostname", + }, + { + Name: "WriteFile", + Arguments: []interface{}{ + filepath.Join(testRootDir, sandboxesDir, testID, "hostname"), + []byte(realhostname + "\n"), + os.FileMode(0644), + }, + }, { Name: "CopyFile", Arguments: []interface{}{ @@ -246,6 +272,17 @@ options timeout:1 "should create sandbox shm when ipc namespace mode is not NODE": { ipcMode: runtime.NamespaceMode_POD, expectedCalls: []ostesting.CalledDetail{ + { + Name: "Hostname", + }, + { + Name: "WriteFile", + Arguments: []interface{}{ + filepath.Join(testRootDir, sandboxesDir, testID, "hostname"), + []byte(realhostname + "\n"), + os.FileMode(0644), + }, + }, { Name: "CopyFile", Arguments: []interface{}{ @@ -275,10 +312,48 @@ options timeout:1 }, }, }, + "should create /etc/hostname when hostname is set": { + hostname: "test-hostname", + ipcMode: runtime.NamespaceMode_NODE, + expectedCalls: []ostesting.CalledDetail{ + { + Name: "WriteFile", + Arguments: []interface{}{ + filepath.Join(testRootDir, sandboxesDir, testID, "hostname"), + []byte("test-hostname\n"), + os.FileMode(0644), + }, + }, + { + Name: "CopyFile", + Arguments: []interface{}{ + "/etc/hosts", + filepath.Join(testRootDir, sandboxesDir, testID, "hosts"), + os.FileMode(0644), + }, + }, + { + Name: "CopyFile", + Arguments: []interface{}{ + "/etc/resolv.conf", + filepath.Join(testRootDir, sandboxesDir, testID, "resolv.conf"), + os.FileMode(0644), + }, + }, + { + Name: "Stat", + Arguments: []interface{}{"/dev/shm"}, + }, + }, + }, } { t.Logf("TestCase %q", desc) c := newTestCRIService() + c.os.(*ostesting.FakeOS).HostnameFn = func() (string, error) { + return realhostname, nil + } cfg := &runtime.PodSandboxConfig{ + Hostname: test.hostname, DnsConfig: test.dnsConfig, Linux: &runtime.LinuxPodSandboxConfig{ SecurityContext: &runtime.LinuxSandboxSecurityContext{