github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/migrator/peer/fabric/v2/peer_test.go (about) 1 /* 2 * Copyright contributors to the Hyperledger Fabric Operator project 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package v2_test 20 21 import ( 22 "context" 23 "strings" 24 25 . "github.com/onsi/ginkgo/v2" 26 . "github.com/onsi/gomega" 27 "github.com/pkg/errors" 28 29 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 30 controllermocks "github.com/IBM-Blockchain/fabric-operator/controllers/mocks" 31 config "github.com/IBM-Blockchain/fabric-operator/operatorconfig" 32 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/common" 33 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/deployer" 34 v2peer "github.com/IBM-Blockchain/fabric-operator/pkg/apis/peer/v2" 35 v2config "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/peer/config/v2" 36 v2 "github.com/IBM-Blockchain/fabric-operator/pkg/migrator/peer/fabric/v2" 37 "github.com/IBM-Blockchain/fabric-operator/pkg/migrator/peer/fabric/v2/mocks" 38 39 appsv1 "k8s.io/api/apps/v1" 40 batchv1 "k8s.io/api/batch/v1" 41 corev1 "k8s.io/api/core/v1" 42 v1 "k8s.io/api/core/v1" 43 "k8s.io/apimachinery/pkg/api/resource" 44 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 45 "k8s.io/apimachinery/pkg/runtime" 46 "k8s.io/apimachinery/pkg/types" 47 48 k8sclient "sigs.k8s.io/controller-runtime/pkg/client" 49 "sigs.k8s.io/yaml" 50 ) 51 52 var _ = Describe("V2 peer migrator", func() { 53 var ( 54 deploymentManager *mocks.DeploymentManager 55 configMapManager *mocks.ConfigMapManager 56 client *controllermocks.Client 57 migrator *v2.Migrate 58 instance *current.IBPPeer 59 ) 60 const FABRIC_V2 = "2.2.5-1" 61 BeforeEach(func() { 62 deploymentManager = &mocks.DeploymentManager{} 63 configMapManager = &mocks.ConfigMapManager{} 64 client = &controllermocks.Client{} 65 66 instance = ¤t.IBPPeer{ 67 ObjectMeta: metav1.ObjectMeta{ 68 Name: "ibppeer", 69 }, 70 Spec: current.IBPPeerSpec{ 71 Images: ¤t.PeerImages{ 72 PeerImage: "peerimage", 73 PeerTag: "peertag", 74 }, 75 Resources: ¤t.PeerResources{}, 76 }, 77 } 78 79 replicas := int32(1) 80 dep := &appsv1.Deployment{ 81 Spec: appsv1.DeploymentSpec{ 82 Replicas: &replicas, 83 Template: corev1.PodTemplateSpec{ 84 Spec: corev1.PodSpec{ 85 Containers: []corev1.Container{ 86 v1.Container{ 87 Name: "peer", 88 }, 89 v1.Container{ 90 Name: "dind", 91 }, 92 }, 93 }, 94 }, 95 }, 96 } 97 deploymentManager.GetReturns(dep, nil) 98 deploymentManager.DeploymentStatusReturns(appsv1.DeploymentStatus{}, nil) 99 deploymentManager.GetSchemeReturns(&runtime.Scheme{}) 100 101 client.GetStub = func(ctx context.Context, types types.NamespacedName, obj k8sclient.Object) error { 102 switch obj.(type) { 103 case *batchv1.Job: 104 job := obj.(*batchv1.Job) 105 job.Status.Active = int32(1) 106 } 107 return nil 108 } 109 110 configMapManager.GetCoreConfigReturns(&corev1.ConfigMap{ 111 BinaryData: map[string][]byte{ 112 "core.yaml": []byte{}, 113 }, 114 }, nil) 115 116 migrator = &v2.Migrate{ 117 DeploymentManager: deploymentManager, 118 ConfigMapManager: configMapManager, 119 Client: client, 120 } 121 }) 122 123 Context("migration needed", func() { 124 It("returns false if deployment not found", func() { 125 deploymentManager.GetReturns(nil, errors.New("not found")) 126 needed := migrator.MigrationNeeded(instance) 127 Expect(needed).To(Equal(false)) 128 }) 129 130 It("returns true if config map not updated", func() { 131 dep := &appsv1.Deployment{ 132 Spec: appsv1.DeploymentSpec{ 133 Template: corev1.PodTemplateSpec{ 134 Spec: corev1.PodSpec{ 135 Containers: []corev1.Container{ 136 v1.Container{ 137 Name: "peer", 138 }, 139 }, 140 }, 141 }, 142 }, 143 } 144 deploymentManager.GetReturns(dep, nil) 145 146 needed := migrator.MigrationNeeded(instance) 147 Expect(needed).To(Equal(true)) 148 }) 149 150 It("returns true if deployment has dind container", func() { 151 needed := migrator.MigrationNeeded(instance) 152 Expect(needed).To(Equal(true)) 153 }) 154 }) 155 156 Context("upgrade dbs peer", func() { 157 BeforeEach(func() { 158 client.ListStub = func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 159 if strings.Contains(opts[0].(*k8sclient.ListOptions).LabelSelector.String(), "app") { 160 pods := obj.(*corev1.PodList) 161 pods.Items = []corev1.Pod{} 162 } 163 if strings.Contains(opts[0].(*k8sclient.ListOptions).LabelSelector.String(), "job-name") { 164 pods := obj.(*corev1.PodList) 165 pods.Items = []corev1.Pod{ 166 corev1.Pod{ 167 Status: corev1.PodStatus{ 168 ContainerStatuses: []corev1.ContainerStatus{ 169 corev1.ContainerStatus{ 170 State: corev1.ContainerState{ 171 Terminated: &corev1.ContainerStateTerminated{}, 172 }, 173 }, 174 }, 175 }, 176 }, 177 } 178 } 179 return nil 180 } 181 }) 182 183 It("returns an error if unable to reset peer", func() { 184 deploymentManager.GetReturns(nil, errors.New("restore failed")) 185 err := migrator.UpgradeDBs(instance, config.DBMigrationTimeouts{ 186 JobStart: common.MustParseDuration("1s"), 187 JobCompletion: common.MustParseDuration("1s"), 188 }) 189 Expect(err).To(HaveOccurred()) 190 Expect(err).Should(MatchError(ContainSubstring("restore failed"))) 191 }) 192 193 It("upgrade dbs", func() { 194 status := appsv1.DeploymentStatus{ 195 Replicas: int32(0), 196 } 197 deploymentManager.DeploymentStatusReturnsOnCall(0, status, nil) 198 199 status.Replicas = 1 200 deploymentManager.DeploymentStatusReturnsOnCall(1, status, nil) 201 202 err := migrator.UpgradeDBs(instance, config.DBMigrationTimeouts{ 203 JobStart: common.MustParseDuration("1s"), 204 JobCompletion: common.MustParseDuration("1s"), 205 }) 206 Expect(err).NotTo(HaveOccurred()) 207 }) 208 }) 209 210 Context("update config", func() { 211 It("returns an error if unable to get config map", func() { 212 configMapManager.GetCoreConfigReturns(nil, errors.New("get config map failed")) 213 err := migrator.UpdateConfig(instance, FABRIC_V2) 214 Expect(err).To(HaveOccurred()) 215 Expect(err).Should(MatchError(ContainSubstring("get config map failed"))) 216 }) 217 218 It("returns an error if unable to update config map", func() { 219 configMapManager.CreateOrUpdateReturns(errors.New("update config map failed")) 220 err := migrator.UpdateConfig(instance, FABRIC_V2) 221 Expect(err).To(HaveOccurred()) 222 Expect(err).Should(MatchError(ContainSubstring("update config map failed"))) 223 }) 224 225 It("sets relevant v2.x fields in config", func() { 226 err := migrator.UpdateConfig(instance, FABRIC_V2) 227 Expect(err).NotTo(HaveOccurred()) 228 229 _, config := configMapManager.CreateOrUpdateArgsForCall(0) 230 core := config.(*v2config.Core) 231 232 By("setting external builder", func() { 233 Expect(core.Chaincode.ExternalBuilders).To(ContainElement( 234 v2peer.ExternalBuilder{ 235 Name: "ibp-builder", 236 Path: "/usr/local", 237 EnvironmentWhiteList: []string{ 238 "IBP_BUILDER_ENDPOINT", 239 "IBP_BUILDER_SHARED_DIR", 240 }, 241 PropogateEnvironment: []string{ 242 "IBP_BUILDER_ENDPOINT", 243 "IBP_BUILDER_SHARED_DIR", 244 "PEER_NAME", 245 }, 246 }, 247 )) 248 }) 249 250 By("setting install timeout", func() { 251 Expect(core.Chaincode.InstallTimeout).To(Equal(common.MustParseDuration("300s"))) 252 }) 253 254 By("setting lifecycle chaincode", func() { 255 Expect(core.Chaincode.System["_lifecycle"]).To(Equal("enable")) 256 }) 257 258 By("setting limits", func() { 259 Expect(core.Peer.Limits).To(Equal(v2peer.Limits{ 260 Concurrency: v2peer.Concurrency{ 261 DeliverService: 2500, 262 EndorserService: 2500, 263 }, 264 })) 265 }) 266 267 By("setting implicit collection dissemination policy", func() { 268 Expect(core.Peer.Gossip.PvtData.ImplicitCollectionDisseminationPolicy).To(Equal(v2peer.ImplicitCollectionDisseminationPolicy{ 269 RequiredPeerCount: 0, 270 MaxPeerCount: 1, 271 })) 272 }) 273 274 }) 275 276 It("updates config map", func() { 277 err := migrator.UpdateConfig(instance, FABRIC_V2) 278 Expect(err).NotTo(HaveOccurred()) 279 }) 280 }) 281 282 Context("set chaincode launcher resource on CR", func() { 283 BeforeEach(func() { 284 client.GetStub = func(ctx context.Context, nn types.NamespacedName, obj k8sclient.Object) error { 285 switch obj.(type) { 286 case *corev1.ConfigMap: 287 dep := &deployer.Config{ 288 Defaults: &deployer.Defaults{ 289 Resources: &deployer.Resources{ 290 Peer: ¤t.PeerResources{ 291 CCLauncher: &corev1.ResourceRequirements{ 292 Requests: corev1.ResourceList{ 293 corev1.ResourceCPU: resource.MustParse("2"), 294 corev1.ResourceMemory: resource.MustParse("200Mi"), 295 }, 296 Limits: corev1.ResourceList{ 297 corev1.ResourceCPU: resource.MustParse("3"), 298 corev1.ResourceMemory: resource.MustParse("3Gi"), 299 }, 300 }, 301 }, 302 }, 303 }, 304 } 305 306 bytes, err := yaml.Marshal(dep) 307 Expect(err).NotTo(HaveOccurred()) 308 309 cm := obj.(*corev1.ConfigMap) 310 cm.Data = map[string]string{ 311 "settings.yaml": string(bytes), 312 } 313 } 314 315 return nil 316 } 317 318 client.ListStub = func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 319 switch obj.(type) { 320 case *current.IBPConsoleList: 321 list := obj.(*current.IBPConsoleList) 322 list.Items = []current.IBPConsole{current.IBPConsole{}} 323 } 324 325 return nil 326 } 327 }) 328 329 It("sets resources based on deployer config map", func() { 330 err := migrator.SetChaincodeLauncherResourceOnCR(instance) 331 Expect(err).NotTo(HaveOccurred()) 332 333 _, cr, _ := client.UpdateArgsForCall(0) 334 Expect(cr).NotTo(BeNil()) 335 Expect(*cr.(*current.IBPPeer).Spec.Resources.CCLauncher).To(Equal(corev1.ResourceRequirements{ 336 Requests: corev1.ResourceList{ 337 corev1.ResourceCPU: resource.MustParse("2"), 338 corev1.ResourceMemory: resource.MustParse("200Mi"), 339 }, 340 Limits: corev1.ResourceList{ 341 corev1.ResourceCPU: resource.MustParse("3"), 342 corev1.ResourceMemory: resource.MustParse("3Gi"), 343 }}, 344 )) 345 }) 346 347 It("sets resources default config map", func() { 348 client.GetStub = nil 349 350 err := migrator.SetChaincodeLauncherResourceOnCR(instance) 351 Expect(err).NotTo(HaveOccurred()) 352 353 _, cr, _ := client.UpdateArgsForCall(0) 354 Expect(cr).NotTo(BeNil()) 355 Expect(*cr.(*current.IBPPeer).Spec.Resources.CCLauncher).To(Equal(corev1.ResourceRequirements{ 356 Requests: corev1.ResourceList{ 357 corev1.ResourceCPU: resource.MustParse("0.1"), 358 corev1.ResourceMemory: resource.MustParse("100Mi"), 359 }, 360 Limits: corev1.ResourceList{ 361 corev1.ResourceCPU: resource.MustParse("2"), 362 corev1.ResourceMemory: resource.MustParse("2Gi"), 363 }}, 364 )) 365 }) 366 }) 367 })