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 }