Skip to content
This repository was archived by the owner on Mar 9, 2022. It is now read-only.

Commit 3c7c404

Browse files
committed
Set /etc/hostname.
Signed-off-by: Lantao Liu <[email protected]>
1 parent 71909a1 commit 3c7c404

7 files changed

Lines changed: 254 additions & 6 deletions

File tree

integration/pod_hostname_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
Copyright 2018 The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package integration
18+
19+
import (
20+
"io/ioutil"
21+
"os"
22+
"path/filepath"
23+
"testing"
24+
"time"
25+
26+
"github.com/stretchr/testify/assert"
27+
"github.com/stretchr/testify/require"
28+
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
29+
)
30+
31+
func TestPodHostname(t *testing.T) {
32+
hostname, err := os.Hostname()
33+
require.NoError(t, err)
34+
for name, test := range map[string]struct {
35+
opts []PodSandboxOpts
36+
expectedHostname string
37+
}{
38+
"regular pod with custom hostname": {
39+
opts: []PodSandboxOpts{
40+
WithPodHostname("test-hostname"),
41+
},
42+
expectedHostname: "test-hostname",
43+
},
44+
"host network pod without custom hostname": {
45+
opts: []PodSandboxOpts{
46+
WithHostNetwork,
47+
},
48+
expectedHostname: hostname,
49+
},
50+
"host network pod with custom hostname": {
51+
opts: []PodSandboxOpts{
52+
WithHostNetwork,
53+
WithPodHostname("test-hostname"),
54+
},
55+
expectedHostname: "test-hostname",
56+
},
57+
} {
58+
t.Run(name, func(t *testing.T) {
59+
testPodLogDir, err := ioutil.TempDir("/tmp", "hostname")
60+
require.NoError(t, err)
61+
defer os.RemoveAll(testPodLogDir)
62+
63+
opts := append(test.opts, WithPodLogDirectory(testPodLogDir))
64+
t.Log("Create a sandbox with hostname")
65+
sbConfig := PodSandboxConfig("sandbox", "hostname", opts...)
66+
sb, err := runtimeService.RunPodSandbox(sbConfig)
67+
require.NoError(t, err)
68+
defer func() {
69+
assert.NoError(t, runtimeService.StopPodSandbox(sb))
70+
assert.NoError(t, runtimeService.RemovePodSandbox(sb))
71+
}()
72+
73+
const (
74+
testImage = "busybox"
75+
containerName = "test-container"
76+
)
77+
t.Logf("Pull test image %q", testImage)
78+
img, err := imageService.PullImage(&runtime.ImageSpec{Image: testImage}, nil)
79+
require.NoError(t, err)
80+
defer func() {
81+
assert.NoError(t, imageService.RemoveImage(&runtime.ImageSpec{Image: img}))
82+
}()
83+
84+
t.Log("Create a container to print env")
85+
cnConfig := ContainerConfig(
86+
containerName,
87+
testImage,
88+
WithCommand("sh", "-c",
89+
"echo -n /etc/hostname= && cat /etc/hostname && env"),
90+
WithLogPath(containerName),
91+
)
92+
cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
93+
require.NoError(t, err)
94+
95+
t.Log("Start the container")
96+
require.NoError(t, runtimeService.StartContainer(cn))
97+
98+
t.Log("Wait for container to finish running")
99+
require.NoError(t, Eventually(func() (bool, error) {
100+
s, err := runtimeService.ContainerStatus(cn)
101+
if err != nil {
102+
return false, err
103+
}
104+
if s.GetState() == runtime.ContainerState_CONTAINER_EXITED {
105+
return true, nil
106+
}
107+
return false, nil
108+
}, time.Second, 30*time.Second))
109+
110+
content, err := ioutil.ReadFile(filepath.Join(testPodLogDir, containerName))
111+
assert.NoError(t, err)
112+
113+
t.Log("Search hostname env in container log")
114+
assert.Contains(t, string(content), "HOSTNAME="+test.expectedHostname)
115+
116+
t.Log("Search /etc/hostname content in container log")
117+
assert.Contains(t, string(content), "/etc/hostname="+test.expectedHostname)
118+
})
119+
}
120+
}

integration/test_utils.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ func WithPodLogDirectory(dir string) PodSandboxOpts {
127127
}
128128
}
129129

