github.com/IBM-Blockchain/fabric-operator@v1.0.4/integration/e2ev2/orderer_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 e2ev2_test 20 21 import ( 22 "bytes" 23 "context" 24 "encoding/base64" 25 "encoding/json" 26 "fmt" 27 "time" 28 29 . "github.com/onsi/ginkgo/v2" 30 . "github.com/onsi/gomega" 31 "k8s.io/utils/pointer" 32 "sigs.k8s.io/controller-runtime/pkg/client" 33 34 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 35 "github.com/IBM-Blockchain/fabric-operator/integration" 36 "github.com/IBM-Blockchain/fabric-operator/integration/helper" 37 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/common" 38 v1 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/orderer/v1" 39 v2 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/orderer/v2" 40 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config" 41 v2config "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/orderer/config/v2" 42 baseorderer "github.com/IBM-Blockchain/fabric-operator/pkg/offering/base/orderer" 43 44 corev1 "k8s.io/api/core/v1" 45 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 46 "k8s.io/apimachinery/pkg/runtime" 47 "k8s.io/apimachinery/pkg/types" 48 ) 49 50 const ( 51 IBPORDERERS = "ibporderers" 52 53 signCert = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNUekNDQWZXZ0F3SUJBZ0lVQWNnakVkOHBkOE43Vjg0YmFleG4yQzU0dWtzd0NnWUlLb1pJemowRUF3SXcKYURFTE1Ba0dBMVVFQmhNQ1ZWTXhGekFWQmdOVkJBZ1REazV2Y25Sb0lFTmhjbTlzYVc1aE1SUXdFZ1lEVlFRSwpFd3RJZVhCbGNteGxaR2RsY2pFUE1BMEdBMVVFQ3hNR1JtRmljbWxqTVJrd0Z3WURWUVFERXhCbVlXSnlhV010ClkyRXRjMlZ5ZG1WeU1CNFhEVEU1TVRFeE1qRTRNell3TUZvWERUSTBNVEV4TURFNE5ERXdNRm93WHpFTE1Ba0cKQTFVRUJoTUNWVk14RnpBVkJnTlZCQWdURGs1dmNuUm9JRU5oY205c2FXNWhNUlF3RWdZRFZRUUtFd3RJZVhCbApjbXhsWkdkbGNqRVBNQTBHQTFVRUN4TUdZMnhwWlc1ME1SQXdEZ1lEVlFRREV3ZHZjbVJsY21WeU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUU2NFJwK1pvVnYyaTg0cE5KUUFNUHJpenJmZVlNT2Y0UnZ1eHkKNHZOUU1Pd3JEemlIZkFLTnZmdUJlbDhpQ2dndHRXM2paZTVkSEFZaFVIS2Ryb3FodmFPQmhUQ0JnakFPQmdOVgpIUThCQWY4RUJBTUNCNEF3REFZRFZSMFRBUUgvQkFJd0FEQWRCZ05WSFE0RUZnUVVWakl3Y1YwYXRNZmZWV1E5CnhtenpXVG9uYmlJd0h3WURWUjBqQkJnd0ZvQVVTUU9ZL0Z5YnNXcTlIWEo3c296aUFyLzhtQkV3SWdZRFZSMFIKQkJzd0dZSVhVMkZoWkhNdFRXRmpRbTl2YXkxUWNtOHViRzlqWVd3d0NnWUlLb1pJemowRUF3SURTQUF3UlFJaApBUGE4Y3VjL3QvOW45ZDZlSHZoUWdialNBK1k2dytERW1ka2RpdnJHaGE5RUFpQXdTZStlVGdsQWJYQVNoTnhwCkJpR0Rjc2IwZ1pmRmhQd1pIN1VnQW1IQjN3PT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" 54 certKey = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ1p2VWRsUVZ6QlVSc3I2STMKZEVvd0ZlVGkvVkNLZVZqMmFwN2x3QWNYSzJLaFJBTkNBQVRyaEduNW1oVy9hTHppazBsQUF3K3VMT3Q5NWd3NQovaEcrN0hMaTgxQXc3Q3NQT0lkOEFvMjkrNEY2WHlJS0NDMjFiZU5sN2wwY0JpRlFjcDJ1aXFHOQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==" 55 caCert = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNGakNDQWIyZ0F3SUJBZ0lVZi84bk94M2NqM1htVzNDSUo1L0Q1ejRRcUVvd0NnWUlLb1pJemowRUF3SXcKYURFTE1Ba0dBMVVFQmhNQ1ZWTXhGekFWQmdOVkJBZ1REazV2Y25Sb0lFTmhjbTlzYVc1aE1SUXdFZ1lEVlFRSwpFd3RJZVhCbGNteGxaR2RsY2pFUE1BMEdBMVVFQ3hNR1JtRmljbWxqTVJrd0Z3WURWUVFERXhCbVlXSnlhV010ClkyRXRjMlZ5ZG1WeU1CNFhEVEU1TVRBek1ERTNNamd3TUZvWERUTTBNVEF5TmpFM01qZ3dNRm93YURFTE1Ba0cKQTFVRUJoTUNWVk14RnpBVkJnTlZCQWdURGs1dmNuUm9JRU5oY205c2FXNWhNUlF3RWdZRFZRUUtFd3RJZVhCbApjbXhsWkdkbGNqRVBNQTBHQTFVRUN4TUdSbUZpY21sak1Sa3dGd1lEVlFRREV4Qm1ZV0p5YVdNdFkyRXRjMlZ5CmRtVnlNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVSbzNmbUc2UHkyUHd6cUMwNnFWZDlFOFgKZ044eldqZzFMb3lnMmsxdkQ4MXY1dENRRytCTVozSUJGQnI2VTRhc0tZTUREakd6TElERmdUUTRjVDd1VktORgpNRU13RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUV3SFFZRFZSME9CQllFCkZFa0RtUHhjbTdGcXZSMXllN0tNNGdLLy9KZ1JNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRC92QVFVSEh2SWwKQWZZLzM5UWdEU2ltTWpMZnhPTG44NllyR1EvWHpkQVpBaUFpUmlyZmlMdzVGbXBpRDhtYmlmRjV4bzdFUzdqNApaUWQyT0FUNCt5OWE0Zz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" 56 ) 57 58 var _ = Describe("orderer", func() { 59 var ( 60 node1 helper.Orderer 61 ) 62 63 BeforeEach(func() { 64 node1 = orderer.Nodes[0] 65 Eventually(node1.PodIsRunning, time.Second*60, time.Second*2).Should((Equal(true))) 66 67 ClearOperatorConfig() 68 }) 69 70 AfterEach(func() { 71 // Set flag if a test falls 72 if CurrentGinkgoTestDescription().Failed { 73 testFailed = true 74 } 75 }) 76 77 Context("config overrides", func() { 78 var ( 79 podName string 80 bytes []byte 81 ) 82 83 BeforeEach(func() { 84 cm, err := kclient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), orderer.Name+"node1-config", metav1.GetOptions{}) 85 Expect(err).NotTo(HaveOccurred()) 86 87 ordererBytes := cm.BinaryData["orderer.yaml"] 88 ordererConfig, err := v2config.ReadOrdererFromBytes(ordererBytes) 89 Expect(err).NotTo(HaveOccurred()) 90 Expect(ordererConfig.General.Keepalive.ServerMinInterval.Duration).To(Equal(common.MustParseDuration("30h").Duration)) 91 92 configOverride := &v2config.Orderer{ 93 Orderer: v2.Orderer{ 94 General: v2.General{ 95 Keepalive: v1.Keepalive{ 96 ServerInterval: common.MustParseDuration("20h"), 97 }, 98 }, 99 }, 100 } 101 configBytes, err := json.Marshal(configOverride) 102 Expect(err).NotTo(HaveOccurred()) 103 orderer.CR.Spec.ConfigOverride = &runtime.RawExtension{Raw: configBytes} 104 105 orderer.CR.Name = orderer.CR.Name + "node1" 106 107 bytes, err = json.Marshal(orderer.CR) 108 Expect(err).NotTo(HaveOccurred()) 109 110 podName = node1.GetRunningPods()[0].Name 111 }) 112 113 It("updates config based on overrides", func() { 114 result := ibpCRClient.Patch(types.MergePatchType).Namespace(namespace).Resource(IBPORDERERS).Name(node1.Name).Body(bytes).Do(context.TODO()) 115 Expect(result.Error()).NotTo(HaveOccurred()) 116 117 By("updating config in config map", func() { 118 var ordererConfig *v2config.Orderer 119 Eventually(func() bool { 120 cm, err := kclient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), orderer.Name+"node1-config", metav1.GetOptions{}) 121 if err != nil { 122 return false 123 } 124 125 ordererBytes := cm.BinaryData["orderer.yaml"] 126 ordererConfig, err = v2config.ReadOrdererFromBytes(ordererBytes) 127 if err != nil { 128 return false 129 } 130 131 if ordererConfig.General.Keepalive.ServerInterval.Duration == common.MustParseDuration("20h").Duration { 132 return true 133 } 134 135 return false 136 }).Should(Equal(true)) 137 138 Expect(ordererConfig.General.Keepalive.ServerMinInterval.Duration).To(Equal(common.MustParseDuration("30h").Duration)) 139 Expect(ordererConfig.General.Keepalive.ServerInterval.Duration).To(Equal(common.MustParseDuration("20h").Duration)) 140 }) 141 142 By("restarting orderer pods", func() { 143 Eventually(func() bool { 144 pods := node1.GetRunningPods() 145 if len(pods) == 0 { 146 return false 147 } 148 149 newPodName := pods[0].Name 150 if newPodName != podName { 151 return true 152 } 153 154 return false 155 }).Should(Equal(true)) 156 }) 157 }) 158 }) 159 160 Context("msp certs", func() { 161 var ( 162 podName string 163 oldsigncert []byte 164 oldkeystore []byte 165 oldcacert []byte 166 ) 167 168 BeforeEach(func() { 169 Eventually(func() int { return len(node1.GetRunningPods()) }).Should(Equal(1)) 170 171 pods := node1.GetPods() 172 podName = pods[0].Name 173 174 // Store original certs 175 oldsigncert = EcertSignCert(node1.Name) 176 oldkeystore = EcertKeystore(node1.Name) 177 oldcacert = EcertCACert(node1.Name) 178 }) 179 180 It("updates secrets for new certs passed through MSP spec", func() { 181 182 patch := func(i client.Object) { 183 testOrderer := i.(*current.IBPOrderer) 184 testOrderer.Spec.Secret = ¤t.SecretSpec{ 185 MSP: ¤t.MSPSpec{ 186 Component: ¤t.MSP{ 187 SignCerts: signCert, 188 KeyStore: certKey, 189 CACerts: []string{caCert}, 190 }, 191 }, 192 } 193 } 194 195 err := integration.ResilientPatch(ibpCRClient, node1.Name, namespace, "ibporderers", 3, ¤t.IBPOrderer{}, patch) 196 Expect(err).NotTo(HaveOccurred()) 197 198 By("restarting node", func() { 199 Eventually(func() bool { 200 pods := node1.GetPods() 201 if len(pods) != 1 { 202 return false 203 } 204 205 newPodName := pods[0].Name 206 if newPodName == podName { 207 return false 208 } 209 210 return true 211 }).Should(Equal(true)) 212 213 Eventually(node1.PodIsRunning).Should((Equal(true))) 214 }) 215 216 By("backing up old signcert", func() { 217 backup := GetBackup("ecert", node1.Name) 218 Expect(len(backup.List)).NotTo(Equal(0)) 219 Expect(backup.List[len(backup.List)-1].SignCerts).To(Equal(base64.StdEncoding.EncodeToString(oldsigncert))) 220 Expect(backup.List[len(backup.List)-1].KeyStore).To(Equal(base64.StdEncoding.EncodeToString(oldkeystore))) 221 Expect(backup.List[len(backup.List)-1].CACerts).To(Equal([]string{base64.StdEncoding.EncodeToString(oldcacert)})) 222 }) 223 224 By("updating signcert secret", func() { 225 Expect(bytes.Equal(oldsigncert, EcertSignCert(node1.Name))).To(Equal(false)) 226 }) 227 228 By("updating keystore secret", func() { 229 Expect(bytes.Equal(oldkeystore, EcertKeystore(node1.Name))).To(Equal(false)) 230 }) 231 232 By("updating cacert secret", func() { 233 Expect(bytes.Equal(oldcacert, EcertCACert(node1.Name))).To(Equal(false)) 234 }) 235 }) 236 }) 237 238 Context("node ou updated", func() { 239 var ( 240 podName string 241 bytes []byte 242 ibporderer *current.IBPOrderer 243 secret *corev1.Secret 244 ) 245 246 BeforeEach(func() { 247 // Pods seem to run slower and restart slower when running test in Travis. 248 SetDefaultEventuallyTimeout(540 * time.Second) 249 250 Eventually(func() int { return len(node1.GetRunningPods()) }).Should(Equal(1)) 251 podName = node1.GetRunningPods()[0].Name 252 253 // Make sure config is in expected state 254 cm, err := kclient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), node1.Name+"-config", metav1.GetOptions{}) 255 Expect(err).NotTo(HaveOccurred()) 256 257 configBytes := cm.BinaryData["config.yaml"] 258 cfg, err := config.NodeOUConfigFromBytes(configBytes) 259 Expect(err).NotTo(HaveOccurred()) 260 Expect(cfg.NodeOUs.Enable).To(Equal(true)) 261 262 secret, err = kclient.CoreV1(). 263 Secrets(namespace). 264 Get(context.TODO(), fmt.Sprintf("ecert-%s-signcert", node1.Name), metav1.GetOptions{}) 265 Expect(err).NotTo(HaveOccurred()) 266 267 result := ibpCRClient.Get().Namespace(namespace).Resource(IBPORDERERS).Name(node1.Name).Do(context.TODO()) 268 Expect(result.Error()).NotTo(HaveOccurred()) 269 270 ibporderer = ¤t.IBPOrderer{} 271 result.Into(ibporderer) 272 }) 273 274 It("disables nodeOU", func() { 275 By("providing admin certs", func() { 276 var err error 277 adminCert := base64.StdEncoding.EncodeToString(secret.Data["cert.pem"]) 278 279 ibporderer.Spec.Secret.Enrollment.Component.AdminCerts = []string{adminCert} 280 ibporderer.Spec.Secret.MSP = nil 281 bytes, err = json.Marshal(ibporderer) 282 Expect(err).NotTo(HaveOccurred()) 283 284 result := ibpCRClient.Put().Namespace(namespace).Resource(IBPORDERERS).Name(node1.Name).Body(bytes).Do(context.TODO()) 285 Expect(result.Error()).NotTo(HaveOccurred()) 286 287 Eventually(func() bool { 288 _, err := kclient.CoreV1(). 289 Secrets(namespace). 290 Get(context.TODO(), fmt.Sprintf("ecert-%s-admincerts", node1.Name), metav1.GetOptions{}) 291 if err != nil { 292 return false 293 } 294 return true 295 }).Should(Equal(true)) 296 }) 297 298 By("disabling nodeOU", func() { 299 result := ibpCRClient.Get().Namespace(namespace).Resource(IBPORDERERS).Name(node1.Name).Do(context.TODO()) 300 Expect(result.Error()).NotTo(HaveOccurred()) 301 302 ibporderer = ¤t.IBPOrderer{} 303 result.Into(ibporderer) 304 305 // Disable node ou 306 ibporderer.Spec.DisableNodeOU = pointer.Bool(true) 307 bytes, err := json.Marshal(ibporderer) 308 Expect(err).NotTo(HaveOccurred()) 309 310 result = ibpCRClient.Patch(types.MergePatchType).Namespace(namespace).Resource(IBPORDERERS).Name(node1.Name).Body(bytes).Do(context.TODO()) 311 Expect(result.Error()).NotTo(HaveOccurred()) 312 }) 313 314 By("updating config map", func() { 315 Eventually(func() bool { 316 cm, err := kclient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), node1.Name+"-config", metav1.GetOptions{}) 317 if err != nil { 318 return false 319 } 320 321 configBytes := cm.BinaryData["config.yaml"] 322 nodeOUConfig, err := config.NodeOUConfigFromBytes(configBytes) 323 if err != nil { 324 return false 325 } 326 327 return nodeOUConfig.NodeOUs.Enable 328 }).Should(Equal(false)) 329 }) 330 331 By("restarting orderer node pods", func() { 332 Eventually(func() bool { 333 pods := node1.GetRunningPods() 334 if len(pods) == 0 { 335 return false 336 } 337 338 newPodName := pods[0].Name 339 if newPodName != podName { 340 return true 341 } 342 343 return false 344 }).Should(Equal(true)) 345 }) 346 }) 347 }) 348 }) 349 350 func GetOrderer(tlsCert, caHost string) *helper.Orderer { 351 cr, err := helper.OrdererCR(namespace, domain, ordererUsername, tlsCert, caHost) 352 Expect(err).NotTo(HaveOccurred()) 353 354 nodes := []helper.Orderer{ 355 helper.Orderer{ 356 Name: cr.Name + "node1", 357 Namespace: namespace, 358 CR: cr.DeepCopy(), 359 NodeName: fmt.Sprintf("%s%s%d", cr.Name, baseorderer.NODE, 1), 360 NativeResourcePoller: integration.NativeResourcePoller{ 361 Name: cr.Name + "node1", 362 Namespace: namespace, 363 Client: kclient, 364 }, 365 }, 366 } 367 368 nodes[0].CR.ObjectMeta.Name = cr.Name + "node1" 369 370 return &helper.Orderer{ 371 Name: cr.Name, 372 Namespace: namespace, 373 CR: cr, 374 NodeName: fmt.Sprintf("%s-%s%d", cr.Name, baseorderer.NODE, 1), 375 NativeResourcePoller: integration.NativeResourcePoller{ 376 Name: cr.Name, 377 Namespace: namespace, 378 Client: kclient, 379 }, 380 Nodes: nodes, 381 } 382 }