Skip to content
Merged
9 changes: 9 additions & 0 deletions apis/apps/v1/component_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,3 +460,12 @@ const (
// FailedComponentPhase indicates that there are some pods of the component not in a 'Running' state.
FailedComponentPhase ComponentPhase = "Failed"
)

// component conditions

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend a set of condition definitions:

const (
    // ConditionTypeProvisioned indicates whether the required resources have been provisioned
    ConditionTypeProvisioned = "Provisioned"

    // ConditionTypeReady indicates whether the Component is ready to provide service
    ConditionTypeReady = "Ready"

    // ConditionTypeHealthy indicates whether the Component is healthy
    ConditionTypeHealthy = "Healthy"

    // ConditionTypeAvailable indicates whether the Component is available (based on availability policy)
    ConditionTypeAvailable = "Available"

    // ConditionTypeProgressing indicates whether the Component is undergoing changes
    ConditionTypeProgressing = "Progressing" // or Reconciling

    // ConditionTypeTerminating indicates whether the Component is being terminated
    ConditionTypeTerminating = "Terminating"
)

const (
// ConditionTypeHealthy indicates component controller is applying updates
ConditionTypeProgressing = "Progressing"

// ConditionTypeHealthy indicates its workload resource is updated, running and ready.
ConditionTypeHealthy = "Healthy"
)
7 changes: 5 additions & 2 deletions apis/apps/v1/componentdefinition_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1275,8 +1275,13 @@ type ReplicasLimit struct {
}

