docker命令解析

来源:互联网 发布:ubuntu下安装windows 编辑:程序博客网 时间:2024/05/16 10:59

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

package mainimport (    "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 commandfunc 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` commandfunc 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 0