mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 09:49:50 +00:00
Implement mount propagation in kubelet
This commit is contained in:
parent
0c767355d8
commit
03b753daad
@ -43,6 +43,7 @@ go_library(
|
|||||||
"//pkg/fieldpath:go_default_library",
|
"//pkg/fieldpath:go_default_library",
|
||||||
"//pkg/kubelet/apis:go_default_library",
|
"//pkg/kubelet/apis:go_default_library",
|
||||||
"//pkg/kubelet/apis/cri:go_default_library",
|
"//pkg/kubelet/apis/cri:go_default_library",
|
||||||
|
"//pkg/kubelet/apis/cri/v1alpha1/runtime:go_default_library",
|
||||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||||
"//pkg/kubelet/apis/kubeletconfig/v1alpha1:go_default_library",
|
"//pkg/kubelet/apis/kubeletconfig/v1alpha1:go_default_library",
|
||||||
"//pkg/kubelet/cadvisor:go_default_library",
|
"//pkg/kubelet/cadvisor:go_default_library",
|
||||||
@ -170,6 +171,7 @@ go_test(
|
|||||||
"//pkg/api/install:go_default_library",
|
"//pkg/api/install:go_default_library",
|
||||||
"//pkg/capabilities:go_default_library",
|
"//pkg/capabilities:go_default_library",
|
||||||
"//pkg/kubelet/apis:go_default_library",
|
"//pkg/kubelet/apis:go_default_library",
|
||||||
|
"//pkg/kubelet/apis/cri/v1alpha1/runtime:go_default_library",
|
||||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||||
"//pkg/kubelet/cadvisor/testing:go_default_library",
|
"//pkg/kubelet/cadvisor/testing:go_default_library",
|
||||||
"//pkg/kubelet/cm:go_default_library",
|
"//pkg/kubelet/cm:go_default_library",
|
||||||
@ -225,6 +227,7 @@ go_test(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/testing:go_default_library",
|
"//vendor/k8s.io/client-go/testing:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||||
|
@ -389,6 +389,8 @@ type Mount struct {
|
|||||||
ReadOnly bool
|
ReadOnly bool
|
||||||
// Whether the mount needs SELinux relabeling
|
// Whether the mount needs SELinux relabeling
|
||||||
SELinuxRelabel bool
|
SELinuxRelabel bool
|
||||||
|
// Requested propagation mode
|
||||||
|
Propagation runtimeapi.MountPropagation
|
||||||
}
|
}
|
||||||
|
|
||||||
type PortMapping struct {
|
type PortMapping struct {
|
||||||
|
@ -41,6 +41,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
|
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
"k8s.io/client-go/tools/remotecommand"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
||||||
@ -48,7 +49,9 @@ import (
|
|||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||||
"k8s.io/kubernetes/pkg/api/v1/resource"
|
"k8s.io/kubernetes/pkg/api/v1/resource"
|
||||||
"k8s.io/kubernetes/pkg/api/v1/validation"
|
"k8s.io/kubernetes/pkg/api/v1/validation"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/fieldpath"
|
"k8s.io/kubernetes/pkg/fieldpath"
|
||||||
|
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/envvars"
|
"k8s.io/kubernetes/pkg/kubelet/envvars"
|
||||||
@ -188,12 +191,19 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
propagation, err := translateMountPropagation(mount.MountPropagation)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
glog.V(5).Infof("Pod %q container %q mount %q has propagation %q", format.Pod(pod), container.Name, mount.Name, propagation)
|
||||||
|
|
||||||
mounts = append(mounts, kubecontainer.Mount{
|
mounts = append(mounts, kubecontainer.Mount{
|
||||||
Name: mount.Name,
|
Name: mount.Name,
|
||||||
ContainerPath: containerPath,
|
ContainerPath: containerPath,
|
||||||
HostPath: hostPath,
|
HostPath: hostPath,
|
||||||
ReadOnly: mount.ReadOnly,
|
ReadOnly: mount.ReadOnly,
|
||||||
SELinuxRelabel: relabelVolume,
|
SELinuxRelabel: relabelVolume,
|
||||||
|
Propagation: propagation,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if mountEtcHostsFile {
|
if mountEtcHostsFile {
|
||||||
@ -207,6 +217,26 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h
|
|||||||
return mounts, nil
|
return mounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// translateMountPropagation transforms v1.MountPropagationMode to
|
||||||
|
// runtimeapi.MountPropagation.
|
||||||
|
func translateMountPropagation(mountMode *v1.MountPropagationMode) (runtimeapi.MountPropagation, error) {
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(features.MountPropagation) {
|
||||||
|
// mount propagation is disabled, use private as in the old versions
|
||||||
|
return runtimeapi.MountPropagation_PROPAGATION_PRIVATE, nil
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case mountMode == nil:
|
||||||
|
// HostToContainer is the default
|
||||||
|
return runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, nil
|
||||||
|
case *mountMode == v1.MountPropagationHostToContainer:
|
||||||
|
return runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, nil
|
||||||
|
case *mountMode == v1.MountPropagationBidirectional:
|
||||||
|
return runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("invalid MountPropagation mode: %q", mountMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// makeHostsMount makes the mountpoint for the hosts file that the containers
|
// makeHostsMount makes the mountpoint for the hosts file that the containers
|
||||||
// in a pod are injected with.
|
// in a pod are injected with.
|
||||||
func makeHostsMount(podDir, podIP, hostName, hostDomainName string, hostAliases []v1.HostAlias, useHostNetwork bool) (*kubecontainer.Mount, error) {
|
func makeHostsMount(podDir, podIP, hostName, hostDomainName string, hostAliases []v1.HostAlias, useHostNetwork bool) (*kubecontainer.Mount, error) {
|
||||||
|
@ -35,6 +35,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
core "k8s.io/client-go/testing"
|
core "k8s.io/client-go/testing"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
@ -42,6 +43,7 @@ import (
|
|||||||
// api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() is changed
|
// api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() is changed
|
||||||
// to "v1"?
|
// to "v1"?
|
||||||
_ "k8s.io/kubernetes/pkg/api/install"
|
_ "k8s.io/kubernetes/pkg/api/install"
|
||||||
|
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/server/portforward"
|
"k8s.io/kubernetes/pkg/kubelet/server/portforward"
|
||||||
@ -49,6 +51,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestMakeMounts(t *testing.T) {
|
func TestMakeMounts(t *testing.T) {
|
||||||
|
bTrue := true
|
||||||
|
propagationHostToContainer := v1.MountPropagationHostToContainer
|
||||||
|
propagationBidirectional := v1.MountPropagationBidirectional
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
container v1.Container
|
container v1.Container
|
||||||
podVolumes kubecontainer.VolumeMap
|
podVolumes kubecontainer.VolumeMap
|
||||||
@ -56,18 +62,20 @@ func TestMakeMounts(t *testing.T) {
|
|||||||
expectedErrMsg string
|
expectedErrMsg string
|
||||||
expectedMounts []kubecontainer.Mount
|
expectedMounts []kubecontainer.Mount
|
||||||
}{
|
}{
|
||||||
"valid mounts": {
|
"valid mounts in unprivileged container": {
|
||||||
podVolumes: kubecontainer.VolumeMap{
|
podVolumes: kubecontainer.VolumeMap{
|
||||||
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
|
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
|
||||||
"disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}},
|
"disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}},
|
||||||
"disk5": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/var/lib/kubelet/podID/volumes/empty/disk5"}},
|
"disk5": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/var/lib/kubelet/podID/volumes/empty/disk5"}},
|
||||||
},
|
},
|
||||||
container: v1.Container{
|
container: v1.Container{
|
||||||
|
Name: "container1",
|
||||||
VolumeMounts: []v1.VolumeMount{
|
VolumeMounts: []v1.VolumeMount{
|
||||||
{
|
{
|
||||||
MountPath: "/etc/hosts",
|
MountPath: "/etc/hosts",
|
||||||
Name: "disk",
|
Name: "disk",
|
||||||
ReadOnly: false,
|
ReadOnly: false,
|
||||||
|
MountPropagation: &propagationHostToContainer,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MountPath: "/mnt/path3",
|
MountPath: "/mnt/path3",
|
||||||
@ -93,6 +101,7 @@ func TestMakeMounts(t *testing.T) {
|
|||||||
HostPath: "/mnt/disk",
|
HostPath: "/mnt/disk",
|
||||||
ReadOnly: false,
|
ReadOnly: false,
|
||||||
SELinuxRelabel: false,
|
SELinuxRelabel: false,
|
||||||
|
Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "disk",
|
Name: "disk",
|
||||||
@ -100,6 +109,7 @@ func TestMakeMounts(t *testing.T) {
|
|||||||
HostPath: "/mnt/disk",
|
HostPath: "/mnt/disk",
|
||||||
ReadOnly: true,
|
ReadOnly: true,
|
||||||
SELinuxRelabel: false,
|
SELinuxRelabel: false,
|
||||||
|
Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "disk4",
|
Name: "disk4",
|
||||||
@ -107,6 +117,7 @@ func TestMakeMounts(t *testing.T) {
|
|||||||
HostPath: "/mnt/host",
|
HostPath: "/mnt/host",
|
||||||
ReadOnly: false,
|
ReadOnly: false,
|
||||||
SELinuxRelabel: false,
|
SELinuxRelabel: false,
|
||||||
|
Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "disk5",
|
Name: "disk5",
|
||||||
@ -114,6 +125,66 @@ func TestMakeMounts(t *testing.T) {
|
|||||||
HostPath: "/var/lib/kubelet/podID/volumes/empty/disk5",
|
HostPath: "/var/lib/kubelet/podID/volumes/empty/disk5",
|
||||||
ReadOnly: false,
|
ReadOnly: false,
|
||||||
SELinuxRelabel: false,
|
SELinuxRelabel: false,
|
||||||
|
Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
"valid mounts in privileged container": {
|
||||||
|
podVolumes: kubecontainer.VolumeMap{
|
||||||
|
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
|
||||||
|
"disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}},
|
||||||
|
"disk5": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/var/lib/kubelet/podID/volumes/empty/disk5"}},
|
||||||
|
},
|
||||||
|
container: v1.Container{
|
||||||
|
Name: "container1",
|
||||||
|
VolumeMounts: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
MountPath: "/etc/hosts",
|
||||||
|
Name: "disk",
|
||||||
|
ReadOnly: false,
|
||||||
|
MountPropagation: &propagationBidirectional,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MountPath: "/mnt/path3",
|
||||||
|
Name: "disk",
|
||||||
|
ReadOnly: true,
|
||||||
|
MountPropagation: &propagationHostToContainer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MountPath: "/mnt/path4",
|
||||||
|
Name: "disk4",
|
||||||
|
ReadOnly: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SecurityContext: &v1.SecurityContext{
|
||||||
|
Privileged: &bTrue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedMounts: []kubecontainer.Mount{
|
||||||
|
{
|
||||||
|
Name: "disk",
|
||||||
|
ContainerPath: "/etc/hosts",
|
||||||
|
HostPath: "/mnt/disk",
|
||||||
|
ReadOnly: false,
|
||||||
|
SELinuxRelabel: false,
|
||||||
|
Propagation: runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "disk",
|
||||||
|
ContainerPath: "/mnt/path3",
|
||||||
|
HostPath: "/mnt/disk",
|
||||||
|
ReadOnly: true,
|
||||||
|
SELinuxRelabel: false,
|
||||||
|
Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "disk4",
|
||||||
|
ContainerPath: "/mnt/path4",
|
||||||
|
HostPath: "/mnt/host",
|
||||||
|
ReadOnly: false,
|
||||||
|
SELinuxRelabel: false,
|
||||||
|
Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
@ -161,6 +232,12 @@ func TestMakeMounts(t *testing.T) {
|
|||||||
HostNetwork: true,
|
HostNetwork: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
// test makeMounts with enabled mount propagation
|
||||||
|
err := utilfeature.DefaultFeatureGate.Set("MountPropagation=true")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to enable feature gate for MountPropagation: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
mounts, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes)
|
mounts, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes)
|
||||||
|
|
||||||
@ -178,6 +255,24 @@ func TestMakeMounts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, tc.expectedMounts, mounts, "mounts of container %+v", tc.container)
|
assert.Equal(t, tc.expectedMounts, mounts, "mounts of container %+v", tc.container)
|
||||||
|
|
||||||
|
// test makeMounts with disabled mount propagation
|
||||||
|
err = utilfeature.DefaultFeatureGate.Set("MountPropagation=false")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to enable feature gate for MountPropagation: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mounts, err = makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes)
|
||||||
|
if !tc.expectErr {
|
||||||
|
expectedPrivateMounts := []kubecontainer.Mount{}
|
||||||
|
for _, mount := range tc.expectedMounts {
|
||||||
|
// all mounts are expected to be private when mount
|
||||||
|
// propagation is disabled
|
||||||
|
mount.Propagation = runtimeapi.MountPropagation_PROPAGATION_PRIVATE
|
||||||
|
expectedPrivateMounts = append(expectedPrivateMounts, mount)
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectedPrivateMounts, mounts, "mounts of container %+v", tc.container)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -294,6 +294,7 @@ func (m *kubeGenericRuntimeManager) makeMounts(opts *kubecontainer.RunContainerO
|
|||||||
ContainerPath: v.ContainerPath,
|
ContainerPath: v.ContainerPath,
|
||||||
Readonly: v.ReadOnly,
|
Readonly: v.ReadOnly,
|
||||||
SelinuxRelabel: selinuxRelabel,
|
SelinuxRelabel: selinuxRelabel,
|
||||||
|
Propagation: v.Propagation,
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeMounts = append(volumeMounts, mount)
|
volumeMounts = append(volumeMounts, mount)
|
||||||
|
Loading…
Reference in New Issue
Block a user