fabric源码解读【基础】:命令行组件cobra

文章目录

  • cobra介绍
    • 示例
    • 添加子命令
    • 添加参数
    • 命令执行方式
    • 获取参数值
    • 帮助文档
  • fabric中的使用,以peer节点源码举例:
    • peer命令
    • peer channel 命令
    • peer channel create 命令

cobra介绍

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

fabric中的使用,以peer节点源码举例:

peer命令

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 channel 命令

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 命令

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)
}

你可能感兴趣的:(区块链)