130+
// Add pod hostname.
131+
func WithPodHostname(hostname string) PodSandboxOpts {
132+
return func(p *runtime.PodSandboxConfig) {
133+
p.Hostname = hostname
134+
}
135+
}
136+
130137
// PodSandboxConfig generates a pod sandbox config for test.
131138
func PodSandboxConfig(name, ns string, opts ...PodSandboxOpts) *runtime.PodSandboxConfig {
132139
config := &runtime.PodSandboxConfig{

pkg/server/container_create.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,7 @@ func (c *criService) generateContainerSpec(id string, sandboxID string, sandboxP
346346

347347
// Add HOSTNAME env.
348348
hostname := sandboxConfig.GetHostname()
349-
if sandboxConfig.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE &&
350-
hostname == "" {
349+
if sandboxConfig.GetHostname() == "" {
351350
hostname, err = c.os.Hostname()
352351
if err != nil {
353352
return nil, err
@@ -466,6 +465,14 @@ func (c *criService) generateVolumeMounts(containerRootDir string, criMounts []*
466465
func (c *criService) generateContainerMounts(sandboxID string, config *runtime.ContainerConfig) []*runtime.Mount {
467466
var mounts []*runtime.Mount
468467
securityContext := config.GetLinux().GetSecurityContext()
468+
if !isInCRIMounts(etcHostname, config.GetMounts()) {
469+
mounts = append(mounts, &runtime.Mount{
470+
ContainerPath: etcHostname,
471+
HostPath: c.getSandboxHostname(sandboxID),
472+
Readonly: securityContext.GetReadonlyRootfs(),
473+
})
474+
}
475+
469476
if !isInCRIMounts(etcHosts, config.GetMounts()) {
470477
mounts = append(mounts, &runtime.Mount{
471478
ContainerPath: etcHosts,

pkg/server/container_create_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,11 @@ func TestGenerateContainerMounts(t *testing.T) {
534534
ReadonlyRootfs: true,
535535
},
536536
expectedMounts: []*runtime.Mount{
537+
{
538+
ContainerPath: "/etc/hostname",
539+
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hostname"),
540+
Readonly: true,
541+
},
537542
{
538543
ContainerPath: "/etc/hosts",
539544
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hosts"),
@@ -554,6 +559,11 @@ func TestGenerateContainerMounts(t *testing.T) {
554559
"should setup rw mount when rootfs is read-write": {
555560
securityContext: &runtime.LinuxContainerSecurityContext{},
556561
expectedMounts: []*runtime.Mount{
562+
{
563+
ContainerPath: "/etc/hostname",
564+
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hostname"),
565+
Readonly: false,
566+
},
557567
{
558568
ContainerPath: "/etc/hosts",
559569
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hosts"),
@@ -576,6 +586,11 @@ func TestGenerateContainerMounts(t *testing.T) {
576586
NamespaceOptions: &runtime.NamespaceOption{Ipc: runtime.NamespaceMode_NODE},
577587
},
578588
expectedMounts: []*runtime.Mount{
589+
{
590+
ContainerPath: "/etc/hostname",
591+
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hostname"),
592+
Readonly: false,
593+
},
579594
{
580595
ContainerPath: "/etc/hosts",
581596
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hosts"),
@@ -595,6 +610,10 @@ func TestGenerateContainerMounts(t *testing.T) {
595610
},
596611
"should skip contaner mounts if already mounted by CRI": {
597612
criMounts: []*runtime.Mount{
613+
{
614+
ContainerPath: "/etc/hostname",
615+
HostPath: "/test-etc-hostname",
616+
},
598617
{
599618
ContainerPath: "/etc/hosts",
600619
HostPath: "/test-etc-host",

pkg/server/helpers.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ const (
9595
devShm = "/dev/shm"
9696
// etcHosts is the default path of /etc/hosts file.
9797
etcHosts = "/etc/hosts"
98+
// etcHostname is the default path of /etc/hostname file.
99+
etcHostname = "/etc/hostname"
98100
// resolvConfPath is the abs path of resolv.conf on host or container.
99101
resolvConfPath = "/etc/resolv.conf"
100102
// hostnameEnv is the key for HOSTNAME env.
@@ -183,6 +185,11 @@ func (c *criService) getVolatileContainerRootDir(id string) string {
183185
return filepath.Join(c.config.StateDir, containersDir, id)
184186
}
185187

188+
// getSandboxHostname returns the hostname file path inside the sandbox root directory.
189+
func (c *criService) getSandboxHostname(id string) string {
190+
return filepath.Join(c.getSandboxRootDir(id), "hostname")
191+
}
192+
186193
// getSandboxHosts returns the hosts file path inside the sandbox root directory.
187194
func (c *criService) getSandboxHosts(id string) string {
188195
return filepath.Join(c.getSandboxRootDir(id), "hosts")

pkg/server/sandbox_run.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
227227
}
228228
}()
229229

230-
// Setup sandbox /dev/shm, /etc/hosts and /etc/resolv.conf.
230+
// Setup sandbox /dev/shm, /etc/hosts, /etc/resolv.conf and /etc/hostname.
231231
if err = c.setupSandboxFiles(id, config); err != nil {
232232
return nil, errors.Wrapf(err, "failed to setup sandbox files")
233233
}
@@ -431,9 +431,22 @@ func (c *criService) generateSandboxContainerSpec(id string, config *runtime.Pod
431431
return g.Spec(), nil
432432
}
433433

434-
// setupSandboxFiles sets up necessary sandbox files including /dev/shm, /etc/hosts
435-
// and /etc/resolv.conf.
434+
// setupSandboxFiles sets up necessary sandbox files including /dev/shm, /etc/hosts,
435+
// /etc/resolv.conf and /etc/hostname.
436436
func (c *criService) setupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
437+
sandboxEtcHostname := c.getSandboxHostname(id)
438+
hostname := config.GetHostname()
439+
if hostname == "" {
440+
var err error
441+
hostname, err = c.os.Hostname()
442+
if err != nil {
443+
return errors.Wrap(err, "failed to get hostname")
444+
}
445+
}
446+
if err := c.os.WriteFile(sandboxEtcHostname, []byte(hostname+"\n"), 0644); err != nil {
447+
return errors.Wrapf(err, "failed to write hostname to %q", sandboxEtcHostname)
448+
}
449+
437450
// TODO(random-liu): Consider whether we should maintain /etc/hosts and /etc/resolv.conf in kubelet.
438451
sandboxEtcHosts := c.getSandboxHosts(id)
439452
if err := c.os.CopyFile(etcHosts, sandboxEtcHosts, 0644); err != nil {

pkg/server/sandbox_run_test.go

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,30 @@ func TestGenerateSandboxContainerSpec(t *testing.T) {
178178
}
179179

180180
func TestSetupSandboxFiles(t *testing.T) {
181-
const testID = "test-id"
181+
const (
182+
testID = "test-id"
183+
realhostname = "test-real-hostname"
184+
)
182185
for desc, test := range map[string]struct {
183186
dnsConfig *runtime.DNSConfig
187+
hostname string
184188
ipcMode runtime.NamespaceMode
185189
expectedCalls []ostesting.CalledDetail
186190
}{
187191
"should check host /dev/shm existence when ipc mode is NODE": {
188192
ipcMode: runtime.NamespaceMode_NODE,
189193
expectedCalls: []ostesting.CalledDetail{
194+
{
195+
Name: "Hostname",
196+
},
197+
{
198+
Name: "WriteFile",
199+
Arguments: []interface{}{
200+
filepath.Join(testRootDir, sandboxesDir, testID, "hostname"),
201+
[]byte(realhostname + "\n"),
202+
os.FileMode(0644),
203+
},
204+
},
190205
{
191206
Name: "CopyFile",
192207
Arguments: []interface{}{
@@ -217,6 +232,17 @@ func TestSetupSandboxFiles(t *testing.T) {
217232
},
218233
ipcMode: runtime.NamespaceMode_NODE,
219234
expectedCalls: []ostesting.CalledDetail{
235+
{
236+
Name: "Hostname",
237+
},
238+
{
239+
Name: "WriteFile",
240+
Arguments: []interface{}{
241+
filepath.Join(testRootDir, sandboxesDir, testID, "hostname"),
242+
[]byte(realhostname + "\n"),
243+
os.FileMode(0644),
244+
},
245+
},
220246
{
221247
Name: "CopyFile",
222248
Arguments: []interface{}{
@@ -244,6 +270,17 @@ options timeout:1
244270
"should create sandbox shm when ipc namespace mode is not NODE": {
245271
ipcMode: runtime.NamespaceMode_POD,
246272
expectedCalls: []ostesting.CalledDetail{
273+
{
274+
Name: "Hostname",
275+
},
276+
{
277+
Name: "WriteFile",
278+
Arguments: []interface{}{
279+
filepath.Join(testRootDir, sandboxesDir, testID, "hostname"),
280+
[]byte(realhostname + "\n"),
281+
os.FileMode(0644),
282+
},
283+
},
247284
{
248285
Name: "CopyFile",
249286
Arguments: []interface{}{
@@ -273,10 +310,48 @@ options timeout:1
273310
},
274311
},
275312
},
313+
"should create /etc/hostname when hostname is set": {
314+
hostname: "test-hostname",
315+
ipcMode: runtime.NamespaceMode_NODE,
316+
expectedCalls: []ostesting.CalledDetail{
317+
{
318+
Name: "WriteFile",
319+
Arguments: []interface{}{
320+
filepath.Join(testRootDir, sandboxesDir, testID, "hostname"),
321+
[]byte("test-hostname\n"),
322+
os.FileMode(0644),
323+
},
324+
},
325+
{
326+
Name: "CopyFile",
327+
Arguments: []interface{}{
328+
"/etc/hosts",
329+
filepath.Join(testRootDir, sandboxesDir, testID, "hosts"),
330+
os.FileMode(0644),
331+
},
332+
},
333+
{
334+
Name: "CopyFile",
335+
Arguments: []interface{}{
336+
"/etc/resolv.conf",
337+
filepath.Join(testRootDir, sandboxesDir, testID, "resolv.conf"),
338+
os.FileMode(0644),
339+
},
340+
},
341+
{
342+
Name: "Stat",
343+
Arguments: []interface{}{"/dev/shm"},
344+
},
345+
},
346+
},
276347
} {
277348
t.Logf("TestCase %q", desc)
278349
c := newTestCRIService()
350+
c.os.(*ostesting.FakeOS).HostnameFn = func() (string, error) {
351+
return realhostname, nil
352+
}
279353
cfg := &runtime.PodSandboxConfig{
354+
Hostname: test.hostname,
280355
DnsConfig: test.dnsConfig,
281356
Linux: &runtime.LinuxPodSandboxConfig{
282357
SecurityContext: &runtime.LinuxSandboxSecurityContext{

0 commit comments

Comments
 (0)