Skip to content

Commit

Permalink
[FAB-3160] Provide config-relative path feature
Browse files Browse the repository at this point in the history
Introduction
======================
The primary goal of this patch is to create the notion of a
"config-relative" path reference.  For example, a configuration file
"/etc/foo/bar.yaml" that contains a key "bat" with a value
"baz/blaz/blamo" can be used to specify that "baz/blaz/blamo" should
be considered relative to the configuration file itself.  In this case,
it would be expected to be found at /etc/foo/baz/blaz/blamo.  FAB-2037
does a much more thorough job of explaining the rationale on why
config-relative is considered important/good-form.

This is in stark contrast to what we have today, which is a jumbled
mess of assumed GOPATH relative, CWD relative, ENVVAR absolute and
sometimes even ENVVAR relative.  Therefore, an additional positive
side-effect of this endeavor is that this patch also substantially
cleans up some technical debt that had been accumulating in the tree
for some time related to ad-hoc pathing, DRY violations, and just
general inconsistencies in how configuration files were managed.

Design Details
==========================
This patch refactors the basic configuration system into the notion of
a tree rooted at a configuration-path.  By default, this path is
$GOROOT/..../fabric/sampleconfig during dev/test and
/etc/hyperledger/fabric during runtime.  The root may be overridden
at any time by specifying the environment variable FABRIC_CFG_PATH.
(Note that this variable unifies and replaces the former PEER_CFG_PATH
and ORDERER_CFG_PATH).

The dev/test environment will operate out of the ./fabric/sampleconfig
configuration root.  The build-system will package that root into
/etc/hyperledger/fabric in the runtime context with the intention of
the end-user/admin/deployer replacing parts or all of the sampleconfig
to suit their application.

Since configuration-relative paths are now possible, the configuration
files may reference other relative files and they will behave
appropriately regardless of the context in which they are executed.
For example, consider the files ./sampleconfig/tls/server.[crt|key].
A configuration file may contain a key "tls/server.key" and the system
will properly resolve this relative file even at runtime. This is (IMO)
far more natural than assuming a path is relative to the CWD of where
the command is executed, which is how most of the system behaves today
(or requires awkward and very specific ENVVAR overrides).

This will be conducive to something like a package-installer
(e.g. RPM/DEB) or a docker environment to augment/replace elements
of the configuration root and to freely move the configuration around
as the package/deployer sees fit.

As an example, a deployment on Kubernetes might opt to volume mount
/etc/hyperledger/fabric to replace the entire config, or it might just use
a secrets mount on /etc/hyperledger/fabric/peer/tls.  An RPM packager
might opt to install the configuration files in the default
/etc/hyperledger/fabric, whereas an unprivledged user might install them
in ~/hyperledger.  The point is, it shouldn't matter where they are and the
user shouldn't need a PhD in CORE_* variables to get it to work.

This is part of an overall effort to improve the user-experience as we
march towards a v1.0 release.

Fixes FAB-3169 as part of FAB-2037

Change-Id: I5f47f554c2f956ec2e1afebd9bd82b0bbb62892a
Signed-off-by: Greg Haskins <gregory.haskins@gmail.com>
  • Loading branch information
