Set /etc/hostname.

Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
Lantao Liu 2019-02-07 18:30:15 -08:00
parent 7c2498d2e6
commit 089d4fbfb8
6 changed files with 137 additions and 11 deletions

View File

@ -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)
})
}
}

View File

@ -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,

View File

@ -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",

View File

@ -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")

View File

@ -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 {

View File

@ -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{