fix: kubelet event about unattached volumes is incorrect (#112719)

* fix kubelet event

* add ut

* fix ut
This commit is contained in:
weizhi 2023-01-04 17:51:59 +08:00 committed by GitHub
parent 41b442534d
commit 575616cc72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 127 additions and 13 deletions

View File

@ -419,18 +419,21 @@ func (vm *volumeManager) WaitForAttachAndMount(pod *v1.Pod) error {
if err != nil {
unmountedVolumes :=
vm.getUnmountedVolumes(uniquePodName, expectedVolumes)
// Also get unattached volumes for error message
// Also get unattached volumes and volumes not in dsw for error message
unattachedVolumes :=
vm.getUnattachedVolumes(expectedVolumes)
vm.getUnattachedVolumes(uniquePodName)
volumesNotInDSW :=
vm.getVolumesNotInDSW(uniquePodName, expectedVolumes)
if len(unmountedVolumes) == 0 {
return nil
}
return fmt.Errorf(
"unmounted volumes=%v, unattached volumes=%v: %s",
"unmounted volumes=%v, unattached volumes=%v, failed to process volumes=%v: %s",
unmountedVolumes,
unattachedVolumes,
volumesNotInDSW,
err)
}
@ -474,15 +477,30 @@ func (vm *volumeManager) WaitForUnmount(pod *v1.Pod) error {
return nil
}
// getUnattachedVolumes returns a list of the volumes that are expected to be attached but
// are not currently attached to the node
func (vm *volumeManager) getUnattachedVolumes(expectedVolumes []string) []string {
unattachedVolumes := []string{}
for _, volume := range expectedVolumes {
if !vm.actualStateOfWorld.VolumeExists(v1.UniqueVolumeName(volume)) {
unattachedVolumes = append(unattachedVolumes, volume)
func (vm *volumeManager) getVolumesNotInDSW(uniquePodName types.UniquePodName, expectedVolumes []string) []string {
volumesNotInDSW := sets.NewString(expectedVolumes...)
for _, volumeToMount := range vm.desiredStateOfWorld.GetVolumesToMount() {
if volumeToMount.PodName == uniquePodName {
volumesNotInDSW.Delete(volumeToMount.OuterVolumeSpecName)
}
}
return volumesNotInDSW.List()
}
// getUnattachedVolumes returns a list of the volumes that are expected to be attached but
// are not currently attached to the node
func (vm *volumeManager) getUnattachedVolumes(uniquePodName types.UniquePodName) []string {
unattachedVolumes := []string{}
for _, volumeToMount := range vm.desiredStateOfWorld.GetVolumesToMount() {
if volumeToMount.PodName == uniquePodName &&
volumeToMount.PluginIsAttachable &&
!vm.actualStateOfWorld.VolumeExists(volumeToMount.VolumeName) {
unattachedVolumes = append(unattachedVolumes, volumeToMount.OuterVolumeSpecName)
}
}
return unattachedVolumes
}

View File

@ -21,6 +21,7 @@ import (
"os"
"reflect"
"strconv"
"strings"
"testing"
"time"
@ -137,6 +138,82 @@ func TestGetMountedVolumesForPodAndGetVolumesInUse(t *testing.T) {
}
}
func TestWaitForAttachAndMountError(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("volumeManagerTest")
if err != nil {
t.Fatalf("can't make a temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
podManager := kubepod.NewBasicPodManager(podtest.NewFakeMirrorClient())
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "abc",
Namespace: "nsA",
UID: "1234",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "container1",
VolumeMounts: []v1.VolumeMount{
{
Name: volumetest.FailMountDeviceVolumeName,
MountPath: "/vol1",
},
{
Name: "vol2",
MountPath: "/vol2",
},
{
Name: "vol3",
MountPath: "/vol3",
},
},
},
},
Volumes: []v1.Volume{
{
Name: volumetest.FailMountDeviceVolumeName,
VolumeSource: v1.VolumeSource{
ConfigMap: &v1.ConfigMapVolumeSource{},
},
},
{
Name: "vol2",
VolumeSource: v1.VolumeSource{
RBD: &v1.RBDVolumeSource{},
},
},
{
Name: "vol3",
VolumeSource: v1.VolumeSource{
AzureDisk: &v1.AzureDiskVolumeSource{},
},
},
},
},
}
kubeClient := fake.NewSimpleClientset(pod)
manager := newTestVolumeManager(t, tmpDir, podManager, kubeClient, nil)
stopCh := runVolumeManager(manager)
defer close(stopCh)
podManager.SetPods([]*v1.Pod{pod})
err = manager.WaitForAttachAndMount(pod)
if err == nil {
t.Errorf("Expected error, got none")
}
if !strings.Contains(err.Error(),
"unattached volumes=[vol2], failed to process volumes=[vol3]") {
t.Errorf("Unexpected error info: %v", err)
}
}
func TestInitialPendingVolumesForPodAndGetVolumesInUse(t *testing.T) {
tmpDir, err := utiltesting.MkTmpdir("volumeManagerTest")
if err != nil {
@ -282,14 +359,29 @@ func (p *fakePodStateProvider) ShouldPodContainersBeTerminating(uid kubetypes.UI
}
func newTestVolumeManager(t *testing.T, tmpDir string, podManager kubepod.Manager, kubeClient clientset.Interface, node *v1.Node) VolumeManager {
plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil}
attachablePlug := &volumetest.FakeVolumePlugin{
PluginName: "fake",
Host: nil,
CanSupportFn: func(spec *volume.Spec) bool {
return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.RBD != nil) ||
(spec.Volume != nil && spec.Volume.RBD != nil)
},
}
unattachablePlug := &volumetest.FakeVolumePlugin{
PluginName: "unattachable-fake-plugin",
Host: nil,
NonAttachable: true,
CanSupportFn: func(spec *volume.Spec) bool {
return spec.Volume != nil && spec.Volume.ConfigMap != nil
},
}
fakeRecorder := &record.FakeRecorder{}
plugMgr := &volume.VolumePluginMgr{}
// TODO (#51147) inject mock prober
fakeVolumeHost := volumetest.NewFakeKubeletVolumeHost(t, tmpDir, kubeClient, nil)
fakeVolumeHost.WithNode(node)
plugMgr.InitPlugins([]volume.VolumePlugin{plug}, nil /* prober */, fakeVolumeHost)
plugMgr.InitPlugins([]volume.VolumePlugin{attachablePlug, unattachablePlug}, nil /* prober */, fakeVolumeHost)
stateProvider := &fakePodStateProvider{}
fakePathHandler := volumetest.NewBlockVolumePathHandler()
vm := NewVolumeManager(

View File

@ -183,6 +183,7 @@ type FakeVolumePlugin struct {
SupportsRemount bool
SupportsSELinux bool
DisableNodeExpansion bool
CanSupportFn func(*volume.Spec) bool
// default to false which means it is attachable by default
NonAttachable bool
@ -269,7 +270,10 @@ func (plugin *FakeVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error)
}
func (plugin *FakeVolumePlugin) CanSupport(spec *volume.Spec) bool {
// TODO: maybe pattern-match on spec.Name() to decide?
if plugin.CanSupportFn != nil {
return plugin.CanSupportFn(spec)
}
return true
}