Fabric中Cobra的使用简析

fabric版本:fabric-1.1.0
主要想记录一下分析过程,最后能理解类似如下命令是怎么回事:

peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f  $rootDir/networks/single-dev-env/config/channel-artifacts/channel.tx

直接拿上面的命令分析,首先在fabric/peer/目录下的main.go文件中可以看到:

var mainCmd = &cobra.Command{
    Use: "peer",
    PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
        // check for --logging-level pflag first, which should override all other
        // log settings. if --logging-level is not set, use CORE_LOGGING_LEVEL
        // (environment variable takes priority; otherwise, the value set in
        // core.yaml)
        var loggingSpec string
        if viper.GetString("logging_level") != "" {
            loggingSpec = viper.GetString("logging_level")
        } else {
            loggingSpec = viper.GetString("logging.level")
        }
        flogging.InitFromSpec(loggingSpec)

        return nil
    },
    Run: func(cmd *cobra.Command, args []string) {
        if versionFlag {
            fmt.Print(version.GetInfo())
        } else {
            cmd.HelpFunc()(cmd, args)
        }
    },
}

1、这里定义了一个mainCmd命令,并对Use、PersistentPreRunE、Run字段进行了赋值。
2、命令执行时会去执行Run字段定义的回调函数,此外Cobra还提供了四个函数:PersistentPreRun、PreRun、PostRun、PersistentPostRun,可以在执行这个回调函数Run之前和之后执行。它们的执行顺序依次是:PersistentPreRun、PreRun、Run、PostRun、PersistentPostRun。而且对于PersistentPreRun和PersistentPostRun,子命令是继承的,即子命令如果没有自定义自己的PersistentPreRun和PersistentPostRun,那它就会执行父命令的这两个函数。
3、这里PersistentPreRunE先于Run执行,作为根命令,只完成PersistentPreRunE指定的检查、初始化日志系统并缓存配置的功能,和Run指定的版本打印、命令帮助功能。

继续main.go往下看,在main()中可以看到:

    mainCmd.AddCommand(version.Cmd())
    mainCmd.AddCommand(node.Cmd())
    mainCmd.AddCommand(chaincode.Cmd(nil))
    mainCmd.AddCommand(clilogging.Cmd(nil))
    mainCmd.AddCommand(channel.Cmd(nil))

主命令是peer,看到AddCommand()里的参数便大概可以理解peer version、peer node、peer chaincode等命令了。随便进入一个子命令对应的目录,比如fabric/peer/channel,在其下的channel.go中:

func Cmd(cf *ChannelCmdFactory) *cobra.Command {
    AddFlags(channelCmd)

    channelCmd.AddCommand(createCmd(cf))
    channelCmd.AddCommand(fetchCmd(cf))
    channelCmd.AddCommand(joinCmd(cf))
    channelCmd.AddCommand(listCmd(cf))
    channelCmd.AddCommand(updateCmd(cf))
    channelCmd.AddCommand(signconfigtxCmd(cf))
    channelCmd.AddCommand(getinfoCmd(cf))

    return channelCmd
}

又看到了AddCommand(),是不是又可以继续去找它的子命令了呀?先别急,上面还有一行AddFlags(channelCmd),打开:

// AddFlags adds flags for create and join
func AddFlags(cmd *cobra.Command) {
    common.AddOrdererFlags(cmd)
}

继续打开:

// AddOrdererFlags adds flags for orderer-related commands
func AddOrdererFlags(cmd *cobra.Command) {
    flags := cmd.PersistentFlags()
    flags.StringVarP(&OrderingEndpoint, "orderer", "o", "", "Ordering service endpoint")
    flags.BoolVarP(&tlsEnabled, "tls", "", false, "Use TLS when communicating with the orderer endpoint")
    flags.BoolVarP(&clientAuth, "clientauth", "", false,
        "Use mutual TLS when communicating with the orderer endpoint")
    flags.StringVarP(&caFile, "cafile", "", "",
        "Path to file containing PEM-encoded trusted certificate(s) for the ordering endpoint")
    flags.StringVarP(&keyFile, "keyfile", "", "",
        "Path to file containing PEM-encoded private key to use for mutual TLS "+
            "communication with the orderer endpoint")
    flags.StringVarP(&certFile, "certfile", "", "",
        "Path to file containing PEM-encoded X509 public key to use for "+
            "mutual TLS communication with the orderer endpoint")
    flags.StringVarP(&ordererTLSHostnameOverride, "ordererTLSHostnameOverride",
        "", "", "The hostname override to use when validating the TLS connection to the orderer.")
}

