docker命令解析

以前docker启动客户端和服务端使用的都是同一个命令(二进制可执行文件),现在docker将客户端程序和服务端程序分开了分别为docker和dockerd。而且docker的命令解析使用了一个第三方的库,名字叫cobra,github地址在:https://github.com/getting-started。客户端和服务端使用的是一样的命令解析方式,所以我们选取客户端分析docker的命令解析过程。
首先我们介绍下cobra这个库的简单用法,从cobra的github页面的例子中删减了下:

package main

import (
    "fmt"

    "github.com/spf13/cobra"
)

func main() {
   //1.定义主命令
    var Version bool
    var rootCmd = &cobra.Command{
        Use:   "root [sub]",
        Short: "My root command",
       //命令执行的函数
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Printf("Inside rootCmd Run with args: %v\n", args)
            if Version {
             fmt.Printf("Version:1.0\n")
            }
        },


    }
   //2.定义子命令
    var subCmd = &cobra.Command{
        Use:   "sub [no options!]",
        Short: "My subcommand",
        //命令执行的函数
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Printf("Inside subCmd Run with args: %v\n", args)
        },

    }
    //添加子命令
    rootCmd.AddCommand(subCmd)
   //3.为命令添加选项
    flags := rootCmd.Flags()
    flags.BoolVarP(&Version, "version", "v", false, "Print version information and quit")
      //执行命令
    _ = rootCmd.Execute()
}

基本用法大概就是四步:
1.定义一个主命令(包含命令执行函数等)
2.定义若干子命令(包含命令执行函数等,根据需要可以为子命令定义子命令),并添加到主命令
3.为命令添加选项
4.执行命令
好了,了解了cobra的大概用法,我们从docker(客户端)的main函数开始分析
贴一下main函数:


func main() {
    // Set terminal emulation based on platform as required.
    stdin, stdout, stderr := term.StdStreams()
    logrus.SetOutput(stderr)

    dockerCli := command.NewDockerCli(stdin, stdout, stderr)
    //定义主命令
    cmd := newDockerCommand(dockerCli)
   //执行命令
    if err := cmd.Execute(); err != nil {
        if sterr, ok := err.(cli.StatusError); ok {
            if sterr.Status != "" {
                fmt.Fprintln(stderr, sterr.Status)
            }
            // StatusError should only be used for errors, and all errors should
            // have a non-zero exit status, so never exit with 0
            if sterr.StatusCode == 0 {
                os.Exit(1)
            }
            os.Exit(sterr.StatusCode)
        }
        fmt.Fprintln(stderr, err)
        os.Exit(1)
    }
}

main函数中并没有我们前面提到的完整四步,而是使用了一个函数newDockerCommand来做了其他工作。我们接着分析该函数,代码不多,我们还是贴一下该函数代码:

func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command {
    //选项结构对象,之后通过传入的选项选项参数进行填充
    opts := cliflags.NewClientOptions()
    //选项集合
    var flags *pflag.FlagSet
       //定义主命令
    cmd := &cobra.Command{
        Use:              "docker [OPTIONS] COMMAND [arg...]",
        Short:            "A self-sufficient runtime for containers.",
        SilenceUsage:     true,
        SilenceErrors:    true,
        TraverseChildren: true,
        Args:             noArgs,
        RunE: func(cmd *cobra.Command, args []string) error {
            if opts.Version {
                showVersion()
                return nil
            }
            fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
            return nil
        },
        PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
            // flags must be the top-level command flags, not cmd.Flags()
            opts.Common.SetDefaultOptions(flags)
            dockerPreRun(opts)
            return dockerCli.Initialize(opts)
        },
    }
    //设置默认的处理方式
    cli.SetupRootCommand(cmd)
        //为主命令添加选项
    flags = cmd.Flags()
    flags.BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit")
    flags.StringVar(&opts.ConfigDir, "config", cliconfig.ConfigDir(), "Location of client config files")
    //为主命令添加公共选项
    opts.Common.InstallFlags(flags)
        //设置命令的输入
    cmd.SetOutput(dockerCli.Out())
    //添加daemon选项,其实已经作废了,应为新代码已经将客户端和服务端守护进程命令分开了,不需要这个选项区分启动客户端还是服务端
    cmd.AddCommand(newDaemonCommand())
    // AddCommands adds all the commands from cli/command to the root command
    //添加子命令及子命令的选项
    commands.AddCommands(cmd, dockerCli)

    return cmd
}

可以看到,表面上在该函数里面也只是定义了主命令,并为主命令添加了选项。那子命令呢?看到

//添加子命令及子命令的选项
commands.AddCommands(cmd, dockerCli)

