github.com/IBM-Blockchain/fabric-operator@v1.0.4/controllers/ibpca/predicate_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 ibpca 20 21 import ( 22 "context" 23 "fmt" 24 "sync" 25 26 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 27 "github.com/IBM-Blockchain/fabric-operator/controllers/mocks" 28 v1 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/ca/v1" 29 "github.com/IBM-Blockchain/fabric-operator/pkg/util" 30 corev1 "k8s.io/api/core/v1" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 "k8s.io/apimachinery/pkg/runtime" 33 "k8s.io/apimachinery/pkg/types" 34 k8sclient "sigs.k8s.io/controller-runtime/pkg/client" 35 "sigs.k8s.io/controller-runtime/pkg/event" 36 yaml "sigs.k8s.io/yaml" 37 38 . "github.com/onsi/ginkgo/v2" 39 . "github.com/onsi/gomega" 40 ) 41 42 var _ = Describe("predicates", func() { 43 var ( 44 reconciler *ReconcileIBPCA 45 client *mocks.Client 46 oldCA, newCA *current.IBPCA 47 ) 48 49 Context("create func predicate", func() { 50 var ( 51 e event.CreateEvent 52 ) 53 54 BeforeEach(func() { 55 oldCA = ¤t.IBPCA{ 56 ObjectMeta: metav1.ObjectMeta{ 57 Name: "test-ca1", 58 }, 59 Spec: current.IBPCASpec{}, 60 } 61 62 newCA = ¤t.IBPCA{ 63 ObjectMeta: metav1.ObjectMeta{ 64 Name: oldCA.GetName(), 65 }, 66 Status: current.IBPCAStatus{ 67 CRStatus: current.CRStatus{ 68 Type: current.Deployed, 69 }, 70 }, 71 } 72 73 e = event.CreateEvent{ 74 Object: newCA, 75 } 76 77 client = &mocks.Client{ 78 GetStub: func(ctx context.Context, types types.NamespacedName, obj k8sclient.Object) error { 79 switch obj.(type) { 80 case *corev1.ConfigMap: 81 cm := obj.(*corev1.ConfigMap) 82 bytes, err := yaml.Marshal(oldCA.Spec) 83 Expect(err).NotTo((HaveOccurred())) 84 cm.BinaryData = map[string][]byte{ 85 "spec": bytes, 86 } 87 } 88 89 return nil 90 }, 91 ListStub: func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 92 switch obj.(type) { 93 case *current.IBPCAList: 94 caList := obj.(*current.IBPCAList) 95 caList.Items = []current.IBPCA{ 96 {ObjectMeta: metav1.ObjectMeta{Name: "test-ca1"}}, 97 {ObjectMeta: metav1.ObjectMeta{Name: "test-ca2"}}, 98 {ObjectMeta: metav1.ObjectMeta{Name: "test-ca2"}}, 99 } 100 case *current.IBPPeerList: 101 peerList := obj.(*current.IBPPeerList) 102 peerList.Items = []current.IBPPeer{ 103 {ObjectMeta: metav1.ObjectMeta{Name: "test-peer"}}, 104 } 105 } 106 return nil 107 }, 108 } 109 110 reconciler = &ReconcileIBPCA{ 111 update: map[string][]Update{}, 112 client: client, 113 mutex: &sync.Mutex{}, 114 } 115 }) 116 117 It("sets update flags to false if instance has status type and a create event is detected but no spec changes detected", func() { 118 create := reconciler.CreateFunc(e) 119 Expect(create).To(Equal(true)) 120 121 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{ 122 specUpdated: false, 123 caOverridesUpdated: false, 124 tlscaOverridesUpdated: false, 125 })) 126 }) 127 128 It("sets update flags to true if instance has status type and a create event is detected and spec changes detected", func() { 129 jm, err := util.ConvertToJsonMessage(&v1.ServerConfig{}) 130 Expect(err).NotTo(HaveOccurred()) 131 132 spec := current.IBPCASpec{ 133 ImagePullSecrets: []string{"pullsecret1"}, 134 ConfigOverride: ¤t.ConfigOverride{ 135 CA: &runtime.RawExtension{Raw: *jm}, 136 TLSCA: &runtime.RawExtension{Raw: *jm}, 137 }, 138 } 139 binaryData, err := yaml.Marshal(spec) 140 Expect(err).NotTo(HaveOccurred()) 141 142 client.GetStub = func(ctx context.Context, types types.NamespacedName, obj k8sclient.Object) error { 143 switch obj.(type) { 144 case *corev1.ConfigMap: 145 o := obj.(*corev1.ConfigMap) 146 o.BinaryData = map[string][]byte{ 147 "spec": binaryData, 148 } 149 } 150 return nil 151 } 152 create := reconciler.CreateFunc(e) 153 Expect(create).To(Equal(true)) 154 155 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{ 156 specUpdated: true, 157 caOverridesUpdated: true, 158 tlscaOverridesUpdated: true, 159 })) 160 }) 161 162 It("does not trigger update if instance does not have status type and a create event is detected", func() { 163 newCA.Status.Type = "" 164 165 create := reconciler.CreateFunc(e) 166 Expect(create).To(Equal(true)) 167 168 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{})) 169 }) 170 171 It("returns false if new instance's name already exists for another custom resource", func() { 172 newCA.Status.Type = "" 173 newCA.Name = "test-peer" 174 175 create := reconciler.CreateFunc(e) 176 Expect(create).To(Equal(false)) 177 Expect(newCA.Status.Type).To(Equal(current.Error)) 178 }) 179 180 It("returns false if new instance's name already exists for another IBPCA custom resource", func() { 181 newCA.Status.Type = "" 182 newCA.Name = "test-ca2" 183 184 create := reconciler.CreateFunc(e) 185 Expect(create).To(Equal(false)) 186 Expect(newCA.Status.Type).To(Equal(current.Error)) 187 }) 188 189 Context("fabric version", func() { 190 It("returns no updates when fabric version is not changed", func() { 191 reconciler.CreateFunc(e) 192 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{})) 193 }) 194 195 When("fabric version updated", func() { 196 BeforeEach(func() { 197 newCA.Spec.FabricVersion = "2.2.1-1" 198 }) 199 200 It("sets fabric version to true on version change", func() { 201 reconciler.CreateFunc(e) 202 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{ 203 specUpdated: true, 204 fabricVersionUpdated: true, 205 })) 206 }) 207 }) 208 }) 209 210 Context("images", func() { 211 It("returns no updates when images are not changed", func() { 212 reconciler.CreateFunc(e) 213 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{})) 214 }) 215 216 When("images updated", func() { 217 BeforeEach(func() { 218 newCA.Spec.Images = ¤t.CAImages{ 219 CAImage: "caimage2", 220 } 221 }) 222 223 It("sets imagesUpdated to true on image nil to non-nil update", func() { 224 reconciler.CreateFunc(e) 225 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{ 226 specUpdated: true, 227 imagesUpdated: true, 228 })) 229 }) 230 231 It("sets imagesUpdated to true on image changes", func() { 232 oldCA.Spec.Images = ¤t.CAImages{ 233 CAImage: "caimage1", 234 } 235 236 reconciler.CreateFunc(e) 237 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{ 238 specUpdated: true, 239 imagesUpdated: true, 240 })) 241 }) 242 }) 243 }) 244 }) 245 246 Context("update func", func() { 247 var ( 248 e event.UpdateEvent 249 ) 250 251 BeforeEach(func() { 252 oldCA = ¤t.IBPCA{ 253 ObjectMeta: metav1.ObjectMeta{ 254 Name: "test-ca1", 255 }, 256 } 257 258 newCA = ¤t.IBPCA{ 259 ObjectMeta: metav1.ObjectMeta{ 260 Name: oldCA.Name, 261 }, 262 } 263 264 e = event.UpdateEvent{ 265 ObjectOld: oldCA, 266 ObjectNew: newCA, 267 } 268 269 client = &mocks.Client{} 270 reconciler = &ReconcileIBPCA{ 271 update: map[string][]Update{}, 272 client: client, 273 mutex: &sync.Mutex{}, 274 } 275 }) 276 277 It("returns false if zone being update", func() { 278 oldCA.Spec.Zone = "old_zone" 279 newCA.Spec.Zone = "new_zone" 280 Expect(reconciler.UpdateFunc(e)).To(Equal(false)) 281 }) 282 283 It("returns false if region being update", func() { 284 oldCA.Spec.Region = "old_region" 285 newCA.Spec.Region = "new_region" 286 Expect(reconciler.UpdateFunc(e)).To(Equal(false)) 287 }) 288 289 It("returns false old and new objects are equal", func() { 290 Expect(reconciler.UpdateFunc(e)).To(Equal(false)) 291 }) 292 293 It("returns true if spec updated", func() { 294 newCA.Spec.ImagePullSecrets = []string{"secret1"} 295 Expect(reconciler.UpdateFunc(e)).To(Equal(true)) 296 Expect(reconciler.GetUpdateStatus(newCA).SpecUpdated()).To(Equal(true)) 297 }) 298 299 It("returns true if ca overrides created for the first time", func() { 300 newCA.Spec.ConfigOverride = ¤t.ConfigOverride{} 301 Expect(reconciler.UpdateFunc(e)).To(Equal(true)) 302 Expect(reconciler.GetUpdateStatus(newCA).CAOverridesUpdated()).To(Equal(true)) 303 Expect(reconciler.GetUpdateStatus(newCA).TLSCAOverridesUpdated()).To(Equal(true)) 304 }) 305 306 It("returns true if enrollment ca overrides updated", func() { 307 oldCA.Spec.ConfigOverride = ¤t.ConfigOverride{} 308 newCA.Spec.ConfigOverride = ¤t.ConfigOverride{ 309 CA: &runtime.RawExtension{}, 310 } 311 312 Expect(reconciler.UpdateFunc(e)).To(Equal(true)) 313 Expect(reconciler.GetUpdateStatus(newCA).CAOverridesUpdated()).To(Equal(true)) 314 Expect(reconciler.GetUpdateStatus(newCA).TLSCAOverridesUpdated()).To(Equal(false)) 315 }) 316 317 Context("ca crypto", func() { 318 var ( 319 oldSecret *corev1.Secret 320 newSecret *corev1.Secret 321 ) 322 323 BeforeEach(func() { 324 oldSecret = &corev1.Secret{ 325 ObjectMeta: metav1.ObjectMeta{ 326 Name: fmt.Sprintf("%s-ca-crypto", newCA.Name), 327 OwnerReferences: []metav1.OwnerReference{ 328 { 329 Name: newCA.Name, 330 Kind: "IBPCA", 331 }, 332 }, 333 }, 334 } 335 newSecret = &corev1.Secret{ 336 ObjectMeta: metav1.ObjectMeta{ 337 Name: fmt.Sprintf("%s-ca-crypto", newCA.Name), 338 OwnerReferences: []metav1.OwnerReference{ 339 { 340 Name: newCA.Name, 341 Kind: "IBPCA", 342 }, 343 }, 344 }, 345 } 346 e = event.UpdateEvent{ 347 ObjectOld: oldSecret, 348 ObjectNew: newSecret, 349 } 350 }) 351 352 It("returns false if secret data not changed between old and new secret", func() { 353 oldSecret.Data = map[string][]byte{ 354 "tls-cert.pem": []byte("cert"), 355 } 356 newSecret.Data = map[string][]byte{ 357 "tls-cert.pem": []byte("cert"), 358 } 359 Expect(reconciler.UpdateFunc(e)).To(Equal(false)) 360 }) 361 362 It("returns true if secret data changed between old and new secret", func() { 363 oldSecret.Data = map[string][]byte{ 364 "tls-cert.pem": []byte("cert"), 365 } 366 newSecret.Data = map[string][]byte{ 367 "tls-cert.pem": []byte("newcert"), 368 } 369 Expect(reconciler.UpdateFunc(e)).To(Equal(true)) 370 Expect(reconciler.GetUpdateStatus(newCA).CACryptoUpdated()).To(Equal(true)) 371 }) 372 373 It("returns false if anything other than secret data changed between old and new secret", func() { 374 oldSecret.APIVersion = "v1" 375 newSecret.APIVersion = "v2" 376 Expect(reconciler.UpdateFunc(e)).To(Equal(false)) 377 }) 378 }) 379 380 It("returns true if tls ca overrides updated", func() { 381 caConfig := &v1.ServerConfig{ 382 CAConfig: v1.CAConfig{ 383 CA: v1.CAInfo{ 384 Name: "ca", 385 }, 386 }, 387 } 388 389 caJson, err := util.ConvertToJsonMessage(caConfig) 390 Expect(err).NotTo(HaveOccurred()) 391 392 oldCA.Spec.ConfigOverride = ¤t.ConfigOverride{} 393 newCA.Spec.ConfigOverride = ¤t.ConfigOverride{ 394 TLSCA: &runtime.RawExtension{Raw: *caJson}, 395 } 396 397 Expect(reconciler.UpdateFunc(e)).To(Equal(true)) 398 Expect(reconciler.GetUpdateStatus(newCA).CAOverridesUpdated()).To(Equal(false)) 399 Expect(reconciler.GetUpdateStatus(newCA).TLSCAOverridesUpdated()).To(Equal(true)) 400 401 }) 402 403 Context("remove element", func() { 404 BeforeEach(func() { 405 reconciler.PushUpdate(newCA.Name, Update{ 406 caOverridesUpdated: true, 407 }) 408 409 reconciler.PushUpdate(newCA.Name, Update{ 410 tlscaOverridesUpdated: true, 411 }) 412 413 Expect(reconciler.GetUpdateStatus(newCA).CAOverridesUpdated()).To(Equal(true)) 414 Expect(reconciler.GetUpdateStatusAtElement(newCA, 1).TLSCAOverridesUpdated()).To(Equal(true)) 415 }) 416 417 It("removes top element", func() { 418 reconciler.PopUpdate(newCA.Name) 419 Expect(reconciler.GetUpdateStatus(newCA).CAOverridesUpdated()).To(Equal(false)) 420 Expect(reconciler.GetUpdateStatus(newCA).TLSCAOverridesUpdated()).To(Equal(true)) 421 }) 422 423 It("removing more elements than in slice should not panic", func() { 424 reconciler.PopUpdate(newCA.Name) 425 reconciler.PopUpdate(newCA.Name) 426 reconciler.PopUpdate(newCA.Name) 427 Expect(reconciler.GetUpdateStatus(newCA).SpecUpdated()).To(Equal(false)) 428 Expect(reconciler.GetUpdateStatus(newCA).CAOverridesUpdated()).To(Equal(false)) 429 Expect(reconciler.GetUpdateStatus(newCA).TLSCAOverridesUpdated()).To(Equal(false)) 430 }) 431 }) 432 433 Context("push update", func() { 434 It("pushes update only if missing for certificate update", func() { 435 reconciler.PushUpdate(newCA.Name, Update{specUpdated: true}) 436 Expect(len(reconciler.update[newCA.Name])).To(Equal(1)) 437 reconciler.PushUpdate(newCA.Name, Update{caOverridesUpdated: true}) 438 Expect(len(reconciler.update[newCA.Name])).To(Equal(2)) 439 reconciler.PushUpdate(newCA.Name, Update{tlscaOverridesUpdated: true}) 440 Expect(len(reconciler.update[newCA.Name])).To(Equal(3)) 441 reconciler.PushUpdate(newCA.Name, Update{tlscaOverridesUpdated: true}) 442 Expect(len(reconciler.update[newCA.Name])).To(Equal(3)) 443 reconciler.PushUpdate(newCA.Name, Update{restartNeeded: true, specUpdated: true}) 444 Expect(len(reconciler.update[newCA.Name])).To(Equal(4)) 445 }) 446 }) 447 448 Context("fabric version", func() { 449 It("returns no updates when fabric version is not changed", func() { 450 reconciler.UpdateFunc(e) 451 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{})) 452 }) 453 454 When("fabric version updated", func() { 455 BeforeEach(func() { 456 newCA.Spec.FabricVersion = "2.2.1-1" 457 }) 458 459 It("sets fabric version to true on version change", func() { 460 reconciler.UpdateFunc(e) 461 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{ 462 specUpdated: true, 463 fabricVersionUpdated: true, 464 })) 465 }) 466 }) 467 }) 468 469 Context("images", func() { 470 It("returns no updates when images are not changed", func() { 471 reconciler.UpdateFunc(e) 472 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{})) 473 }) 474 475 When("images updated", func() { 476 BeforeEach(func() { 477 newCA.Spec.Images = ¤t.CAImages{ 478 CAImage: "caimage2", 479 } 480 481 oldCA.Spec.Images = ¤t.CAImages{ 482 CAImage: "caimage1", 483 } 484 }) 485 486 It("sets imagesUpdated to true on image changes", func() { 487 reconciler.UpdateFunc(e) 488 Expect(reconciler.GetUpdateStatus(newCA)).To(Equal(&Update{ 489 specUpdated: true, 490 imagesUpdated: true, 491 })) 492 }) 493 }) 494 }) 495 }) 496 })