github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/transformer_component.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/util/sets"
    26  	"sigs.k8s.io/controller-runtime/pkg/client"
    27  
    28  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    29  	"github.com/1aal/kubeblocks/controllers/apps/components"
    30  	"github.com/1aal/kubeblocks/pkg/controller/builder"
    31  	"github.com/1aal/kubeblocks/pkg/controller/graph"
    32  	"github.com/1aal/kubeblocks/pkg/controller/model"
    33  	ictrlutil "github.com/1aal/kubeblocks/pkg/controllerutil"
    34  )
    35  
    36  // ComponentTransformer transforms all components to a K8s objects DAG
    37  type ComponentTransformer struct {
    38  	client.Client
    39  }
    40  
    41  var _ graph.Transformer = &ComponentTransformer{}
    42  
    43  func (c *ComponentTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error {
    44  	transCtx, _ := ctx.(*clusterTransformContext)
    45  	cluster := transCtx.Cluster
    46  
    47  	clusterDef := transCtx.ClusterDef
    48  	clusterVer := transCtx.ClusterVer
    49  	reqCtx := ictrlutil.RequestCtx{
    50  		Ctx:      transCtx.Context,
    51  		Log:      transCtx.Logger,
    52  		Recorder: transCtx.EventRecorder,
    53  	}
    54  
    55  	var err error
    56  	dags4Component := make([]*graph.DAG, 0)
    57  	if cluster.IsStatusUpdating() {
    58  		// status existed components
    59  		err = c.transform4StatusUpdate(reqCtx, clusterDef, clusterVer, cluster, &dags4Component)
    60  	} else {
    61  		// create new components or update existed components
    62  		err = c.transform4SpecUpdate(reqCtx, clusterDef, clusterVer, cluster, &dags4Component)
    63  	}
    64  	if err != nil && !ictrlutil.IsDelayedRequeueError(err) {
    65  		return err
    66  	}
    67  
    68  	for _, subDag := range dags4Component {
    69  		for _, v := range subDag.Vertices() {
    70  			node, ok := v.(*model.ObjectVertex)
    71  			if !ok {
    72  				panic("runtime error, unexpected lifecycle vertex type")
    73  			}
    74  			if node.Obj == nil {
    75  				panic("runtime error, nil vertex object")
    76  			}
    77  		}
    78  		dag.Merge(subDag)
    79  	}
    80  	return err
    81  }
    82  
    83  func (c *ComponentTransformer) transform4SpecUpdate(reqCtx ictrlutil.RequestCtx, clusterDef *appsv1alpha1.ClusterDefinition,
    84  	clusterVer *appsv1alpha1.ClusterVersion, cluster *appsv1alpha1.Cluster, dags *[]*graph.DAG) error {
    85  	compSpecMap := make(map[string]*appsv1alpha1.ClusterComponentSpec)
    86  	compDefMap := make(map[string]*appsv1alpha1.ClusterComponentDefinition)
    87  	for _, spec := range cluster.Spec.ComponentSpecs {
    88  		compSpecMap[spec.Name] = &spec
    89  	}
    90  	for _, compDef := range clusterDef.Spec.ComponentDefs {
    91  		compDefMap[compDef.Name] = &compDef
    92  	}
    93  	compProto := sets.KeySet(compSpecMap)
    94  	// if component spec is empty, generate the component spec from cluster template and cluster
    95  	if cluster.Spec.ComponentSpecs == nil {
    96  		compProto = sets.KeySet(compDefMap)
    97  	}
    98  	// TODO(refactor): should review that whether it is reasonable to use component status
    99  	compStatus := sets.KeySet(cluster.Status.Components)
   100  
   101  	createSet := compProto.Difference(compStatus)
   102  	updateSet := compProto.Intersection(compStatus)
   103  	deleteSet := compStatus.Difference(compProto)
   104  
   105  	for compName := range createSet {
   106  		dag := newDAGWithPlaceholder(cluster.Namespace, cluster.Name, compName)
   107  		comp, err := components.NewComponent(reqCtx, c.Client, clusterDef, clusterVer, cluster, compName, dag)
   108  		if err != nil {
   109  			return err
   110  		}
   111  		if comp == nil {
   112  			continue
   113  		}
   114  		if err := comp.Create(reqCtx, c.Client); err != nil {
   115  			return err
   116  		}
   117  		*dags = append(*dags, dag)
   118  	}
   119  
   120  	for compName := range deleteSet {
   121  		dag := newDAGWithPlaceholder(cluster.Namespace, cluster.Name, compName)
   122  		comp, err := components.NewComponent(reqCtx, c.Client, clusterDef, clusterVer, cluster, compName, dag)
   123  		if err != nil {
   124  			return err
   125  		}
   126  		if comp == nil {
   127  			continue
   128  		}
   129  		if err := comp.Delete(reqCtx, c.Client); err != nil {
   130  			return err
   131  		}
   132  		*dags = append(*dags, dag)
   133  	}
   134  
   135  	for compName := range updateSet {
   136  		dag := newDAGWithPlaceholder(cluster.Namespace, cluster.Name, compName)
   137  		comp, err := components.NewComponent(reqCtx, c.Client, clusterDef, clusterVer, cluster, compName, dag)
   138  		if err != nil {
   139  			return err
   140  		}
   141  		if err := comp.Update(reqCtx, c.Client); err != nil {
   142  			return err
   143  		}
   144  		*dags = append(*dags, dag)
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  func (c *ComponentTransformer) transform4StatusUpdate(reqCtx ictrlutil.RequestCtx, clusterDef *appsv1alpha1.ClusterDefinition,
   151  	clusterVer *appsv1alpha1.ClusterVersion, cluster *appsv1alpha1.Cluster, dags *[]*graph.DAG) error {
   152  	var delayedError error
   153  	for _, compSpec := range cluster.Spec.ComponentSpecs {
   154  		dag := newDAGWithPlaceholder(cluster.Namespace, cluster.Name, compSpec.Name)
   155  		comp, err := components.NewComponent(reqCtx, c.Client, clusterDef, clusterVer, cluster, compSpec.Name, dag)
   156  		if err != nil {
   157  			return err
   158  		}
   159  		if err := comp.Status(reqCtx, c.Client); err != nil {
   160  			if !ictrlutil.IsDelayedRequeueError(err) {
   161  				return err
   162  			}
   163  			if delayedError == nil {
   164  				delayedError = err
   165  			}
   166  		}
   167  		*dags = append(*dags, dag)
   168  	}
   169  	return delayedError
   170  }
   171  
   172  func newDAGWithPlaceholder(namespace, clusterName, compName string) *graph.DAG {
   173  	root := builder.NewReplicatedStateMachineBuilder(namespace, fmt.Sprintf("%s-%s", clusterName, compName)).GetObject()
   174  	dag := graph.NewDAG()
   175  	model.NewGraphClient(nil).Root(dag, nil, root, nil)
   176  	return dag
   177  }