From 9f07b96e7b2ad039757e260d5142ba425c3157ee Mon Sep 17 00:00:00 2001 From: Jason Yellick Date: Wed, 25 Jan 2017 11:36:40 -0500 Subject: [PATCH] [FAB-1851] Add peer sharedconfig https://jira.hyperledger.org/browse/FAB-1851 This changeset adds a peer sharedconfig package. The sharedconfig is named as such, because it is configuration which is shared across all peers for a channel, as opposed to the local configuration which the peer sources from its yaml file. Change-Id: I46bc408ed15a065020722666ee8e50d9f679e665 Signed-off-by: Jason Yellick --- core/peer/peer.go | 5 +- peer/sharedconfig/sharedconfig.go | 114 +++++++++++++++++++++++++ peer/sharedconfig/sharedconfig_test.go | 102 ++++++++++++++++++++++ peer/sharedconfig/sharedconfig_util.go | 39 +++++++++ 4 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 peer/sharedconfig/sharedconfig.go create mode 100644 peer/sharedconfig/sharedconfig_test.go create mode 100644 peer/sharedconfig/sharedconfig_util.go diff --git a/core/peer/peer.go b/core/peer/peer.go index 4fdb9f3d66d..76ecadde8a5 100644 --- a/core/peer/peer.go +++ b/core/peer/peer.go @@ -35,6 +35,7 @@ import ( "github.com/hyperledger/fabric/core/ledger/ledgermgmt" "github.com/hyperledger/fabric/gossip/service" mspmgmt "github.com/hyperledger/fabric/msp/mgmt" + "github.com/hyperledger/fabric/peer/sharedconfig" "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/utils" ) @@ -160,8 +161,10 @@ func createChain(cid string, ledger ledger.PeerLedger, cb *common.Block) error { return err } + sharedConfigHandler := sharedconfig.NewDescriptorImpl() + configtxInitializer := configtx.NewInitializer() - // TODO Hook peer shared config manager in here once it exists + configtxInitializer.Handlers()[common.ConfigurationItem_Peer] = sharedConfigHandler configtxManager, err := configtx.NewManagerImpl(configEnvelope, configtxInitializer) if err != nil { return err diff --git a/peer/sharedconfig/sharedconfig.go b/peer/sharedconfig/sharedconfig.go new file mode 100644 index 00000000000..dd5e446bf3f --- /dev/null +++ b/peer/sharedconfig/sharedconfig.go @@ -0,0 +1,114 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sharedconfig + +import ( + "fmt" + + cb "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" + + "github.com/golang/protobuf/proto" + "github.com/op/go-logging" +) + +// Peer config keys +const ( + // AnchorPeersKey is the cb.ConfigurationItem type key name for the AnchorPeers message + AnchorPeersKey = "AnchorPeers" +) + +var logger = logging.MustGetLogger("peer/sharedconfig") + +// Descriptor stores the common peer configuration +// It is intended to be the primary accessor of DescriptorImpl +// It is intended to discourage use of the other exported DescriptorImpl methods +// which are used for updating the chain configuration by the configtx.Manager +type Descriptor interface { + // AnchorPeers returns the list of anchor peers for the channel + AnchorPeers() []*pb.AnchorPeer +} + +type sharedConfig struct { + anchorPeers []*pb.AnchorPeer +} + +// DescriptorImpl is an implementation of Manager and configtx.ConfigHandler +// In general, it should only be referenced as an Impl for the configtx.Manager +type DescriptorImpl struct { + pendingConfig *sharedConfig + config *sharedConfig +} + +// NewDescriptorImpl creates a new DescriptorImpl with the given CryptoHelper +func NewDescriptorImpl() *DescriptorImpl { + return &DescriptorImpl{ + config: &sharedConfig{}, + } +} + +// AnchorPeers returns the list of valid orderer addresses to connect to to invoke Broadcast/Deliver +func (di *DescriptorImpl) AnchorPeers() []*pb.AnchorPeer { + return di.config.anchorPeers +} + +// BeginConfig is used to start a new configuration proposal +func (di *DescriptorImpl) BeginConfig() { + logger.Debugf("Beginning a possible new peer shared configuration") + if di.pendingConfig != nil { + logger.Panicf("Programming error, cannot call begin in the middle of a proposal") + } + di.pendingConfig = &sharedConfig{} +} + +// RollbackConfig is used to abandon a new configuration proposal +func (di *DescriptorImpl) RollbackConfig() { + logger.Debugf("Rolling back proposed peer shared configuration") + di.pendingConfig = nil +} + +// CommitConfig is used to commit a new configuration proposal +func (di *DescriptorImpl) CommitConfig() { + logger.Debugf("Committing new peer shared configuration") + if di.pendingConfig == nil { + logger.Panicf("Programming error, cannot call commit without an existing proposal") + } + di.config = di.pendingConfig + di.pendingConfig = nil +} + +// ProposeConfig is used to add new configuration to the configuration proposal +func (di *DescriptorImpl) ProposeConfig(configItem *cb.ConfigurationItem) error { + if configItem.Type != cb.ConfigurationItem_Peer { + return fmt.Errorf("Expected type of ConfigurationItem_Peer, got %v", configItem.Type) + } + + switch configItem.Key { + case AnchorPeersKey: + anchorPeers := &pb.AnchorPeers{} + if err := proto.Unmarshal(configItem.Value, anchorPeers); err != nil { + return fmt.Errorf("Unmarshaling error for %s: %s", configItem.Key, err) + } + if logger.IsEnabledFor(logging.DEBUG) { + logger.Debugf("Setting %s to %v", configItem.Key, anchorPeers.AnchorPeers) + } + di.pendingConfig.anchorPeers = anchorPeers.AnchorPeers + default: + logger.Warningf("Uknown Peer configuration item with key %s", configItem.Key) + } + return nil +} diff --git a/peer/sharedconfig/sharedconfig_test.go b/peer/sharedconfig/sharedconfig_test.go new file mode 100644 index 00000000000..44b6ae4ee15 --- /dev/null +++ b/peer/sharedconfig/sharedconfig_test.go @@ -0,0 +1,102 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sharedconfig + +import ( + "reflect" + "testing" + + cb "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" + + logging "github.com/op/go-logging" +) + +func init() { + logging.SetLevel(logging.DEBUG, "") +} + +func makeInvalidConfigItem(key string) *cb.ConfigurationItem { + return &cb.ConfigurationItem{ + Type: cb.ConfigurationItem_Peer, + Key: key, + Value: []byte("Garbage Data"), + } +} + +func TestInterface(t *testing.T) { + _ = Descriptor(NewDescriptorImpl()) +} + +func TestDoubleBegin(t *testing.T) { + defer func() { + if err := recover(); err == nil { + t.Fatalf("Should have panicked on multiple begin configs") + } + }() + + m := NewDescriptorImpl() + m.BeginConfig() + m.BeginConfig() +} + +func TestCommitWithoutBegin(t *testing.T) { + defer func() { + if err := recover(); err == nil { + t.Fatalf("Should have panicked on multiple begin configs") + } + }() + + m := NewDescriptorImpl() + m.CommitConfig() +} + +func TestRollback(t *testing.T) { + m := NewDescriptorImpl() + m.pendingConfig = &sharedConfig{} + m.RollbackConfig() + if m.pendingConfig != nil { + t.Fatalf("Should have cleared pending config on rollback") + } +} + +func TestAnchorPeers(t *testing.T) { + endVal := []*pb.AnchorPeer{ + &pb.AnchorPeer{Host: "foo", Port: 234, Cert: []byte("foocert")}, + &pb.AnchorPeer{Host: "bar", Port: 237, Cert: []byte("barcert")}, + } + invalidMessage := makeInvalidConfigItem(AnchorPeersKey) + validMessage := TemplateAnchorPeers(endVal) + m := NewDescriptorImpl() + m.BeginConfig() + + err := m.ProposeConfig(invalidMessage) + if err == nil { + t.Fatalf("Should have failed on invalid message") + } + + err = m.ProposeConfig(validMessage) + if err != nil { + t.Fatalf("Error applying valid config: %s", err) + } + + m.CommitConfig() + + if newVal := m.AnchorPeers(); !reflect.DeepEqual(newVal, endVal) { + t.Fatalf("Unexpected anchor peers, got %v expected %v", newVal, endVal) + } +} diff --git a/peer/sharedconfig/sharedconfig_util.go b/peer/sharedconfig/sharedconfig_util.go new file mode 100644 index 00000000000..ab109881de1 --- /dev/null +++ b/peer/sharedconfig/sharedconfig_util.go @@ -0,0 +1,39 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sharedconfig + +import ( + cb "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" + "github.com/hyperledger/fabric/protos/utils" +) + +var defaultAnchorPeers = []*pb.AnchorPeer{} + +// TemplateAnchorPeers creates a headerless configuration item representing the anchor peers +func TemplateAnchorPeers(anchorPeers []*pb.AnchorPeer) *cb.ConfigurationItem { + return &cb.ConfigurationItem{ + Type: cb.ConfigurationItem_Peer, + Key: AnchorPeersKey, + Value: utils.MarshalOrPanic(&pb.AnchorPeers{AnchorPeers: anchorPeers}), + } +} + +// DefaultAnchorPeers creates a headerless configuration item for the default orderer addresses +func DefaultAnchorPeers() *cb.ConfigurationItem { + return TemplateAnchorPeers(defaultAnchorPeers) +}