diff --git a/common/tools/configtxgen/encoder/encoder.go b/common/tools/configtxgen/encoder/encoder.go index 9e4b37fd694..f301ea27632 100644 --- a/common/tools/configtxgen/encoder/encoder.go +++ b/common/tools/configtxgen/encoder/encoder.go @@ -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() diff --git a/common/tools/configtxgen/localconfig/config.go b/common/tools/configtxgen/localconfig/config.go index eaef9be82f2..df81bceadf4 100644 --- a/common/tools/configtxgen/localconfig/config.go +++ b/common/tools/configtxgen/localconfig/config.go @@ -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() @@ -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) @@ -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 } diff --git a/common/tools/configtxgen/main.go b/common/tools/configtxgen/main.go index d02def891fb..0da6b251e1f 100644 --- a/common/tools/configtxgen/main.go +++ b/common/tools/configtxgen/main.go @@ -25,6 +25,7 @@ import ( "github.com/hyperledger/fabric/protos/utils" logging "github.com/op/go-logging" + "github.com/pkg/errors" ) var exitCode = 0 @@ -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") @@ -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") @@ -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) } } @@ -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() { diff --git a/common/tools/configtxgen/main_test.go b/common/tools/configtxgen/main_test.go index 34dd4a097af..527a482875c 100644 --- a/common/tools/configtxgen/main_test.go +++ b/common/tools/configtxgen/main_test.go @@ -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()) +}