func calculateDiffsWithExpectation(cs *appsv1alpha1.CloneSet, pods []*v1.Pod, currentRevision, updateRevision string) (res expectationDiffs) {
	coreControl := clonesetcore.New(cs)
	replicas := int(*cs.Spec.Replicas)
	var partition, maxSurge, maxUnavailable, scaleMaxUnavailable int
	if cs.Spec.UpdateStrategy.Partition != nil {
		if pValue, err := util.CalculatePartitionReplicas(cs.Spec.UpdateStrategy.Partition, cs.Spec.Replicas); err != nil {
			// TODO: maybe, we should block pod update if partition settings is wrong
			klog.Errorf("CloneSet %s/%s partition value is illegal", cs.Namespace, cs.Name)
		} else {
			partition = pValue
		}
	}
	if cs.Spec.UpdateStrategy.MaxSurge != nil {
		maxSurge, _ = intstrutil.GetValueFromIntOrPercent(cs.Spec.UpdateStrategy.MaxSurge, replicas, true)
	}
	maxUnavailable, _ = intstrutil.GetValueFromIntOrPercent(
		intstrutil.ValueOrDefault(cs.Spec.UpdateStrategy.MaxUnavailable, intstrutil.FromString(appsv1alpha1.DefaultCloneSetMaxUnavailable)), replicas, maxSurge == 0)
	scaleMaxUnavailable, _ = intstrutil.GetValueFromIntOrPercent(
		intstrutil.ValueOrDefault(cs.Spec.ScaleStrategy.MaxUnavailable, intstrutil.FromInt(math.MaxInt32)), replicas, true)

	var newRevisionCount, newRevisionActiveCount, oldRevisionCount, oldRevisionActiveCount int
	var unavailableNewRevisionCount, unavailableOldRevisionCount int
	var toDeleteNewRevisionCount, toDeleteOldRevisionCount, preDeletingCount int
	defer func() {
		if res.isEmpty() {
			return
		}
		klog.V(1).Infof("Calculate diffs for CloneSet %s/%s, replicas=%d, partition=%d, maxSurge=%d, maxUnavailable=%d,"+
			" allPods=%d, newRevisionPods=%d, newRevisionActivePods=%d, oldRevisionPods=%d, oldRevisionActivePods=%d,"+
			" unavailableNewRevisionCount=%d, unavailableOldRevisionCount=%d,"+
			" preDeletingCount=%d, toDeleteNewRevisionCount=%d, toDeleteOldRevisionCount=%d."+
			" Result: %+v",
			cs.Namespace, cs.Name, replicas, partition, maxSurge, maxUnavailable,
			len(pods), newRevisionCount, newRevisionActiveCount, oldRevisionCount, oldRevisionActiveCount,
			unavailableNewRevisionCount, unavailableOldRevisionCount,
			preDeletingCount, toDeleteNewRevisionCount, toDeleteOldRevisionCount,
			res)
	}()

	for _, p := range pods {
		if clonesetutils.EqualToRevisionHash("", p, updateRevision) {
			newRevisionCount++

			switch state := lifecycle.GetPodLifecycleState(p); state {
			case appspub.LifecycleStatePreparingDelete:
				preDeletingCount++
			default:
				newRevisionActiveCount++

				if isSpecifiedDelete(cs, p) {
					toDeleteNewRevisionCount++
				} else if !isPodAvailable(coreControl, p, cs.Spec.MinReadySeconds) {
					unavailableNewRevisionCount++
				}
			}

		} else {
			oldRevisionCount++

			switch state := lifecycle.GetPodLifecycleState(p); state {
			case appspub.LifecycleStatePreparingDelete:
				preDeletingCount++
			default:
				oldRevisionActiveCount++

				if isSpecifiedDelete(cs, p) {
					toDeleteOldRevisionCount++
				} else if !isPodAvailable(coreControl, p, cs.Spec.MinReadySeconds) {
					unavailableOldRevisionCount++
				}
			}
		}
	}

	updateOldDiff := oldRevisionActiveCount - partition
	updateNewDiff := newRevisionActiveCount - (replicas - partition)
	totalUnavailable := preDeletingCount + unavailableNewRevisionCount + unavailableOldRevisionCount
	// If the currentRevision and updateRevision are consistent, Pods can only update to this revision
	// If the CloneSetPartitionRollback is not enabled, Pods can only update to the new revision
	if updateRevision == currentRevision || !utilfeature.DefaultFeatureGate.Enabled(features.CloneSetPartitionRollback) {
		updateOldDiff = integer.IntMax(updateOldDiff, 0)
		updateNewDiff = integer.IntMin(updateNewDiff, 0)
	}

	// calculate the number of surge to use
	if maxSurge > 0 {

		// Use surge for maxUnavailable not satisfied before scaling
		var scaleSurge, scaleOldRevisionSurge int
		if toDeleteCount := toDeleteNewRevisionCount + toDeleteOldRevisionCount; toDeleteCount > 0 {
			scaleSurge = integer.IntMin(integer.IntMax((unavailableNewRevisionCount+unavailableOldRevisionCount+toDeleteCount+preDeletingCount)-maxUnavailable, 0), toDeleteCount)
			if scaleSurge > toDeleteNewRevisionCount {
				scaleOldRevisionSurge = scaleSurge - toDeleteNewRevisionCount
			}
		}

		// Use surge for old and new revision updating
		var updateSurge, updateOldRevisionSurge int
		if util.IsIntPlusAndMinus(updateOldDiff, updateNewDiff) {
			if util.IntAbs(updateOldDiff) <= util.IntAbs(updateNewDiff) {
				updateSurge = util.IntAbs(updateOldDiff)
				if updateOldDiff < 0 {
					updateOldRevisionSurge = updateSurge
				}
			} else {
				updateSurge = util.IntAbs(updateNewDiff)
				if updateNewDiff > 0 {
					updateOldRevisionSurge = updateSurge
				}
			}
		}

		// It is because the controller is designed not to do scale and update in once reconcile
		if scaleSurge >= updateSurge {
			res.useSurge = integer.IntMin(maxSurge, scaleSurge)
			res.useSurgeOldRevision = integer.IntMin(res.useSurge, scaleOldRevisionSurge)
		} else {
			res.useSurge = integer.IntMin(maxSurge, updateSurge)
			res.useSurgeOldRevision = integer.IntMin(res.useSurge, updateOldRevisionSurge)
		}
	}

	res.scaleNum = replicas + res.useSurge - len(pods)
	if res.scaleNum > 0 {
		res.scaleNumOldRevision = integer.IntMax(partition+res.useSurgeOldRevision-oldRevisionCount, 0)
	} else if res.scaleNum < 0 {
		res.scaleNumOldRevision = integer.IntMin(partition+res.useSurgeOldRevision-oldRevisionCount, 0)
	}

	if res.scaleNum > 0 {
		res.scaleUpLimit = integer.IntMax(scaleMaxUnavailable-totalUnavailable, 0)
		res.scaleUpLimit = integer.IntMin(res.scaleNum, res.scaleUpLimit)
	}

	if toDeleteNewRevisionCount > 0 || toDeleteOldRevisionCount > 0 || res.scaleNum < 0 {
		res.deleteReadyLimit = integer.IntMax(maxUnavailable+(len(pods)-replicas)-totalUnavailable, 0)
	}

	// The consistency between scale and update will be guaranteed by syncCloneSet and expectations
	if util.IntAbs(updateOldDiff) <= util.IntAbs(updateNewDiff) {
		res.updateNum = updateOldDiff
	} else {
		res.updateNum = 0 - updateNewDiff
	}
	if res.updateNum != 0 {
		res.updateMaxUnavailable = maxUnavailable + len(pods) - replicas
	}

	return
}
// This is the most important algorithm in cloneset-controller.
// It calculates the pod numbers to scaling and updating for current CloneSet.
This is the most important algorithm in cloneset-controller.
It calculates the pod numbers to scaling and updating for current CloneSet.
// This is the most important algorithm in cloneset - controller . // It calculates the pod numbers to scaling and updating for current CloneSet .
func calculateDiffsWithExpectation(cs *appsv1alpha1.CloneSet, pods []*v1.Pod, currentRevision, updateRevision string) (res expectationDiffs) {
	coreControl := clonesetcore.New(cs)
	replicas := int(*cs.Spec.Replicas)
	var partition, maxSurge, maxUnavailable, scaleMaxUnavailable int
	if cs.Spec.UpdateStrategy.Partition != nil {
		if pValue, err := util.CalculatePartitionReplicas(cs.Spec.UpdateStrategy.Partition, cs.Spec.Replicas); err != nil {
			klog.Errorf("CloneSet %s/%s partition value is illegal", cs.Namespace, cs.Name)
		} else {
			partition = pValue
		}
	}
	if cs.Spec.UpdateStrategy.MaxSurge != nil {
		maxSurge, _ = intstrutil.GetValueFromIntOrPercent(cs.Spec.UpdateStrategy.MaxSurge, replicas, true)
	}
	maxUnavailable, _ = intstrutil.GetValueFromIntOrPercent(
		intstrutil.ValueOrDefault(cs.Spec.UpdateStrategy.MaxUnavailable, intstrutil.FromString(appsv1alpha1.DefaultCloneSetMaxUnavailable)), replicas, maxSurge == 0)
	scaleMaxUnavailable, _ = intstrutil.GetValueFromIntOrPercent(
		intstrutil.ValueOrDefault(cs.Spec.ScaleStrategy.MaxUnavailable, intstrutil.FromInt(math.MaxInt32)), replicas, true)
	var newRevisionCount, newRevisionActiveCount, oldRevisionCount, oldRevisionActiveCount int
	var unavailableNewRevisionCount, unavailableOldRevisionCount int
	var toDeleteNewRevisionCount, toDeleteOldRevisionCount, preDeletingCount int
	defer func() {
		if res.isEmpty() {
			return
		}
		klog.V(1).Infof("Calculate diffs for CloneSet %s/%s, replicas=%d, partition=%d, maxSurge=%d, maxUnavailable=%d,"+
			" allPods=%d, newRevisionPods=%d, newRevisionActivePods=%d, oldRevisionPods=%d, oldRevisionActivePods=%d,"+
			" unavailableNewRevisionCount=%d, unavailableOldRevisionCount=%d,"+
			" preDeletingCount=%d, toDeleteNewRevisionCount=%d, toDeleteOldRevisionCount=%d."+
			" Result: %+v",
			cs.Namespace, cs.Name, replicas, partition, maxSurge, maxUnavailable,
			len(pods), newRevisionCount, newRevisionActiveCount, oldRevisionCount, oldRevisionActiveCount,
			unavailableNewRevisionCount, unavailableOldRevisionCount,
			preDeletingCount, toDeleteNewRevisionCount, toDeleteOldRevisionCount,
			res)
	}()
	for _, p := range pods {
		if clonesetutils.EqualToRevisionHash("", p, updateRevision) {
			newRevisionCount++
			switch state := lifecycle.GetPodLifecycleState(p); state {
			case appspub.LifecycleStatePreparingDelete:
				preDeletingCount++
			default:
				newRevisionActiveCount++
				if isSpecifiedDelete(cs, p) {
					toDeleteNewRevisionCount++
				} else if !isPodAvailable(coreControl, p, cs.Spec.MinReadySeconds) {
					unavailableNewRevisionCount++
				}
			}
		} else {
			oldRevisionCount++
			switch state := lifecycle.GetPodLifecycleState(p); state {
			case appspub.LifecycleStatePreparingDelete:
				preDeletingCount++
			default:
				oldRevisionActiveCount++
				if isSpecifiedDelete(cs, p) {
					toDeleteOldRevisionCount++
				} else if !isPodAvailable(coreControl, p, cs.Spec.MinReadySeconds) {
					unavailableOldRevisionCount++
				}
			}
		}
	}
	updateOldDiff := oldRevisionActiveCount - partition
	updateNewDiff := newRevisionActiveCount - (replicas - partition)
	totalUnavailable := preDeletingCount + unavailableNewRevisionCount + unavailableOldRevisionCount
	if updateRevision == currentRevision || !utilfeature.DefaultFeatureGate.Enabled(features.CloneSetPartitionRollback) {
		updateOldDiff = integer.IntMax(updateOldDiff, 0)
		updateNewDiff = integer.IntMin(updateNewDiff, 0)
	}
	if maxSurge > 0 {
		var scaleSurge, scaleOldRevisionSurge int
		if toDeleteCount := toDeleteNewRevisionCount + toDeleteOldRevisionCount; toDeleteCount > 0 {
			scaleSurge = integer.IntMin(integer.IntMax((unavailableNewRevisionCount+unavailableOldRevisionCount+toDeleteCount+preDeletingCount)-maxUnavailable, 0), toDeleteCount)
			if scaleSurge > toDeleteNewRevisionCount {
				scaleOldRevisionSurge = scaleSurge - toDeleteNewRevisionCount
			}
		}
		var updateSurge, updateOldRevisionSurge int
		if util.IsIntPlusAndMinus(updateOldDiff, updateNewDiff) {
			if util.IntAbs(updateOldDiff) <= util.IntAbs(updateNewDiff) {
				updateSurge = util.IntAbs(updateOldDiff)
				if updateOldDiff < 0 {
					updateOldRevisionSurge = updateSurge
				}
			} else {
				updateSurge = util.IntAbs(updateNewDiff)
				if updateNewDiff > 0 {
					updateOldRevisionSurge = updateSurge
				}
			}
		}
		if scaleSurge >= updateSurge {
			res.useSurge = integer.IntMin(maxSurge, scaleSurge)
			res.useSurgeOldRevision = integer.IntMin(res.useSurge, scaleOldRevisionSurge)
		} else {
			res.useSurge = integer.IntMin(maxSurge, updateSurge)
			res.useSurgeOldRevision = integer.IntMin(res.useSurge, updateOldRevisionSurge)
		}
	}
	res.scaleNum = replicas + res.useSurge - len(pods)
	if res.scaleNum > 0 {
		res.scaleNumOldRevision = integer.IntMax(partition+res.useSurgeOldRevision-oldRevisionCount, 0)
	} else if res.scaleNum < 0 {
		res.scaleNumOldRevision = integer.IntMin(partition+res.useSurgeOldRevision-oldRevisionCount, 0)
	}
	if res.scaleNum > 0 {
		res.scaleUpLimit = integer.IntMax(scaleMaxUnavailable-totalUnavailable, 0)
		res.scaleUpLimit = integer.IntMin(res.scaleNum, res.scaleUpLimit)
	}
	if toDeleteNewRevisionCount > 0 || toDeleteOldRevisionCount > 0 || res.scaleNum < 0 {
		res.deleteReadyLimit = integer.IntMax(maxUnavailable+(len(pods)-replicas)-totalUnavailable, 0)
	}
	if util.IntAbs(updateOldDiff) <= util.IntAbs(updateNewDiff) {
		res.updateNum = updateOldDiff
	} else {
		res.updateNum = 0 - updateNewDiff
	}
	if res.updateNum != 0 {
		res.updateMaxUnavailable = maxUnavailable + len(pods) - replicas
	}
	return
}
func calculateDiffsWithExpectation ( cs * appsv1alpha1 . CloneSet , pods [ ] * v1 . Pod , currentRevision , updateRevision string ) ( res expectationDiffs ) { coreControl := clonesetcore . New ( cs ) replicas := int ( * cs . Spec . Replicas ) var partition , maxSurge , maxUnavailable , scaleMaxUnavailable int if cs . Spec . UpdateStrategy . Partition != nil { if pValue , err := util . CalculatePartitionReplicas ( cs . Spec . UpdateStrategy . Partition , cs . Spec . Replicas ) ; err != nil { klog . Errorf ( " " , cs . Namespace , cs . Name ) } else { partition = pValue } } if cs . Spec . UpdateStrategy . MaxSurge != nil { maxSurge , _ = intstrutil . GetValueFromIntOrPercent ( cs . Spec . UpdateStrategy . MaxSurge , replicas , true ) } maxUnavailable , _ = intstrutil . GetValueFromIntOrPercent ( intstrutil . ValueOrDefault ( cs . Spec . UpdateStrategy . MaxUnavailable , intstrutil . FromString ( appsv1alpha1 . DefaultCloneSetMaxUnavailable ) ) , replicas , maxSurge == 0 ) scaleMaxUnavailable , _ = intstrutil . GetValueFromIntOrPercent ( intstrutil . ValueOrDefault ( cs . Spec . ScaleStrategy . MaxUnavailable , intstrutil . FromInt ( math . MaxInt32 ) ) , replicas , true ) var newRevisionCount , newRevisionActiveCount , oldRevisionCount , oldRevisionActiveCount int var unavailableNewRevisionCount , unavailableOldRevisionCount int var toDeleteNewRevisionCount , toDeleteOldRevisionCount , preDeletingCount int defer func ( ) { if res . isEmpty ( ) { return } klog . V ( 1 ) . Infof ( " " + " " + " " + " " + " " , cs . Namespace , cs . Name , replicas , partition , maxSurge , maxUnavailable , len ( pods ) , newRevisionCount , newRevisionActiveCount , oldRevisionCount , oldRevisionActiveCount , unavailableNewRevisionCount , unavailableOldRevisionCount , preDeletingCount , toDeleteNewRevisionCount , toDeleteOldRevisionCount , res ) } ( ) for _ , p := range pods { if clonesetutils . EqualToRevisionHash ( " " , p , updateRevision ) { newRevisionCount ++ switch state := lifecycle . GetPodLifecycleState ( p ) ; state { case appspub . LifecycleStatePreparingDelete : preDeletingCount ++ default : newRevisionActiveCount ++ if isSpecifiedDelete ( cs , p ) { toDeleteNewRevisionCount ++ } else if ! isPodAvailable ( coreControl , p , cs . Spec . MinReadySeconds ) { unavailableNewRevisionCount ++ } } } else { oldRevisionCount ++ switch state := lifecycle . GetPodLifecycleState ( p ) ; state { case appspub . LifecycleStatePreparingDelete : preDeletingCount ++ default : oldRevisionActiveCount ++ if isSpecifiedDelete ( cs , p ) { toDeleteOldRevisionCount ++ } else if ! isPodAvailable ( coreControl , p , cs . Spec . MinReadySeconds ) { unavailableOldRevisionCount ++ } } } } updateOldDiff := oldRevisionActiveCount - partition updateNewDiff := newRevisionActiveCount - ( replicas - partition ) totalUnavailable := preDeletingCount + unavailableNewRevisionCount + unavailableOldRevisionCount if updateRevision == currentRevision || ! utilfeature . DefaultFeatureGate . Enabled ( features . CloneSetPartitionRollback ) { updateOldDiff = integer . IntMax ( updateOldDiff , 0 ) updateNewDiff = integer . IntMin ( updateNewDiff , 0 ) } if maxSurge > 0 { var scaleSurge , scaleOldRevisionSurge int if toDeleteCount := toDeleteNewRevisionCount + toDeleteOldRevisionCount ; toDeleteCount > 0 { scaleSurge = integer . IntMin ( integer . IntMax ( ( unavailableNewRevisionCount + unavailableOldRevisionCount + toDeleteCount + preDeletingCount ) - maxUnavailable , 0 ) , toDeleteCount ) if scaleSurge > toDeleteNewRevisionCount { scaleOldRevisionSurge = scaleSurge - toDeleteNewRevisionCount } } var updateSurge , updateOldRevisionSurge int if util . IsIntPlusAndMinus ( updateOldDiff , updateNewDiff ) { if util . IntAbs ( updateOldDiff ) <= util . IntAbs ( updateNewDiff ) { updateSurge = util . IntAbs ( updateOldDiff ) if updateOldDiff < 0 { updateOldRevisionSurge = updateSurge } } else { updateSurge = util . IntAbs ( updateNewDiff ) if updateNewDiff > 0 { updateOldRevisionSurge = updateSurge } } } if scaleSurge >= updateSurge { res . useSurge = integer . IntMin ( maxSurge , scaleSurge ) res . useSurgeOldRevision = integer . IntMin ( res . useSurge , scaleOldRevisionSurge ) } else { res . useSurge = integer . IntMin ( maxSurge , updateSurge ) res . useSurgeOldRevision = integer . IntMin ( res . useSurge , updateOldRevisionSurge ) } } res . scaleNum = replicas + res . useSurge - len ( pods ) if res . scaleNum > 0 { res . scaleNumOldRevision = integer . IntMax ( partition + res . useSurgeOldRevision - oldRevisionCount , 0 ) } else if res . scaleNum < 0 { res . scaleNumOldRevision = integer . IntMin ( partition + res . useSurgeOldRevision - oldRevisionCount , 0 ) } if res . scaleNum > 0 { res . scaleUpLimit = integer . IntMax ( scaleMaxUnavailable - totalUnavailable , 0 ) res . scaleUpLimit = integer . IntMin ( res . scaleNum , res . scaleUpLimit ) } if toDeleteNewRevisionCount > 0 || toDeleteOldRevisionCount > 0 || res . scaleNum < 0 { res . deleteReadyLimit = integer . IntMax ( maxUnavailable + ( len ( pods ) - replicas ) - totalUnavailable , 0 ) } if util . IntAbs ( updateOldDiff ) <= util . IntAbs ( updateNewDiff ) { res . updateNum = updateOldDiff } else { res . updateNum = 0 - updateNewDiff } if res . updateNum != 0 { res . updateMaxUnavailable = maxUnavailable + len ( pods ) - replicas } return }
This is the most important algorithm in cloneset-controller.
This is the most important algorithm in cloneset - controller .
// TODO: maybe, we should block pod update if partition settings is wrong // If the currentRevision and updateRevision are consistent, Pods can only update to this revision // If the CloneSetPartitionRollback is not enabled, Pods can only update to the new revision // calculate the number of surge to use // Use surge for maxUnavailable not satisfied before scaling // Use surge for old and new revision updating // It is because the controller is designed not to do scale and update in once reconcile // The consistency between scale and update will be guaranteed by syncCloneSet and expectations