github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/offering/openshift/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 openshiftpeer
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"regexp"
    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/openshift/peer/override"
    36  	"github.com/IBM-Blockchain/fabric-operator/pkg/operatorerrors"
    37  	"github.com/IBM-Blockchain/fabric-operator/version"
    38  	openshiftv1 "github.com/openshift/api/config/v1"
    39  	routev1 "github.com/openshift/api/route/v1"
    40  	"github.com/pkg/errors"
    41  	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    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("openshift_peer")
    50  
    51  type Override interface {
    52  	basepeer.Override
    53  	PeerRoute(object v1.Object, route *routev1.Route, action resources.Action) error
    54  	OperationsRoute(object v1.Object, route *routev1.Route, action resources.Action) error
    55  	PeerGRPCRoute(object v1.Object, route *routev1.Route, action resources.Action) error
    56  }
    57  
    58  var _ basepeer.IBPPeer = &Peer{}
    59  
    60  type Peer struct {
    61  	*basepeer.Peer
    62  
    63  	RouteManager           resources.Manager
    64  	OperationsRouteManager resources.Manager
    65  	GRPCRouteManager       resources.Manager
    66  	RestClient             *clientset.Clientset
    67  
    68  	Override Override
    69  }
    70  
    71  func New(client controllerclient.Client, scheme *runtime.Scheme, config *config.Config, restclient *clientset.Clientset) *Peer {
    72  	o := &override.Override{
    73  		Override: basepeeroverride.Override{
    74  			Client:                        client,
    75  			DefaultCouchContainerFile:     config.PeerInitConfig.CouchContainerFile,
    76  			DefaultCouchInitContainerFile: config.PeerInitConfig.CouchInitContainerFile,
    77  			DefaultCCLauncherFile:         config.PeerInitConfig.CCLauncherFile,
    78  		},
    79  	}
    80  
    81  	peer := &Peer{
    82  		Peer:       basepeer.New(client, scheme, config, o),
    83  		Override:   o,
    84  		RestClient: restclient,
    85  	}
    86  
    87  	peer.CreateManagers()
    88  	return peer
    89  }
    90  
    91  func (p *Peer) CreateManagers() {
    92  	resourceManager := resourcemanager.New(p.Client, p.Scheme)
    93  	p.RouteManager = resourceManager.CreateRouteManager("peer", p.Override.PeerRoute, p.GetLabels, p.Config.PeerInitConfig.RouteFile)
    94  	p.OperationsRouteManager = resourceManager.CreateRouteManager("operations", p.Override.OperationsRoute, p.GetLabels, p.Config.PeerInitConfig.RouteFile)
    95  	p.GRPCRouteManager = resourceManager.CreateRouteManager("grpcweb", p.Override.PeerGRPCRoute, p.GetLabels, p.Config.PeerInitConfig.RouteFile)
    96  }
    97  
    98  func (p *Peer) ReconcileManagers(instance *current.IBPPeer, update basepeer.Update) error {
    99  	err := p.Peer.ReconcileManagers(instance, update)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	err = p.RouteManager.Reconcile(instance, update.SpecUpdated())
   105  	if err != nil {
   106  		return errors.Wrap(err, "failed Peer Route reconciliation")
   107  	}
   108  
   109  	err = p.OperationsRouteManager.Reconcile(instance, update.SpecUpdated())
   110  	if err != nil {
   111  		return errors.Wrap(err, "failed Operations Route reconciliation")
   112  	}
   113  
   114  	err = p.GRPCRouteManager.Reconcile(instance, update.SpecUpdated())
   115  	if err != nil {
   116  		return errors.Wrap(err, "failed Peer GRPC Route reconciliation")
   117  	}
   118  
   119  	return nil
   120  }
   121  
   122  func (p *Peer) Reconcile(instance *current.IBPPeer, update basepeer.Update) (common.Result, error) {
   123  	var err error
   124  	var status *current.CRStatus
   125  
   126  	versionSet, err := p.SetVersion(instance)
   127  	if err != nil {
   128  		return common.Result{}, errors.Wrap(err, fmt.Sprintf("failed updating CR '%s' to version '%s'", instance.Name, version.Operator))
   129  	}
   130  	if versionSet {
   131  		log.Info("Instance version updated, requeuing request...")
   132  		return common.Result{
   133  			Result: reconcile.Result{
   134  				Requeue: true,
   135  			},
   136  		}, nil
   137  	}
   138  
   139  	updatecr, err := p.SelectDinDArgs(instance)
   140  	if err != nil {
   141  		log.Info("Cannot get cluster version. Ignoring openshift cluster version")
   142  	}
   143  
   144  	update.SetDindArgsUpdated(updatecr)
   145  	instanceUpdated, err := p.PreReconcileChecks(instance, update)
   146  	if err != nil {
   147  		return common.Result{}, errors.Wrap(err, "failed pre reconcile checks")
   148  	}
   149  
   150  	// We do not have to wait for service to get the external endpoint
   151  	// thus we call UpdateExternalEndpoint in reconcile before reconcile managers
   152  	externalEndpointUpdated := p.UpdateExternalEndpoint(instance)
   153  
   154  	hostAPI := fmt.Sprintf("%s-%s-peer.%s", instance.Namespace, instance.Name, instance.Spec.Domain)
   155  	hostOperations := fmt.Sprintf("%s-%s-operations.%s", instance.Namespace, instance.Name, instance.Spec.Domain)
   156  	hostGrpc := fmt.Sprintf("%s-%s-grpcweb.%s", instance.Namespace, instance.Name, instance.Spec.Domain)
   157  	hosts := []string{hostAPI, hostOperations, hostGrpc, "127.0.0.1"}
   158  	csrHostUpdated := p.CheckCSRHosts(instance, hosts)
   159  
   160  	if instanceUpdated || externalEndpointUpdated || csrHostUpdated {
   161  		log.Info(fmt.Sprintf("Updating instance after pre reconcile checks: %t, updating external endpoint: %t, csrhost Updated: %t", instanceUpdated, externalEndpointUpdated, csrHostUpdated))
   162  		err := p.Client.Patch(context.TODO(), instance, nil, controllerclient.PatchOption{
   163  			Resilient: &controllerclient.ResilientPatch{
   164  				Retry:    3,
   165  				Into:     &current.IBPPeer{},
   166  				Strategy: k8sclient.MergeFrom,
   167  			},
   168  		})
   169  		if err != nil {
   170  			return common.Result{}, errors.Wrap(err, "failed to update instance after prereconcile checks")
   171  		}
   172  
   173  		log.Info("Instance updated, requeuing request...")
   174  		return common.Result{
   175  			Result: reconcile.Result{
   176  				Requeue: true,
   177  			},
   178  		}, nil
   179  	}
   180  
   181  	jobRunning, err := p.HandleMigrationJobs(k8sclient.MatchingLabels{
   182  		"owner":    instance.GetName(),
   183  		"job-name": fmt.Sprintf("%s-dbmigration", instance.GetName()),
   184  	}, instance)
   185  	if jobRunning {
   186  		log.Info(fmt.Sprintf("Requeuing request until job completes"))
   187  		return common.Result{
   188  			Result: reconcile.Result{
   189  				Requeue: true,
   190  			},
   191  		}, nil
   192  	}
   193  	if err != nil {
   194  		return common.Result{}, err
   195  	}
   196  
   197  	err = p.Initialize(instance, update)
   198  	if err != nil {
   199  		return common.Result{}, operatorerrors.Wrap(err, operatorerrors.PeerInitilizationFailed, "failed to initialize peer")
   200  	}
   201  
   202  	if update.PeerTagUpdated() {
   203  		if err := p.ReconcileFabricPeerMigrationV1_4(instance); err != nil {
   204  			return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer versions")
   205  		}
   206  	}
   207  
   208  	if update.MigrateToV2() {
   209  		if err := p.ReconcileFabricPeerMigrationV2_0(instance); err != nil {
   210  			return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer to version v2.0.x")
   211  		}
   212  	}
   213  
   214  	if update.MigrateToV24() {
   215  		if err := p.ReconcileFabricPeerMigrationV2_4(instance); err != nil {
   216  			return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer to version v2.4.x")
   217  		}
   218  	}
   219  
   220  	err = p.ReconcileManagers(instance, update)
   221  	if err != nil {
   222  		return common.Result{}, errors.Wrap(err, "failed to reconcile managers")
   223  	}
   224  
   225  	err = p.UpdateConnectionProfile(instance)
   226  	if err != nil {
   227  		return common.Result{}, errors.Wrap(err, "failed to create connection profile")
   228  	}
   229  
   230  	err = p.CheckStates(instance)
   231  	if err != nil {
   232  		return common.Result{}, errors.Wrap(err, "failed to check and restore state")
   233  	}
   234  
   235  	// custom product logic can be implemented here
   236  	// No-Op atm
   237  	status, result, err := p.CustomLogic(instance, update)
   238  	if err != nil {
   239  		return common.Result{}, errors.Wrap(err, "failed to run custom offering logic")
   240  	}
   241  	if result != nil {
   242  		log.Info(fmt.Sprintf("Finished reconciling '%s' with Custom Logic result", instance.GetName()))
   243  		return *result, nil
   244  	}
   245  
   246  	if update.EcertUpdated() {
   247  		log.Info("Ecert was updated")
   248  		// Request deployment restart for tls cert update
   249  		err = p.Restart.ForCertUpdate(commoninit.ECERT, instance)
   250  		if err != nil {
   251  			return common.Result{}, errors.Wrap(err, "failed to update restart config")
   252  		}
   253  	}
   254  
   255  	if update.TLSCertUpdated() {
   256  		log.Info("TLS cert was updated")
   257  		// Request deployment restart for ecert update
   258  		err = p.Restart.ForCertUpdate(commoninit.TLS, instance)
   259  		if err != nil {
   260  			return common.Result{}, errors.Wrap(err, "failed to update restart config")
   261  		}
   262  	}
   263  
   264  	if update.MSPUpdated() {
   265  		err = p.UpdateMSPCertificates(instance)
   266  		if err != nil {
   267  			return common.Result{}, errors.Wrap(err, "failed to update certificates passed in MSP spec")
   268  		}
   269  	}
   270  
   271  	if err := p.HandleActions(instance, update); err != nil {
   272  		return common.Result{}, err
   273  	}
   274  
   275  	// If configs were update during initialize, need to restart pods to pick up new
   276  	// config changes. This should be done as the last the step, specifically after ReconcileManagers,
   277  	// to allow all any updates to the deployment to be completed before restarting.
   278  	// Trigger deployment restart by deleting deployment
   279  	if err := p.HandleRestart(instance, update); err != nil {
   280  		return common.Result{}, err
   281  	}
   282  
   283  	return common.Result{
   284  		Status: status,
   285  	}, nil
   286  }
   287  
   288  func (p *Peer) SelectDinDArgs(instance *current.IBPPeer) (bool, error) {
   289  
   290  	if len(instance.Spec.DindArgs) != 0 {
   291  		return false, nil
   292  	}
   293  
   294  	clusterversion := openshiftv1.ClusterVersion{}
   295  
   296  	err := p.RestClient.RESTClient().Get().
   297  		AbsPath("apis", "config.openshift.io", "v1", "clusterversions", "version").
   298  		Do(context.TODO()).
   299  		Into(&clusterversion)
   300  
   301  	if err != nil {
   302  		return false, err
   303  	}
   304  
   305  	dindargs := []string{"--log-driver", "fluentd", "--log-opt", "fluentd-address=localhost:9880", "--mtu", "1400", "--iptables=true"}
   306  
   307  	re := regexp.MustCompile(`4\.[0-9]\.[0-9]`)
   308  	if re.MatchString(clusterversion.Status.Desired.Version) {
   309  		dindargs = []string{"--log-driver", "fluentd", "--log-opt", "fluentd-address=localhost:9880", "--mtu", "1400", "--iptables=false"}
   310  	}
   311  
   312  	instance.Spec.DindArgs = dindargs
   313  
   314  	return true, nil
   315  }