Skip to content

Commit 769e4ce

Browse files
authored
chore: expose more conditions in component status (#10032)
1 parent b46fa2f commit 769e4ce

14 files changed

Lines changed: 1011 additions & 141 deletions

apis/apps/v1/component_types.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,3 +460,18 @@ const (
460460
// FailedComponentPhase indicates that there are some pods of the component not in a 'Running' state.
461461
FailedComponentPhase ComponentPhase = "Failed"
462462
)
463+
464+
// component condition types
465+
const (
466+
// ComponentConditionProgressing indicates component controller is applying updates, or workload resource is being updated.
467+
ComponentConditionProgressing = "Progressing"
468+
469+
// ComponentConditionHealthy indicates its workload resource is running and ready.
470+
ComponentConditionHealthy = "Healthy"
471+
472+
// ComponentConditionAvailable indicates the component can serve requests normally.
473+
ComponentConditionAvailable = "Available"
474+
475+
// ComponentConditionProvisioningStarted indicates the operator starts resource provisioning to create or change the cluster.
476+
ComponentConditionProvisioningStarted = "ProvisioningStarted"
477+
)

apis/apps/v1/componentdefinition_types.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,8 +1273,13 @@ type ReplicasLimit struct {
12731273
}
12741274

12751275
// ComponentAvailable defines the strategies for determining whether the component is available.
1276+
//
1277+
// If both `WithPhases` and `WithRole` are specified, the component will be considered
1278+
// unavailable if any of them fail.
1279+
// If `WithProbe` is specified, `WithPhases` and `WithRole` fields are ignored.
12761280
type ComponentAvailable struct {
12771281
// Specifies the phases that the component will go through to be considered available.
1282+
// Multiple phases are separated by comma.
12781283
//
12791284
// This field is immutable once set.
12801285
//
@@ -1290,8 +1295,6 @@ type ComponentAvailable struct {
12901295

12911296
// Specifies the strategies for determining whether the component is available based on the available probe.
12921297
//
1293-
// If specified, it will take precedence over the WithPhases and WithRole fields.
1294-
//
12951298
// This field is immutable once set.
12961299
//
12971300
// +optional

config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ spec:
110110
withPhases:
111111
description: |-
112112
Specifies the phases that the component will go through to be considered available.
113+
Multiple phases are separated by comma.
113114

114115

115116
This field is immutable once set.
@@ -119,9 +120,6 @@ spec:
119120
Specifies the strategies for determining whether the component is available based on the available probe.
120121

121122

122-
If specified, it will take precedence over the WithPhases and WithRole fields.
123-
124-
125123
This field is immutable once set.
126124
properties:
127125
condition:

controllers/apps/cluster/transformer_cluster_component_status.go

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,9 @@ import (
2727
"golang.org/x/exp/maps"
2828
corev1 "k8s.io/api/core/v1"
2929
"k8s.io/apimachinery/pkg/util/sets"
30-
"sigs.k8s.io/controller-runtime/pkg/client"
3130

3231
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
3332
"github.com/apecloud/kubeblocks/pkg/constant"
34-
"github.com/apecloud/kubeblocks/pkg/controller/component"
3533
"github.com/apecloud/kubeblocks/pkg/controller/graph"
3634
)
3735

@@ -54,7 +52,7 @@ func (t *clusterComponentStatusTransformer) Transform(ctx graph.TransformContext
5452
}
5553

5654
func (t *clusterComponentStatusTransformer) transform(transCtx *clusterTransformContext) error {
57-
comps, shardingComps, err := t.listClusterComponents(transCtx)
55+
comps, shardingComps, err := listClusterComponents(transCtx.Context, transCtx.Client, transCtx.Cluster)
5856
if err != nil {
5957
return err
6058
}
@@ -65,55 +63,6 @@ func (t *clusterComponentStatusTransformer) transform(transCtx *clusterTransform
6563
return nil
6664
}
6765

68-
func (t *clusterComponentStatusTransformer) listClusterComponents(
69-
transCtx *clusterTransformContext) (map[string]*appsv1.Component, map[string][]*appsv1.Component, error) {
70-
var (
71-
cluster = transCtx.Cluster
72-
)
73-
74-
compList := &appsv1.ComponentList{}
75-
ml := client.MatchingLabels(constant.GetClusterLabels(cluster.Name))
76-
if err := transCtx.Client.List(transCtx.Context, compList, client.InNamespace(cluster.Namespace), ml); err != nil {
77-
return nil, nil, err
78-
}
79-
80-
if len(compList.Items) == 0 {
81-
return nil, nil, nil
82-
}
83-
84-
comps := make(map[string]*appsv1.Component)
85-
shardingComps := make(map[string][]*appsv1.Component)
86-
87-
sharding := func(comp *appsv1.Component) bool {
88-
shardingName := shardingCompNName(comp)
89-
if len(shardingName) == 0 {
90-
return false
91-
}
92-
93-
if _, ok := shardingComps[shardingName]; !ok {
94-
shardingComps[shardingName] = []*appsv1.Component{comp}
95-
} else {
96-
shardingComps[shardingName] = append(shardingComps[shardingName], comp)
97-
}
98-
return true
99-
}
100-
101-
for i, comp := range compList.Items {
102-
if sharding(&compList.Items[i]) {
103-
continue
104-
}
105-
compName, err := component.ShortName(cluster.Name, comp.Name)
106-
if err != nil {
107-
return nil, nil, err
108-
}
109-
if _, ok := comps[compName]; ok {
110-
return nil, nil, fmt.Errorf("duplicate component name: %s", compName)
111-
}
112-
comps[compName] = &compList.Items[i]
113-
}
114-
return comps, shardingComps, nil
115-
}
116-
11766
func (t *clusterComponentStatusTransformer) transformCompStatus(transCtx *clusterTransformContext, comps map[string]*appsv1.Component) {
11867
var (
11968
cluster = transCtx.Cluster

controllers/apps/cluster/transformer_cluster_status.go

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
2020
package cluster
2121

2222
import (
23+
"context"
24+
"fmt"
2325
"slices"
2426

2527
"golang.org/x/exp/maps"
2628
"k8s.io/apimachinery/pkg/api/meta"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"sigs.k8s.io/controller-runtime/pkg/client"
2731

2832
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
2933
"github.com/apecloud/kubeblocks/pkg/controller/graph"
@@ -41,7 +45,7 @@ func (t *clusterStatusTransformer) Transform(ctx graph.TransformContext, dag *gr
4145
graphCli, _ := transCtx.Client.(model.GraphClient)
4246

4347
defer func() { t.markClusterDagStatusAction(graphCli, dag, origCluster, cluster) }()
44-
if err := t.reconcileClusterStatus(cluster); err != nil {
48+
if err := t.reconcileClusterStatus(transCtx.Context, transCtx.Client, cluster); err != nil {
4549
return err
4650
}
4751
return nil
@@ -53,13 +57,12 @@ func (t *clusterStatusTransformer) markClusterDagStatusAction(graphCli model.Gra
5357
}
5458
}
5559

56-
func (t *clusterStatusTransformer) reconcileClusterStatus(cluster *appsv1.Cluster) error {
60+
func (t *clusterStatusTransformer) reconcileClusterStatus(ctx context.Context, cli client.Reader, cluster *appsv1.Cluster) error {
5761
if len(cluster.Status.Components) == 0 && len(cluster.Status.Shardings) == 0 {
5862
return nil
5963
}
60-
oldPhase := t.reconcileClusterPhase(cluster)
61-
t.syncClusterConditions(cluster, oldPhase)
62-
return nil
64+
t.reconcileClusterPhase(cluster)
65+
return t.syncClusterConditions(ctx, cli, cluster)
6366
}
6467

6568
func (t *clusterStatusTransformer) reconcileClusterPhase(cluster *appsv1.Cluster) appsv1.ClusterPhase {
@@ -89,29 +92,99 @@ func (t *clusterStatusTransformer) reconcileClusterPhase(cluster *appsv1.Cluster
8992
return phase
9093
}
9194

92-
func (t *clusterStatusTransformer) syncClusterConditions(cluster *appsv1.Cluster, oldPhase appsv1.ClusterPhase) {
93-
if cluster.Status.Phase == appsv1.RunningClusterPhase && oldPhase != cluster.Status.Phase {
95+
func (t *clusterStatusTransformer) syncClusterConditions(ctx context.Context, cli client.Reader, cluster *appsv1.Cluster) error {
96+
if cluster.Status.Phase == appsv1.RunningClusterPhase {
9497
meta.SetStatusCondition(&cluster.Status.Conditions, newClusterReadyCondition(cluster.Name))
95-
return
98+
} else {
99+
kindNames := map[string][]string{}
100+
for kind, statusMap := range map[string]map[string]appsv1.ClusterComponentStatus{
101+
"component": cluster.Status.Components,
102+
"sharding": t.shardingToCompStatus(cluster.Status.Shardings),
103+
} {
104+
for name, status := range statusMap {
105+
if status.Phase == appsv1.FailedComponentPhase {
106+
if _, ok := kindNames[kind]; !ok {
107+
kindNames[kind] = []string{}
108+
}
109+
kindNames[kind] = append(kindNames[kind], name)
110+
}
111+
}
112+
}
113+
if len(kindNames) > 0 {
114+
meta.SetStatusCondition(&cluster.Status.Conditions, newClusterNotReadyCondition(cluster.Name, kindNames))
115+
}
96116
}
97117

98-
kindNames := map[string][]string{}
99-
for kind, statusMap := range map[string]map[string]appsv1.ClusterComponentStatus{
100-
"component": cluster.Status.Components,
101-
"sharding": t.shardingToCompStatus(cluster.Status.Shardings),
102-
} {
103-
for name, status := range statusMap {
104-
if status.Phase == appsv1.FailedComponentPhase {
105-
if _, ok := kindNames[kind]; !ok {
106-
kindNames[kind] = []string{}
118+
setAvailableCondition := func() error {
119+
comps, shardingComps, err := listClusterComponents(ctx, cli, cluster)
120+
if err != nil {
121+
return err
122+
}
123+
available := true
124+
aggregatedMessage := ""
125+
defer func() {
126+
var condition metav1.Condition
127+
if available {
128+
condition = metav1.Condition{
129+
Type: appsv1.ConditionTypeAvailable,
130+
Status: metav1.ConditionTrue,
131+
Message: "All components are available",
132+
Reason: "Available",
133+
}
134+
} else {
135+
condition = metav1.Condition{
136+
Type: appsv1.ConditionTypeAvailable,
137+
Status: metav1.ConditionFalse,
138+
Message: aggregatedMessage,
139+
Reason: "Unavailable",
107140
}
108-
kindNames[kind] = append(kindNames[kind], name)
109141
}
142+
143+
meta.SetStatusCondition(&cluster.Status.Conditions, condition)
144+
}()
145+
146+
if len(comps) == 0 && len(shardingComps) == 0 {
147+
available = false
148+
aggregatedMessage = "no component exists; "
149+
return nil
110150
}
151+
152+
for _, comp := range comps {
153+
compCond := meta.FindStatusCondition(comp.Status.Conditions, appsv1.ConditionTypeAvailable)
154+
if compCond != nil {
155+
if compCond.Status != metav1.ConditionTrue {
156+
available = false
157+
message := fmt.Sprintf("component %s is not available", comp.Name)
158+
aggregatedMessage += message + "; "
159+
}
160+
} else {
161+
available = false
162+
message := fmt.Sprintf("component %s has no available condition", comp.Name)
163+
aggregatedMessage += message + "; "
164+
}
165+
}
166+
167+
for shardingName, comps := range shardingComps {
168+
for _, comp := range comps {
169+
compCond := meta.FindStatusCondition(comp.Status.Conditions, appsv1.ConditionTypeAvailable)
170+
if compCond != nil {
171+
if compCond.Status != metav1.ConditionTrue {
172+
available = false
173+
message := fmt.Sprintf("component %s of sharding %s is not available", comp.Name, shardingName)
174+
aggregatedMessage += message + "; "
175+
}
176+
} else {
177+
available = false
178+
message := fmt.Sprintf("component %s of sharding %s has no available condition", comp.Name, shardingName)
179+
aggregatedMessage += message + "; "
180+
}
181+
}
182+
}
183+
184+
return nil
111185
}
112-
if len(kindNames) > 0 {
113-
meta.SetStatusCondition(&cluster.Status.Conditions, newClusterNotReadyCondition(cluster.Name, kindNames))
114-
}
186+
187+
return setAvailableCondition()
115188
}
116189

117190
func (t *clusterStatusTransformer) shardingToCompStatus(shardingStatus map[string]appsv1.ClusterShardingStatus) map[string]appsv1.ClusterComponentStatus {

0 commit comments

Comments
 (0)