Skip to content

Commit

Permalink
[FAB-6103] Add printOrg to configtxgen
Browse files Browse the repository at this point in the history
One of the most common questions when attempting to add an organization
to a channel is how to get the JSON definition for that organization.
The only real way to do this at present is to generate a genesis block,
decode the block, then go look for the org definition.

This CR adds a '-printOrg' flag to configtxgen which will print the
organization's definition as JSON to make this process easier.

Change-Id: Id12098f36feae8ca4751b82afaf2914166077e50
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Oct 13, 2017
1 parent 2802430 commit 6abde4b
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 34 deletions.
2 changes: 1 addition & 1 deletion common/tools/configtxgen/encoder/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func NewOrdererGroup(conf *genesisconfig.Orderer) (*cb.ConfigGroup, error) {
func NewOrdererOrgGroup(conf *genesisconfig.Organization) (*cb.ConfigGroup, error) {
mspConfig, err := msp.GetVerifyingMspConfig(conf.MSPDir, conf.ID)
if err != nil {
return nil, errors.Wrapf(err, "1 - Error loading MSP configuration for org %s: %s", conf.Name)
return nil, errors.Wrapf(err, "1 - Error loading MSP configuration for org: %s", conf.Name)
}

ordererOrgGroup := cb.NewConfigGroup()
Expand Down
101 changes: 73 additions & 28 deletions common/tools/configtxgen/localconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,40 @@ var genesisDefaults = TopLevel{
},
}

// LoadTopLevel simply loads the configtx.yaml file into the structs above
// and completes their initialization. Note, for environment overrides to work properly
// within a profile, Load(profile string) should be called when attempting to work within
// a particular profile.
func LoadTopLevel() *TopLevel {
config := viper.New()
cf.InitViper(config, configName)

// For environment variables
config.SetEnvPrefix(Prefix)
config.AutomaticEnv()

replacer := strings.NewReplacer(".", "_")
config.SetEnvKeyReplacer(replacer)

err := config.ReadInConfig()
if err != nil {
logger.Panic("Error reading configuration: ", err)
}
logger.Debugf("Using config file: %s", config.ConfigFileUsed())

var uconf TopLevel
err = viperutil.EnhancedExactUnmarshal(config, &uconf)
if err != nil {
logger.Panic("Error unmarshaling config into struct: ", err)
}

(&uconf).completeInitialization(filepath.Dir(config.ConfigFileUsed()))

logger.Infof("Loaded configuration: %s", config.ConfigFileUsed())

return &uconf
}

// Load returns the orderer/application config combination that corresponds to a given profile.
func Load(profile string) *Profile {
config := viper.New()
Expand All @@ -176,6 +210,7 @@ func Load(profile string) *Profile {
// For environment variables
config.SetEnvPrefix(Prefix)
config.AutomaticEnv()

// This replacer allows substitution within the particular profile without having to fully qualify the name
replacer := strings.NewReplacer(strings.ToUpper(fmt.Sprintf("profiles.%s.", profile)), "", ".", "_")
config.SetEnvKeyReplacer(replacer)
Expand Down Expand Up @@ -204,64 +239,74 @@ func Load(profile string) *Profile {
return result
}

func (t *TopLevel) completeInitialization(configDir string) {
for _, org := range t.Organizations {
org.completeInitialization(configDir)
}

if t.Orderer != nil {
t.Orderer.completeInitialization()
}
}

func (p *Profile) completeInitialization(configDir string) {
if p.Orderer != nil {
for _, org := range p.Orderer.Organizations {
if org.AdminPrincipal == "" {
org.AdminPrincipal = AdminRoleAdminPrincipal
}
translatePaths(configDir, org)
org.completeInitialization(configDir)
}
}

if p.Application != nil {
for _, org := range p.Application.Organizations {
if org.AdminPrincipal == "" {
org.AdminPrincipal = AdminRoleAdminPrincipal
}
translatePaths(configDir, org)
org.completeInitialization(configDir)
}
}

if p.Consortiums != nil {
for _, consortium := range p.Consortiums {
for _, org := range consortium.Organizations {
if org.AdminPrincipal == "" {
org.AdminPrincipal = AdminRoleAdminPrincipal
}
translatePaths(configDir, org)
org.completeInitialization(configDir)
}
}
}

// Some profiles will not define orderer parameters
if p.Orderer == nil {
return
if p.Orderer != nil {
p.Orderer.completeInitialization()
}
}

func (org *Organization) completeInitialization(configDir string) {
if org.AdminPrincipal == "" {
org.AdminPrincipal = AdminRoleAdminPrincipal
}
translatePaths(configDir, org)
}

func (oc *Orderer) completeInitialization() {
for {
switch {
case p.Orderer.OrdererType == "":
case oc.OrdererType == "":
logger.Infof("Orderer.OrdererType unset, setting to %s", genesisDefaults.Orderer.OrdererType)
p.Orderer.OrdererType = genesisDefaults.Orderer.OrdererType
case p.Orderer.Addresses == nil:
oc.OrdererType = genesisDefaults.Orderer.OrdererType
case oc.Addresses == nil:
logger.Infof("Orderer.Addresses unset, setting to %s", genesisDefaults.Orderer.Addresses)
p.Orderer.Addresses = genesisDefaults.Orderer.Addresses
case p.Orderer.BatchTimeout == 0:
oc.Addresses = genesisDefaults.Orderer.Addresses
case oc.BatchTimeout == 0:
logger.Infof("Orderer.BatchTimeout unset, setting to %s", genesisDefaults.Orderer.BatchTimeout)
p.Orderer.BatchTimeout = genesisDefaults.Orderer.BatchTimeout
case p.Orderer.BatchSize.MaxMessageCount == 0:
oc.BatchTimeout = genesisDefaults.Orderer.BatchTimeout
case oc.BatchSize.MaxMessageCount == 0:
logger.Infof("Orderer.BatchSize.MaxMessageCount unset, setting to %s", genesisDefaults.Orderer.BatchSize.MaxMessageCount)
p.Orderer.BatchSize.MaxMessageCount = genesisDefaults.Orderer.BatchSize.MaxMessageCount
case p.Orderer.BatchSize.AbsoluteMaxBytes == 0:
oc.BatchSize.MaxMessageCount = genesisDefaults.Orderer.BatchSize.MaxMessageCount
case oc.BatchSize.AbsoluteMaxBytes == 0:
logger.Infof("Orderer.BatchSize.AbsoluteMaxBytes unset, setting to %s", genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes)
p.Orderer.BatchSize.AbsoluteMaxBytes = genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes
case p.Orderer.BatchSize.PreferredMaxBytes == 0:
oc.BatchSize.AbsoluteMaxBytes = genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes
case oc.BatchSize.PreferredMaxBytes == 0:
logger.Infof("Orderer.BatchSize.PreferredMaxBytes unset, setting to %s", genesisDefaults.Orderer.BatchSize.PreferredMaxBytes)
p.Orderer.BatchSize.PreferredMaxBytes = genesisDefaults.Orderer.BatchSize.PreferredMaxBytes
case p.Orderer.Kafka.Brokers == nil:
oc.BatchSize.PreferredMaxBytes = genesisDefaults.Orderer.BatchSize.PreferredMaxBytes
case oc.Kafka.Brokers == nil:
logger.Infof("Orderer.Kafka.Brokers unset, setting to %v", genesisDefaults.Orderer.Kafka.Brokers)
p.Orderer.Kafka.Brokers = genesisDefaults.Orderer.Kafka.Brokers
oc.Kafka.Brokers = genesisDefaults.Orderer.Kafka.Brokers
default:
return
}
Expand Down
40 changes: 35 additions & 5 deletions common/tools/configtxgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/hyperledger/fabric/protos/utils"

logging "github.com/op/go-logging"
"github.com/pkg/errors"
)

var exitCode = 0
Expand Down Expand Up @@ -204,8 +205,25 @@ func doInspectChannelCreateTx(inspectChannelCreateTx string) error {
return nil
}

func doPrintOrg(t *genesisconfig.TopLevel, printOrg string) error {
for _, org := range t.Organizations {
if org.Name == printOrg {
og, err := encoder.NewOrdererOrgGroup(org)
if err != nil {
return errors.Wrapf(err, "bad org definition for org %s", org.Name)
}

if err := protolator.DeepMarshalJSON(os.Stdout, og); err != nil {
return errors.Wrapf(err, "malformed org definition for org: %s", org.Name)
}
return nil
}
}
return errors.Errorf("organization %s not found", printOrg)
}

func main() {
var outputBlock, outputChannelCreateTx, profile, channelID, inspectBlock, inspectChannelCreateTx, outputAnchorPeersUpdate, asOrg string
var outputBlock, outputChannelCreateTx, profile, channelID, inspectBlock, inspectChannelCreateTx, outputAnchorPeersUpdate, asOrg, printOrg string

flag.StringVar(&outputBlock, "outputBlock", "", "The path to write the genesis block to (if set)")
flag.StringVar(&channelID, "channelID", genesisconfig.TestChainID, "The channel ID to use in the configtx")
Expand All @@ -215,6 +233,7 @@ func main() {
flag.StringVar(&inspectChannelCreateTx, "inspectChannelCreateTx", "", "Prints the configuration contained in the transaction at the specified path")
flag.StringVar(&outputAnchorPeersUpdate, "outputAnchorPeersUpdate", "", "Creates an config update to update an anchor peer (works only with the default channel creation, and only for the first update)")
flag.StringVar(&asOrg, "asOrg", "", "Performs the config generation as a particular organization (by name), only including values in the write set that org (likely) has privilege to set")
flag.StringVar(&printOrg, "printOrg", "", "Prints the definition of an organization as JSON. (useful for adding an org to a channel manually)")

version := flag.Bool("version", false, "Show version information")

Expand Down Expand Up @@ -242,16 +261,21 @@ func main() {

logger.Info("Loading configuration")
factory.InitFactories(nil)
config := genesisconfig.Load(profile)
var profileConfig *genesisconfig.Profile
if outputBlock != "" || outputChannelCreateTx != "" || outputAnchorPeersUpdate != "" {
profileConfig = genesisconfig.Load(profile)
}

topLevelConfig := genesisconfig.LoadTopLevel()

if outputBlock != "" {
if err := doOutputBlock(config, channelID, outputBlock); err != nil {
if err := doOutputBlock(profileConfig, channelID, outputBlock); err != nil {
logger.Fatalf("Error on outputBlock: %s", err)
}
}

if outputChannelCreateTx != "" {
if err := doOutputChannelCreateTx(config, channelID, outputChannelCreateTx); err != nil {
if err := doOutputChannelCreateTx(profileConfig, channelID, outputChannelCreateTx); err != nil {
logger.Fatalf("Error on outputChannelCreateTx: %s", err)
}
}
Expand All @@ -269,10 +293,16 @@ func main() {
}

if outputAnchorPeersUpdate != "" {
if err := doOutputAnchorPeersUpdate(config, channelID, outputAnchorPeersUpdate, asOrg); err != nil {
if err := doOutputAnchorPeersUpdate(profileConfig, channelID, outputAnchorPeersUpdate, asOrg); err != nil {
logger.Fatalf("Error on inspectChannelCreateTx: %s", err)
}
}

if printOrg != "" {
if err := doPrintOrg(topLevelConfig, printOrg); err != nil {
logger.Fatalf("Error on printOrg: %s", err)
}
}
}

func printVersion() {
Expand Down
11 changes: 11 additions & 0 deletions common/tools/configtxgen/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,14 @@ func TestBlockFlags(t *testing.T) {
_, err := os.Stat(blockDest)
assert.NoError(t, err, "Block file is written successfully")
}

func TestPrintOrg(t *testing.T) {
factory.InitFactories(nil)
config := genesisconfig.LoadTopLevel()

assert.NoError(t, doPrintOrg(config, genesisconfig.SampleOrgName), "Good org to print")

err := doPrintOrg(config, genesisconfig.SampleOrgName+".wrong")
assert.Error(t, err, "Bad org name")
assert.Regexp(t, "organization [^ ]* not found", err.Error())
}

0 comments on commit 6abde4b

Please sign in to comment.