Docker Client

Code is in api/client/

Go's init function

The init function is a special function in go. Each source file can have an init function, or even multiple init functions. They will be evaluated right before execution - meaning that after all variables have been initialized. Go initializes in the following order:

imported packages -> local variables -> init function(s) -> main

This means that by the time init runs variables which rely on imported code will be usable. It can be used to verify program state or do some kind of bootstrapping for a library.

Example init function

func init() {}

Parsing Arguments

Help and Version Flags: docker/flags.go

var (
    flHelp    = flag.Bool([]string{"h", "-help"}, false, "Print usage")
    flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
)

Flags common to client and server: docker/common.go

var (
    commonFlags = &cli.CommonFlags{FlagSet: new(flag.FlagSet)}
}
func init() {
    cmd := commonFlags.FlagSet

    cmd.BoolVar(&commonFlags.Debug, []string{"D", "-debug"}, false, "Enable debug mode")
    cmd.StringVar(&commonFlags.LogLevel, []string{"l", "-log-level"}, "info", "Set the logging level")
    cmd.BoolVar(&commonFlags.TLS, []string{"-tls"}, false, "Use TLS; implied by --tlsverify")
    cmd.BoolVar(&commonFlags.TLSVerify, []string{"-tlsverify"}, dockerTLSVerify, "Use TLS and verify the remote")

    // TODO use flag flag.String([]string{"i", "-identity"}, "", "Path to libtrust key file")

    var tlsOptions tlsconfig.Options
    commonFlags.TLSOptions = &tlsOptions
    cmd.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust certs signed only by this CA")
    cmd.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file")
    cmd.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file")

    cmd.Var(opts.NewNamedListOptsRef("hosts", &commonFlags.Hosts, opts.ValidateHost), []string{"H", "-host"}, "Daemon socket(s) to connect to")
}

Client Specific Flags: docker/client.go

var clientFlags = &cli.ClientFlags{FlagSet: new(flag.FlagSet), Common: commonFlags}

func init() {
    client := clientFlags.FlagSet
    client.StringVar(&clientFlags.ConfigDir, []string{"-config"}, cliconfig.ConfigDir(), "Location of client config files")

    clientFlags.PostParse = func() {
        clientFlags.Common.PostParse()

        if clientFlags.ConfigDir != "" {
            cliconfig.SetConfigDir(clientFlags.ConfigDir)
        }

        if clientFlags.Common.TrustKey == "" {
            clientFlags.Common.TrustKey = filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile)
        }

        if clientFlags.Common.Debug {
            utils.EnableDebug()
        }
    }
}

client

Cli

cmdHelp

DockerCli

CmdLogout cmdAttach cmdRun cmdPull ...

Implementation

NewDockerCli, the DockerCli factory function.

clientCli := client.NewDockerCli(.., clientFlags)
c := cli.New(clientCli, daemonCli)
c.Run(flag.Args()...)
=>command = cli.command()
=>command()
func New(handlers ...Handler) *Cli {
    // make the generic Cli object the first cli handler
    // in order to handle `docker help` appropriately
    cli := new(Cli)
    cli.handlers = append([]Handler{cli}, handlers...)
    return cli
}
func (cli *Cli) command(args ...string) (func(...string) error, error) {
    for _, c := range cli.handlers {
        if c == nil {
            continue
        }
        camelArgs := make([]string, len(args))
        for i, s := range args {
            if len(s) == 0 {
                return nil, errors.New("empty command")
            }
            camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
        }
        methodName := "Cmd" + strings.Join(camelArgs, "")
        method := reflect.ValueOf(c).MethodByName(methodName)
        if method.IsValid() {
            if c, ok := c.(Initializer); ok {
                if err := c.Initialize(); err != nil {
                    return nil, initErr{err}
                }
            }
            return method.Interface().(func(...string) error), nil
        }
    }
    return nil, errors.New("command not found")
}
func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli {
    cli := &DockerCli{
        in:      in,
        out:     out,
        err:     err,
        keyFile: clientFlags.Common.TrustKey,
    }

    cli.init = func() error {
        clientFlags.PostParse()
        configFile, e := cliconfig.Load(cliconfig.ConfigDir())
        if e != nil {
            fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e)
        }
        if !configFile.ContainsAuth() {
            credentials.DetectDefaultStore(configFile)
        }
        cli.configFile = configFile

        host, err := getServerHost(clientFlags.Common.Hosts, clientFlags.Common.TLSOptions)
        if err != nil {
            return err
        }

        customHeaders := cli.configFile.HTTPHeaders
        if customHeaders == nil {
            customHeaders = map[string]string{}
        }
        customHeaders["User-Agent"] = clientUserAgent()

        verStr := api.DefaultVersion
        if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" {
            verStr = tmpStr
        }

        httpClient, err := newHTTPClient(host, clientFlags.Common.TLSOptions)
        if err != nil {
            return err
        }

        client, err := client.NewClient(host, verStr, httpClient, customHeaders)
        if err != nil {
            return err
        }
        cli.client = client

        if cli.in != nil {
            cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in)
        }
        if cli.out != nil {
            cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out)
        }

        return nil
    }

    return cli
}
cmdPull
=>imagePullPrivileged
  =>responseBody := cli.client.ImagePull
    =>tryImageCreate
      =>post("/images/create")
        =>cancellable.Do