/* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package common import ( "fmt" "io" "os" "path/filepath" "github.com/hyperledger/fabric/cmd/common/comm" "github.com/hyperledger/fabric/cmd/common/signer" "gopkg.in/alecthomas/kingpin.v2" ) const ( saveConfigCommand = "saveConfig" ) var ( // Function used to terminate the CLI terminate = os.Exit // Function used to redirect output to outWriter io.Writer = os.Stderr // CLI arguments mspID *string tlsCA, tlsCert, tlsKey, userKey, userCert **os.File configFile *string ) // CLICommand defines a command that is added to the CLI // via an external consumer. type CLICommand func(Config) error // CLI defines a command line interpreter type CLI struct { app *kingpin.Application dispatchers map[string]CLICommand } // NewCLI creates a new CLI with the given name and help message func NewCLI(name, help string) *CLI { return &CLI{ app: kingpin.New(name, help), dispatchers: make(map[string]CLICommand), } } // Command adds a new top-level command to the CLI func (cli *CLI) Command(name, help string, onCommand CLICommand) *kingpin.CmdClause { cmd := cli.app.Command(name, help) cli.dispatchers[name] = onCommand return cmd } // Run makes the CLI process the arguments and executes the command(s) with the flag(s) func (cli *CLI) Run(args []string) { configFile = cli.app.Flag("configFile", "Specifies the config file to load the configuration from").String() persist := cli.app.Command(saveConfigCommand, "Save the config passed by flags into the file specified by --configFile") configureFlags(cli.app) command := kingpin.MustParse(cli.app.Parse(args)) if command == persist.FullCommand() { if *configFile == "" { out("--configFile must be used to specify the configuration file") return } persistConfig(parseFlagsToConfig(), *configFile) return } var conf Config if *configFile == "" { conf = parseFlagsToConfig() } else { conf = loadConfig(*configFile) } f, exists := cli.dispatchers[command] if !exists { out("Unknown command:", command) terminate(1) return } err := f(conf) if err != nil { out(err) terminate(1) return } } func configureFlags(persistCommand *kingpin.Application) { // TLS flags tlsCA = persistCommand.Flag("peerTLSCA", "Sets the TLS CA certificate file path that verifies the TLS peer's certificate").File() tlsCert = persistCommand.Flag("tlsCert", "(Optional) Sets the client TLS certificate file path that is used when the peer enforces client authentication").File() tlsKey = persistCommand.Flag("tlsKey", "(Optional) Sets the client TLS key file path that is used when the peer enforces client authentication").File() // Enrollment flags userKey = persistCommand.Flag("userKey", "Sets the user's key file path that is used to sign messages sent to the peer").File() userCert = persistCommand.Flag("userCert", "Sets the user's certificate file path that is used to authenticate the messages sent to the peer").File() mspID = persistCommand.Flag("MSP", "Sets the MSP ID of the user, which represents the CA(s) that issued its user certificate").String() } func persistConfig(conf Config, file string) { if err := conf.ToFile(file); err != nil { out("Failed persisting configuration:", err) terminate(1) } } func loadConfig(file string) Config { conf, err := ConfigFromFile(file) if err != nil { out("Failed loading config", err) terminate(1) return Config{} } return conf } func parseFlagsToConfig() Config { conf := Config{ SignerConfig: signer.Config{ MSPID: *mspID, IdentityPath: evaluateFileFlag(userCert), KeyPath: evaluateFileFlag(userKey), }, TLSConfig: comm.Config{ KeyPath: evaluateFileFlag(tlsKey), CertPath: evaluateFileFlag(tlsCert), PeerCACertPath: evaluateFileFlag(tlsCA), }, } return conf } func evaluateFileFlag(f **os.File) string { if f == nil { return "" } if *f == nil { return "" } path, err := filepath.Abs((*f).Name()) if err != nil { out("Failed listing", (*f).Name(), ":", err) terminate(1) } return path } func out(a ...interface{}) { fmt.Fprintln(outWriter, a...) }