From 639dc8797c1e339d0faaf4d5b359d07972134b26 Mon Sep 17 00:00:00 2001 From: Jason Yellick Date: Mon, 16 Jan 2017 15:47:02 -0500 Subject: [PATCH] [FAB-1679] Initial config tran inspector https://jira.hyperledger.org/browse/FAB-1679 This is an initial and incomplete pass at inspecting a configuration transaction. It is currently oriented very much towards developers as it produces a lot of redundant and or extraneous information, but is being written with an eye towards being utilized by other tools. Change-Id: I39f1ef869a2b1d2c24407aaa4d2071a07fce6f9d Signed-off-by: Jason Yellick --- common/configtx/inspector/common_types.go | 117 +++++++++++++++ common/configtx/inspector/inspector.go | 61 ++++++++ common/configtx/inspector/inspector_test.go | 36 +++++ common/configtx/inspector/orderer_types.go | 152 ++++++++++++++++++++ common/configtx/inspector/policy_types.go | 63 ++++++++ common/configtx/inspector/terminal_types.go | 81 +++++++++++ 6 files changed, 510 insertions(+) create mode 100644 common/configtx/inspector/common_types.go create mode 100644 common/configtx/inspector/inspector.go create mode 100644 common/configtx/inspector/inspector_test.go create mode 100644 common/configtx/inspector/orderer_types.go create mode 100644 common/configtx/inspector/policy_types.go create mode 100644 common/configtx/inspector/terminal_types.go diff --git a/common/configtx/inspector/common_types.go b/common/configtx/inspector/common_types.go new file mode 100644 index 00000000000..8fec55f706a --- /dev/null +++ b/common/configtx/inspector/common_types.go @@ -0,0 +1,117 @@ +/* +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 inspector + +import ( + "fmt" + cb "github.com/hyperledger/fabric/protos/common" + + "github.com/golang/protobuf/proto" +) + +func viewableConfigurationEnvelope(name string, configEnvelope *cb.ConfigurationEnvelope) Viewable { + return &field{ + name: name, + values: []Viewable{viewableSignedConfigurationItemSlice("Items", configEnvelope.Items)}, + } +} + +func viewableSignedConfigurationItemSlice(name string, signedConfigItems []*cb.SignedConfigurationItem) Viewable { + values := make([]Viewable, len(signedConfigItems)) + for i, item := range signedConfigItems { + values[i] = viewableSignedConfigurationItem(fmt.Sprintf("Element %d", i), item) + } + return &field{ + name: name, + values: values, + } +} + +func viewableSignedConfigurationItem(name string, signedConfigItem *cb.SignedConfigurationItem) Viewable { + var viewableConfigItem Viewable + + configItem := &cb.ConfigurationItem{} + + if err := proto.Unmarshal(signedConfigItem.ConfigurationItem, configItem); err != nil { + viewableConfigItem = viewableError(name, err) + } else { + viewableConfigItem = viewableConfigurationItem("ConfigurationItem", configItem) + } + + return &field{ + name: name, + values: []Viewable{viewableConfigItem, viewableConfigurationSignatureSlice("Signatures", signedConfigItem.Signatures)}, + } +} + +func viewableConfigurationSignatureSlice(name string, configSigs []*cb.ConfigurationSignature) Viewable { + values := make([]Viewable, len(configSigs)) + for i, item := range configSigs { + values[i] = viewableConfigurationSignature(fmt.Sprintf("Element %d", i), item) + } + return &field{ + name: name, + values: values, + } +} + +func viewableConfigurationSignature(name string, configSig *cb.ConfigurationSignature) Viewable { + children := make([]Viewable, 2) + + sigHeader := &cb.SignatureHeader{} + err := proto.Unmarshal(configSig.SignatureHeader, sigHeader) + if err == nil { + children[0] = viewableSignatureHeader("SignatureHeader", sigHeader) + } else { + children[0] = viewableError("SignatureHeader", err) + } + + children[1] = viewableBytes("Signature", configSig.Signature) + + return &field{ + name: name, + values: children, + } +} + +func viewableSignatureHeader(name string, sh *cb.SignatureHeader) Viewable { + return &field{ + name: name, + values: []Viewable{viewableBytes("Creator", sh.Creator), viewableBytes("Nonce", sh.Nonce)}, + } +} + +func viewableConfigurationItem(name string, ci *cb.ConfigurationItem) Viewable { + + values := make([]Viewable, 6) // Type, Key, Header, LastModified, ModificationPolicy, Value + values[0] = viewableString("Type", fmt.Sprintf("%v", ci.Type)) + values[1] = viewableString("Key", ci.Key) + values[2] = viewableString("Header", "TODO") + values[3] = viewableString("LastModified", fmt.Sprintf("%d", ci.LastModified)) + values[4] = viewableString("ModificationPolicy", ci.ModificationPolicy) + + typeFactory, ok := typeMap[ci.Type] + if ok { + values[5] = typeFactory.Value(ci) + } else { + values[5] = viewableError("Value", fmt.Errorf("Unknown message type: %v", ci.Type)) + } + return &field{ + name: name, + values: values, + } +} diff --git a/common/configtx/inspector/inspector.go b/common/configtx/inspector/inspector.go new file mode 100644 index 00000000000..340305283c2 --- /dev/null +++ b/common/configtx/inspector/inspector.go @@ -0,0 +1,61 @@ +/* +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 inspector + +import ( + "fmt" + cb "github.com/hyperledger/fabric/protos/common" +) + +var typeMap map[cb.ConfigurationItem_ConfigurationType]ConfigurationItemValueLens = make(map[cb.ConfigurationItem_ConfigurationType]ConfigurationItemValueLens) + +type ConfigurationItemValueLens interface { + // Value takes a config item and returns a Viewable version of its value + Value(configItem *cb.ConfigurationItem) Viewable +} + +type Viewable interface { + Value() string + Children() []Viewable +} + +type field struct { + name string + values []Viewable +} + +func (f *field) Value() string { + return fmt.Sprintf("%s:", f.name) +} + +func (f *field) Children() []Viewable { + return f.values +} + +const indent = 4 + +func printViewable(viewable Viewable, curDepth int) { + fmt.Printf(fmt.Sprintf("%%%ds%%s\n", curDepth*indent), "", viewable.Value()) + for _, child := range viewable.Children() { + printViewable(child, curDepth+1) + } +} + +func PrintConfiguration(configEnvelope *cb.ConfigurationEnvelope) { + viewable := viewableConfigurationEnvelope("ConfigurationEnvelope", configEnvelope) + printViewable(viewable, 0) +} diff --git a/common/configtx/inspector/inspector_test.go b/common/configtx/inspector/inspector_test.go new file mode 100644 index 00000000000..7a91def8fd8 --- /dev/null +++ b/common/configtx/inspector/inspector_test.go @@ -0,0 +1,36 @@ +/* +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 inspector + +import ( + "testing" + + configtxtest "github.com/hyperledger/fabric/common/configtx/test" + cb "github.com/hyperledger/fabric/protos/common" +) + +func TestFromTemplate(t *testing.T) { + ordererTemplate := configtxtest.GetOrdererTemplate() + signedItems, err := ordererTemplate.Items("SampleChainID") + if err != nil { + t.Fatalf("Error creating signed items: %s", err) + } + configEnvelope := &cb.ConfigurationEnvelope{ + Items: signedItems, + } + PrintConfiguration(configEnvelope) +} diff --git a/common/configtx/inspector/orderer_types.go b/common/configtx/inspector/orderer_types.go new file mode 100644 index 00000000000..6a779aeadc8 --- /dev/null +++ b/common/configtx/inspector/orderer_types.go @@ -0,0 +1,152 @@ +/* +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 inspector + +import ( + "fmt" + + "github.com/golang/protobuf/proto" + cb "github.com/hyperledger/fabric/protos/common" + ab "github.com/hyperledger/fabric/protos/orderer" +) + +// This file contains the functions needed to create Viewables for protos defined in +// the orderer configuration proto + +type ordererTypes struct{} + +func (ot ordererTypes) Value(configItem *cb.ConfigurationItem) Viewable { + name := "Value" + switch configItem.Key { + case "ConsensusType": + value := &ab.ConsensusType{} + if err := proto.Unmarshal(configItem.Value, value); err != nil { + return viewableError(name, err) + } + return viewableConsensusType(configItem.Key, value) + case "BatchSize": + value := &ab.BatchSize{} + if err := proto.Unmarshal(configItem.Value, value); err != nil { + return viewableError(name, err) + } + return viewableBatchSize(configItem.Key, value) + case "BatchTimeout": + value := &ab.BatchTimeout{} + if err := proto.Unmarshal(configItem.Value, value); err != nil { + return viewableError(name, err) + } + return viewableBatchTimeout(configItem.Key, value) + case "CreationPolicy": + value := &ab.CreationPolicy{} + if err := proto.Unmarshal(configItem.Value, value); err != nil { + return viewableError(name, err) + } + return viewableCreationPolicy(configItem.Key, value) + case "IngressPolicy": + value := &ab.IngressPolicy{} + if err := proto.Unmarshal(configItem.Value, value); err != nil { + return viewableError(name, err) + } + return viewableIngressPolicy(configItem.Key, value) + case "EgressPolicy": + value := &ab.EgressPolicy{} + if err := proto.Unmarshal(configItem.Value, value); err != nil { + return viewableError(name, err) + } + return viewableEgressPolicy(configItem.Key, value) + case "ChainCreators": + value := &ab.ChainCreators{} + if err := proto.Unmarshal(configItem.Value, value); err != nil { + return viewableError(name, err) + } + return viewableChainCreators(configItem.Key, value) + case "KafkaBrokers": + value := &ab.KafkaBrokers{} + if err := proto.Unmarshal(configItem.Value, value); err != nil { + return viewableError(name, err) + } + return viewableKafkaBrokers(configItem.Key, value) + default: + } + return viewableError(name, fmt.Errorf("Unknown key: %s", configItem.Key)) +} + +func viewableConsensusType(name string, consensusType *ab.ConsensusType) Viewable { + return &field{ + name: name, + values: []Viewable{viewableString("Type", consensusType.Type)}, + } +} + +func viewableBatchTimeout(name string, batchTimeout *ab.BatchTimeout) Viewable { + return &field{ + name: name, + values: []Viewable{viewableString("Timeout", batchTimeout.Timeout)}, + } +} + +func viewableIngressPolicy(name string, ingressPolicy *ab.IngressPolicy) Viewable { + return &field{ + name: name, + values: []Viewable{viewableString("Name", ingressPolicy.Name)}, + } +} + +func viewableEgressPolicy(name string, egressPolicy *ab.EgressPolicy) Viewable { + return &field{ + name: name, + values: []Viewable{viewableString("Name", egressPolicy.Name)}, + } +} + +func viewableChainCreators(name string, creators *ab.ChainCreators) Viewable { + return &field{ + name: name, + values: []Viewable{viewableStringSlice("Policies", creators.Policies)}, + } +} + +func viewableKafkaBrokers(name string, brokers *ab.KafkaBrokers) Viewable { + return &field{ + name: name, + values: []Viewable{viewableStringSlice("Brokers", brokers.Brokers)}, + } +} + +func viewableCreationPolicy(name string, creationPolicy *ab.CreationPolicy) Viewable { + return &field{ + name: name, + values: []Viewable{ + viewableString("Policy", creationPolicy.Policy), + viewableBytes("Digest", creationPolicy.Digest), + }, + } +} + +func viewableBatchSize(name string, batchSize *ab.BatchSize) Viewable { + return &field{ + name: name, + values: []Viewable{ + viewableUint32("MaxMessageCount", batchSize.MaxMessageCount), + viewableUint32("AbsoluteMaxBytes", batchSize.AbsoluteMaxBytes), + }, + } +} + +func init() { + typeMap[cb.ConfigurationItem_Orderer] = ordererTypes{} +} diff --git a/common/configtx/inspector/policy_types.go b/common/configtx/inspector/policy_types.go new file mode 100644 index 00000000000..3ecfb478e83 --- /dev/null +++ b/common/configtx/inspector/policy_types.go @@ -0,0 +1,63 @@ +/* +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 inspector + +import ( + "fmt" + + cb "github.com/hyperledger/fabric/protos/common" + + "github.com/golang/protobuf/proto" +) + +type policyTypes struct{} + +func (ot policyTypes) Value(configItem *cb.ConfigurationItem) Viewable { + name := "Value" + value := &cb.Policy{} + if err := proto.Unmarshal(configItem.Value, value); err != nil { + return viewableError(name, err) + } + + values := make([]Viewable, 2) + values[0] = viewableInt32("Type", value.Type) + + switch value.Type { + case int32(cb.Policy_SIGNATURE): + sPolicy := &cb.SignaturePolicyEnvelope{} + if err := proto.Unmarshal(value.Policy, sPolicy); err != nil { + values[1] = viewableError("Policy", err) + } + + values[1] = viewableSignaturePolicyEnvelope("Policy", sPolicy) + default: + values[1] = viewableError("Policy", fmt.Errorf("Unknown policy type: %s", value.Type)) + } + + return &field{ + name: name, + values: values, + } +} + +func viewableSignaturePolicyEnvelope(name string, signaturePolicyEnvelope *cb.SignaturePolicyEnvelope) Viewable { + return viewableString(name, "TODO") +} + +func init() { + typeMap[cb.ConfigurationItem_Policy] = policyTypes{} +} diff --git a/common/configtx/inspector/terminal_types.go b/common/configtx/inspector/terminal_types.go new file mode 100644 index 00000000000..f9e658502cf --- /dev/null +++ b/common/configtx/inspector/terminal_types.go @@ -0,0 +1,81 @@ +/* +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 inspector + +import ( + "fmt" +) + +// This file contains helper functions which create Viewables for fields with no more subfields +// For instance, to display an error, string, slice of strings, etc. + +func viewableError(name string, source error) Viewable { + return &field{ + name: name, + values: []Viewable{&field{ + name: fmt.Sprintf("ERROR: %s", source), + }}, + } +} + +func viewableBytes(name string, source []byte) Viewable { + return &field{ + name: name, + values: []Viewable{&field{ + name: fmt.Sprintf("%x", source), + }}, + } +} + +func viewableString(name string, source string) Viewable { + return &field{ + name: name, + values: []Viewable{&field{ + name: source, + }}, + } +} + +func viewableUint32(name string, source uint32) Viewable { + return &field{ + name: name, + values: []Viewable{&field{ + name: fmt.Sprintf("%d", source), + }}, + } +} + +func viewableInt32(name string, source int32) Viewable { + return &field{ + name: name, + values: []Viewable{&field{ + name: fmt.Sprintf("%d", source), + }}, + } +} + +func viewableStringSlice(name string, source []string) Viewable { + values := make([]Viewable, len(source)) + for i, str := range source { + values[i] = viewableString(fmt.Sprintf("Index %d", i), str) + } + + return &field{ + name: name, + values: values, + } +}