From 86f65d3bf31ea1e4d14be497aab89adfe02f5565 Mon Sep 17 00:00:00 2001 From: Jason Yellick Date: Wed, 1 Mar 2017 18:59:42 -0500 Subject: [PATCH] [FAB-2577] Add JSON rendering of configResult https://jira.hyperledger.org/browse/FAB-2577 FAB-2574 split the config result from the config manager to allow other pieces fo the system to utilize it. This CR adds the ability for the config result to render the config result as JSON to be used in a later changeset for inspecting the configuration. Change-Id: Ieeccaf8486706f0a47ea09196eca12caae007a2b Signed-off-by: Jason Yellick --- common/configtx/config.go | 73 +++++++++++++++++++++++++++++++ common/configtx/config_test.go | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 common/configtx/config_test.go diff --git a/common/configtx/config.go b/common/configtx/config.go index 7fd4292bbbc..ac29dc314d3 100644 --- a/common/configtx/config.go +++ b/common/configtx/config.go @@ -17,6 +17,7 @@ limitations under the License. package configtx import ( + "bytes" "fmt" "github.com/hyperledger/fabric/common/config" @@ -24,6 +25,7 @@ import ( "github.com/hyperledger/fabric/common/policies" cb "github.com/hyperledger/fabric/protos/common" + "github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/proto" ) @@ -37,6 +39,77 @@ type configResult struct { deserializedValues map[string]proto.Message } +func (cr *configResult) JSON() string { + var buffer bytes.Buffer + buffer.WriteString("{") + cr.bufferJSON(&buffer) + buffer.WriteString("}") + return buffer.String() + +} + +// bufferJSON takes a buffer and writes a JSON representation of the configResult into the buffer +// Note that we use some mildly ad-hoc JSON encoding because the proto documentation explicitly +// mentions that the encoding/json package does not correctly marshal proto objects, and we +// do not have a proto object (nor can one be defined) which presents the mixed-map style of +// keys mapping to different types of the config +func (cr *configResult) bufferJSON(buffer *bytes.Buffer) { + jpb := &jsonpb.Marshaler{ + EmitDefaults: true, + Indent: " ", + } + + // "GroupName": { + buffer.WriteString("\"") + buffer.WriteString(cr.groupName) + buffer.WriteString("\": {") + + // "Values": { + buffer.WriteString("\"Values\": {") + count := 0 + for key, value := range cr.group.Values { + // "Key": { + buffer.WriteString("\"") + buffer.WriteString(key) + buffer.WriteString("\": {") + // "Version": "X", + buffer.WriteString("\"Version\":\"") + buffer.WriteString(fmt.Sprintf("%d", value.Version)) + buffer.WriteString("\",") + // "ModPolicy": "foo", + buffer.WriteString("\"ModPolicy\":\"") + buffer.WriteString(value.ModPolicy) + buffer.WriteString("\",") + // "Value": protoAsJSON + buffer.WriteString("\"Value\":") + jpb.Marshal(buffer, cr.deserializedValues[key]) + // }, + buffer.WriteString("}") + count++ + if count < len(cr.group.Values) { + buffer.WriteString(",") + } + } + // }, + buffer.WriteString("},") + + // "Groups": { + count = 0 + buffer.WriteString("\"Groups\": {") + for _, subResult := range cr.subResults { + subResult.bufferJSON(buffer) + count++ + if count < len(cr.subResults) { + buffer.WriteString(",") + } + } + // } + buffer.WriteString("}") + + // } + buffer.WriteString("}") +} + func (cr *configResult) preCommit() error { for _, subResult := range cr.subResults { err := subResult.preCommit() diff --git a/common/configtx/config_test.go b/common/configtx/config_test.go new file mode 100644 index 00000000000..73074e466ef --- /dev/null +++ b/common/configtx/config_test.go @@ -0,0 +1,79 @@ +/* +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 configtx + +import ( + "bytes" + "encoding/json" + "strings" + "testing" + + cb "github.com/hyperledger/fabric/protos/common" + ab "github.com/hyperledger/fabric/protos/orderer" + + "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/assert" +) + +func TestJSON(t *testing.T) { + cr := &configResult{ + groupName: "rootGroup", + group: &cb.ConfigGroup{ + Values: map[string]*cb.ConfigValue{ + "outer": &cb.ConfigValue{Version: 1, ModPolicy: "mod1"}, + }, + }, + subResults: []*configResult{ + &configResult{ + groupName: "innerGroup1", + group: &cb.ConfigGroup{ + Values: map[string]*cb.ConfigValue{ + "inner1": &cb.ConfigValue{ModPolicy: "mod3"}, + }, + }, + deserializedValues: map[string]proto.Message{ + "inner1": &ab.ConsensusType{Type: "inner1"}, + }, + }, + &configResult{ + groupName: "innerGroup2", + group: &cb.ConfigGroup{ + Values: map[string]*cb.ConfigValue{ + "inner2": &cb.ConfigValue{ModPolicy: "mod3"}, + }, + }, + deserializedValues: map[string]proto.Message{ + "inner2": &ab.ConsensusType{Type: "inner2"}, + }, + }, + }, + deserializedValues: map[string]proto.Message{ + "outer": &ab.ConsensusType{Type: "outer"}, + }, + } + + buffer := &bytes.Buffer{} + assert.NoError(t, json.Indent(buffer, []byte(cr.JSON()), "", ""), "JSON should parse nicely") + + expected := "{\"rootGroup\":{\"Values\":{\"outer\":{\"Version\":\"1\",\"ModPolicy\":\"mod1\",\"Value\":{\"type\":\"outer\"}}},\"Groups\":{\"innerGroup1\":{\"Values\":{\"inner1\":{\"Version\":\"0\",\"ModPolicy\":\"mod3\",\"Value\":{\"type\":\"inner1\"}}},\"Groups\":{}},\"innerGroup2\":{\"Values\":{\"inner2\":{\"Version\":\"0\",\"ModPolicy\":\"mod3\",\"Value\":{\"type\":\"inner2\"}}},\"Groups\":{}}}}}" + + // Remove all newlines and spaces from the JSON + compactedJSON := strings.Replace(strings.Replace(buffer.String(), "\n", "", -1), " ", "", -1) + + assert.Equal(t, expected, compactedJSON) + +}