fabric 中命令行组件使用cobra组件
Cobra提供简单的接口来创建强大的现代化CLI接口,比如git与go工具。Cobra同时也是一个程序, 用于创建CLI程序,配置文件使用viper组件
关于cobra的使用可以查看文档 ,官方地址:https://github.com/spf13/cobra
// 定义命令
var rootCmd = &cobra.Command{
Use: "hugo",
Short: "Hugo is a very fast static site generator",
Long: `A Fast and Flexible Static Site Generator built with
love by spf13 and friends in Go.
Complete documentation is available at http://hugo.spf13.com`,
Run: func(cmd *cobra.Command, args []string) {
// 定义命令方法
},
}
func Execute() {
// 执行命令。可以添加子命令rootCmd.AddCommand
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func main() {
cmd.Execute()
}
可以为命令添加子命令
cmd.AddCommand(cmd) // 添加子命令
当你的参数作为persistent flag存在时,在其所有的子命令之下该参数都是可见的。而local flag则只能在该命令调用时执行。
testCmd.PersistentFlags().String("foo", "", "A help for foo") //全局flag
testCmd.Flags().String("foolocal", "", "A help for foo") // 本地flag
cobra命令组件支持简写,以string 为例,
cmd.Flags().String():cmd.Flags().String(“f”, “”, “test”)
test --f
StringP():cmd.Flags().StringP(“aaa”, “a”, “”, “test”)
test --aaa
test -a
获取参数由3中
1:通过参数,
var testCmd = &cobra.Command{
Use: "test",
Short: "test",
Long: `test test`,
Run: func(cmd *cobra.Command, args []string) {
// 打印参数
fmt.Println("test called",args)
},
}
2 使用get
str := testCmd.Flags().GetString("aaa")
3 绑定(fabric中使用),执行函数时可直接使用
flags.StringVarP(&channelID, "channelID", "c", common.UndefinedParamValue, "In case of a newChain command, the channel ID to create.")
更多介绍看官方介绍 https://github.com/spf13/cobra
peer模块在 fabric/peer
中,其中包含7个子命令
>peer
>chaincode
>channel
>clilogging
>common
>gossip
>node
>version
main.go
源码fabric/peer/main.go
// peer 主命令
var mainCmd = &cobra.Command{
Use: "peer", // 使用方法,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
// 检查配置文件中的 日志级别core.yaml CORE_LOGGING_LEVEL )
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) {
// 如果只输入peer打印帮助命令
if versionFlag {
fmt.Print(version.GetInfo())
} else {
cmd.HelpFunc()(cmd, args)
}
},
}
func main() {
// 使用viper组件检查环境变量
// 添加子命令 version、node、 chaincode、 clilogging 、channel
mainCmd.AddCommand(version.Cmd()) // 版本子命令
mainCmd.AddCommand(node.Cmd()) // 节点子命令
mainCmd.AddCommand(chaincode.Cmd(nil)) // 链码子命令
mainCmd.AddCommand(clilogging.Cmd(nil)) // 日志子命令
mainCmd.AddCommand(channel.Cmd(nil)) // 通道管理子命令
// ... msp 配置
// 执行命令
if mainCmd.Execute() != nil {
os.Exit(1)
}
logger.Info("Exiting.....")
}
如 peer.yaml中的 command,使子命令node:
peer node start
创建通道子命令命令:
$CAFILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/suisui.com/orderers/orderer.suisui.com/msp/tlscacerts/tlsca.suisui.com-cert.pem
$ peer channel create -o orderer.suisui.com:7050 -c suisuichannel -f ./channel-artifacts/channel.tx --tls true --cafile $CAFILE
peer/node/channel.go
// 返回channel 命令
func Cmd(cf *ChannelCmdFactory) *cobra.Command {
AddFlags(channelCmd) // 添加公共参数
// 添加子命令,每个子命令都是一个单独的go文件
channelCmd.AddCommand(createCmd(cf)) // peer channel create :create.go
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
}
// 添加公共参数 for create and join
func AddFlags(cmd *cobra.Command) {
common.AddOrdererFlags(cmd)
}
var flags *pflag.FlagSet
// 初始化 重置flag
func init() {
resetFlags()
}
// 添加公共参数并绑定,这个func抽取出来是为了方便测试
func resetFlags() {
flags = &pflag.FlagSet{}
// 定义falg有两种方式,
// flag 全局可见 如:Cmd.PersistentFlags().String("foo", "", "A help for foo")
// flag 当前命令可见如:Cmd.Flags().String("foolocal", "", "A help for foo")
// flag 执行方式也有两种 如string有 StringVarP StringVar
// StringVarP: 支持简写
// cmd.Flags().String("foo", "", "test"),使用 --foo
// cmd.Flags().StringP("foo", "f", "", "test") 使用 --foo或者 -f
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")
}
// 批量添加falg,子命令会调用这个方法
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())
}
}
}
peer/common/ordereerenv.go
// 添加flag
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.")
}
peer/channel/create.go
定义了 createCmd(cf *ChannelCmdFactory)函数用于创建子命令,
peer channel create
用于创建新的应用通道,构造通道配置交易信息通过broadcast服务客户端发送给Orderer节点,请求创建新的通道,然后获取该通道的创始块写入到本地文件。具体查看后面peer 节点的分析文章
// 定义 peer channel create 子命令
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", // 通道id
"file", // 配置文件
"timeout", // 创建通道超时时间
}
// 调用channel.go中定义的批量添加标签
attachFlags(createCmd, flagList)
return createCmd
}
func executeCreate(cf *ChannelCmdFactory) error {
var err error
// 创建通道配置交易信息并发送
if err = sendCreateChainTransaction(cf); err != nil {
return err
}
var block *cb.Block
// 获取创始块
if block, err = getGenesisBlock(cf); err != nil {
return err
}
b, err := proto.Marshal(block)
if err != nil {
return err
}
file := channelID + ".block" // 写入本地文件
if err = ioutil.WriteFile(file, b, 0644); err != nil {
return err
}
return nil
}
// 创建通道
func create(cmd *cobra.Command, args []string, cf *ChannelCmdFactory) error {
//the global chainID filled by the "-c" command
if channelID == common.UndefinedParamValue {
return errors.New("Must supply channel ID")
}
var err error
if cf == nil {
cf, err = InitCmdFactory(EndorserNotRequired, OrdererRequired)
if err != nil {
return err
}
}
return executeCreate(cf)
}