Skip to content

Commit cfdaf18

Browse files
author
Kubernetes Submit Queue
authored
Merge pull request kubernetes#34298 from derekwaynecarr/ns-controller-panic
Automatic merge from submit-queue Fix potential panic in namespace controller when rapidly create/delet… Fixes kubernetes#33676 The theory is this could occur in either of the following scenarios: 1. HA environment where a GET to a different API server than what the WATCH was read from 1. In a many controller scenario (i.e. where multiple finalizers participate), a namespace that is created and deleted with the same name could trip up the other namespace controller to see a namespace with the same name that was not actually in a delete state. Added checks to verify uid matches across retry operations. /cc @liggitt @kubernetes/rh-cluster-infra
2 parents e233f14 + e634312 commit cfdaf18

File tree

1 file changed

+16
-1
lines changed

1 file changed

+16
-1
lines changed

pkg/controller/namespace/namespace_controller_utils.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ func (o operationNotSupportedCache) isSupported(key operationKey) bool {
7171
type updateNamespaceFunc func(kubeClient clientset.Interface, namespace *api.Namespace) (*api.Namespace, error)
7272

7373
// retryOnConflictError retries the specified fn if there was a conflict error
74+
// it will return an error if the UID for an object changes across retry operations.
7475
// TODO RetryOnConflict should be a generic concept in client code
7576
func retryOnConflictError(kubeClient clientset.Interface, namespace *api.Namespace, fn updateNamespaceFunc) (result *api.Namespace, err error) {
7677
latestNamespace := namespace
@@ -82,10 +83,14 @@ func retryOnConflictError(kubeClient clientset.Interface, namespace *api.Namespa
8283
if !errors.IsConflict(err) {
8384
return nil, err
8485
}
86+
prevNamespace := latestNamespace
8587
latestNamespace, err = kubeClient.Core().Namespaces().Get(latestNamespace.Name)
8688
if err != nil {
8789
return nil, err
8890
}
91+
if prevNamespace.UID != latestNamespace.UID {
92+
return nil, fmt.Errorf("namespace uid has changed across retries")
93+
}
8994
}
9095
}
9196

@@ -385,9 +390,19 @@ func syncNamespace(
385390
return err
386391
}
387392

393+
// the latest view of the namespace asserts that namespace is no longer deleting..
394+
if namespace.DeletionTimestamp.IsZero() {
395+
return nil
396+
}
397+
388398
// if the namespace is already finalized, delete it
389399
if finalized(namespace) {
390-
err = kubeClient.Core().Namespaces().Delete(namespace.Name, nil)
400+
var opts *api.DeleteOptions
401+
uid := namespace.UID
402+
if len(uid) > 0 {
403+
opts = &api.DeleteOptions{Preconditions: &api.Preconditions{UID: &uid}}
404+
}
405+
err = kubeClient.Core().Namespaces().Delete(namespace.Name, opts)
391406
if err != nil && !errors.IsNotFound(err) {
392407
return err
393408
}

0 commit comments

Comments
 (0)