github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/rsm/plan_builder.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 rsm
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  
    26  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    27  	ctrl "sigs.k8s.io/controller-runtime"
    28  	"sigs.k8s.io/controller-runtime/pkg/client"
    29  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    30  
    31  	workloads "github.com/1aal/kubeblocks/apis/workloads/v1alpha1"
    32  	"github.com/1aal/kubeblocks/pkg/controller/graph"
    33  	"github.com/1aal/kubeblocks/pkg/controller/model"
    34  	intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil"
    35  )
    36  
    37  type PlanBuilder struct {
    38  	req          ctrl.Request
    39  	cli          client.Client
    40  	transCtx     *rsmTransformContext
    41  	transformers graph.TransformerChain
    42  }
    43  
    44  var _ graph.PlanBuilder = &PlanBuilder{}
    45  
    46  type Plan struct {
    47  	dag      *graph.DAG
    48  	walkFunc graph.WalkFunc
    49  	cli      client.Client
    50  	transCtx *rsmTransformContext
    51  }
    52  
    53  var _ graph.Plan = &Plan{}
    54  
    55  func init() {
    56  	model.AddScheme(workloads.AddToScheme)
    57  }
    58  
    59  // PlanBuilder implementation
    60  
    61  func (b *PlanBuilder) Init() error {
    62  	rsm := &workloads.ReplicatedStateMachine{}
    63  	if err := b.cli.Get(b.transCtx.Context, b.req.NamespacedName, rsm); err != nil {
    64  		return err
    65  	}
    66  	b.AddTransformer(&initTransformer{ReplicatedStateMachine: rsm})
    67  	return nil
    68  }
    69  
    70  func (b *PlanBuilder) AddTransformer(transformer ...graph.Transformer) graph.PlanBuilder {
    71  	b.transformers = append(b.transformers, transformer...)
    72  	return b
    73  }
    74  
    75  func (b *PlanBuilder) AddParallelTransformer(transformer ...graph.Transformer) graph.PlanBuilder {
    76  	b.transformers = append(b.transformers, &model.ParallelTransformer{Transformers: transformer})
    77  	return b
    78  }
    79  
    80  func (b *PlanBuilder) Build() (graph.Plan, error) {
    81  	var err error
    82  	// new a DAG and apply chain on it, after that we should get the final Plan
    83  	dag := graph.NewDAG()
    84  	err = b.transformers.ApplyTo(b.transCtx, dag)
    85  	// log for debug
    86  	b.transCtx.Logger.Info(fmt.Sprintf("DAG: %s", dag))
    87  
    88  	// we got the execution plan
    89  	plan := &Plan{
    90  		dag:      dag,
    91  		walkFunc: b.rsmWalkFunc,
    92  		cli:      b.cli,
    93  		transCtx: b.transCtx,
    94  	}
    95  	return plan, err
    96  }
    97  
    98  // Plan implementation
    99  
   100  func (p *Plan) Execute() error {
   101  	return p.dag.WalkReverseTopoOrder(p.walkFunc, nil)
   102  }
   103  
   104  // Do the real works
   105  
   106  func (b *PlanBuilder) rsmWalkFunc(v graph.Vertex) error {
   107  	vertex, ok := v.(*model.ObjectVertex)
   108  	if !ok {
   109  		return fmt.Errorf("wrong vertex type %v", v)
   110  	}
   111  	if vertex.Action == nil {
   112  		return errors.New("vertex action can't be nil")
   113  	}
   114  	switch *vertex.Action {
   115  	case model.CREATE:
   116  		err := b.cli.Create(b.transCtx.Context, vertex.Obj)
   117  		if err != nil && !apierrors.IsAlreadyExists(err) {
   118  			return err
   119  		}
   120  	case model.UPDATE:
   121  		err := b.cli.Update(b.transCtx.Context, vertex.Obj)
   122  		if err != nil && !apierrors.IsNotFound(err) {
   123  			return err
   124  		}
   125  	case model.DELETE:
   126  		finalizer := getFinalizer(vertex.Obj)
   127  		if controllerutil.RemoveFinalizer(vertex.Obj, finalizer) {
   128  			err := b.cli.Update(b.transCtx.Context, vertex.Obj)
   129  			if err != nil && !apierrors.IsNotFound(err) {
   130  				b.transCtx.Logger.Error(err, fmt.Sprintf("delete %T error: %s", vertex.Obj, vertex.Obj.GetName()))
   131  				return err
   132  			}
   133  		}
   134  		if !model.IsObjectDeleting(vertex.Obj) {
   135  			err := b.cli.Delete(b.transCtx.Context, vertex.Obj)
   136  			if err != nil && !apierrors.IsNotFound(err) {
   137  				return err
   138  			}
   139  		}
   140  	case model.STATUS:
   141  		if err := b.cli.Status().Update(b.transCtx.Context, vertex.Obj); err != nil {
   142  			return err
   143  		}
   144  	}
   145  	return nil
   146  }
   147  
   148  // NewRSMPlanBuilder returns a RSMPlanBuilder powered PlanBuilder
   149  func NewRSMPlanBuilder(ctx intctrlutil.RequestCtx, cli client.Client, req ctrl.Request) graph.PlanBuilder {
   150  	return &PlanBuilder{
   151  		req: req,
   152  		cli: cli,
   153  		transCtx: &rsmTransformContext{
   154  			Context:       ctx.Ctx,
   155  			Client:        model.NewGraphClient(cli),
   156  			EventRecorder: ctx.Recorder,
   157  			Logger:        ctx.Log,
   158  		},
   159  	}
   160  }