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 }