github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/migrator/peer/fabric/v2/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 v2
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"reflect"
    25  	"strings"
    26  
    27  	"github.com/pkg/errors"
    28  
    29  	current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1"
    30  	config "github.com/IBM-Blockchain/fabric-operator/operatorconfig"
    31  	"github.com/IBM-Blockchain/fabric-operator/pkg/action"
    32  	"github.com/IBM-Blockchain/fabric-operator/pkg/apis/common"
    33  	"github.com/IBM-Blockchain/fabric-operator/pkg/apis/deployer"
    34  	v2peer "github.com/IBM-Blockchain/fabric-operator/pkg/apis/peer/v2"
    35  	initializer "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/peer"
    36  	v2config "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/peer/config/v2"
    37  	k8sclient "github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient"
    38  	ver "github.com/IBM-Blockchain/fabric-operator/version"
    39  
    40  	appsv1 "k8s.io/api/apps/v1"
    41  	corev1 "k8s.io/api/core/v1"
    42  	"k8s.io/apimachinery/pkg/api/resource"
    43  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    44  	"k8s.io/apimachinery/pkg/runtime"
    45  	"k8s.io/apimachinery/pkg/types"
    46  
    47  	"sigs.k8s.io/controller-runtime/pkg/client"
    48  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    49  	"sigs.k8s.io/yaml"
    50  )
    51  
    52  var log = logf.Log.WithName("peer_fabric_migrator")
    53  
    54  //go:generate counterfeiter -o mocks/configmapmanager.go -fake-name ConfigMapManager . ConfigMapManager
    55  type ConfigMapManager interface {
    56  	GetCoreConfig(*current.IBPPeer) (*corev1.ConfigMap, error)
    57  	CreateOrUpdate(*current.IBPPeer, initializer.CoreConfig) error
    58  }
    59  
    60  //go:generate counterfeiter -o mocks/deploymentmanager.go -fake-name DeploymentManager . DeploymentManager
    61  type DeploymentManager interface {
    62  	Get(metav1.Object) (client.Object, error)
    63  	Delete(metav1.Object) error
    64  	DeploymentStatus(metav1.Object) (appsv1.DeploymentStatus, error)
    65  	GetScheme() *runtime.Scheme
    66  }
    67  
    68  type Migrate struct {
    69  	DeploymentManager DeploymentManager
    70  	ConfigMapManager  ConfigMapManager
    71  	Client            k8sclient.Client
    72  }
    73  
    74  func (m *Migrate) MigrationNeeded(instance metav1.Object) bool {
    75  	// Check for DinD container, if DinD container not found this is
    76  	// v2 fabric IBP instance
    77  	obj, err := m.DeploymentManager.Get(instance)
    78  	if err != nil {
    79  		// If deployment does not exist, this instance is not a healthy
    80  		// state and migration should be avoided
    81  		return false
    82  	}
    83  
    84  	var deploymentUpdated bool
    85  	var configUpdated bool
    86  
    87  	dep := obj.(*appsv1.Deployment)
    88  	for _, cont := range dep.Spec.Template.Spec.Containers {
    89  		if strings.ToLower(cont.Name) == "dind" {
    90  			// DinD container found, instance is not at v2
    91  			deploymentUpdated = false
    92  		}
    93  	}
    94  
    95  	cm, err := m.ConfigMapManager.GetCoreConfig(instance.(*current.IBPPeer))
    96  	if err != nil {
    97  		// If config map does not exist, this instance is not a healthy
    98  		// state and migration should be avoided
    99  		return false
   100  	}
   101  
   102  	v1corebytes := cm.BinaryData["core.yaml"]
   103  
   104  	core := &v2config.Core{}
   105  	err = yaml.Unmarshal(v1corebytes, core)
   106  	if err != nil {
   107  		return false
   108  	}
   109  
   110  	configUpdated = configHasBeenUpdated(core)
   111  
   112  	return !deploymentUpdated || !configUpdated
   113  }
   114  
   115  func (m *Migrate) UpgradeDBs(instance metav1.Object, timeouts config.DBMigrationTimeouts) error {
   116  	log.Info(fmt.Sprintf("Resetting Peer '%s'", instance.GetName()))
   117  	return action.UpgradeDBs(m.DeploymentManager, m.Client, instance.(*current.IBPPeer), timeouts)
   118  }
   119  
   120  func (m *Migrate) UpdateConfig(instance metav1.Object, version string) error {
   121  	log.Info("Updating config to v2")
   122  	cm, err := m.ConfigMapManager.GetCoreConfig(instance.(*current.IBPPeer))
   123  	if err != nil {
   124  		return errors.Wrap(err, "failed to get config map")
   125  	}
   126  	v1corebytes := cm.BinaryData["core.yaml"]
   127  
   128  	core := &v2config.Core{}
   129  	err = yaml.Unmarshal(v1corebytes, core)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	// resetting VM endpoint
   135  	// As per this PR #2165, VM and Ledger structs been added to Peer. endpoint is not required for v2 peer as there is no DinD
   136  	core.VM.Endpoint = ""
   137  
   138  	core.Chaincode.ExternalBuilders = []v2peer.ExternalBuilder{
   139  		v2peer.ExternalBuilder{
   140  			Name: "ibp-builder",
   141  			Path: "/usr/local",
   142  			EnvironmentWhiteList: []string{
   143  				"IBP_BUILDER_ENDPOINT",
   144  				"IBP_BUILDER_SHARED_DIR",
   145  			},
   146  			PropogateEnvironment: []string{
   147  				"IBP_BUILDER_ENDPOINT",
   148  				"IBP_BUILDER_SHARED_DIR",
   149  				"PEER_NAME",
   150  			},
   151  		},
   152  	}
   153  
   154  	core.Chaincode.InstallTimeout = common.MustParseDuration("300s")
   155  	if core.Chaincode.System == nil {
   156  		core.Chaincode.System = make(map[string]string)
   157  	}
   158  	core.Chaincode.System["_lifecycle"] = "enable"
   159  
   160  	core.Peer.Limits.Concurrency.DeliverService = 2500
   161  	core.Peer.Limits.Concurrency.EndorserService = 2500
   162  
   163  	core.Peer.Gossip.PvtData.ImplicitCollectionDisseminationPolicy.RequiredPeerCount = 0
   164  	core.Peer.Gossip.PvtData.ImplicitCollectionDisseminationPolicy.MaxPeerCount = 1
   165  
   166  	currentVer := ver.String(version)
   167  	if currentVer.EqualWithoutTag(ver.V2_4_1) || currentVer.GreaterThan(ver.V2_4_1) {
   168  		trueVal := true
   169  		core.Peer.Gateway = v2peer.Gateway{
   170  			Enabled:            &trueVal,
   171  			EndorsementTimeout: common.MustParseDuration("30s"),
   172  			DialTimeout:        common.MustParseDuration("120s"),
   173  		}
   174  		core.Peer.Limits.Concurrency.GatewayService = 500
   175  		core.Ledger.State.SnapShots = v2peer.SnapShots{
   176  			RootDir: "/data/peer/ledgersData/snapshots/",
   177  		}
   178  	}
   179  
   180  	core.Ledger.State.CouchdbConfig.CacheSize = 64
   181  	core.Ledger.State.CouchdbConfig.MaxRetries = 10
   182  
   183  	err = m.ConfigMapManager.CreateOrUpdate(instance.(*current.IBPPeer), core)
   184  	if err != nil {
   185  		return err
   186  	}
   187  
   188  	return nil
   189  }
   190  
   191  // SetChaincodeLauncherResourceOnCR will update the peer's CR by adding chaincode launcher
   192  // resources. The default resources are defined in deployer's config map, which is part
   193  // IBPConsole resource. The default resources are extracted for the chaincode launcher
   194  // by reading the deployer's config map and updating the CR.
   195  func (m *Migrate) SetChaincodeLauncherResourceOnCR(instance metav1.Object) error {
   196  	log.Info("Setting chaincode launcher resource on CR")
   197  	cr := instance.(*current.IBPPeer)
   198  
   199  	if cr.Spec.Resources != nil && cr.Spec.Resources.CCLauncher != nil {
   200  		// No need to proceed further if Chaincode launcher resources already set
   201  		return nil
   202  	}
   203  
   204  	consoleList := &current.IBPConsoleList{}
   205  	if err := m.Client.List(context.TODO(), consoleList); err != nil {
   206  		return err
   207  	}
   208  	consoles := consoleList.Items
   209  
   210  	// If no consoles found, set default resource for chaincode launcher container
   211  	rr := &corev1.ResourceRequirements{
   212  		Requests: corev1.ResourceList{
   213  			corev1.ResourceCPU:    resource.MustParse("0.1"),
   214  			corev1.ResourceMemory: resource.MustParse("100Mi"),
   215  		},
   216  		Limits: corev1.ResourceList{
   217  			corev1.ResourceCPU:    resource.MustParse("2"),
   218  			corev1.ResourceMemory: resource.MustParse("2Gi"),
   219  		},
   220  	}
   221  
   222  	if len(consoles) > 0 {
   223  		log.Info("Setting chaincode launcher resource on CR based on deployer config from config map")
   224  		// Get config map associated with console
   225  		cm := &corev1.ConfigMap{}
   226  		nn := types.NamespacedName{
   227  			Name:      fmt.Sprintf("%s-deployer", consoles[0].GetName()),
   228  			Namespace: instance.GetNamespace(),
   229  		}
   230  		if err := m.Client.Get(context.TODO(), nn, cm); err != nil {
   231  			return err
   232  		}
   233  
   234  		settingsBytes := []byte(cm.Data["settings.yaml"])
   235  		settings := &deployer.Config{}
   236  		if err := yaml.Unmarshal(settingsBytes, settings); err != nil {
   237  			return err
   238  		}
   239  
   240  		if settings.Defaults != nil && settings.Defaults.Resources != nil &&
   241  			settings.Defaults.Resources.Peer != nil && settings.Defaults.Resources.Peer.CCLauncher != nil {
   242  
   243  			rr = settings.Defaults.Resources.Peer.CCLauncher
   244  		}
   245  	}
   246  
   247  	log.Info(fmt.Sprintf("Setting chaincode launcher resource on CR to %+v", rr))
   248  	if cr.Spec.Resources == nil {
   249  		cr.Spec.Resources = &current.PeerResources{}
   250  	}
   251  	cr.Spec.Resources.CCLauncher = rr
   252  	if err := m.Client.Update(context.TODO(), cr); err != nil {
   253  		return err
   254  	}
   255  
   256  	return nil
   257  }
   258  
   259  // Updates required from v1.4 to v2.x:
   260  // - External builders
   261  // - Limits
   262  // - Install timeout
   263  // - Implicit collection dissemination policy
   264  func configHasBeenUpdated(core *v2config.Core) bool {
   265  	if len(core.Chaincode.ExternalBuilders) == 0 {
   266  		return false
   267  	}
   268  	if core.Chaincode.ExternalBuilders[0].Name != "ibp-builder" {
   269  		return false
   270  	}
   271  
   272  	// Check if install timeout was set
   273  	if reflect.DeepEqual(core.Chaincode.InstallTimeout, common.Duration{}) {
   274  		return false
   275  	}
   276  
   277  	if core.Peer.Limits.Concurrency.DeliverService != 2500 {
   278  		return false
   279  	}
   280  
   281  	if core.Peer.Limits.Concurrency.EndorserService != 2500 {
   282  		return false
   283  	}
   284  
   285  	return true
   286  }