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 = &current.IBPCA{
    56  				ObjectMeta: metav1.ObjectMeta{
    57  					Name: "test-ca1",
    58  				},
    59  				Spec: current.IBPCASpec{},
    60  			}
    61  
    62  			newCA = &current.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: &current.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 = &current.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 = &current.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 = &current.IBPCA{
   253  				ObjectMeta: metav1.ObjectMeta{
   254  					Name: "test-ca1",
   255  				},
   256  			}
   257  
   258  			newCA = &current.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 = &current.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 = &current.ConfigOverride{}
   308  			newCA.Spec.ConfigOverride = &current.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 = &current.ConfigOverride{}
   393  			newCA.Spec.ConfigOverride = &current.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 = &current.CAImages{
   478  						CAImage: "caimage2",
   479  					}
   480  
   481  					oldCA.Spec.Images = &current.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  })