github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/manager/resources/deployment/manager_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 deployment_test
    20  
    21  import (
    22  	"context"
    23  
    24  	"github.com/IBM-Blockchain/fabric-operator/controllers/mocks"
    25  	"github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources"
    26  	"github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/deployment"
    27  	. "github.com/onsi/ginkgo/v2"
    28  	. "github.com/onsi/gomega"
    29  	"github.com/pkg/errors"
    30  	appsv1 "k8s.io/api/apps/v1"
    31  	corev1 "k8s.io/api/core/v1"
    32  	k8serror "k8s.io/apimachinery/pkg/api/errors"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  	"k8s.io/apimachinery/pkg/types"
    36  	"sigs.k8s.io/controller-runtime/pkg/client"
    37  )
    38  
    39  var _ = Describe("Deployment manager", func() {
    40  	var (
    41  		mockKubeClient *mocks.Client
    42  		manager        *deployment.Manager
    43  		instance       metav1.Object
    44  	)
    45  
    46  	BeforeEach(func() {
    47  		mockKubeClient = &mocks.Client{}
    48  		mockKubeClient.GetStub = func(ctx context.Context, types types.NamespacedName, obj client.Object) error {
    49  			switch obj.(type) {
    50  			case *appsv1.Deployment:
    51  				o := obj.(*appsv1.Deployment)
    52  				manager.BasedOnCR(instance, o)
    53  				o.Status.Replicas = 1
    54  				o.Status.UpdatedReplicas = 1
    55  			}
    56  			return nil
    57  		}
    58  
    59  		manager = &deployment.Manager{
    60  			DeploymentFile: "../../../../definitions/ca/deployment.yaml",
    61  			Client:         mockKubeClient,
    62  			OverrideFunc: func(object v1.Object, d *appsv1.Deployment, action resources.Action) error {
    63  				d.Spec = appsv1.DeploymentSpec{
    64  					Template: corev1.PodTemplateSpec{
    65  						Spec: corev1.PodSpec{
    66  							Containers: []corev1.Container{corev1.Container{
    67  								Name: "container",
    68  							}},
    69  						},
    70  					},
    71  				}
    72  				return nil
    73  			},
    74  			LabelsFunc: func(v1.Object) map[string]string {
    75  				return map[string]string{}
    76  			},
    77  		}
    78  
    79  		instance = &metav1.ObjectMeta{}
    80  	})
    81  
    82  	Context("reconciles the deployment instance", func() {
    83  		It("does not try to create deployment if the get request returns an error other than 'not found'", func() {
    84  			errMsg := "connection refused"
    85  			mockKubeClient.GetReturns(errors.New(errMsg))
    86  			err := manager.Reconcile(instance, false)
    87  			Expect(err).To(HaveOccurred())
    88  			Expect(err.Error()).To(Equal(errMsg))
    89  		})
    90  
    91  		When("deployment does not exist", func() {
    92  			BeforeEach(func() {
    93  				notFoundErr := &k8serror.StatusError{
    94  					ErrStatus: metav1.Status{
    95  						Reason: metav1.StatusReasonNotFound,
    96  					},
    97  				}
    98  				mockKubeClient.GetReturns(notFoundErr)
    99  			})
   100  
   101  			It("returns an error if fails to load default config", func() {
   102  				manager.DeploymentFile = "bad.yaml"
   103  				err := manager.Reconcile(instance, false)
   104  				Expect(err).To(HaveOccurred())
   105  				Expect(err.Error()).To(ContainSubstring("no such file or directory"))
   106  			})
   107  
   108  			It("returns an error if override deployment value fails", func() {
   109  				manager.OverrideFunc = func(v1.Object, *appsv1.Deployment, resources.Action) error {
   110  					return errors.New("creation override failed")
   111  				}
   112  				err := manager.Reconcile(instance, false)
   113  				Expect(err).To(HaveOccurred())
   114  				Expect(err.Error()).Should(ContainSubstring("creation override failed"))
   115  			})
   116  
   117  			It("returns an error if the creation of the Deployment fails", func() {
   118  				errMsg := "unable to create service"
   119  				mockKubeClient.CreateReturns(errors.New(errMsg))
   120  				err := manager.Reconcile(instance, false)
   121  				Expect(err).To(HaveOccurred())
   122  				Expect(err.Error()).To(Equal(errMsg))
   123  			})
   124  
   125  			It("does not return an error on a successfull Deployment creation", func() {
   126  				err := manager.Reconcile(instance, false)
   127  				Expect(err).NotTo(HaveOccurred())
   128  			})
   129  		})
   130  
   131  		When("deployment already exists", func() {
   132  			It("returns an error if override deployment value fails", func() {
   133  				manager.OverrideFunc = func(v1.Object, *appsv1.Deployment, resources.Action) error {
   134  					return errors.New("update override failed")
   135  				}
   136  				err := manager.Reconcile(instance, true)
   137  				Expect(err).To(HaveOccurred())
   138  				Expect(err.Error()).Should(ContainSubstring("update override failed"))
   139  			})
   140  
   141  			It("returns an error if the updating of Deployment fails", func() {
   142  				errMsg := "unable to update deployment"
   143  				mockKubeClient.PatchReturns(errors.New(errMsg))
   144  				err := manager.Reconcile(instance, true)
   145  				Expect(err).To(HaveOccurred())
   146  				Expect(err.Error()).To(Equal(errMsg))
   147  			})
   148  
   149  			It("does not return an error on a successfull Deployment update", func() {
   150  				err := manager.Reconcile(instance, true)
   151  				Expect(err).NotTo(HaveOccurred())
   152  			})
   153  		})
   154  	})
   155  
   156  	Context("check deployment state", func() {
   157  		It("returns an error if an unexpected change in deployment is detected", func() {
   158  			dep := &appsv1.Deployment{}
   159  
   160  			mockKubeClient.GetStub = func(ctx context.Context, types types.NamespacedName, obj client.Object) error {
   161  				switch obj.(type) {
   162  				case *appsv1.Deployment:
   163  					dep = obj.(*appsv1.Deployment)
   164  					dep.Spec = appsv1.DeploymentSpec{
   165  						Selector: &metav1.LabelSelector{
   166  							MatchLabels: map[string]string{"app": ""},
   167  						},
   168  						Template: corev1.PodTemplateSpec{
   169  							Spec: corev1.PodSpec{
   170  								Containers: []corev1.Container{corev1.Container{
   171  									Name: "test-container",
   172  								}},
   173  							},
   174  						},
   175  					}
   176  				}
   177  				return nil
   178  			}
   179  
   180  			err := manager.CheckState(dep)
   181  			Expect(err).To(HaveOccurred())
   182  			Expect(err.Error()).To(ContainSubstring("deployment () has been edited manually, and does not match what is expected based on the CR: unexpected mismatch: Template.Spec.Containers.slice[0].Name: test-container != container"))
   183  		})
   184  
   185  		It("returns no error if no changes detected for deployment", func() {
   186  			err := manager.CheckState(&appsv1.Deployment{})
   187  			Expect(err).NotTo(HaveOccurred())
   188  		})
   189  	})
   190  
   191  	Context("restore deployment state", func() {
   192  		It("returns an error if the restoring deployment state fails", func() {
   193  			errMsg := "unable to restore deployment"
   194  			mockKubeClient.PatchReturns(errors.New(errMsg))
   195  			err := manager.RestoreState(&appsv1.Deployment{})
   196  			Expect(err).To(HaveOccurred())
   197  			Expect(err.Error()).To(Equal(errMsg))
   198  		})
   199  
   200  		It("returns no error if able to restore deployment state", func() {
   201  			err := manager.RestoreState(&appsv1.Deployment{})
   202  			Expect(err).NotTo(HaveOccurred())
   203  		})
   204  	})
   205  })