350 lines
11 KiB
Go
350 lines
11 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package config_test
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/hyperledger/fabric-protos-go/common"
|
|
"github.com/hyperledger/fabric-protos-go/discovery"
|
|
"github.com/hyperledger/fabric-protos-go/msp"
|
|
"github.com/hyperledger/fabric/common/channelconfig"
|
|
"github.com/hyperledger/fabric/common/configtx/test"
|
|
"github.com/hyperledger/fabric/discovery/support/config"
|
|
"github.com/hyperledger/fabric/discovery/support/mocks"
|
|
"github.com/hyperledger/fabric/internal/configtxgen/encoder"
|
|
"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
"github.com/onsi/gomega/gexec"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestMSPIDMapping(t *testing.T) {
|
|
randString := func() string {
|
|
buff := make([]byte, 10)
|
|
rand.Read(buff)
|
|
return hex.EncodeToString(buff)
|
|
}
|
|
|
|
dir := filepath.Join(os.TempDir(), fmt.Sprintf("TestMSPIDMapping_%s", randString()))
|
|
os.Mkdir(dir, 0o700)
|
|
defer os.RemoveAll(dir)
|
|
|
|
cryptogen, err := gexec.Build("github.com/hyperledger/fabric/cmd/cryptogen")
|
|
require.NoError(t, err)
|
|
defer os.Remove(cryptogen)
|
|
|
|
idemixgen, err := gexec.Build("github.com/IBM/idemix/tools/idemixgen", "-mod=mod")
|
|
require.NoError(t, err)
|
|
defer os.Remove(idemixgen)
|
|
|
|
cryptoConfigDir := filepath.Join(dir, "crypto-config")
|
|
b, err := exec.Command(cryptogen, "generate", fmt.Sprintf("--output=%s", cryptoConfigDir)).CombinedOutput()
|
|
require.NoError(t, err, string(b))
|
|
|
|
idemixConfigDir := filepath.Join(dir, "crypto-config", "idemix")
|
|
b, err = exec.Command(idemixgen, "ca-keygen", fmt.Sprintf("--output=%s", idemixConfigDir)).CombinedOutput()
|
|
require.NoError(t, err, string(b))
|
|
|
|
profileConfig := genesisconfig.Load("TwoOrgsChannel", "testdata/")
|
|
ordererConfig := genesisconfig.Load("TwoOrgsOrdererGenesis", "testdata/")
|
|
profileConfig.Orderer = ordererConfig.Orderer
|
|
|
|
// Override the MSP directory with our randomly generated and populated path
|
|
for _, org := range ordererConfig.Orderer.Organizations {
|
|
org.MSPDir = filepath.Join(cryptoConfigDir, "ordererOrganizations", "example.com", "msp")
|
|
org.Name = randString()
|
|
}
|
|
|
|
// Randomize organization names
|
|
for _, org := range profileConfig.Application.Organizations {
|
|
org.Name = randString()
|
|
// Non bccsp-msp orgs don't have the crypto material produced by cryptogen,
|
|
// we need to use the idemix crypto folder instead.
|
|
if org.MSPType != "bccsp" {
|
|
org.MSPDir = filepath.Join(idemixConfigDir)
|
|
continue
|
|
}
|
|
org.MSPDir = filepath.Join(cryptoConfigDir, "peerOrganizations", "org1.example.com", "msp")
|
|
}
|
|
|
|
channelGroup, err := encoder.NewChannelGroup(profileConfig)
|
|
require.NoError(t, err)
|
|
fakeConfigGetter := &mocks.ConfigGetter{}
|
|
fakeConfigGetter.GetCurrConfigReturnsOnCall(
|
|
0,
|
|
&common.Config{
|
|
ChannelGroup: channelGroup,
|
|
},
|
|
)
|
|
|
|
cs := config.NewDiscoverySupport(fakeConfigGetter)
|
|
res, err := cs.Config("mychannel")
|
|
require.NoError(t, err)
|
|
|
|
actualKeys := make(map[string]struct{})
|
|
for key := range res.Orderers {
|
|
actualKeys[key] = struct{}{}
|
|
}
|
|
|
|
for key := range res.Msps {
|
|
actualKeys[key] = struct{}{}
|
|
}
|
|
|
|
// Note that Org3MSP is an idemix org, but it shouldn't be listed here
|
|
// because peers can't have idemix credentials
|
|
expected := map[string]struct{}{
|
|
"OrdererMSP": {},
|
|
"Org1MSP": {},
|
|
"Org2MSP": {},
|
|
}
|
|
require.Equal(t, expected, actualKeys)
|
|
}
|
|
|
|
func TestSupportGreenPath(t *testing.T) {
|
|
fakeConfigGetter := &mocks.ConfigGetter{}
|
|
fakeConfigGetter.GetCurrConfigReturnsOnCall(0, nil)
|
|
|
|
cs := config.NewDiscoverySupport(fakeConfigGetter)
|
|
res, err := cs.Config("test")
|
|
require.Nil(t, res)
|
|
require.Equal(t, "could not get last config for channel test", err.Error())
|
|
|
|
config, err := test.MakeChannelConfig("test")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config)
|
|
|
|
fakeConfigGetter.GetCurrConfigReturnsOnCall(1, config)
|
|
res, err = cs.Config("test")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
}
|
|
|
|
func TestValidateConfig(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config *common.Config
|
|
containsError string
|
|
}{
|
|
{
|
|
name: "nil Config field",
|
|
config: &common.Config{},
|
|
containsError: "field Config.ChannelGroup is nil",
|
|
},
|
|
{
|
|
name: "nil Groups field",
|
|
config: &common.Config{
|
|
ChannelGroup: &common.ConfigGroup{},
|
|
},
|
|
containsError: "field Config.ChannelGroup.Groups is nil",
|
|
},
|
|
{
|
|
name: "no orderer group key",
|
|
config: &common.Config{
|
|
ChannelGroup: &common.ConfigGroup{
|
|
Groups: map[string]*common.ConfigGroup{
|
|
channelconfig.ApplicationGroupKey: {},
|
|
},
|
|
},
|
|
},
|
|
containsError: "key Config.ChannelGroup.Groups[Orderer] is missing",
|
|
},
|
|
{
|
|
name: "no application group key",
|
|
config: &common.Config{
|
|
ChannelGroup: &common.ConfigGroup{
|
|
Groups: map[string]*common.ConfigGroup{
|
|
channelconfig.OrdererGroupKey: {
|
|
Groups: map[string]*common.ConfigGroup{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
containsError: "key Config.ChannelGroup.Groups[Application] is missing",
|
|
},
|
|
{
|
|
name: "no groups key in orderer group",
|
|
config: &common.Config{
|
|
ChannelGroup: &common.ConfigGroup{
|
|
Groups: map[string]*common.ConfigGroup{
|
|
channelconfig.ApplicationGroupKey: {
|
|
Groups: map[string]*common.ConfigGroup{},
|
|
},
|
|
channelconfig.OrdererGroupKey: {},
|
|
},
|
|
},
|
|
},
|
|
containsError: "key Config.ChannelGroup.Groups[Orderer].Groups is nil",
|
|
},
|
|
{
|
|
name: "no groups key in application group",
|
|
config: &common.Config{
|
|
ChannelGroup: &common.ConfigGroup{
|
|
Groups: map[string]*common.ConfigGroup{
|
|
channelconfig.ApplicationGroupKey: {},
|
|
channelconfig.OrdererGroupKey: {
|
|
Groups: map[string]*common.ConfigGroup{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
containsError: "key Config.ChannelGroup.Groups[Application].Groups is nil",
|
|
},
|
|
{
|
|
name: "no Values in ChannelGroup",
|
|
config: &common.Config{
|
|
ChannelGroup: &common.ConfigGroup{
|
|
Groups: map[string]*common.ConfigGroup{
|
|
channelconfig.ApplicationGroupKey: {
|
|
Groups: map[string]*common.ConfigGroup{},
|
|
},
|
|
channelconfig.OrdererGroupKey: {
|
|
Groups: map[string]*common.ConfigGroup{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
containsError: "field Config.ChannelGroup.Values is nil",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
test := test
|
|
t.Run(test.name, func(t *testing.T) {
|
|
err := config.ValidateConfig(test.config)
|
|
require.Contains(t, test.containsError, err.Error())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOrdererEndpoints(t *testing.T) {
|
|
t.Run("Global endpoints", func(t *testing.T) {
|
|
channelConfig, err := test.MakeChannelConfig("mychannel")
|
|
require.NoError(t, err)
|
|
|
|
fakeConfigGetter := &mocks.ConfigGetter{}
|
|
cs := config.NewDiscoverySupport(fakeConfigGetter)
|
|
|
|
fakeConfigGetter.GetCurrConfigReturnsOnCall(0, channelConfig)
|
|
|
|
injectGlobalOrdererEndpoint(t, channelConfig, "globalEndpoint:7050")
|
|
|
|
res, err := cs.Config("test")
|
|
require.NoError(t, err)
|
|
require.Equal(t, map[string]*discovery.Endpoints{
|
|
"SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "globalEndpoint", Port: 7050}}},
|
|
}, res.Orderers)
|
|
})
|
|
|
|
t.Run("Per org endpoints alongside global endpoints", func(t *testing.T) {
|
|
channelConfig, err := test.MakeChannelConfig("mychannel")
|
|
require.NoError(t, err)
|
|
|
|
fakeConfigGetter := &mocks.ConfigGetter{}
|
|
cs := config.NewDiscoverySupport(fakeConfigGetter)
|
|
|
|
fakeConfigGetter.GetCurrConfigReturnsOnCall(0, channelConfig)
|
|
|
|
injectAdditionalEndpointPair(t, channelConfig, "perOrgEndpoint:7050", "anotherOrg")
|
|
injectAdditionalEndpointPair(t, channelConfig, "endpointWithoutAPortName", "aBadOrg")
|
|
|
|
res, err := cs.Config("test")
|
|
require.NoError(t, err)
|
|
require.Equal(t, map[string]*discovery.Endpoints{
|
|
"SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "127.0.0.1", Port: 7050}}},
|
|
"anotherOrg": {Endpoint: []*discovery.Endpoint{{Host: "perOrgEndpoint", Port: 7050}}},
|
|
"aBadOrg": {},
|
|
}, res.Orderers)
|
|
})
|
|
|
|
t.Run("Per org endpoints without global endpoints", func(t *testing.T) {
|
|
channelConfig, err := test.MakeChannelConfig("mychannel")
|
|
require.NoError(t, err)
|
|
|
|
fakeConfigGetter := &mocks.ConfigGetter{}
|
|
cs := config.NewDiscoverySupport(fakeConfigGetter)
|
|
|
|
fakeConfigGetter.GetCurrConfigReturnsOnCall(0, channelConfig)
|
|
|
|
removeGlobalEndpoints(t, channelConfig)
|
|
injectAdditionalEndpointPair(t, channelConfig, "perOrgEndpoint:7050", "SampleOrg")
|
|
injectAdditionalEndpointPair(t, channelConfig, "endpointWithoutAPortName", "aBadOrg")
|
|
|
|
res, err := cs.Config("test")
|
|
require.NoError(t, err)
|
|
require.Equal(t, map[string]*discovery.Endpoints{
|
|
"SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "perOrgEndpoint", Port: 7050}}},
|
|
"aBadOrg": {},
|
|
}, res.Orderers)
|
|
})
|
|
}
|
|
|
|
func removeGlobalEndpoints(t *testing.T, config *common.Config) {
|
|
// Remove the orderer addresses
|
|
delete(config.ChannelGroup.Values, channelconfig.OrdererAddressesKey)
|
|
}
|
|
|
|
func injectGlobalOrdererEndpoint(t *testing.T, config *common.Config, endpoint string) {
|
|
ordererAddresses := channelconfig.OrdererAddressesValue([]string{endpoint})
|
|
// Replace the orderer addresses
|
|
config.ChannelGroup.Values[ordererAddresses.Key()] = &common.ConfigValue{
|
|
Value: protoutil.MarshalOrPanic(ordererAddresses.Value()),
|
|
ModPolicy: "/Channel/Orderer/Admins",
|
|
}
|
|
// Remove the per org addresses, if applicable
|
|
ordererGrps := config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups
|
|
for _, grp := range ordererGrps {
|
|
if grp.Values[channelconfig.EndpointsKey] == nil {
|
|
continue
|
|
}
|
|
grp.Values[channelconfig.EndpointsKey].Value = nil
|
|
}
|
|
}
|
|
|
|
func injectAdditionalEndpointPair(t *testing.T, config *common.Config, endpoint string, orgName string) {
|
|
ordererGrp := config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups
|
|
// Get the first orderer org config
|
|
var firstOrdererConfig *common.ConfigGroup
|
|
for _, grp := range ordererGrp {
|
|
firstOrdererConfig = grp
|
|
break
|
|
}
|
|
// Duplicate it.
|
|
secondOrdererConfig := proto.Clone(firstOrdererConfig).(*common.ConfigGroup)
|
|
ordererGrp[orgName] = secondOrdererConfig
|
|
// Reach the FabricMSPConfig buried in it.
|
|
mspConfig := &msp.MSPConfig{}
|
|
err := proto.Unmarshal(secondOrdererConfig.Values[channelconfig.MSPKey].Value, mspConfig)
|
|
require.NoError(t, err)
|
|
|
|
fabricConfig := &msp.FabricMSPConfig{}
|
|
err = proto.Unmarshal(mspConfig.Config, fabricConfig)
|
|
require.NoError(t, err)
|
|
|
|
// Rename it.
|
|
fabricConfig.Name = orgName
|
|
|
|
// Pack the MSP config back into the config
|
|
secondOrdererConfig.Values[channelconfig.MSPKey].Value = protoutil.MarshalOrPanic(&msp.MSPConfig{
|
|
Config: protoutil.MarshalOrPanic(fabricConfig),
|
|
Type: mspConfig.Type,
|
|
})
|
|
|
|
// Inject the endpoint
|
|
ordererOrgProtos := &common.OrdererAddresses{
|
|
Addresses: []string{endpoint},
|
|
}
|
|
secondOrdererConfig.Values[channelconfig.EndpointsKey].Value = protoutil.MarshalOrPanic(ordererOrgProtos)
|
|
}
|