github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/common/configvalues/channel/orderer/sharedconfig.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 orderer
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  	"strconv"
    23  	"strings"
    24  	"time"
    25  
    26  	api "github.com/hyperledger/fabric/common/configvalues"
    27  	"github.com/hyperledger/fabric/common/configvalues/channel/common/organization"
    28  	"github.com/hyperledger/fabric/common/configvalues/msp"
    29  	cb "github.com/hyperledger/fabric/protos/common"
    30  	ab "github.com/hyperledger/fabric/protos/orderer"
    31  
    32  	"github.com/golang/protobuf/proto"
    33  	"github.com/op/go-logging"
    34  )
    35  
    36  const (
    37  	// GroupKey is the group name for the orderer config
    38  	GroupKey = "Orderer"
    39  )
    40  
    41  var orgSchema = &cb.ConfigGroupSchema{
    42  	Groups: map[string]*cb.ConfigGroupSchema{},
    43  	Values: map[string]*cb.ConfigValueSchema{
    44  		"MSP": nil, // TODO, consolidate into a constant once common org code exists
    45  	},
    46  	Policies: map[string]*cb.ConfigPolicySchema{
    47  	// TODO, set appropriately once hierarchical policies are implemented
    48  	},
    49  }
    50  
    51  var Schema = &cb.ConfigGroupSchema{
    52  	Groups: map[string]*cb.ConfigGroupSchema{
    53  		"": orgSchema,
    54  	},
    55  	Values: map[string]*cb.ConfigValueSchema{
    56  		ConsensusTypeKey:            nil,
    57  		BatchSizeKey:                nil,
    58  		BatchTimeoutKey:             nil,
    59  		ChainCreationPolicyNamesKey: nil,
    60  		KafkaBrokersKey:             nil,
    61  		IngressPolicyNamesKey:       nil,
    62  		EgressPolicyNamesKey:        nil,
    63  	},
    64  	Policies: map[string]*cb.ConfigPolicySchema{
    65  	// TODO, set appropriately once hierarchical policies are implemented
    66  	},
    67  }
    68  
    69  const (
    70  	// ConsensusTypeKey is the cb.ConfigItem type key name for the ConsensusType message
    71  	ConsensusTypeKey = "ConsensusType"
    72  
    73  	// BatchSizeKey is the cb.ConfigItem type key name for the BatchSize message
    74  	BatchSizeKey = "BatchSize"
    75  
    76  	// BatchTimeoutKey is the cb.ConfigItem type key name for the BatchTimeout message
    77  	BatchTimeoutKey = "BatchTimeout"
    78  
    79  	// ChainCreationPolicyNamesKey is the cb.ConfigItem type key name for the ChainCreationPolicyNames message
    80  	ChainCreationPolicyNamesKey = "ChainCreationPolicyNames"
    81  
    82  	// KafkaBrokersKey is the cb.ConfigItem type key name for the KafkaBrokers message
    83  	KafkaBrokersKey = "KafkaBrokers"
    84  
    85  	// IngressPolicyNamesKey is the cb.ConfigItem type key name for the IngressPolicyNames message
    86  	IngressPolicyNamesKey = "IngressPolicyNames"
    87  
    88  	// EgressPolicyNamesKey is the cb.ConfigItem type key name for the EgressPolicyNames message
    89  	EgressPolicyNamesKey = "EgressPolicyNames"
    90  )
    91  
    92  var logger = logging.MustGetLogger("configtx/handlers/orderer")
    93  
    94  type ordererConfig struct {
    95  	consensusType            string
    96  	batchSize                *ab.BatchSize
    97  	batchTimeout             time.Duration
    98  	chainCreationPolicyNames []string
    99  	kafkaBrokers             []string
   100  	ingressPolicyNames       []string
   101  	egressPolicyNames        []string
   102  	orgs                     map[string]*organization.OrgConfig
   103  }
   104  
   105  // ManagerImpl is an implementation of configtxapi.OrdererConfig and configtxapi.ValueProposer
   106  type ManagerImpl struct {
   107  	pendingConfig *ordererConfig
   108  	config        *ordererConfig
   109  
   110  	mspConfig *msp.MSPConfigHandler
   111  }
   112  
   113  // NewManagerImpl creates a new ManagerImpl
   114  func NewManagerImpl(mspConfig *msp.MSPConfigHandler) *ManagerImpl {
   115  	return &ManagerImpl{
   116  		config:    &ordererConfig{},
   117  		mspConfig: mspConfig,
   118  	}
   119  }
   120  
   121  // ConsensusType returns the configured consensus type
   122  func (pm *ManagerImpl) ConsensusType() string {
   123  	return pm.config.consensusType
   124  }
   125  
   126  // BatchSize returns the maximum number of messages to include in a block
   127  func (pm *ManagerImpl) BatchSize() *ab.BatchSize {
   128  	return pm.config.batchSize
   129  }
   130  
   131  // BatchTimeout returns the amount of time to wait before creating a batch
   132  func (pm *ManagerImpl) BatchTimeout() time.Duration {
   133  	return pm.config.batchTimeout
   134  }
   135  
   136  // ChainCreationPolicyNames returns the policy names which are allowed for chain creation
   137  // This field is only set for the system ordering chain
   138  func (pm *ManagerImpl) ChainCreationPolicyNames() []string {
   139  	return pm.config.chainCreationPolicyNames
   140  }
   141  
   142  // KafkaBrokers returns the addresses (IP:port notation) of a set of "bootstrap"
   143  // Kafka brokers, i.e. this is not necessarily the entire set of Kafka brokers
   144  // used for ordering
   145  func (pm *ManagerImpl) KafkaBrokers() []string {
   146  	return pm.config.kafkaBrokers
   147  }
   148  
   149  // IngressPolicyNames returns the name of the policy to validate incoming broadcast messages against
   150  func (pm *ManagerImpl) IngressPolicyNames() []string {
   151  	return pm.config.ingressPolicyNames
   152  }
   153  
   154  // EgressPolicyNames returns the name of the policy to validate incoming deliver seeks against
   155  func (pm *ManagerImpl) EgressPolicyNames() []string {
   156  	return pm.config.egressPolicyNames
   157  }
   158  
   159  // BeginValueProposals is used to start a new config proposal
   160  func (pm *ManagerImpl) BeginValueProposals(groups []string) ([]api.ValueProposer, error) {
   161  	logger.Debugf("Beginning a possible new orderer shared config")
   162  	if pm.pendingConfig != nil {
   163  		logger.Panicf("Programming error, cannot call begin in the middle of a proposal")
   164  	}
   165  	pm.pendingConfig = &ordererConfig{
   166  		orgs: make(map[string]*organization.OrgConfig),
   167  	}
   168  	orgHandlers := make([]api.ValueProposer, len(groups))
   169  	for i, group := range groups {
   170  		org, ok := pm.pendingConfig.orgs[group]
   171  		if !ok {
   172  			org = organization.NewOrgConfig(group, pm.mspConfig)
   173  			pm.pendingConfig.orgs[group] = org
   174  		}
   175  		orgHandlers[i] = org
   176  	}
   177  	return orgHandlers, nil
   178  }
   179  
   180  // RollbackProposals is used to abandon a new config proposal
   181  func (pm *ManagerImpl) RollbackProposals() {
   182  	logger.Debugf("Rolling back orderer config")
   183  	pm.pendingConfig = nil
   184  }
   185  
   186  // CommitProposals is used to commit a new config proposal
   187  func (pm *ManagerImpl) CommitProposals() {
   188  	if pm.pendingConfig == nil {
   189  		logger.Fatalf("Programming error, cannot call commit without an existing proposal")
   190  	}
   191  	pm.config = pm.pendingConfig
   192  	pm.pendingConfig = nil
   193  	if logger.IsEnabledFor(logging.DEBUG) {
   194  		logger.Debugf("Adopting new orderer shared config: %+v", pm.config)
   195  	}
   196  }
   197  
   198  // ProposeValue is used to add new config to the config proposal
   199  func (pm *ManagerImpl) ProposeValue(key string, configValue *cb.ConfigValue) error {
   200  	switch key {
   201  	case ConsensusTypeKey:
   202  		consensusType := &ab.ConsensusType{}
   203  		if err := proto.Unmarshal(configValue.Value, consensusType); err != nil {
   204  			return fmt.Errorf("Unmarshaling error for ConsensusType: %s", err)
   205  		}
   206  		if pm.config.consensusType == "" {
   207  			// The first config we accept the consensus type regardless
   208  			pm.config.consensusType = consensusType.Type
   209  		}
   210  		if consensusType.Type != pm.config.consensusType {
   211  			return fmt.Errorf("Attempted to change the consensus type from %s to %s after init", pm.config.consensusType, consensusType.Type)
   212  		}
   213  		pm.pendingConfig.consensusType = consensusType.Type
   214  	case BatchSizeKey:
   215  		batchSize := &ab.BatchSize{}
   216  		if err := proto.Unmarshal(configValue.Value, batchSize); err != nil {
   217  			return fmt.Errorf("Unmarshaling error for BatchSize: %s", err)
   218  		}
   219  		if batchSize.MaxMessageCount == 0 {
   220  			return fmt.Errorf("Attempted to set the batch size max message count to an invalid value: 0")
   221  		}
   222  		if batchSize.AbsoluteMaxBytes == 0 {
   223  			return fmt.Errorf("Attempted to set the batch size absolute max bytes to an invalid value: 0")
   224  		}
   225  		if batchSize.PreferredMaxBytes == 0 {
   226  			return fmt.Errorf("Attempted to set the batch size preferred max bytes to an invalid value: 0")
   227  		}
   228  		if batchSize.PreferredMaxBytes > batchSize.AbsoluteMaxBytes {
   229  			return fmt.Errorf("Attempted to set the batch size preferred max bytes (%v) greater than the absolute max bytes (%v).", batchSize.PreferredMaxBytes, batchSize.AbsoluteMaxBytes)
   230  		}
   231  		pm.pendingConfig.batchSize = batchSize
   232  	case BatchTimeoutKey:
   233  		var timeoutValue time.Duration
   234  		var err error
   235  		batchTimeout := &ab.BatchTimeout{}
   236  		if err = proto.Unmarshal(configValue.Value, batchTimeout); err != nil {
   237  			return fmt.Errorf("Unmarshaling error for BatchTimeout: %s", err)
   238  		}
   239  		if timeoutValue, err = time.ParseDuration(batchTimeout.Timeout); err != nil {
   240  			return fmt.Errorf("Attempted to set the batch timeout to a invalid value: %s", err)
   241  		}
   242  		if timeoutValue <= 0 {
   243  			return fmt.Errorf("Attempted to set the batch timeout to a non-positive value: %s", timeoutValue.String())
   244  		}
   245  		pm.pendingConfig.batchTimeout = timeoutValue
   246  	case ChainCreationPolicyNamesKey:
   247  		chainCreationPolicyNames := &ab.ChainCreationPolicyNames{}
   248  		if err := proto.Unmarshal(configValue.Value, chainCreationPolicyNames); err != nil {
   249  			return fmt.Errorf("Unmarshaling error for ChainCreator: %s", err)
   250  		}
   251  		if chainCreationPolicyNames.Names == nil {
   252  			// Proto unmarshals empty slices to nil, but this poses a problem for us in detecting the system chain
   253  			// if it does not set this value, so explicitly set the policies to the empty string slice, if it is set
   254  			pm.pendingConfig.chainCreationPolicyNames = []string{}
   255  		} else {
   256  			pm.pendingConfig.chainCreationPolicyNames = chainCreationPolicyNames.Names
   257  		}
   258  	case IngressPolicyNamesKey:
   259  		ingressPolicyNames := &ab.IngressPolicyNames{}
   260  		if err := proto.Unmarshal(configValue.Value, ingressPolicyNames); err != nil {
   261  			return fmt.Errorf("Unmarshaling error for IngressPolicyNames: %s", err)
   262  		}
   263  		pm.pendingConfig.ingressPolicyNames = ingressPolicyNames.Names
   264  	case EgressPolicyNamesKey:
   265  		egressPolicyNames := &ab.EgressPolicyNames{}
   266  		if err := proto.Unmarshal(configValue.Value, egressPolicyNames); err != nil {
   267  			return fmt.Errorf("Unmarshaling error for EgressPolicyNames: %s", err)
   268  		}
   269  		pm.pendingConfig.egressPolicyNames = egressPolicyNames.Names
   270  	case KafkaBrokersKey:
   271  		kafkaBrokers := &ab.KafkaBrokers{}
   272  		if err := proto.Unmarshal(configValue.Value, kafkaBrokers); err != nil {
   273  			return fmt.Errorf("Unmarshaling error for KafkaBrokers: %s", err)
   274  		}
   275  		if len(kafkaBrokers.Brokers) == 0 {
   276  			return fmt.Errorf("Kafka broker set cannot be nil")
   277  		}
   278  		for _, broker := range kafkaBrokers.Brokers {
   279  			if !brokerEntrySeemsValid(broker) {
   280  				return fmt.Errorf("Invalid broker entry: %s", broker)
   281  			}
   282  		}
   283  		pm.pendingConfig.kafkaBrokers = kafkaBrokers.Brokers
   284  	}
   285  	return nil
   286  }
   287  
   288  // This does just a barebones sanity check.
   289  func brokerEntrySeemsValid(broker string) bool {
   290  	if !strings.Contains(broker, ":") {
   291  		return false
   292  	}
   293  
   294  	parts := strings.Split(broker, ":")
   295  	if len(parts) > 2 {
   296  		return false
   297  	}
   298  
   299  	host := parts[0]
   300  	port := parts[1]
   301  
   302  	if _, err := strconv.ParseUint(port, 10, 16); err != nil {
   303  		return false
   304  	}
   305  
   306  	// Valid hostnames may contain only the ASCII letters 'a' through 'z' (in a
   307  	// case-insensitive manner), the digits '0' through '9', and the hyphen. IP
   308  	// v4 addresses are  represented in dot-decimal notation, which consists of
   309  	// four decimal numbers, each ranging from 0 to 255, separated by dots,
   310  	// e.g., 172.16.254.1
   311  	// The following regular expression:
   312  	// 1. allows just a-z (case-insensitive), 0-9, and the dot and hyphen characters
   313  	// 2. does not allow leading trailing dots or hyphens
   314  	re, _ := regexp.Compile("^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9])$")
   315  	matched := re.FindString(host)
   316  	return len(matched) == len(host)
   317  }
   318  
   319  // PreCommit returns nil
   320  func (pm *ManagerImpl) PreCommit() error { return nil }