Change:
IMO it feels like a bad practice to have mutating resource handlers. The deletePod handler is used for queueing a Recreate deployment once it has no pods running. It should merely look at the ReplicaSets that have owner refs as opposed to using dc.getReplicaSetsForDeployment. The orphaning/adoption dance is already happening inside the reconcilation loop.
I'd like to work on this
feel free to grab it. I can't assign the issue to you so I'll add myself; ping me on the PR when you have one.

Discussion:
glog.V(4).Infof("Pod %s deleted.", pod.Name) if d := dc.getDeploymentForPod(pod); d != nil && d.Spec.Strategy.Type == extensions.RecreateDeploymentStrategyType { // Sync if this Deployment now has no more Pods. <del> rsList, err := dc.getReplicaSetsForDeployment(d) </del> <ins> rsList, err := util.ListReplicaSets(d, util.RsListFromClient(dc.client)) </ins> if err != nil { return }

foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) foo.Spec.Strategy.Type = extensions.RecreateDeploymentStrategyType <del> rs := newReplicaSet(foo, "foo-1", 1) pod := generatePodFromRS(rs) </del> <ins> rs1 := newReplicaSet(foo, "foo-1", 1) rs2 := newReplicaSet(foo, "foo-1", 1) pod1 := generatePodFromRS(rs1) pod2 := generatePodFromRS(rs2) </ins> f.dLister = append(f.dLister, foo) <del> f.rsLister = append(f.rsLister, rs) </del> <ins> // Let's pretend this is a different pod. The gist is that the pod lister needs to // return a non-empty list. f.podLister = append(f.podLister, pod1, pod2) c, _ := f.newController() enqueued := false c.enqueueDeployment = func(d *extensions.Deployment) { if d.Name == "foo" { enqueued = true } } c.deletePod(pod1) if enqueued { t.Errorf("expected deployment %q not to be queued after pod deletion", foo.Name) } } // TestPodDeletionPartialReplicaSetOwnershipEnqueueRecreateDeployment ensures that // the deletion of a pod will requeue a Recreate deployment iff there is no other // pod returned from the client in the case where a deployment has multiple replica // sets, some of which have empty owner references. func TestPodDeletionPartialReplicaSetOwnershipEnqueueRecreateDeployment(t *testing.T) { f := newFixture(t) foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) foo.Spec.Strategy.Type = extensions.RecreateDeploymentStrategyType rs1 := newReplicaSet(foo, "foo-1", 1) rs2 := newReplicaSet(foo, "foo-2", 2) rs2.OwnerReferences = nil pod := generatePodFromRS(rs1) f.dLister = append(f.dLister, foo) f.rsLister = append(f.rsLister, rs1, rs2) f.objects = append(f.objects, foo, rs1, rs2) c, _ := f.newController() enqueued := false c.enqueueDeployment = func(d *extensions.Deployment) { if d.Name == "foo" { enqueued = true } } c.deletePod(pod) if !enqueued { t.Errorf("expected deployment %q to be queued after pod deletion", foo.Name) } } // TestPodDeletionPartialReplicaSetOwnershipDoesntEnqueueRecreateDeployment that the // deletion of a pod will not requeue a Recreate deployment iff there are other pods // returned from the client in the case where a deployment has multiple replica sets, // some of which have empty owner references. func TestPodDeletionPartialReplicaSetOwnershipDoesntEnqueueRecreateDeployment(t *testing.T) { f := newFixture(t) foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) foo.Spec.Strategy.Type = extensions.RecreateDeploymentStrategyType rs1 := newReplicaSet(foo, "foo-1", 1) rs2 := newReplicaSet(foo, "foo-2", 2) rs2.OwnerReferences = nil pod := generatePodFromRS(rs1) f.dLister = append(f.dLister, foo) f.rsLister = append(f.rsLister, rs1, rs2) f.objects = append(f.objects, foo, rs1, rs2) </ins> // Let's pretend this is a different pod. The gist is that the pod lister needs to // return a non-empty list. f.podLister = append(f.podLister, pod)

// Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets. // The third returned value is the new replica set, and it may be nil if it doesn't exist yet. func GetAllReplicaSets(deployment *extensions.Deployment, c clientset.Interface) ([]*extensions.ReplicaSet, []*extensions.ReplicaSet, *extensions.ReplicaSet, error) { <del> rsList, err := ListReplicaSets(deployment, rsListFromClient(c)) </del> <ins> rsList, err := ListReplicaSets(deployment, RsListFromClient(c)) </ins> if err != nil { return nil, nil, nil, err }

// GetOldReplicaSets returns the old replica sets targeted by the given Deployment; get PodList and ReplicaSetList from client interface. // Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets. func GetOldReplicaSets(deployment *extensions.Deployment, c clientset.Interface) ([]*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { <del> rsList, err := ListReplicaSets(deployment, rsListFromClient(c)) </del> <ins> rsList, err := ListReplicaSets(deployment, RsListFromClient(c)) </ins> if err != nil { return nil, nil, err }

// GetNewReplicaSet returns a replica set that matches the intent of the given deployment; get ReplicaSetList from client interface. // Returns nil if the new replica set doesn't exist yet. func GetNewReplicaSet(deployment *extensions.Deployment, c clientset.Interface) (*extensions.ReplicaSet, error) { <del> rsList, err := ListReplicaSets(deployment, rsListFromClient(c)) </del> <ins> rsList, err := ListReplicaSets(deployment, RsListFromClient(c)) </ins> if err != nil { return nil, err } return FindNewReplicaSet(deployment, rsList) } <del> // rsListFromClient returns an rsListFunc that wraps the given client. func rsListFromClient(c clientset.Interface) rsListFunc { </del> <ins> // RsListFromClient returns an rsListFunc that wraps the given client. func RsListFromClient(c clientset.Interface) RsListFunc { </ins> return func(namespace string, options metav1.ListOptions) ([]*extensions.ReplicaSet, error) { rsList, err := c.Extensions().ReplicaSets(namespace).List(options) if err != nil {

} // TODO: switch this to full namespacers <del> type rsListFunc func(string, metav1.ListOptions) ([]*extensions.ReplicaSet, error) </del> <ins> type RsListFunc func(string, metav1.ListOptions) ([]*extensions.ReplicaSet, error) </ins> type podListFunc func(string, metav1.ListOptions) (*v1.PodList, error) // ListReplicaSets returns a slice of RSes the given deployment targets. // Note that this does NOT attempt to reconcile ControllerRef (adopt/orphan), // because only the controller itself should do that. // However, it does filter out anything whose ControllerRef doesn't match. <del> func ListReplicaSets(deployment *extensions.Deployment, getRSList rsListFunc) ([]*extensions.ReplicaSet, error) { </del> <ins> func ListReplicaSets(deployment *extensions.Deployment, getRSList RsListFunc) ([]*extensions.ReplicaSet, error) { </ins> // TODO: Right now we list replica sets by their labels. We should list them by selector, i.e. the replica set's selector //       should be a superset of the deployment's selector, see https://github.com/kubernetes/kubernetes/issues/19830. namespace := deployment.Namespace