diff --git a/.github/workflows/endtoend-tests.yaml b/.github/workflows/endtoend-tests.yaml index ba39aa45..d250b196 100644 --- a/.github/workflows/endtoend-tests.yaml +++ b/.github/workflows/endtoend-tests.yaml @@ -31,4 +31,8 @@ jobs: - uses: actions/checkout@v3 - name: Sample Network E2E Test - run: sample-network/scripts/run-e2e-test.sh || cat sample-network/network-debug.log + run: sample-network/scripts/run-e2e-test.sh + + - name: Show Debug Log + if: ${{ always() }} + run: cat sample-network/network-debug.log diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index 80c2ed84..01420788 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -39,8 +39,7 @@ jobs: go-version: ${{ env.GO_VER }} - name: license header checks run: scripts/check-license.sh - # TODO: run in hyperledger-labs - # - name: gosec - # run: scripts/go-sec.sh + - name: gosec + run: scripts/go-sec.sh - name: run tests run: make test diff --git a/pkg/offering/base/orderer/node.go b/pkg/offering/base/orderer/node.go index 623de0bb..aad23746 100644 --- a/pkg/offering/base/orderer/node.go +++ b/pkg/offering/base/orderer/node.go @@ -64,7 +64,8 @@ import ( ) const ( - NODE = "node" + NODE = "node" + DaysToSecondsConversion = int64(24 * 60 * 60) ) type Override interface { @@ -1716,5 +1717,224 @@ func (n *Node) HandleRestart(instance *current.IBPOrderer, update Update) error func (n *Node) CustomLogic(instance *current.IBPOrderer, update Update) (*current.CRStatus, *common.Result, error) { var status *current.CRStatus var err error + if !n.CanSetCertificateTimer(instance, update) { + log.Info("Certificate update detected but all nodes not yet deployed, requeuing request...") + return status, &common.Result{ + Result: reconcile.Result{ + Requeue: true, + }, + }, nil + } + + // Check if crypto needs to be backed up before an update overrides exisitng secrets + if update.CryptoBackupNeeded() { + log.Info("Performing backup of TLS and ecert crypto") + err = common.BackupCrypto(n.Client, n.Scheme, instance, n.GetLabels(instance)) + if err != nil { + return status, nil, errors.Wrap(err, "failed to backup TLS and ecert crypto") + } + } + + status, err = n.CheckCertificates(instance) + if err != nil { + return status, nil, errors.Wrap(err, "failed to check for expiring certificates") + } + + if update.CertificateCreated() { + log.Info(fmt.Sprintf("%s certificate was created, setting timer for certificate renewal", update.GetCreatedCertType())) + err = n.SetCertificateTimer(instance, update.GetCreatedCertType()) + if err != nil { + return status, nil, errors.Wrap(err, "failed to set timer for certificate renewal") + } + } + + if update.EcertUpdated() { + log.Info("Ecert was updated, setting timer for certificate renewal") + err = n.SetCertificateTimer(instance, commoninit.ECERT) + if err != nil { + return status, nil, errors.Wrap(err, "failed to set timer for certificate renewal") + } + } + + if update.TLSCertUpdated() { + log.Info("TLS cert was updated, setting timer for certificate renewal") + err = n.SetCertificateTimer(instance, commoninit.TLS) + if err != nil { + return status, nil, errors.Wrap(err, "failed to set timer for certificate renewal") + } + } return status, nil, err + +} + +func (n *Node) CheckCertificates(instance *current.IBPOrderer) (*current.CRStatus, error) { + numSecondsBeforeExpire := instance.Spec.GetNumSecondsWarningPeriod() + statusType, message, err := n.CertificateManager.CheckCertificatesForExpire(instance, numSecondsBeforeExpire) + if err != nil { + return nil, err + } + + crStatus := ¤t.CRStatus{ + Type: statusType, + Message: message, + } + + switch statusType { + case current.Deployed: + crStatus.Reason = "allPodsRunning" + if message == "" { + crStatus.Message = "allPodsRunning" + } + default: + crStatus.Reason = "certRenewalRequired" + } + + return crStatus, nil +} + +func (n *Node) SetCertificateTimer(instance *current.IBPOrderer, certType commoninit.SecretType) error { + certName := fmt.Sprintf("%s-%s-signcert", certType, instance.Name) + numSecondsBeforeExpire := instance.Spec.GetNumSecondsWarningPeriod() + duration, err := n.CertificateManager.GetDurationToNextRenewal(certType, instance, numSecondsBeforeExpire) + if err != nil { + return err + } + + log.Info((fmt.Sprintf("Creating timer to renew %s %d days before it expires", certName, int(numSecondsBeforeExpire/DaysToSecondsConversion)))) + + if n.RenewCertTimers[certName] != nil { + n.RenewCertTimers[certName].Stop() + n.RenewCertTimers[certName] = nil + } + n.RenewCertTimers[certName] = time.AfterFunc(duration, func() { + // Check certs for updated status & set status so that reconcile is triggered after cert renewal. Reconcile loop will handle + // checking certs again to determine whether instance status can return to Deployed + err := n.UpdateCRStatus(instance) + if err != nil { + log.Error(err, "failed to update CR status") + } + + // get instance + instanceLatest := ¤t.IBPOrderer{} + err = n.Client.Get(context.TODO(), types.NamespacedName{Namespace: instance.Namespace, Name: instance.Name}, instanceLatest) + if err != nil { + log.Error(err, "failed to get latest instance") + return + } + + // Orderer TLS certs can be auto-renewed for 1.4.9+ or 2.2.1+ orderers + if certType == commoninit.TLS { + // if renewal is disabled + if n.Config.Operator.Orderer.Renewals.DisableTLScert { + log.Info(fmt.Sprintf("%s cannot be auto-renewed because orderer tls renewal is disabled", certName)) + return + } + switch version.GetMajorReleaseVersion(instanceLatest.Spec.FabricVersion) { + case version.V2: + if version.String(instanceLatest.Spec.FabricVersion).LessThan("2.2.1") { + log.Info(fmt.Sprintf("%s cannot be auto-renewed because v2 orderer is less than 2.2.1, force renewal required", certName)) + return + } + case version.V1: + if version.String(instanceLatest.Spec.FabricVersion).LessThan("1.4.9") { + log.Info(fmt.Sprintf("%s cannot be auto-renewed because v1.4 orderer less than 1.4.9, force renewal required", certName)) + return + } + default: + log.Info(fmt.Sprintf("%s cannot be auto-renewed, force renewal required", certName)) + return + } + } + + err = common.BackupCrypto(n.Client, n.Scheme, instance, n.GetLabels(instance)) + if err != nil { + log.Error(err, "failed to backup crypto before renewing cert") + return + } + + err = n.RenewCert(certType, instanceLatest, false) + if err != nil { + log.Info(fmt.Sprintf("Failed to renew %s certificate: %s, status of %s remaining in Warning phase", certType, err, instanceLatest.GetName())) + return + } + log.Info(fmt.Sprintf("%s renewal complete", certName)) + }) + + return nil +} + +// NOTE: This is called by the timer's subroutine when it goes off, not during a reconcile loop. +// Therefore, it won't be overriden by the "SetStatus" method in ibporderer_controller.go +func (n *Node) UpdateCRStatus(instance *current.IBPOrderer) error { + status, err := n.CheckCertificates(instance) + if err != nil { + return errors.Wrap(err, "failed to check certificates") + } + + // Get most up-to-date instance at the time of update + updatedInstance := ¤t.IBPOrderer{} + err = n.Client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, updatedInstance) + if err != nil { + return errors.Wrap(err, "failed to get new instance") + } + + // Don't trigger reconcile if status remaining the same + if updatedInstance.Status.Type == status.Type && updatedInstance.Status.Reason == status.Reason && updatedInstance.Status.Message == status.Message { + return nil + } + + updatedInstance.Status.Type = status.Type + updatedInstance.Status.Reason = status.Reason + updatedInstance.Status.Message = status.Message + updatedInstance.Status.Status = current.True + updatedInstance.Status.LastHeartbeatTime = time.Now().String() + + log.Info(fmt.Sprintf("Updating status of IBPOrderer node %s to %s phase", instance.Name, status.Type)) + err = n.Client.UpdateStatus(context.TODO(), updatedInstance) + if err != nil { + return errors.Wrapf(err, "failed to update status to %s phase", status.Type) + } + + return nil +} + +// This function checks whether the parent orderer node (if parent exists) or node itself is in +// Deployed or Warning state. We don't want to set a timer to renew certifictes before all nodes +// are Deployed as a certificate renewal updates the parent status to Warning while renewing. +func (n *Node) CanSetCertificateTimer(instance *current.IBPOrderer, update Update) bool { + if update.CertificateCreated() || update.CertificateUpdated() { + parentName := instance.Labels["parent"] + if parentName == "" { + // If parent not found, check individual node + if !(instance.Status.Type == current.Deployed || instance.Status.Type == current.Warning) { + log.Info(fmt.Sprintf("%s has no parent, node not yet deployed", instance.Name)) + return false + } else { + log.Info(fmt.Sprintf("%s has no parent, node is deployed", instance.Name)) + return true + } + } + + nn := types.NamespacedName{ + Name: parentName, + Namespace: instance.GetNamespace(), + } + + parentInstance := ¤t.IBPOrderer{} + err := n.Client.Get(context.TODO(), nn, parentInstance) + if err != nil { + log.Error(err, fmt.Sprintf("%s parent not found", instance.Name)) + return false + } + + // If parent not yet deployed, but cert update detected, then prevent timer from being set until parent + // (and subequently all child nodes) are deployed + if !(parentInstance.Status.Type == current.Deployed || parentInstance.Status.Type == current.Warning) { + log.Info(fmt.Sprintf("%s has parent, parent not yet deployed", instance.Name)) + return false + } + } + + log.Info(fmt.Sprintf("%s has parent, parent is deployed", instance.Name)) + return true } diff --git a/pkg/offering/base/orderer/node_test.go b/pkg/offering/base/orderer/node_test.go index 5200c7ed..f8c6bfc8 100644 --- a/pkg/offering/base/orderer/node_test.go +++ b/pkg/offering/base/orderer/node_test.go @@ -20,11 +20,20 @@ package baseorderer_test import ( "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" "encoding/json" + "encoding/pem" "fmt" + "math/big" "os" + "strings" "time" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" cmocks "github.com/IBM-Blockchain/fabric-operator/controllers/mocks" config "github.com/IBM-Blockchain/fabric-operator/operatorconfig" @@ -32,6 +41,7 @@ import ( "github.com/IBM-Blockchain/fabric-operator/pkg/apis/deployer" v1 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/orderer/v1" v2 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/orderer/v2" + "github.com/IBM-Blockchain/fabric-operator/pkg/certificate" commonconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config" "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/mspparser" ordererinit "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/orderer" @@ -48,6 +58,7 @@ import ( "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/controller-runtime/pkg/client" @@ -545,6 +556,355 @@ var _ = Describe("Base Orderer Node", func() { }) }) + Context("check certificates", func() { + It("returns error if fails to get certificate expiry info", func() { + certificateMgr.CheckCertificatesForExpireReturns("", "", errors.New("cert expiry error")) + _, err := node.CheckCertificates(instance) + Expect(err).To(HaveOccurred()) + }) + + It("sets cr status with certificate expiry info", func() { + certificateMgr.CheckCertificatesForExpireReturns(current.Warning, "cert renewal required", nil) + status, err := node.CheckCertificates(instance) + Expect(err).NotTo(HaveOccurred()) + Expect(status.Type).To(Equal(current.Warning)) + Expect(status.Message).To(Equal("cert renewal required")) + }) + }) + + Context("set certificate timer", func() { + BeforeEach(func() { + mockKubeClient.GetStub = func(ctx context.Context, types types.NamespacedName, obj client.Object) error { + switch obj.(type) { + case *current.IBPOrderer: + o := obj.(*current.IBPOrderer) + o.Kind = "IBPOrderer" + o.Name = "orderer1" + o.Namespace = "random" + o.Spec.Secret = ¤t.SecretSpec{ + Enrollment: ¤t.EnrollmentSpec{ + TLS: ¤t.Enrollment{ + EnrollID: "enrollID", + }, + }, + } + o.Status.Type = current.Deployed + case *corev1.Secret: + o := obj.(*corev1.Secret) + if strings.Contains(o.Name, "crypto-backup") { + return k8serrors.NewNotFound(schema.GroupResource{}, "not found") + } + } + return nil + } + + instance.Spec.Secret = ¤t.SecretSpec{ + Enrollment: ¤t.EnrollmentSpec{ + Component: ¤t.Enrollment{ + EnrollID: "enrollID", + }, + }, + } + }) + + Context("sets timer to renew tls certificate", func() { + BeforeEach(func() { + certificateMgr.GetDurationToNextRenewalReturns(time.Duration(3*time.Second), nil) + }) + + It("does not renew certificate if disabled in config", func() { + instance.Spec.FabricVersion = "1.4.9" + node.Config.Operator.Orderer.Renewals.DisableTLScert = true + err := node.SetCertificateTimer(instance, "tls") + Expect(err).NotTo(HaveOccurred()) + Expect(node.RenewCertTimers["tls-orderer1-signcert"]).NotTo(BeNil()) + + By("not renewing certificate", func() { + Eventually(func() bool { + return mockKubeClient.UpdateStatusCallCount() == 1 && + certificateMgr.RenewCertCallCount() == 0 + }, time.Duration(5*time.Second)).Should(Equal(true)) + + // timer.Stop() == false means that it already fired + Expect(node.RenewCertTimers["tls-orderer1-signcert"].Stop()).To(Equal(false)) + }) + }) + + It("does not renew certificate if fabric version is less than 1.4.9 or 2.2.1", func() { + instance.Spec.FabricVersion = "1.4.7" + err := node.SetCertificateTimer(instance, "tls") + Expect(err).NotTo(HaveOccurred()) + Expect(node.RenewCertTimers["tls-orderer1-signcert"]).NotTo(BeNil()) + + By("not renewing certificate", func() { + Eventually(func() bool { + return mockKubeClient.UpdateStatusCallCount() == 1 && + certificateMgr.RenewCertCallCount() == 0 + }, time.Duration(5*time.Second)).Should(Equal(true)) + + // timer.Stop() == false means that it already fired + Expect(node.RenewCertTimers["tls-orderer1-signcert"].Stop()).To(Equal(false)) + }) + }) + + It("renews certificate if fabric version is greater than or equal to 1.4.9 or 2.2.1", func() { + instance.Spec.FabricVersion = "2.2.1" + mockKubeClient.GetStub = func(ctx context.Context, types types.NamespacedName, obj client.Object) error { + switch obj.(type) { + case *current.IBPOrderer: + o := obj.(*current.IBPOrderer) + o.Kind = "IBPOrderer" + o.Name = "orderer1" + o.Namespace = "random" + o.Spec.Secret = ¤t.SecretSpec{ + Enrollment: ¤t.EnrollmentSpec{ + TLS: ¤t.Enrollment{ + EnrollID: "enrollID", + }, + }, + } + o.Status.Type = current.Deployed + o.Spec.FabricVersion = "2.2.1" + case *corev1.Secret: + o := obj.(*corev1.Secret) + switch types.Name { + case "ecert-" + instance.Name + "-signcert": + o.Name = "ecert-" + instance.Name + "-signcert" + o.Namespace = instance.Namespace + o.Data = map[string][]byte{"cert.pem": generateCertPemBytes(29)} + case "ecert-" + instance.Name + "-keystore": + o.Name = "ecert-" + instance.Name + "-keystore" + o.Namespace = instance.Namespace + o.Data = map[string][]byte{"key.pem": []byte("")} + case instance.Name + "-crypto-backup": + return k8serrors.NewNotFound(schema.GroupResource{}, "not found") + } + } + return nil + } + err := node.SetCertificateTimer(instance, "tls") + Expect(err).NotTo(HaveOccurred()) + Expect(node.RenewCertTimers["tls-orderer1-signcert"]).NotTo(BeNil()) + + By("renewing certificate", func() { + Eventually(func() bool { + return mockKubeClient.UpdateStatusCallCount() == 1 && + certificateMgr.RenewCertCallCount() == 1 + }, time.Duration(5*time.Second)).Should(Equal(true)) + + // timer.Stop() == false means that it already fired + Expect(node.RenewCertTimers["tls-orderer1-signcert"].Stop()).To(Equal(false)) + }) + }) + }) + + Context("sets timer to renew ecert certificate", func() { + BeforeEach(func() { + certificateMgr.GetDurationToNextRenewalReturns(time.Duration(3*time.Second), nil) + mockKubeClient.UpdateStatusReturns(nil) + certificateMgr.RenewCertReturns(nil) + }) + + It("does not return error, but certificate fails to renew after timer", func() { + certificateMgr.RenewCertReturns(errors.New("failed to renew cert")) + err := node.SetCertificateTimer(instance, "ecert") + Expect(err).NotTo(HaveOccurred()) + Expect(node.RenewCertTimers["ecert-orderer1-signcert"]).NotTo(BeNil()) + + By("certificate fails to be renewed", func() { + Eventually(func() bool { + return mockKubeClient.UpdateStatusCallCount() == 1 && + certificateMgr.RenewCertCallCount() == 1 + }, time.Duration(5*time.Second)).Should(Equal(true)) + + // timer.Stop() == false means that it already fired + Expect(node.RenewCertTimers["ecert-orderer1-signcert"].Stop()).To(Equal(false)) + }) + }) + + It("does not return error, and certificate is successfully renewed after timer", func() { + err := node.SetCertificateTimer(instance, "ecert") + Expect(err).NotTo(HaveOccurred()) + Expect(node.RenewCertTimers["ecert-orderer1-signcert"]).NotTo(BeNil()) + + By("certificate successfully renewed", func() { + Eventually(func() bool { + return mockKubeClient.UpdateStatusCallCount() == 1 && + certificateMgr.RenewCertCallCount() == 1 + }, time.Duration(5*time.Second)).Should(Equal(true)) + + // timer.Stop() == false means that it already fired + Expect(node.RenewCertTimers["ecert-orderer1-signcert"].Stop()).To(Equal(false)) + }) + }) + + It("does not return error, and timer is set to renew certificate at a later time", func() { + // Set expiration date of certificate to be > 30 days from now + certificateMgr.GetDurationToNextRenewalReturns(time.Duration(35*24*time.Hour), nil) + + err := node.SetCertificateTimer(instance, "ecert") + Expect(err).NotTo(HaveOccurred()) + Expect(node.RenewCertTimers["ecert-orderer1-signcert"]).NotTo(BeNil()) + + // timer.Stop() == true means that it has not fired but is now stopped + Expect(node.RenewCertTimers["ecert-orderer1-signcert"].Stop()).To(Equal(true)) + }) + }) + + Context("read certificate expiration date to set timer correctly", func() { + BeforeEach(func() { + node.CertificateManager = &certificate.CertificateManager{ + Client: mockKubeClient, + Scheme: &runtime.Scheme{}, + } + + // set to 30 days + instance.Spec.NumSecondsWarningPeriod = 30 * baseorderer.DaysToSecondsConversion + }) + + It("doesn't return error if timer is set correctly, but error in renewing certificate when timer goes off", func() { + // Set ecert signcert expiration date to be 29 days from now, cert is renewed if expires within 30 days + mockKubeClient.GetStub = func(ctx context.Context, types types.NamespacedName, obj client.Object) error { + switch obj.(type) { + case *current.IBPOrderer: + o := obj.(*current.IBPOrderer) + o.Kind = "IBPOrderer" + instance = o + + case *corev1.Secret: + o := obj.(*corev1.Secret) + switch types.Name { + case "ecert-" + instance.Name + "-signcert": + o.Name = "ecert-" + instance.Name + "-signcert" + o.Namespace = instance.Namespace + o.Data = map[string][]byte{"cert.pem": generateCertPemBytes(29)} + case "ecert-" + instance.Name + "-keystore": + o.Name = "ecert-" + instance.Name + "-keystore" + o.Namespace = instance.Namespace + o.Data = map[string][]byte{"key.pem": []byte("")} + case instance.Name + "-crypto-backup": + return k8serrors.NewNotFound(schema.GroupResource{}, "not found") + } + } + return nil + } + + err := node.SetCertificateTimer(instance, "ecert") + Expect(err).NotTo(HaveOccurred()) + Expect(node.RenewCertTimers["ecert-orderer1-signcert"]).NotTo(BeNil()) + + // Wait for timer to go off + time.Sleep(5 * time.Second) + + // timer.Stop() == false means that it already fired + Expect(node.RenewCertTimers["ecert-orderer1-signcert"].Stop()).To(Equal(false)) + }) + + It("doesn't return error if timer is set correctly, timer doesn't go off because certificate isn't ready for renewal", func() { + // Set ecert signcert expiration date to be 50 days from now, cert is renewed if expires within 30 days + mockKubeClient.GetStub = func(ctx context.Context, types types.NamespacedName, obj client.Object) error { + switch obj.(type) { + case *current.IBPOrderer: + o := obj.(*current.IBPOrderer) + o.Kind = "IBPOrderer" + instance = o + + case *corev1.Secret: + o := obj.(*corev1.Secret) + switch types.Name { + case "ecert-" + instance.Name + "-signcert": + o.Name = "ecert-" + instance.Name + "-signcert" + o.Namespace = instance.Namespace + o.Data = map[string][]byte{"cert.pem": generateCertPemBytes(50)} + case "ecert-" + instance.Name + "-keystore": + o.Name = "ecert-" + instance.Name + "-keystore" + o.Namespace = instance.Namespace + o.Data = map[string][]byte{"key.pem": []byte("")} + case instance.Name + "-crypto-backup": + return k8serrors.NewNotFound(schema.GroupResource{}, "not found") + } + } + return nil + } + + err := node.SetCertificateTimer(instance, "ecert") + Expect(err).NotTo(HaveOccurred()) + + // Timer shouldn't go off + time.Sleep(5 * time.Second) + + Expect(node.RenewCertTimers["ecert-orderer1-signcert"]).NotTo(BeNil()) + // timer.Stop() == true means that it has not fired but is now stopped + Expect(node.RenewCertTimers["ecert-orderer1-signcert"].Stop()).To(Equal(true)) + }) + }) + }) + + Context("renew cert", func() { + BeforeEach(func() { + instance.Spec.Secret = ¤t.SecretSpec{ + Enrollment: ¤t.EnrollmentSpec{ + Component: ¤t.Enrollment{}, + }, + } + + certificateMgr.RenewCertReturns(nil) + }) + + It("returns error if secret spec is missing", func() { + instance.Spec.Secret = nil + err := node.RenewCert("ecert", instance, true) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("missing secret spec for instance 'orderer1'")) + }) + + It("returns error if certificate generated by MSP", func() { + instance.Spec.Secret = ¤t.SecretSpec{ + MSP: ¤t.MSPSpec{}, + } + err := node.RenewCert("ecert", instance, true) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("cannot auto-renew certificate created by MSP, force renewal required")) + }) + + It("returns error if certificate manager fails to renew certificate", func() { + certificateMgr.RenewCertReturns(errors.New("failed to renew cert")) + err := node.RenewCert("ecert", instance, true) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("failed to renew cert")) + }) + + It("does not return error if certificate manager successfully renews cert", func() { + err := node.RenewCert("ecert", instance, true) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + Context("update cr status", func() { + It("returns error if fails to get current instance", func() { + mockKubeClient.GetReturns(errors.New("get error")) + err := node.UpdateCRStatus(instance) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("failed to get new instance: get error")) + }) + + It("returns error if fails to update instance status", func() { + mockKubeClient.UpdateStatusReturns(errors.New("update status error")) + certificateMgr.CheckCertificatesForExpireReturns(current.Warning, "cert renewal required", nil) + err := node.UpdateCRStatus(instance) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("failed to update status to Warning phase: update status error")) + }) + + It("sets instance CR status to Warning", func() { + certificateMgr.CheckCertificatesForExpireReturns(current.Warning, "cert renewal required", nil) + err := node.UpdateCRStatus(instance) + Expect(err).NotTo(HaveOccurred()) + Expect(instance.Status.Type).To(Equal(current.Warning)) + Expect(instance.Status.Reason).To(Equal("certRenewalRequired")) + Expect(instance.Status.Message).To(Equal("cert renewal required")) + }) + }) Context("fabric orderer migration", func() { BeforeEach(func() { @@ -722,3 +1082,24 @@ var _ = Describe("Base Orderer Node", func() { }) }) }) + +func generateCertPemBytes(daysUntilExpired int) []byte { + certtemplate := x509.Certificate{ + SerialNumber: big.NewInt(1), + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Duration(daysUntilExpired) * time.Hour * 24), + } + + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + Expect(err).NotTo(HaveOccurred()) + + cert, err := x509.CreateCertificate(rand.Reader, &certtemplate, &certtemplate, &priv.PublicKey, priv) + Expect(err).NotTo(HaveOccurred()) + + block := &pem.Block{ + Type: "CERTIFICATE", + Bytes: cert, + } + + return pem.EncodeToMemory(block) +} diff --git a/pkg/offering/base/peer/override/deployment.go b/pkg/offering/base/peer/override/deployment.go index ab6233e9..c52e5f47 100644 --- a/pkg/offering/base/peer/override/deployment.go +++ b/pkg/offering/base/peer/override/deployment.go @@ -709,6 +709,13 @@ func (o *Override) CommonDeploymentOverrides(instance *current.IBPPeer, deployme } } + if resourcesRequest.Init != nil { + err = initContainer.UpdateResources(resourcesRequest.Init) + if err != nil { + return errors.Wrap(err, "resource update for init failed") + } + } + if instance.UsingCouchDB() { couchdb := deployment.MustGetContainer(COUCHDB) if resourcesRequest.CouchDB != nil { diff --git a/pkg/util/util.go b/pkg/util/util.go index f5671b0d..a43443cd 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -838,7 +838,11 @@ func IsTCPReachable(url string) bool { return false } - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + return + } + }() return true }