feat: add VolumeAttachment wait after drain using cached client#207
feat: add VolumeAttachment wait after drain using cached client#207
Conversation
31bfc06 to
232802c
Compare
| | `API_KEY` | CAST AI API key (required) | - | | ||
| | `API_URL` | CAST AI API URL (required) | - | | ||
| | `CLUSTER_ID` | CAST AI cluster ID (required) | - | | ||
| | `DRAIN_VOLUME_DETACH_TIMEOUT` | Default timeout for waiting for VolumeAttachments to detach during node drain | `60s` | |
There was a problem hiding this comment.
Is this a good idea? Feel free to ignore if it's not.
| | `DRAIN_VOLUME_DETACH_TIMEOUT` | Default timeout for waiting for VolumeAttachments to detach during node drain | `60s` | | |
| | `FALLBACK_DRAIN_VOLUME_DETACH_TIMEOUT` | Default timeout for waiting for VolumeAttachments to detach during node drain | `60s` | |
There was a problem hiding this comment.
Hmm... how about DRAIN_VOLUME_DETACH_DEFAULT_TIMEOUT? So it's clearer to the user, that it's a default value, but it could be overridden by something (API payload in that case).
internal/actions/actions.go
Outdated
| // May be nil if informer sync failed. | ||
| VAWaiter VolumeDetachmentWaiter |
There was a problem hiding this comment.
Do you think the config should instead contain only static configuration and handler objects should be passed separately?
There was a problem hiding this comment.
Yeah, makes sense, given that Kubernetes clients are passed seperatelly already.
| r.Equal(60*time.Second, h.getVolumeDetachTimeout(req)) | ||
| }) | ||
|
|
||
| t.Run("returns per-action timeout when set", func(t *testing.T) { |
There was a problem hiding this comment.
This tests seem repetitive, could they be rewritten as table tests?
Tsonov
left a comment
There was a problem hiding this comment.
Overall LGTM, mostly polishing questions
b58ae66 to
1a244a6
Compare
Add optional feature to wait for VolumeAttachments to be deleted after draining a node, preventing Multi-Attach errors when CSI drivers need time to clean up volumes before the node is terminated. Uses controller-runtime's cached client with field indexes for efficient VolumeAttachment and Pod queries without hitting the API server directly. Follows Karpenter's approach of excluding VAs from non-drainable pods (DaemonSets, static pods) to avoid deadlocks. Key changes: - Add Drain config section with WaitForVolumeDetach, VolumeDetachTimeout, and CacheSyncTimeout settings - Create controller-runtime cache with field indexes for VolumeAttachment and Pod resources by spec.nodeName - Implement getVolumeAttachmentsForNode() using Karpenter-style filtering - Graceful degradation: if cache sync fails, feature is disabled but drain operations continue normally Environment variables: - DRAIN_WAIT_FOR_VOLUME_DETACH: Enable feature (default: false) - DRAIN_VOLUME_DETACH_TIMEOUT: Max wait time (default: 60s) - CACHE_SYNC_TIMEOUT: Cache sync timeout (default: 120s)
…tach The spec.nodeName field selector is not supported by the Kubernetes API server for VolumeAttachment resources. This caused the waitForVolumeDetach function to fail silently. Switch to using the controller-runtime cached client which has a custom field indexer configured for spec.nodeName lookups on VolumeAttachments.
…onfig - Fix typos: EnablePodInfomer -> EnablePodInformer, EnableNodeInfomer -> EnableNodeInformer - Add nil checks in addIndexers and reportCacheSize for disabled informers - Fix Start() to sync node/pod informers independently - Fix VA getter logic to check vaAvailable flag instead of nil - Add DisableWaitForVolumeDetach config option - Update run.go to use new informer options - Update tests to enable informers explicitly
…table-driven tests - Move VolumeDetachmentWaiter from internal/actions to internal/volume package - Rename to DetachmentWaiter for cleaner API within volume package - Update drain handler and config to use new package location - Convert TestShouldWaitForVolumeDetach and TestGetVolumeDetachTimeout to table-driven tests - Rename config field VolumeDetachTimeout to VolumeDetachDefaultTimeout for clarity
Add explicit SelfSubjectAccessReview check before starting the VA informer to verify get/list/watch permissions on volumeattachments.storage.k8s.io. Benefits: - Fast feedback (~50ms) vs waiting for 30s sync timeout - Clear error messages indicating which specific permission is missing - Graceful degradation - VA feature disabled if permissions not granted The check uses the standard authorization.k8s.io/v1 SelfSubjectAccessReview API which any authenticated user can call to check their own permissions.
ad83b3f to
a777d17
Compare
| log logrus.FieldLogger, | ||
| clientset kubernetes.Interface, | ||
| castNamespace string, | ||
| volumeDetachTimeout time.Duration, |
There was a problem hiding this comment.
wouldn't it make more sense to move the default timeout to the waiter instead? Then the following logic wouldn't be needed in the waiter constructor:
if timeout == 0 {
timeout = DefaultVolumeDetachTimeout
}
and this drain handler wouldn't need to know about the default which should be a bit simpler logic
There was a problem hiding this comment.
Makes also sense, my reasoning was that we could reuse this somewhere else, where we want to have a different default timeout, but I don't see any objections against a default timeout on the waiter
internal/volume/detachment_waiter.go
Outdated
| vaIndexer cache.Indexer, | ||
| pollInterval time.Duration, | ||
| ) DetachmentWaiter { | ||
| if vaIndexer == nil { |
There was a problem hiding this comment.
the caller already can check for such condition, no need to hide such complexity on lower levels. Constructors should return something useful if were called or return an error if something is missing. That should help to avoid silent failures
internal/informer/manager.go
Outdated
| // Returns (true, nil) if all permissions are available. | ||
| // Returns (false, nil) if any permission is missing (logs which ones). | ||
| // Returns (false, error) if the permission check itself fails. | ||
| func (m *Manager) checkVAPermissions(ctx context.Context) (bool, error) { |
There was a problem hiding this comment.
wouldn't be enough to just return an error in case of generic error, noPermissions type err or no error when everything is good?
Adds optional feature to wait for VolumeAttachments to be deleted after node drain, preventing CSI Multi-Attach errors.
Key Changes:
spec.nodeNameNew Environment Variables:
Things to Remember