Change:
Seeing this in v1.6.1. a Deployment and a Service the Deployment add an ownerRef that points back to the Service delete the Service The Service and the Deployment are deleted, but the ReplicaSet and pods remain. This is neither Orphan nor Foreground/Background policy. This behavior should never happen. Note: if I make a delete call to the API with propagationPolicy set explicitly then things behave as expected, so this only occurs when the resource's default propagation policy is being used.
There are no sig labels on this issue. Please by:<br(1) mentioning a sig: <br(2) specifying the label manually: <br<brNote: method (1) will trigger a notification to the team. You can find the team list and label list
cc
This is expected. The default garbage collection policy for old controller types (rc, rs, deployment) etc. is orphaning, for backward compatibility. And when doing BackgroundGC, the GC controller doesn't set the deleteOptions.propagationPolicy. We met a similar problem for deleting deployment -RS -pods. Because that is a very common use case, we made a special fix in As you noticed, if you set deleteOptions.propagationPolicy to Orphan or ForegroundGC, it works as you expected.
that seems wrong. if I'm using the garbage collection feature to propagate (whether a user specified it or it was a default of a parent resource), I expect GC to set propagation policy.
rather than a one-off fix in the RS controller, why not address the break in the propagation chain in the GC controller?
In the backgroundGC, the owner is deleted immediately, so unlike orphaning or the foregroundGC, there is no finalizer left for the GC controller to determine the intention of the user. In the absence of the user's intention, the GC controller doesn't set deleteOptions.PropagationPolicy when deleting dependents.
If it's not orphaning, and it's not foregroundGC, the only logical option that remains is backgroundGC
There is another case, user might intentionally choose not to set DeleteOptions.Propagation policy and wants to respect the existing finalizers of the dependents or the default GCstrategy defined by the REST storage of the dependents.
except that it isn't... if I delete resource X and specify no propagation policy, that resource strategy's default propagation policy should take effect just as if the user had specified it.
Add CronJobs to the list of affected resources, since you have CronJob-Job-Pod chain. :+1: to what Jordan said just above.

Discussion:
return claimed, utilerrors.NewAggregate(errlist) } <del> // AdoptReplicaSet sends a patch to take control of the ReplicaSet and also // sets the finalizers to foregroundDeletion. It returns the error if // the patching fails. </del> <ins> // AdoptReplicaSet sends a patch to take control of the ReplicaSet. It returns // the error if the patching fails. </ins> func (m *ReplicaSetControllerRefManager) AdoptReplicaSet(rs *extensions.ReplicaSet) error { if err := m.canAdopt(); err != nil { return fmt.Errorf("can't adopt ReplicaSet %v/%v (%v): %v", rs.Namespace, rs.Name, rs.UID, err)

// Note that ValidateOwnerReferences() will reject this patch if another // OwnerReference exists with controller=true. addControllerPatch := fmt.Sprintf( <del> `{ "metadata": { "ownerReferences": [{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}], "uid":"%s", "finalizers": ["foregroundDeletion"] } }`, </del> <ins> `{"metadata":{"ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"uid":"%s"}}`, </ins> m.controllerKind.GroupVersion(), m.controllerKind.Kind, m.controller.GetName(), m.controller.GetUID(), rs.UID) return m.rsControl.PatchReplicaSet(rs.Namespace, rs.Name, []byte(addControllerPatch))

Name:            d.Name + "-" + podTemplateSpecHash, Namespace:       d.Namespace, OwnerReferences: []metav1.OwnerReference{*newControllerRef(d)}, <del> Finalizers:      []string{metav1.FinalizerDeleteDependents}, </del> }, Spec: extensions.ReplicaSetSpec{ Replicas:        new(int32),

break } } <del> glog.V(2).Infof("at least one owner of object %s has FinalizerDeletingDependents, and the object itself has dependents, so it is going to be deleted with Foreground", item.identity) </del> <ins> glog.V(2).Infof("at least one owner of object %s has FinalizerDeletingDependents, and the object itself has dependents, so it is going to be deleted in Foreground", item.identity) </ins> // the deletion event will be observed by the graphBuilder, so the item // will be processed again in processDeletingDependentsItem. If it // doesn't have dependents, the function will remove the

default: // item doesn't have any solid owner, so it needs to be garbage // collected. Also, none of item's owners is waiting for the deletion of <del> // the dependents, so GC deletes item with Default. glog.V(2).Infof("delete object %s with Default", item.identity) return gc.deleteObject(item.identity, nil) </del> <ins> // the dependents, so set propagationPolicy based on existing finalizers. var policy metav1.DeletionPropagation switch { case hasOrphanFinalizer(latest): // if an existing orphan finalizer is already on the object, honor it. policy = metav1.DeletePropagationOrphan case hasDeleteDependentsFinalizer(latest): // if an existing foreground finalizer is already on the object, honor it. policy = metav1.DeletePropagationForeground default: // otherwise, default to background. policy = metav1.DeletePropagationBackground } glog.V(2).Infof("delete object %s with propagation policy %s", item.identity, policy) return gc.deleteObject(item.identity, &policy) </ins> } }