github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/offering/base/orderer/override/deployment_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 override_test 20 21 import ( 22 "context" 23 "encoding/json" 24 "errors" 25 "fmt" 26 27 . "github.com/onsi/ginkgo/v2" 28 . "github.com/onsi/gomega" 29 "gopkg.in/yaml.v2" 30 appsv1 "k8s.io/api/apps/v1" 31 corev1 "k8s.io/api/core/v1" 32 "k8s.io/apimachinery/pkg/api/resource" 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 "k8s.io/apimachinery/pkg/runtime" 35 "k8s.io/apimachinery/pkg/types" 36 "sigs.k8s.io/controller-runtime/pkg/client" 37 38 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 39 "github.com/IBM-Blockchain/fabric-operator/controllers/mocks" 40 "github.com/IBM-Blockchain/fabric-operator/operatorconfig" 41 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/common" 42 v2orderer "github.com/IBM-Blockchain/fabric-operator/pkg/apis/orderer/v2" 43 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config" 44 v2ordererconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/orderer/config/v2" 45 "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources" 46 dep "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/deployment" 47 "github.com/IBM-Blockchain/fabric-operator/pkg/offering/base/orderer/override" 48 "github.com/IBM-Blockchain/fabric-operator/pkg/util" 49 ) 50 51 var _ = Describe("Base Orderer Deployment Overrides", func() { 52 var ( 53 overrider *override.Override 54 instance *current.IBPOrderer 55 deployment *appsv1.Deployment 56 mockKubeClient *mocks.Client 57 ) 58 59 BeforeEach(func() { 60 var err error 61 62 deployment, err = util.GetDeploymentFromFile("../../../../../definitions/orderer/deployment.yaml") 63 Expect(err).NotTo(HaveOccurred()) 64 65 mockKubeClient = &mocks.Client{} 66 mockKubeClient.GetStub = func(ctx context.Context, types types.NamespacedName, obj client.Object) error { 67 switch obj.(type) { 68 case *corev1.ConfigMap: 69 hsmConfig := &config.HSMConfig{ 70 Type: "hsm", 71 Version: "v1", 72 MountPaths: []config.MountPath{ 73 config.MountPath{ 74 Name: "hsmcrypto", 75 Secret: "hsmcrypto", 76 MountPath: "/hsm", 77 Paths: []config.Path{ 78 { 79 Key: "cafile.pem", 80 Path: "cafile.pem", 81 }, 82 { 83 Key: "cert.pem", 84 Path: "cert.pem", 85 }, 86 { 87 Key: "key.pem", 88 Path: "key.pem", 89 }, 90 { 91 Key: "server.pem", 92 Path: "server.pem", 93 }, 94 }, 95 }, 96 config.MountPath{ 97 Name: "hsmconfig", 98 Secret: "hsmcrypto", 99 MountPath: "/etc/Chrystoki.conf", 100 SubPath: "Chrystoki.conf", 101 Paths: []config.Path{ 102 { 103 Key: "Chrystoki.conf", 104 Path: "Chrystoki.conf", 105 }, 106 }, 107 }, 108 }, 109 Envs: []corev1.EnvVar{ 110 { 111 Name: "env1", 112 Value: "env1value", 113 }, 114 }, 115 } 116 117 configBytes, err := yaml.Marshal(hsmConfig) 118 if err != nil { 119 return err 120 } 121 o := obj.(*corev1.ConfigMap) 122 o.Data = map[string]string{"ibp-hsm-config.yaml": string(configBytes)} 123 } 124 return nil 125 } 126 127 overrider = &override.Override{ 128 Client: mockKubeClient, 129 } 130 131 replicas := int32(1) 132 instance = ¤t.IBPOrderer{ 133 ObjectMeta: metav1.ObjectMeta{ 134 Name: "ordereroverride", 135 Namespace: "namespace1", 136 }, 137 Spec: current.IBPOrdererSpec{ 138 License: current.License{ 139 Accept: true, 140 }, 141 OrgName: "orderermsp", 142 MSPID: "orderermsp", 143 OrdererType: "solo", 144 ExternalAddress: "0.0.0.0", 145 GenesisProfile: "Initial", 146 Storage: ¤t.OrdererStorages{}, 147 Service: ¤t.Service{}, 148 Images: ¤t.OrdererImages{ 149 OrdererInitImage: "fake-init-image", 150 OrdererInitTag: "1234", 151 OrdererImage: "fake-orderer-image", 152 OrdererTag: "1234", 153 GRPCWebImage: "fake-grpcweb-image", 154 GRPCWebTag: "1234", 155 }, 156 SystemChannelName: "testchainid", 157 Arch: []string{"test-arch"}, 158 Zone: "dal", 159 Region: "us-south", 160 ImagePullSecrets: []string{"pullsecret1"}, 161 Replicas: &replicas, 162 Resources: ¤t.OrdererResources{ 163 Orderer: &corev1.ResourceRequirements{ 164 Requests: corev1.ResourceList{ 165 corev1.ResourceCPU: resource.MustParse("0.6m"), 166 corev1.ResourceMemory: resource.MustParse("0.4m"), 167 corev1.ResourceEphemeralStorage: resource.MustParse("0.1m"), 168 }, 169 Limits: corev1.ResourceList{ 170 corev1.ResourceCPU: resource.MustParse("0.7m"), 171 corev1.ResourceMemory: resource.MustParse("0.5m"), 172 corev1.ResourceEphemeralStorage: resource.MustParse("0.5m"), 173 }, 174 }, 175 GRPCProxy: &corev1.ResourceRequirements{ 176 Requests: corev1.ResourceList{ 177 corev1.ResourceCPU: resource.MustParse("0.1m"), 178 corev1.ResourceMemory: resource.MustParse("0.2m"), 179 corev1.ResourceEphemeralStorage: resource.MustParse("0.1m"), 180 }, 181 Limits: corev1.ResourceList{ 182 corev1.ResourceCPU: resource.MustParse("0.3m"), 183 corev1.ResourceMemory: resource.MustParse("0.4m"), 184 corev1.ResourceEphemeralStorage: resource.MustParse("0.5m"), 185 }, 186 }, 187 }, 188 }, 189 } 190 }) 191 192 Context("create", func() { 193 It("returns an error if license is not accepted", func() { 194 instance.Spec.License.Accept = false 195 err := overrider.Deployment(instance, deployment, resources.Create) 196 Expect(err).To(HaveOccurred()) 197 Expect(err.Error()).To(Equal("user must accept license before continuing")) 198 }) 199 200 It("returns an error if value for Orderer Type not provided", func() { 201 instance.Spec.OrdererType = "" 202 err := overrider.Deployment(instance, deployment, resources.Create) 203 Expect(err).To(HaveOccurred()) 204 Expect(err.Error()).To(Equal("Orderer Type not provided")) 205 }) 206 207 It("returns an error if value for System Channel Name not provided", func() { 208 instance.Spec.SystemChannelName = "" 209 err := overrider.Deployment(instance, deployment, resources.Create) 210 Expect(err).To(HaveOccurred()) 211 Expect(err.Error()).To(Equal("System Channel Name not provided")) 212 }) 213 214 It("returns an error if value for Org Name not provided", func() { 215 instance.Spec.OrgName = "" 216 err := overrider.Deployment(instance, deployment, resources.Create) 217 Expect(err).To(HaveOccurred()) 218 Expect(err.Error()).To(Equal("Orderer Org Name not provided")) 219 }) 220 221 It("returns an error if value for External Address not provided", func() { 222 instance.Spec.ExternalAddress = "" 223 err := overrider.Deployment(instance, deployment, resources.Create) 224 Expect(err).To(HaveOccurred()) 225 Expect(err.Error()).To(Equal("External Address not set")) 226 }) 227 228 It("overrides values based on spec", func() { 229 mockKubeClient.GetReturnsOnCall(1, errors.New("no inter cert found")) 230 err := overrider.Deployment(instance, deployment, resources.Create) 231 Expect(err).NotTo(HaveOccurred()) 232 233 By("setting pull secret", func() { 234 Expect(deployment.Spec.Template.Spec.ImagePullSecrets).To(Equal([]corev1.LocalObjectReference{corev1.LocalObjectReference{ 235 Name: instance.Spec.ImagePullSecrets[0], 236 }})) 237 }) 238 239 By("setting env from", func() { 240 envFrom := corev1.EnvFromSource{ 241 ConfigMapRef: &corev1.ConfigMapEnvSource{ 242 LocalObjectReference: corev1.LocalObjectReference{ 243 Name: instance.Name + "-env", 244 }, 245 }, 246 } 247 Expect(deployment.Spec.Template.Spec.Containers[0].EnvFrom).To(ContainElement(envFrom)) 248 }) 249 250 By("setting orderer-data volume", func() { 251 volume := corev1.Volume{ 252 Name: "orderer-data", 253 VolumeSource: corev1.VolumeSource{ 254 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ 255 ClaimName: instance.Name + "-pvc", 256 }, 257 }, 258 } 259 Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(volume)) 260 }) 261 262 By("setting EXTERNAL_ADDRESS env var on grpcweb container", func() { 263 ev := corev1.EnvVar{ 264 Name: "EXTERNAL_ADDRESS", 265 Value: instance.Spec.ExternalAddress, 266 } 267 Expect(deployment.Spec.Template.Spec.Containers[1].Env).To(ContainElement(ev)) 268 }) 269 270 By("setting ecert admincerts volume and volume mount", func() { 271 v := corev1.Volume{ 272 Name: "ecert-admincerts", 273 VolumeSource: corev1.VolumeSource{ 274 Secret: &corev1.SecretVolumeSource{ 275 SecretName: fmt.Sprintf("ecert-%s-admincerts", instance.Name), 276 }, 277 }, 278 } 279 Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(v)) 280 281 vm := corev1.VolumeMount{ 282 Name: "ecert-admincerts", 283 MountPath: "/certs/msp/admincerts", 284 } 285 Expect(deployment.Spec.Template.Spec.Containers[0].VolumeMounts).To(ContainElement(vm)) 286 }) 287 288 By("setting ecert cacerts volume", func() { 289 v := corev1.Volume{ 290 Name: "ecert-cacerts", 291 VolumeSource: corev1.VolumeSource{ 292 Secret: &corev1.SecretVolumeSource{ 293 SecretName: fmt.Sprintf("ecert-%s-cacerts", instance.Name), 294 }, 295 }, 296 } 297 Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(v)) 298 }) 299 300 By("setting ecert keystore volume", func() { 301 v := corev1.Volume{ 302 Name: "ecert-keystore", 303 VolumeSource: corev1.VolumeSource{ 304 Secret: &corev1.SecretVolumeSource{ 305 SecretName: fmt.Sprintf("ecert-%s-keystore", instance.Name), 306 }, 307 }, 308 } 309 Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(v)) 310 }) 311 312 By("setting ecert signcert volume", func() { 313 v := corev1.Volume{ 314 Name: "ecert-signcert", 315 VolumeSource: corev1.VolumeSource{ 316 Secret: &corev1.SecretVolumeSource{ 317 SecretName: fmt.Sprintf("ecert-%s-signcert", instance.Name), 318 }, 319 }, 320 } 321 Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(v)) 322 }) 323 324 By("setting tls cacerts volume", func() { 325 v := corev1.Volume{ 326 Name: "tls-cacerts", 327 VolumeSource: corev1.VolumeSource{ 328 Secret: &corev1.SecretVolumeSource{ 329 SecretName: fmt.Sprintf("tls-%s-cacerts", instance.Name), 330 }, 331 }, 332 } 333 Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(v)) 334 }) 335 336 By("setting tls keystore volume", func() { 337 v := corev1.Volume{ 338 Name: "tls-keystore", 339 VolumeSource: corev1.VolumeSource{ 340 Secret: &corev1.SecretVolumeSource{ 341 SecretName: fmt.Sprintf("tls-%s-keystore", instance.Name), 342 }, 343 }, 344 } 345 Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(v)) 346 }) 347 348 By("setting tls signcert volume", func() { 349 v := corev1.Volume{ 350 Name: "tls-signcert", 351 VolumeSource: corev1.VolumeSource{ 352 Secret: &corev1.SecretVolumeSource{ 353 SecretName: fmt.Sprintf("tls-%s-signcert", instance.Name), 354 }, 355 }, 356 } 357 Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(v)) 358 }) 359 360 By("setting orderer-genesis volume", func() { 361 v := corev1.Volume{ 362 Name: "orderer-genesis", 363 VolumeSource: corev1.VolumeSource{ 364 Secret: &corev1.SecretVolumeSource{ 365 SecretName: fmt.Sprintf("%s-genesis", instance.Name), 366 }, 367 }, 368 } 369 Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(v)) 370 }) 371 372 By("setting orderer-config volume", func() { 373 v := corev1.Volume{ 374 Name: "orderer-config", 375 VolumeSource: corev1.VolumeSource{ 376 ConfigMap: &corev1.ConfigMapVolumeSource{ 377 LocalObjectReference: corev1.LocalObjectReference{ 378 Name: instance.Name + "-config", 379 }, 380 }, 381 }, 382 } 383 Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(v)) 384 }) 385 386 By("setting affinity", func() { 387 expectedAffinity := overrider.GetAffinity(instance) 388 Expect(deployment.Spec.Template.Spec.Affinity).To(Equal(expectedAffinity)) 389 }) 390 391 OrdererDeploymentCommonOverrides(instance, deployment) 392 }) 393 394 It("overrides values based on whether disableProbes is set to true", func() { 395 overrider.Config = &operatorconfig.Config{ 396 Operator: operatorconfig.Operator{ 397 Orderer: operatorconfig.Orderer{ 398 DisableProbes: "true", 399 }, 400 }, 401 } 402 mockKubeClient.GetReturnsOnCall(1, errors.New("no inter cert found")) 403 err := overrider.Deployment(instance, deployment, resources.Create) 404 Expect(err).NotTo(HaveOccurred()) 405 406 By("setting probe values to nil when IBPOPERATOR_ORDERER_DISABLE_PROBES is set to true", func() { 407 d := dep.New(deployment) 408 Expect(d.MustGetContainer(override.ORDERER).ReadinessProbe).To(BeNil()) 409 Expect(d.MustGetContainer(override.ORDERER).LivenessProbe).To(BeNil()) 410 Expect(d.MustGetContainer(override.ORDERER).StartupProbe).To(BeNil()) 411 }) 412 413 }) 414 }) 415 416 Context("update", func() { 417 It("overrides values based on spec", func() { 418 err := overrider.Deployment(instance, deployment, resources.Update) 419 Expect(err).NotTo(HaveOccurred()) 420 421 OrdererDeploymentCommonOverrides(instance, deployment) 422 }) 423 }) 424 425 Context("Replicas", func() { 426 When("Replicas is greater than 1", func() { 427 It("returns an error", func() { 428 replicas := int32(2) 429 instance.Spec.Replicas = &replicas 430 err := overrider.Deployment(instance, deployment, resources.Create) 431 Expect(err).To(HaveOccurred()) 432 Expect(err.Error()).To(Equal("replicas > 1 not allowed in IBPOrderer")) 433 }) 434 }) 435 When("Replicas is equal to 1", func() { 436 It("returns success", func() { 437 replicas := int32(1) 438 instance.Spec.Replicas = &replicas 439 err := overrider.Deployment(instance, deployment, resources.Create) 440 Expect(err).NotTo(HaveOccurred()) 441 }) 442 }) 443 When("Replicas is equal to 0", func() { 444 It("returns success", func() { 445 replicas := int32(0) 446 instance.Spec.Replicas = &replicas 447 err := overrider.Deployment(instance, deployment, resources.Create) 448 Expect(err).NotTo(HaveOccurred()) 449 }) 450 }) 451 When("Replicas is nil", func() { 452 It("returns success", func() { 453 instance.Spec.Replicas = nil 454 err := overrider.Deployment(instance, deployment, resources.Create) 455 Expect(err).NotTo(HaveOccurred()) 456 }) 457 }) 458 }) 459 460 Context("images", func() { 461 var image *current.OrdererImages 462 463 BeforeEach(func() { 464 image = ¤t.OrdererImages{ 465 OrdererInitImage: "init-image", 466 OrdererImage: "orderer-image", 467 GRPCWebImage: "grpcweb-image", 468 } 469 instance.Spec.Images = image 470 }) 471 472 When("no tag is passed", func() { 473 It("uses 'latest' for image tags", func() { 474 err := overrider.Deployment(instance, deployment, resources.Create) 475 Expect(err).NotTo(HaveOccurred()) 476 Expect(deployment.Spec.Template.Spec.InitContainers[0].Image).To(Equal("init-image:latest")) 477 Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("orderer-image:latest")) 478 Expect(deployment.Spec.Template.Spec.Containers[1].Image).To(Equal("grpcweb-image:latest")) 479 }) 480 }) 481 482 When("tag is passed", func() { 483 It("uses the passed in tag for image tags", func() { 484 image.OrdererInitTag = "1.0.0" 485 image.OrdererTag = "2.0.0" 486 image.GRPCWebTag = "3.0.0" 487 488 err := overrider.Deployment(instance, deployment, resources.Create) 489 Expect(err).NotTo(HaveOccurred()) 490 Expect(deployment.Spec.Template.Spec.InitContainers[0].Image).To(Equal("init-image:1.0.0")) 491 Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("orderer-image:2.0.0")) 492 Expect(deployment.Spec.Template.Spec.Containers[1].Image).To(Equal("grpcweb-image:3.0.0")) 493 }) 494 }) 495 }) 496 497 Context("HSM", func() { 498 BeforeEach(func() { 499 configOverride := v2ordererconfig.Orderer{ 500 Orderer: v2orderer.Orderer{ 501 General: v2orderer.General{ 502 BCCSP: &common.BCCSP{ 503 ProviderName: "PKCS11", 504 PKCS11: &common.PKCS11Opts{ 505 Label: "partition1", 506 Pin: "B6T9Q7mGNG", 507 }, 508 }, 509 }, 510 }, 511 } 512 513 configBytes, err := json.Marshal(configOverride) 514 Expect(err).NotTo(HaveOccurred()) 515 configRaw := json.RawMessage(configBytes) 516 517 instance.Spec.ConfigOverride = &runtime.RawExtension{Raw: configRaw} 518 }) 519 520 It("sets proxy env on orderer container", func() { 521 instance.Spec.HSM = ¤t.HSM{PKCS11Endpoint: "1.2.3.4"} 522 err := overrider.Deployment(instance, deployment, resources.Create) 523 Expect(err).NotTo(HaveOccurred()) 524 525 d := dep.New(deployment) 526 Expect(d.MustGetContainer(override.ORDERER).Env).To(ContainElement(corev1.EnvVar{ 527 Name: "PKCS11_PROXY_SOCKET", 528 Value: "1.2.3.4", 529 })) 530 }) 531 532 It("configures deployment to use HSM init image", func() { 533 err := overrider.Deployment(instance, deployment, resources.Create) 534 Expect(err).NotTo(HaveOccurred()) 535 536 d := dep.New(deployment) 537 By("setting volume mounts", func() { 538 Expect(d.MustGetContainer(override.ORDERER).VolumeMounts).To(ContainElement(corev1.VolumeMount{ 539 Name: "shared", 540 MountPath: "/hsm/lib", 541 SubPath: "hsm", 542 })) 543 544 Expect(d.MustGetContainer(override.ORDERER).VolumeMounts).To(ContainElement(corev1.VolumeMount{ 545 Name: "hsmconfig", 546 MountPath: "/etc/Chrystoki.conf", 547 SubPath: "Chrystoki.conf", 548 })) 549 }) 550 551 By("setting env vars", func() { 552 Expect(d.MustGetContainer(override.ORDERER).Env).To(ContainElement(corev1.EnvVar{ 553 Name: "env1", 554 Value: "env1value", 555 })) 556 }) 557 558 By("creating HSM init container", func() { 559 Expect(d.ContainerExists("hsm-client")).To(Equal(true)) 560 }) 561 }) 562 }) 563 }) 564 565 func OrdererDeploymentCommonOverrides(instance *current.IBPOrderer, dep *appsv1.Deployment) { 566 By("setting orderer resources", func() { 567 r, err := util.GetResourcePatch(&corev1.ResourceRequirements{}, instance.Spec.Resources.Orderer) 568 Expect(err).NotTo(HaveOccurred()) 569 Expect(dep.Spec.Template.Spec.Containers[0].Resources).To(Equal(*r)) 570 }) 571 572 By("setting grpcweb resources", func() { 573 r, err := util.GetResourcePatch(&corev1.ResourceRequirements{}, instance.Spec.Resources.GRPCProxy) 574 Expect(err).NotTo(HaveOccurred()) 575 Expect(dep.Spec.Template.Spec.Containers[1].Resources).To(Equal(*r)) 576 }) 577 578 By("setting init image", func() { 579 Expect(dep.Spec.Template.Spec.InitContainers[0].Image).To(Equal(fmt.Sprintf("%s:%s", instance.Spec.Images.OrdererInitImage, instance.Spec.Images.OrdererInitTag))) 580 }) 581 582 By("setting orderer image", func() { 583 Expect(dep.Spec.Template.Spec.Containers[0].Image).To(Equal(fmt.Sprintf("%s:%s", instance.Spec.Images.OrdererImage, instance.Spec.Images.OrdererTag))) 584 }) 585 586 By("setting grpcweb image", func() { 587 Expect(dep.Spec.Template.Spec.Containers[1].Image).To(Equal(fmt.Sprintf("%s:%s", instance.Spec.Images.GRPCWebImage, instance.Spec.Images.GRPCWebTag))) 588 }) 589 590 By("setting replicas", func() { 591 Expect(dep.Spec.Replicas).To(Equal(instance.Spec.Replicas)) 592 }) 593 }