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 }