github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/cli/command/cli.go (about)

     1  package command
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  
    12  	"github.com/docker/docker/api"
    13  	cliflags "github.com/docker/docker/cli/flags"
    14  	"github.com/docker/docker/cliconfig"
    15  	"github.com/docker/docker/cliconfig/configfile"
    16  	"github.com/docker/docker/cliconfig/credentials"
    17  	"github.com/docker/docker/client"
    18  	"github.com/docker/docker/dockerversion"
    19  	dopts "github.com/docker/docker/opts"
    20  	"github.com/docker/go-connections/sockets"
    21  	"github.com/docker/go-connections/tlsconfig"
    22  	"golang.org/x/net/context"
    23  )
    24  
    25  // Streams is an interface which exposes the standard input and output streams
    26  type Streams interface {
    27  	In() *InStream
    28  	Out() *OutStream
    29  	Err() io.Writer
    30  }
    31  
    32  // DockerCli represents the docker command line client.
    33  // Instances of the client can be returned from NewDockerCli.
    34  type DockerCli struct {
    35  	configFile      *configfile.ConfigFile
    36  	in              *InStream
    37  	out             *OutStream
    38  	err             io.Writer
    39  	keyFile         string
    40  	client          client.APIClient
    41  	hasExperimental *bool
    42  }
    43  
    44  // HasExperimental returns true if experimental features are accessible
    45  func (cli *DockerCli) HasExperimental() bool {
    46  	if cli.hasExperimental == nil {
    47  		if cli.client == nil {
    48  			cli.Initialize(cliflags.NewClientOptions())
    49  		}
    50  		enabled := false
    51  		cli.hasExperimental = &enabled
    52  		enabled, _ = cli.client.Ping(context.Background())
    53  	}
    54  
    55  	return *cli.hasExperimental
    56  }
    57  
    58  // Client returns the APIClient
    59  func (cli *DockerCli) Client() client.APIClient {
    60  	return cli.client
    61  }
    62  
    63  // Out returns the writer used for stdout
    64  func (cli *DockerCli) Out() *OutStream {
    65  	return cli.out
    66  }
    67  
    68  // Err returns the writer used for stderr
    69  func (cli *DockerCli) Err() io.Writer {
    70  	return cli.err
    71  }
    72  
    73  // In returns the reader used for stdin
    74  func (cli *DockerCli) In() *InStream {
    75  	return cli.in
    76  }
    77  
    78  // ConfigFile returns the ConfigFile
    79  func (cli *DockerCli) ConfigFile() *configfile.ConfigFile {
    80  	return cli.configFile
    81  }
    82  
    83  // CredentialsStore returns a new credentials store based
    84  // on the settings provided in the configuration file.
    85  func (cli *DockerCli) CredentialsStore() credentials.Store {
    86  	if cli.configFile.CredentialsStore != "" {
    87  		return credentials.NewNativeStore(cli.configFile)
    88  	}
    89  	return credentials.NewFileStore(cli.configFile)
    90  }
    91  
    92  // Initialize the dockerCli runs initialization that must happen after command
    93  // line flags are parsed.
    94  func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error {
    95  	cli.configFile = LoadDefaultConfigFile(cli.err)
    96  
    97  	var err error
    98  	cli.client, err = NewAPIClientFromFlags(opts.Common, cli.configFile)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	if opts.Common.TrustKey == "" {
   103  		cli.keyFile = filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile)
   104  	} else {
   105  		cli.keyFile = opts.Common.TrustKey
   106  	}
   107  
   108  	return nil
   109  }
   110  
   111  // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err.
   112  func NewDockerCli(in io.ReadCloser, out, err io.Writer) *DockerCli {
   113  	return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err}
   114  }
   115  
   116  // LoadDefaultConfigFile attempts to load the default config file and returns
   117  // an initialized ConfigFile struct if none is found.
   118  func LoadDefaultConfigFile(err io.Writer) *configfile.ConfigFile {
   119  	configFile, e := cliconfig.Load(cliconfig.ConfigDir())
   120  	if e != nil {
   121  		fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
   122  	}
   123  	if !configFile.ContainsAuth() {
   124  		credentials.DetectDefaultStore(configFile)
   125  	}
   126  	return configFile
   127  }
   128  
   129  // NewAPIClientFromFlags creates a new APIClient from command line flags
   130  func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
   131  	host, err := getServerHost(opts.Hosts, opts.TLSOptions)
   132  	if err != nil {
   133  		return &client.Client{}, err
   134  	}
   135  
   136  	customHeaders := configFile.HTTPHeaders
   137  	if customHeaders == nil {
   138  		customHeaders = map[string]string{}
   139  	}
   140  	customHeaders["User-Agent"] = UserAgent()
   141  
   142  	verStr := api.DefaultVersion
   143  	if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" {
   144  		verStr = tmpStr
   145  	}
   146  
   147  	httpClient, err := newHTTPClient(host, opts.TLSOptions)
   148  	if err != nil {
   149  		return &client.Client{}, err
   150  	}
   151  
   152  	return client.NewClient(host, verStr, httpClient, customHeaders)
   153  }
   154  
   155  func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string, err error) {
   156  	switch len(hosts) {
   157  	case 0:
   158  		host = os.Getenv("DOCKER_HOST")
   159  	case 1:
   160  		host = hosts[0]
   161  	default:
   162  		return "", errors.New("Please specify only one -H")
   163  	}
   164  
   165  	host, err = dopts.ParseHost(tlsOptions != nil, host)
   166  	return
   167  }
   168  
   169  func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, error) {
   170  	if tlsOptions == nil {
   171  		// let the api client configure the default transport.
   172  		return nil, nil
   173  	}
   174  
   175  	config, err := tlsconfig.Client(*tlsOptions)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	tr := &http.Transport{
   180  		TLSClientConfig: config,
   181  	}
   182  	proto, addr, _, err := client.ParseHost(host)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  
   187  	sockets.ConfigureTransport(tr, proto, addr)
   188  
   189  	return &http.Client{
   190  		Transport: tr,
   191  	}, nil
   192  }
   193  
   194  // UserAgent returns the user agent string used for making API requests
   195  func UserAgent() string {
   196  	return "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")"
   197  }