github.com/IBM-Blockchain/fabric-operator@v1.0.4/controllers/ibpconsole/ibpconsole_controller_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 ibpconsole 20 21 import ( 22 "context" 23 "fmt" 24 25 . "github.com/onsi/ginkgo/v2" 26 . "github.com/onsi/gomega" 27 "github.com/pkg/errors" 28 29 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 30 consolemocks "github.com/IBM-Blockchain/fabric-operator/controllers/ibpconsole/mocks" 31 "github.com/IBM-Blockchain/fabric-operator/controllers/mocks" 32 config "github.com/IBM-Blockchain/fabric-operator/operatorconfig" 33 "github.com/IBM-Blockchain/fabric-operator/pkg/offering/common" 34 "github.com/IBM-Blockchain/fabric-operator/pkg/operatorerrors" 35 corev1 "k8s.io/api/core/v1" 36 k8serror "k8s.io/apimachinery/pkg/api/errors" 37 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 38 "k8s.io/apimachinery/pkg/runtime" 39 "k8s.io/apimachinery/pkg/types" 40 "sigs.k8s.io/controller-runtime/pkg/client" 41 "sigs.k8s.io/controller-runtime/pkg/event" 42 "sigs.k8s.io/controller-runtime/pkg/reconcile" 43 ) 44 45 var _ = Describe("ReconcileIBPConsole", func() { 46 const ( 47 testRoleBindingFile = "../../../definitions/console/rolebinding.yaml" 48 testServiceAccountFile = "../../../definitions/console/serviceaccount.yaml" 49 ) 50 51 var ( 52 reconciler *ReconcileIBPConsole 53 request reconcile.Request 54 mockKubeClient *mocks.Client 55 mockConsoleReconcile *consolemocks.ConsoleReconcile 56 instance *current.IBPConsole 57 ) 58 59 BeforeEach(func() { 60 mockKubeClient = &mocks.Client{} 61 mockConsoleReconcile = &consolemocks.ConsoleReconcile{} 62 instance = ¤t.IBPConsole{ 63 Spec: current.IBPConsoleSpec{}, 64 } 65 instance.Name = "test-console" 66 instance.Namespace = "test-namespace" 67 68 mockKubeClient.GetStub = func(ctx context.Context, types types.NamespacedName, obj client.Object) error { 69 switch obj.(type) { 70 case *current.IBPConsole: 71 o := obj.(*current.IBPConsole) 72 o.Kind = "IBPConsole" 73 o.Spec = instance.Spec 74 o.Name = instance.Name 75 76 instance.Status = o.Status 77 } 78 return nil 79 } 80 81 mockKubeClient.UpdateStatusStub = func(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { 82 switch obj.(type) { 83 case *current.IBPConsole: 84 o := obj.(*current.IBPConsole) 85 instance.Status = o.Status 86 } 87 return nil 88 } 89 90 mockKubeClient.ListStub = func(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) error { 91 switch obj.(type) { 92 case *corev1.NodeList: 93 nodeList := obj.(*corev1.NodeList) 94 node := corev1.Node{} 95 node.Labels = map[string]string{} 96 node.Labels["topology.kubernetes.io/zone"] = "dal" 97 node.Labels["topology.kubernetes.io/region"] = "us-south" 98 nodeList.Items = append(nodeList.Items, node) 99 case *current.IBPConsoleList: 100 list := obj.(*current.IBPConsoleList) 101 console1 := current.IBPConsole{} 102 console1.Name = "test-console1" 103 console2 := current.IBPConsole{} 104 console2.Name = "test-console1" 105 list.Items = []current.IBPConsole{console1, console2} 106 case *current.IBPPeerList: 107 caList := obj.(*current.IBPPeerList) 108 p1 := current.IBPPeer{} 109 p1.Name = "test-peer" 110 caList.Items = []current.IBPPeer{p1} 111 } 112 return nil 113 } 114 115 reconciler = &ReconcileIBPConsole{ 116 Config: &config.Config{}, 117 Offering: mockConsoleReconcile, 118 client: mockKubeClient, 119 scheme: &runtime.Scheme{}, 120 } 121 request = reconcile.Request{ 122 NamespacedName: types.NamespacedName{ 123 Namespace: "test-namespace", 124 Name: "test", 125 }, 126 } 127 }) 128 129 Context("Reconciles", func() { 130 It("does not return an error if the custom resource is 'not fonund'", func() { 131 notFoundErr := &k8serror.StatusError{ 132 ErrStatus: metav1.Status{ 133 Reason: metav1.StatusReasonNotFound, 134 }, 135 } 136 mockKubeClient.GetReturns(notFoundErr) 137 _, err := reconciler.Reconcile(context.TODO(), request) 138 Expect(err).NotTo(HaveOccurred()) 139 }) 140 141 It("returns an error if the request to get custom resource return any other errors besides 'not found'", func() { 142 alreadyExistsErr := &k8serror.StatusError{ 143 ErrStatus: metav1.Status{ 144 Message: "already exists", 145 Reason: metav1.StatusReasonAlreadyExists, 146 }, 147 } 148 mockKubeClient.GetReturns(alreadyExistsErr) 149 _, err := reconciler.Reconcile(context.TODO(), request) 150 Expect(err).To(HaveOccurred()) 151 Expect(err.Error()).To(Equal("already exists")) 152 }) 153 154 It("returns an error if it encountered a non-breaking error", func() { 155 errMsg := "failed to reconcile deployment encountered breaking error" 156 mockConsoleReconcile.ReconcileReturns(common.Result{}, errors.New(errMsg)) 157 _, err := reconciler.Reconcile(context.TODO(), request) 158 Expect(err).To(HaveOccurred()) 159 Expect(err.Error()).To(Equal(fmt.Sprintf("Console instance '%s' encountered error: %s", instance.Name, errMsg))) 160 }) 161 162 It("does not return an error if it encountered a breaking error", func() { 163 mockConsoleReconcile.ReconcileReturns(common.Result{}, operatorerrors.New(operatorerrors.InvalidDeploymentCreateRequest, "failed to reconcile deployment encountered breaking error")) 164 _, err := reconciler.Reconcile(context.TODO(), request) 165 Expect(err).NotTo(HaveOccurred()) 166 }) 167 }) 168 169 Context("set status", func() { 170 It("returns an error if the custom resource is not found", func() { 171 notFoundErr := &k8serror.StatusError{ 172 ErrStatus: metav1.Status{ 173 Reason: metav1.StatusReasonNotFound, 174 }, 175 } 176 mockKubeClient.GetReturns(notFoundErr) 177 err := reconciler.SetStatus(instance, notFoundErr) 178 Expect(err).To(HaveOccurred()) 179 }) 180 181 It("sets the status to error if error occured during IBPConsole reconciliation", func() { 182 reconciler.SetStatus(instance, errors.New("ibpconsole error")) 183 Expect(instance.Status.Type).To(Equal(current.Error)) 184 Expect(instance.Status.Message).To(Equal("ibpconsole error")) 185 }) 186 187 It("sets the status to deploying if pod is not yet running", func() { 188 mockKubeClient.ListStub = func(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) error { 189 podList := obj.(*corev1.PodList) 190 pod := corev1.Pod{} 191 podList.Items = append(podList.Items, pod) 192 return nil 193 } 194 reconciler.SetStatus(instance, nil) 195 Expect(instance.Status.Type).To(Equal(current.Deploying)) 196 }) 197 198 It("sets the status to deployed if pod is running", func() { 199 reconciler.SetStatus(instance, nil) 200 Expect(instance.Status.Type).To(Equal(current.Deployed)) 201 }) 202 }) 203 204 Context("create func predicate", func() { 205 var ( 206 e event.CreateEvent 207 ) 208 209 BeforeEach(func() { 210 e = event.CreateEvent{ 211 Object: instance, 212 } 213 }) 214 215 It("returns false if new console's name already exists for another IBPConsole", func() { 216 instance.Name = "test-console1" 217 create := reconciler.CreateFunc(e) 218 Expect(create).To(Equal(false)) 219 Expect(instance.Status.Type).To(Equal(current.Error)) 220 }) 221 222 It("returns false if new console's name already exists for another custom resource", func() { 223 instance.Name = "test-peer" 224 create := reconciler.CreateFunc(e) 225 Expect(create).To(Equal(false)) 226 Expect(instance.Status.Type).To(Equal(current.Error)) 227 }) 228 229 It("returns true if new console with valid name created", func() { 230 create := reconciler.CreateFunc(e) 231 Expect(create).To(Equal(true)) 232 }) 233 }) 234 })