github.com/IBM-Blockchain/fabric-operator@v1.0.4/integration/peer/peer_test.go (about)

     1  //go:build !pkcs11
     2  // +build !pkcs11
     3  
     4  /*
     5   * Copyright contributors to the Hyperledger Fabric Operator project
     6   *
     7   * SPDX-License-Identifier: Apache-2.0
     8   *
     9   * Licensed under the Apache License, Version 2.0 (the "License");
    10   * you may not use this file except in compliance with the License.
    11   * You may obtain a copy of the License at:
    12   *
    13   * 	  http://www.apache.org/licenses/LICENSE-2.0
    14   *
    15   * Unless required by applicable law or agreed to in writing, software
    16   * distributed under the License is distributed on an "AS IS" BASIS,
    17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    18   * See the License for the specific language governing permissions and
    19   * limitations under the License.
    20   */
    21  
    22  package peer_test
    23  
    24  import (
    25  	"context"
    26  	"encoding/base64"
    27  	"encoding/json"
    28  	"fmt"
    29  	"time"
    30  
    31  	current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1"
    32  	"github.com/IBM-Blockchain/fabric-operator/integration"
    33  	"github.com/IBM-Blockchain/fabric-operator/pkg/apis/common"
    34  	v1 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/peer/v1"
    35  	v2 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/peer/v2"
    36  	config "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/peer/config/v2"
    37  	. "github.com/onsi/ginkgo/v2"
    38  	. "github.com/onsi/gomega"
    39  	appsv1 "k8s.io/api/apps/v1"
    40  	corev1 "k8s.io/api/core/v1"
    41  	"k8s.io/apimachinery/pkg/api/resource"
    42  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    43  	"k8s.io/apimachinery/pkg/runtime"
    44  	"k8s.io/apimachinery/pkg/types"
    45  	"k8s.io/apimachinery/pkg/util/wait"
    46  	"k8s.io/utils/pointer"
    47  	"sigs.k8s.io/yaml"
    48  )
    49  
    50  const (
    51  	adminCert = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNUakNDQWZXZ0F3SUJBZ0lVWHdiSXdVeXBmZE1WU1dZU24zWFBFcFZhd2tRd0NnWUlLb1pJemowRUF3SXcKYURFTE1Ba0dBMVVFQmhNQ1ZWTXhGekFWQmdOVkJBZ1REazV2Y25Sb0lFTmhjbTlzYVc1aE1SUXdFZ1lEVlFRSwpFd3RJZVhCbGNteGxaR2RsY2pFUE1BMEdBMVVFQ3hNR1JtRmljbWxqTVJrd0Z3WURWUVFERXhCbVlXSnlhV010ClkyRXRjMlZ5ZG1WeU1CNFhEVEU1TVRFeE1qRTJNRGd3TUZvWERUSXdNVEV4TVRFMk1UTXdNRm93WHpFTE1Ba0cKQTFVRUJoTUNWVk14RnpBVkJnTlZCQWdURGs1dmNuUm9JRU5oY205c2FXNWhNUlF3RWdZRFZRUUtFd3RJZVhCbApjbXhsWkdkbGNqRVBNQTBHQTFVRUN4TUdZMnhwWlc1ME1SQXdEZ1lEVlFRREV3ZHZjbVJsY21WeU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUV2S2RXNytpVVYxbVB3N0J3S2FESkNYVmpha2dqTDhwWCtWaHcKaENLSkNLeXE4Vis4U29tK1AyYzBXdExxbytFU1dVWENKNFJiN0pyOWIzZVc2SmplaHFPQmhUQ0JnakFPQmdOVgpIUThCQWY4RUJBTUNCNEF3REFZRFZSMFRBUUgvQkFJd0FEQWRCZ05WSFE0RUZnUVVSUW4yemN2b3hUUE1rV1JPClZxaG9DL293YXZnd0h3WURWUjBqQkJnd0ZvQVVTUU9ZL0Z5YnNXcTlIWEo3c296aUFyLzhtQkV3SWdZRFZSMFIKQkJzd0dZSVhVMkZoWkhNdFRXRmpRbTl2YXkxUWNtOHViRzlqWVd3d0NnWUlLb1pJemowRUF3SURSd0F3UkFJZwpCNEZmM1dUOWYxcWRjaXBUUzJ6dXFWVDl5WUc1S0dYWmpTN0cyaHZrd0JJQ0lHMXRHb0FkZzNoVWk2TkpyblFJClhaRXFOcWlJWmhPL2hPRmd1emE0VUpZaQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="
    52  	signCert  = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNUekNDQWZXZ0F3SUJBZ0lVQWNnakVkOHBkOE43Vjg0YmFleG4yQzU0dWtzd0NnWUlLb1pJemowRUF3SXcKYURFTE1Ba0dBMVVFQmhNQ1ZWTXhGekFWQmdOVkJBZ1REazV2Y25Sb0lFTmhjbTlzYVc1aE1SUXdFZ1lEVlFRSwpFd3RJZVhCbGNteGxaR2RsY2pFUE1BMEdBMVVFQ3hNR1JtRmljbWxqTVJrd0Z3WURWUVFERXhCbVlXSnlhV010ClkyRXRjMlZ5ZG1WeU1CNFhEVEU1TVRFeE1qRTRNell3TUZvWERUSTBNVEV4TURFNE5ERXdNRm93WHpFTE1Ba0cKQTFVRUJoTUNWVk14RnpBVkJnTlZCQWdURGs1dmNuUm9JRU5oY205c2FXNWhNUlF3RWdZRFZRUUtFd3RJZVhCbApjbXhsWkdkbGNqRVBNQTBHQTFVRUN4TUdZMnhwWlc1ME1SQXdEZ1lEVlFRREV3ZHZjbVJsY21WeU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUU2NFJwK1pvVnYyaTg0cE5KUUFNUHJpenJmZVlNT2Y0UnZ1eHkKNHZOUU1Pd3JEemlIZkFLTnZmdUJlbDhpQ2dndHRXM2paZTVkSEFZaFVIS2Ryb3FodmFPQmhUQ0JnakFPQmdOVgpIUThCQWY4RUJBTUNCNEF3REFZRFZSMFRBUUgvQkFJd0FEQWRCZ05WSFE0RUZnUVVWakl3Y1YwYXRNZmZWV1E5CnhtenpXVG9uYmlJd0h3WURWUjBqQkJnd0ZvQVVTUU9ZL0Z5YnNXcTlIWEo3c296aUFyLzhtQkV3SWdZRFZSMFIKQkJzd0dZSVhVMkZoWkhNdFRXRmpRbTl2YXkxUWNtOHViRzlqWVd3d0NnWUlLb1pJemowRUF3SURTQUF3UlFJaApBUGE4Y3VjL3QvOW45ZDZlSHZoUWdialNBK1k2dytERW1ka2RpdnJHaGE5RUFpQXdTZStlVGdsQWJYQVNoTnhwCkJpR0Rjc2IwZ1pmRmhQd1pIN1VnQW1IQjN3PT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
    53  	certKey   = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ1p2VWRsUVZ6QlVSc3I2STMKZEVvd0ZlVGkvVkNLZVZqMmFwN2x3QWNYSzJLaFJBTkNBQVRyaEduNW1oVy9hTHppazBsQUF3K3VMT3Q5NWd3NQovaEcrN0hMaTgxQXc3Q3NQT0lkOEFvMjkrNEY2WHlJS0NDMjFiZU5sN2wwY0JpRlFjcDJ1aXFHOQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg=="
    54  	caCert    = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNGakNDQWIyZ0F3SUJBZ0lVZi84bk94M2NqM1htVzNDSUo1L0Q1ejRRcUVvd0NnWUlLb1pJemowRUF3SXcKYURFTE1Ba0dBMVVFQmhNQ1ZWTXhGekFWQmdOVkJBZ1REazV2Y25Sb0lFTmhjbTlzYVc1aE1SUXdFZ1lEVlFRSwpFd3RJZVhCbGNteGxaR2RsY2pFUE1BMEdBMVVFQ3hNR1JtRmljbWxqTVJrd0Z3WURWUVFERXhCbVlXSnlhV010ClkyRXRjMlZ5ZG1WeU1CNFhEVEU1TVRBek1ERTNNamd3TUZvWERUTTBNVEF5TmpFM01qZ3dNRm93YURFTE1Ba0cKQTFVRUJoTUNWVk14RnpBVkJnTlZCQWdURGs1dmNuUm9JRU5oY205c2FXNWhNUlF3RWdZRFZRUUtFd3RJZVhCbApjbXhsWkdkbGNqRVBNQTBHQTFVRUN4TUdSbUZpY21sak1Sa3dGd1lEVlFRREV4Qm1ZV0p5YVdNdFkyRXRjMlZ5CmRtVnlNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVSbzNmbUc2UHkyUHd6cUMwNnFWZDlFOFgKZ044eldqZzFMb3lnMmsxdkQ4MXY1dENRRytCTVozSUJGQnI2VTRhc0tZTUREakd6TElERmdUUTRjVDd1VktORgpNRU13RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUV3SFFZRFZSME9CQllFCkZFa0RtUHhjbTdGcXZSMXllN0tNNGdLLy9KZ1JNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRC92QVFVSEh2SWwKQWZZLzM5UWdEU2ltTWpMZnhPTG44NllyR1EvWHpkQVpBaUFpUmlyZmlMdzVGbXBpRDhtYmlmRjV4bzdFUzdqNApaUWQyT0FUNCt5OWE0Zz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
    55  )
    56  
    57  type CoreConfig interface {
    58  	ToBytes() ([]byte, error)
    59  }
    60  
    61  var (
    62  	defaultRequestsPeer = corev1.ResourceList{
    63  		corev1.ResourceCPU:              resource.MustParse("10m"),
    64  		corev1.ResourceMemory:           resource.MustParse("20M"),
    65  		corev1.ResourceEphemeralStorage: resource.MustParse("100M"),
    66  	}
    67  
    68  	defaultLimitsPeer = corev1.ResourceList{
    69  		corev1.ResourceCPU:              resource.MustParse("100m"),
    70  		corev1.ResourceMemory:           resource.MustParse("200M"),
    71  		corev1.ResourceEphemeralStorage: resource.MustParse("1G"),
    72  	}
    73  
    74  	defaultRequestsFluentd = corev1.ResourceList{
    75  		corev1.ResourceCPU:              resource.MustParse("10m"),
    76  		corev1.ResourceMemory:           resource.MustParse("20M"),
    77  		corev1.ResourceEphemeralStorage: resource.MustParse("100M"),
    78  	}
    79  
    80  	defaultLimitsFluentd = corev1.ResourceList{
    81  		corev1.ResourceCPU:              resource.MustParse("100m"),
    82  		corev1.ResourceMemory:           resource.MustParse("200M"),
    83  		corev1.ResourceEphemeralStorage: resource.MustParse("1G"),
    84  	}
    85  
    86  	defaultRequestsCouchdb = corev1.ResourceList{
    87  		corev1.ResourceCPU:              resource.MustParse("20m"),
    88  		corev1.ResourceMemory:           resource.MustParse("40M"),
    89  		corev1.ResourceEphemeralStorage: resource.MustParse("100M"),
    90  	}
    91  
    92  	defaultLimitsCouchdb = corev1.ResourceList{
    93  		corev1.ResourceCPU:              resource.MustParse("200m"),
    94  		corev1.ResourceMemory:           resource.MustParse("400M"),
    95  		corev1.ResourceEphemeralStorage: resource.MustParse("1G"),
    96  	}
    97  
    98  	defaultRequestsDind = corev1.ResourceList{
    99  		corev1.ResourceCPU:              resource.MustParse("10m"),
   100  		corev1.ResourceMemory:           resource.MustParse("20M"),
   101  		corev1.ResourceEphemeralStorage: resource.MustParse("100M"),
   102  	}
   103  
   104  	defaultLimitsDind = corev1.ResourceList{
   105  		corev1.ResourceCPU:              resource.MustParse("100m"),
   106  		corev1.ResourceMemory:           resource.MustParse("200M"),
   107  		corev1.ResourceEphemeralStorage: resource.MustParse("1G"),
   108  	}
   109  
   110  	defaultRequestsProxy = corev1.ResourceList{
   111  		corev1.ResourceCPU:              resource.MustParse("10m"),
   112  		corev1.ResourceMemory:           resource.MustParse("20M"),
   113  		corev1.ResourceEphemeralStorage: resource.MustParse("100M"),
   114  	}
   115  
   116  	defaultLimitsProxy = corev1.ResourceList{
   117  		corev1.ResourceCPU:              resource.MustParse("100m"),
   118  		corev1.ResourceMemory:           resource.MustParse("200M"),
   119  		corev1.ResourceEphemeralStorage: resource.MustParse("1G"),
   120  	}
   121  
   122  	testMSPSpec = &current.MSPSpec{
   123  		Component: &current.MSP{
   124  			KeyStore:   certKey,
   125  			SignCerts:  signCert,
   126  			CACerts:    []string{caCert},
   127  			AdminCerts: []string{adminCert},
   128  		},
   129  		TLS: &current.MSP{
   130  			KeyStore:  certKey,
   131  			SignCerts: signCert,
   132  			CACerts:   []string{caCert},
   133  		},
   134  	}
   135  )
   136  
   137  var (
   138  	peer  *Peer
   139  	peer2 *Peer
   140  	peer3 *Peer
   141  )
   142  
   143  var _ = Describe("Interaction between IBP-Operator and Kubernetes cluster", func() {
   144  	SetDefaultEventuallyTimeout(420 * time.Second)
   145  	SetDefaultEventuallyPollingInterval(time.Second)
   146  
   147  	BeforeEach(func() {
   148  		peer = GetPeer1()
   149  		CreatePeer(peer)
   150  
   151  		peer2 = GetPeer2()
   152  		CreatePeer(peer2)
   153  
   154  		peer3 = GetPeer3()
   155  		CreatePeer(peer3)
   156  
   157  		integration.ClearOperatorConfig(kclient, namespace)
   158  	})
   159  
   160  	AfterEach(func() {
   161  		// Set flag if a test falls
   162  		if CurrentGinkgoTestDescription().Failed {
   163  			testFailed = true
   164  		}
   165  	})
   166  
   167  	Context("IBPPeer controller", func() {
   168  		When("applying first instance of IBPPeer CR", func() {
   169  			var (
   170  				err error
   171  				dep *appsv1.Deployment
   172  			)
   173  
   174  			It("creates a IBPPeer custom resource", func() {
   175  				By("setting the CR status to deploying", func() {
   176  					Eventually(peer.pollForCRStatus).Should((Equal(current.Deploying)))
   177  				})
   178  
   179  				By("creating pvcs", func() {
   180  					Eventually(peer.PVCExists).Should((Equal(true)))
   181  					Expect(peer.getPVCStorageFromSpec(fmt.Sprintf("%s-pvc", peer.Name))).To(Equal("150Mi"))
   182  					Expect(peer.getPVCStorageFromSpec(fmt.Sprintf("%s-statedb-pvc", peer.Name))).To(Equal("1Gi"))
   183  				})
   184  
   185  				By("creating a service", func() {
   186  					Eventually(peer.ServiceExists).Should((Equal(true)))
   187  				})
   188  
   189  				By("creating a configmap", func() {
   190  					Eventually(peer.ConfigMapExists).Should((Equal(true)))
   191  				})
   192  
   193  				By("starting a ingress", func() {
   194  					Eventually(peer.IngressExists).Should((Equal(true)))
   195  				})
   196  
   197  				By("creating a deployment", func() {
   198  					Eventually(peer.DeploymentExists).Should((Equal(true)))
   199  				})
   200  
   201  				By("starting a pod", func() {
   202  					Eventually(peer.PodIsRunning).Should((Equal(true)))
   203  				})
   204  
   205  				By("creating config map that contains spec", func() {
   206  					Eventually(func() bool {
   207  						_, err := kclient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), peer.Name+"-spec", metav1.GetOptions{})
   208  						if err != nil {
   209  							return false
   210  						}
   211  						return true
   212  					}).Should(Equal(true))
   213  				})
   214  
   215  				By("setting the CR status to deployed when pod is running", func() {
   216  					Eventually(peer.pollForCRStatus).Should((Equal(current.Deployed)))
   217  				})
   218  
   219  				cm, err := kclient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), peer.Name+"-config", metav1.GetOptions{})
   220  				Expect(err).NotTo(HaveOccurred())
   221  
   222  				coreBytes := cm.BinaryData["core.yaml"]
   223  				core, err := config.ReadCoreFromBytes(coreBytes)
   224  				Expect(err).NotTo(HaveOccurred())
   225  
   226  				By("overriding peer section in core.yaml", func() {
   227  					configOverride, err := peer.CR.GetConfigOverride()
   228  					Expect(err).NotTo(HaveOccurred())
   229  					bytes, err := configOverride.(CoreConfig).ToBytes()
   230  					Expect(err).NotTo(HaveOccurred())
   231  					coreConfig := &config.Core{}
   232  					err = yaml.Unmarshal(bytes, coreConfig)
   233  					Expect(err).NotTo(HaveOccurred())
   234  					Expect(core.Peer.ID).To(Equal(coreConfig.Peer.ID))
   235  					Expect(string(coreBytes)).To(ContainSubstring("chaincode"))
   236  					Expect(string(coreBytes)).To(ContainSubstring("vm"))
   237  					Expect(string(coreBytes)).To(ContainSubstring("ledger"))
   238  					Expect(string(coreBytes)).To(ContainSubstring("operations"))
   239  					Expect(string(coreBytes)).To(ContainSubstring("metrics"))
   240  				})
   241  
   242  				By("overriding chaincode section in core.yaml", func() {
   243  					configOverride, err := peer.CR.GetConfigOverride()
   244  					Expect(err).NotTo(HaveOccurred())
   245  					bytes, err := configOverride.(CoreConfig).ToBytes()
   246  					Expect(err).NotTo(HaveOccurred())
   247  					coreConfig := &config.Core{}
   248  					err = yaml.Unmarshal(bytes, coreConfig)
   249  					Expect(err).NotTo(HaveOccurred())
   250  					Expect(core.Chaincode.StartupTimeout).To(Equal(coreConfig.Chaincode.StartupTimeout))
   251  					Expect(core.Chaincode.ExecuteTimeout).To(Equal(coreConfig.Chaincode.ExecuteTimeout))
   252  					Expect(core.Chaincode.InstallTimeout).To(Equal(coreConfig.Chaincode.InstallTimeout))
   253  				})
   254  
   255  				By("creating secrets contain DeliveryClient.AddressOverrides ca certs", func() {
   256  					Expect(core.Peer.DeliveryClient.AddressOverrides[0].CACertsFile).To(Equal("/orderer/certs/cert0.pem"))
   257  					Expect(core.Peer.DeliveryClient.AddressOverrides[1].CACertsFile).To(Equal("/orderer/certs/cert1.pem"))
   258  
   259  					s, err := kclient.CoreV1().Secrets(namespace).Get(context.TODO(), peer.Name+"-orderercacerts", metav1.GetOptions{})
   260  					Expect(err).NotTo(HaveOccurred())
   261  
   262  					data := s.Data
   263  					Expect(len(data)).To(Equal(2))
   264  
   265  					caCertBytes, err := base64.StdEncoding.DecodeString(caCert)
   266  					Expect(err).NotTo(HaveOccurred())
   267  
   268  					signCertBytes, err := base64.StdEncoding.DecodeString(signCert)
   269  					Expect(err).NotTo(HaveOccurred())
   270  
   271  					Expect(data["cert0.pem"]).To(Equal(caCertBytes))
   272  					Expect(data["cert1.pem"]).To(Equal(signCertBytes))
   273  				})
   274  
   275  				By("overriding operations section in core.yaml", func() {
   276  					configOverride, err := peer.CR.GetConfigOverride()
   277  					Expect(err).NotTo(HaveOccurred())
   278  					bytes, err := configOverride.(CoreConfig).ToBytes()
   279  					Expect(err).NotTo(HaveOccurred())
   280  					coreConfig := &config.Core{}
   281  					err = yaml.Unmarshal(bytes, coreConfig)
   282  					Expect(err).NotTo(HaveOccurred())
   283  					Expect(core.Operations.ListenAddress).To(Equal(coreConfig.Operations.ListenAddress))
   284  					Expect(core.Operations.TLS.Certificate).To(Equal(coreConfig.Operations.TLS.Certificate))
   285  				})
   286  
   287  				By("overriding metrics section in core.yaml", func() {
   288  					configOverride, err := peer.CR.GetConfigOverride()
   289  					Expect(err).NotTo(HaveOccurred())
   290  					bytes, err := configOverride.(CoreConfig).ToBytes()
   291  					Expect(err).NotTo(HaveOccurred())
   292  					coreConfig := &config.Core{}
   293  					err = yaml.Unmarshal(bytes, coreConfig)
   294  					Expect(err).NotTo(HaveOccurred())
   295  					Expect(core.Metrics.Statsd.Address).To(Equal(coreConfig.Metrics.Statsd.Address))
   296  				})
   297  			})
   298  
   299  			// TODO: Test marked as pending until portworx issue is resolved, currently zone is
   300  			// required to be passed for provisioning to work. Once portworx is working again, this
   301  			// test should be reenabled
   302  			PIt("should not find zone and region", func() {
   303  				// Wait for new deployment before querying deployment for updates
   304  				wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) {
   305  					dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{})
   306  					if dep != nil {
   307  						if dep.Status.UpdatedReplicas >= 1 && dep.Status.Conditions[0].Type == appsv1.DeploymentAvailable {
   308  							return true, nil
   309  						}
   310  					}
   311  					return false, nil
   312  				})
   313  
   314  				dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{})
   315  				Expect(err).NotTo(HaveOccurred())
   316  
   317  				By("checking zone", func() {
   318  					Expect(peer.TestAffinityZone(dep)).Should((Equal(false)))
   319  				})
   320  
   321  				By("checking region", func() {
   322  					Expect(peer.TestAffinityRegion(dep)).Should((Equal(false)))
   323  				})
   324  			})
   325  
   326  			When("the custom resource is updated", func() {
   327  				var (
   328  					dep                        *appsv1.Deployment
   329  					newResourceRequestsPeer    corev1.ResourceList
   330  					newResourceLimitsPeer      corev1.ResourceList
   331  					newResourceRequestsProxy   corev1.ResourceList
   332  					newResourceLimitsProxy     corev1.ResourceList
   333  					newResourceRequestsCouchdb corev1.ResourceList
   334  					newResourceLimitsCouchdb   corev1.ResourceList
   335  				)
   336  
   337  				BeforeEach(func() {
   338  					newResourceRequestsPeer = map[corev1.ResourceName]resource.Quantity{
   339  						corev1.ResourceCPU:              resource.MustParse("90m"),
   340  						corev1.ResourceMemory:           resource.MustParse("180M"),
   341  						corev1.ResourceEphemeralStorage: resource.MustParse("100M"),
   342  					}
   343  					newResourceLimitsPeer = map[corev1.ResourceName]resource.Quantity{
   344  						corev1.ResourceCPU:              resource.MustParse("90m"),
   345  						corev1.ResourceMemory:           resource.MustParse("180M"),
   346  						corev1.ResourceEphemeralStorage: resource.MustParse("1G"),
   347  					}
   348  
   349  					newResourceRequestsProxy = map[corev1.ResourceName]resource.Quantity{
   350  						corev1.ResourceCPU:              resource.MustParse("91m"),
   351  						corev1.ResourceMemory:           resource.MustParse("181M"),
   352  						corev1.ResourceEphemeralStorage: resource.MustParse("100M"),
   353  					}
   354  					newResourceLimitsProxy = map[corev1.ResourceName]resource.Quantity{
   355  						corev1.ResourceCPU:              resource.MustParse("91m"),
   356  						corev1.ResourceMemory:           resource.MustParse("181M"),
   357  						corev1.ResourceEphemeralStorage: resource.MustParse("1G"),
   358  					}
   359  
   360  					newResourceRequestsCouchdb = map[corev1.ResourceName]resource.Quantity{
   361  						corev1.ResourceCPU:              resource.MustParse("193m"),
   362  						corev1.ResourceMemory:           resource.MustParse("383M"),
   363  						corev1.ResourceEphemeralStorage: resource.MustParse("100M"),
   364  					}
   365  					newResourceLimitsCouchdb = map[corev1.ResourceName]resource.Quantity{
   366  						corev1.ResourceCPU:              resource.MustParse("193m"),
   367  						corev1.ResourceMemory:           resource.MustParse("383M"),
   368  						corev1.ResourceEphemeralStorage: resource.MustParse("1G"),
   369  					}
   370  
   371  					peer.CR.Spec.Resources = &current.PeerResources{
   372  						Peer: &corev1.ResourceRequirements{
   373  							Requests: newResourceRequestsPeer,
   374  							Limits:   newResourceLimitsPeer,
   375  						},
   376  						GRPCProxy: &corev1.ResourceRequirements{
   377  							Requests: newResourceRequestsProxy,
   378  							Limits:   newResourceLimitsProxy,
   379  						},
   380  						CouchDB: &corev1.ResourceRequirements{
   381  							Requests: newResourceRequestsCouchdb,
   382  							Limits:   newResourceLimitsCouchdb,
   383  						},
   384  					}
   385  
   386  					startupTimeout, err := common.ParseDuration("200s")
   387  					Expect(err).NotTo(HaveOccurred())
   388  
   389  					configOverride := config.Core{
   390  						Core: v2.Core{
   391  							Peer: v2.Peer{
   392  								ID: "new-peerid",
   393  							},
   394  							Chaincode: v2.Chaincode{
   395  								StartupTimeout: startupTimeout,
   396  							},
   397  						},
   398  					}
   399  
   400  					configBytes, err := json.Marshal(configOverride)
   401  					Expect(err).NotTo(HaveOccurred())
   402  
   403  					peer.CR.Spec.ConfigOverride = &runtime.RawExtension{Raw: configBytes}
   404  
   405  					Eventually(peer.DeploymentExists).Should((Equal(true)))
   406  					dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{})
   407  					Expect(err).NotTo(HaveOccurred())
   408  				})
   409  
   410  				It("updates the instance of IBPPeer if resources and config overrides are updated in CR", func() {
   411  					peerResources := dep.Spec.Template.Spec.Containers[0].Resources
   412  					Expect(peerResources.Requests).To(Equal(defaultRequestsPeer))
   413  					Expect(peerResources.Limits).To(Equal(defaultLimitsPeer))
   414  
   415  					proxyResources := dep.Spec.Template.Spec.Containers[1].Resources
   416  					Expect(proxyResources.Requests).To(Equal(defaultRequestsProxy))
   417  					Expect(proxyResources.Limits).To(Equal(defaultLimitsProxy))
   418  
   419  					couchDBResources := dep.Spec.Template.Spec.Containers[2].Resources
   420  					Expect(couchDBResources.Requests).To(Equal(defaultRequestsCouchdb))
   421  					Expect(couchDBResources.Limits).To(Equal(defaultLimitsCouchdb))
   422  
   423  					bytes, err := json.Marshal(peer.CR)
   424  					Expect(err).NotTo(HaveOccurred())
   425  
   426  					result := ibpCRClient.Patch(types.MergePatchType).Namespace(namespace).Resource("ibppeers").Name(peer.Name).Body(bytes).Do(context.TODO())
   427  					Expect(result.Error()).NotTo(HaveOccurred())
   428  
   429  					// Wait for new deployment before querying deployment for updates
   430  					wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) {
   431  						dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{})
   432  						if dep != nil {
   433  							if dep.Status.UpdatedReplicas >= 1 && dep.Status.Conditions[0].Type == appsv1.DeploymentAvailable {
   434  								if dep.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu().MilliValue() == newResourceRequestsProxy.Cpu().MilliValue() {
   435  									return true, nil
   436  								}
   437  							}
   438  						}
   439  						return false, nil
   440  					})
   441  
   442  					dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{})
   443  					Expect(err).NotTo(HaveOccurred())
   444  
   445  					updatedPeerResources := dep.Spec.Template.Spec.Containers[0].Resources
   446  					Expect(updatedPeerResources.Requests).To(Equal(newResourceRequestsPeer))
   447  					Expect(updatedPeerResources.Limits).To(Equal(newResourceLimitsPeer))
   448  
   449  					updatedProxyResources := dep.Spec.Template.Spec.Containers[1].Resources
   450  					Expect(updatedProxyResources.Requests).To(Equal(newResourceRequestsProxy))
   451  					Expect(updatedProxyResources.Limits).To(Equal(newResourceLimitsProxy))
   452  
   453  					updatedCouchDBResources := dep.Spec.Template.Spec.Containers[2].Resources
   454  					Expect(updatedCouchDBResources.Requests).To(Equal(newResourceRequestsCouchdb))
   455  					Expect(updatedCouchDBResources.Limits).To(Equal(newResourceLimitsCouchdb))
   456  
   457  					By("updating the config map with new values from override", func() {
   458  						core := &config.Core{}
   459  
   460  						Eventually(func() string {
   461  							cm, err := kclient.CoreV1().ConfigMaps(namespace).Get(context.TODO(), peer.Name+"-config", metav1.GetOptions{})
   462  							Expect(err).NotTo(HaveOccurred())
   463  
   464  							coreBytes := cm.BinaryData["core.yaml"]
   465  							core, err = config.ReadCoreFromBytes(coreBytes)
   466  							Expect(err).NotTo(HaveOccurred())
   467  
   468  							return core.Peer.ID
   469  						}).Should(Equal("new-peerid"))
   470  
   471  						configOverride, err := peer.CR.GetConfigOverride()
   472  						Expect(err).NotTo(HaveOccurred())
   473  
   474  						bytes, err := configOverride.(CoreConfig).ToBytes()
   475  						Expect(err).NotTo(HaveOccurred())
   476  
   477  						coreConfig := &config.Core{}
   478  						err = yaml.Unmarshal(bytes, coreConfig)
   479  						Expect(err).NotTo(HaveOccurred())
   480  						Expect(core.Chaincode.StartupTimeout).To(Equal(coreConfig.Chaincode.StartupTimeout))
   481  					})
   482  				})
   483  			})
   484  
   485  			When("a deployment managed by operator is manually edited", func() {
   486  				var (
   487  					err error
   488  					dep *appsv1.Deployment
   489  				)
   490  
   491  				BeforeEach(func() {
   492  					Eventually(func() bool {
   493  						dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{})
   494  						if err == nil && dep != nil {
   495  							return true
   496  						}
   497  						return false
   498  					}).Should(Equal(true))
   499  				})
   500  
   501  				It("restores states", func() {
   502  					origRequests := dep.Spec.Template.Spec.Containers[0].Resources.Requests
   503  
   504  					dep.Spec.Template.Spec.Containers[0].Resources.Requests = corev1.ResourceList{
   505  						corev1.ResourceCPU:    resource.MustParse("107m"),
   506  						corev1.ResourceMemory: resource.MustParse("207M"),
   507  					}
   508  
   509  					depBytes, err := json.Marshal(dep)
   510  					Expect(err).NotTo(HaveOccurred())
   511  
   512  					kclient.AppsV1().Deployments(namespace).Patch(context.TODO(), peer.Name, types.MergePatchType, depBytes, metav1.PatchOptions{})
   513  					// Wait for new deployment before querying deployment for updates
   514  					wait.Poll(500*time.Millisecond, 300*time.Second, func() (bool, error) {
   515  						dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{})
   516  						if dep != nil {
   517  							if len(dep.Spec.Template.Spec.Containers) >= 1 {
   518  								if dep.Spec.Template.Spec.Containers[0].Resources.Requests.Cpu().MilliValue() == origRequests.Cpu().MilliValue() {
   519  									return true, nil
   520  								}
   521  							}
   522  						}
   523  						return false, nil
   524  					})
   525  
   526  					dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer.Name, metav1.GetOptions{})
   527  					Expect(err).NotTo(HaveOccurred())
   528  
   529  					Expect(dep.Spec.Template.Spec.Containers[0].Resources.Requests).To(Equal(origRequests))
   530  				})
   531  			})
   532  
   533  			When("admin certs are updated in peer spec", func() {
   534  				It("updates the admin cert secret", func() {
   535  					sec, err := kclient.CoreV1().Secrets(namespace).Get(context.TODO(), "ecert-ibppeer1-admincerts", metav1.GetOptions{})
   536  					Expect(err).NotTo(HaveOccurred())
   537  
   538  					certBytes := sec.Data["admincert-0.pem"]
   539  					certBase64 := base64.StdEncoding.EncodeToString(certBytes)
   540  					Expect(certBase64).To(Equal(adminCert))
   541  
   542  					peer.CR.Spec.Secret.MSP.Component.AdminCerts = []string{signCert}
   543  					bytes, err := json.Marshal(peer.CR)
   544  					Expect(err).NotTo(HaveOccurred())
   545  
   546  					result := ibpCRClient.Patch(types.MergePatchType).Namespace(namespace).Resource("ibppeers").Name(peer.Name).Body(bytes).Do(context.TODO())
   547  					Expect(result.Error()).NotTo(HaveOccurred())
   548  
   549  					Eventually(peer.checkAdminCertUpdate).Should(Equal(signCert))
   550  				})
   551  			})
   552  		})
   553  
   554  		When("applying the second instance of IBPPeer CR", func() {
   555  			var (
   556  				err error
   557  				dep *appsv1.Deployment
   558  			)
   559  
   560  			It("creates a second IBPPeer custom resource", func() {
   561  				By("starting a pod", func() {
   562  					Eventually(peer2.PodIsRunning).Should((Equal(true)))
   563  				})
   564  			})
   565  
   566  			// TODO: Test marked as pending until portworx issue is resolved, currently zone is
   567  			// required to be passed for provisioning to work. Once portworx is working again, this
   568  			// test should be reenabled
   569  			PIt("should find zone and region", func() {
   570  				// Wait for new deployment before querying deployment for updates
   571  				wait.Poll(500*time.Millisecond, 60*time.Second, func() (bool, error) {
   572  					dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer2.Name, metav1.GetOptions{})
   573  					if dep != nil {
   574  						if dep.Status.UpdatedReplicas >= 1 && dep.Status.Conditions[0].Type == appsv1.DeploymentAvailable {
   575  							return true, nil
   576  						}
   577  					}
   578  					return false, nil
   579  				})
   580  
   581  				dep, err = kclient.AppsV1().Deployments(namespace).Get(context.TODO(), peer2.Name, metav1.GetOptions{})
   582  				Expect(err).NotTo(HaveOccurred())
   583  
   584  				By("checking zone", func() {
   585  					Expect(peer2.TestAffinityZone(dep)).To((Equal(true)))
   586  				})
   587  
   588  				By("checking region", func() {
   589  					Expect(peer2.TestAffinityRegion(dep)).To((Equal(true)))
   590  				})
   591  			})
   592  		})
   593  
   594  		Context("operator pod restart", func() {
   595  			var (
   596  				oldPodName string
   597  			)
   598  
   599  			Context("should not trigger deployment restart if config overrides not updated", func() {
   600  				BeforeEach(func() {
   601  					Eventually(peer.PodIsRunning).Should((Equal(true)))
   602  
   603  					Eventually(func() int { return len(peer.GetRunningPods()) }).Should(Equal(1))
   604  					oldPodName = peer.GetRunningPods()[0].Name
   605  				})
   606  
   607  				It("does not restart the peer pod", func() {
   608  					Eventually(peer.PodIsRunning).Should((Equal(true)))
   609  
   610  					Eventually(func() bool {
   611  						pods := peer.GetRunningPods()
   612  						if len(pods) != 1 {
   613  							return false
   614  						}
   615  
   616  						newPodName := pods[0].Name
   617  						if newPodName == oldPodName {
   618  							return true
   619  						}
   620  
   621  						return false
   622  					}).Should(Equal(true))
   623  				})
   624  			})
   625  
   626  			PContext("should trigger deployment restart if config overrides are updated", func() {
   627  				BeforeEach(func() {
   628  					Eventually(peer.PodIsRunning).Should((Equal(true)))
   629  					Eventually(func() int {
   630  						return len(peer.GetPods())
   631  					}).Should(Equal(1))
   632  
   633  					configOverride := config.Core{
   634  						Core: v2.Core{
   635  							Peer: v2.Peer{
   636  								ID: "new-id",
   637  							},
   638  						},
   639  					}
   640  
   641  					configBytes, err := json.Marshal(configOverride)
   642  					Expect(err).NotTo(HaveOccurred())
   643  
   644  					peer.CR.Spec.ConfigOverride = &runtime.RawExtension{Raw: configBytes}
   645  
   646  					bytes, err := json.Marshal(peer.CR)
   647  					Expect(err).NotTo(HaveOccurred())
   648  
   649  					result := ibpCRClient.Patch(types.MergePatchType).Namespace(namespace).Resource("ibppeers").Name(peer.Name).Body(bytes).Do(context.TODO())
   650  					Expect(result.Error()).NotTo(HaveOccurred())
   651  				})
   652  
   653  				It("restarts the peer pod", func() {
   654  					Eventually(peer.PodIsRunning).Should((Equal(false)))
   655  					Eventually(peer.PodIsRunning).Should((Equal(true)))
   656  
   657  					Eventually(func() bool {
   658  						pods := peer.GetPods()
   659  						if len(pods) != 1 {
   660  							return false
   661  						}
   662  
   663  						newPodName := pods[0].Name
   664  						if newPodName == oldPodName {
   665  							return false
   666  						}
   667  
   668  						return true
   669  					}).Should(Equal(true))
   670  				})
   671  			})
   672  		})
   673  
   674  		When("applying incorrectly configured third instance of IBPPeer CR", func() {
   675  			It("should set the CR status to error", func() {
   676  				Eventually(peer3.pollForCRStatus).Should((Equal(current.Error)))
   677  
   678  				crStatus := &current.IBPPeer{}
   679  				result := ibpCRClient.Get().Namespace(namespace).Resource("ibppeers").Name(peer3.Name).Do(context.TODO())
   680  				result.Into(crStatus)
   681  
   682  				Expect(crStatus.Status.Message).To(ContainSubstring("user must accept license before continuing"))
   683  			})
   684  		})
   685  
   686  		Context("delete crs", func() {
   687  			It("should delete IBPPeer CR", func() {
   688  				By("deleting the first instance of IBPPeer CR", func() {
   689  					result := ibpCRClient.Delete().Namespace(namespace).Resource("ibppeers").Name(peer.Name).Do(context.TODO())
   690  					Expect(result.Error()).NotTo(HaveOccurred())
   691  				})
   692  
   693  				By("deleting the second instance of IBPPeer CR", func() {
   694  					result := ibpCRClient.Delete().Namespace(namespace).Resource("ibppeers").Name(peer2.Name).Do(context.TODO())
   695  					Expect(result.Error()).NotTo(HaveOccurred())
   696  				})
   697  
   698  				By("deleting the third instance of IBPPeer CR", func() {
   699  					result := ibpCRClient.Delete().Namespace(namespace).Resource("ibppeers").Name(peer3.Name).Do(context.TODO())
   700  					Expect(result.Error()).NotTo(HaveOccurred())
   701  				})
   702  			})
   703  		})
   704  	})
   705  })
   706  
   707  func GetPeer1() *Peer {
   708  	startupTimeout, err := common.ParseDuration("200s")
   709  	Expect(err).NotTo(HaveOccurred())
   710  	executeTimeout, err := common.ParseDuration("20s")
   711  	Expect(err).NotTo(HaveOccurred())
   712  	installTimeout, err := common.ParseDuration("600s")
   713  	Expect(err).NotTo(HaveOccurred())
   714  
   715  	configOverride := config.Core{
   716  		Core: v2.Core{
   717  			Peer: v2.Peer{
   718  				ID: "testPeerID",
   719  				DeliveryClient: v1.DeliveryClient{
   720  					AddressOverrides: []v1.AddressOverride{
   721  						v1.AddressOverride{
   722  							CACertsFile: caCert,
   723  						},
   724  						v1.AddressOverride{
   725  							CACertsFile: signCert,
   726  						},
   727  					},
   728  				},
   729  			},
   730  			Chaincode: v2.Chaincode{
   731  				StartupTimeout: startupTimeout,
   732  				ExecuteTimeout: executeTimeout,
   733  				InstallTimeout: installTimeout,
   734  			},
   735  			Metrics: v1.Metrics{
   736  				Statsd: v1.Statsd{
   737  					Address: "127.0.0.1:9445",
   738  				},
   739  			},
   740  			Operations: v1.Operations{
   741  				ListenAddress: "127.0.0.1:9444",
   742  				TLS: v1.OperationsTLS{
   743  					Certificate: v1.File{
   744  						File: "ops-tls-cert.pem",
   745  					},
   746  				},
   747  			},
   748  		},
   749  	}
   750  
   751  	configBytes, err := json.Marshal(configOverride)
   752  	Expect(err).NotTo(HaveOccurred())
   753  
   754  	name := "ibppeer1"
   755  	cr := &current.IBPPeer{
   756  		TypeMeta: metav1.TypeMeta{
   757  			Kind:       "IBPPeer",
   758  			APIVersion: "ibp.com/v1beta1",
   759  		},
   760  		Spec: current.IBPPeerSpec{
   761  			License: current.License{
   762  				Accept: true,
   763  			},
   764  			MSPID:            "test-peer-mspid",
   765  			ImagePullSecrets: []string{"regcred"},
   766  			Region:           "select",
   767  			Zone:             "select",
   768  			Images: &current.PeerImages{
   769  				CouchDBImage:  integration.CouchdbImage,
   770  				CouchDBTag:    integration.CouchdbTag,
   771  				GRPCWebImage:  integration.GrpcwebImage,
   772  				GRPCWebTag:    integration.GrpcwebTag,
   773  				PeerImage:     integration.PeerImage,
   774  				PeerTag:       integration.PeerTag,
   775  				PeerInitImage: integration.InitImage,
   776  				PeerInitTag:   integration.InitTag,
   777  			},
   778  			Domain: integration.TestAutomation1IngressDomain,
   779  			Resources: &current.PeerResources{
   780  				DinD: &corev1.ResourceRequirements{
   781  
   782  					Requests: defaultRequestsDind,
   783  					Limits:   defaultLimitsDind,
   784  				},
   785  				Peer: &corev1.ResourceRequirements{
   786  					Requests: defaultRequestsPeer,
   787  					Limits:   defaultLimitsPeer,
   788  				},
   789  				GRPCProxy: &corev1.ResourceRequirements{
   790  					Requests: defaultRequestsProxy,
   791  					Limits:   defaultLimitsProxy,
   792  				},
   793  				FluentD: &corev1.ResourceRequirements{
   794  					Requests: defaultRequestsFluentd,
   795  					Limits:   defaultLimitsFluentd,
   796  				},
   797  				CouchDB: &corev1.ResourceRequirements{
   798  					Requests: defaultRequestsCouchdb,
   799  					Limits:   defaultLimitsCouchdb,
   800  				},
   801  			},
   802  			Storage: &current.PeerStorages{
   803  				Peer: &current.StorageSpec{
   804  					Size: "150Mi",
   805  				},
   806  				StateDB: &current.StorageSpec{
   807  					Size: "1Gi",
   808  				},
   809  			},
   810  			Ingress: current.Ingress{
   811  				TlsSecretName: "tlssecret",
   812  			},
   813  			Secret: &current.SecretSpec{
   814  				MSP: testMSPSpec,
   815  			},
   816  			ConfigOverride: &runtime.RawExtension{Raw: configBytes},
   817  			DisableNodeOU:  pointer.Bool(true),
   818  			FabricVersion:  integration.FabricVersion + "-1",
   819  		},
   820  	}
   821  	cr.Name = name
   822  
   823  	return &Peer{
   824  		Name: name,
   825  		CR:   cr,
   826  		NativeResourcePoller: integration.NativeResourcePoller{
   827  			Name:      name,
   828  			Namespace: namespace,
   829  			Client:    kclient,
   830  		},
   831  	}
   832  }
   833  
   834  func GetPeer2() *Peer {
   835  	name := "ibppeer2"
   836  	cr := &current.IBPPeer{
   837  		Spec: current.IBPPeerSpec{
   838  			License: current.License{
   839  				Accept: true,
   840  			},
   841  			MSPID:            "test-peer2-mspid",
   842  			StateDb:          "leveldb",
   843  			Region:           "select",
   844  			Zone:             "select",
   845  			ImagePullSecrets: []string{"regcred"},
   846  			Images: &current.PeerImages{
   847  				CouchDBImage:  integration.CouchdbImage,
   848  				CouchDBTag:    integration.CouchdbTag,
   849  				GRPCWebImage:  integration.GrpcwebImage,
   850  				GRPCWebTag:    integration.GrpcwebTag,
   851  				PeerImage:     integration.PeerImage,
   852  				PeerTag:       integration.PeerTag,
   853  				PeerInitImage: integration.InitImage,
   854  				PeerInitTag:   integration.InitTag,
   855  			},
   856  			Domain: integration.TestAutomation1IngressDomain,
   857  			Storage: &current.PeerStorages{
   858  				Peer: &current.StorageSpec{
   859  					Size: "150Mi",
   860  				},
   861  				StateDB: &current.StorageSpec{
   862  					Size: "1Gi",
   863  				},
   864  			},
   865  			Ingress: current.Ingress{
   866  				TlsSecretName: "tlssecret",
   867  			},
   868  			Secret: &current.SecretSpec{
   869  				MSP: testMSPSpec,
   870  			},
   871  			DisableNodeOU: pointer.Bool(true),
   872  			FabricVersion: integration.FabricVersion + "-1",
   873  		},
   874  	}
   875  	cr.Name = name
   876  
   877  	return &Peer{
   878  		Name: name,
   879  		CR:   cr,
   880  		NativeResourcePoller: integration.NativeResourcePoller{
   881  			Name:      name,
   882  			Namespace: namespace,
   883  			Client:    kclient,
   884  		},
   885  	}
   886  }
   887  
   888  func GetPeer3() *Peer {
   889  	name := "ibppeer3"
   890  	cr := &current.IBPPeer{
   891  		Spec: current.IBPPeerSpec{
   892  			Domain:        integration.TestAutomation1IngressDomain,
   893  			FabricVersion: integration.FabricVersion + "-1",
   894  		},
   895  	}
   896  	cr.Name = name
   897  
   898  	return &Peer{
   899  		Name: name,
   900  		CR:   cr,
   901  		NativeResourcePoller: integration.NativeResourcePoller{
   902  			Name:      name,
   903  			Namespace: namespace,
   904  			Client:    kclient,
   905  		},
   906  	}
   907  }