github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/common/configvalues/root/proposer.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 config 18 19 import ( 20 "fmt" 21 22 api "github.com/hyperledger/fabric/common/configvalues" 23 cb "github.com/hyperledger/fabric/protos/common" 24 25 "github.com/golang/protobuf/proto" 26 logging "github.com/op/go-logging" 27 ) 28 29 var logger = logging.MustGetLogger("common/config") 30 31 // Values defines a mechanism to supply messages to unamrshal from config 32 // and a mechanism to validate the results 33 type Values interface { 34 // ProtoMsg behaves like a map lookup for key 35 ProtoMsg(key string) (proto.Message, bool) 36 37 // Validate should ensure that the values set into the proto messages are correct 38 Validate() error 39 40 // Commit should call back into the Value handler to update the config 41 Commit() 42 } 43 44 // Handler 45 type Handler interface { 46 Allocate() Values 47 NewGroup(name string) (api.ValueProposer, error) 48 } 49 50 type config struct { 51 allocated Values 52 groups map[string]api.ValueProposer 53 } 54 55 type Proposer struct { 56 vh Handler 57 current *config 58 pending *config 59 } 60 61 func NewProposer(vh Handler) *Proposer { 62 return &Proposer{ 63 vh: vh, 64 } 65 } 66 67 // BeginValueProposals called when a config proposal is begun 68 func (p *Proposer) BeginValueProposals(groups []string) ([]api.ValueProposer, error) { 69 if p.pending != nil { 70 logger.Panicf("Duplicated BeginValueProposals without Rollback or Commit") 71 } 72 73 result := make([]api.ValueProposer, len(groups)) 74 75 p.pending = &config{ 76 allocated: p.vh.Allocate(), 77 groups: make(map[string]api.ValueProposer), 78 } 79 80 for i, groupName := range groups { 81 var group api.ValueProposer 82 var ok bool 83 84 if p.current == nil { 85 ok = false 86 } else { 87 group, ok = p.current.groups[groupName] 88 } 89 90 if !ok { 91 var err error 92 group, err = p.vh.NewGroup(groupName) 93 if err != nil { 94 p.pending = nil 95 return nil, fmt.Errorf("Error creating group %s: %s", groupName, err) 96 } 97 } 98 99 p.pending.groups[groupName] = group 100 result[i] = group 101 } 102 103 return result, nil 104 } 105 106 // ProposeValue called when config is added to a proposal 107 func (p *Proposer) ProposeValue(key string, configValue *cb.ConfigValue) error { 108 msg, ok := p.pending.allocated.ProtoMsg(key) 109 if !ok { 110 return fmt.Errorf("Unknown value key: %s", key) 111 } 112 113 if err := proto.Unmarshal(configValue.Value, msg); err != nil { 114 return fmt.Errorf("Error unmarshaling key to proto message: %s", err) 115 } 116 117 return nil 118 } 119 120 // Validate ensures that the new config values is a valid change 121 func (p *Proposer) PreCommit() error { 122 return p.pending.allocated.Validate() 123 } 124 125 // RollbackProposals called when a config proposal is abandoned 126 func (p *Proposer) RollbackProposals() { 127 p.pending = nil 128 } 129 130 // CommitProposals called when a config proposal is committed 131 func (p *Proposer) CommitProposals() { 132 if p.pending == nil { 133 logger.Panicf("Attempted to commit with no pending values (indicates no Begin invoked)") 134 } 135 p.current = p.pending 136 p.current.allocated.Commit() 137 p.pending = nil 138 }