ghaskins committed Apr 24, 2017
1 parent 6ab1799 commit 8ce1073
Show file tree
Hide file tree
Showing 98 changed files with 542 additions and 510 deletions.
18 changes: 6 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ K := $(foreach exec,$(EXECUTABLES),\
GOSHIM_DEPS = $(shell ./scripts/goListFiles.sh $(PKGNAME)/core/chaincode/shim)
JAVASHIM_DEPS = $(shell git ls-files core/chaincode/shim/java)
PROTOS = $(shell git ls-files *.proto | grep -v vendor)
MSP_SAMPLECONFIG = $(shell git ls-files msp/sampleconfig/*)
PROJECT_FILES = $(shell git ls-files)
IMAGES = peer orderer ccenv javaenv buildenv testenv zookeeper kafka couchdb
RELEASE_PLATFORMS = windows-amd64 darwin-amd64 linux-amd64 linux-ppc64le linux-s390x
Expand Down Expand Up @@ -208,21 +207,14 @@ build/image/javaenv/payload: build/javashim.tar.bz2 \
build/protos.tar.bz2 \
settings.gradle
build/image/peer/payload: build/docker/bin/peer \
peer/core.yaml \
build/msp-sampleconfig.tar.bz2 \
common/configtx/tool/configtx.yaml
build/sampleconfig.tar.bz2
build/image/orderer/payload: build/docker/bin/orderer \
build/msp-sampleconfig.tar.bz2 \
orderer/orderer.yaml \
common/configtx/tool/configtx.yaml
build/sampleconfig.tar.bz2
build/image/buildenv/payload: build/gotools.tar.bz2 \
build/docker/gotools/bin/protoc-gen-go
build/image/testenv/payload: build/docker/bin/orderer \
orderer/orderer.yaml \
common/configtx/tool/configtx.yaml \
build/docker/bin/peer \
peer/core.yaml \
build/msp-sampleconfig.tar.bz2 \
build/sampleconfig.tar.bz2 \
images/testenv/install-softhsm2.sh
build/image/zookeeper/payload: images/zookeeper/docker-entrypoint.sh
build/image/kafka/payload: images/kafka/docker-entrypoint.sh \
Expand Down Expand Up @@ -261,9 +253,11 @@ build/goshim.tar.bz2: $(GOSHIM_DEPS)
@echo "Creating $@"
@tar -jhc -C $(GOPATH)/src $(patsubst $(GOPATH)/src/%,%,$(GOSHIM_DEPS)) > $@

build/sampleconfig.tar.bz2:
(cd sampleconfig && tar -jc *) > $@

build/javashim.tar.bz2: $(JAVASHIM_DEPS)
build/protos.tar.bz2: $(PROTOS)
build/msp-sampleconfig.tar.bz2: $(MSP_SAMPLECONFIG)

build/%.tar.bz2:
@echo "Creating $@"
Expand Down
5 changes: 0 additions & 5 deletions bddtests/docker-compose-next-4.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ services:
file: docker-compose-orderer-solo.yml
service: orderer0
environment:
- ORDERER_GENERAL_LOCALMSPDIR=${ORDERER0_ORDERER_GENERAL_LOCALMSPDIR}
- ORDERER_GENERAL_LOCALMSPID=${ORDERER0_ORDERER_GENERAL_LOCALMSPID}
- ORDERER_GENERAL_TLS_ENABLED=true
- ORDERER_GENERAL_TLS_PRIVATEKEY=${ORDERER0_ORDERER_GENERAL_TLS_PRIVATEKEY}
Expand All @@ -24,7 +23,6 @@ services:
- CORE_PEER_GOSSIP_BOOTSTRAP=peer1:7051
- CORE_PEER_PROFILE_ENABLED=true
- CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050
- CORE_PEER_MSPCONFIGPATH=${PEER0_CORE_PEER_MSPCFGPATH}
- CORE_PEER_LOCALMSPID=${PEER0_CORE_PEER_LOCALMSPID}
- CORE_PEER_TLS_CERT_FILE=${PEER0_CORE_PEER_TLS_CERT_FILE}
- CORE_PEER_TLS_KEY_FILE=${PEER0_CORE_PEER_TLS_KEY_FILE}
Expand All @@ -44,7 +42,6 @@ services:
- CORE_PEER_ID=vp1
- CORE_PEER_GOSSIP_BOOTSTRAP=peer0:7051
- CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050
- CORE_PEER_MSPCONFIGPATH=${PEER1_CORE_PEER_MSPCFGPATH}
- CORE_PEER_LOCALMSPID=${PEER1_CORE_PEER_LOCALMSPID}
- CORE_PEER_TLS_CERT_FILE=${PEER1_CORE_PEER_TLS_CERT_FILE}
- CORE_PEER_TLS_KEY_FILE=${PEER1_CORE_PEER_TLS_KEY_FILE}
Expand All @@ -62,7 +59,6 @@ services:
- CORE_PEER_ID=vp2
- CORE_PEER_GOSSIP_BOOTSTRAP=peer3:7051
- CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050
- CORE_PEER_MSPCONFIGPATH=${PEER2_CORE_PEER_MSPCFGPATH}
- CORE_PEER_LOCALMSPID=${PEER2_CORE_PEER_LOCALMSPID}
- CORE_PEER_TLS_CERT_FILE=${PEER2_CORE_PEER_TLS_CERT_FILE}
- CORE_PEER_TLS_KEY_FILE=${PEER2_CORE_PEER_TLS_KEY_FILE}
Expand All @@ -80,7 +76,6 @@ services:
- CORE_PEER_ID=vp3
- CORE_PEER_GOSSIP_BOOTSTRAP=peer2:7051
- CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050
- CORE_PEER_MSPCONFIGPATH=${PEER3_CORE_PEER_MSPCFGPATH}
- CORE_PEER_LOCALMSPID=${PEER3_CORE_PEER_LOCALMSPID}
- CORE_PEER_TLS_CERT_FILE=${PEER3_CORE_PEER_TLS_CERT_FILE}
- CORE_PEER_TLS_KEY_FILE=${PEER3_CORE_PEER_TLS_KEY_FILE}
Expand Down
2 changes: 0 additions & 2 deletions bddtests/orderer-3-kafka-1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ services:
service: orderer0
image: hyperledger/fabric-testenv-orderer
environment:
- ORDERER_GENERAL_LOCALMSPDIR=${ORDERER1_ORDERER_GENERAL_LOCALMSPDIR}
- ORDERER_GENERAL_LOCALMSPID=${ORDERER1_ORDERER_GENERAL_LOCALMSPID}
- ORDERER_GENERAL_TLS_PRIVATEKEY=${ORDERER1_ORDERER_GENERAL_TLS_PRIVATEKEY}
- ORDERER_GENERAL_TLS_CERTIFICATE=${ORDERER1_ORDERER_GENERAL_TLS_CERTIFICATE}
Expand All @@ -19,7 +18,6 @@ services:
service: orderer0
image: hyperledger/fabric-testenv-orderer
environment:
- ORDERER_GENERAL_LOCALMSPDIR=${ORDERER2_ORDERER_GENERAL_LOCALMSPDIR}
- ORDERER_GENERAL_LOCALMSPID=${ORDERER2_ORDERER_GENERAL_LOCALMSPID}
- ORDERER_GENERAL_TLS_PRIVATEKEY=${ORDERER2_ORDERER_GENERAL_TLS_PRIVATEKEY}
- ORDERER_GENERAL_TLS_CERTIFICATE=${ORDERER2_ORDERER_GENERAL_TLS_CERTIFICATE}
Expand Down
4 changes: 2 additions & 2 deletions bddtests/regression/go/ote/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ Check your Docker and Docker-Compose versions with the following commands:
```

### Environment Variables for configuration
Find default values of all variables in hyperledger/fabric/orderer/orderer.yaml
and hyperledger/fabric/peer/core.yaml.
Find default values of all variables in hyperledger/fabric/sampleconfig/orderer.yaml
and hyperledger/fabric/sampleconfig/core.yaml.
```
CONFIGTX_ORDERER_ORDERERTYPE solo
CONFIGTX_ORDERER_BATCHSIZE_MAXMESSAGECOUNT 10
Expand Down
4 changes: 0 additions & 4 deletions bddtests/regression/go/ote/network.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,6 @@
"zookeeper",
"kafka0"
],
"volumes": [
"/opt/gopath/src/github.com/hyperledger/fabric/msp/sampleconfig:/etc/hyperledger/msp/sampleconfig"
],
"container_name": "orderer0",
"networks": [
"bridge"
Expand Down Expand Up @@ -110,7 +107,6 @@
},
"working_dir": "/opt/gopath/src/github.com/hyperledger/fabric/peer",
"volumes": [
"/opt/gopath/src/github.com/hyperledger/fabric/msp/sampleconfig:/etc/hyperledger/msp/sampleconfig",
"/var/run:/host/var/run/"
],
"command": "peer node start",
Expand Down
2 changes: 1 addition & 1 deletion bddtests/regression/go/ote/ote.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ package main
// ======================
//
// This file ote.go contains main(), for executing from command line
// using environment variables to override those in orderer/orderer.yaml
// using environment variables to override those in sampleconfig/orderer.yaml
// or to set OTE test configuration parameters.
//
// Function ote() is called by main after reading environment variables,
Expand Down
2 changes: 1 addition & 1 deletion bddtests/steps/endorser_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def getExample02ChaincodeSpec():
def _createDeploymentSpecAsFile(ccSpec, outputPath):
'''peer chaincode package -n myCC -c '{"Args":["init","a","100","b","200"]}' -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 --logging-level=DEBUG test.file'''
myEnv = os.environ.copy()
myEnv['CORE_PEER_MSPCONFIGPATH'] = "./../msp/sampleconfig"
myEnv['CORE_PEER_MSPCONFIGPATH'] = "./../sampleconfig/msp"
nameArgs = ["-n", ccSpec.chaincode_id.name]
ctorArgs = ["-c", json.dumps({'Args' : [item for item in ccSpec.input.args]})]
pathArgs = ["-p", ccSpec.chaincode_id.path]
Expand Down
5 changes: 4 additions & 1 deletion common/config/msp/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ package msp
import (
"testing"

"github.com/hyperledger/fabric/core/config"
"github.com/hyperledger/fabric/msp"
mspprotos "github.com/hyperledger/fabric/protos/msp"
"github.com/stretchr/testify/assert"
)

func TestMSPConfigManager(t *testing.T) {
conf, err := msp.GetLocalMspConfig("../../../msp/sampleconfig/", nil, "DEFAULT")
mspDir, err := config.GetDevMspDir()
assert.NoError(t, err)
conf, err := msp.GetLocalMspConfig(mspDir, nil, "DEFAULT")
assert.NoError(t, err)

// test success:
Expand Down
32 changes: 5 additions & 27 deletions common/configtx/test/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,14 @@ limitations under the License.
package test

import (
"os"
"path/filepath"

"github.com/hyperledger/fabric/common/config"
configtxmsp "github.com/hyperledger/fabric/common/config/msp"
"github.com/hyperledger/fabric/common/configtx"
genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig"
"github.com/hyperledger/fabric/common/configtx/tool/provisional"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/genesis"
cf "github.com/hyperledger/fabric/core/config"
"github.com/hyperledger/fabric/msp"
cb "github.com/hyperledger/fabric/protos/common"
mspproto "github.com/hyperledger/fabric/protos/msp"
Expand All @@ -39,33 +37,13 @@ const (
AcceptAllPolicyKey = "AcceptAllPolicy"
)

func dirExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}

func getConfigDir() string {
mspSampleConfig := "/msp/sampleconfig"
peerPath := filepath.Join(os.Getenv("PEER_CFG_PATH"), mspSampleConfig)
ordererPath := filepath.Join(os.Getenv("ORDERER_CFG_PATH"), mspSampleConfig)
switch {
case dirExists(peerPath):
return peerPath
case dirExists(ordererPath):
return ordererPath
}

gopath := os.Getenv("GOPATH")
for _, p := range filepath.SplitList(gopath) {
samplePath := filepath.Join(p, "src/github.com/hyperledger/fabric", mspSampleConfig)
if !dirExists(samplePath) {
continue
}
return samplePath
mspDir, err := cf.GetDevMspDir()
if err != nil {
logger.Panicf("Could not find genesis.yaml, try setting GOPATH correctly")
}

logger.Panicf("Could not find genesis.yaml, try setting PEER_CFG_PATH, ORDERER_CFG_PATH, or GOPATH correctly")
return ""
return mspDir
}

// MakeGenesisBlock creates a genesis block using the test templates for the given chainID
Expand Down
10 changes: 8 additions & 2 deletions common/configtx/test/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"path/filepath"
"testing"

"github.com/hyperledger/fabric/core/config"
"github.com/hyperledger/fabric/msp"
logging "github.com/op/go-logging"
)
Expand All @@ -43,10 +44,15 @@ func TestMakeGenesisBlock(t *testing.T) {

func TestMakeGenesisBlockFromMSPs(t *testing.T) {

mspDir, err := config.GetDevMspDir()
if err != nil {
t.Fatalf("Error getting DevMspDir: %s", err)
}

ordererOrgID := "TestOrdererOrg"
appOrgID := "TestAppOrg"
appMSPConf, err := msp.GetLocalMspConfig("msp/sampleconfig", nil, appOrgID)
ordererMSPConf, err := msp.GetLocalMspConfig("msp/sampleconfig", nil, ordererOrgID)
appMSPConf, err := msp.GetLocalMspConfig(mspDir, nil, appOrgID)
ordererMSPConf, err := msp.GetLocalMspConfig(mspDir, nil, ordererOrgID)
if err != nil {
t.Fatalf("Error making genesis block from MSPs: %s", err)
}
Expand Down
71 changes: 23 additions & 48 deletions common/configtx/tool/localconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ package localconfig

import (
"fmt"
"os"
"path/filepath"

"strings"
"time"

Expand All @@ -28,7 +27,10 @@ import (

"github.com/spf13/viper"

"path/filepath"

bccsp "github.com/hyperledger/fabric/bccsp/factory"
cf "github.com/hyperledger/fabric/core/config"
)

var logger = flogging.MustGetLogger("configtx/tool/localconfig")
Expand All @@ -43,16 +45,6 @@ const (
Prefix string = "CONFIGTX"
)

var (
configName string
configFileName string
)

func init() {
configName = strings.ToLower(Prefix)
configFileName = configName + ".yaml"
}

// TopLevel consists of the structs used by the configtxgen tool.
type TopLevel struct {
Profiles map[string]*Profile `yaml:"Profiles"`
Expand Down Expand Up @@ -137,7 +129,7 @@ var genesisDefaults = TopLevel{
},
}

func (p *Profile) completeInitialization() {
func (p *Profile) initDefaults() {
for {
switch {
case p.Orderer.OrdererType == "":
Expand Down Expand Up @@ -167,46 +159,29 @@ func (p *Profile) completeInitialization() {
}
}

// Load returns the orderer/application config combination that corresponds to a given profile.
func Load(profile string) *Profile {
config := viper.New()

config.SetConfigName(configName)
var cfgPath string
func translatePaths(configDir string, org *Organization) {
cf.TranslatePathInPlace(configDir, &org.MSPDir)
cf.TranslatePathInPlace(configDir, &org.BCCSP.SwOpts.FileKeystore.KeyStorePath)
}

// Candidate paths to look for the config file in, based on GOPATH
searchPath := []string{
os.Getenv("ORDERER_CFG_PATH"),
os.Getenv("PEER_CFG_PATH"),
}
func (p *Profile) completeInitialization(configDir string) {
p.initDefaults()

for _, p := range filepath.SplitList(os.Getenv("GOPATH")) {
searchPath = append(searchPath, filepath.Join(p, "src/github.com/hyperledger/fabric/common/configtx/tool/"))
// Fix up any relative paths
for _, org := range p.Application.Organizations {
translatePaths(configDir, org)
}

for _, path := range searchPath {
if len(path) == 0 {
// No point printing a "checking for" message below for an empty path
continue
}
logger.Infof("Looking for %s in: %s", configFileName, path)
if _, err := os.Stat(filepath.Join(path, configFileName)); err != nil {
// The YAML file does not exist in this component of the path
continue
}
cfgPath = path
logger.Infof("Found %s there", configFileName)
break
for _, org := range p.Orderer.Organizations {
translatePaths(configDir, org)
}
}

if cfgPath == "" {
logger.Fatalf("Could not find %s in paths of %s."+
"Try setting ORDERER_CFG_PATH, PEER_CFG_PATH, or GOPATH correctly.",
configFileName, searchPath)
}
// Load returns the orderer/application config combination that corresponds to a given profile.
func Load(profile string) *Profile {
config := viper.New()

// Path to look for the config file in
config.AddConfigPath(cfgPath)
cf.InitViper(config, "configtx")

// For environment variables
config.SetEnvPrefix(Prefix)
Expand All @@ -217,7 +192,7 @@ func Load(profile string) *Profile {

err := config.ReadInConfig()
if err != nil {
logger.Panicf("Error reading configuration from %s in %s: %s", configFileName, cfgPath, err)
logger.Panicf("Error reading configuration: %s", err)
}

var uconf TopLevel
Expand All @@ -231,7 +206,7 @@ func Load(profile string) *Profile {
logger.Panicf("Could not find profile %s", profile)
}

result.completeInitialization()
result.completeInitialization(filepath.Dir(config.ConfigFileUsed()))

return result
}
Loading

0 comments on commit 8ce1073

Please sign in to comment.