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 = &current.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  })