diff --git a/api/v1alpha1/mysqlcluster_types.go b/api/v1alpha1/mysqlcluster_types.go index 0f96cf30..7e7d1ef1 100644 --- a/api/v1alpha1/mysqlcluster_types.go +++ b/api/v1alpha1/mysqlcluster_types.go @@ -242,16 +242,34 @@ type Persistence struct { Size string `json:"size,omitempty"` } +// ClusterState defines cluster state. +type ClusterState string + +const ( + // ClusterInitState indicates whether the cluster is initializing. + ClusterInitState ClusterState = "Initializing" + // ClusterUpdateState indicates whether the cluster is being updated. + ClusterUpdateState ClusterState = "Updating" + // ClusterReadyState indicates whether all containers in the pod are ready. + ClusterReadyState ClusterState = "Ready" + // ClusterCloseState indicates whether the cluster is closed. + ClusterCloseState ClusterState = "Closed" +) + // ClusterConditionType defines type for cluster condition type. type ClusterConditionType string const ( - // ClusterInit indicates whether the cluster is initializing. - ClusterInit ClusterConditionType = "Initializing" - // ClusterReady indicates whether all containers in the pod are ready. - ClusterReady ClusterConditionType = "Ready" - // ClusterError indicates whether the cluster encountered an error. - ClusterError ClusterConditionType = "Error" + // ConditionInit indicates whether the cluster is initializing. + ConditionInit ClusterConditionType = "Initializing" + // ConditionUpdate indicates whether the cluster is being updated. + ConditionUpdate ClusterConditionType = "Updating" + // ConditionReady indicates whether all containers in the pod are ready. + ConditionReady ClusterConditionType = "Ready" + // ConditionClose indicates whether the cluster is closed. + ConditionClose ClusterConditionType = "Closed" + // ConditionError indicates whether there is an error in the cluster. + ConditionError ClusterConditionType = "Error" ) // ClusterCondition defines type for cluster conditions. @@ -321,7 +339,7 @@ type MysqlClusterStatus struct { // ReadyNodes represents number of the nodes that are in ready state. ReadyNodes int `json:"readyNodes,omitempty"` // State - State ClusterConditionType `json:"state,omitempty"` + State ClusterState `json:"state,omitempty"` // Conditions contains the list of the cluster conditions fulfilled. Conditions []ClusterCondition `json:"conditions,omitempty"` // Nodes contains the list of the node status fulfilled. diff --git a/mysqlcluster/syncer/statefulset.go b/mysqlcluster/syncer/statefulset.go index 3d864161..06c46bbc 100644 --- a/mysqlcluster/syncer/statefulset.go +++ b/mysqlcluster/syncer/statefulset.go @@ -470,6 +470,7 @@ func (s *StatefulSetSyncer) applyNWait(ctx context.Context, pod *corev1.Pod) err if pod.ObjectMeta.Labels["controller-revision-hash"] == s.sfs.Status.UpdateRevision { log.Info("pod is already updated", "pod name", pod.Name) } else { + s.Status.State = apiv1alpha1.ClusterUpdateState log.Info("updating pod", "pod", pod.Name, "key", s.Unwrap()) if pod.DeletionTimestamp != nil { log.Info("pod is being deleted", "pod", pod.Name, "key", s.Unwrap()) diff --git a/mysqlcluster/syncer/status.go b/mysqlcluster/syncer/status.go index ba5c8508..4e86e919 100644 --- a/mysqlcluster/syncer/status.go +++ b/mysqlcluster/syncer/status.go @@ -78,12 +78,7 @@ func (s *StatusSyncer) GetOwner() runtime.Object { return s.MysqlCluster } // Sync persists data into the external store. func (s *StatusSyncer) Sync(ctx context.Context) (syncer.SyncResult, error) { - clusterCondition := apiv1alpha1.ClusterCondition{ - Type: apiv1alpha1.ClusterInit, - Status: corev1.ConditionTrue, - LastTransitionTime: metav1.NewTime(time.Now()), - } - s.Status.State = apiv1alpha1.ClusterInit + clusterCondition := s.updateClusterStatus() list := corev1.PodList{} err := s.cli.List( @@ -109,23 +104,24 @@ func (s *StatusSyncer) Sync(ctx context.Context) (syncer.SyncResult, error) { } case corev1.PodScheduled: if cond.Reason == corev1.PodReasonUnschedulable { + // When an error occurs, it is first recorded in the condition, + // but the cluster status is not updated immediately. clusterCondition = apiv1alpha1.ClusterCondition{ - Type: apiv1alpha1.ClusterError, + Type: apiv1alpha1.ConditionError, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(time.Now()), Reason: corev1.PodReasonUnschedulable, Message: cond.Message, } - s.Status.State = apiv1alpha1.ClusterError } } } } s.Status.ReadyNodes = len(readyNodes) - if s.Status.ReadyNodes == int(*s.Spec.Replicas) { - s.Status.State = apiv1alpha1.ClusterReady - clusterCondition.Type = apiv1alpha1.ClusterReady + if s.Status.ReadyNodes == int(*s.Spec.Replicas) && int(*s.Spec.Replicas) != 0 { + s.Status.State = apiv1alpha1.ClusterReadyState + clusterCondition.Type = apiv1alpha1.ConditionReady } if len(s.Status.Conditions) == 0 { @@ -140,10 +136,45 @@ func (s *StatusSyncer) Sync(ctx context.Context) (syncer.SyncResult, error) { s.Status.Conditions = s.Status.Conditions[len(s.Status.Conditions)-maxStatusesQuantity:] } - // update ready nodes' status. + // Update ready nodes' status. return syncer.SyncResult{}, s.updateNodeStatus(ctx, s.cli, readyNodes) } +// updateClusterStatus update the cluster status and returns condition. +func (s *StatusSyncer) updateClusterStatus() apiv1alpha1.ClusterCondition { + clusterCondition := apiv1alpha1.ClusterCondition{ + Type: apiv1alpha1.ConditionInit, + Status: corev1.ConditionTrue, + LastTransitionTime: metav1.NewTime(time.Now()), + } + + oldState := s.Status.State + // If the state does not exist, the cluster is being initialized. + if oldState == "" { + s.Status.State = apiv1alpha1.ClusterInitState + return clusterCondition + } + // If the expected number of replicas and the actual number + // of replicas are both 0, the cluster has been closed. + if int(*s.Spec.Replicas) == 0 && s.Status.ReadyNodes == 0 { + clusterCondition.Type = apiv1alpha1.ConditionClose + s.Status.State = apiv1alpha1.ClusterCloseState + return clusterCondition + } + // When the cluster is ready or closed, the number of replicas changes, + // indicating that the cluster is updating nodes. + if oldState == apiv1alpha1.ClusterReadyState || oldState == apiv1alpha1.ClusterCloseState { + if int(*s.Spec.Replicas) != s.Status.ReadyNodes { + clusterCondition.Type = apiv1alpha1.ConditionUpdate + s.Status.State = apiv1alpha1.ClusterUpdateState + return clusterCondition + } + } + + clusterCondition.Type = apiv1alpha1.ClusterConditionType(oldState) + return clusterCondition +} + // updateNodeStatus update the node status. func (s *StatusSyncer) updateNodeStatus(ctx context.Context, cli client.Client, pods []corev1.Pod) error { sctName := s.GetNameForResource(utils.Secret)