1. 程式人生 > >Fabric中Cobra的使用簡析

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不迷路。

到此便可以大概理解文章開頭的命令以及引數了。