github.com/IBM-Blockchain/fabric-operator@v1.0.4/integration/peer/peer_test.go (about) 1 //go:build !pkcs11 2 // +build !pkcs11 3 4 /* 5 * Copyright contributors to the Hyperledger Fabric Operator project 6 * 7 * SPDX-License-Identifier: Apache-2.0 8 * 9 * Licensed under the Apache License, Version 2.0 (the "License"); 10 * you may not use this file except in compliance with the License. 11 * You may obtain a copy of the License at: 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package peer_test 23 24 import ( 25 "context" 26 "encoding/base64" 27 "encoding/json" 28 "fmt" 29 "time" 30 31 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 32 "github.com/IBM-Blockchain/fabric-operator/integration" 33 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/common" 34 v1 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/peer/v1" 35 v2 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/peer/v2" 36 config "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/peer/config/v2" 37 . "github.com/onsi/ginkgo/v2" 38 . "github.com/onsi/gomega" 39 appsv1 "k8s.io/api/apps/v1" 40 corev1 "k8s.io/api/core/v1" 41 "k8s.io/apimachinery/pkg/api/resource" 42 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 43 "k8s.io/apimachinery/pkg/runtime" 44 "k8s.io/apimachinery/pkg/types" 45 "k8s.io/apimachinery/pkg/util/wait" 46 "k8s.io/utils/pointer" 47 "sigs.k8s.io/yaml" 48 ) 49 50 const ( 51 adminCert = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNUakNDQWZXZ0F3SUJBZ0lVWHdiSXdVeXBmZE1WU1dZU24zWFBFcFZhd2tRd0NnWUlLb1pJemowRUF3SXcKYURFTE1Ba0dBMVVFQmhNQ1ZWTXhGekFWQmdOVkJBZ1REazV2Y25Sb0lFTmhjbTlzYVc1aE1SUXdFZ1lEVlFRSwpFd3RJZVhCbGNteGxaR2RsY2pFUE1BMEdBMVVFQ3hNR1JtRmljbWxqTVJrd0Z3WURWUVFERXhCbVlXSnlhV010ClkyRXRjMlZ5ZG1WeU1CNFhEVEU1TVRFeE1qRTJNRGd3TUZvWERUSXdNVEV4TVRFMk1UTXdNRm93WHpFTE1Ba0cKQTFVRUJoTUNWVk14RnpBVkJnTlZCQWdURGs1dmNuUm9JRU5oY205c2FXNWhNUlF3RWdZRFZRUUtFd3RJZVhCbApjbXhsWkdkbGNqRVBNQTBHQTFVRUN4TUdZMnhwWlc1ME1SQXdEZ1lEVlFRREV3ZHZjbVJsY21WeU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUV2S2RXNytpVVYxbVB3N0J3S2FESkNYVmpha2dqTDhwWCtWaHcKaENLSkNLeXE4Vis4U29tK1AyYzBXdExxbytFU1dVWENKNFJiN0pyOWIzZVc2SmplaHFPQmhUQ0JnakFPQmdOVgpIUThCQWY4RUJBTUNCNEF3REFZRFZSMFRBUUgvQkFJd0FEQWRCZ05WSFE0RUZnUVVSUW4yemN2b3hUUE1rV1JPClZxaG9DL293YXZnd0h3WURWUjBqQkJnd0ZvQVVTUU9ZL0Z5YnNXcTlIWEo3c296aUFyLzhtQkV3SWdZRFZSMFIKQkJzd0dZSVhVMkZoWkhNdFRXRmpRbTl2YXkxUWNtOHViRzlqWVd3d0NnWUlLb1pJemowRUF3SURSd0F3UkFJZwpCNEZmM1dUOWYxcWRjaXBUUzJ6dXFWVDl5WUc1S0dYWmpTN0cyaHZrd0JJQ0lHMXRHb0FkZzNoVWk2TkpyblFJClhaRXFOcWlJWmhPL2hPRmd1emE0VUpZaQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" 52 signCert = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNUekNDQWZXZ0F3SUJBZ0lVQWNnakVkOHBkOE43Vjg0YmFleG4yQzU0dWtzd0NnWUlLb1pJemowRUF3SXcKYURFTE1Ba0dBMVVFQmhNQ1ZWTXhGekFWQmdOVkJBZ1REazV2Y25Sb0lFTmhjbTlzYVc1aE1SUXdFZ1lEVlFRSwpFd3RJZVhCbGNteGxaR2RsY2pFUE1BMEdBMVVFQ3hNR1JtRmljbWxqTVJrd0Z3WURWUVFERXhCbVlXSnlhV010ClkyRXRjMlZ5ZG1WeU1CNFhEVEU1TVRFeE1qRTRNell3TUZvWERUSTBNVEV4TURFNE5ERXdNRm93WHpFTE1Ba0cKQTFVRUJoTUNWVk14RnpBVkJnTlZCQWdURGs1dmNuUm9JRU5oY205c2FXNWhNUlF3RWdZRFZRUUtFd3RJZVhCbApjbXhsWkdkbGNqRVBNQTBHQTFVRUN4TUdZMnhwWlc1ME1SQXdEZ1lEVlFRREV3ZHZjbVJsY21WeU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUU2NFJwK1pvVnYyaTg0cE5KUUFNUHJpenJmZVlNT2Y0UnZ1eHkKNHZOUU1Pd3JEemlIZkFLTnZmdUJlbDhpQ2dndHRXM2paZTVkSEFZaFVIS2Ryb3FodmFPQmhUQ0JnakFPQmdOVgpIUThCQWY4RUJBTUNCNEF3REFZRFZSMFRBUUgvQkFJd0FEQWRCZ05WSFE0RUZnUVVWakl3Y1YwYXRNZmZWV1E5CnhtenpXVG9uYmlJd0h3WURWUjBqQkJnd0ZvQVVTUU9ZL0Z5YnNXcTlIWEo3c296aUFyLzhtQkV3SWdZRFZSMFIKQkJzd0dZSVhVMkZoWkhNdFRXRmpRbTl2YXkxUWNtOHViRzlqWVd3d0NnWUlLb1pJemowRUF3SURTQUF3UlFJaApBUGE4Y3VjL3QvOW45ZDZlSHZoUWdialNBK1k2dytERW1ka2RpdnJHaGE5RUFpQXdTZStlVGdsQWJYQVNoTnhwCkJpR0Rjc2IwZ1pmRmhQd1pIN1VnQW1IQjN3PT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" 53 certKey = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ1p2VWRsUVZ6QlVSc3I2STMKZEVvd0ZlVGkvVkNLZVZqMmFwN2x3QWNYSzJLaFJBTkNBQVRyaEduNW1oVy9hTHppazBsQUF3K3VMT3Q5NWd3NQovaEcrN0hMaTgxQXc3Q3NQT0lkOEFvMjkrNEY2WHlJS0NDMjFiZU5sN2wwY0JpRlFjcDJ1aXFHOQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==" 54 caCert = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNGakNDQWIyZ0F3SUJBZ0lVZi84bk94M2NqM1htVzNDSUo1L0Q1ejRRcUVvd0NnWUlLb1pJemowRUF3SXcKYURFTE1Ba0dBMVVFQmhNQ1ZWTXhGekFWQmdOVkJBZ1REazV2Y25Sb0lFTmhjbTlzYVc1aE1SUXdFZ1lEVlFRSwpFd3RJZVhCbGNteGxaR2RsY2pFUE1BMEdBMVVFQ3hNR1JtRmljbWxqTVJrd0Z3WURWUVFERXhCbVlXSnlhV010ClkyRXRjMlZ5ZG1WeU1CNFhEVEU1TVRBek1ERTNNamd3TUZvWERUTTBNVEF5TmpFM01qZ3dNRm93YURFTE1Ba0cKQTFVRUJoTUNWVk14RnpBVkJnTlZCQWdURGs1dmNuUm9JRU5oY205c2FXNWhNUlF3RWdZRFZRUUtFd3RJZVhCbApjbXhsWkdkbGNqRVBNQTBHQTFVRUN4TUdSbUZpY21sak1Sa3dGd1lEVlFRREV4Qm1ZV0p5YVdNdFkyRXRjMlZ5CmRtVnlNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVSbzNmbUc2UHkyUHd6cUMwNnFWZDlFOFgKZ044eldqZzFMb3lnMmsxdkQ4MXY1dENRRytCTVozSUJGQnI2VTRhc0tZTUREakd6TElERmdUUTRjVDd1VktORgpNRU13RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUV3SFFZRFZSME9CQllFCkZFa0RtUHhjbTdGcXZSMXllN0tNNGdLLy9KZ1JNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRC92QVFVSEh2SWwKQWZZLzM5UWdEU2ltTWpMZnhPTG44NllyR1EvWHpkQVpBaUFpUmlyZmlMdzVGbXBpRDhtYmlmRjV4bzdFUzdqNApaUWQyT0FUNCt5OWE0Zz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" 55 ) 56 57 type CoreConfig interface { 58 ToBytes() ([]byte, error) 59 } 60 61 var ( 62 defaultRequestsPeer = corev1.ResourceList{ 63 corev1.ResourceCPU: resource.MustParse("10m"), 64 corev1.ResourceMemory: resource.MustParse("20M"), 65 corev1.ResourceEphemeralStorage: resource.MustParse("100M"), 66 } 67 68 defaultLimitsPeer = corev1.ResourceList{ 69 corev1.ResourceCPU: resource.MustParse("100m"), 70 corev1.ResourceMemory: resource.MustParse("200M"), 71 corev1.ResourceEphemeralStorage: resource.MustParse("1G"), 72 } 73 74 defaultRequestsFluentd = corev1.ResourceList{ 75 corev1.ResourceCPU: resource.MustParse("10m"), 76 corev1.ResourceMemory: resource.MustParse("20M"), 77 corev1.ResourceEphemeralStorage: resource.MustParse("100M"), 78 } 79 80 defaultLimitsFluentd = corev1.ResourceList{ 81 corev1.ResourceCPU: resource.MustParse("100m"), 82 corev1.ResourceMemory: resource.MustParse("200M"), 83 corev1.ResourceEphemeralStorage: resource.MustParse("1G"), 84 } 85 86 defaultRequestsCouchdb = corev1.ResourceList{ 87 corev1.ResourceCPU: resource.MustParse("20m"), 88 corev1.ResourceMemory: resource.MustParse("40M"), 89 corev1.ResourceEphemeralStorage: resource.MustParse("100M"), 90 } 91 92 defaultLimitsCouchdb = corev1.ResourceList{ 93 corev1.ResourceCPU: resource.MustParse("200m"), 94 corev1.ResourceMemory: resource.MustParse("400M"), 95 corev1.ResourceEphemeralStorage: resource.MustParse("1G"), 96 } 97 98 defaultRequestsDind = corev1.ResourceList{ 99 corev1.ResourceCPU: resource.MustParse("10m"), 100 corev1.ResourceMemory: resource.MustParse("20M"), 101 corev1.ResourceEphemeralStorage: resource.MustParse("100M"), 102 } 103 104 defaultLimitsDind = corev1.ResourceList{ 105 corev1.ResourceCPU: resource.MustParse("100m"), 106 corev1.ResourceMemory: resource.MustParse("200M"), 107 corev1.ResourceEphemeralStorage: resource.MustParse("1G"), 108 } 109 110 defaultRequestsProxy = corev1.ResourceList{ 111 corev1.ResourceCPU: resource.MustParse("10m"), 112 corev1.ResourceMemory: resource.MustParse("20M"), 113 corev1.ResourceEphemeralStorage: resource.MustParse("100M"), 114 } 115 116 defaultLimitsProxy = corev1.ResourceList{ 117 corev1.ResourceCPU: resource.MustParse("100m"), 118 corev1.ResourceMemory: resource.MustParse("200M"), 119 corev1.ResourceEphemeralStorage: resource.MustParse("1G"), 120 } 121 122 testMSPSpec = ¤t.MSPSpec{ 123 Component: ¤t.MSP{ 124 KeyStore: certKey, 125 SignCerts: signCert, 126 CACerts: []string{caCert}, 127 AdminCerts: []string{adminCert}, 128 }, 129 TLS: ¤t.MSP{ 130 KeyStore: certKey, 131 SignCerts: signCert, 132 CACerts: []string{caCert}, 133 }, 134 } 135 ) 136 137 var ( 138 peer *Peer 139 peer2 *Peer 140 peer3 *Peer 141 ) 142 143 var _ = Describe("Interaction between IBP-Operator and Kubernetes cluster", func() { 144 SetDefaultEventuallyTimeout(420 * time.Second) 145 SetDefaultEventuallyPollingInterval(time.Second) 146 147 BeforeEach(func() { 148 peer = GetPeer1() 149 CreatePeer(peer) 150 151 peer2 = GetPeer2() 152 CreatePeer(peer2) 153 154 peer3 = GetPeer3() 155 CreatePeer(peer3) 156 157 integration.ClearOperatorConfig(kclient, namespace) 158 }) 159 160 AfterEach(func() { 161 // Set flag if a test falls 162 if CurrentGinkgoTestDescription().Failed { 163 testFailed = true 164 } 165 }) 166 167 Context("IBPPeer controller", func() { 168 When("applying first instance of IBPPeer CR", func() { 169 var ( 170 err error 171 dep *appsv1.Deployment 172 ) 173 174 It("creates a IBPPeer custom resource", func() { 175 By("setting the CR status to deploying", func() { 176 Eventually(peer.pollForCRStatus).Should((Equal(current.Deploying))) 177 }) 178 179 By("creating pvcs", func() { 180 Eventually(peer.PVCExists).Should((Equal(true))) 181 Expect(peer.getPVCStorageFromSpec(fmt.Sprintf("%s-pvc", peer.Name))).To(Equal("150Mi")) 182 Expect(peer.getPVCStorageFromSpec(fmt.Sprintf("%s-statedb-pvc", peer.Name))).To(Equal("1Gi")) 183 }) 184 185 By("creating a service", func() { 186 Eventually(peer.ServiceExists).Should((Equal(true))) 187 }) 188 189 By("creating a configmap", func() { 190 Eventually(peer.ConfigMapExists).Should((Equal(true))) 191 }) 192 193 By("starting a ingress", func() { 194 Eventually(peer.IngressExists).Should((Equal(true))) 195 }) 196 197 By("creating a deployment", func() { 198 Eventually(peer.DeploymentExists).Should((Equal(true))) 199 }) 200 201 By("starting a pod", func() { 202 Eventually(peer.PodIsRunning).Should((Equal(true))) 203 }) 204 205 By("creating config map that contains spec", func() { 206 Eventually(func() bool { 207 _, err := kclient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), peer.Name+"-spec", metav1.GetOptions{}) 208 if err != nil { 209 return false 210 } 211 return true 212 }).Should(Equal(true)) 213 }) 214 215 By("setting the CR status to deployed when pod is running", func() { 216 Eventually(peer.pollForCRStatus).Should((Equal(current.Deployed))) 217 }) 218 219 cm, err := kclient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), peer.Name+"-config", metav1.GetOptions{}) 220 Expect(err).NotTo(HaveOccurred()) 221 222 coreBytes := cm.BinaryData["core.yaml"] 223 core, err := config.ReadCoreFromBytes(coreBytes) 224 Expect(err).NotTo(HaveOccurred()) 225 226 By("overriding peer section in core.yaml", func() { 227 configOverride, err := peer.CR.GetConfigOverride() 228 Expect(err).NotTo(HaveOccurred()) 229 bytes, err := configOverride.(CoreConfig).ToBytes() 230 Expect(err).NotTo(HaveOccurred()) 231 coreConfig := &config.Core{} 232 err = yaml.Unmarshal(bytes, coreConfig) 233 Expect(err).NotTo(HaveOccurred()) 234 Expect(core.Peer.ID).To(Equal(coreConfig.Peer.ID)) 235 Expect(string(coreBytes)).To(ContainSubstring("chaincode")) 236 Expect(string(coreBytes)).To(ContainSubstring("vm")) 237 Expect(string(coreBytes)).To(ContainSubstring("ledger")) 238 Expect(string(coreBytes)).To(ContainSubstring("operations")) 239 Expect(string(coreBytes)).To(ContainSubstring("metrics")) 240 }) 241 242 By("overriding chaincode section in core.yaml", func() { 243 configOverride, err := peer.CR.GetConfigOverride() 244 Expect(err).NotTo(HaveOccurred()) 245 bytes, err := configOverride.(CoreConfig).ToBytes() 246 Expect(err).NotTo(HaveOccurred()) 247 coreConfig := &config.Core{} 248 err = yaml.Unmarshal(bytes, coreConfig) 249 Expect(err).NotTo(HaveOccurred()) 250 Expect(core.Chaincode.StartupTimeout).To(Equal(coreConfig.Chaincode.StartupTimeout)) 251 Expect(core.Chaincode.ExecuteTimeout).To(Equal(coreConfig.Chaincode.ExecuteTimeout)) 252 Expect(core.Chaincode.InstallTimeout).To(Equal(coreConfig.Chaincode.InstallTimeout)) 253 }) 254 255 By("creating secrets contain DeliveryClient.AddressOverrides ca certs", func() { 256 Expect(core.Peer.DeliveryClient.AddressOverrides[0].CACertsFile).To(Equal("/orderer/certs/cert0.pem")) 257 Expect(core.Peer.DeliveryClient.AddressOverrides[1].CACertsFile).To(Equal("/orderer/certs/cert1.pem")) 258 259 s, err := kclient.CoreV1().Secrets(namespace).Get(context.TODO(), peer.Name+"-orderercacerts", metav1.GetOptions{}) 260 Expect(err).NotTo(HaveOccurred()) 261 262 data := s.Data 263 Expect(len(data)).To(Equal(2)) 264 265 caCertBytes, err := base64.StdEncoding.DecodeString(caCert) 266 Expect(err).NotTo(HaveOccurred()) 267 268 signCertBytes, err := base64.StdEncoding.DecodeString(signCert) 269 Expect(err).NotTo(HaveOccurred()) 270 271 Expect(data["cert0.pem"]).To(Equal(caCertBytes)) 272 Expect(data["cert1.pem"]).To(Equal(signCertBytes)) 273 }) 274 275 By("overriding operations section in core.yaml", func() { 276 configOverride, err := peer.CR.GetConfigOverride() 277 Expect(err).NotTo(HaveOccurred()) 278 bytes, err := configOverride.(CoreConfig).ToBytes() 279 Expect(err).NotTo(HaveOccurred()) 280 coreConfig := &config.Core{} 281 err = yaml.Unmarshal(bytes, coreConfig) 282 Expect(err).NotTo(HaveOccurred()) 283 Expect(core.Operations.ListenAddress).To(Equal(coreConfig.Operations.ListenAddress)) 284 Expect(core.Operations.TLS.Certificate).To(Equal(coreConfig.Operations.TLS.Certificate)) 285 }) 286 287 By("overriding metrics section in core.yaml", func() { 288 configOverride, err := peer.CR.GetConfigOverride() 289 Expect(err).NotTo(HaveOccurred()) 290 bytes, err := configOverride.(CoreConfig).ToBytes() 291 Expect(err).NotTo(HaveOccurred()) 292 coreConfig := &config.Core{} 293 err = yaml.Unmarshal(bytes, coreConfig) 294 Expect(err).NotTo(HaveOccurred()) 295 Expect(core.Metrics.Statsd.Address).To(Equal(coreConfig.Metrics.Statsd.Address)) 296 }) 297 }) 298 299 // TODO: Test marked as pending until portworx issue is resolved, currently zone is 300 // required to be passed for provisioning to work. Once portworx is working again, this 301 // test should be reenabled 302 PIt("should not find zone and region", func() { 303 // Wait for new deployment before querying deployment for updates 304 wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) { 305 dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{}) 306 if dep != nil { 307 if dep.Status.UpdatedReplicas >= 1 && dep.Status.Conditions[0].Type == appsv1.DeploymentAvailable { 308 return true, nil 309 } 310 } 311 return false, nil 312 }) 313 314 dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{}) 315 Expect(err).NotTo(HaveOccurred()) 316 317 By("checking zone", func() { 318 Expect(peer.TestAffinityZone(dep)).Should((Equal(false))) 319 }) 320 321 By("checking region", func() { 322 Expect(peer.TestAffinityRegion(dep)).Should((Equal(false))) 323 }) 324 }) 325 326 When("the custom resource is updated", func() { 327 var ( 328 dep *appsv1.Deployment 329 newResourceRequestsPeer corev1.ResourceList 330 newResourceLimitsPeer corev1.ResourceList 331 newResourceRequestsProxy corev1.ResourceList 332 newResourceLimitsProxy corev1.ResourceList 333 newResourceRequestsCouchdb corev1.ResourceList 334 newResourceLimitsCouchdb corev1.ResourceList 335 ) 336 337 BeforeEach(func() { 338 newResourceRequestsPeer = map[corev1.ResourceName]resource.Quantity{ 339 corev1.ResourceCPU: resource.MustParse("90m"), 340 corev1.ResourceMemory: resource.MustParse("180M"), 341 corev1.ResourceEphemeralStorage: resource.MustParse("100M"), 342 } 343 newResourceLimitsPeer = map[corev1.ResourceName]resource.Quantity{ 344 corev1.ResourceCPU: resource.MustParse("90m"), 345 corev1.ResourceMemory: resource.MustParse("180M"), 346 corev1.ResourceEphemeralStorage: resource.MustParse("1G"), 347 } 348 349 newResourceRequestsProxy = map[corev1.ResourceName]resource.Quantity{ 350 corev1.ResourceCPU: resource.MustParse("91m"), 351 corev1.ResourceMemory: resource.MustParse("181M"), 352 corev1.ResourceEphemeralStorage: resource.MustParse("100M"), 353 } 354 newResourceLimitsProxy = map[corev1.ResourceName]resource.Quantity{ 355 corev1.ResourceCPU: resource.MustParse("91m"), 356 corev1.ResourceMemory: resource.MustParse("181M"), 357 corev1.ResourceEphemeralStorage: resource.MustParse("1G"), 358 } 359 360 newResourceRequestsCouchdb = map[corev1.ResourceName]resource.Quantity{ 361 corev1.ResourceCPU: resource.MustParse("193m"), 362 corev1.ResourceMemory: resource.MustParse("383M"), 363 corev1.ResourceEphemeralStorage: resource.MustParse("100M"), 364 } 365 newResourceLimitsCouchdb = map[corev1.ResourceName]resource.Quantity{ 366 corev1.ResourceCPU: resource.MustParse("193m"), 367 corev1.ResourceMemory: resource.MustParse("383M"), 368 corev1.ResourceEphemeralStorage: resource.MustParse("1G"), 369 } 370 371 peer.CR.Spec.Resources = ¤t.PeerResources{ 372 Peer: &corev1.ResourceRequirements{ 373 Requests: newResourceRequestsPeer, 374 Limits: newResourceLimitsPeer, 375 }, 376 GRPCProxy: &corev1.ResourceRequirements{ 377 Requests: newResourceRequestsProxy, 378 Limits: newResourceLimitsProxy, 379 }, 380 CouchDB: &corev1.ResourceRequirements{ 381 Requests: newResourceRequestsCouchdb, 382 Limits: newResourceLimitsCouchdb, 383 }, 384 } 385 386 startupTimeout, err := common.ParseDuration("200s") 387 Expect(err).NotTo(HaveOccurred()) 388 389 configOverride := config.Core{ 390 Core: v2.Core{ 391 Peer: v2.Peer{ 392 ID: "new-peerid", 393 }, 394 Chaincode: v2.Chaincode{ 395 StartupTimeout: startupTimeout, 396 }, 397 }, 398 } 399 400 configBytes, err := json.Marshal(configOverride) 401 Expect(err).NotTo(HaveOccurred()) 402 403 peer.CR.Spec.ConfigOverride = &runtime.RawExtension{Raw: configBytes} 404 405 Eventually(peer.DeploymentExists).Should((Equal(true))) 406 dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{}) 407 Expect(err).NotTo(HaveOccurred()) 408 }) 409 410 It("updates the instance of IBPPeer if resources and config overrides are updated in CR", func() { 411 peerResources := dep.Spec.Template.Spec.Containers[0].Resources 412 Expect(peerResources.Requests).To(Equal(defaultRequestsPeer)) 413 Expect(peerResources.Limits).To(Equal(defaultLimitsPeer)) 414 415 proxyResources := dep.Spec.Template.Spec.Containers[1].Resources 416 Expect(proxyResources.Requests).To(Equal(defaultRequestsProxy)) 417 Expect(proxyResources.Limits).To(Equal(defaultLimitsProxy)) 418 419 couchDBResources := dep.Spec.Template.Spec.Containers[2].Resources 420 Expect(couchDBResources.Requests).To(Equal(defaultRequestsCouchdb)) 421 Expect(couchDBResources.Limits).To(Equal(defaultLimitsCouchdb)) 422 423 bytes, err := json.Marshal(peer.CR) 424 Expect(err).NotTo(HaveOccurred()) 425 426 result := ibpCRClient.Patch(types.MergePatchType).Namespace(namespace).Resource("ibppeers").Name(peer.Name).Body(bytes).Do(context.TODO()) 427 Expect(result.Error()).NotTo(HaveOccurred()) 428 429 // Wait for new deployment before querying deployment for updates 430 wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) { 431 dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{}) 432 if dep != nil { 433 if dep.Status.UpdatedReplicas >= 1 && dep.Status.Conditions[0].Type == appsv1.DeploymentAvailable { 434 if dep.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu().MilliValue() == newResourceRequestsProxy.Cpu().MilliValue() { 435 return true, nil 436 } 437 } 438 } 439 return false, nil 440 }) 441 442 dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{}) 443 Expect(err).NotTo(HaveOccurred()) 444 445 updatedPeerResources := dep.Spec.Template.Spec.Containers[0].Resources 446 Expect(updatedPeerResources.Requests).To(Equal(newResourceRequestsPeer)) 447 Expect(updatedPeerResources.Limits).To(Equal(newResourceLimitsPeer)) 448 449 updatedProxyResources := dep.Spec.Template.Spec.Containers[1].Resources 450 Expect(updatedProxyResources.Requests).To(Equal(newResourceRequestsProxy)) 451 Expect(updatedProxyResources.Limits).To(Equal(newResourceLimitsProxy)) 452 453 updatedCouchDBResources := dep.Spec.Template.Spec.Containers[2].Resources 454 Expect(updatedCouchDBResources.Requests).To(Equal(newResourceRequestsCouchdb)) 455 Expect(updatedCouchDBResources.Limits).To(Equal(newResourceLimitsCouchdb)) 456 457 By("updating the config map with new values from override", func() { 458 core := &config.Core{} 459 460 Eventually(func() string { 461 cm, err := kclient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), peer.Name+"-config", metav1.GetOptions{}) 462 Expect(err).NotTo(HaveOccurred()) 463 464 coreBytes := cm.BinaryData["core.yaml"] 465 core, err = config.ReadCoreFromBytes(coreBytes) 466 Expect(err).NotTo(HaveOccurred()) 467 468 return core.Peer.ID 469 }).Should(Equal("new-peerid")) 470 471 configOverride, err := peer.CR.GetConfigOverride() 472 Expect(err).NotTo(HaveOccurred()) 473 474 bytes, err := configOverride.(CoreConfig).ToBytes() 475 Expect(err).NotTo(HaveOccurred()) 476 477 coreConfig := &config.Core{} 478 err = yaml.Unmarshal(bytes, coreConfig) 479 Expect(err).NotTo(HaveOccurred()) 480 Expect(core.Chaincode.StartupTimeout).To(Equal(coreConfig.Chaincode.StartupTimeout)) 481 }) 482 }) 483 }) 484 485 When("a deployment managed by operator is manually edited", func() { 486 var ( 487 err error 488 dep *appsv1.Deployment 489 ) 490 491 BeforeEach(func() { 492 Eventually(func() bool { 493 dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{}) 494 if err == nil && dep != nil { 495 return true 496 } 497 return false 498 }).Should(Equal(true)) 499 }) 500 501 It("restores states", func() { 502 origRequests := dep.Spec.Template.Spec.Containers[0].Resources.Requests 503 504 dep.Spec.Template.Spec.Containers[0].Resources.Requests = corev1.ResourceList{ 505 corev1.ResourceCPU: resource.MustParse("107m"), 506 corev1.ResourceMemory: resource.MustParse("207M"), 507 } 508 509 depBytes, err := json.Marshal(dep) 510 Expect(err).NotTo(HaveOccurred()) 511 512 kclient.AppsV1().Deployments(namespace).Patch(context.TODO(), peer.Name, types.MergePatchType, depBytes, metav1.PatchOptions{}) 513 // Wait for new deployment before querying deployment for updates 514 wait.Poll(500*time.Millisecond, 300*time.Second, func() (bool, error) { 515 dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{}) 516 if dep != nil { 517 if len(dep.Spec.Template.Spec.Containers) >= 1 { 518 if dep.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu().MilliValue() == origRequests.Cpu().MilliValue() { 519 return true, nil 520 } 521 } 522 } 523 return false, nil 524 }) 525 526 dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{}) 527 Expect(err).NotTo(HaveOccurred()) 528 529 Expect(dep.Spec.Template.Spec.Containers[0].Resources.Requests).To(Equal(origRequests)) 530 }) 531 }) 532 533 When("admin certs are updated in peer spec", func() { 534 It("updates the admin cert secret", func() { 535 sec, err := kclient.CoreV1().Secrets(namespace).Get(context.TODO(), "ecert-ibppeer1-admincerts", metav1.GetOptions{}) 536 Expect(err).NotTo(HaveOccurred()) 537 538 certBytes := sec.Data["admincert-0.pem"] 539 certBase64 := base64.StdEncoding.EncodeToString(certBytes) 540 Expect(certBase64).To(Equal(adminCert)) 541 542 peer.CR.Spec.Secret.MSP.Component.AdminCerts = []string{signCert} 543 bytes, err := json.Marshal(peer.CR) 544 Expect(err).NotTo(HaveOccurred()) 545 546 result := ibpCRClient.Patch(types.MergePatchType).Namespace(namespace).Resource("ibppeers").Name(peer.Name).Body(bytes).Do(context.TODO()) 547 Expect(result.Error()).NotTo(HaveOccurred()) 548 549 Eventually(peer.checkAdminCertUpdate).Should(Equal(signCert)) 550 }) 551 }) 552 }) 553 554 When("applying the second instance of IBPPeer CR", func() { 555 var ( 556 err error 557 dep *appsv1.Deployment 558 ) 559 560 It("creates a second IBPPeer custom resource", func() { 561 By("starting a pod", func() { 562 Eventually(peer2.PodIsRunning).Should((Equal(true))) 563 }) 564 }) 565 566 // TODO: Test marked as pending until portworx issue is resolved, currently zone is 567 // required to be passed for provisioning to work. Once portworx is working again, this 568 // test should be reenabled 569 PIt("should find zone and region", func() { 570 // Wait for new deployment before querying deployment for updates 571 wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) { 572 dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer2.Name, metav1.GetOptions{}) 573 if dep != nil { 574 if dep.Status.UpdatedReplicas >= 1 && dep.Status.Conditions[0].Type == appsv1.DeploymentAvailable { 575 return true, nil 576 } 577 } 578 return false, nil 579 }) 580 581 dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer2.Name, metav1.GetOptions{}) 582 Expect(err).NotTo(HaveOccurred()) 583 584 By("checking zone", func() { 585 Expect(peer2.TestAffinityZone(dep)).To((Equal(true))) 586 }) 587 588 By("checking region", func() { 589 Expect(peer2.TestAffinityRegion(dep)).To((Equal(true))) 590 }) 591 }) 592 }) 593 594 Context("operator pod restart", func() { 595 var ( 596 oldPodName string 597 ) 598 599 Context("should not trigger deployment restart if config overrides not updated", func() { 600 BeforeEach(func() { 601 Eventually(peer.PodIsRunning).Should((Equal(true))) 602 603 Eventually(func() int { return len(peer.GetRunningPods()) }).Should(Equal(1)) 604 oldPodName = peer.GetRunningPods()[0].Name 605 }) 606 607 It("does not restart the peer pod", func() { 608 Eventually(peer.PodIsRunning).Should((Equal(true))) 609 610 Eventually(func() bool { 611 pods := peer.GetRunningPods() 612 if len(pods) != 1 { 613 return false 614 } 615 616 newPodName := pods[0].Name 617 if newPodName == oldPodName { 618 return true 619 } 620 621 return false 622 }).Should(Equal(true)) 623 }) 624 }) 625 626 PContext("should trigger deployment restart if config overrides are updated", func() { 627 BeforeEach(func() { 628 Eventually(peer.PodIsRunning).Should((Equal(true))) 629 Eventually(func() int { 630 return len(peer.GetPods()) 631 }).Should(Equal(1)) 632 633 configOverride := config.Core{ 634 Core: v2.Core{ 635 Peer: v2.Peer{ 636 ID: "new-id", 637 }, 638 }, 639 } 640 641 configBytes, err := json.Marshal(configOverride) 642 Expect(err).NotTo(HaveOccurred()) 643 644 peer.CR.Spec.ConfigOverride = &runtime.RawExtension{Raw: configBytes} 645 646 bytes, err := json.Marshal(peer.CR) 647 Expect(err).NotTo(HaveOccurred()) 648 649 result := ibpCRClient.Patch(types.MergePatchType).Namespace(namespace).Resource("ibppeers").Name(peer.Name).Body(bytes).Do(context.TODO()) 650 Expect(result.Error()).NotTo(HaveOccurred()) 651 }) 652 653 It("restarts the peer pod", func() { 654 Eventually(peer.PodIsRunning).Should((Equal(false))) 655 Eventually(peer.PodIsRunning).Should((Equal(true))) 656 657 Eventually(func() bool { 658 pods := peer.GetPods() 659 if len(pods) != 1 { 660 return false 661 } 662 663 newPodName := pods[0].Name 664 if newPodName == oldPodName { 665 return false 666 } 667 668 return true 669 }).Should(Equal(true)) 670 }) 671 }) 672 }) 673 674 When("applying incorrectly configured third instance of IBPPeer CR", func() { 675 It("should set the CR status to error", func() { 676 Eventually(peer3.pollForCRStatus).Should((Equal(current.Error))) 677 678 crStatus := ¤t.IBPPeer{} 679 result := ibpCRClient.Get().Namespace(namespace).Resource("ibppeers").Name(peer3.Name).Do(context.TODO()) 680 result.Into(crStatus) 681 682 Expect(crStatus.Status.Message).To(ContainSubstring("user must accept license before continuing")) 683 }) 684 }) 685 686 Context("delete crs", func() { 687 It("should delete IBPPeer CR", func() { 688 By("deleting the first instance of IBPPeer CR", func() { 689 result := ibpCRClient.Delete().Namespace(namespace).Resource("ibppeers").Name(peer.Name).Do(context.TODO()) 690 Expect(result.Error()).NotTo(HaveOccurred()) 691 }) 692 693 By("deleting the second instance of IBPPeer CR", func() { 694 result := ibpCRClient.Delete().Namespace(namespace).Resource("ibppeers").Name(peer2.Name).Do(context.TODO()) 695 Expect(result.Error()).NotTo(HaveOccurred()) 696 }) 697 698 By("deleting the third instance of IBPPeer CR", func() { 699 result := ibpCRClient.Delete().Namespace(namespace).Resource("ibppeers").Name(peer3.Name).Do(context.TODO()) 700 Expect(result.Error()).NotTo(HaveOccurred()) 701 }) 702 }) 703 }) 704 }) 705 }) 706 707 func GetPeer1() *Peer { 708 startupTimeout, err := common.ParseDuration("200s") 709 Expect(err).NotTo(HaveOccurred()) 710 executeTimeout, err := common.ParseDuration("20s") 711 Expect(err).NotTo(HaveOccurred()) 712 installTimeout, err := common.ParseDuration("600s") 713 Expect(err).NotTo(HaveOccurred()) 714 715 configOverride := config.Core{ 716 Core: v2.Core{ 717 Peer: v2.Peer{ 718 ID: "testPeerID", 719 DeliveryClient: v1.DeliveryClient{ 720 AddressOverrides: []v1.AddressOverride{ 721 v1.AddressOverride{ 722 CACertsFile: caCert, 723 }, 724 v1.AddressOverride{ 725 CACertsFile: signCert, 726 }, 727 }, 728 }, 729 }, 730 Chaincode: v2.Chaincode{ 731 StartupTimeout: startupTimeout, 732 ExecuteTimeout: executeTimeout, 733 InstallTimeout: installTimeout, 734 }, 735 Metrics: v1.Metrics{ 736 Statsd: v1.Statsd{ 737 Address: "127.0.0.1:9445", 738 }, 739 }, 740 Operations: v1.Operations{ 741 ListenAddress: "127.0.0.1:9444", 742 TLS: v1.OperationsTLS{ 743 Certificate: v1.File{ 744 File: "ops-tls-cert.pem", 745 }, 746 }, 747 }, 748 }, 749 } 750 751 configBytes, err := json.Marshal(configOverride) 752 Expect(err).NotTo(HaveOccurred()) 753 754 name := "ibppeer1" 755 cr := ¤t.IBPPeer{ 756 TypeMeta: metav1.TypeMeta{ 757 Kind: "IBPPeer", 758 APIVersion: "ibp.com/v1beta1", 759 }, 760 Spec: current.IBPPeerSpec{ 761 License: current.License{ 762 Accept: true, 763 }, 764 MSPID: "test-peer-mspid", 765 ImagePullSecrets: []string{"regcred"}, 766 Region: "select", 767 Zone: "select", 768 Images: ¤t.PeerImages{ 769 CouchDBImage: integration.CouchdbImage, 770 CouchDBTag: integration.CouchdbTag, 771 GRPCWebImage: integration.GrpcwebImage, 772 GRPCWebTag: integration.GrpcwebTag, 773 PeerImage: integration.PeerImage, 774 PeerTag: integration.PeerTag, 775 PeerInitImage: integration.InitImage, 776 PeerInitTag: integration.InitTag, 777 }, 778 Domain: integration.TestAutomation1IngressDomain, 779 Resources: ¤t.PeerResources{ 780 DinD: &corev1.ResourceRequirements{ 781 782 Requests: defaultRequestsDind, 783 Limits: defaultLimitsDind, 784 }, 785 Peer: &corev1.ResourceRequirements{ 786 Requests: defaultRequestsPeer, 787 Limits: defaultLimitsPeer, 788 }, 789 GRPCProxy: &corev1.ResourceRequirements{ 790 Requests: defaultRequestsProxy, 791 Limits: defaultLimitsProxy, 792 }, 793 FluentD: &corev1.ResourceRequirements{ 794 Requests: defaultRequestsFluentd, 795 Limits: defaultLimitsFluentd, 796 }, 797 CouchDB: &corev1.ResourceRequirements{ 798 Requests: defaultRequestsCouchdb, 799 Limits: defaultLimitsCouchdb, 800 }, 801 }, 802 Storage: ¤t.PeerStorages{ 803 Peer: ¤t.StorageSpec{ 804 Size: "150Mi", 805 }, 806 StateDB: ¤t.StorageSpec{ 807 Size: "1Gi", 808 }, 809 }, 810 Ingress: current.Ingress{ 811 TlsSecretName: "tlssecret", 812 }, 813 Secret: ¤t.SecretSpec{ 814 MSP: testMSPSpec, 815 }, 816 ConfigOverride: &runtime.RawExtension{Raw: configBytes}, 817 DisableNodeOU: pointer.Bool(true), 818 FabricVersion: integration.FabricVersion + "-1", 819 }, 820 } 821 cr.Name = name 822 823 return &Peer{ 824 Name: name, 825 CR: cr, 826 NativeResourcePoller: integration.NativeResourcePoller{ 827 Name: name, 828 Namespace: namespace, 829 Client: kclient, 830 }, 831 } 832 } 833 834 func GetPeer2() *Peer { 835 name := "ibppeer2" 836 cr := ¤t.IBPPeer{ 837 Spec: current.IBPPeerSpec{ 838 License: current.License{ 839 Accept: true, 840 }, 841 MSPID: "test-peer2-mspid", 842 StateDb: "leveldb", 843 Region: "select", 844 Zone: "select", 845 ImagePullSecrets: []string{"regcred"}, 846 Images: ¤t.PeerImages{ 847 CouchDBImage: integration.CouchdbImage, 848 CouchDBTag: integration.CouchdbTag, 849 GRPCWebImage: integration.GrpcwebImage, 850 GRPCWebTag: integration.GrpcwebTag, 851 PeerImage: integration.PeerImage, 852 PeerTag: integration.PeerTag, 853 PeerInitImage: integration.InitImage, 854 PeerInitTag: integration.InitTag, 855 }, 856 Domain: integration.TestAutomation1IngressDomain, 857 Storage: ¤t.PeerStorages{ 858 Peer: ¤t.StorageSpec{ 859 Size: "150Mi", 860 }, 861 StateDB: ¤t.StorageSpec{ 862 Size: "1Gi", 863 }, 864 }, 865 Ingress: current.Ingress{ 866 TlsSecretName: "tlssecret", 867 }, 868 Secret: ¤t.SecretSpec{ 869 MSP: testMSPSpec, 870 }, 871 DisableNodeOU: pointer.Bool(true), 872 FabricVersion: integration.FabricVersion + "-1", 873 }, 874 } 875 cr.Name = name 876 877 return &Peer{ 878 Name: name, 879 CR: cr, 880 NativeResourcePoller: integration.NativeResourcePoller{ 881 Name: name, 882 Namespace: namespace, 883 Client: kclient, 884 }, 885 } 886 } 887 888 func GetPeer3() *Peer { 889 name := "ibppeer3" 890 cr := ¤t.IBPPeer{ 891 Spec: current.IBPPeerSpec{ 892 Domain: integration.TestAutomation1IngressDomain, 893 FabricVersion: integration.FabricVersion + "-1", 894 }, 895 } 896 cr.Name = name 897 898 return &Peer{ 899 Name: name, 900 CR: cr, 901 NativeResourcePoller: integration.NativeResourcePoller{ 902 Name: name, 903 Namespace: namespace, 904 Client: kclient, 905 }, 906 } 907 }