// ComponentAvailable defines the strategies for determining whether the component is available.
//
// If both `WithPhases` and `WithRole` are specified, the component will be considered
// unavailable if any of them fail.
// If `WithProbe` is specified, `WithPhases` and `WithRole` fields are ignored.
type ComponentAvailable struct {
// Specifies the phases that the component will go through to be considered available.
// Multiple phases are separated by comma.
//
// This field is immutable once set.
//
Expand All @@ -1292,8 +1297,6 @@ type ComponentAvailable struct {

// Specifies the strategies for determining whether the component is available based on the available probe.
//
// If specified, it will take precedence over the WithPhases and WithRole fields.
//
// This field is immutable once set.
//
// +optional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ spec:
withPhases:
description: |-
Specifies the phases that the component will go through to be considered available.
Multiple phases are separated by comma.


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


If specified, it will take precedence over the WithPhases and WithRole fields.


This field is immutable once set.
properties:
condition:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ import (
"golang.org/x/exp/maps"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/controller-runtime/pkg/client"

appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
"github.com/apecloud/kubeblocks/pkg/constant"
"github.com/apecloud/kubeblocks/pkg/controller/component"
"github.com/apecloud/kubeblocks/pkg/controller/graph"
)

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

func (t *clusterComponentStatusTransformer) transform(transCtx *clusterTransformContext) error {
comps, shardingComps, err := t.listClusterComponents(transCtx)
comps, shardingComps, err := listClusterComponents(transCtx.Context, transCtx.Client, transCtx.Cluster)
if err != nil {
return err
}
Expand All @@ -65,55 +63,6 @@ func (t *clusterComponentStatusTransformer) transform(transCtx *clusterTransform
return nil
}

func (t *clusterComponentStatusTransformer) listClusterComponents(
transCtx *clusterTransformContext) (map[string]*appsv1.Component, map[string][]*appsv1.Component, error) {
var (
cluster = transCtx.Cluster
)

compList := &appsv1.ComponentList{}
ml := client.MatchingLabels(constant.GetClusterLabels(cluster.Name))
if err := transCtx.Client.List(transCtx.Context, compList, client.InNamespace(cluster.Namespace), ml); err != nil {
return nil, nil, err
}

if len(compList.Items) == 0 {
return nil, nil, nil
}

comps := make(map[string]*appsv1.Component)
shardingComps := make(map[string][]*appsv1.Component)

sharding := func(comp *appsv1.Component) bool {
shardingName := shardingCompNName(comp)
if len(shardingName) == 0 {
return false
}

if _, ok := shardingComps[shardingName]; !ok {
shardingComps[shardingName] = []*appsv1.Component{comp}
} else {
shardingComps[shardingName] = append(shardingComps[shardingName], comp)
}
return true
}

for i, comp := range compList.Items {
if sharding(&compList.Items[i]) {
continue
}
compName, err := component.ShortName(cluster.Name, comp.Name)
if err != nil {
return nil, nil, err
}
if _, ok := comps[compName]; ok {
return nil, nil, fmt.Errorf("duplicate component name: %s", compName)
}
comps[compName] = &compList.Items[i]
}
return comps, shardingComps, nil
}

func (t *clusterComponentStatusTransformer) transformCompStatus(transCtx *clusterTransformContext, comps map[string]*appsv1.Component) {
var (
cluster = transCtx.Cluster
Expand Down
97 changes: 78 additions & 19 deletions controllers/apps/cluster/transformer_cluster_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package cluster

import (
"context"
"fmt"
"slices"

"golang.org/x/exp/maps"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

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

defer func() { t.markClusterDagStatusAction(graphCli, dag, origCluster, cluster) }()
if err := t.reconcileClusterStatus(cluster); err != nil {
if err := t.reconcileClusterStatus(transCtx.Context, transCtx.Client, cluster); err != nil {
return err
}
return nil
Expand All @@ -53,13 +57,12 @@ func (t *clusterStatusTransformer) markClusterDagStatusAction(graphCli model.Gra
}
}

func (t *clusterStatusTransformer) reconcileClusterStatus(cluster *appsv1.Cluster) error {
func (t *clusterStatusTransformer) reconcileClusterStatus(ctx context.Context, cli client.Reader, cluster *appsv1.Cluster) error {
if len(cluster.Status.Components) == 0 && len(cluster.Status.Shardings) == 0 {
return nil
}
oldPhase := t.reconcileClusterPhase(cluster)
t.syncClusterConditions(cluster, oldPhase)
return nil
return t.syncClusterConditions(ctx, cli, cluster, oldPhase)
}

func (t *clusterStatusTransformer) reconcileClusterPhase(cluster *appsv1.Cluster) appsv1.ClusterPhase {
Expand Down Expand Up @@ -89,29 +92,85 @@ func (t *clusterStatusTransformer) reconcileClusterPhase(cluster *appsv1.Cluster
return phase
}

func (t *clusterStatusTransformer) syncClusterConditions(cluster *appsv1.Cluster, oldPhase appsv1.ClusterPhase) {
func (t *clusterStatusTransformer) syncClusterConditions(ctx context.Context, cli client.Reader, cluster *appsv1.Cluster, oldPhase appsv1.ClusterPhase) error {
if cluster.Status.Phase == appsv1.RunningClusterPhase && oldPhase != cluster.Status.Phase {
meta.SetStatusCondition(&cluster.Status.Conditions, newClusterReadyCondition(cluster.Name))
return
} else {
kindNames := map[string][]string{}
for kind, statusMap := range map[string]map[string]appsv1.ClusterComponentStatus{
"component": cluster.Status.Components,
"sharding": t.shardingToCompStatus(cluster.Status.Shardings),
} {
for name, status := range statusMap {
if status.Phase == appsv1.FailedComponentPhase {
if _, ok := kindNames[kind]; !ok {
kindNames[kind] = []string{}
}
kindNames[kind] = append(kindNames[kind], name)
}
}
}
if len(kindNames) > 0 {
meta.SetStatusCondition(&cluster.Status.Conditions, newClusterNotReadyCondition(cluster.Name, kindNames))
}
}

kindNames := map[string][]string{}
for kind, statusMap := range map[string]map[string]appsv1.ClusterComponentStatus{
"component": cluster.Status.Components,
"sharding": t.shardingToCompStatus(cluster.Status.Shardings),
} {
for name, status := range statusMap {
if status.Phase == appsv1.FailedComponentPhase {
if _, ok := kindNames[kind]; !ok {
kindNames[kind] = []string{}
setAvailableCondition := func() error {
comps, shardingComps, err := listClusterComponents(ctx, cli, cluster)
if err != nil {
return err
}
available := true
message := ""
defer func() {
var condition metav1.Condition
if available {
condition = metav1.Condition{
Type: appsv1.ConditionTypeAvailable,
Status: metav1.ConditionTrue,
Message: "All components are available",
Reason: "Available",
}
} else {
condition = metav1.Condition{
Type: appsv1.ConditionTypeAvailable,
Status: metav1.ConditionFalse,
Message: message,
Reason: "Unavailable",
}
}

meta.SetStatusCondition(&cluster.Status.Conditions, condition)
}()

for _, comp := range comps {
compCond := meta.FindStatusCondition(comp.Status.Conditions, appsv1.ConditionTypeAvailable)
if compCond != nil {
if compCond.Status != metav1.ConditionTrue {
available = false
message = fmt.Sprintf("component %s is not available", comp.Name)
return nil
Comment thread
cjc7373 marked this conversation as resolved.
Outdated
}
kindNames[kind] = append(kindNames[kind], name)
}
}

for shardingName, comps := range shardingComps {
for _, comp := range comps {
compCond := meta.FindStatusCondition(comp.Status.Conditions, appsv1.ConditionTypeAvailable)
if compCond != nil {
if compCond.Status != metav1.ConditionTrue {
available = false
message = fmt.Sprintf("component %s of sharding %s is not available", comp.Name, shardingName)
return nil
Comment thread
cjc7373 marked this conversation as resolved.
Outdated
}
}
}
}

return nil
}
if len(kindNames) > 0 {
meta.SetStatusCondition(&cluster.Status.Conditions, newClusterNotReadyCondition(cluster.Name, kindNames))
}

return setAvailableCondition()
}

func (t *clusterStatusTransformer) shardingToCompStatus(shardingStatus map[string]appsv1.ClusterShardingStatus) map[string]appsv1.ClusterComponentStatus {
Expand Down
46 changes: 46 additions & 0 deletions controllers/apps/cluster/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package cluster

import (
"context"
"fmt"
"reflect"

"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -31,6 +32,7 @@ import (
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
"github.com/apecloud/kubeblocks/pkg/constant"
"github.com/apecloud/kubeblocks/pkg/controller/component"
"github.com/apecloud/kubeblocks/pkg/controller/model"
dptypes "github.com/apecloud/kubeblocks/pkg/dataprotection/types"
)
Expand Down Expand Up @@ -139,3 +141,47 @@ func isOwnedByComp(obj client.Object) bool {
}
return false
}

func listClusterComponents(ctx context.Context, cli client.Reader, cluster *appsv1.Cluster) (map[string]*appsv1.Component, map[string][]*appsv1.Component, error) {
compList := &appsv1.ComponentList{}
ml := client.MatchingLabels(constant.GetClusterLabels(cluster.Name))
if err := cli.List(ctx, compList, client.InNamespace(cluster.Namespace), ml); err != nil {
return nil, nil, err
}

if len(compList.Items) == 0 {
return nil, nil, nil
}

comps := make(map[string]*appsv1.Component)
shardingComps := make(map[string][]*appsv1.Component)

sharding := func(comp *appsv1.Component) bool {
shardingName := shardingCompNName(comp)
if len(shardingName) == 0 {
return false
}

if _, ok := shardingComps[shardingName]; !ok {
shardingComps[shardingName] = []*appsv1.Component{comp}
} else {
shardingComps[shardingName] = append(shardingComps[shardingName], comp)
}
return true
}

for i, comp := range compList.Items {
if sharding(&compList.Items[i]) {
continue
}
compName, err := component.ShortName(cluster.Name, comp.Name)
if err != nil {
return nil, nil, err
}
if _, ok := comps[compName]; ok {
return nil, nil, fmt.Errorf("duplicate component name: %s", compName)
}
comps[compName] = &compList.Items[i]
}
return comps, shardingComps, nil
}
Loading