// handleReplicationEnd listens for various events that can cause the replication to end. // // These are: // * The replication signals that it has finished executing. // * The replication is canceled. // * Errors in lock renewal (as signaled on the passed channel). // In this case, the error needs to be communicated up a level. // The passed channel may be nil, in which case this case is not checked, // but the others still are. // // When replication finishes for any of these reasons, this function is responsible for: // * Stopping the replication (if it has not already) // * Destroying its session (passed in to this function. // Passing nil is legal, in which case it is not destroyed) func (r *replication) handleReplicationEnd(session kp.Session, renewalErrCh chan error) { defer func() { close(r.quitCh) close(r.errCh) if r.rateLimiter != nil { r.rateLimiter.Stop() } if session != nil { _ = session.Destroy() } }() select { case <-r.replicationDoneCh: case <-r.replicationCancelledCh: // If the replication is enacted, wait for it to exit <-r.enactedCh case err := <-renewalErrCh: // communicate the error to the caller. r.errCh <- replicationError{ err: err, isFatal: true, } return } }
// Like CreateRollingUpdateFromExistingRCs except will create the new RC based // on passed parameters, using oldRCID for the old RC. The new RC and new RU // will be created transactionally (all or nothing) func (s consulStore) CreateRollingUpdateFromOneExistingRCWithID( oldRCID rc_fields.ID, desiredReplicas int, minimumReplicas int, leaveOld bool, rollDelay time.Duration, newRCManifest manifest.Manifest, newRCNodeSelector klabels.Selector, newRCPodLabels klabels.Set, newRCLabels klabels.Set, rollLabels klabels.Set, ) (u roll_fields.Update, err error) { // There are cases where this function will create the new RC and // subsequently fail, in which case we need to do some cleanup. // cleans up new RC, might be nil if we didn't create one var newRCCleanup func() // If we had an error and the rc cleanup function is set, run it defer func() { if err != nil && newRCCleanup != nil { newRCCleanup() } }() var session kp.Session var renewalErrCh chan error session, renewalErrCh, err = s.newRUCreationSession() if err != nil { return roll_fields.Update{}, err } defer session.Destroy() rcIDs := rc_fields.IDs{oldRCID} err = s.lockRCs(rcIDs, session) if err != nil { return roll_fields.Update{}, err } err = s.checkForConflictingUpdates(rcIDs) if err != nil { return roll_fields.Update{}, err } // Now create the new RC, first checking if our session is still valid var newRCID rc_fields.ID select { case err = <-renewalErrCh: return roll_fields.Update{}, err default: rc, err := s.rcstore.Create(newRCManifest, newRCNodeSelector, newRCPodLabels) if err != nil { return roll_fields.Update{}, err } newRCCleanup = func() { err := s.rcstore.Delete(newRCID, false) if err != nil { s.logger.WithError(err).Errorln("Unable to cleanup RC %s after failed RU creation attempt", newRCID) } } newRCID = rc.ID // Get a lock on the new RC we just created so no parallel // update creations can use it err = s.lockRCs(rc_fields.IDs{newRCID}, session) if err != nil { return roll_fields.Update{}, err } } rcIDs = append(rcIDs, newRCID) // Check for conflicts again in case an update was created on the new // RC between when we created it and locked it err = s.checkForConflictingUpdates(rcIDs) if err != nil { return roll_fields.Update{}, err } err = s.labeler.SetLabels(labels.RC, newRCID.String(), newRCLabels) if err != nil { return roll_fields.Update{}, err } u = roll_fields.Update{ OldRC: oldRCID, NewRC: newRCID, DesiredReplicas: desiredReplicas, MinimumReplicas: minimumReplicas, LeaveOld: leaveOld, RollDelay: rollDelay, } return s.attemptRUCreation(u, rollLabels, renewalErrCh) }