From d4d7f5836236e693a69d8796dc63d34d66e7ad3d Mon Sep 17 00:00:00 2001 From: Claudiu Belu Date: Wed, 4 Sep 2019 09:16:21 -0700 Subject: [PATCH] Windows: Fixes termination-file mounting for containerd If Containerd is used on Windows, then we can also mount individual files into containers (e.g.: termination-log files), which was not possible with Docker. Checks if the container runtime is containerd, and if it is, then also mount the termination-log file. --- pkg/kubelet/container/runtime.go | 3 +++ pkg/kubelet/container/testing/fake_runtime.go | 4 ++++ pkg/kubelet/container/testing/runtime_mock.go | 5 +++++ pkg/kubelet/kubelet_pods.go | 7 ++++--- pkg/kubelet/kuberuntime/BUILD | 1 + pkg/kubelet/kuberuntime/kuberuntime_container.go | 13 +++++++++++-- pkg/kubelet/kuberuntime/kuberuntime_manager.go | 12 ++++++++++++ 7 files changed, 40 insertions(+), 5 deletions(-) diff --git a/pkg/kubelet/container/runtime.go b/pkg/kubelet/container/runtime.go index 9699a5b5cfe..71f6dc20f7e 100644 --- a/pkg/kubelet/container/runtime.go +++ b/pkg/kubelet/container/runtime.go @@ -63,6 +63,9 @@ type Runtime interface { // Type returns the type of the container runtime. Type() string + //SupportsSingleFileMapping returns whether the container runtime supports single file mappings or not. + SupportsSingleFileMapping() bool + // Version returns the version information of the container runtime. Version() (Version, error) diff --git a/pkg/kubelet/container/testing/fake_runtime.go b/pkg/kubelet/container/testing/fake_runtime.go index e7e22e203c2..1824b8b84b7 100644 --- a/pkg/kubelet/container/testing/fake_runtime.go +++ b/pkg/kubelet/container/testing/fake_runtime.go @@ -177,6 +177,10 @@ func (f *FakeRuntime) Type() string { return f.RuntimeType } +func (f *FakeRuntime) SupportsSingleFileMapping() bool { + return true +} + func (f *FakeRuntime) Version() (kubecontainer.Version, error) { f.Lock() defer f.Unlock() diff --git a/pkg/kubelet/container/testing/runtime_mock.go b/pkg/kubelet/container/testing/runtime_mock.go index 45c4b485bf2..aa363649e30 100644 --- a/pkg/kubelet/container/testing/runtime_mock.go +++ b/pkg/kubelet/container/testing/runtime_mock.go @@ -47,6 +47,11 @@ func (r *Mock) Type() string { return args.Get(0).(string) } +func (r *Mock) SupportsSingleFileMapping() bool { + args := r.Called() + return args.Get(0).(bool) +} + func (r *Mock) Version() (kubecontainer.Version, error) { args := r.Called() return args.Get(0).(kubecontainer.Version), args.Error(1) diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index 013d0f55aea..fe57831ff9d 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -471,9 +471,10 @@ func (kl *Kubelet) GenerateRunContainerOptions(pod *v1.Pod, container *v1.Contai } opts.Mounts = append(opts.Mounts, mounts...) - // Disabling adding TerminationMessagePath on Windows as these files would be mounted as docker volume and - // Docker for Windows has a bug where only directories can be mounted - if len(container.TerminationMessagePath) != 0 && runtime.GOOS != "windows" { + // adding TerminationMessagePath on Windows is only allowed if ContainerD is used. Individual files cannot + // be mounted as volumes using Docker for Windows. + supportsSingleFileMapping := kl.containerRuntime.SupportsSingleFileMapping() + if len(container.TerminationMessagePath) != 0 && supportsSingleFileMapping { p := kl.getPodContainerDir(pod.UID, container.Name) if err := os.MkdirAll(p, 0750); err != nil { klog.Errorf("Error on creating %q: %v", p, err) diff --git a/pkg/kubelet/kuberuntime/BUILD b/pkg/kubelet/kuberuntime/BUILD index 3813012d891..ba6cb6dea94 100644 --- a/pkg/kubelet/kuberuntime/BUILD +++ b/pkg/kubelet/kuberuntime/BUILD @@ -53,6 +53,7 @@ go_library( "//pkg/util/parsers:go_default_library", "//pkg/util/selinux:go_default_library", "//pkg/util/tail:go_default_library", + "//pkg/volume/util:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container.go b/pkg/kubelet/kuberuntime/kuberuntime_container.go index 81e6809f11d..962509999ed 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container.go @@ -25,6 +25,7 @@ import ( "net/url" "os" "path/filepath" + goruntime "runtime" "sort" "strings" "sync" @@ -47,6 +48,7 @@ import ( "k8s.io/kubernetes/pkg/kubelet/util/format" "k8s.io/kubernetes/pkg/util/selinux" "k8s.io/kubernetes/pkg/util/tail" + volumeutil "k8s.io/kubernetes/pkg/volume/util" ) var ( @@ -292,7 +294,9 @@ func (m *kubeGenericRuntimeManager) makeMounts(opts *kubecontainer.RunContainerO // The reason we create and mount the log file in here (not in kubelet) is because // the file's location depends on the ID of the container, and we need to create and // mount the file before actually starting the container. - if opts.PodContainerDir != "" && len(container.TerminationMessagePath) != 0 { + // we can only mount individual files (e.g.: /etc/hosts, termination-log files) on Windows only if we're using Containerd. + supportsSingleFileMapping := m.SupportsSingleFileMapping() + if opts.PodContainerDir != "" && len(container.TerminationMessagePath) != 0 && supportsSingleFileMapping { // Because the PodContainerDir contains pod uid and container name which is unique enough, // here we just add a random id to make the path unique for different instances // of the same container. @@ -312,10 +316,13 @@ func (m *kubeGenericRuntimeManager) makeMounts(opts *kubecontainer.RunContainerO utilruntime.HandleError(fmt.Errorf("unable to set termination-log file permissions %q: %v", containerLogPath, err)) } + // Volume Mounts fail on Windows if it is not of the form C:/ + containerLogPath = volumeutil.MakeAbsolutePath(goruntime.GOOS, containerLogPath) + terminationMessagePath := volumeutil.MakeAbsolutePath(goruntime.GOOS, container.TerminationMessagePath) selinuxRelabel := selinux.SELinuxEnabled() volumeMounts = append(volumeMounts, &runtimeapi.Mount{ HostPath: containerLogPath, - ContainerPath: container.TerminationMessagePath, + ContainerPath: terminationMessagePath, SelinuxRelabel: selinuxRelabel, }) } @@ -355,6 +362,8 @@ func getTerminationMessage(status *runtimeapi.ContainerStatus, terminationMessag if len(terminationMessagePath) == 0 { return "", fallbackToLogs } + // Volume Mounts fail on Windows if it is not of the form C:/ + terminationMessagePath = volumeutil.MakeAbsolutePath(goruntime.GOOS, terminationMessagePath) for _, mount := range status.Mounts { if mount.ContainerPath != terminationMessagePath { continue diff --git a/pkg/kubelet/kuberuntime/kuberuntime_manager.go b/pkg/kubelet/kuberuntime/kuberuntime_manager.go index 7ca444e4cdf..044848f4ade 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_manager.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_manager.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "os" + goruntime "runtime" "time" cadvisorapi "github.com/google/cadvisor/info/v1" @@ -243,6 +244,17 @@ func (m *kubeGenericRuntimeManager) Type() string { return m.runtimeName } +// SupportsSingleFileMapping returns whether the container runtime supports single file mappings or not. +// It is supported on Windows only if the container runtime is containerd. +func (m *kubeGenericRuntimeManager) SupportsSingleFileMapping() bool { + switch goruntime.GOOS { + case "windows": + return m.Type() != types.DockerContainerRuntime + default: + return true + } +} + func newRuntimeVersion(version string) (*utilversion.Version, error) { if ver, err := utilversion.ParseSemantic(version); err == nil { return ver, err