func maxContainerRestarts(pod *api.Pod) int { maxRestarts := 0 for _, c := range pod.Status.ContainerStatuses { maxRestarts = integer.IntMax(maxRestarts, int(c.RestartCount)) } return maxRestarts }
// Update all pods for a ReplicationController (oldRc) by creating a new // controller (newRc) with 0 replicas, and synchronously scaling oldRc and // newRc until oldRc has 0 replicas and newRc has the original # of desired // replicas. Cleanup occurs based on a RollingUpdaterCleanupPolicy. // // Each interval, the updater will attempt to make progress however it can // without violating any availability constraints defined by the config. This // means the amount scaled up or down each interval will vary based on the // timeliness of readiness and the updater will always try to make progress, // even slowly. // // If an update from newRc to oldRc is already in progress, we attempt to // drive it to completion. If an error occurs at any step of the update, the // error will be returned. // // A scaling event (either up or down) is considered progress; if no progress // is made within the config.Timeout, an error is returned. // // TODO: make this handle performing a rollback of a partially completed // rollout. func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error { out := config.Out oldRc := config.OldRc scaleRetryParams := NewRetryParams(config.Interval, config.Timeout) // Find an existing controller (for continuing an interrupted update) or // create a new one if necessary. sourceId := fmt.Sprintf("%s:%s", oldRc.Name, oldRc.UID) newRc, existed, err := r.getOrCreateTargetController(config.NewRc, sourceId) if err != nil { return err } if existed { fmt.Fprintf(out, "Continuing update with existing controller %s.\n", newRc.Name) } else { fmt.Fprintf(out, "Created %s\n", newRc.Name) } // Extract the desired replica count from the controller. desiredAnnotation, err := strconv.Atoi(newRc.Annotations[desiredReplicasAnnotation]) if err != nil { return fmt.Errorf("Unable to parse annotation for %s: %s=%s", newRc.Name, desiredReplicasAnnotation, newRc.Annotations[desiredReplicasAnnotation]) } desired := int32(desiredAnnotation) // Extract the original replica count from the old controller, adding the // annotation if it doesn't yet exist. _, hasOriginalAnnotation := oldRc.Annotations[originalReplicasAnnotation] if !hasOriginalAnnotation { existing, err := r.c.ReplicationControllers(oldRc.Namespace).Get(oldRc.Name) if err != nil { return err } originReplicas := strconv.Itoa(int(existing.Spec.Replicas)) applyUpdate := func(rc *api.ReplicationController) { if rc.Annotations == nil { rc.Annotations = map[string]string{} } rc.Annotations[originalReplicasAnnotation] = originReplicas } if oldRc, err = updateRcWithRetries(r.c, existing.Namespace, existing, applyUpdate); err != nil { return err } } // maxSurge is the maximum scaling increment and maxUnavailable are the maximum pods // that can be unavailable during a rollout. maxSurge, maxUnavailable, err := deployment.ResolveFenceposts(&config.MaxSurge, &config.MaxUnavailable, desired) if err != nil { return err } // Validate maximums. if desired > 0 && maxUnavailable == 0 && maxSurge == 0 { return fmt.Errorf("one of maxSurge or maxUnavailable must be specified") } // The minumum pods which must remain available througout the update // calculated for internal convenience. minAvailable := int32(integer.IntMax(0, int(desired-maxUnavailable))) // If the desired new scale is 0, then the max unavailable is necessarily // the effective scale of the old RC regardless of the configuration // (equivalent to 100% maxUnavailable). if desired == 0 { maxUnavailable = oldRc.Spec.Replicas minAvailable = 0 } fmt.Fprintf(out, "Scaling up %s from %d to %d, scaling down %s from %d to 0 (keep %d pods available, don't exceed %d pods)\n", newRc.Name, newRc.Spec.Replicas, desired, oldRc.Name, oldRc.Spec.Replicas, minAvailable, desired+maxSurge) // give a caller incremental notification and allow them to exit early goal := desired - newRc.Spec.Replicas if goal < 0 { goal = -goal } progress := func(complete bool) error { if config.OnProgress == nil { return nil } progress := desired - newRc.Spec.Replicas if progress < 0 { progress = -progress } percentage := 100 if !complete && goal > 0 { percentage = int((goal - progress) * 100 / goal) } return config.OnProgress(oldRc, newRc, percentage) } // Scale newRc and oldRc until newRc has the desired number of replicas and // oldRc has 0 replicas. progressDeadline := time.Now().UnixNano() + config.Timeout.Nanoseconds() for newRc.Spec.Replicas != desired || oldRc.Spec.Replicas != 0 { // Store the existing replica counts for progress timeout tracking. newReplicas := newRc.Spec.Replicas oldReplicas := oldRc.Spec.Replicas // Scale up as much as possible. scaledRc, err := r.scaleUp(newRc, oldRc, desired, maxSurge, maxUnavailable, scaleRetryParams, config) if err != nil { return err } newRc = scaledRc // notify the caller if necessary if err := progress(false); err != nil { return err } // Wait between scaling operations for things to settle. time.Sleep(config.UpdatePeriod) // Scale down as much as possible. scaledRc, err = r.scaleDown(newRc, oldRc, desired, minAvailable, maxUnavailable, maxSurge, config) if err != nil { return err } oldRc = scaledRc // notify the caller if necessary if err := progress(false); err != nil { return err } // If we are making progress, continue to advance the progress deadline. // Otherwise, time out with an error. progressMade := (newRc.Spec.Replicas != newReplicas) || (oldRc.Spec.Replicas != oldReplicas) if progressMade { progressDeadline = time.Now().UnixNano() + config.Timeout.Nanoseconds() } else if time.Now().UnixNano() > progressDeadline { return fmt.Errorf("timed out waiting for any update progress to be made") } } // notify the caller if necessary if err := progress(true); err != nil { return err } // Housekeeping and cleanup policy execution. return r.cleanup(oldRc, newRc, config) }
// Update all pods for a ReplicationController (oldRc) by creating a new // controller (newRc) with 0 replicas, and synchronously scaling oldRc and // newRc until oldRc has 0 replicas and newRc has the original # of desired // replicas. Cleanup occurs based on a RollingUpdaterCleanupPolicy. // // Each interval, the updater will attempt to make progress however it can // without violating any availability constraints defined by the config. This // means the amount scaled up or down each interval will vary based on the // timeliness of readiness and the updater will always try to make progress, // even slowly. // // If an update from newRc to oldRc is already in progress, we attempt to // drive it to completion. If an error occurs at any step of the update, the // error will be returned. // // A scaling event (either up or down) is considered progress; if no progress // is made within the config.Timeout, an error is returned. // // TODO: make this handle performing a rollback of a partially completed // rollout. func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error { out := config.Out oldRc := config.OldRc scaleRetryParams := NewRetryParams(config.Interval, config.Timeout) // Find an existing controller (for continuing an interrupted update) or // create a new one if necessary. sourceId := fmt.Sprintf("%s:%s", oldRc.Name, oldRc.UID) newRc, existed, err := r.getOrCreateTargetController(config.NewRc, sourceId) if err != nil { return err } if existed { fmt.Fprintf(out, "Continuing update with existing controller %s.\n", newRc.Name) } else { fmt.Fprintf(out, "Created %s\n", newRc.Name) } // Extract the desired replica count from the controller. desired, err := strconv.Atoi(newRc.Annotations[desiredReplicasAnnotation]) if err != nil { return fmt.Errorf("Unable to parse annotation for %s: %s=%s", newRc.Name, desiredReplicasAnnotation, newRc.Annotations[desiredReplicasAnnotation]) } // Extract the original replica count from the old controller, adding the // annotation if it doesn't yet exist. _, hasOriginalAnnotation := oldRc.Annotations[originalReplicasAnnotation] if !hasOriginalAnnotation { existing, err := r.c.ReplicationControllers(oldRc.Namespace).Get(oldRc.Name) if err != nil { return err } if existing.Annotations == nil { existing.Annotations = map[string]string{} } existing.Annotations[originalReplicasAnnotation] = strconv.Itoa(existing.Spec.Replicas) updated, err := r.c.ReplicationControllers(existing.Namespace).Update(existing) if err != nil { return err } oldRc = updated } original, err := strconv.Atoi(oldRc.Annotations[originalReplicasAnnotation]) if err != nil { return fmt.Errorf("Unable to parse annotation for %s: %s=%s\n", oldRc.Name, originalReplicasAnnotation, oldRc.Annotations[originalReplicasAnnotation]) } // The maximum pods which can go unavailable during the update. maxUnavailable, err := extractMaxValue(config.MaxUnavailable, "maxUnavailable", desired) if err != nil { return err } // The maximum scaling increment. maxSurge, err := extractMaxValue(config.MaxSurge, "maxSurge", desired) if err != nil { return err } // Validate maximums. if desired > 0 && maxUnavailable == 0 && maxSurge == 0 { return fmt.Errorf("one of maxSurge or maxUnavailable must be specified") } // The minumum pods which must remain available througout the update // calculated for internal convenience. minAvailable := integer.IntMax(0, desired-maxUnavailable) // If the desired new scale is 0, then the max unavailable is necessarily // the effective scale of the old RC regardless of the configuration // (equivalent to 100% maxUnavailable). if desired == 0 { maxUnavailable = original minAvailable = 0 } fmt.Fprintf(out, "Scaling up %s from %d to %d, scaling down %s from %d to 0 (keep %d pods available, don't exceed %d pods)\n", newRc.Name, newRc.Spec.Replicas, desired, oldRc.Name, oldRc.Spec.Replicas, minAvailable, original+maxSurge) // Scale newRc and oldRc until newRc has the desired number of replicas and // oldRc has 0 replicas. progressDeadline := time.Now().UnixNano() + config.Timeout.Nanoseconds() for newRc.Spec.Replicas != desired || oldRc.Spec.Replicas != 0 { // Store the existing replica counts for progress timeout tracking. newReplicas := newRc.Spec.Replicas oldReplicas := oldRc.Spec.Replicas // Scale up as much as possible. scaledRc, err := r.scaleUp(newRc, oldRc, original, desired, maxSurge, maxUnavailable, scaleRetryParams, config) if err != nil { return err } newRc = scaledRc // Wait between scaling operations for things to settle. time.Sleep(config.UpdatePeriod) // Scale down as much as possible. scaledRc, err = r.scaleDown(newRc, oldRc, desired, minAvailable, maxUnavailable, maxSurge, config) if err != nil { return err } oldRc = scaledRc // If we are making progress, continue to advance the progress deadline. // Otherwise, time out with an error. progressMade := (newRc.Spec.Replicas != newReplicas) || (oldRc.Spec.Replicas != oldReplicas) if progressMade { progressDeadline = time.Now().UnixNano() + config.Timeout.Nanoseconds() } else if time.Now().UnixNano() > progressDeadline { return fmt.Errorf("timed out waiting for any update progress to be made") } } // Housekeeping and cleanup policy execution. return r.cleanup(oldRc, newRc, config) }