github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/common/config/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  	"sync"
    22  
    23  	"github.com/golang/protobuf/proto"
    24  	"github.com/hyperledger/fabric/common/flogging"
    25  )
    26  
    27  var logger = flogging.MustGetLogger("common/config")
    28  
    29  // ValueDeserializer provides a mechanism to retrieve proto messages to deserialize config values into
    30  type ValueDeserializer interface {
    31  	// Deserialize takes a Value key as a string, and a marshaled Value value as bytes
    32  	// and returns the deserialized version of that value.  Note, this function operates
    33  	// with side effects intended.  Using a ValueDeserializer to deserialize a message will
    34  	// generally set the value in the Values interface that the ValueDeserializer derived from
    35  	// Therefore, the proto.Message may be safely discarded, but may be retained for
    36  	// inspection and or debugging purposes.
    37  	Deserialize(key string, value []byte) (proto.Message, error)
    38  }
    39  
    40  // Values defines a mechanism to supply messages to unamrshal from config
    41  // and a mechanism to validate the results
    42  type Values interface {
    43  	ValueDeserializer
    44  
    45  	// Validate should ensure that the values set into the proto messages are correct
    46  	// and that the new group values are allowed.  It also includes a tx ID in case cross
    47  	// Handler invocations (ie to the MSP Config Manager) must be made
    48  	Validate(interface{}, map[string]ValueProposer) error
    49  
    50  	// Commit should call back into the Value handler to update the config
    51  	Commit()
    52  }
    53  
    54  // Handler
    55  type Handler interface {
    56  	Allocate() Values
    57  	NewGroup(name string) (ValueProposer, error)
    58  }
    59  
    60  type config struct {
    61  	allocated Values
    62  	groups    map[string]ValueProposer
    63  }
    64  
    65  type Proposer struct {
    66  	vh          Handler
    67  	pending     map[interface{}]*config
    68  	current     *config
    69  	pendingLock sync.RWMutex
    70  }
    71  
    72  func NewProposer(vh Handler) *Proposer {
    73  	return &Proposer{
    74  		vh:      vh,
    75  		current: &config{},
    76  		pending: make(map[interface{}]*config),
    77  	}
    78  }
    79  
    80  // BeginValueProposals called when a config proposal is begun
    81  func (p *Proposer) BeginValueProposals(tx interface{}, groups []string) (ValueDeserializer, []ValueProposer, error) {
    82  	p.pendingLock.Lock()
    83  	defer p.pendingLock.Unlock()
    84  	if _, ok := p.pending[tx]; ok {
    85  		logger.Panicf("Duplicated BeginValueProposals without Rollback or Commit")
    86  	}
    87  
    88  	result := make([]ValueProposer, len(groups))
    89  
    90  	pending := &config{
    91  		allocated: p.vh.Allocate(),
    92  		groups:    make(map[string]ValueProposer),
    93  	}
    94  
    95  	for i, groupName := range groups {
    96  		var group ValueProposer
    97  		var ok bool
    98  
    99  		if p.current == nil {
   100  			ok = false
   101  		} else {
   102  			group, ok = p.current.groups[groupName]
   103  		}
   104  
   105  		if !ok {
   106  			var err error
   107  			group, err = p.vh.NewGroup(groupName)
   108  			if err != nil {
   109  				pending = nil
   110  				return nil, nil, fmt.Errorf("Error creating group %s: %s", groupName, err)
   111  			}
   112  		}
   113  
   114  		pending.groups[groupName] = group
   115  		result[i] = group
   116  	}
   117  
   118  	p.pending[tx] = pending
   119  
   120  	return pending.allocated, result, nil
   121  }
   122  
   123  // Validate ensures that the new config values is a valid change
   124  func (p *Proposer) PreCommit(tx interface{}) error {
   125  	p.pendingLock.RLock()
   126  	pending, ok := p.pending[tx]
   127  	p.pendingLock.RUnlock()
   128  	if !ok {
   129  		logger.Panicf("Serious Programming Error: attempted to pre-commit tx which had not been begun")
   130  	}
   131  	return pending.allocated.Validate(tx, pending.groups)
   132  }
   133  
   134  // RollbackProposals called when a config proposal is abandoned
   135  func (p *Proposer) RollbackProposals(tx interface{}) {
   136  	p.pendingLock.Lock()
   137  	defer p.pendingLock.Unlock()
   138  	delete(p.pending, tx)
   139  }
   140  
   141  // CommitProposals called when a config proposal is committed
   142  func (p *Proposer) CommitProposals(tx interface{}) {
   143  	p.pendingLock.Lock()
   144  	defer p.pendingLock.Unlock()
   145  	pending, ok := p.pending[tx]
   146  	if !ok {
   147  		logger.Panicf("Serious Programming Error: attempted to commit tx which had not been begun")
   148  	}
   149  	p.current = pending
   150  	p.current.allocated.Commit()
   151  	delete(p.pending, tx)
   152  }