github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/initializer/orderer/initializer.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 initializer
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  
    29  	current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1"
    30  	"github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common"
    31  	"github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config"
    32  	"github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/enroller"
    33  	"github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/secretmanager"
    34  	ordererconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/orderer/config/v1"
    35  	v2ordererconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/orderer/config/v2"
    36  	v24ordererconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/orderer/config/v24"
    37  	"github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient"
    38  	k8sclient "github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient"
    39  	"github.com/IBM-Blockchain/fabric-operator/pkg/util"
    40  	"github.com/IBM-Blockchain/fabric-operator/version"
    41  	"github.com/pkg/errors"
    42  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    43  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    44  	"k8s.io/apimachinery/pkg/runtime"
    45  
    46  	corev1 "k8s.io/api/core/v1"
    47  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    48  )
    49  
    50  var log = logf.Log.WithName("orderer_initializer")
    51  
    52  type Config struct {
    53  	ConfigTxFile       string
    54  	OrdererFile        string
    55  	OrdererV2File      string
    56  	OrdererV24File     string
    57  	OUFile             string
    58  	InterOUFile        string
    59  	DeploymentFile     string
    60  	PVCFile            string
    61  	ServiceFile        string
    62  	CMFile             string
    63  	RoleFile           string
    64  	ServiceAccountFile string
    65  	RoleBindingFile    string
    66  	IngressFile        string
    67  	Ingressv1beta1File string
    68  	RouteFile          string
    69  	StoragePath        string
    70  }
    71  
    72  type Response struct {
    73  	Config OrdererConfig
    74  	Crypto *config.CryptoResponse
    75  }
    76  
    77  //go:generate counterfeiter -o mocks/ibporderer.go -fake-name IBPOrderer . IBPOrderer
    78  
    79  type IBPOrderer interface {
    80  	OverrideConfig(newConfig OrdererConfig) error
    81  	GenerateCrypto() (*config.CryptoResponse, error)
    82  	GetConfig() OrdererConfig
    83  }
    84  
    85  type Initializer struct {
    86  	Config   *Config
    87  	Scheme   *runtime.Scheme
    88  	Client   k8sclient.Client
    89  	Name     string
    90  	Timeouts enroller.HSMEnrollJobTimeouts
    91  
    92  	Validator     common.CryptoValidator
    93  	SecretManager *secretmanager.SecretManager
    94  }
    95  
    96  func New(client controllerclient.Client, scheme *runtime.Scheme, cfg *Config, name string, validator common.CryptoValidator) *Initializer {
    97  	initializer := &Initializer{
    98  		Client:    client,
    99  		Scheme:    scheme,
   100  		Config:    cfg,
   101  		Name:      name,
   102  		Validator: validator,
   103  	}
   104  
   105  	initializer.SecretManager = secretmanager.New(client, scheme, initializer.GetLabels)
   106  
   107  	return initializer
   108  }
   109  
   110  func (i *Initializer) Create(overrides OrdererConfig, orderer IBPOrderer, storagePath string) (*Response, error) {
   111  	var err error
   112  
   113  	log.Info(fmt.Sprintf("Creating orderer %s's config and crypto...", i.Name))
   114  
   115  	err = os.RemoveAll(storagePath)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	err = orderer.OverrideConfig(overrides)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	cresp, err := orderer.GenerateCrypto()
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	err = os.RemoveAll(storagePath)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	return &Response{
   136  		Config: orderer.GetConfig(),
   137  		Crypto: cresp,
   138  	}, nil
   139  }
   140  
   141  func (i *Initializer) Update(overrides OrdererConfig, orderer IBPOrderer) (*Response, error) {
   142  	var err error
   143  
   144  	log.Info(fmt.Sprintf("Updating orderer %s's config...", i.Name))
   145  
   146  	err = orderer.OverrideConfig(overrides)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	return &Response{
   152  		Config: orderer.GetConfig(),
   153  	}, nil
   154  }
   155  
   156  func (i *Initializer) GetEnrollers(cryptos *config.Cryptos, instance *current.IBPOrderer, storagePath string) error {
   157  	// If no enrollment information provided, don't need to proceed further
   158  	if instance.Spec.Secret == nil || instance.Spec.Secret.Enrollment == nil {
   159  		return nil
   160  	}
   161  
   162  	enrollmentSpec := instance.Spec.Secret.Enrollment
   163  	if enrollmentSpec.Component != nil && cryptos.Enrollment == nil {
   164  		bytes, err := enrollmentSpec.Component.GetCATLSBytes()
   165  		if err != nil {
   166  			return err
   167  		}
   168  
   169  		cryptos.Enrollment, err = enroller.Factory(enrollmentSpec.Component, i.Client, instance,
   170  			filepath.Join(storagePath, "ecert"),
   171  			i.Scheme,
   172  			bytes,
   173  			i.Timeouts,
   174  		)
   175  		if err != nil {
   176  			return err
   177  		}
   178  	}
   179  
   180  	// err := common.GetSWEnrollers(cryptos, enrollmentSpec, storagePath)
   181  	err := common.GetCommonEnrollers(cryptos, enrollmentSpec, storagePath)
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  func (i *Initializer) GetMSPCrypto(cryptos *config.Cryptos, instance *current.IBPOrderer) error {
   190  	mspSpec := instance.Spec.Secret.MSP
   191  	if mspSpec != nil {
   192  		err := common.GetMSPCrypto(cryptos, mspSpec)
   193  		if err != nil {
   194  			return err
   195  		}
   196  	}
   197  
   198  	return nil
   199  }
   200  
   201  func (i *Initializer) GetInitOrderer(instance *current.IBPOrderer, storagePath string) (*Orderer, error) {
   202  	cryptos := &config.Cryptos{}
   203  
   204  	if instance.Spec.Secret != nil {
   205  		// Prioritize any crypto passed through MSP spec first
   206  		err := i.GetMSPCrypto(cryptos, instance)
   207  		if err != nil {
   208  			return nil, errors.Wrap(err, "failed to populate init orderer with MSP spec")
   209  		}
   210  
   211  		err = i.GetEnrollers(cryptos, instance, storagePath)
   212  		if err != nil {
   213  			return nil, errors.Wrap(err, "failed to populate init orderer with Enrollment spec")
   214  		}
   215  	}
   216  
   217  	return &Orderer{
   218  		Cryptos: cryptos,
   219  	}, nil
   220  }
   221  
   222  func (i *Initializer) GetUpdatedOrderer(instance *current.IBPOrderer) (*Orderer, error) {
   223  	cryptos := &config.Cryptos{}
   224  
   225  	// Only check for any new certs passed through MSP spec
   226  	err := i.GetMSPCrypto(cryptos, instance)
   227  	if err != nil {
   228  		return nil, errors.Wrap(err, "failed to populate updated init orderer with MSP spec")
   229  	}
   230  
   231  	return &Orderer{
   232  		Cryptos: cryptos,
   233  	}, nil
   234  }
   235  
   236  func (i *Initializer) GenerateSecrets(prefix common.SecretType, instance *current.IBPOrderer, crypto *config.Response) error {
   237  	if crypto == nil {
   238  		return nil
   239  	}
   240  	return i.SecretManager.GenerateSecrets(prefix, instance, crypto)
   241  }
   242  
   243  func (i *Initializer) GenerateSecretsFromResponse(instance *current.IBPOrderer, cryptoResponse *config.CryptoResponse) error {
   244  	return i.SecretManager.GenerateSecretsFromResponse(instance, cryptoResponse)
   245  }
   246  
   247  func (i *Initializer) UpdateSecrets(prefix common.SecretType, instance *current.IBPOrderer, crypto *config.Response) error {
   248  	if crypto == nil {
   249  		return nil
   250  	}
   251  	return i.SecretManager.UpdateSecrets(prefix, instance, crypto)
   252  }
   253  
   254  func (i *Initializer) UpdateSecretsFromResponse(instance *current.IBPOrderer, cryptoResponse *config.CryptoResponse) error {
   255  	return i.SecretManager.UpdateSecretsFromResponse(instance, cryptoResponse)
   256  }
   257  
   258  func (i *Initializer) GetCrypto(instance *current.IBPOrderer) (*config.CryptoResponse, error) {
   259  	return i.SecretManager.GetCryptoResponseFromSecrets(instance)
   260  }
   261  
   262  func (i *Initializer) Delete(instance *current.IBPOrderer) error {
   263  	name := fmt.Sprintf("%s%s", instance.Name, i.Name)
   264  	prefix := "ecert"
   265  	err := i.SecretManager.DeleteSecrets(prefix, instance, name)
   266  	if err != nil {
   267  		return err
   268  	}
   269  
   270  	prefix = "tls"
   271  	err = i.SecretManager.DeleteSecrets(prefix, instance, name)
   272  	if err != nil {
   273  		return err
   274  	}
   275  
   276  	cm := &corev1.ConfigMap{}
   277  	cm.Name = instance.Name + "-" + i.Name + "-config"
   278  	cm.Namespace = instance.Namespace
   279  
   280  	err = i.Client.Delete(context.TODO(), cm)
   281  	if err != nil {
   282  		if !k8serrors.IsNotFound(err) {
   283  			return errors.Wrapf(err, "failed to delete config map '%s'", cm.Name)
   284  		}
   285  	}
   286  
   287  	return nil
   288  }
   289  
   290  func (i *Initializer) MissingCrypto(instance *current.IBPOrderer) bool {
   291  	isHSMEnabled := instance.IsHSMEnabled()
   292  	if isHSMEnabled {
   293  		i.Validator.SetHSMEnabled(true)
   294  	}
   295  
   296  	checkClientAuth := instance.ClientAuthCryptoSet()
   297  	err := common.CheckCrypto(i.Validator, instance, checkClientAuth)
   298  	if err != nil {
   299  		log.Info(err.Error())
   300  		return true
   301  	}
   302  
   303  	return false
   304  }
   305  
   306  func (i *Initializer) CreateOrUpdateConfigMap(instance *current.IBPOrderer, orderer OrdererConfig) error {
   307  	name := fmt.Sprintf("%s-config", instance.GetName())
   308  	log.Info(fmt.Sprintf("Creating/Updating config map '%s'...", name))
   309  
   310  	cm := &corev1.ConfigMap{
   311  		ObjectMeta: metav1.ObjectMeta{
   312  			Name:      name,
   313  			Namespace: instance.GetNamespace(),
   314  			Labels:    i.GetLabels(instance),
   315  		},
   316  		BinaryData: map[string][]byte{},
   317  	}
   318  
   319  	existing, err := i.GetConfigFromConfigMap(instance)
   320  	if err != nil {
   321  		if !k8serrors.IsNotFound(err) {
   322  			return err
   323  		}
   324  	}
   325  	if existing != nil {
   326  		cm.BinaryData = existing.BinaryData
   327  	}
   328  
   329  	if orderer != nil {
   330  		err := i.addOrdererConfigToCM(instance, cm, orderer)
   331  		if err != nil {
   332  			return err
   333  		}
   334  	}
   335  
   336  	err = i.addNodeOUToCM(instance, cm)
   337  	if err != nil {
   338  		return err
   339  	}
   340  
   341  	err = i.Client.CreateOrUpdate(context.TODO(), cm, k8sclient.CreateOrUpdateOption{
   342  		Owner:  instance,
   343  		Scheme: i.Scheme,
   344  	})
   345  	if err != nil {
   346  		return errors.Wrap(err, "failed to create Orderer config map")
   347  	}
   348  
   349  	return nil
   350  }
   351  
   352  func (i *Initializer) addOrdererConfigToCM(instance *current.IBPOrderer, cm *corev1.ConfigMap, orderer OrdererConfig) error {
   353  	ordererBytes, err := orderer.ToBytes()
   354  	if err != nil {
   355  		return err
   356  	}
   357  	cm.BinaryData["orderer.yaml"] = ordererBytes
   358  
   359  	return nil
   360  }
   361  
   362  func (i *Initializer) addNodeOUToCM(instance *current.IBPOrderer, cm *corev1.ConfigMap) error {
   363  	if !instance.Spec.NodeOUDisabled() {
   364  		configFilePath := i.Config.OUFile
   365  		// Check if both intermediate ecerts and tlscerts secrets exists
   366  		if util.IntermediateSecretExists(i.Client, instance.Namespace, fmt.Sprintf("ecert-%s-intercerts", instance.Name)) &&
   367  			util.IntermediateSecretExists(i.Client, instance.Namespace, fmt.Sprintf("tls-%s-intercerts", instance.Name)) {
   368  			configFilePath = i.Config.InterOUFile
   369  		}
   370  		ouBytes, err := ioutil.ReadFile(filepath.Clean(configFilePath))
   371  		if err != nil {
   372  			return err
   373  		}
   374  		cm.BinaryData["config.yaml"] = ouBytes
   375  	} else {
   376  		// Set enabled to false in config
   377  		nodeOUConfig, err := config.NodeOUConfigFromBytes(cm.BinaryData["config.yaml"])
   378  		if err != nil {
   379  			return err
   380  		}
   381  
   382  		nodeOUConfig.NodeOUs.Enable = false
   383  		ouBytes, err := config.NodeOUConfigToBytes(nodeOUConfig)
   384  		if err != nil {
   385  			return err
   386  		}
   387  
   388  		cm.BinaryData["config.yaml"] = ouBytes
   389  	}
   390  
   391  	return nil
   392  }
   393  
   394  func (i *Initializer) GetConfigFromConfigMap(instance *current.IBPOrderer) (*corev1.ConfigMap, error) {
   395  	return common.GetConfigFromConfigMap(i.Client, instance)
   396  }
   397  
   398  func GetDomain(address string) string {
   399  	u := strings.Split(address, ":")
   400  	return u[0]
   401  }
   402  
   403  func (i *Initializer) GetLabels(instance metav1.Object) map[string]string {
   404  	label := os.Getenv("OPERATOR_LABEL_PREFIX")
   405  	if label == "" {
   406  		label = "fabric"
   407  	}
   408  
   409  	return map[string]string{
   410  		"app":                          instance.GetName(),
   411  		"app.kubernetes.io/name":       label,
   412  		"app.kubernetes.io/instance":   label + "orderer",
   413  		"app.kubernetes.io/managed-by": label + "-operator",
   414  	}
   415  }
   416  
   417  func (i *Initializer) CheckIfAdminCertsUpdated(instance *current.IBPOrderer) (bool, error) {
   418  	log.Info("Checking if admin certs updated")
   419  	current := common.GetAdminCertsFromSecret(i.Client, instance)
   420  	updated := common.GetAdminCertsFromSpec(instance.Spec.Secret)
   421  
   422  	return common.CheckIfCertsDifferent(current, updated)
   423  }
   424  
   425  func (i *Initializer) UpdateAdminSecret(instance *current.IBPOrderer) error {
   426  	return i.SecretManager.UpdateAdminCertSecret(instance, instance.Spec.Secret)
   427  }
   428  
   429  func (i *Initializer) GetCoreConfigFromFile(instance *current.IBPOrderer, file string) (OrdererConfig, error) {
   430  	switch version.GetMajorReleaseVersion(instance.Spec.FabricVersion) {
   431  	case version.V2:
   432  		currentVer := version.String(instance.Spec.FabricVersion)
   433  		if currentVer.EqualWithoutTag(version.V2_4_1) || currentVer.GreaterThan(version.V2_4_1) {
   434  			log.Info("v2.4.x Fabric Orderer requested")
   435  			v24config, err := v24ordererconfig.ReadOrdererFile(file)
   436  			if err != nil {
   437  				return nil, errors.Wrap(err, "failed to read v2.4.x default config file")
   438  			}
   439  			return v24config, nil
   440  		} else {
   441  			log.Info("v2.2.x Fabric Orderer requested")
   442  			v2config, err := v2ordererconfig.ReadOrdererFile(file)
   443  			if err != nil {
   444  				return nil, errors.Wrap(err, "failed to read v2.2.x default config file")
   445  			}
   446  			return v2config, nil
   447  		}
   448  	case version.V1:
   449  		fallthrough
   450  	default:
   451  		// Choosing to default to v1.4 to not break backwards comptability, if coming
   452  		// from a previous version of operator the 'FabricVersion' field would not be set and would
   453  		// result in an error. // TODO: Determine if we want to throw error or handle setting
   454  		// FabricVersion as part of migration logic.
   455  		log.Info("v1.4 Fabric Orderer requested")
   456  		oconfig, err := ordererconfig.ReadOrdererFile(file)
   457  		if err != nil {
   458  			return nil, errors.Wrap(err, "failed to read v1.4 default config file")
   459  		}
   460  		return oconfig, nil
   461  	}
   462  }
   463  
   464  func (i *Initializer) GetCoreConfigFromBytes(instance *current.IBPOrderer, bytes []byte) (OrdererConfig, error) {
   465  	switch version.GetMajorReleaseVersion(instance.Spec.FabricVersion) {
   466  	case version.V2:
   467  		currentVer := version.String(instance.Spec.FabricVersion)
   468  		if currentVer.EqualWithoutTag(version.V2_4_1) || currentVer.GreaterThan(version.V2_4_1) {
   469  			log.Info("v2.4.x Fabric Orderer requested")
   470  			v24config, err := v24ordererconfig.ReadOrdererFromBytes(bytes)
   471  			if err != nil {
   472  				return nil, err
   473  			}
   474  			return v24config, nil
   475  		} else {
   476  			log.Info("v2.2.x Fabric Orderer requested")
   477  			v2config, err := v2ordererconfig.ReadOrdererFromBytes(bytes)
   478  			if err != nil {
   479  				return nil, err
   480  			}
   481  			return v2config, nil
   482  		}
   483  	case version.V1:
   484  		fallthrough
   485  	default:
   486  		// Choosing to default to v1.4 to not break backwards comptability, if coming
   487  		// from a previous version of operator the 'FabricVersion' field would not be set and would
   488  		// result in an error.
   489  		log.Info("v1.4 Fabric Orderer requested")
   490  		oconfig, err := ordererconfig.ReadOrdererFromBytes(bytes)
   491  		if err != nil {
   492  			return nil, err
   493  		}
   494  		return oconfig, nil
   495  	}
   496  }