github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/rsm/plan_builder_test.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 "context" 24 25 . "github.com/onsi/ginkgo/v2" 26 . "github.com/onsi/gomega" 27 28 "github.com/golang/mock/gomock" 29 apps "k8s.io/api/apps/v1" 30 corev1 "k8s.io/api/core/v1" 31 "k8s.io/apimachinery/pkg/api/resource" 32 ctrl "sigs.k8s.io/controller-runtime" 33 "sigs.k8s.io/controller-runtime/pkg/client" 34 35 workloads "github.com/1aal/kubeblocks/apis/workloads/v1alpha1" 36 "github.com/1aal/kubeblocks/pkg/controller/builder" 37 "github.com/1aal/kubeblocks/pkg/controller/model" 38 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 39 mockclient "github.com/1aal/kubeblocks/pkg/testutil/k8s/mocks" 40 ) 41 42 var _ = Describe("plan builder test", func() { 43 Context("rsmWalkFunc function", func() { 44 var rsmBuilder *PlanBuilder 45 46 BeforeEach(func() { 47 cli := k8sMock 48 reqCtx := intctrlutil.RequestCtx{ 49 Ctx: ctx, 50 Log: logger, 51 } 52 req := ctrl.Request{} 53 planBuilder := NewRSMPlanBuilder(reqCtx, cli, req) 54 rsmBuilder, _ = planBuilder.(*PlanBuilder) 55 56 rsm = builder.NewReplicatedStateMachineBuilder(namespace, name). 57 AddFinalizers([]string{getFinalizer(&workloads.ReplicatedStateMachine{})}). 58 GetObject() 59 }) 60 61 It("should create object", func() { 62 v := &model.ObjectVertex{ 63 Obj: rsm, 64 Action: model.ActionCreatePtr(), 65 } 66 k8sMock.EXPECT(). 67 Create(gomock.Any(), gomock.Any(), gomock.Any()). 68 DoAndReturn(func(_ context.Context, obj *workloads.ReplicatedStateMachine, _ ...client.CreateOption) error { 69 Expect(obj).ShouldNot(BeNil()) 70 Expect(obj.Namespace).Should(Equal(rsm.Namespace)) 71 Expect(obj.Name).Should(Equal(rsm.Name)) 72 Expect(obj.Finalizers).Should(Equal(rsm.Finalizers)) 73 return nil 74 }).Times(1) 75 Expect(rsmBuilder.rsmWalkFunc(v)).Should(Succeed()) 76 }) 77 78 It("should update sts object", func() { 79 stsOrig := builder.NewStatefulSetBuilder(namespace, name).SetReplicas(3).GetObject() 80 sts := stsOrig.DeepCopy() 81 replicas := int32(5) 82 sts.Spec.Replicas = &replicas 83 v := &model.ObjectVertex{ 84 OriObj: stsOrig, 85 Obj: sts, 86 Action: model.ActionUpdatePtr(), 87 } 88 k8sMock.EXPECT(). 89 Update(gomock.Any(), gomock.Any(), gomock.Any()). 90 DoAndReturn(func(_ context.Context, obj *apps.StatefulSet, _ ...client.UpdateOption) error { 91 Expect(obj).ShouldNot(BeNil()) 92 Expect(obj.Namespace).Should(Equal(sts.Namespace)) 93 Expect(obj.Name).Should(Equal(sts.Name)) 94 Expect(obj.Spec.Replicas).Should(Equal(sts.Spec.Replicas)) 95 Expect(obj.Spec.Template).Should(Equal(sts.Spec.Template)) 96 Expect(obj.Spec.UpdateStrategy).Should(Equal(sts.Spec.UpdateStrategy)) 97 return nil 98 }).Times(1) 99 Expect(rsmBuilder.rsmWalkFunc(v)).Should(Succeed()) 100 }) 101 102 It("should update svc object", func() { 103 svcOrig := builder.NewServiceBuilder(namespace, name).SetType(corev1.ServiceTypeLoadBalancer).GetObject() 104 svc := svcOrig.DeepCopy() 105 svc.Spec.Selector = map[string]string{"foo": "bar"} 106 v := &model.ObjectVertex{ 107 OriObj: svcOrig, 108 Obj: svc, 109 Action: model.ActionUpdatePtr(), 110 } 111 k8sMock.EXPECT(). 112 Update(gomock.Any(), gomock.Any(), gomock.Any()). 113 DoAndReturn(func(_ context.Context, obj *corev1.Service, _ ...client.UpdateOption) error { 114 Expect(obj).ShouldNot(BeNil()) 115 Expect(obj.Namespace).Should(Equal(svc.Namespace)) 116 Expect(obj.Name).Should(Equal(svc.Name)) 117 Expect(obj.Spec).Should(Equal(svc.Spec)) 118 return nil 119 }).Times(1) 120 Expect(rsmBuilder.rsmWalkFunc(v)).Should(Succeed()) 121 }) 122 123 It("should update pvc object", func() { 124 pvcOrig := builder.NewPVCBuilder(namespace, name).GetObject() 125 pvc := pvcOrig.DeepCopy() 126 pvc.Spec.Resources = corev1.ResourceRequirements{ 127 Requests: map[corev1.ResourceName]resource.Quantity{ 128 corev1.ResourceStorage: resource.MustParse("500m"), 129 }, 130 } 131 v := &model.ObjectVertex{ 132 OriObj: pvcOrig, 133 Obj: pvc, 134 Action: model.ActionUpdatePtr(), 135 } 136 k8sMock.EXPECT(). 137 Update(gomock.Any(), gomock.Any(), gomock.Any()). 138 DoAndReturn(func(_ context.Context, obj *corev1.PersistentVolumeClaim, _ ...client.UpdateOption) error { 139 Expect(obj).ShouldNot(BeNil()) 140 Expect(obj.Namespace).Should(Equal(pvc.Namespace)) 141 Expect(obj.Name).Should(Equal(pvc.Name)) 142 Expect(obj.Spec.Resources).Should(Equal(pvc.Spec.Resources)) 143 return nil 144 }).Times(1) 145 Expect(rsmBuilder.rsmWalkFunc(v)).Should(Succeed()) 146 }) 147 148 It("should delete object", func() { 149 v := &model.ObjectVertex{ 150 Obj: rsm, 151 Action: model.ActionDeletePtr(), 152 } 153 k8sMock.EXPECT(). 154 Update(gomock.Any(), gomock.Any(), gomock.Any()). 155 DoAndReturn(func(_ context.Context, obj *workloads.ReplicatedStateMachine, _ ...client.UpdateOption) error { 156 Expect(obj).ShouldNot(BeNil()) 157 Expect(obj.Finalizers).Should(HaveLen(0)) 158 return nil 159 }).Times(1) 160 k8sMock.EXPECT(). 161 Delete(gomock.Any(), gomock.Any(), gomock.Any()). 162 DoAndReturn(func(_ context.Context, obj *workloads.ReplicatedStateMachine, _ ...client.DeleteOption) error { 163 Expect(obj).ShouldNot(BeNil()) 164 Expect(obj.Namespace).Should(Equal(rsm.Namespace)) 165 Expect(obj.Name).Should(Equal(rsm.Name)) 166 Expect(obj.Finalizers).Should(HaveLen(0)) 167 return nil 168 }).Times(1) 169 Expect(rsmBuilder.rsmWalkFunc(v)).Should(Succeed()) 170 }) 171 172 It("should update object status", func() { 173 rsm.Generation = 2 174 rsm.Status.ObservedGeneration = 2 175 rsmOrig := rsm.DeepCopy() 176 rsmOrig.Status.ObservedGeneration = 1 177 178 v := &model.ObjectVertex{ 179 Obj: rsm, 180 OriObj: rsmOrig, 181 Action: model.ActionStatusPtr(), 182 } 183 ct := gomock.NewController(GinkgoT()) 184 statusWriter := mockclient.NewMockStatusWriter(ct) 185 186 gomock.InOrder( 187 k8sMock.EXPECT().Status().Return(statusWriter), 188 statusWriter.EXPECT(). 189 Update(gomock.Any(), gomock.Any(), gomock.Any()). 190 DoAndReturn(func(_ context.Context, obj *workloads.ReplicatedStateMachine, _ ...client.UpdateOption) error { 191 Expect(obj).ShouldNot(BeNil()) 192 Expect(obj.Namespace).Should(Equal(rsm.Namespace)) 193 Expect(obj.Name).Should(Equal(rsm.Name)) 194 Expect(obj.Status.ObservedGeneration).Should(Equal(rsm.Status.ObservedGeneration)) 195 return nil 196 }).Times(1), 197 ) 198 Expect(rsmBuilder.rsmWalkFunc(v)).Should(Succeed()) 199 }) 200 201 It("should return error if no action set", func() { 202 v := &model.ObjectVertex{} 203 err := rsmBuilder.rsmWalkFunc(v) 204 Expect(err).ShouldNot(BeNil()) 205 Expect(err.Error()).Should(ContainSubstring("vertex action can't be nil")) 206 }) 207 208 It("should return nil and do nothing if action is Noop", func() { 209 v := &model.ObjectVertex{ 210 Action: model.ActionNoopPtr(), 211 } 212 Expect(rsmBuilder.rsmWalkFunc(v)).Should(Succeed()) 213 }) 214 }) 215 })