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 }