github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/transformer_cluster_status.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package apps 21 22 import ( 23 "fmt" 24 25 "k8s.io/apimachinery/pkg/api/meta" 26 27 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 28 "github.com/1aal/kubeblocks/pkg/controller/graph" 29 "github.com/1aal/kubeblocks/pkg/controller/model" 30 ) 31 32 type ClusterStatusTransformer struct { 33 // replicasNotReadyCompNames records the component names that are not ready. 34 notReadyCompNames map[string]struct{} 35 // replicasNotReadyCompNames records the component names which replicas are not ready. 36 replicasNotReadyCompNames map[string]struct{} 37 } 38 39 var _ graph.Transformer = &ClusterStatusTransformer{} 40 41 func (t *ClusterStatusTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error { 42 transCtx, _ := ctx.(*clusterTransformContext) 43 origCluster := transCtx.OrigCluster 44 cluster := transCtx.Cluster 45 graphCli, _ := transCtx.Client.(model.GraphClient) 46 47 updateObservedGeneration := func() { 48 cluster.Status.ObservedGeneration = cluster.Generation 49 cluster.Status.ClusterDefGeneration = transCtx.ClusterDef.Generation 50 } 51 52 switch { 53 case origCluster.IsUpdating(): 54 transCtx.Logger.Info(fmt.Sprintf("update cluster status after applying resources, generation: %d", cluster.Generation)) 55 updateObservedGeneration() 56 graphCli.Status(dag, origCluster, cluster) 57 case origCluster.IsStatusUpdating(): 58 defer func() { graphCli.Status(dag, origCluster, cluster) }() 59 // reconcile the phase and conditions of the Cluster.status 60 if err := t.reconcileClusterStatus(cluster); err != nil { 61 return err 62 } 63 case origCluster.IsDeleting(): 64 return fmt.Errorf("unexpected cluster status: %+v", origCluster) 65 default: 66 panic(fmt.Sprintf("runtime error - unknown cluster status: %+v", origCluster)) 67 } 68 69 return nil 70 } 71 72 func (t *ClusterStatusTransformer) reconcileClusterPhase(cluster *appsv1alpha1.Cluster) { 73 var ( 74 isAllComponentCreating = true 75 isAllComponentRunning = true 76 isAllComponentWorking = true 77 hasComponentStopping = false 78 isAllComponentStopped = true 79 isAllComponentFailed = true 80 ) 81 isPhaseIn := func(phase appsv1alpha1.ClusterComponentPhase, phases ...appsv1alpha1.ClusterComponentPhase) bool { 82 for _, p := range phases { 83 if p == phase { 84 return true 85 } 86 } 87 return false 88 } 89 for _, status := range cluster.Status.Components { 90 phase := status.Phase 91 if !isPhaseIn(phase, appsv1alpha1.CreatingClusterCompPhase) { 92 isAllComponentCreating = false 93 } 94 if !isPhaseIn(phase, appsv1alpha1.RunningClusterCompPhase) { 95 isAllComponentRunning = false 96 } 97 if !isPhaseIn(phase, appsv1alpha1.CreatingClusterCompPhase, 98 appsv1alpha1.RunningClusterCompPhase, 99 appsv1alpha1.UpdatingClusterCompPhase) { 100 isAllComponentWorking = false 101 } 102 if isPhaseIn(phase, appsv1alpha1.StoppingClusterCompPhase) { 103 hasComponentStopping = true 104 } 105 if !isPhaseIn(phase, appsv1alpha1.StoppedClusterCompPhase) { 106 isAllComponentStopped = false 107 } 108 if !isPhaseIn(phase, appsv1alpha1.FailedClusterCompPhase) { 109 isAllComponentFailed = false 110 } 111 } 112 113 switch { 114 case isAllComponentRunning: 115 if cluster.Status.Phase != appsv1alpha1.RunningClusterPhase { 116 t.syncClusterPhaseToRunning(cluster) 117 } 118 case isAllComponentCreating: 119 cluster.Status.Phase = appsv1alpha1.CreatingClusterPhase 120 case isAllComponentWorking: 121 cluster.Status.Phase = appsv1alpha1.UpdatingClusterPhase 122 case isAllComponentStopped: 123 if cluster.Status.Phase != appsv1alpha1.StoppedClusterPhase { 124 t.syncClusterPhaseToStopped(cluster) 125 } 126 case hasComponentStopping: 127 cluster.Status.Phase = appsv1alpha1.StoppingClusterPhase 128 case isAllComponentFailed: 129 cluster.Status.Phase = appsv1alpha1.FailedClusterPhase 130 default: 131 cluster.Status.Phase = appsv1alpha1.AbnormalClusterPhase 132 } 133 } 134 135 // reconcileClusterStatus reconciles phase and conditions of the Cluster.status. 136 func (t *ClusterStatusTransformer) reconcileClusterStatus(cluster *appsv1alpha1.Cluster) error { 137 if len(cluster.Status.Components) == 0 { 138 return nil 139 } 140 initClusterStatusParams := func() { 141 t.notReadyCompNames = map[string]struct{}{} 142 t.replicasNotReadyCompNames = map[string]struct{}{} 143 } 144 initClusterStatusParams() 145 146 // removes the invalid component of status.components which is deleted from spec.components. 147 t.removeInvalidCompStatus(cluster) 148 149 // do analysis of Cluster.Status.component and update the results to status synchronizer. 150 t.doAnalysisAndUpdateSynchronizer(cluster) 151 152 // handle the ready condition. 153 t.syncReadyConditionForCluster(cluster) 154 155 // sync the cluster phase. 156 t.reconcileClusterPhase(cluster) 157 return nil 158 } 159 160 // removeInvalidCompStatus removes the invalid component of status.components which is deleted from spec.components. 161 func (t *ClusterStatusTransformer) removeInvalidCompStatus(cluster *appsv1alpha1.Cluster) { 162 // remove the invalid component in status.components when the component is deleted from spec.components. 163 tmpCompsStatus := map[string]appsv1alpha1.ClusterComponentStatus{} 164 compsStatus := cluster.Status.Components 165 for _, v := range cluster.Spec.ComponentSpecs { 166 if compStatus, ok := compsStatus[v.Name]; ok { 167 tmpCompsStatus[v.Name] = compStatus 168 } 169 } 170 // keep valid components' status 171 cluster.Status.Components = tmpCompsStatus 172 } 173 174 // doAnalysisAndUpdateSynchronizer analyzes the Cluster.Status.Components and updates the results to the synchronizer. 175 func (t *ClusterStatusTransformer) doAnalysisAndUpdateSynchronizer(cluster *appsv1alpha1.Cluster) { 176 // analysis the status of components and calculate the cluster phase. 177 for k, v := range cluster.Status.Components { 178 if v.PodsReady == nil || !*v.PodsReady { 179 t.replicasNotReadyCompNames[k] = struct{}{} 180 t.notReadyCompNames[k] = struct{}{} 181 } 182 switch v.Phase { 183 case appsv1alpha1.AbnormalClusterCompPhase, appsv1alpha1.FailedClusterCompPhase: 184 t.notReadyCompNames[k] = struct{}{} 185 } 186 } 187 } 188 189 // syncReadyConditionForCluster syncs the cluster conditions with ClusterReady and ReplicasReady type. 190 func (t *ClusterStatusTransformer) syncReadyConditionForCluster(cluster *appsv1alpha1.Cluster) { 191 if len(t.replicasNotReadyCompNames) == 0 { 192 // if all replicas of cluster are ready, set ReasonAllReplicasReady to status.conditions 193 readyCondition := newAllReplicasPodsReadyConditions() 194 meta.SetStatusCondition(&cluster.Status.Conditions, readyCondition) 195 } else { 196 meta.SetStatusCondition(&cluster.Status.Conditions, newReplicasNotReadyCondition(t.replicasNotReadyCompNames)) 197 } 198 199 if len(t.notReadyCompNames) > 0 { 200 meta.SetStatusCondition(&cluster.Status.Conditions, newComponentsNotReadyCondition(t.notReadyCompNames)) 201 } 202 } 203 204 // syncClusterPhaseToRunning syncs the cluster phase to Running. 205 func (t *ClusterStatusTransformer) syncClusterPhaseToRunning(cluster *appsv1alpha1.Cluster) { 206 cluster.Status.Phase = appsv1alpha1.RunningClusterPhase 207 meta.SetStatusCondition(&cluster.Status.Conditions, newClusterReadyCondition(cluster.Name)) 208 } 209 210 // syncClusterPhaseToStopped syncs the cluster phase to Stopped. 211 func (t *ClusterStatusTransformer) syncClusterPhaseToStopped(cluster *appsv1alpha1.Cluster) { 212 cluster.Status.Phase = appsv1alpha1.StoppedClusterPhase 213 }