func (dc *DeploymentController) updateDeployment(old, cur interface{}) { oldD := old.(*extensions.Deployment) curD := cur.(*extensions.Deployment) glog.V(4).Infof("Updating deployment %s", oldD.Name) dc.enqueueDeployment(curD) // If the selector of the current deployment just changed, we need to requeue any old // overlapping deployments. If the new selector steps on another deployment, the current // deployment will get denied during the resync loop. if !reflect.DeepEqual(curD.Spec.Selector, oldD.Spec.Selector) { deployments, err := dc.dLister.Deployments(curD.Namespace).List(labels.Everything()) if err != nil { utilruntime.HandleError(fmt.Errorf("error listing deployments in namespace %s: %v", curD.Namespace, err)) return } // Trigger cleanup of any old overlapping deployments; we don't care about any error // returned here. for i := range deployments { otherD := deployments[i] oldOverlaps, oldErr := util.OverlapsWith(oldD, otherD) curOverlaps, curErr := util.OverlapsWith(curD, otherD) // Enqueue otherD so it gets cleaned up if oldErr == nil && curErr == nil && oldOverlaps && !curOverlaps { dc.enqueueDeployment(otherD) } } } }
func (dc *DeploymentController) deleteDeployment(obj interface{}) { d, ok := obj.(*extensions.Deployment) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { utilruntime.HandleError(fmt.Errorf("Couldn't get object from tombstone %#v", obj)) return } d, ok = tombstone.Obj.(*extensions.Deployment) if !ok { utilruntime.HandleError(fmt.Errorf("Tombstone contained object that is not a Deployment %#v", obj)) return } } glog.V(4).Infof("Deleting deployment %s", d.Name) dc.enqueueDeployment(d) deployments, err := dc.dLister.Deployments(d.Namespace).List(labels.Everything()) if err != nil { utilruntime.HandleError(fmt.Errorf("error listing deployments in namespace %s: %v", d.Namespace, err)) return } // Trigger cleanup of any old overlapping deployments; we don't care about any error // returned here. for i := range deployments { otherD := deployments[i] overlaps, err := util.OverlapsWith(d, otherD) // Enqueue otherD so it gets cleaned up if err == nil && overlaps { dc.enqueueDeployment(otherD) } } }
// handleOverlap relists all deployment in the same namespace for overlaps, and avoid syncing // the newer overlapping ones (only sync the oldest one). New/old is determined by when the // deployment's selector is last updated. func (dc *DeploymentController) handleOverlap(d *extensions.Deployment) error { deployments, err := dc.dLister.Deployments(d.Namespace).List(labels.Everything()) if err != nil { return fmt.Errorf("error listing deployments in namespace %s: %v", d.Namespace, err) } overlapping := false for _, other := range deployments { foundOverlaps, err := util.OverlapsWith(d, other) if err != nil { return err } if foundOverlaps { deploymentCopy, err := util.DeploymentDeepCopy(other) if err != nil { return err } overlapping = true // Skip syncing this one if older overlapping one is found. if util.SelectorUpdatedBefore(deploymentCopy, d) { // We don't care if the overlapping annotation update failed or not (we don't make decision on it) dc.markDeploymentOverlap(d, deploymentCopy.Name) dc.clearDeploymentOverlap(deploymentCopy) return fmt.Errorf("found deployment %s/%s has overlapping selector with an older deployment %s/%s, skip syncing it", d.Namespace, d.Name, deploymentCopy.Namespace, deploymentCopy.Name) } dc.markDeploymentOverlap(deploymentCopy, d.Name) d, _ = dc.clearDeploymentOverlap(d) } } if !overlapping { // We don't care if the overlapping annotation update failed or not (we don't make decision on it) d, _ = dc.clearDeploymentOverlap(d) } return nil }
// handleOverlap will avoid syncing the newer overlapping ones (only sync the oldest one). New/old is // determined by when the deployment's selector is last updated. func (dc *DeploymentController) handleOverlap(d *extensions.Deployment, deployments []*extensions.Deployment) (bool, error) { overlapping := false var errs []error for i := range deployments { otherD := deployments[i] if d.Name == otherD.Name { continue } // Error is already checked during validation foundOverlaps, _ := util.OverlapsWith(d, otherD) // If the otherD deployment overlaps with the current we need to identify which one // holds the set longer and mark the other as overlapping. Requeue the overlapping // deployments if this one has been marked deleted, we only update its status as long // as it is not actually deleted. if foundOverlaps && d.DeletionTimestamp == nil { overlapping = true // Look at the overlapping annotation in both deployments. If one of them has it and points // to the other one then we don't need to compare their timestamps. otherOverlapsWith := otherD.Annotations[util.OverlapAnnotation] currentOverlapsWith := d.Annotations[util.OverlapAnnotation] // The other deployment is already marked as overlapping with the current one. if otherOverlapsWith == d.Name { var err error if d, err = dc.clearDeploymentOverlap(d, otherD.Name); err != nil { errs = append(errs, err) } continue } otherCopy, err := util.DeploymentDeepCopy(otherD) if err != nil { return false, err } // Skip syncing this one if older overlapping one is found. if currentOverlapsWith == otherCopy.Name || util.SelectorUpdatedBefore(otherCopy, d) { if _, err = dc.markDeploymentOverlap(d, otherCopy.Name); err != nil { return false, err } if _, err = dc.clearDeploymentOverlap(otherCopy, d.Name); err != nil { return false, err } return true, fmt.Errorf("deployment %s/%s has overlapping selector with an older deployment %s/%s, skip syncing it", d.Namespace, d.Name, otherCopy.Namespace, otherCopy.Name) } // TODO: We need to support annotations in deployments that overlap with multiple other // deployments. if _, err = dc.markDeploymentOverlap(otherCopy, d.Name); err != nil { errs = append(errs, err) } // This is going to get some deployments into update hotlooping if we remove the overlapping // annotation unconditionally. // // Scenario: // --> Deployment foo with label selector A=A is created. // --> Deployment bar with label selector A=A,B=B is created. Marked as overlapping since it // overlaps with foo. // --> Deployment baz with label selector B=B is created. Marked as overlapping, since it // overlaps with bar, bar overlapping annotation is cleaned up. Next sync loop marks bar // as overlapping and it gets in an update hotloop. if d, err = dc.clearDeploymentOverlap(d, otherCopy.Name); err != nil { errs = append(errs, err) } continue } // If the otherD deployment does not overlap with the current deployment *anymore* // we need to cleanup otherD from the overlapping annotation so it can be synced by // the deployment controller. dName, hasOverlappingAnnotation := otherD.Annotations[util.OverlapAnnotation] if hasOverlappingAnnotation && dName == d.Name { otherCopy, err := util.DeploymentDeepCopy(otherD) if err != nil { return false, err } if _, err = dc.clearDeploymentOverlap(otherCopy, d.Name); err != nil { errs = append(errs, err) } } } if !overlapping { var err error if d, err = dc.clearDeploymentOverlap(d, ""); err != nil { errs = append(errs, err) } } return false, utilerrors.NewAggregate(errs) }