TestMakeMounts: add new cases for the image volume feature

This commit is contained in:
carlory 2024-11-05 15:08:52 +08:00
parent bc79d3ba87
commit 2529d7d5a6
2 changed files with 243 additions and 34 deletions

View File

@ -20,16 +20,21 @@ limitations under the License.
package kubelet
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/utils/ptr"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
_ "k8s.io/kubernetes/pkg/apis/core/install"
"k8s.io/kubernetes/pkg/features"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/volume"
volumetest "k8s.io/kubernetes/pkg/volume/testing"
"k8s.io/kubernetes/pkg/volume/util/hostutil"
"k8s.io/kubernetes/pkg/volume/util/subpath"
@ -40,16 +45,21 @@ func TestMakeMounts(t *testing.T) {
propagationHostToContainer := v1.MountPropagationHostToContainer
propagationBidirectional := v1.MountPropagationBidirectional
propagationNone := v1.MountPropagationNone
image1 := &runtimeapi.ImageSpec{Image: "image1"}
image2 := &runtimeapi.ImageSpec{Image: "image2"}
testCases := map[string]struct {
container v1.Container
podVolumes kubecontainer.VolumeMap
imageVolumes kubecontainer.ImageVolumes
imageVolumeFeatureEnabled []bool
supportsRRO bool
expectErr bool
expectedErrMsg string
expectedMounts []kubecontainer.Mount
}{
"valid mounts in unprivileged container": {
"valid mounts in unprivileged container": { // TODO: remove it once image volume feature is GA
imageVolumeFeatureEnabled: []bool{true, false},
podVolumes: kubecontainer.VolumeMap{
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
"disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}},
@ -118,7 +128,8 @@ func TestMakeMounts(t *testing.T) {
},
expectErr: false,
},
"valid mounts in privileged container": {
"valid mounts in privileged container": { // TODO: remove it once image volume feature is GA
imageVolumeFeatureEnabled: []bool{true, false},
podVolumes: kubecontainer.VolumeMap{
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
"disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}},
@ -177,7 +188,198 @@ func TestMakeMounts(t *testing.T) {
},
expectErr: false,
},
"valid mounts in unprivileged container with image volumes": {
imageVolumeFeatureEnabled: []bool{true},
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"}},
"image1": kubecontainer.VolumeInfo{Mounter: &stubVolume{attributes: volume.Attributes{ReadOnly: true}}},
"image2": kubecontainer.VolumeInfo{Mounter: &stubVolume{attributes: volume.Attributes{ReadOnly: true}}},
},
imageVolumes: kubecontainer.ImageVolumes{
"image1": image1,
"image2": image2,
},
container: v1.Container{
Name: "container1",
VolumeMounts: []v1.VolumeMount{
{
MountPath: "/etc/hosts",
Name: "disk",
ReadOnly: false,
MountPropagation: &propagationHostToContainer,
},
{
MountPath: "/mnt/path3",
Name: "disk",
ReadOnly: true,
MountPropagation: &propagationNone,
},
{
MountPath: "/mnt/path4",
Name: "disk4",
ReadOnly: false,
},
{
MountPath: "/mnt/path5",
Name: "disk5",
ReadOnly: false,
},
{
MountPath: "/mnt/image1",
Name: "image1",
ReadOnly: false,
},
{
MountPath: "/mnt/image2",
Name: "image2",
ReadOnly: true,
},
},
},
expectedMounts: []kubecontainer.Mount{
{
Name: "disk",
ContainerPath: "/etc/hosts",
HostPath: "/mnt/disk",
ReadOnly: false,
SELinuxRelabel: false,
Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER,
},
{
Name: "disk",
ContainerPath: "/mnt/path3",
HostPath: "/mnt/disk",
ReadOnly: true,
SELinuxRelabel: false,
Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE,
},
{
Name: "disk4",
ContainerPath: "/mnt/path4",
HostPath: "/mnt/host",
ReadOnly: false,
SELinuxRelabel: false,
Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE,
},
{
Name: "disk5",
ContainerPath: "/mnt/path5",
HostPath: "/var/lib/kubelet/podID/volumes/empty/disk5",
ReadOnly: false,
SELinuxRelabel: false,
Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE,
},
{
Name: "image1",
ContainerPath: "/mnt/image1",
Image: image1,
ReadOnly: true,
SELinuxRelabel: false,
},
{
Name: "image2",
ContainerPath: "/mnt/image2",
Image: image2,
ReadOnly: true,
SELinuxRelabel: false,
},
},
expectErr: false,
},
"valid mounts in privileged container with image volumes": {
imageVolumeFeatureEnabled: []bool{true},
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"}},
"image1": kubecontainer.VolumeInfo{Mounter: &stubVolume{attributes: volume.Attributes{ReadOnly: true}}},
"image2": kubecontainer.VolumeInfo{Mounter: &stubVolume{attributes: volume.Attributes{ReadOnly: true}}},
},
imageVolumes: kubecontainer.ImageVolumes{
"image1": image1,
"image2": image2,
},
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,
},
{
MountPath: "/mnt/image1",
Name: "image1",
ReadOnly: false,
},
{
MountPath: "/mnt/image2",
Name: "image2",
ReadOnly: true,
},
},
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_PRIVATE,
},
{
Name: "image1",
ContainerPath: "/mnt/image1",
Image: image1,
ReadOnly: true,
SELinuxRelabel: false,
},
{
Name: "image2",
ContainerPath: "/mnt/image2",
Image: image2,
ReadOnly: true,
SELinuxRelabel: false,
},
},
expectErr: false,
},
"invalid absolute SubPath": {
imageVolumeFeatureEnabled: []bool{true, false},
podVolumes: kubecontainer.VolumeMap{
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
},
@ -195,6 +397,7 @@ func TestMakeMounts(t *testing.T) {
expectedErrMsg: "error SubPath `/must/not/be/absolute` must not be an absolute path",
},
"invalid SubPath with backsteps": {
imageVolumeFeatureEnabled: []bool{true, false},
podVolumes: kubecontainer.VolumeMap{
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
},
@ -212,7 +415,7 @@ func TestMakeMounts(t *testing.T) {
expectedErrMsg: "unable to provision SubPath `no/backsteps/../allowed`: must not contain '..'",
},
"volume doesn't exist": {
podVolumes: kubecontainer.VolumeMap{},
imageVolumeFeatureEnabled: []bool{true, false},
container: v1.Container{
VolumeMounts: []v1.VolumeMount{
{
@ -226,6 +429,7 @@ func TestMakeMounts(t *testing.T) {
expectedErrMsg: "cannot find volume \"disk\" to mount into container \"\"",
},
"volume mounter is nil": {
imageVolumeFeatureEnabled: []bool{true, false},
podVolumes: kubecontainer.VolumeMap{
"disk": kubecontainer.VolumeInfo{},
},
@ -244,7 +448,10 @@ func TestMakeMounts(t *testing.T) {
}
for name, tc := range testCases {
for _, featureEnabled := range tc.imageVolumeFeatureEnabled {
name := fmt.Sprintf("features.ImageVolume is %v, %s", featureEnabled, name)
t.Run(name, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ImageVolume, featureEnabled)
fhu := hostutil.NewFakeHostUtil(nil)
fsp := &subpath.FakeSubpath{}
pod := v1.Pod{
@ -253,7 +460,7 @@ func TestMakeMounts(t *testing.T) {
},
}
mounts, _, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", []string{""}, tc.podVolumes, fhu, fsp, nil, tc.supportsRRO, nil)
mounts, _, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", []string{""}, tc.podVolumes, fhu, fsp, nil, tc.supportsRRO, tc.imageVolumes)
// validate only the error if we expect an error
if tc.expectErr {
@ -271,6 +478,7 @@ func TestMakeMounts(t *testing.T) {
assert.Equal(t, tc.expectedMounts, mounts, "mounts of container %+v", tc.container)
})
}
}
}
func TestMakeMountsEtcHostsFile(t *testing.T) {

View File

@ -690,6 +690,7 @@ func TestVolumeUnmountAndDetachControllerEnabled(t *testing.T) {
type stubVolume struct {
path string
attributes volume.Attributes
volume.MetricsNil
}
@ -698,7 +699,7 @@ func (f *stubVolume) GetPath() string {
}
func (f *stubVolume) GetAttributes() volume.Attributes {
return volume.Attributes{}
return f.attributes
}
func (f *stubVolume) SetUp(mounterArgs volume.MounterArgs) error {