github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/common/configtx/manager.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 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  	"github.com/hyperledger/fabric/common/flogging"
    26  	cb "github.com/hyperledger/fabric/protos/common"
    27  	"github.com/hyperledger/fabric/protos/utils"
    28  )
    29  
    30  var logger = flogging.MustGetLogger("common/configtx")
    31  
    32  // Constraints for valid channel and config IDs
    33  var (
    34  	channelAllowedChars = "[a-z][a-z0-9.-]*"
    35  	configAllowedChars  = "[a-zA-Z0-9.-]+"
    36  	maxLength           = 249
    37  	illegalNames        = map[string]struct{}{
    38  		".":  struct{}{},
    39  		"..": struct{}{},
    40  	}
    41  )
    42  
    43  type configSet struct {
    44  	channelID string
    45  	sequence  uint64
    46  	configMap map[string]comparable
    47  	configEnv *cb.ConfigEnvelope
    48  }
    49  
    50  type configManager struct {
    51  	api.Resources
    52  	callOnUpdate []func(api.Manager)
    53  	initializer  api.Initializer
    54  	current      *configSet
    55  }
    56  
    57  // validateConfigID makes sure that the config element names (ie map key of
    58  // ConfigGroup) comply with the following restrictions
    59  //      1. Contain only ASCII alphanumerics, dots '.', dashes '-'
    60  //      2. Are shorter than 250 characters.
    61  //      3. Are not the strings "." or "..".
    62  func validateConfigID(configID string) error {
    63  	re, _ := regexp.Compile(configAllowedChars)
    64  	// Length
    65  	if len(configID) <= 0 {
    66  		return fmt.Errorf("config ID illegal, cannot be empty")
    67  	}
    68  	if len(configID) > maxLength {
    69  		return fmt.Errorf("config ID illegal, cannot be longer than %d", maxLength)
    70  	}
    71  	// Illegal name
    72  	if _, ok := illegalNames[configID]; ok {
    73  		return fmt.Errorf("name '%s' for config ID is not allowed", configID)
    74  	}
    75  	// Illegal characters
    76  	matched := re.FindString(configID)
    77  	if len(matched) != len(configID) {
    78  		return fmt.Errorf("config ID '%s' contains illegal characters", configID)
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  // validateChannelID makes sure that proposed channel IDs comply with the
    85  // following restrictions:
    86  //      1. Contain only lower case ASCII alphanumerics, dots '.', and dashes '-'
    87  //      2. Are shorter than 250 characters.
    88  //      3. Start with a letter
    89  //
    90  // This is the intersection of the Kafka restrictions and CouchDB restrictions
    91  // with the following exception: '.' is converted to '_' in the CouchDB naming
    92  // This is to accomodate existing channel names with '.', especially in the
    93  // behave tests which rely on the dot notation for their sluggification.
    94  func validateChannelID(channelID string) error {
    95  	re, _ := regexp.Compile(channelAllowedChars)
    96  	// Length
    97  	if len(channelID) <= 0 {
    98  		return fmt.Errorf("channel ID illegal, cannot be empty")
    99  	}
   100  	if len(channelID) > maxLength {
   101  		return fmt.Errorf("channel ID illegal, cannot be longer than %d", maxLength)
   102  	}
   103  
   104  	// Illegal characters
   105  	matched := re.FindString(channelID)
   106  	if len(matched) != len(channelID) {
   107  		return fmt.Errorf("channel ID '%s' contains illegal characters", channelID)
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  func NewManagerImpl(envConfig *cb.Envelope, initializer api.Initializer, callOnUpdate []func(api.Manager)) (api.Manager, error) {
   114  	if envConfig == nil {
   115  		return nil, fmt.Errorf("Nil envelope")
   116  	}
   117  
   118  	configEnv := &cb.ConfigEnvelope{}
   119  	header, err := utils.UnmarshalEnvelopeOfType(envConfig, cb.HeaderType_CONFIG, configEnv)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("Bad envelope: %s", err)
   122  	}
   123  
   124  	if configEnv.Config == nil {
   125  		return nil, fmt.Errorf("Nil config envelope Config")
   126  	}
   127  
   128  	if configEnv.Config.ChannelGroup == nil {
   129  		return nil, fmt.Errorf("nil channel group")
   130  	}
   131  
   132  	if err := validateChannelID(header.ChannelId); err != nil {
   133  		return nil, fmt.Errorf("Bad channel id: %s", err)
   134  	}
   135  
   136  	configMap, err := MapConfig(configEnv.Config.ChannelGroup)
   137  	if err != nil {
   138  		return nil, fmt.Errorf("Error converting config to map: %s", err)
   139  	}
   140  
   141  	cm := &configManager{
   142  		Resources:   initializer,
   143  		initializer: initializer,
   144  		current: &configSet{
   145  			sequence:  configEnv.Config.Sequence,
   146  			configMap: configMap,
   147  			channelID: header.ChannelId,
   148  			configEnv: configEnv,
   149  		},
   150  		callOnUpdate: callOnUpdate,
   151  	}
   152  
   153  	result, err := cm.processConfig(configEnv.Config.ChannelGroup)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	result.commit()
   158  	cm.commitCallbacks()
   159  
   160  	return cm, nil
   161  }
   162  
   163  func (cm *configManager) commitCallbacks() {
   164  	for _, callback := range cm.callOnUpdate {
   165  		callback(cm)
   166  	}
   167  }
   168  
   169  // ProposeConfigUpdate takes in an Envelope of type CONFIG_UPDATE and produces a
   170  // ConfigEnvelope to be used as the Envelope Payload Data of a CONFIG message
   171  func (cm *configManager) ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) {
   172  	return cm.proposeConfigUpdate(configtx)
   173  }
   174  
   175  func (cm *configManager) proposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) {
   176  	configUpdateEnv, err := envelopeToConfigUpdate(configtx)
   177  	if err != nil {
   178  		return nil, fmt.Errorf("Error converting envelope to config update: %s", err)
   179  	}
   180  
   181  	configMap, err := cm.authorizeUpdate(configUpdateEnv)
   182  	if err != nil {
   183  		return nil, fmt.Errorf("Error authorizing update: %s", err)
   184  	}
   185  
   186  	channelGroup, err := configMapToConfig(configMap)
   187  	if err != nil {
   188  		return nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err)
   189  	}
   190  
   191  	result, err := cm.processConfig(channelGroup)
   192  	if err != nil {
   193  		return nil, fmt.Errorf("Error processing updated config: %s", err)
   194  	}
   195  
   196  	result.rollback()
   197  
   198  	return &cb.ConfigEnvelope{
   199  		Config: &cb.Config{
   200  			Sequence:     cm.current.sequence + 1,
   201  			ChannelGroup: channelGroup,
   202  		},
   203  		LastUpdate: configtx,
   204  	}, nil
   205  }
   206  
   207  func (cm *configManager) prepareApply(configEnv *cb.ConfigEnvelope) (map[string]comparable, *configResult, error) {
   208  	if configEnv == nil {
   209  		return nil, nil, fmt.Errorf("Attempted to apply config with nil envelope")
   210  	}
   211  
   212  	if configEnv.Config == nil {
   213  		return nil, nil, fmt.Errorf("Config cannot be nil")
   214  	}
   215  
   216  	if configEnv.Config.Sequence != cm.current.sequence+1 {
   217  		return nil, nil, fmt.Errorf("Config at sequence %d, cannot prepare to update to %d", cm.current.sequence, configEnv.Config.Sequence)
   218  	}
   219  
   220  	configUpdateEnv, err := envelopeToConfigUpdate(configEnv.LastUpdate)
   221  	if err != nil {
   222  		return nil, nil, err
   223  	}
   224  
   225  	configMap, err := cm.authorizeUpdate(configUpdateEnv)
   226  	if err != nil {
   227  		return nil, nil, err
   228  	}
   229  
   230  	channelGroup, err := configMapToConfig(configMap)
   231  	if err != nil {
   232  		return nil, nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err)
   233  	}
   234  
   235  	if !reflect.DeepEqual(channelGroup, configEnv.Config.ChannelGroup) {
   236  		return nil, nil, fmt.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result")
   237  	}
   238  
   239  	result, err := cm.processConfig(channelGroup)
   240  	if err != nil {
   241  		return nil, nil, err
   242  	}
   243  
   244  	return configMap, result, nil
   245  }
   246  
   247  // Validate simulates applying a ConfigEnvelope to become the new config
   248  func (cm *configManager) Validate(configEnv *cb.ConfigEnvelope) error {
   249  	_, result, err := cm.prepareApply(configEnv)
   250  	if err != nil {
   251  		return err
   252  	}
   253  
   254  	result.rollback()
   255  
   256  	return nil
   257  }
   258  
   259  // Apply attempts to apply a ConfigEnvelope to become the new config
   260  func (cm *configManager) Apply(configEnv *cb.ConfigEnvelope) error {
   261  	configMap, result, err := cm.prepareApply(configEnv)
   262  	if err != nil {
   263  		return err
   264  	}
   265  
   266  	result.commit()
   267  
   268  	cm.current = &configSet{
   269  		configMap: configMap,
   270  		channelID: cm.current.channelID,
   271  		sequence:  configEnv.Config.Sequence,
   272  		configEnv: configEnv,
   273  	}
   274  
   275  	cm.commitCallbacks()
   276  
   277  	return nil
   278  }
   279  
   280  // ChainID retrieves the chain ID associated with this manager
   281  func (cm *configManager) ChainID() string {
   282  	return cm.current.channelID
   283  }
   284  
   285  // Sequence returns the current sequence number of the config
   286  func (cm *configManager) Sequence() uint64 {
   287  	return cm.current.sequence
   288  }
   289  
   290  // ConfigEnvelope returns the current config envelope
   291  func (cm *configManager) ConfigEnvelope() *cb.ConfigEnvelope {
   292  	return cm.current.configEnv
   293  }