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" runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
) )
func TestPodHostnameEnv(t *testing.T) { func TestPodHostname(t *testing.T) {
hostname, err := os.Hostname() hostname, err := os.Hostname()
require.NoError(t, err) require.NoError(t, err)
for name, test := range map[string]struct { for name, test := range map[string]struct {
@ -56,13 +56,13 @@ func TestPodHostnameEnv(t *testing.T) {
}, },
} { } {
t.Run(name, func(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) require.NoError(t, err)
defer os.RemoveAll(testPodLogDir) defer os.RemoveAll(testPodLogDir)
opts := append(test.opts, WithPodLogDirectory(testPodLogDir)) opts := append(test.opts, WithPodLogDirectory(testPodLogDir))
t.Log("Create a sandbox with hostname") t.Log("Create a sandbox with hostname")
sbConfig := PodSandboxConfig("sandbox", "hostname-env", opts...) sbConfig := PodSandboxConfig("sandbox", "hostname", opts...)
sb, err := runtimeService.RunPodSandbox(sbConfig, *runtimeHandler) sb, err := runtimeService.RunPodSandbox(sbConfig, *runtimeHandler)
require.NoError(t, err) require.NoError(t, err)
defer func() { defer func() {
@ -85,7 +85,8 @@ func TestPodHostnameEnv(t *testing.T) {
cnConfig := ContainerConfig( cnConfig := ContainerConfig(
containerName, containerName,
testImage, testImage,
WithCommand("env"), WithCommand("sh", "-c",
"echo -n /etc/hostname= && cat /etc/hostname && env"),
WithLogPath(containerName), WithLogPath(containerName),
) )
cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig) cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
@ -106,10 +107,14 @@ func TestPodHostnameEnv(t *testing.T) {
return false, nil return false, nil
}, time.Second, 30*time.Second)) }, time.Second, 30*time.Second))
t.Log("Search hostname env in container log")
content, err := ioutil.ReadFile(filepath.Join(testPodLogDir, containerName)) content, err := ioutil.ReadFile(filepath.Join(testPodLogDir, containerName))
assert.NoError(t, err) assert.NoError(t, err)
t.Log("Search hostname env in container log")
assert.Contains(t, string(content), "HOSTNAME="+test.expectedHostname) 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. // Add HOSTNAME env.
hostname := sandboxConfig.GetHostname() hostname := sandboxConfig.GetHostname()
if sandboxConfig.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtime.NamespaceMode_NODE && if sandboxConfig.GetHostname() == "" {
hostname == "" {
hostname, err = c.os.Hostname() hostname, err = c.os.Hostname()
if err != nil { if err != nil {
return nil, err 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 { func (c *criService) generateContainerMounts(sandboxID string, config *runtime.ContainerConfig) []*runtime.Mount {
var mounts []*runtime.Mount var mounts []*runtime.Mount
securityContext := config.GetLinux().GetSecurityContext() 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()) { if !isInCRIMounts(etcHosts, config.GetMounts()) {
mounts = append(mounts, &runtime.Mount{ mounts = append(mounts, &runtime.Mount{
ContainerPath: etcHosts, ContainerPath: etcHosts,

View File

@ -534,6 +534,11 @@ func TestGenerateContainerMounts(t *testing.T) {
ReadonlyRootfs: true, ReadonlyRootfs: true,
}, },
expectedMounts: []*runtime.Mount{ expectedMounts: []*runtime.Mount{
{
ContainerPath: "/etc/hostname",
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hostname"),
Readonly: true,
},
{ {
ContainerPath: "/etc/hosts", ContainerPath: "/etc/hosts",
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "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": { "should setup rw mount when rootfs is read-write": {
securityContext: &runtime.LinuxContainerSecurityContext{}, securityContext: &runtime.LinuxContainerSecurityContext{},
expectedMounts: []*runtime.Mount{ expectedMounts: []*runtime.Mount{
{
ContainerPath: "/etc/hostname",
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hostname"),
Readonly: false,
},
{ {
ContainerPath: "/etc/hosts", ContainerPath: "/etc/hosts",
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hosts"), HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hosts"),
@ -576,6 +586,11 @@ func TestGenerateContainerMounts(t *testing.T) {
NamespaceOptions: &runtime.NamespaceOption{Ipc: runtime.NamespaceMode_NODE}, NamespaceOptions: &runtime.NamespaceOption{Ipc: runtime.NamespaceMode_NODE},
}, },
expectedMounts: []*runtime.Mount{ expectedMounts: []*runtime.Mount{
{
ContainerPath: "/etc/hostname",
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "hostname"),
Readonly: false,
},
{ {
ContainerPath: "/etc/hosts", ContainerPath: "/etc/hosts",
HostPath: filepath.Join(testRootDir, sandboxesDir, testSandboxID, "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": { "should skip container mounts if already mounted by CRI": {
criMounts: []*runtime.Mount{ criMounts: []*runtime.Mount{
{
ContainerPath: "/etc/hostname",
HostPath: "/test-etc-hostname",
},
{ {
ContainerPath: "/etc/hosts", ContainerPath: "/etc/hosts",
HostPath: "/test-etc-host", HostPath: "/test-etc-host",

View File

@ -99,6 +99,8 @@ const (
devShm = "/dev/shm" devShm = "/dev/shm"
// etcHosts is the default path of /etc/hosts file. // etcHosts is the default path of /etc/hosts file.
etcHosts = "/etc/hosts" 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 is the abs path of resolv.conf on host or container.
resolvConfPath = "/etc/resolv.conf" resolvConfPath = "/etc/resolv.conf"
// hostnameEnv is the key for HOSTNAME env. // 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) 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. // getSandboxHosts returns the hosts file path inside the sandbox root directory.
func (c *criService) getSandboxHosts(id string) string { func (c *criService) getSandboxHosts(id string) string {
return filepath.Join(c.getSandboxRootDir(id), "hosts") 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 { if err = c.setupSandboxFiles(id, config); err != nil {
return nil, errors.Wrapf(err, "failed to setup sandbox files") 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 return g.Config, nil
} }
// setupSandboxFiles sets up necessary sandbox files including /dev/shm, /etc/hosts // setupSandboxFiles sets up necessary sandbox files including /dev/shm, /etc/hosts,
// and /etc/resolv.conf. // /etc/resolv.conf and /etc/hostname.
func (c *criService) setupSandboxFiles(id string, config *runtime.PodSandboxConfig) error { 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. // TODO(random-liu): Consider whether we should maintain /etc/hosts and /etc/resolv.conf in kubelet.
sandboxEtcHosts := c.getSandboxHosts(id) sandboxEtcHosts := c.getSandboxHosts(id)
if err := c.os.CopyFile(etcHosts, sandboxEtcHosts, 0644); err != nil { 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) { func TestSetupSandboxFiles(t *testing.T) {
const testID = "test-id" const (
testID = "test-id"
realhostname = "test-real-hostname"
)
for desc, test := range map[string]struct { for desc, test := range map[string]struct {
dnsConfig *runtime.DNSConfig dnsConfig *runtime.DNSConfig
hostname string
ipcMode runtime.NamespaceMode ipcMode runtime.NamespaceMode
expectedCalls []ostesting.CalledDetail expectedCalls []ostesting.CalledDetail
}{ }{
"should check host /dev/shm existence when ipc mode is NODE": { "should check host /dev/shm existence when ipc mode is NODE": {
ipcMode: runtime.NamespaceMode_NODE, ipcMode: runtime.NamespaceMode_NODE,
expectedCalls: []ostesting.CalledDetail{ expectedCalls: []ostesting.CalledDetail{
{
Name: "Hostname",
},
{
Name: "WriteFile",
Arguments: []interface{}{
filepath.Join(testRootDir, sandboxesDir, testID, "hostname"),
[]byte(realhostname + "\n"),
os.FileMode(0644),
},
},
{ {
Name: "CopyFile", Name: "CopyFile",
Arguments: []interface{}{ Arguments: []interface{}{
@ -219,6 +234,17 @@ func TestSetupSandboxFiles(t *testing.T) {
}, },
ipcMode: runtime.NamespaceMode_NODE, ipcMode: runtime.NamespaceMode_NODE,
expectedCalls: []ostesting.CalledDetail{ expectedCalls: []ostesting.CalledDetail{
{
Name: "Hostname",
},
{
Name: "WriteFile",
Arguments: []interface{}{
filepath.Join(testRootDir, sandboxesDir, testID, "hostname"),
[]byte(realhostname + "\n"),
os.FileMode(0644),
},
},
{ {
Name: "CopyFile", Name: "CopyFile",
Arguments: []interface{}{ Arguments: []interface{}{
@ -246,6 +272,17 @@ options timeout:1
"should create sandbox shm when ipc namespace mode is not NODE": { "should create sandbox shm when ipc namespace mode is not NODE": {
ipcMode: runtime.NamespaceMode_POD, ipcMode: runtime.NamespaceMode_POD,
expectedCalls: []ostesting.CalledDetail{ expectedCalls: []ostesting.CalledDetail{
{
Name: "Hostname",
},
{
Name: "WriteFile",
Arguments: []interface{}{
filepath.Join(testRootDir, sandboxesDir, testID, "hostname"),
[]byte(realhostname + "\n"),
os.FileMode(0644),
},
},
{ {
Name: "CopyFile", Name: "CopyFile",
Arguments: []interface{}{ 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) t.Logf("TestCase %q", desc)
c := newTestCRIService() c := newTestCRIService()
c.os.(*ostesting.FakeOS).HostnameFn = func() (string, error) {
return realhostname, nil
}
cfg := &runtime.PodSandboxConfig{ cfg := &runtime.PodSandboxConfig{
Hostname: test.hostname,
DnsConfig: test.dnsConfig, DnsConfig: test.dnsConfig,
Linux: &runtime.LinuxPodSandboxConfig{ Linux: &runtime.LinuxPodSandboxConfig{
SecurityContext: &runtime.LinuxSandboxSecurityContext{ SecurityContext: &runtime.LinuxSandboxSecurityContext{