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不迷路。
到此便可以大概理解文章開頭的命令以及引數了。