Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apis/apps/v1/cluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,8 @@ const (
// DeletingClusterPhase indicates the cluster is being deleted.
DeletingClusterPhase ClusterPhase = "Deleting"

// FailedClusterPhase represents all components are in `Failed` phase, indicates that the cluster is unavailable.
// FailedClusterPhase represents all components are in `Failed` phase, indicating that the cluster failed to reach
// its desired state.
FailedClusterPhase ClusterPhase = "Failed"

// AbnormalClusterPhase represents some components are in `Failed` phase, indicates that the cluster is in
Expand Down
6 changes: 4 additions & 2 deletions apis/apps/v1/component_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,13 +459,15 @@ const (
// StoppedComponentPhase indicates the component is stopped.
StoppedComponentPhase ComponentPhase = "Stopped"

// FailedComponentPhase indicates that there are some pods of the component not in a 'Running' state.
// FailedComponentPhase indicates that the component failed to reach its desired state, for example because of a
// workload or initial restore failure.
FailedComponentPhase ComponentPhase = "Failed"
)

// component condition types
const (
// ComponentConditionProgressing indicates component controller is applying updates, or workload resource is being updated.
// ComponentConditionProgressing indicates the component is applying updates, its workload is being updated, or its
// initial restore is running.
ComponentConditionProgressing = "Progressing"

// ComponentConditionHealthy indicates its workload resource is running and ready.
Expand Down
16 changes: 8 additions & 8 deletions apis/workloads/v1/instanceset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,8 +738,8 @@ const (
// InstanceFailure is added in an instance set when at least one of its instances(pods) is in a `Failed` phase.
InstanceFailure ConditionType = "InstanceFailure"

// InstanceRestore indicates whether the initial data restore for this Instance or InstanceSet has completed.
InstanceRestore ConditionType = "Restore"
// Restore indicates whether the initial data restore for this Instance or InstanceSet has completed.
Restore ConditionType = "Restore"

// InstanceUpdateRestricted represents a ConditionType that indicates updates to an InstanceSet are blocked(when the
// PodUpdatePolicy is set to StrictInPlace but the pods cannot be updated in-place).
Expand All @@ -762,14 +762,14 @@ const (
// ReasonInstanceFailure is a reason for condition InstanceFailure.
ReasonInstanceFailure = "InstanceFailure"

// ReasonRestoreCompleted is a reason for condition InstanceRestore.
ReasonRestoreCompleted = "RestoreCompleted"
// ReasonRestoreCompleted is a reason for condition Restore.
ReasonRestoreCompleted = "Completed"

// ReasonRestoreRunning is a reason for condition InstanceRestore.
ReasonRestoreRunning = "RestoreRunning"
// ReasonRestoreRunning is a reason for condition Restore.
ReasonRestoreRunning = "Running"

// ReasonRestoreFailed is a reason for condition InstanceRestore.
ReasonRestoreFailed = "RestoreFailed"
// ReasonRestoreFailed is a reason for condition Restore.
ReasonRestoreFailed = "Failed"

// ReasonInstanceUpdateRestricted is a reason for condition InstanceUpdateRestricted.
ReasonInstanceUpdateRestricted = "InstanceUpdateRestricted"
Expand Down
6 changes: 3 additions & 3 deletions controllers/apps/cluster/cluster_status_conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ const (
ReasonApplyResourcesSucceed = "ApplyResourcesSucceed" // ReasonApplyResourcesSucceed applies resources succeeded to create or change the cluster
ReasonClusterReady = "ClusterReady" // ReasonClusterReady the components of cluster are ready, the component phase is running
ReasonComponentsNotReady = "ComponentsNotReady" // ReasonComponentsNotReady the components of cluster are not ready
ReasonRestoreCompleted = "RestoreCompleted"
ReasonRestoreRunning = "RestoreRunning"
ReasonRestoreFailed = "RestoreFailed"
ReasonRestoreCompleted = "Completed"
ReasonRestoreRunning = "Running"
ReasonRestoreFailed = "Failed"
)

func setProvisioningStartedCondition(conditions *[]metav1.Condition, clusterName string, clusterGeneration int64, err error) {
Expand Down
22 changes: 22 additions & 0 deletions controllers/apps/cluster/transformer_cluster_status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,25 @@ var _ = Describe("syncClusterConditions", func() {
Expect(availCond.Status).Should(Equal(metav1.ConditionFalse))
})
})

var _ = DescribeTable("compose cluster phase from restore-projected component phases",
func(componentPhases []appsv1.ComponentPhase, expected appsv1.ClusterPhase) {
statuses := make([]appsv1.ClusterComponentStatus, 0, len(componentPhases))
for _, phase := range componentPhases {
statuses = append(statuses, appsv1.ClusterComponentStatus{Phase: phase})
}
Expect(composeClusterPhase(statuses)).Should(Equal(expected))
},
Entry("all components are restoring",
[]appsv1.ComponentPhase{appsv1.CreatingComponentPhase, appsv1.CreatingComponentPhase},
appsv1.CreatingClusterPhase),
Entry("some components are restoring",
[]appsv1.ComponentPhase{appsv1.CreatingComponentPhase, appsv1.RunningComponentPhase},
appsv1.UpdatingClusterPhase),
Entry("all components failed to restore",
[]appsv1.ComponentPhase{appsv1.FailedComponentPhase, appsv1.FailedComponentPhase},
appsv1.FailedClusterPhase),
Entry("some components failed to restore",
[]appsv1.ComponentPhase{appsv1.FailedComponentPhase, appsv1.RunningComponentPhase},
appsv1.AbnormalClusterPhase),
)
84 changes: 71 additions & 13 deletions controllers/apps/component/transformer_component_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,26 +166,74 @@ func (t *componentStatusTransformer) reconcileStatus(transCtx *componentTransfor
}, phase)
}()

var (
workloadPhase appsv1.ComponentPhase
statusMessage map[string]string
phaseTransitionMsg string
)
switch {
case isDeleting:
t.setComponentStatusPhase(transCtx, appsv1.DeletingComponentPhase, nil, "component is Deleting")
workloadPhase = appsv1.DeletingComponentPhase
phaseTransitionMsg = "component is Deleting"
case stopped && (hasRunningPods || !checkPostProvisionDone(transCtx)):
t.setComponentStatusPhase(transCtx, appsv1.StoppingComponentPhase, nil, "component is Stopping")
workloadPhase = appsv1.StoppingComponentPhase
phaseTransitionMsg = "component is Stopping"
case stopped:
t.setComponentStatusPhase(transCtx, appsv1.StoppedComponentPhase, nil, "component is Stopped")
workloadPhase = appsv1.StoppedComponentPhase
phaseTransitionMsg = "component is Stopped"
case isITSUpdatedNRunning && !hasRunningScaleOut && !hasRunningVolumeExpansion:
t.setComponentStatusPhase(transCtx, appsv1.RunningComponentPhase, nil, "component is Running")
workloadPhase = appsv1.RunningComponentPhase
phaseTransitionMsg = "component is Running"
case !hasFailure && isInCreatingPhase:
t.setComponentStatusPhase(transCtx, appsv1.CreatingComponentPhase, nil, "component is Creating")
workloadPhase = appsv1.CreatingComponentPhase
phaseTransitionMsg = "component is Creating"
case !hasFailure && isInStartingPhase:
t.setComponentStatusPhase(transCtx, appsv1.StartingComponentPhase, nil, "component is Starting")
workloadPhase = appsv1.StartingComponentPhase
phaseTransitionMsg = "component is Starting"
case !hasFailure:
t.setComponentStatusPhase(transCtx, appsv1.UpdatingComponentPhase, nil, "component is Updating")
workloadPhase = appsv1.UpdatingComponentPhase
phaseTransitionMsg = "component is Updating"
default:
t.setComponentStatusPhase(transCtx, appsv1.FailedComponentPhase, messages, "component is Failed")
workloadPhase = appsv1.FailedComponentPhase
statusMessage = messages
phaseTransitionMsg = "component is Failed"
}

if err := t.reconcileRestoreCondition(transCtx); err != nil {
return err
}
phase := t.phaseWithRestore(workloadPhase)
if phase != workloadPhase {
phaseTransitionMsg = fmt.Sprintf("component is %s", phase)
}
t.setComponentStatusPhase(transCtx, phase, statusMessage, phaseTransitionMsg)

return t.reconcileStatusCondition(transCtx)
return t.reconcileStatusConditionsWithoutRestore(transCtx)
}

