func validateClusterUpdatingStatus()

in pkg/controllers/updaterun/validation.go [178:253]


func validateClusterUpdatingStatus(
	curStage, updatingStageIndex, lastFinishedStageIndex int,
	stageStatus *placementv1beta1.StageUpdatingStatus,
	updateRun *placementv1beta1.ClusterStagedUpdateRun,
) (int, int, error) {
	stageSucceedCond := meta.FindStatusCondition(stageStatus.Conditions, string(placementv1beta1.StageUpdatingConditionSucceeded))
	stageStartedCond := meta.FindStatusCondition(stageStatus.Conditions, string(placementv1beta1.StageUpdatingConditionProgressing))
	if condition.IsConditionStatusTrue(stageSucceedCond, updateRun.Generation) {
		// The stage has finished.
		if updatingStageIndex != -1 && curStage > updatingStageIndex {
			// The finished stage is after the updating stage.
			unexpectedErr := controller.NewUnexpectedBehaviorError(fmt.Errorf("the finished stage `%d` is after the updating stage `%d`", curStage, updatingStageIndex))
			klog.ErrorS(unexpectedErr, "The finished stage is after the updating stage", "clusterStagedUpdateRun", klog.KObj(updateRun))
			return -1, -1, fmt.Errorf("%w: %s", errStagedUpdatedAborted, unexpectedErr.Error())
		}
		// Make sure that all the clusters are updated.
		for curCluster := range stageStatus.Clusters {
			// Check if the cluster is still updating.
			if !condition.IsConditionStatusTrue(meta.FindStatusCondition(
				stageStatus.Clusters[curCluster].Conditions,
				string(placementv1beta1.ClusterUpdatingConditionSucceeded)),
				updateRun.Generation) {
				// The clusters in the finished stage should all have finished too.
				unexpectedErr := controller.NewUnexpectedBehaviorError(fmt.Errorf("cluster `%s` in the finished stage `%s` has not succeeded", stageStatus.Clusters[curCluster].ClusterName, stageStatus.StageName))
				klog.ErrorS(unexpectedErr, "The cluster in a finished stage is still updating", "clusterStagedUpdateRun", klog.KObj(updateRun))
				return -1, -1, fmt.Errorf("%w: %s", errStagedUpdatedAborted, unexpectedErr.Error())
			}
		}
		if curStage != lastFinishedStageIndex+1 {
			// The current finished stage is not right after the last finished stage.
			unexpectedErr := controller.NewUnexpectedBehaviorError(fmt.Errorf("the finished stage `%s` is not right after the last finished stage with index `%d`", stageStatus.StageName, lastFinishedStageIndex))
			klog.ErrorS(unexpectedErr, "There's not yet started stage before the finished stage", "clusterStagedUpdateRun", klog.KObj(updateRun))
			return -1, -1, fmt.Errorf("%w: %s", errStagedUpdatedAborted, unexpectedErr.Error())
		}
		// Record the last finished stage so we can continue from the next stage if no stage is updating.
		lastFinishedStageIndex = curStage
	} else if condition.IsConditionStatusFalse(stageSucceedCond, updateRun.Generation) {
		// The stage has failed.
		failedErr := fmt.Errorf("the stage `%s` has failed, err: %s", stageStatus.StageName, stageSucceedCond.Message)
		klog.ErrorS(failedErr, "The stage has failed", "stageCond", stageSucceedCond, "clusterStagedUpdateRun", klog.KObj(updateRun))
		return -1, -1, fmt.Errorf("%w: %s", errStagedUpdatedAborted, failedErr.Error())
	} else if stageStartedCond != nil {
		// The stage is still updating.
		if updatingStageIndex != -1 {
			// There should be only one stage updating at a time.
			unexpectedErr := controller.NewUnexpectedBehaviorError(fmt.Errorf("the stage `%s` is updating, but there is already a stage with index `%d` updating", stageStatus.StageName, updatingStageIndex))
			klog.ErrorS(unexpectedErr, "Detected more than one updating stages", "clusterStagedUpdateRun", klog.KObj(updateRun))
			return -1, -1, fmt.Errorf("%w: %s", errStagedUpdatedAborted, unexpectedErr.Error())
		}
		if curStage != lastFinishedStageIndex+1 {
			// The current updating stage is not right after the last finished stage.
			unexpectedErr := controller.NewUnexpectedBehaviorError(fmt.Errorf("the updating stage `%s` is not right after the last finished stage with index `%d`", stageStatus.StageName, lastFinishedStageIndex))
			klog.ErrorS(unexpectedErr, "There's not yet started stage before the updating stage", "clusterStagedUpdateRun", klog.KObj(updateRun))
			return -1, -1, fmt.Errorf("%w: %s", errStagedUpdatedAborted, unexpectedErr.Error())
		}
		updatingStageIndex = curStage
		// Collect the updating clusters.
		var updatingClusters []string
		for j := range stageStatus.Clusters {
			clusterStartedCond := meta.FindStatusCondition(stageStatus.Clusters[j].Conditions, string(placementv1beta1.ClusterUpdatingConditionStarted))
			clusterFinishedCond := meta.FindStatusCondition(stageStatus.Clusters[j].Conditions, string(placementv1beta1.ClusterUpdatingConditionSucceeded))
			if condition.IsConditionStatusTrue(clusterStartedCond, updateRun.Generation) &&
				!(condition.IsConditionStatusTrue(clusterFinishedCond, updateRun.Generation) || condition.IsConditionStatusFalse(clusterFinishedCond, updateRun.Generation)) {
				updatingClusters = append(updatingClusters, stageStatus.Clusters[j].ClusterName)
			}
		}
		// We don't allow more than one clusters to be updating at the same time.
		// TODO(wantjian): support multiple clusters updating at the same time.
		if len(updatingClusters) > 1 {
			unexpectedErr := controller.NewUnexpectedBehaviorError(fmt.Errorf("more than one cluster is updating in the stage `%s`, clusters: %v", stageStatus.StageName, updatingClusters))
			klog.ErrorS(unexpectedErr, "Detected more than one updating clusters in the stage", "clusterStagedUpdateRun", klog.KObj(updateRun))
			return -1, -1, fmt.Errorf("%w: %s", errStagedUpdatedAborted, unexpectedErr.Error())
		}
	}
	return updatingStageIndex, lastFinishedStageIndex, nil
}