|
|
@ -34,6 +34,7 @@ import (
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
|
|
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
|
|
"k8s.io/apimachinery/pkg/watch"
|
|
|
|
"k8s.io/apimachinery/pkg/watch"
|
|
|
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
|
|
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
|
|
@ -628,120 +629,120 @@ var (
|
|
|
|
errEmptiedFinalizers = fmt.Errorf("emptied finalizers")
|
|
|
|
errEmptiedFinalizers = fmt.Errorf("emptied finalizers")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// shouldUpdateFinalizerOrphanDependents returns if the finalizers need to be
|
|
|
|
// shouldOrphanDependents returns true if the finalizer for orphaning should be set
|
|
|
|
// updated for FinalizerOrphanDependents. In the order of highest to lowest
|
|
|
|
// updated for FinalizerOrphanDependents. In the order of highest to lowest
|
|
|
|
// priority, there are three factors affect whether to add/remove the
|
|
|
|
// priority, there are three factors affect whether to add/remove the
|
|
|
|
// FinalizerOrphanDependents: options, existing finalizers of the object,
|
|
|
|
// FinalizerOrphanDependents: options, existing finalizers of the object,
|
|
|
|
// and e.DeleteStrategy.DefaultGarbageCollectionPolicy.
|
|
|
|
// and e.DeleteStrategy.DefaultGarbageCollectionPolicy.
|
|
|
|
func shouldUpdateFinalizerOrphanDependents(e *Store, accessor metav1.Object, options *metav1.DeleteOptions) (shouldUpdate bool, shouldOrphan bool) {
|
|
|
|
func shouldOrphanDependents(e *Store, accessor metav1.Object, options *metav1.DeleteOptions) bool {
|
|
|
|
shouldOrphan = false
|
|
|
|
|
|
|
|
// Get default orphan policy from this REST object type
|
|
|
|
|
|
|
|
if gcStrategy, ok := e.DeleteStrategy.(rest.GarbageCollectionDeleteStrategy); ok {
|
|
|
|
if gcStrategy, ok := e.DeleteStrategy.(rest.GarbageCollectionDeleteStrategy); ok {
|
|
|
|
if gcStrategy.DefaultGarbageCollectionPolicy() == rest.OrphanDependents {
|
|
|
|
if gcStrategy.DefaultGarbageCollectionPolicy() == rest.Unsupported {
|
|
|
|
shouldOrphan = true
|
|
|
|
// return false to indicate that we should NOT orphan
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If a finalizer is set in the object, it overrides the default
|
|
|
|
// An explicit policy was set at deletion time, that overrides everything
|
|
|
|
hasOrphanFinalizer := false
|
|
|
|
|
|
|
|
finalizers := accessor.GetFinalizers()
|
|
|
|
|
|
|
|
for _, f := range finalizers {
|
|
|
|
|
|
|
|
// validation should make sure the two cases won't be true at the same
|
|
|
|
|
|
|
|
// time.
|
|
|
|
|
|
|
|
switch f {
|
|
|
|
|
|
|
|
case metav1.FinalizerOrphanDependents:
|
|
|
|
|
|
|
|
shouldOrphan = true
|
|
|
|
|
|
|
|
hasOrphanFinalizer = true
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
case metav1.FinalizerDeleteDependents:
|
|
|
|
|
|
|
|
shouldOrphan = false
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If an explicit policy was set at deletion time, that overrides both
|
|
|
|
|
|
|
|
if options != nil && options.OrphanDependents != nil {
|
|
|
|
if options != nil && options.OrphanDependents != nil {
|
|
|
|
shouldOrphan = *options.OrphanDependents
|
|
|
|
return *options.OrphanDependents
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if options != nil && options.PropagationPolicy != nil {
|
|
|
|
if options != nil && options.PropagationPolicy != nil {
|
|
|
|
switch *options.PropagationPolicy {
|
|
|
|
switch *options.PropagationPolicy {
|
|
|
|
case metav1.DeletePropagationOrphan:
|
|
|
|
case metav1.DeletePropagationOrphan:
|
|
|
|
shouldOrphan = true
|
|
|
|
return true
|
|
|
|
case metav1.DeletePropagationBackground, metav1.DeletePropagationForeground:
|
|
|
|
case metav1.DeletePropagationBackground, metav1.DeletePropagationForeground:
|
|
|
|
shouldOrphan = false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
shouldUpdate = shouldOrphan != hasOrphanFinalizer
|
|
|
|
// If a finalizer is set in the object, it overrides the default
|
|
|
|
return shouldUpdate, shouldOrphan
|
|
|
|
// validation should make sure the two cases won't be true at the same time.
|
|
|
|
|
|
|
|
finalizers := accessor.GetFinalizers()
|
|
|
|
|
|
|
|
for _, f := range finalizers {
|
|
|
|
|
|
|
|
switch f {
|
|
|
|
|
|
|
|
case metav1.FinalizerOrphanDependents:
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
case metav1.FinalizerDeleteDependents:
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get default orphan policy from this REST object type if it exists
|
|
|
|
|
|
|
|
if gcStrategy, ok := e.DeleteStrategy.(rest.GarbageCollectionDeleteStrategy); ok {
|
|
|
|
|
|
|
|
if gcStrategy.DefaultGarbageCollectionPolicy() == rest.OrphanDependents {
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// shouldUpdateFinalizerDeleteDependents returns if the finalizers need to be
|
|
|
|
// shouldDeleteDependents returns true if the finalizer for foreground deletion should be set
|
|
|
|
// updated for FinalizerDeleteDependents. In the order of highest to lowest
|
|
|
|
// updated for FinalizerDeleteDependents. In the order of highest to lowest
|
|
|
|
// priority, there are three factors affect whether to add/remove the
|
|
|
|
// priority, there are three factors affect whether to add/remove the
|
|
|
|
// FinalizerDeleteDependents: options, existing finalizers of the object, and
|
|
|
|
// FinalizerDeleteDependents: options, existing finalizers of the object, and
|
|
|
|
// e.DeleteStrategy.DefaultGarbageCollectionPolicy.
|
|
|
|
// e.DeleteStrategy.DefaultGarbageCollectionPolicy.
|
|
|
|
func shouldUpdateFinalizerDeleteDependents(e *Store, accessor metav1.Object, options *metav1.DeleteOptions) (shouldUpdate bool, shouldDeleteDependentInForeground bool) {
|
|
|
|
func shouldDeleteDependents(e *Store, accessor metav1.Object, options *metav1.DeleteOptions) bool {
|
|
|
|
// default to false
|
|
|
|
// Get default orphan policy from this REST object type
|
|
|
|
shouldDeleteDependentInForeground = false
|
|
|
|
if gcStrategy, ok := e.DeleteStrategy.(rest.GarbageCollectionDeleteStrategy); ok && gcStrategy.DefaultGarbageCollectionPolicy() == rest.Unsupported {
|
|
|
|
|
|
|
|
// return false to indicate that we should NOT delete in foreground
|
|
|
|
// If a finalizer is set in the object, it overrides the default
|
|
|
|
return false
|
|
|
|
hasFinalizerDeleteDependents := false
|
|
|
|
|
|
|
|
finalizers := accessor.GetFinalizers()
|
|
|
|
|
|
|
|
for _, f := range finalizers {
|
|
|
|
|
|
|
|
// validation has made sure the two cases won't be true at the same
|
|
|
|
|
|
|
|
// time.
|
|
|
|
|
|
|
|
switch f {
|
|
|
|
|
|
|
|
case metav1.FinalizerDeleteDependents:
|
|
|
|
|
|
|
|
shouldDeleteDependentInForeground = true
|
|
|
|
|
|
|
|
hasFinalizerDeleteDependents = true
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
case metav1.FinalizerOrphanDependents:
|
|
|
|
|
|
|
|
shouldDeleteDependentInForeground = false
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If an explicit policy was set at deletion time, that overrides both
|
|
|
|
// If an explicit policy was set at deletion time, that overrides both
|
|
|
|
if options != nil && options.OrphanDependents != nil {
|
|
|
|
if options != nil && options.OrphanDependents != nil {
|
|
|
|
shouldDeleteDependentInForeground = false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if options != nil && options.PropagationPolicy != nil {
|
|
|
|
if options != nil && options.PropagationPolicy != nil {
|
|
|
|
switch *options.PropagationPolicy {
|
|
|
|
switch *options.PropagationPolicy {
|
|
|
|
case metav1.DeletePropagationForeground:
|
|
|
|
case metav1.DeletePropagationForeground:
|
|
|
|
shouldDeleteDependentInForeground = true
|
|
|
|
return true
|
|
|
|
case metav1.DeletePropagationBackground, metav1.DeletePropagationOrphan:
|
|
|
|
case metav1.DeletePropagationBackground, metav1.DeletePropagationOrphan:
|
|
|
|
shouldDeleteDependentInForeground = false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
shouldUpdate = shouldDeleteDependentInForeground != hasFinalizerDeleteDependents
|
|
|
|
// If a finalizer is set in the object, it overrides the default
|
|
|
|
return shouldUpdate, shouldDeleteDependentInForeground
|
|
|
|
// validation has made sure the two cases won't be true at the same time.
|
|
|
|
}
|
|
|
|
finalizers := accessor.GetFinalizers()
|
|
|
|
|
|
|
|
for _, f := range finalizers {
|
|
|
|
// shouldUpdateFinalizers returns if we need to update the finalizers of the
|
|
|
|
switch f {
|
|
|
|
// object, and the desired list of finalizers.
|
|
|
|
case metav1.FinalizerDeleteDependents:
|
|
|
|
func shouldUpdateFinalizers(e *Store, accessor metav1.Object, options *metav1.DeleteOptions) (shouldUpdate bool, newFinalizers []string) {
|
|
|
|
return true
|
|
|
|
shouldUpdate1, shouldOrphan := shouldUpdateFinalizerOrphanDependents(e, accessor, options)
|
|
|
|
case metav1.FinalizerOrphanDependents:
|
|
|
|
shouldUpdate2, shouldDeleteDependentInForeground := shouldUpdateFinalizerDeleteDependents(e, accessor, options)
|
|
|
|
return false
|
|
|
|
oldFinalizers := accessor.GetFinalizers()
|
|
|
|
}
|
|
|
|
if !shouldUpdate1 && !shouldUpdate2 {
|
|
|
|
|
|
|
|
return false, oldFinalizers
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// deletionFinalizers returns the deletion finalizers we should set on the object and a bool indicate they did or
|
|
|
|
|
|
|
|
// did not change.
|
|
|
|
|
|
|
|
func deletionFinalizers(e *Store, accessor metav1.Object, options *metav1.DeleteOptions) (bool, []string) {
|
|
|
|
|
|
|
|
shouldOrphan := shouldOrphanDependents(e, accessor, options)
|
|
|
|
|
|
|
|
shouldDeleteDependentInForeground := shouldDeleteDependents(e, accessor, options)
|
|
|
|
|
|
|
|
newFinalizers := []string{}
|
|
|
|
|
|
|
|
|
|
|
|
// first remove both finalizers, add them back if needed.
|
|
|
|
// first remove both finalizers, add them back if needed.
|
|
|
|
for _, f := range oldFinalizers {
|
|
|
|
for _, f := range accessor.GetFinalizers() {
|
|
|
|
if f == metav1.FinalizerOrphanDependents || f == metav1.FinalizerDeleteDependents {
|
|
|
|
if f == metav1.FinalizerOrphanDependents || f == metav1.FinalizerDeleteDependents {
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newFinalizers = append(newFinalizers, f)
|
|
|
|
newFinalizers = append(newFinalizers, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if shouldOrphan {
|
|
|
|
if shouldOrphan {
|
|
|
|
newFinalizers = append(newFinalizers, metav1.FinalizerOrphanDependents)
|
|
|
|
newFinalizers = append(newFinalizers, metav1.FinalizerOrphanDependents)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if shouldDeleteDependentInForeground {
|
|
|
|
if shouldDeleteDependentInForeground {
|
|
|
|
newFinalizers = append(newFinalizers, metav1.FinalizerDeleteDependents)
|
|
|
|
newFinalizers = append(newFinalizers, metav1.FinalizerDeleteDependents)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
oldFinalizerSet := sets.NewString(accessor.GetFinalizers()...)
|
|
|
|
|
|
|
|
newFinalizersSet := sets.NewString(newFinalizers...)
|
|
|
|
|
|
|
|
if oldFinalizerSet.Equal(newFinalizersSet) {
|
|
|
|
|
|
|
|
return false, accessor.GetFinalizers()
|
|
|
|
|
|
|
|
}
|
|
|
|
return true, newFinalizers
|
|
|
|
return true, newFinalizers
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -871,8 +872,8 @@ func (e *Store) updateForGracefulDeletionAndFinalizers(ctx genericapirequest.Con
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
shouldUpdate, newFinalizers := shouldUpdateFinalizers(e, existingAccessor, options)
|
|
|
|
needsUpdate, newFinalizers := deletionFinalizers(e, existingAccessor, options)
|
|
|
|
if shouldUpdate {
|
|
|
|
if needsUpdate {
|
|
|
|
existingAccessor.SetFinalizers(newFinalizers)
|
|
|
|
existingAccessor.SetFinalizers(newFinalizers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -964,7 +965,7 @@ func (e *Store) Delete(ctx genericapirequest.Context, name string, options *meta
|
|
|
|
// Handle combinations of graceful deletion and finalization by issuing
|
|
|
|
// Handle combinations of graceful deletion and finalization by issuing
|
|
|
|
// the correct updates.
|
|
|
|
// the correct updates.
|
|
|
|
if e.EnableGarbageCollection {
|
|
|
|
if e.EnableGarbageCollection {
|
|
|
|
shouldUpdateFinalizers, _ := shouldUpdateFinalizers(e, accessor, options)
|
|
|
|
shouldUpdateFinalizers, _ := deletionFinalizers(e, accessor, options)
|
|
|
|
// TODO: remove the check, because we support no-op updates now.
|
|
|
|
// TODO: remove the check, because we support no-op updates now.
|
|
|
|
if graceful || pendingFinalizers || shouldUpdateFinalizers {
|
|
|
|
if graceful || pendingFinalizers || shouldUpdateFinalizers {
|
|
|
|
err, ignoreNotFound, deleteImmediately, out, lastExisting = e.updateForGracefulDeletionAndFinalizers(ctx, name, key, options, preconditions, obj)
|
|
|
|
err, ignoreNotFound, deleteImmediately, out, lastExisting = e.updateForGracefulDeletionAndFinalizers(ctx, name, key, options, preconditions, obj)
|
|
|
|