github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/common/configtx/manager.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8                   http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package configtx
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"regexp"
    23  
    24  	"github.com/hyperledger/fabric/common/configtx/api"
    25  	cb "github.com/hyperledger/fabric/protos/common"
    26  
    27  	logging "github.com/op/go-logging"
    28  )
    29  
    30  var logger = logging.MustGetLogger("common/configtx")
    31  
    32  // Constraints for valid chain IDs
    33  var (
    34  	allowedChars = "[a-zA-Z0-9.-]+"
    35  	maxLength    = 249
    36  	illegalNames = map[string]struct{}{
    37  		".":  struct{}{},
    38  		"..": struct{}{},
    39  	}
    40  )
    41  
    42  // NewConfigItemPolicyKey is the ID of the policy used when no other policy can be resolved, for instance when attempting to create a new config item
    43  const NewConfigItemPolicyKey = "NewConfigItemPolicy"
    44  
    45  type configManager struct {
    46  	api.Resources
    47  	sequence     uint64
    48  	chainID      string
    49  	config       map[string]comparable
    50  	callOnUpdate []func(api.Manager)
    51  	initializer  api.Initializer
    52  	configEnv    *cb.ConfigEnvelope
    53  }
    54  
    55  func computeSequence(configGroup *cb.ConfigGroup) uint64 {
    56  	max := uint64(0)
    57  	for _, value := range configGroup.Values {
    58  		if value.Version > max {
    59  			max = value.Version
    60  		}
    61  	}
    62  
    63  	for _, group := range configGroup.Groups {
    64  		if groupMax := computeSequence(group); groupMax > max {
    65  			max = groupMax
    66  		}
    67  	}
    68  
    69  	return max
    70  }
    71  
    72  // validateChainID makes sure that proposed chain IDs (i.e. channel names)
    73  // comply with the following restrictions:
    74  //      1. Contain only ASCII alphanumerics, dots '.', dashes '-'
    75  //      2. Are shorter than 250 characters.
    76  //      3. Are not the strings "." or "..".
    77  //
    78  // Our hand here is forced by:
    79  // https://github.com/apache/kafka/blob/trunk/core/src/main/scala/kafka/common/Topic.scala#L29
    80  func validateChainID(chainID string) error {
    81  	re, _ := regexp.Compile(allowedChars)
    82  	// Length
    83  	if len(chainID) <= 0 {
    84  		return fmt.Errorf("chain ID illegal, cannot be empty")
    85  	}
    86  	if len(chainID) > maxLength {
    87  		return fmt.Errorf("chain ID illegal, cannot be longer than %d", maxLength)
    88  	}
    89  	// Illegal name
    90  	if _, ok := illegalNames[chainID]; ok {
    91  		return fmt.Errorf("name '%s' for chain ID is not allowed", chainID)
    92  	}
    93  	// Illegal characters
    94  	matched := re.FindString(chainID)
    95  	if len(matched) != len(chainID) {
    96  		return fmt.Errorf("Chain ID '%s' contains illegal characters", chainID)
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  func NewManagerImpl(configEnv *cb.ConfigEnvelope, initializer api.Initializer, callOnUpdate []func(api.Manager)) (api.Manager, error) {
   103  	if configEnv == nil {
   104  		return nil, fmt.Errorf("Nil config envelope")
   105  	}
   106  
   107  	if configEnv.Config == nil {
   108  		return nil, fmt.Errorf("Nil config envelope Config")
   109  	}
   110  
   111  	if configEnv.Config.Channel == nil {
   112  		return nil, fmt.Errorf("Nil config envelope Config.Channel")
   113  	}
   114  
   115  	if configEnv.Config.Header == nil {
   116  		return nil, fmt.Errorf("Nil config envelop Config Header")
   117  	}
   118  
   119  	if err := validateChainID(configEnv.Config.Header.ChannelId); err != nil {
   120  		return nil, fmt.Errorf("Bad channel id: %s", err)
   121  	}
   122  
   123  	configMap, err := mapConfig(configEnv.Config.Channel)
   124  	if err != nil {
   125  		return nil, fmt.Errorf("Error converting config to map: %s", err)
   126  	}
   127  
   128  	cm := &configManager{
   129  		Resources:    initializer,
   130  		initializer:  initializer,
   131  		sequence:     computeSequence(configEnv.Config.Channel),
   132  		chainID:      configEnv.Config.Header.ChannelId,
   133  		config:       configMap,
   134  		callOnUpdate: callOnUpdate,
   135  	}
   136  
   137  	result, err := cm.processConfig(configEnv.Config.Channel)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	result.commit()
   142  	cm.commitCallbacks()
   143  
   144  	return cm, nil
   145  }
   146  
   147  func (cm *configManager) commitCallbacks() {
   148  	for _, callback := range cm.callOnUpdate {
   149  		callback(cm)
   150  	}
   151  }
   152  
   153  // Validate attempts to validate a new configtx against the current config state
   154  // It requires an Envelope of type CONFIG_UPDATE
   155  func (cm *configManager) ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) {
   156  	configUpdateEnv, err := envelopeToConfigUpdate(configtx)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	configMap, err := cm.authorizeUpdate(configUpdateEnv)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	channelGroup, err := configMapToConfig(configMap)
   167  	if err != nil {
   168  		return nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err)
   169  	}
   170  
   171  	result, err := cm.processConfig(channelGroup)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	result.rollback()
   177  
   178  	return &cb.ConfigEnvelope{
   179  		Config: &cb.Config{
   180  			Header: &cb.ChannelHeader{
   181  				ChannelId: cm.chainID,
   182  			},
   183  			Channel: channelGroup,
   184  		},
   185  		LastUpdate: configtx,
   186  	}, nil
   187  }
   188  
   189  func (cm *configManager) prepareApply(configEnv *cb.ConfigEnvelope) (map[string]comparable, *configResult, error) {
   190  	if configEnv == nil {
   191  		return nil, nil, fmt.Errorf("Attempted to apply config with nil envelope")
   192  	}
   193  
   194  	configUpdateEnv, err := envelopeToConfigUpdate(configEnv.LastUpdate)
   195  	if err != nil {
   196  		return nil, nil, err
   197  	}
   198  
   199  	configMap, err := cm.authorizeUpdate(configUpdateEnv)
   200  	if err != nil {
   201  		return nil, nil, err
   202  	}
   203  
   204  	channelGroup, err := configMapToConfig(configMap)
   205  	if err != nil {
   206  		return nil, nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err)
   207  	}
   208  
   209  	if configEnv.Config == nil {
   210  		return nil, nil, fmt.Errorf("Config cannot be nil")
   211  	}
   212  
   213  	if !reflect.DeepEqual(channelGroup, configEnv.Config.Channel) {
   214  		return nil, nil, fmt.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result")
   215  	}
   216  
   217  	result, err := cm.processConfig(channelGroup)
   218  	if err != nil {
   219  		return nil, nil, err
   220  	}
   221  
   222  	return configMap, result, nil
   223  }
   224  
   225  // Validate simulates applying a ConfigEnvelope to become the new config
   226  func (cm *configManager) Validate(configEnv *cb.ConfigEnvelope) error {
   227  	_, result, err := cm.prepareApply(configEnv)
   228  	if err != nil {
   229  		return err
   230  	}
   231  
   232  	result.rollback()
   233  
   234  	return nil
   235  }
   236  
   237  // Apply attempts to apply a ConfigEnvelope to become the new config
   238  func (cm *configManager) Apply(configEnv *cb.ConfigEnvelope) error {
   239  	configMap, result, err := cm.prepareApply(configEnv)
   240  	if err != nil {
   241  		return err
   242  	}
   243  
   244  	result.commit()
   245  	cm.commitCallbacks()
   246  
   247  	cm.config = configMap
   248  	cm.sequence++
   249  
   250  	return nil
   251  }
   252  
   253  // ConfigEnvelope retrieve the current ConfigEnvelope, generated after the last successfully applied configuration
   254  func (cm *configManager) ConfigEnvelope() *cb.ConfigEnvelope {
   255  	return cm.configEnv
   256  }
   257  
   258  // ChainID retrieves the chain ID associated with this manager
   259  func (cm *configManager) ChainID() string {
   260  	return cm.chainID
   261  }
   262  
   263  // Sequence returns the current sequence number of the config
   264  func (cm *configManager) Sequence() uint64 {
   265  	return cm.sequence
   266  }