mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Merge pull request #46160 from NickrenREN/fix-UX
Automatic merge from submit-queue fix regression in UX experience for double attach volume send event when volume is not allowed to multi-attach Fixes #46012 **Release note**: ```release-note NONE ```
This commit is contained in:
commit
079020f559
@ -144,7 +144,8 @@ func NewAttachDetachController(
|
|||||||
adc.desiredStateOfWorld,
|
adc.desiredStateOfWorld,
|
||||||
adc.actualStateOfWorld,
|
adc.actualStateOfWorld,
|
||||||
adc.attacherDetacher,
|
adc.attacherDetacher,
|
||||||
adc.nodeStatusUpdater)
|
adc.nodeStatusUpdater,
|
||||||
|
recorder)
|
||||||
|
|
||||||
adc.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
|
adc.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator(
|
||||||
desiredStateOfWorldPopulatorLoopSleepPeriod,
|
desiredStateOfWorldPopulatorLoopSleepPeriod,
|
||||||
|
@ -158,6 +158,10 @@ type nodeManaged struct {
|
|||||||
|
|
||||||
// The volume object represents a volume that should be attached to a node.
|
// The volume object represents a volume that should be attached to a node.
|
||||||
type volumeToAttach struct {
|
type volumeToAttach struct {
|
||||||
|
// multiAttachErrorReported indicates whether the multi-attach error has been reported for the given volume.
|
||||||
|
// It is used to to prevent reporting the error from being reported more than once for a given volume.
|
||||||
|
multiAttachErrorReported bool
|
||||||
|
|
||||||
// volumeName contains the unique identifier for this volume.
|
// volumeName contains the unique identifier for this volume.
|
||||||
volumeName v1.UniqueVolumeName
|
volumeName v1.UniqueVolumeName
|
||||||
|
|
||||||
@ -231,6 +235,7 @@ func (dsw *desiredStateOfWorld) AddPod(
|
|||||||
volumeObj, volumeExists := nodeObj.volumesToAttach[volumeName]
|
volumeObj, volumeExists := nodeObj.volumesToAttach[volumeName]
|
||||||
if !volumeExists {
|
if !volumeExists {
|
||||||
volumeObj = volumeToAttach{
|
volumeObj = volumeToAttach{
|
||||||
|
multiAttachErrorReported: false,
|
||||||
volumeName: volumeName,
|
volumeName: volumeName,
|
||||||
spec: volumeSpec,
|
spec: volumeSpec,
|
||||||
scheduledPods: make(map[types.UniquePodName]pod),
|
scheduledPods: make(map[types.UniquePodName]pod),
|
||||||
@ -349,6 +354,7 @@ func (dsw *desiredStateOfWorld) GetVolumesToAttach() []VolumeToAttach {
|
|||||||
volumesToAttach = append(volumesToAttach,
|
volumesToAttach = append(volumesToAttach,
|
||||||
VolumeToAttach{
|
VolumeToAttach{
|
||||||
VolumeToAttach: operationexecutor.VolumeToAttach{
|
VolumeToAttach: operationexecutor.VolumeToAttach{
|
||||||
|
MultiAttachErrorReported: volumeObj.multiAttachErrorReported,
|
||||||
VolumeName: volumeName,
|
VolumeName: volumeName,
|
||||||
VolumeSpec: volumeObj.spec,
|
VolumeSpec: volumeObj.spec,
|
||||||
NodeName: nodeName,
|
NodeName: nodeName,
|
||||||
|
@ -16,11 +16,13 @@ go_library(
|
|||||||
"//pkg/api/v1:go_default_library",
|
"//pkg/api/v1:go_default_library",
|
||||||
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
"//pkg/controller/volume/attachdetach/cache:go_default_library",
|
||||||
"//pkg/controller/volume/attachdetach/statusupdater:go_default_library",
|
"//pkg/controller/volume/attachdetach/statusupdater:go_default_library",
|
||||||
|
"//pkg/kubelet/events:go_default_library",
|
||||||
"//pkg/util/goroutinemap/exponentialbackoff:go_default_library",
|
"//pkg/util/goroutinemap/exponentialbackoff:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
"//pkg/volume/util/operationexecutor:go_default_library",
|
"//pkg/volume/util/operationexecutor:go_default_library",
|
||||||
"//vendor/github.com/golang/glog:go_default_library",
|
"//vendor/github.com/golang/glog: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/client-go/tools/record:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,9 +25,11 @@ import (
|
|||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/statusupdater"
|
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/statusupdater"
|
||||||
|
kevents "k8s.io/kubernetes/pkg/kubelet/events"
|
||||||
"k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff"
|
"k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
||||||
@ -63,7 +65,8 @@ func NewReconciler(
|
|||||||
desiredStateOfWorld cache.DesiredStateOfWorld,
|
desiredStateOfWorld cache.DesiredStateOfWorld,
|
||||||
actualStateOfWorld cache.ActualStateOfWorld,
|
actualStateOfWorld cache.ActualStateOfWorld,
|
||||||
attacherDetacher operationexecutor.OperationExecutor,
|
attacherDetacher operationexecutor.OperationExecutor,
|
||||||
nodeStatusUpdater statusupdater.NodeStatusUpdater) Reconciler {
|
nodeStatusUpdater statusupdater.NodeStatusUpdater,
|
||||||
|
recorder record.EventRecorder) Reconciler {
|
||||||
return &reconciler{
|
return &reconciler{
|
||||||
loopPeriod: loopPeriod,
|
loopPeriod: loopPeriod,
|
||||||
maxWaitForUnmountDuration: maxWaitForUnmountDuration,
|
maxWaitForUnmountDuration: maxWaitForUnmountDuration,
|
||||||
@ -74,6 +77,7 @@ func NewReconciler(
|
|||||||
attacherDetacher: attacherDetacher,
|
attacherDetacher: attacherDetacher,
|
||||||
nodeStatusUpdater: nodeStatusUpdater,
|
nodeStatusUpdater: nodeStatusUpdater,
|
||||||
timeOfLastSync: time.Now(),
|
timeOfLastSync: time.Now(),
|
||||||
|
recorder: recorder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +91,7 @@ type reconciler struct {
|
|||||||
nodeStatusUpdater statusupdater.NodeStatusUpdater
|
nodeStatusUpdater statusupdater.NodeStatusUpdater
|
||||||
timeOfLastSync time.Time
|
timeOfLastSync time.Time
|
||||||
disableReconciliationSync bool
|
disableReconciliationSync bool
|
||||||
|
recorder record.EventRecorder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc *reconciler) Run(stopCh <-chan struct{}) {
|
func (rc *reconciler) Run(stopCh <-chan struct{}) {
|
||||||
@ -248,7 +253,14 @@ func (rc *reconciler) reconcile() {
|
|||||||
if rc.isMultiAttachForbidden(volumeToAttach.VolumeSpec) {
|
if rc.isMultiAttachForbidden(volumeToAttach.VolumeSpec) {
|
||||||
nodes := rc.actualStateOfWorld.GetNodesForVolume(volumeToAttach.VolumeName)
|
nodes := rc.actualStateOfWorld.GetNodesForVolume(volumeToAttach.VolumeName)
|
||||||
if len(nodes) > 0 {
|
if len(nodes) > 0 {
|
||||||
glog.V(4).Infof("Volume %q is already exclusively attached to node %q and can't be attached to %q", volumeToAttach.VolumeName, nodes, volumeToAttach.NodeName)
|
if !volumeToAttach.MultiAttachErrorReported {
|
||||||
|
simpleMsg, detailedMsg := volumeToAttach.GenerateMsg("Multi-Attach error", "Volume is already exclusively attached to one node and can't be attached to another")
|
||||||
|
for _, pod := range volumeToAttach.ScheduledPods {
|
||||||
|
rc.recorder.Eventf(pod, v1.EventTypeWarning, kevents.FailedAttachVolume, simpleMsg)
|
||||||
|
}
|
||||||
|
volumeToAttach.MultiAttachErrorReported = true
|
||||||
|
glog.Warningf(detailedMsg)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ func Test_Run_Positive_DoNothing(t *testing.T) {
|
|||||||
nsu := statusupdater.NewNodeStatusUpdater(
|
nsu := statusupdater.NewNodeStatusUpdater(
|
||||||
fakeKubeClient, informerFactory.Core().V1().Nodes().Lister(), asw)
|
fakeKubeClient, informerFactory.Core().V1().Nodes().Lister(), asw)
|
||||||
reconciler := NewReconciler(
|
reconciler := NewReconciler(
|
||||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu)
|
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, fakeRecorder)
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
@ -83,7 +83,7 @@ func Test_Run_Positive_OneDesiredVolumeAttach(t *testing.T) {
|
|||||||
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
||||||
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
||||||
reconciler := NewReconciler(
|
reconciler := NewReconciler(
|
||||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu)
|
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, fakeRecorder)
|
||||||
podName := "pod-uid"
|
podName := "pod-uid"
|
||||||
volumeName := v1.UniqueVolumeName("volume-name")
|
volumeName := v1.UniqueVolumeName("volume-name")
|
||||||
volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
|
volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
|
||||||
@ -129,7 +129,7 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithUnmountedVolume(t *te
|
|||||||
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
||||||
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
||||||
reconciler := NewReconciler(
|
reconciler := NewReconciler(
|
||||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu)
|
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, fakeRecorder)
|
||||||
podName := "pod-uid"
|
podName := "pod-uid"
|
||||||
volumeName := v1.UniqueVolumeName("volume-name")
|
volumeName := v1.UniqueVolumeName("volume-name")
|
||||||
volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
|
volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
|
||||||
@ -196,7 +196,7 @@ func Test_Run_Positive_OneDesiredVolumeAttachThenDetachWithMountedVolume(t *test
|
|||||||
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
||||||
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
||||||
reconciler := NewReconciler(
|
reconciler := NewReconciler(
|
||||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu)
|
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, fakeRecorder)
|
||||||
podName := "pod-uid"
|
podName := "pod-uid"
|
||||||
volumeName := v1.UniqueVolumeName("volume-name")
|
volumeName := v1.UniqueVolumeName("volume-name")
|
||||||
volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
|
volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
|
||||||
@ -263,7 +263,7 @@ func Test_Run_Negative_OneDesiredVolumeAttachThenDetachWithUnmountedVolumeUpdate
|
|||||||
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
||||||
nsu := statusupdater.NewFakeNodeStatusUpdater(true /* returnError */)
|
nsu := statusupdater.NewFakeNodeStatusUpdater(true /* returnError */)
|
||||||
reconciler := NewReconciler(
|
reconciler := NewReconciler(
|
||||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu)
|
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, fakeRecorder)
|
||||||
podName := "pod-uid"
|
podName := "pod-uid"
|
||||||
volumeName := v1.UniqueVolumeName("volume-name")
|
volumeName := v1.UniqueVolumeName("volume-name")
|
||||||
volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
|
volumeSpec := controllervolumetesting.GetTestVolumeSpec(string(volumeName), volumeName)
|
||||||
@ -333,7 +333,7 @@ func Test_Run_OneVolumeAttachAndDetachMultipleNodesWithReadWriteMany(t *testing.
|
|||||||
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
||||||
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
||||||
reconciler := NewReconciler(
|
reconciler := NewReconciler(
|
||||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu)
|
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, fakeRecorder)
|
||||||
podName1 := "pod-uid1"
|
podName1 := "pod-uid1"
|
||||||
podName2 := "pod-uid2"
|
podName2 := "pod-uid2"
|
||||||
volumeName := v1.UniqueVolumeName("volume-name")
|
volumeName := v1.UniqueVolumeName("volume-name")
|
||||||
@ -423,7 +423,7 @@ func Test_Run_OneVolumeAttachAndDetachMultipleNodesWithReadWriteOnce(t *testing.
|
|||||||
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
ad := operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(fakeKubeClient, volumePluginMgr, fakeRecorder, false /* checkNodeCapabilitiesBeforeMount */))
|
||||||
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
nsu := statusupdater.NewFakeNodeStatusUpdater(false /* returnError */)
|
||||||
reconciler := NewReconciler(
|
reconciler := NewReconciler(
|
||||||
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu)
|
reconcilerLoopPeriod, maxWaitForUnmountDuration, syncLoopPeriod, false, dsw, asw, ad, nsu, fakeRecorder)
|
||||||
podName1 := "pod-uid1"
|
podName1 := "pod-uid1"
|
||||||
podName2 := "pod-uid2"
|
podName2 := "pod-uid2"
|
||||||
volumeName := v1.UniqueVolumeName("volume-name")
|
volumeName := v1.UniqueVolumeName("volume-name")
|
||||||
|
@ -47,6 +47,7 @@ const (
|
|||||||
NodeNotSchedulable = "NodeNotSchedulable"
|
NodeNotSchedulable = "NodeNotSchedulable"
|
||||||
StartingKubelet = "Starting"
|
StartingKubelet = "Starting"
|
||||||
KubeletSetupFailed = "KubeletSetupFailed"
|
KubeletSetupFailed = "KubeletSetupFailed"
|
||||||
|
FailedAttachVolume = "FailedAttachVolume"
|
||||||
FailedDetachVolume = "FailedDetachVolume"
|
FailedDetachVolume = "FailedDetachVolume"
|
||||||
FailedMountVolume = "FailedMount"
|
FailedMountVolume = "FailedMount"
|
||||||
FailedUnMountVolume = "FailedUnMount"
|
FailedUnMountVolume = "FailedUnMount"
|
||||||
|
@ -211,6 +211,10 @@ func generateVolumeMsg(prefixMsg, suffixMsg, volumeName, details string) (simple
|
|||||||
|
|
||||||
// VolumeToAttach represents a volume that should be attached to a node.
|
// VolumeToAttach represents a volume that should be attached to a node.
|
||||||
type VolumeToAttach struct {
|
type VolumeToAttach struct {
|
||||||
|
// MultiAttachErrorReported indicates whether the multi-attach error has been reported for the given volume.
|
||||||
|
// It is used to to prevent reporting the error from being reported more than once for a given volume.
|
||||||
|
MultiAttachErrorReported bool
|
||||||
|
|
||||||
// VolumeName is the unique identifier for the volume that should be
|
// VolumeName is the unique identifier for the volume that should be
|
||||||
// attached.
|
// attached.
|
||||||
VolumeName v1.UniqueVolumeName
|
VolumeName v1.UniqueVolumeName
|
||||||
|
Loading…
Reference in New Issue
Block a user