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不迷路。
到此便可以大概理解文章开头的命令以及参数了。