标签:manage lis 包名 ctr log release coder short 创建
最近在看doccker的源码,最新的master分支(估计是1.12.4,因为最新的release是1.12.3)命令行解析全部都使用了第3方的包https://github.com/spf13/cobra。然后看了一下别的分支的代码,感觉结构确实清晰了很多,可读性变高了不少。先看一下如何去使用。
客户端main()在docker/docker/cmd/docker下,可以直接使用go build编译(把vendor下的依赖包移出来就可以了)。
L20-58:
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())
cmd.AddCommand(newDaemonCommand())
commands.AddCommands(cmd, dockerCli)
return cmd
}
这个方法会完成所有命令行规则的添加,和相应执行的方法。RunE: func(cmd *cobra.Command, args []string) error {}是命令执行的方法,flags是参数的解析。当我们输入docker -v,执行showversion()方法,*cobra.Command结构体是一个树的结构,docker下面有很多的子命令,比如docker image,然后image下面可以添加参数 -a等,最后只要在main()中执行cmd.Execute()就可以完成所有解析,这是也是第3方包的方法。
ctrl+鼠标右键点击进入commands.AddCommands(cmd, dockerCli)
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),
。。。)
}
封装了一下cmd.AddCommand()方法,然后node.service.stack.swarm.container这些包都写了个大写new方法来创建命令,继续进入container.NewContainerCommand(dockerCli),这是所有的方法都传入了dockerCli(单例模式直视感),
func NewContainerCommand(dockerCli *command.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "container",
Short: "Manage containers",
Args: cli.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
},
}
cmd.AddCommand(
NewAttachCommand(dockerCli),
NewCommitCommand(dockerCli),
NewCopyCommand(dockerCli),
NewCreateCommand(dockerCli),
NewDiffCommand(dockerCli),
NewExecCommand(dockerCli),
NewExportCommand(dockerCli),
NewKillCommand(dockerCli),
NewLogsCommand(dockerCli),
NewPauseCommand(dockerCli),
NewPortCommand(dockerCli),
NewRenameCommand(dockerCli),
NewRestartCommand(dockerCli),
NewRmCommand(dockerCli),
NewRunCommand(dockerCli),
NewStartCommand(dockerCli),
NewStatsCommand(dockerCli),
NewStopCommand(dockerCli),
NewTopCommand(dockerCli),
NewUnpauseCommand(dockerCli),
NewUpdateCommand(dockerCli),
NewWaitCommand(dockerCli),
newListCommand(dockerCli),
newInspectCommand(dockerCli),
NewPruneCommand(dockerCli),
)
return cmd
}
docker contain下又添加了很多的命令,继续进入
func NewCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
var opts createOptions
var copts *runconfigopts.ContainerOptions
cmd := &cobra.Command{
Use: "create [OPTIONS] IMAGE [COMMAND] [ARG...]",
Short: "Create a new container",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
copts.Image = args[0]
if len(args) > 1 {
copts.Args = args[1:]
}
return runCreate(dockerCli, cmd.Flags(), &opts, copts)
},
}
flags := cmd.Flags()
flags.SetInterspersed(false)
flags.StringVar(&opts.name, "name", "", "Assign a name to the container")
// Add an explicit help that doesn‘t have a `-h` to prevent the conflict
// with hostname
flags.Bool("help", false, "Print usage")
command.AddTrustedFlags(flags, true)
copts = runconfigopts.AddFlags(flags)
return cmd
}
这里可以看到解析了name和help,docker run create命令就是这么来的,然后看一下RunE中是如何向服务器端发送请求的,继续进入最后可以得到一个这个方法,response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (types.ContainerCreateResponse, error) {
var response types.ContainerCreateResponse
query := url.Values{}
if containerName != "" {
query.Set("name", containerName)
}
body := configWrapper{
Config: config,
HostConfig: hostConfig,
NetworkingConfig: networkingConfig,
}
serverResp, err := cli.post(ctx, "/containers/create", query, body, nil)
if err != nil {
if serverResp != nil && serverResp.statusCode == 404 && strings.Contains(err.Error(), "No such image") {
return response, imageNotFoundError{config.Image}
}
return response, err
}
err = json.NewDecoder(serverResp.body).Decode(&response)
ensureReaderClosed(serverResp)
return response, err
}
发送post /containers/create请求,细节的地方需要继续去看,服务器端的命令解析也是一样的,目录在docker/docker/cmd/dockerd下,go编译的包是通过包名获取的,所以deamon的命令就是dockerd,有一个很好的方法是dockerd -D,可以看到调试的信息,service api的添加,网络的初始化等。。
https://github.com/zjj2wry/httptest,这是拿那个命令行解析写的一个post请求,可以携带参数,cookies等,然后生成接口文档,对我已经完全够了(目前业务百分之90都是post),主要写个接口得给前端一个文档,偷个懒。。
标签:manage lis 包名 ctr log release coder short 创建
原文地址:http://www.cnblogs.com/52zjj/p/6029739.html