1、flags := cmd.PersistentFlags()是说这些标志的作用范围,可以作用于本命令以及其子命令中。
2、这里flags 是一个FlagSet实例,后面的代码即向这个实例中添加了一些Flag实例。比如flags.StringVarP(&OrderingEndpoint, “orderer”, “o”, “”, “Ordering service endpoint”),里面的过程就是将这几个参数经过一顿操作最后存入到一个Flag实例中相应的字段里,再将这个实例添加到调用StringVarP()的FlagSet实例flags中。
3、AddOrdererFlags()里传的参数是channelCmd,便将channelCmd与这些flag绑定到了一起。
到这里也就知道了peer channel create -o orderer.example.com:7050 …的-o参数。

要看的子命令是create,所以继续找到create这个子命令的位置,在/fabric/peer/channel/create.go中:

func createCmd(cf *ChannelCmdFactory) *cobra.Command {
    createCmd := &cobra.Command{
        Use:   "create",
        Short: createCmdDescription,
        Long:  createCmdDescription,
        RunE: func(cmd *cobra.Command, args []string) error {
            return create(cmd, args, cf)
        },
    }
    flagList := []string{
        "channelID",
        "file",
        "timeout",
    }
    attachFlags(createCmd, flagList)

    return createCmd
}

主要看attachFlags(createCmd, flagList),来到channel.go中,首先看下init()也即resetFlags():

func resetFlags() {
    flags = &pflag.FlagSet{}
    flags.StringVarP(&genesisBlockPath, "blockpath", "b", common.UndefinedParamValue, "Path to file containing genesis block")

    flags.StringVarP(&channelID, "channelID", "c", common.UndefinedParamValue, "In case of a newChain command, the channel ID to create.")
    flags.StringVarP(&channelTxFile, "file", "f", "", "Configuration transaction file generated by a tool such as configtxgen for submitting to orderer")
    flags.IntVarP(&timeout, "timeout", "t", 5, "Channel creation timeout")
}

这个函数的内容和前面的AddOrdererFlags()很像,只不过这里没有设置flag的作用范围。flags.StringVarP(&genesisBlockPath, “blockpath”, “b”, common.UndefinedParamValue, “Path to file containing genesis block”),同样也是最后FlagSet的实例也即调用StringVarP()的flags,把由参数经一系列函数运算成的flag添加了进去。后面其他三个句子也是同理。继续来看attachFlags():

func attachFlags(cmd *cobra.Command, names []string) {
    cmdFlags := cmd.Flags() 
    for _, name := range names {
        if flag := flags.Lookup(name); flag != nil { 
            cmdFlags.AddFlag(flag) 
        } else {
            logger.Fatalf("Could not find flag '%s' to attach to commond '%s'", name, cmd.Name())
        }
    }
}

1、在resetFlags()里没有设置flag的作用范围,这里的第一句cmdFlags := cmd.Flags() ,在.Flags()这个函数中, c.flags即createCmd.flags为nil,执行
flag.NewFlagSet(c.Name(), flag.ContinueOnError),得到一个字段interspersed值为 true的FlagSet,设置了作用范围(我理解)。
2、在上面resetFlags()中,这个FlagSet的实例flags添加了许多的flag,这里flags.Lookup(name)通过name找到上面添加的对应的flag进行返回。
3、通过AddFlag()将2得到的flag添加到另外的FlagSet结构体实例cmdFlags中。
4、attachFlags(createCmd, flagList),通过attachFlags()便将子命令和flags联系到了一起,flag不迷路。

到此便可以大概理解文章开头的命令以及参数了。

猜你喜欢

转载自blog.csdn.net/u010931295/article/details/82690775