Add feature gate for subpath
This commit is contained in:
parent
ba818d8dff
commit
f6d97b5d2b
@ -2264,7 +2264,11 @@ func ValidateVolumeMounts(mounts []core.VolumeMount, voldevices map[string]strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(mnt.SubPath) > 0 {
|
if len(mnt.SubPath) > 0 {
|
||||||
allErrs = append(allErrs, validateLocalDescendingPath(mnt.SubPath, fldPath.Child("subPath"))...)
|
if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeSubpath) {
|
||||||
|
allErrs = append(allErrs, field.Forbidden(fldPath.Child("subPath"), "subPath is disabled by feature-gate"))
|
||||||
|
} else {
|
||||||
|
allErrs = append(allErrs, validateLocalDescendingPath(mnt.SubPath, fldPath.Child("subPath"))...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if mnt.MountPropagation != nil {
|
if mnt.MountPropagation != nil {
|
||||||
|
@ -4766,6 +4766,68 @@ func TestValidateVolumeMounts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateDisabledSubpath(t *testing.T) {
|
||||||
|
utilfeature.DefaultFeatureGate.Set("VolumeSubpath=false")
|
||||||
|
defer utilfeature.DefaultFeatureGate.Set("VolumeSubpath=true")
|
||||||
|
|
||||||
|
volumes := []core.Volume{
|
||||||
|
{Name: "abc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim1"}}},
|
||||||
|
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
||||||
|
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||||
|
}
|
||||||
|
vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
|
||||||
|
if len(v1err) > 0 {
|
||||||
|
t.Errorf("Invalid test volume - expected success %v", v1err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
container := core.Container{
|
||||||
|
SecurityContext: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
goodVolumeDevices := []core.VolumeDevice{
|
||||||
|
{Name: "xyz", DevicePath: "/foofoo"},
|
||||||
|
{Name: "uvw", DevicePath: "/foofoo/share/test"},
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]struct {
|
||||||
|
mounts []core.VolumeMount
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
"subpath not specified": {
|
||||||
|
[]core.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "abc-123",
|
||||||
|
MountPath: "/bab",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
"subpath specified": {
|
||||||
|
[]core.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "abc-123",
|
||||||
|
MountPath: "/bab",
|
||||||
|
SubPath: "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, test := range cases {
|
||||||
|
errs := ValidateVolumeMounts(test.mounts, GetVolumeDeviceMap(goodVolumeDevices), vols, &container, field.NewPath("field"))
|
||||||
|
|
||||||
|
if len(errs) != 0 && !test.expectError {
|
||||||
|
t.Errorf("test %v failed: %+v", name, errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) == 0 && test.expectError {
|
||||||
|
t.Errorf("test %v failed, expected error", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateMountPropagation(t *testing.T) {
|
func TestValidateMountPropagation(t *testing.T) {
|
||||||
bTrue := true
|
bTrue := true
|
||||||
bFalse := false
|
bFalse := false
|
||||||
|
@ -267,6 +267,13 @@ const (
|
|||||||
//
|
//
|
||||||
// Enables control over the primary group ID of containers' init processes.
|
// Enables control over the primary group ID of containers' init processes.
|
||||||
RunAsGroup utilfeature.Feature = "RunAsGroup"
|
RunAsGroup utilfeature.Feature = "RunAsGroup"
|
||||||
|
|
||||||
|
// owner: @saad-ali
|
||||||
|
// ga
|
||||||
|
//
|
||||||
|
// Allow mounting a subpath of a volume in a container
|
||||||
|
// Do not remove this feature gate even though it's GA
|
||||||
|
VolumeSubpath utilfeature.Feature = "VolumeSubpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -313,6 +320,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
|
|||||||
CRIContainerLogRotation: {Default: false, PreRelease: utilfeature.Alpha},
|
CRIContainerLogRotation: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
GCERegionalPersistentDisk: {Default: true, PreRelease: utilfeature.Beta},
|
GCERegionalPersistentDisk: {Default: true, PreRelease: utilfeature.Beta},
|
||||||
RunAsGroup: {Default: false, PreRelease: utilfeature.Alpha},
|
RunAsGroup: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
|
VolumeSubpath: {Default: true, PreRelease: utilfeature.GA},
|
||||||
|
|
||||||
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
||||||
// unintentionally on either side:
|
// unintentionally on either side:
|
||||||
|
@ -194,6 +194,10 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h
|
|||||||
return nil, cleanupAction, err
|
return nil, cleanupAction, err
|
||||||
}
|
}
|
||||||
if mount.SubPath != "" {
|
if mount.SubPath != "" {
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeSubpath) {
|
||||||
|
return nil, cleanupAction, fmt.Errorf("volume subpaths are disabled")
|
||||||
|
}
|
||||||
|
|
||||||
if filepath.IsAbs(mount.SubPath) {
|
if filepath.IsAbs(mount.SubPath) {
|
||||||
return nil, cleanupAction, fmt.Errorf("error SubPath `%s` must not be an absolute path", mount.SubPath)
|
return nil, cleanupAction, fmt.Errorf("error SubPath `%s` must not be an absolute path", mount.SubPath)
|
||||||
}
|
}
|
||||||
|
@ -355,6 +355,62 @@ func TestMakeMounts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDisabledSubpath(t *testing.T) {
|
||||||
|
fm := &mount.FakeMounter{}
|
||||||
|
pod := v1.Pod{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
HostNetwork: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
podVolumes := kubecontainer.VolumeMap{
|
||||||
|
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]struct {
|
||||||
|
container v1.Container
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
"subpath not specified": {
|
||||||
|
v1.Container{
|
||||||
|
VolumeMounts: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
MountPath: "/mnt/path3",
|
||||||
|
Name: "disk",
|
||||||
|
ReadOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
"subpath specified": {
|
||||||
|
v1.Container{
|
||||||
|
VolumeMounts: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
MountPath: "/mnt/path3",
|
||||||
|
SubPath: "/must/not/be/absolute",
|
||||||
|
Name: "disk",
|
||||||
|
ReadOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
utilfeature.DefaultFeatureGate.Set("VolumeSubpath=false")
|
||||||
|
defer utilfeature.DefaultFeatureGate.Set("VolumeSubpath=true")
|
||||||
|
|
||||||
|
for name, test := range cases {
|
||||||
|
_, _, err := makeMounts(&pod, "/pod", &test.container, "fakepodname", "", "", podVolumes, fm)
|
||||||
|
if err != nil && !test.expectError {
|
||||||
|
t.Errorf("test %v failed: %v", name, err)
|
||||||
|
}
|
||||||
|
if err == nil && test.expectError {
|
||||||
|
t.Errorf("test %v failed: expected error", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMakeBlockVolumes(t *testing.T) {
|
func TestMakeBlockVolumes(t *testing.T) {
|
||||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
||||||
defer testKubelet.Cleanup()
|
defer testKubelet.Cleanup()
|
||||||
|
Loading…
Reference in New Issue
Block a user