github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/migrator/peer/fabric/v2/peer_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 v2_test
    20  
    21  import (
    22  	"context"
    23  	"strings"
    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  	controllermocks "github.com/IBM-Blockchain/fabric-operator/controllers/mocks"
    31  	config "github.com/IBM-Blockchain/fabric-operator/operatorconfig"
    32  	"github.com/IBM-Blockchain/fabric-operator/pkg/apis/common"
    33  	"github.com/IBM-Blockchain/fabric-operator/pkg/apis/deployer"
    34  	v2peer "github.com/IBM-Blockchain/fabric-operator/pkg/apis/peer/v2"
    35  	v2config "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/peer/config/v2"
    36  	v2 "github.com/IBM-Blockchain/fabric-operator/pkg/migrator/peer/fabric/v2"
    37  	"github.com/IBM-Blockchain/fabric-operator/pkg/migrator/peer/fabric/v2/mocks"
    38  
    39  	appsv1 "k8s.io/api/apps/v1"
    40  	batchv1 "k8s.io/api/batch/v1"
    41  	corev1 "k8s.io/api/core/v1"
    42  	v1 "k8s.io/api/core/v1"
    43  	"k8s.io/apimachinery/pkg/api/resource"
    44  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    45  	"k8s.io/apimachinery/pkg/runtime"
    46  	"k8s.io/apimachinery/pkg/types"
    47  
    48  	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
    49  	"sigs.k8s.io/yaml"
    50  )
    51  
    52  var _ = Describe("V2 peer migrator", func() {
    53  	var (
    54  		deploymentManager *mocks.DeploymentManager
    55  		configMapManager  *mocks.ConfigMapManager
    56  		client            *controllermocks.Client
    57  		migrator          *v2.Migrate
    58  		instance          *current.IBPPeer
    59  	)
    60  	const FABRIC_V2 = "2.2.5-1"
    61  	BeforeEach(func() {
    62  		deploymentManager = &mocks.DeploymentManager{}
    63  		configMapManager = &mocks.ConfigMapManager{}
    64  		client = &controllermocks.Client{}
    65  
    66  		instance = &current.IBPPeer{
    67  			ObjectMeta: metav1.ObjectMeta{
    68  				Name: "ibppeer",
    69  			},
    70  			Spec: current.IBPPeerSpec{
    71  				Images: &current.PeerImages{
    72  					PeerImage: "peerimage",
    73  					PeerTag:   "peertag",
    74  				},
    75  				Resources: &current.PeerResources{},
    76  			},
    77  		}
    78  
    79  		replicas := int32(1)
    80  		dep := &appsv1.Deployment{
    81  			Spec: appsv1.DeploymentSpec{
    82  				Replicas: &replicas,
    83  				Template: corev1.PodTemplateSpec{
    84  					Spec: corev1.PodSpec{
    85  						Containers: []corev1.Container{
    86  							v1.Container{
    87  								Name: "peer",
    88  							},
    89  							v1.Container{
    90  								Name: "dind",
    91  							},
    92  						},
    93  					},
    94  				},
    95  			},
    96  		}
    97  		deploymentManager.GetReturns(dep, nil)
    98  		deploymentManager.DeploymentStatusReturns(appsv1.DeploymentStatus{}, nil)
    99  		deploymentManager.GetSchemeReturns(&runtime.Scheme{})
   100  
   101  		client.GetStub = func(ctx context.Context, types types.NamespacedName, obj k8sclient.Object) error {
   102  			switch obj.(type) {
   103  			case *batchv1.Job:
   104  				job := obj.(*batchv1.Job)
   105  				job.Status.Active = int32(1)
   106  			}
   107  			return nil
   108  		}
   109  
   110  		configMapManager.GetCoreConfigReturns(&corev1.ConfigMap{
   111  			BinaryData: map[string][]byte{
   112  				"core.yaml": []byte{},
   113  			},
   114  		}, nil)
   115  
   116  		migrator = &v2.Migrate{
   117  			DeploymentManager: deploymentManager,
   118  			ConfigMapManager:  configMapManager,
   119  			Client:            client,
   120  		}
   121  	})
   122  
   123  	Context("migration needed", func() {
   124  		It("returns false if deployment not found", func() {
   125  			deploymentManager.GetReturns(nil, errors.New("not found"))
   126  			needed := migrator.MigrationNeeded(instance)
   127  			Expect(needed).To(Equal(false))
   128  		})
   129  
   130  		It("returns true if config map not updated", func() {
   131  			dep := &appsv1.Deployment{
   132  				Spec: appsv1.DeploymentSpec{
   133  					Template: corev1.PodTemplateSpec{
   134  						Spec: corev1.PodSpec{
   135  							Containers: []corev1.Container{
   136  								v1.Container{
   137  									Name: "peer",
   138  								},
   139  							},
   140  						},
   141  					},
   142  				},
   143  			}
   144  			deploymentManager.GetReturns(dep, nil)
   145  
   146  			needed := migrator.MigrationNeeded(instance)
   147  			Expect(needed).To(Equal(true))
   148  		})
   149  
   150  		It("returns true if deployment has dind container", func() {
   151  			needed := migrator.MigrationNeeded(instance)
   152  			Expect(needed).To(Equal(true))
   153  		})
   154  	})
   155  
   156  	Context("upgrade dbs peer", func() {
   157  		BeforeEach(func() {
   158  			client.ListStub = func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error {
   159  				if strings.Contains(opts[0].(*k8sclient.ListOptions).LabelSelector.String(), "app") {
   160  					pods := obj.(*corev1.PodList)
   161  					pods.Items = []corev1.Pod{}
   162  				}
   163  				if strings.Contains(opts[0].(*k8sclient.ListOptions).LabelSelector.String(), "job-name") {
   164  					pods := obj.(*corev1.PodList)
   165  					pods.Items = []corev1.Pod{
   166  						corev1.Pod{
   167  							Status: corev1.PodStatus{
   168  								ContainerStatuses: []corev1.ContainerStatus{
   169  									corev1.ContainerStatus{
   170  										State: corev1.ContainerState{
   171  											Terminated: &corev1.ContainerStateTerminated{},
   172  										},
   173  									},
   174  								},
   175  							},
   176  						},
   177  					}
   178  				}
   179  				return nil
   180  			}
   181  		})
   182  
   183  		It("returns an error if unable to reset peer", func() {
   184  			deploymentManager.GetReturns(nil, errors.New("restore failed"))
   185  			err := migrator.UpgradeDBs(instance, config.DBMigrationTimeouts{
   186  				JobStart:      common.MustParseDuration("1s"),
   187  				JobCompletion: common.MustParseDuration("1s"),
   188  			})
   189  			Expect(err).To(HaveOccurred())
   190  			Expect(err).Should(MatchError(ContainSubstring("restore failed")))
   191  		})
   192  
   193  		It("upgrade dbs", func() {
   194  			status := appsv1.DeploymentStatus{
   195  				Replicas: int32(0),
   196  			}
   197  			deploymentManager.DeploymentStatusReturnsOnCall(0, status, nil)
   198  
   199  			status.Replicas = 1
   200  			deploymentManager.DeploymentStatusReturnsOnCall(1, status, nil)
   201  
   202  			err := migrator.UpgradeDBs(instance, config.DBMigrationTimeouts{
   203  				JobStart:      common.MustParseDuration("1s"),
   204  				JobCompletion: common.MustParseDuration("1s"),
   205  			})
   206  			Expect(err).NotTo(HaveOccurred())
   207  		})
   208  	})
   209  
   210  	Context("update config", func() {
   211  		It("returns an error if unable to get config map", func() {
   212  			configMapManager.GetCoreConfigReturns(nil, errors.New("get config map failed"))
   213  			err := migrator.UpdateConfig(instance, FABRIC_V2)
   214  			Expect(err).To(HaveOccurred())
   215  			Expect(err).Should(MatchError(ContainSubstring("get config map failed")))
   216  		})
   217  
   218  		It("returns an error if unable to update config map", func() {
   219  			configMapManager.CreateOrUpdateReturns(errors.New("update config map failed"))
   220  			err := migrator.UpdateConfig(instance, FABRIC_V2)
   221  			Expect(err).To(HaveOccurred())
   222  			Expect(err).Should(MatchError(ContainSubstring("update config map failed")))
   223  		})
   224  
   225  		It("sets relevant v2.x fields in config", func() {
   226  			err := migrator.UpdateConfig(instance, FABRIC_V2)
   227  			Expect(err).NotTo(HaveOccurred())
   228  
   229  			_, config := configMapManager.CreateOrUpdateArgsForCall(0)
   230  			core := config.(*v2config.Core)
   231  
   232  			By("setting external builder", func() {
   233  				Expect(core.Chaincode.ExternalBuilders).To(ContainElement(
   234  					v2peer.ExternalBuilder{
   235  						Name: "ibp-builder",
   236  						Path: "/usr/local",
   237  						EnvironmentWhiteList: []string{
   238  							"IBP_BUILDER_ENDPOINT",
   239  							"IBP_BUILDER_SHARED_DIR",
   240  						},
   241  						PropogateEnvironment: []string{
   242  							"IBP_BUILDER_ENDPOINT",
   243  							"IBP_BUILDER_SHARED_DIR",
   244  							"PEER_NAME",
   245  						},
   246  					},
   247  				))
   248  			})
   249  
   250  			By("setting install timeout", func() {
   251  				Expect(core.Chaincode.InstallTimeout).To(Equal(common.MustParseDuration("300s")))
   252  			})
   253  
   254  			By("setting lifecycle chaincode", func() {
   255  				Expect(core.Chaincode.System["_lifecycle"]).To(Equal("enable"))
   256  			})
   257  
   258  			By("setting limits", func() {
   259  				Expect(core.Peer.Limits).To(Equal(v2peer.Limits{
   260  					Concurrency: v2peer.Concurrency{
   261  						DeliverService:  2500,
   262  						EndorserService: 2500,
   263  					},
   264  				}))
   265  			})
   266  
   267  			By("setting implicit collection dissemination policy", func() {
   268  				Expect(core.Peer.Gossip.PvtData.ImplicitCollectionDisseminationPolicy).To(Equal(v2peer.ImplicitCollectionDisseminationPolicy{
   269  					RequiredPeerCount: 0,
   270  					MaxPeerCount:      1,
   271  				}))
   272  			})
   273  
   274  		})
   275  
   276  		It("updates config map", func() {
   277  			err := migrator.UpdateConfig(instance, FABRIC_V2)
   278  			Expect(err).NotTo(HaveOccurred())
   279  		})
   280  	})
   281  
   282  	Context("set chaincode launcher resource on CR", func() {
   283  		BeforeEach(func() {
   284  			client.GetStub = func(ctx context.Context, nn types.NamespacedName, obj k8sclient.Object) error {
   285  				switch obj.(type) {
   286  				case *corev1.ConfigMap:
   287  					dep := &deployer.Config{
   288  						Defaults: &deployer.Defaults{
   289  							Resources: &deployer.Resources{
   290  								Peer: &current.PeerResources{
   291  									CCLauncher: &corev1.ResourceRequirements{
   292  										Requests: corev1.ResourceList{
   293  											corev1.ResourceCPU:    resource.MustParse("2"),
   294  											corev1.ResourceMemory: resource.MustParse("200Mi"),
   295  										},
   296  										Limits: corev1.ResourceList{
   297  											corev1.ResourceCPU:    resource.MustParse("3"),
   298  											corev1.ResourceMemory: resource.MustParse("3Gi"),
   299  										},
   300  									},
   301  								},
   302  							},
   303  						},
   304  					}
   305  
   306  					bytes, err := yaml.Marshal(dep)
   307  					Expect(err).NotTo(HaveOccurred())
   308  
   309  					cm := obj.(*corev1.ConfigMap)
   310  					cm.Data = map[string]string{
   311  						"settings.yaml": string(bytes),
   312  					}
   313  				}
   314  
   315  				return nil
   316  			}
   317  
   318  			client.ListStub = func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error {
   319  				switch obj.(type) {
   320  				case *current.IBPConsoleList:
   321  					list := obj.(*current.IBPConsoleList)
   322  					list.Items = []current.IBPConsole{current.IBPConsole{}}
   323  				}
   324  
   325  				return nil
   326  			}
   327  		})
   328  
   329  		It("sets resources based on deployer config map", func() {
   330  			err := migrator.SetChaincodeLauncherResourceOnCR(instance)
   331  			Expect(err).NotTo(HaveOccurred())
   332  
   333  			_, cr, _ := client.UpdateArgsForCall(0)
   334  			Expect(cr).NotTo(BeNil())
   335  			Expect(*cr.(*current.IBPPeer).Spec.Resources.CCLauncher).To(Equal(corev1.ResourceRequirements{
   336  				Requests: corev1.ResourceList{
   337  					corev1.ResourceCPU:    resource.MustParse("2"),
   338  					corev1.ResourceMemory: resource.MustParse("200Mi"),
   339  				},
   340  				Limits: corev1.ResourceList{
   341  					corev1.ResourceCPU:    resource.MustParse("3"),
   342  					corev1.ResourceMemory: resource.MustParse("3Gi"),
   343  				}},
   344  			))
   345  		})
   346  
   347  		It("sets resources default config map", func() {
   348  			client.GetStub = nil
   349  
   350  			err := migrator.SetChaincodeLauncherResourceOnCR(instance)
   351  			Expect(err).NotTo(HaveOccurred())
   352  
   353  			_, cr, _ := client.UpdateArgsForCall(0)
   354  			Expect(cr).NotTo(BeNil())
   355  			Expect(*cr.(*current.IBPPeer).Spec.Resources.CCLauncher).To(Equal(corev1.ResourceRequirements{
   356  				Requests: corev1.ResourceList{
   357  					corev1.ResourceCPU:    resource.MustParse("0.1"),
   358  					corev1.ResourceMemory: resource.MustParse("100Mi"),
   359  				},
   360  				Limits: corev1.ResourceList{
   361  					corev1.ResourceCPU:    resource.MustParse("2"),
   362  					corev1.ResourceMemory: resource.MustParse("2Gi"),
   363  				}},
   364  			))
   365  		})
   366  	})
   367  })