github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/offering/k8s/peer/peer.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 k8speer
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"strings"
    25  
    26  	current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1"
    27  	config "github.com/IBM-Blockchain/fabric-operator/operatorconfig"
    28  	commoninit "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common"
    29  	controllerclient "github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient"
    30  	"github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources"
    31  	resourcemanager "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/manager"
    32  	basepeer "github.com/IBM-Blockchain/fabric-operator/pkg/offering/base/peer"
    33  	basepeeroverride "github.com/IBM-Blockchain/fabric-operator/pkg/offering/base/peer/override"
    34  	"github.com/IBM-Blockchain/fabric-operator/pkg/offering/common"
    35  	"github.com/IBM-Blockchain/fabric-operator/pkg/offering/k8s/peer/override"
    36  	"github.com/IBM-Blockchain/fabric-operator/pkg/operatorerrors"
    37  	"github.com/IBM-Blockchain/fabric-operator/pkg/util"
    38  	"github.com/IBM-Blockchain/fabric-operator/version"
    39  	"github.com/pkg/errors"
    40  	networkingv1 "k8s.io/api/networking/v1"
    41  	networkingv1beta1 "k8s.io/api/networking/v1beta1"
    42  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    43  	"k8s.io/apimachinery/pkg/runtime"
    44  	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
    45  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    46  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    47  )
    48  
    49  var log = logf.Log.WithName("k8s_peer")
    50  
    51  type Override interface {
    52  	basepeer.Override
    53  	Ingress(v1.Object, *networkingv1.Ingress, resources.Action) error
    54  	Ingressv1beta1(v1.Object, *networkingv1beta1.Ingress, resources.Action) error
    55  }
    56  
    57  var _ basepeer.IBPPeer = &Peer{}
    58  
    59  type Peer struct {
    60  	*basepeer.Peer
    61  
    62  	IngressManager        resources.Manager
    63  	Ingressv1beta1Manager resources.Manager
    64  
    65  	Override Override
    66  }
    67  
    68  func New(client controllerclient.Client, scheme *runtime.Scheme, config *config.Config) *Peer {
    69  	o := &override.Override{
    70  		Override: basepeeroverride.Override{
    71  			Client:                        client,
    72  			DefaultCouchContainerFile:     config.PeerInitConfig.CouchContainerFile,
    73  			DefaultCouchInitContainerFile: config.PeerInitConfig.CouchInitContainerFile,
    74  			DefaultCCLauncherFile:         config.PeerInitConfig.CCLauncherFile,
    75  		},
    76  	}
    77  
    78  	p := &Peer{
    79  		Peer:     basepeer.New(client, scheme, config, o),
    80  		Override: o,
    81  	}
    82  
    83  	p.CreateManagers()
    84  	return p
    85  }
    86  
    87  func (p *Peer) CreateManagers() {
    88  	resourceManager := resourcemanager.New(p.Client, p.Scheme)
    89  	p.IngressManager = resourceManager.CreateIngressManager("", p.Override.Ingress, p.GetLabels, p.Config.PeerInitConfig.IngressFile)
    90  	p.Ingressv1beta1Manager = resourceManager.CreateIngressv1beta1Manager("", p.Override.Ingressv1beta1, p.GetLabels, p.Config.PeerInitConfig.Ingressv1beta1File)
    91  }
    92  
    93  func (p *Peer) ReconcileManagers(instance *current.IBPPeer, update basepeer.Update) error {
    94  	err := p.Peer.ReconcileManagers(instance, update)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	err = p.ReconcileIngressManager(instance, update.SpecUpdated())
   100  	if err != nil {
   101  		return errors.Wrap(err, "failed Ingress reconciliation")
   102  	}
   103  
   104  	return nil
   105  }
   106  
   107  func (p *Peer) Reconcile(instance *current.IBPPeer, update basepeer.Update) (common.Result, error) {
   108  	var err error
   109  	var status *current.CRStatus
   110  
   111  	versionSet, err := p.SetVersion(instance)
   112  	if err != nil {
   113  		return common.Result{}, errors.Wrap(err, fmt.Sprintf("failed updating CR '%s' to version '%s'", instance.Name, version.Operator))
   114  	}
   115  	if versionSet {
   116  		log.Info("Instance version updated, requeuing request...")
   117  		return common.Result{
   118  			Result: reconcile.Result{
   119  				Requeue: true,
   120  			},
   121  		}, nil
   122  	}
   123  
   124  	instanceUpdated, err := p.PreReconcileChecks(instance, update)
   125  	if err != nil {
   126  		return common.Result{}, errors.Wrap(err, "failed pre reconcile checks")
   127  	}
   128  
   129  	// We do not have to wait for service to get the external endpoint
   130  	// thus we call UpdateExternalEndpoint in reconcile before reconcile managers
   131  	externalEndpointUpdated := p.UpdateExternalEndpoint(instance)
   132  
   133  	hostAPI := fmt.Sprintf("%s-%s-peer.%s", instance.Namespace, instance.Name, instance.Spec.Domain)
   134  	hostOperations := fmt.Sprintf("%s-%s-operations.%s", instance.Namespace, instance.Name, instance.Spec.Domain)
   135  	hostGrpcWeb := fmt.Sprintf("%s-%s-grpcweb.%s", instance.Namespace, instance.Name, instance.Spec.Domain)
   136  	hosts := []string{hostAPI, hostOperations, hostGrpcWeb, "127.0.0.1"}
   137  	csrHostUpdated := p.CheckCSRHosts(instance, hosts)
   138  
   139  	if instanceUpdated || externalEndpointUpdated || csrHostUpdated {
   140  		log.Info(fmt.Sprintf("Updating instance after pre reconcile checks: %t, updating external endpoint: %t, csr host updated: %t", instanceUpdated, externalEndpointUpdated, csrHostUpdated))
   141  		err := p.Client.Patch(context.TODO(), instance, nil, controllerclient.PatchOption{
   142  			Resilient: &controllerclient.ResilientPatch{
   143  				Retry:    3,
   144  				Into:     &current.IBPPeer{},
   145  				Strategy: k8sclient.MergeFrom,
   146  			},
   147  		})
   148  		if err != nil {
   149  			return common.Result{}, errors.Wrap(err, "failed to update instance after prereconcile checks")
   150  		}
   151  
   152  		log.Info("Instance updated, requeuing request...")
   153  		return common.Result{
   154  			Result: reconcile.Result{
   155  				Requeue: true,
   156  			},
   157  		}, nil
   158  	}
   159  
   160  	jobRunning, err := p.HandleMigrationJobs(k8sclient.MatchingLabels{
   161  		"owner":    instance.GetName(),
   162  		"job-name": fmt.Sprintf("%s-dbmigration", instance.GetName()),
   163  	}, instance)
   164  	if jobRunning {
   165  		log.Info(fmt.Sprintf("Requeuing request until job completes"))
   166  		return common.Result{
   167  			Result: reconcile.Result{
   168  				Requeue: true,
   169  			},
   170  		}, nil
   171  	}
   172  	if err != nil {
   173  		return common.Result{}, err
   174  	}
   175  
   176  	err = p.Initialize(instance, update)
   177  	if err != nil {
   178  		return common.Result{}, operatorerrors.Wrap(err, operatorerrors.PeerInitilizationFailed, "failed to initialize peer")
   179  	}
   180  
   181  	if update.PeerTagUpdated() {
   182  		if err := p.ReconcileFabricPeerMigrationV1_4(instance); err != nil {
   183  			return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer versions")
   184  		}
   185  	}
   186  
   187  	if update.MigrateToV2() {
   188  		if err := p.ReconcileFabricPeerMigrationV2_0(instance); err != nil {
   189  			return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer to version v2.0.x")
   190  		}
   191  	}
   192  
   193  	if update.MigrateToV24() {
   194  		if err := p.ReconcileFabricPeerMigrationV2_4(instance); err != nil {
   195  			return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer to version v2.4.x")
   196  		}
   197  	}
   198  
   199  	err = p.ReconcileManagers(instance, update)
   200  	if err != nil {
   201  		return common.Result{}, errors.Wrap(err, "failed to reconcile managers")
   202  	}
   203  
   204  	err = p.UpdateConnectionProfile(instance)
   205  	if err != nil {
   206  		return common.Result{}, errors.Wrap(err, "failed to create connection profile")
   207  	}
   208  
   209  	err = p.CheckStates(instance)
   210  	if err != nil {
   211  		return common.Result{}, errors.Wrap(err, "failed to check and restore state")
   212  	}
   213  
   214  	// custom product logic can be implemented here
   215  	// No-Op atm
   216  	status, result, err := p.CustomLogic(instance, update)
   217  	if err != nil {
   218  		return common.Result{}, errors.Wrap(err, "failed to run custom offering logic")
   219  	}
   220  	if result != nil {
   221  		log.Info(fmt.Sprintf("Finished reconciling '%s' with Custom Logic result", instance.GetName()))
   222  		return *result, nil
   223  	}
   224  
   225  	if update.EcertUpdated() {
   226  		log.Info("Ecert was updated")
   227  		// Request deployment restart for tls cert update
   228  		err = p.Restart.ForCertUpdate(commoninit.ECERT, instance)
   229  		if err != nil {
   230  			return common.Result{}, errors.Wrap(err, "failed to update restart config")
   231  		}
   232  	}
   233  
   234  	if update.TLSCertUpdated() {
   235  		log.Info("TLS cert was updated")
   236  		// Request deployment restart for ecert update
   237  		err = p.Restart.ForCertUpdate(commoninit.TLS, instance)
   238  		if err != nil {
   239  			return common.Result{}, errors.Wrap(err, "failed to update restart config")
   240  		}
   241  	}
   242  
   243  	if update.MSPUpdated() {
   244  		err = p.UpdateMSPCertificates(instance)
   245  		if err != nil {
   246  			return common.Result{}, errors.Wrap(err, "failed to update certificates passed in MSP spec")
   247  		}
   248  	}
   249  
   250  	if err := p.HandleActions(instance, update); err != nil {
   251  		return common.Result{}, err
   252  	}
   253  
   254  	// If configs were update during initialize, need to restart pods to pick up new
   255  	// config changes. This should be done as the last the step, specifically after ReconcileManagers,
   256  	// to allow all any updates to the deployment to be completed before restarting.
   257  	// Trigger deployment restart by deleting deployment
   258  	if err := p.HandleRestart(instance, update); err != nil {
   259  		return common.Result{}, err
   260  	}
   261  
   262  	return common.Result{
   263  		Status: status,
   264  	}, nil
   265  }
   266  
   267  func (p *Peer) ReconcileIngressManager(instance *current.IBPPeer, update bool) error {
   268  	if p.Config.Operator.Globals.AllowKubernetesEighteen == "true" {
   269  		// check k8s version
   270  		version, err := util.GetServerVersion()
   271  		if err != nil {
   272  			return err
   273  		}
   274  		if strings.Compare(version.Minor, "19") < 0 { // v1beta
   275  			err = p.Ingressv1beta1Manager.Reconcile(instance, update)
   276  			if err != nil {
   277  				return errors.Wrap(err, "failed Ingressv1beta1 reconciliation")
   278  			}
   279  		} else {
   280  			err = p.IngressManager.Reconcile(instance, update)
   281  			if err != nil {
   282  				return errors.Wrap(err, "failed Ingress reconciliation")
   283  			}
   284  		}
   285  	} else {
   286  		err := p.IngressManager.Reconcile(instance, update)
   287  		if err != nil {
   288  			return errors.Wrap(err, "failed Ingress reconciliation")
   289  		}
   290  	}
   291  	return nil
   292  }