// phaseWithRestore projects the initial restore result onto the component phase.
// Workload lifecycle states caused by explicit user intent take precedence.
func (t *componentStatusTransformer) phaseWithRestore(workloadPhase appsv1.ComponentPhase) appsv1.ComponentPhase {
if slices.Contains([]appsv1.ComponentPhase{
appsv1.DeletingComponentPhase,
appsv1.StoppingComponentPhase,
appsv1.StoppedComponentPhase,
appsv1.FailedComponentPhase,
}, workloadPhase) {
return workloadPhase
}
restoreCond := meta.FindStatusCondition(t.comp.Status.Conditions, appsv1.ConditionTypeRestore)
if restoreCond == nil {
return workloadPhase
}
switch restoreCond.Status {
case metav1.ConditionFalse:
return appsv1.FailedComponentPhase
case metav1.ConditionUnknown:
return appsv1.CreatingComponentPhase
default:
return workloadPhase
}
}

func (t *componentStatusTransformer) workloadGeneration() (*int64, error) {
Expand Down Expand Up @@ -349,11 +397,17 @@ func (t *componentStatusTransformer) updateComponentStatus(transCtx *componentTr
}

func (t *componentStatusTransformer) reconcileStatusCondition(transCtx *componentTransformContext) error {
if err := t.reconcileRestoreCondition(transCtx); err != nil {
return err
}
return t.reconcileStatusConditionsWithoutRestore(transCtx)
}

func (t *componentStatusTransformer) reconcileStatusConditionsWithoutRestore(transCtx *componentTransformContext) error {
return errors.Join(
t.reconcileAvailableCondition(transCtx),
t.reconcileProgressingCondition(transCtx),
t.reconcileHealthyCondition(transCtx),
t.reconcileRestoreCondition(transCtx),
)
}

Expand All @@ -379,7 +433,7 @@ func (t *componentStatusTransformer) reconcileRestoreCondition(transCtx *compone
})
return nil
}
workloadCond := meta.FindStatusCondition(t.runningITS.Status.Conditions, string(workloads.InstanceRestore))
workloadCond := meta.FindStatusCondition(t.runningITS.Status.Conditions, string(workloads.Restore))
if workloadCond == nil {
meta.SetStatusCondition(&t.comp.Status.Conditions, metav1.Condition{
Type: appsv1.ConditionTypeRestore,
Expand Down Expand Up @@ -502,6 +556,10 @@ func (t *componentStatusTransformer) reconcileProgressingCondition(transCtx *com
transCtx.EventRecorder,
appsv1.ComponentConditionProgressing,
func() (status metav1.ConditionStatus, reason string, message string, err error) {
restoreCond := meta.FindStatusCondition(t.comp.Status.Conditions, appsv1.ConditionTypeRestore)
if restoreCond != nil && restoreCond.Status == metav1.ConditionUnknown {
return metav1.ConditionTrue, "RestoreRunning", restoreCond.Message, nil
}
if !t.isWorkloadUpdated() {
return metav1.ConditionTrue, "WorkloadNotUpdated", "observed workload's generation not matching component's", nil
}
Expand Down Expand Up @@ -567,7 +625,7 @@ func (t *componentStatusTransformer) reconcileAvailableCondition(transCtx *compo
appsv1.ComponentConditionAvailable,
func() (status metav1.ConditionStatus, reason string, message string, err error) {
if policy.WithPhases != nil {
status, message1 := t.availableWithPhases(transCtx, transCtx.Component, policy)
status, message1 := t.availableWithPhases(transCtx.Component, policy)
if status != metav1.ConditionTrue {
return status, "PhaseCheckFail", message1, nil
}
Expand All @@ -586,7 +644,7 @@ func (t *componentStatusTransformer) reconcileAvailableCondition(transCtx *compo
)
}

func (t *componentStatusTransformer) availableWithPhases(_ *componentTransformContext,
func (t *componentStatusTransformer) availableWithPhases(
comp *appsv1.Component, policy appsv1.ComponentAvailable) (metav1.ConditionStatus, string) {
if comp.Status.Phase == "" {
return metav1.ConditionUnknown, "the component phase is unknown"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ var _ = Describe("component status transformer conditions", func() {

setWorkloadRestoreCondition := func(status metav1.ConditionStatus) {
runningITS.Status.Conditions = []metav1.Condition{{
Type: string(workloads.InstanceRestore),
Type: string(workloads.Restore),
Status: status,
Reason: workloads.ReasonRestoreRunning,
Message: "workload restore status",
Expand Down Expand Up @@ -282,6 +282,44 @@ var _ = Describe("component status transformer conditions", func() {
})
})

DescribeTable("should project restore status onto component summary conditions",
func(restoreStatus metav1.ConditionStatus, expectedPhase appsv1.ComponentPhase,
expectedAvailable, expectedProgressing metav1.ConditionStatus, expectedRestoreReason string) {
setExpectedRestoreVCT()
setWorkloadRestoreCondition(restoreStatus)
compDef.Spec.Available = &appsv1.ComponentAvailable{
WithPhases: ptr.To("Running"),
}

err := transformer.reconcileStatus(transCtx)
Expect(err).Should(BeNil())
Expect(comp.Status.Phase).Should(Equal(expectedPhase))

restoreCond := meta.FindStatusCondition(comp.Status.Conditions, appsv1.ConditionTypeRestore)
Expect(restoreCond).ShouldNot(BeNil())
Expect(restoreCond.Status).Should(Equal(restoreStatus))
Expect(restoreCond.Reason).Should(Equal(expectedRestoreReason))
availableCond := meta.FindStatusCondition(comp.Status.Conditions, appsv1.ComponentConditionAvailable)
Expect(availableCond).ShouldNot(BeNil())
Expect(availableCond.Status).Should(Equal(expectedAvailable))
healthyCond := meta.FindStatusCondition(comp.Status.Conditions, appsv1.ComponentConditionHealthy)
Expect(healthyCond).ShouldNot(BeNil())
Expect(healthyCond.Status).Should(Equal(metav1.ConditionTrue))
progressingCond := meta.FindStatusCondition(comp.Status.Conditions, appsv1.ComponentConditionProgressing)
Expect(progressingCond).ShouldNot(BeNil())
Expect(progressingCond.Status).Should(Equal(expectedProgressing))
if restoreStatus == metav1.ConditionUnknown {
Expect(progressingCond.Reason).Should(Equal("RestoreRunning"))
}
},
Entry("restore is running", metav1.ConditionUnknown, appsv1.CreatingComponentPhase,
metav1.ConditionFalse, metav1.ConditionTrue, "Running"),
Entry("restore has failed", metav1.ConditionFalse, appsv1.FailedComponentPhase,
metav1.ConditionFalse, metav1.ConditionFalse, "Failed"),
Entry("restore has completed", metav1.ConditionTrue, appsv1.RunningComponentPhase,
metav1.ConditionTrue, metav1.ConditionFalse, "Completed"),
)

Context("reconcileHealthyCondition", func() {
It("should be unhealthy when runningITS is nil", func() {
transformer.runningITS = nil
Expand Down
2 changes: 1 addition & 1 deletion controllers/dataprotection/backup_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2098,7 +2098,7 @@ var _ = Describe("Backup Controller test", func() {
cluster.Status.Conditions = []metav1.Condition{{
Type: kbappsv1.ConditionTypeRestore,
Status: metav1.ConditionTrue,
Reason: "RestoreCompleted",
Reason: "Completed",
LastTransitionTime: metav1.Now(),
}}
})).Should(Succeed())
Expand Down
12 changes: 7 additions & 5 deletions docs/developer_docs/api-reference/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -3896,7 +3896,8 @@ a fragile state and troubleshooting is required.</p>
<td><p>DeletingClusterPhase indicates the cluster is being deleted.</p>
</td>
</tr><tr><td><p>&#34;Failed&#34;</p></td>
<td><p>FailedClusterPhase represents all components are in <code>Failed</code> phase, indicates that the cluster is unavailable.</p>
<td><p>FailedClusterPhase represents all components are in <code>Failed</code> phase, indicating that the cluster failed to reach
its desired state.</p>
</td>
</tr><tr><td><p>&#34;Running&#34;</p></td>
<td><p>RunningClusterPhase represents all components are in <code>Running</code> phase, indicates that the cluster is functioning properly.</p>
Expand Down Expand Up @@ -6685,7 +6686,8 @@ It allows optional mapping for container ports to host ports.</p>
<td><p>DeletingComponentPhase indicates the component is currently being deleted.</p>
</td>
</tr><tr><td><p>&#34;Failed&#34;</p></td>
<td><p>FailedComponentPhase indicates that there are some pods of the component not in a &lsquo;Running&rsquo; state.</p>
<td><p>FailedComponentPhase indicates that the component failed to reach its desired state, for example because of a
workload or initial restore failure.</p>
</td>
</tr><tr><td><p>&#34;Running&#34;</p></td>
<td><p>RunningComponentPhase indicates that all pods of the component are up-to-date and in a &lsquo;Running&rsquo; state.</p>
Expand Down Expand Up @@ -19326,13 +19328,13 @@ and continue for &ldquo;MinReadySeconds&rdquo; seconds. Otherwise, it will be se
ConditionStatus will be True if all its instances(pods) are in a Ready condition.
Or, a NotReady reason with not ready instances encoded in the Message filed will be set.</p>
</td>
</tr><tr><td><p>&#34;Restore&#34;</p></td>
<td><p>InstanceRestore indicates whether the initial data restore for this Instance or InstanceSet has completed.</p>
</td>
</tr><tr><td><p>&#34;InstanceUpdateRestricted&#34;</p></td>
<td><p>InstanceUpdateRestricted represents a ConditionType that indicates updates to an InstanceSet are blocked(when the
PodUpdatePolicy is set to StrictInPlace but the pods cannot be updated in-place).</p>
</td>
</tr><tr><td><p>&#34;Restore&#34;</p></td>
<td><p>Restore indicates whether the initial data restore for this Instance or InstanceSet has completed.</p>
</td>
</tr></tbody>
</table>
<h3 id="workloads.kubeblocks.io/v1.ConfigTemplate">ConfigTemplate
Expand Down
12 changes: 6 additions & 6 deletions pkg/controller/instance/reconciler_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,13 @@ func (r *statusReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
}

func (r *statusReconciler) reconcileRestoreCondition(tree *kubebuilderx.ObjectTree, inst *workloads.Instance) {
restoreCond := meta.FindStatusCondition(inst.Status.Conditions, string(workloads.InstanceRestore))
restoreCond := meta.FindStatusCondition(inst.Status.Conditions, string(workloads.Restore))
if restoreCond != nil && (restoreCond.Status == metav1.ConditionTrue || restoreCond.Status == metav1.ConditionFalse) {
return
}
condition := r.buildRestoreCondition(tree, inst)
if condition == nil {
meta.RemoveStatusCondition(&inst.Status.Conditions, string(workloads.InstanceRestore))
meta.RemoveStatusCondition(&inst.Status.Conditions, string(workloads.Restore))
return
}
meta.SetStatusCondition(&inst.Status.Conditions, *condition)
Expand Down Expand Up @@ -168,7 +168,7 @@ func (r *statusReconciler) buildRestoreCondition(tree *kubebuilderx.ObjectTree,
}
if cond.Status == corev1.ConditionFalse {
return &metav1.Condition{
Type: string(workloads.InstanceRestore),
Type: string(workloads.Restore),
Status: metav1.ConditionFalse,
ObservedGeneration: inst.Generation,
Reason: workloads.ReasonRestoreFailed,
Expand All @@ -181,7 +181,7 @@ func (r *statusReconciler) buildRestoreCondition(tree *kubebuilderx.ObjectTree,
}
if completed == len(expectedPVCNames) {
return &metav1.Condition{
Type: string(workloads.InstanceRestore),
Type: string(workloads.Restore),
Status: metav1.ConditionTrue,
ObservedGeneration: inst.Generation,
Reason: workloads.ReasonRestoreCompleted,
Expand All @@ -190,7 +190,7 @@ func (r *statusReconciler) buildRestoreCondition(tree *kubebuilderx.ObjectTree,
}
sort.Strings(waiting)
return &metav1.Condition{
Type: string(workloads.InstanceRestore),
Type: string(workloads.Restore),
Status: metav1.ConditionUnknown,
ObservedGeneration: inst.Generation,
Reason: workloads.ReasonRestoreRunning,
Expand All @@ -200,7 +200,7 @@ func (r *statusReconciler) buildRestoreCondition(tree *kubebuilderx.ObjectTree,

func findPVCRestoreCondition(pvc *corev1.PersistentVolumeClaim) *corev1.PersistentVolumeClaimCondition {
for i := range pvc.Status.Conditions {
if string(pvc.Status.Conditions[i].Type) == string(workloads.InstanceRestore) {
if string(pvc.Status.Conditions[i].Type) == string(workloads.Restore) {
return &pvc.Status.Conditions[i]
}
}
Expand Down
Loading
Loading