其实该函数的工作就是添加子命令,子命令的子命令,以及命令的选项。

// AddCommands adds all the commands from cli/command to the root command
func AddCommands(cmd *cobra.Command, dockerCli *command.DockerCli) {
    cmd.AddCommand(
        node.NewNodeCommand(dockerCli),
        service.NewServiceCommand(dockerCli),
        stack.NewStackCommand(dockerCli),
        stack.NewTopLevelDeployCommand(dockerCli),
        swarm.NewSwarmCommand(dockerCli),
        container.NewContainerCommand(dockerCli),
        image.NewImageCommand(dockerCli),
        system.NewSystemCommand(dockerCli),
        container.NewRunCommand(dockerCli),
        image.NewBuildCommand(dockerCli),
        network.NewNetworkCommand(dockerCli),
        hide(system.NewEventsCommand(dockerCli)),
        registry.NewLoginCommand(dockerCli),
        registry.NewLogoutCommand(dockerCli),
        registry.NewSearchCommand(dockerCli),
        system.NewVersionCommand(dockerCli),
        volume.NewVolumeCommand(dockerCli),
        hide(system.NewInfoCommand(dockerCli)),
        hide(container.NewAttachCommand(dockerCli)),
        hide(container.NewCommitCommand(dockerCli)),
        hide(container.NewCopyCommand(dockerCli)),
        hide(container.NewCreateCommand(dockerCli)),
        hide(container.NewDiffCommand(dockerCli)),
        hide(container.NewExecCommand(dockerCli)),
        hide(container.NewExportCommand(dockerCli)),
        hide(container.NewKillCommand(dockerCli)),
        hide(container.NewLogsCommand(dockerCli)),
        hide(container.NewPauseCommand(dockerCli)),
        hide(container.NewPortCommand(dockerCli)),
        hide(container.NewPsCommand(dockerCli)),
        hide(container.NewRenameCommand(dockerCli)),
        hide(container.NewRestartCommand(dockerCli)),
        hide(container.NewRmCommand(dockerCli)),
        hide(container.NewStartCommand(dockerCli)),
        hide(container.NewStatsCommand(dockerCli)),
        hide(container.NewStopCommand(dockerCli)),
        hide(container.NewTopCommand(dockerCli)),
        hide(container.NewUnpauseCommand(dockerCli)),
        hide(container.NewUpdateCommand(dockerCli)),
        hide(container.NewWaitCommand(dockerCli)),
        hide(image.NewHistoryCommand(dockerCli)),
        hide(image.NewImagesCommand(dockerCli)),
        hide(image.NewImportCommand(dockerCli)),
        hide(image.NewLoadCommand(dockerCli)),
        hide(image.NewPullCommand(dockerCli)),
        hide(image.NewPushCommand(dockerCli)),
        hide(image.NewRemoveCommand(dockerCli)),
        hide(image.NewSaveCommand(dockerCli)),
        hide(image.NewTagCommand(dockerCli)),
        hide(system.NewInspectCommand(dockerCli)),
        checkpoint.NewCheckpointCommand(dockerCli),
        plugin.NewPluginCommand(dockerCli),
    )
}

可以看到添加了很多子命令,这里面的都是一级子命令,也就是“docker xxx xxx xxx”中跟着docker的命令.注意到有些是被hide函数包裹的,有些则不是,我们在这里先不关心这个。我们选取一个命令docker images来分析

    hide(image.NewImagesCommand(dockerCli)),

进入该函数:

// NewImagesCommand creates a new `docker images` command
func NewImagesCommand(dockerCli *command.DockerCli) *cobra.Command {
    opts := imagesOptions{filter: opts.NewFilterOpt()}

    cmd := &cobra.Command{
        Use:   "images [OPTIONS] [REPOSITORY[:TAG]]",
        Short: "List images",
        Args:  cli.RequiresMaxArgs(1),
        //命令执行函数,有一个错误类型的变量作为返回值
        RunE: func(cmd *cobra.Command, args []string) error {
            if len(args) > 0 {
                opts.matchName = args[0]
            }
            return runImages(dockerCli, opts)
        },
    }
        //为命令添加选项
    flags := cmd.Flags()

    flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only show numeric IDs")
    flags.BoolVarP(&opts.all, "all", "a", false, "Show all images (default hides intermediate images)")
    flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output")
    flags.BoolVar(&opts.showDigests, "digests", false, "Show digests")
    flags.StringVar(&opts.format, "format", "", "Pretty-print images using a Go template")
    flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")

    return cmd
}

当我们执行docker images时,就会执行命令中定义的函数,进而执行runImages(),改函数根据选项执行不同的操作。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值