2016-09-08 13:11:39 -04:00
package command
import (
2018-05-03 21:02:44 -04:00
"context"
2016-09-08 13:11:39 -04:00
"io"
2019-03-06 09:01:12 -05:00
"io/ioutil"
2016-09-08 13:11:39 -04:00
"os"
2017-06-15 14:41:54 -04:00
"path/filepath"
2016-09-08 13:11:39 -04:00
"runtime"
2018-10-09 22:13:01 -04:00
"strconv"
2020-04-07 18:57:41 -04:00
"strings"
"time"
2016-09-08 13:11:39 -04:00
2017-06-15 14:41:54 -04:00
"github.com/docker/cli/cli/config"
2017-04-17 18:07:56 -04:00
cliconfig "github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/configfile"
2018-12-17 05:27:07 -05:00
dcontext "github.com/docker/cli/cli/context"
"github.com/docker/cli/cli/context/docker"
"github.com/docker/cli/cli/context/store"
2018-12-10 09:41:22 -05:00
"github.com/docker/cli/cli/debug"
2017-04-17 18:07:56 -04:00
cliflags "github.com/docker/cli/cli/flags"
2017-06-15 14:41:54 -04:00
manifeststore "github.com/docker/cli/cli/manifest/store"
registryclient "github.com/docker/cli/cli/registry/client"
2019-01-28 08:52:58 -05:00
"github.com/docker/cli/cli/streams"
2017-09-12 12:39:13 -04:00
"github.com/docker/cli/cli/trust"
2019-01-08 10:03:51 -05:00
"github.com/docker/cli/cli/version"
2017-05-15 08:45:19 -04:00
dopts "github.com/docker/cli/opts"
2019-09-20 11:13:26 -04:00
"github.com/docker/docker/api"
2017-06-15 14:41:54 -04:00
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
2017-05-08 13:51:30 -04:00
"github.com/docker/docker/client"
2016-09-08 13:11:39 -04:00
"github.com/docker/go-connections/tlsconfig"
2020-04-16 05:23:37 -04:00
"github.com/moby/term"
2017-03-27 21:21:59 -04:00
"github.com/pkg/errors"
2016-11-17 13:54:10 -05:00
"github.com/spf13/cobra"
2017-10-30 12:21:41 -04:00
"github.com/theupdateframework/notary"
notaryclient "github.com/theupdateframework/notary/client"
"github.com/theupdateframework/notary/passphrase"
2016-09-08 13:11:39 -04:00
)
2016-08-29 14:45:29 -04:00
// Streams is an interface which exposes the standard input and output streams
type Streams interface {
2019-01-28 08:52:58 -05:00
In ( ) * streams . In
Out ( ) * streams . Out
2016-08-29 14:45:29 -04:00
Err ( ) io . Writer
}
2016-12-25 16:23:35 -05:00
// Cli represents the docker command line client.
type Cli interface {
Client ( ) client . APIClient
2019-01-28 08:52:58 -05:00
Out ( ) * streams . Out
2016-12-25 16:23:35 -05:00
Err ( ) io . Writer
2019-01-28 08:52:58 -05:00
In ( ) * streams . In
SetIn ( in * streams . In )
Apply ( ops ... DockerCliOption ) error
2016-11-07 00:54:40 -05:00
ConfigFile ( ) * configfile . ConfigFile
2017-06-27 10:31:38 -04:00
ServerInfo ( ) ServerInfo
2017-12-20 09:04:41 -05:00
ClientInfo ( ) ClientInfo
2017-09-12 12:39:13 -04:00
NotaryClient ( imgRefAndAuth trust . ImageRefAndAuth , actions [ ] string ) ( notaryclient . Repository , error )
2017-12-05 09:41:03 -05:00
DefaultVersion ( ) string
2017-06-15 14:41:54 -04:00
ManifestStore ( ) manifeststore . Store
RegistryClient ( bool ) registryclient . RegistryClient
2018-03-08 14:56:56 -05:00
ContentTrustEnabled ( ) bool
2018-12-17 05:27:07 -05:00
ContextStore ( ) store . Store
CurrentContext ( ) string
StackOrchestrator ( flagValue string ) ( Orchestrator , error )
2018-11-09 09:10:41 -05:00
DockerEndpoint ( ) docker . Endpoint
2016-12-25 16:23:35 -05:00
}
// DockerCli is an instance the docker command line client.
2016-09-08 13:11:39 -04:00
// Instances of the client can be returned from NewDockerCli.
type DockerCli struct {
2019-11-28 10:54:51 -05:00
configFile * configfile . ConfigFile
in * streams . In
out * streams . Out
err io . Writer
client client . APIClient
serverInfo ServerInfo
clientInfo * ClientInfo
contentTrust bool
contextStore store . Store
currentContext string
dockerEndpoint docker . Endpoint
contextStoreConfig store . Config
2016-10-06 10:09:54 -04:00
}
2017-03-14 17:53:29 -04:00
// DefaultVersion returns api.defaultVersion or DOCKER_API_VERSION if specified.
2016-11-02 20:43:32 -04:00
func ( cli * DockerCli ) DefaultVersion ( ) string {
2019-09-20 11:13:26 -04:00
return cli . ClientInfo ( ) . DefaultVersion
2016-09-08 13:11:39 -04:00
}
// Client returns the APIClient
func ( cli * DockerCli ) Client ( ) client . APIClient {
return cli . client
}
// Out returns the writer used for stdout
2019-01-28 08:52:58 -05:00
func ( cli * DockerCli ) Out ( ) * streams . Out {
2016-09-08 13:11:39 -04:00
return cli . out
}
// Err returns the writer used for stderr
func ( cli * DockerCli ) Err ( ) io . Writer {
return cli . err
}
2017-03-30 20:21:14 -04:00
// SetIn sets the reader used for stdin
2019-01-28 08:52:58 -05:00
func ( cli * DockerCli ) SetIn ( in * streams . In ) {
2017-03-30 20:21:14 -04:00
cli . in = in
}
2016-09-08 13:11:39 -04:00
// In returns the reader used for stdin
2019-01-28 08:52:58 -05:00
func ( cli * DockerCli ) In ( ) * streams . In {
2016-09-08 13:11:39 -04:00
return cli . in
}
2016-11-17 13:54:10 -05:00
// ShowHelp shows the command help.
2017-05-03 17:58:52 -04:00
func ShowHelp ( err io . Writer ) func ( * cobra . Command , [ ] string ) error {
return func ( cmd * cobra . Command , args [ ] string ) error {
2020-05-07 08:25:59 -04:00
cmd . SetOut ( err )
2017-05-03 17:58:52 -04:00
cmd . HelpFunc ( ) ( cmd , args )
return nil
}
2016-11-17 13:54:10 -05:00
}
2016-09-08 13:11:39 -04:00
// ConfigFile returns the ConfigFile
func ( cli * DockerCli ) ConfigFile ( ) * configfile . ConfigFile {
2019-09-20 11:13:26 -04:00
if cli . configFile == nil {
cli . loadConfigFile ( )
}
2016-09-08 13:11:39 -04:00
return cli . configFile
}
2019-09-20 11:13:26 -04:00
func ( cli * DockerCli ) loadConfigFile ( ) {
cli . configFile = cliconfig . LoadDefaultConfigFile ( cli . err )
}
2017-03-14 17:53:29 -04:00
// ServerInfo returns the server version details for the host this client is
// connected to
func ( cli * DockerCli ) ServerInfo ( ) ServerInfo {
2017-12-20 09:04:41 -05:00
return cli . serverInfo
}
// ClientInfo returns the client details for the cli
func ( cli * DockerCli ) ClientInfo ( ) ClientInfo {
2019-09-20 11:13:26 -04:00
if cli . clientInfo == nil {
2020-05-24 16:21:20 -04:00
if err := cli . loadClientInfo ( ) ; err != nil {
panic ( err )
}
2019-09-20 11:13:26 -04:00
}
return * cli . clientInfo
}
func ( cli * DockerCli ) loadClientInfo ( ) error {
var experimentalValue string
// Environment variable always overrides configuration
if experimentalValue = os . Getenv ( "DOCKER_CLI_EXPERIMENTAL" ) ; experimentalValue == "" {
experimentalValue = cli . ConfigFile ( ) . Experimental
}
hasExperimental , err := isEnabled ( experimentalValue )
if err != nil {
return errors . Wrap ( err , "Experimental field" )
}
var v string
if cli . client != nil {
v = cli . client . ClientVersion ( )
} else {
v = api . DefaultVersion
}
cli . clientInfo = & ClientInfo {
DefaultVersion : v ,
HasExperimental : hasExperimental ,
}
return nil
2017-03-14 17:53:29 -04:00
}
2018-03-14 12:36:23 -04:00
// ContentTrustEnabled returns whether content trust has been enabled by an
2018-03-08 14:56:56 -05:00
// environment variable.
func ( cli * DockerCli ) ContentTrustEnabled ( ) bool {
return cli . contentTrust
2018-03-08 08:35:17 -05:00
}
2018-10-09 22:13:01 -04:00
// BuildKitEnabled returns whether buildkit is enabled either through a daemon setting
// or otherwise the client-side DOCKER_BUILDKIT environment variable
func BuildKitEnabled ( si ServerInfo ) ( bool , error ) {
buildkitEnabled := si . BuildkitVersion == types . BuilderBuildKit
if buildkitEnv := os . Getenv ( "DOCKER_BUILDKIT" ) ; buildkitEnv != "" {
var err error
buildkitEnabled , err = strconv . ParseBool ( buildkitEnv )
if err != nil {
return false , errors . Wrap ( err , "DOCKER_BUILDKIT environment variable expects boolean value" )
}
}
return buildkitEnabled , nil
}
2017-06-15 14:41:54 -04:00
// ManifestStore returns a store for local manifests
func ( cli * DockerCli ) ManifestStore ( ) manifeststore . Store {
// TODO: support override default location from config file
return manifeststore . NewStore ( filepath . Join ( config . Dir ( ) , "manifests" ) )
}
// RegistryClient returns a client for communicating with a Docker distribution
// registry
func ( cli * DockerCli ) RegistryClient ( allowInsecure bool ) registryclient . RegistryClient {
resolver := func ( ctx context . Context , index * registrytypes . IndexInfo ) types . AuthConfig {
return ResolveAuthConfig ( ctx , cli , index )
}
return registryclient . NewRegistryClient ( resolver , UserAgent ( ) , allowInsecure )
}
2019-01-31 12:50:58 -05:00
// InitializeOpt is the type of the functional options passed to DockerCli.Initialize
type InitializeOpt func ( dockerCli * DockerCli ) error
// WithInitializeClient is passed to DockerCli.Initialize by callers who wish to set a particular API Client for use by the CLI.
func WithInitializeClient ( makeClient func ( dockerCli * DockerCli ) ( client . APIClient , error ) ) InitializeOpt {
return func ( dockerCli * DockerCli ) error {
var err error
dockerCli . client , err = makeClient ( dockerCli )
return err
}
}
2016-09-08 13:11:39 -04:00
// Initialize the dockerCli runs initialization that must happen after command
// line flags are parsed.
2019-01-31 12:50:58 -05:00
func ( cli * DockerCli ) Initialize ( opts * cliflags . ClientOptions , ops ... InitializeOpt ) error {
var err error
for _ , o := range ops {
if err := o ( cli ) ; err != nil {
return err
}
}
2018-12-10 09:41:22 -05:00
cliflags . SetLogLevel ( opts . Common . LogLevel )
if opts . ConfigDir != "" {
cliconfig . SetDir ( opts . ConfigDir )
}
if opts . Common . Debug {
debug . Enable ( )
}
2019-09-20 11:13:26 -04:00
cli . loadConfigFile ( )
2019-01-31 12:50:58 -05:00
2019-05-14 11:22:39 -04:00
baseContextStore := store . New ( cliconfig . ContextStoreDir ( ) , cli . contextStoreConfig )
2019-03-06 09:01:12 -05:00
cli . contextStore = & ContextStoreWithDefault {
2019-05-14 11:22:39 -04:00
Store : baseContextStore ,
2019-03-06 09:01:12 -05:00
Resolver : func ( ) ( * DefaultContext , error ) {
2019-05-16 09:52:37 -04:00
return ResolveDefaultContext ( opts . Common , cli . ConfigFile ( ) , cli . contextStoreConfig , cli . Err ( ) )
2019-03-06 09:01:12 -05:00
} ,
}
2019-02-25 11:35:53 -05:00
cli . currentContext , err = resolveContextName ( opts . Common , cli . configFile , cli . contextStore )
if err != nil {
return err
}
2019-03-06 09:01:12 -05:00
cli . dockerEndpoint , err = resolveDockerEndpoint ( cli . contextStore , cli . currentContext )
2019-02-26 10:08:26 -05:00
if err != nil {
return errors . Wrap ( err , "unable to resolve docker endpoint" )
}
2019-02-25 11:35:53 -05:00
2019-01-31 12:50:58 -05:00
if cli . client == nil {
2019-02-26 10:08:26 -05:00
cli . client , err = newAPIClientFromEndpoint ( cli . dockerEndpoint , cli . configFile )
2019-01-31 12:50:58 -05:00
if tlsconfig . IsErrEncryptedKey ( err ) {
passRetriever := passphrase . PromptRetrieverWithInOut ( cli . In ( ) , cli . Out ( ) , nil )
newClient := func ( password string ) ( client . APIClient , error ) {
2019-02-26 10:08:26 -05:00
cli . dockerEndpoint . TLSPassword = password
return newAPIClientFromEndpoint ( cli . dockerEndpoint , cli . configFile )
2019-01-31 12:50:58 -05:00
}
cli . client , err = getClientWithPassword ( passRetriever , newClient )
}
if err != nil {
return err
2017-02-25 15:17:23 -05:00
}
2016-09-08 13:11:39 -04:00
}
2020-05-19 12:37:24 -04:00
cli . initializeFromClient ( )
2020-05-24 16:21:20 -04:00
if err := cli . loadClientInfo ( ) ; err != nil {
return err
}
2017-09-20 16:13:03 -04:00
return nil
}
2017-02-25 15:17:23 -05:00
2018-12-17 05:27:07 -05:00
// NewAPIClientFromFlags creates a new APIClient from command line flags
func NewAPIClientFromFlags ( opts * cliflags . CommonOptions , configFile * configfile . ConfigFile ) ( client . APIClient , error ) {
2019-05-16 09:52:37 -04:00
storeConfig := DefaultContextStoreConfig ( )
2019-03-06 09:01:12 -05:00
store := & ContextStoreWithDefault {
2019-05-16 09:47:07 -04:00
Store : store . New ( cliconfig . ContextStoreDir ( ) , storeConfig ) ,
2019-03-06 09:01:12 -05:00
Resolver : func ( ) ( * DefaultContext , error ) {
2019-05-16 09:52:37 -04:00
return ResolveDefaultContext ( opts , configFile , storeConfig , ioutil . Discard )
2019-03-06 09:01:12 -05:00
} ,
}
2018-11-09 09:10:41 -05:00
contextName , err := resolveContextName ( opts , configFile , store )
2018-12-17 05:27:07 -05:00
if err != nil {
return nil , err
}
2019-03-06 09:01:12 -05:00
endpoint , err := resolveDockerEndpoint ( store , contextName )
2018-12-17 05:27:07 -05:00
if err != nil {
return nil , errors . Wrap ( err , "unable to resolve docker endpoint" )
}
return newAPIClientFromEndpoint ( endpoint , configFile )
}
func newAPIClientFromEndpoint ( ep docker . Endpoint , configFile * configfile . ConfigFile ) ( client . APIClient , error ) {
clientOpts , err := ep . ClientOpts ( )
if err != nil {
return nil , err
}
2020-09-29 11:01:55 -04:00
customHeaders := make ( map [ string ] string , len ( configFile . HTTPHeaders ) )
for k , v := range configFile . HTTPHeaders {
customHeaders [ k ] = v
2018-12-17 05:27:07 -05:00
}
customHeaders [ "User-Agent" ] = UserAgent ( )
clientOpts = append ( clientOpts , client . WithHTTPHeaders ( customHeaders ) )
return client . NewClientWithOpts ( clientOpts ... )
}
2019-04-15 06:03:03 -04:00
func resolveDockerEndpoint ( s store . Reader , contextName string ) ( docker . Endpoint , error ) {
2019-04-18 09:12:30 -04:00
ctxMeta , err := s . GetMetadata ( contextName )
2019-03-06 09:01:12 -05:00
if err != nil {
return docker . Endpoint { } , err
2018-12-17 05:27:07 -05:00
}
2019-03-06 09:01:12 -05:00
epMeta , err := docker . EndpointFromContext ( ctxMeta )
if err != nil {
return docker . Endpoint { } , err
}
return docker . WithTLSData ( s , contextName , epMeta )
}
// Resolve the Docker endpoint for the default context (based on config, env vars and CLI flags)
func resolveDefaultDockerEndpoint ( opts * cliflags . CommonOptions ) ( docker . Endpoint , error ) {
2018-12-17 05:27:07 -05:00
host , err := getServerHost ( opts . Hosts , opts . TLSOptions )
if err != nil {
return docker . Endpoint { } , err
}
var (
skipTLSVerify bool
tlsData * dcontext . TLSData
)
if opts . TLSOptions != nil {
skipTLSVerify = opts . TLSOptions . InsecureSkipVerify
tlsData , err = dcontext . TLSDataFromFiles ( opts . TLSOptions . CAFile , opts . TLSOptions . CertFile , opts . TLSOptions . KeyFile )
if err != nil {
return docker . Endpoint { } , err
}
}
return docker . Endpoint {
EndpointMeta : docker . EndpointMeta {
2018-11-09 09:10:41 -05:00
Host : host ,
SkipTLSVerify : skipTLSVerify ,
2018-12-17 05:27:07 -05:00
} ,
TLSData : tlsData ,
} , nil
}
2017-12-20 09:04:41 -05:00
func isEnabled ( value string ) ( bool , error ) {
switch value {
case "enabled" :
return true , nil
case "" , "disabled" :
return false , nil
default :
return false , errors . Errorf ( "%q is not valid, should be either enabled or disabled" , value )
}
}
2016-11-02 20:43:32 -04:00
2017-12-20 09:04:41 -05:00
func ( cli * DockerCli ) initializeFromClient ( ) {
2020-04-07 18:57:41 -04:00
ctx := context . Background ( )
if strings . HasPrefix ( cli . DockerEndpoint ( ) . Host , "tcp://" ) {
// @FIXME context.WithTimeout doesn't work with connhelper / ssh connections
// time="2020-04-10T10:16:26Z" level=warning msg="commandConn.CloseWrite: commandconn: failed to wait: signal: killed"
var cancel func ( )
ctx , cancel = context . WithTimeout ( ctx , 2 * time . Second )
defer cancel ( )
}
ping , err := cli . client . Ping ( ctx )
2017-09-20 16:13:03 -04:00
if err != nil {
2017-06-14 16:25:24 -04:00
// Default to true if we fail to connect to daemon
2017-12-20 09:04:41 -05:00
cli . serverInfo = ServerInfo { HasExperimental : true }
2017-09-20 16:13:03 -04:00
if ping . APIVersion != "" {
cli . client . NegotiateAPIVersionPing ( ping )
}
return
2016-11-02 20:43:32 -04:00
}
2017-03-14 17:53:29 -04:00
2017-12-20 09:04:41 -05:00
cli . serverInfo = ServerInfo {
2017-09-20 16:13:03 -04:00
HasExperimental : ping . Experimental ,
OSType : ping . OSType ,
2018-08-06 17:17:03 -04:00
BuildkitVersion : ping . BuilderVersion ,
2017-09-20 16:13:03 -04:00
}
cli . client . NegotiateAPIVersionPing ( ping )
}
func getClientWithPassword ( passRetriever notary . PassRetriever , newClient func ( password string ) ( client . APIClient , error ) ) ( client . APIClient , error ) {
for attempts := 0 ; ; attempts ++ {
passwd , giveup , err := passRetriever ( "private" , "encrypted TLS private" , false , attempts )
if giveup || err != nil {
return nil , errors . Wrap ( err , "private key is encrypted, but could not get passphrase" )
}
apiclient , err := newClient ( passwd )
if ! tlsconfig . IsErrEncryptedKey ( err ) {
return apiclient , err
}
}
2016-09-08 13:11:39 -04:00
}
2017-09-12 12:39:13 -04:00
// NotaryClient provides a Notary Repository to interact with signed metadata for an image
func ( cli * DockerCli ) NotaryClient ( imgRefAndAuth trust . ImageRefAndAuth , actions [ ] string ) ( notaryclient . Repository , error ) {
return trust . GetNotaryRepository ( cli . In ( ) , cli . Out ( ) , UserAgent ( ) , imgRefAndAuth . RepoInfo ( ) , imgRefAndAuth . AuthConfig ( ) , actions ... )
}
2018-12-17 05:27:07 -05:00
// ContextStore returns the ContextStore
func ( cli * DockerCli ) ContextStore ( ) store . Store {
return cli . contextStore
}
// CurrentContext returns the current context name
func ( cli * DockerCli ) CurrentContext ( ) string {
return cli . currentContext
}
// StackOrchestrator resolves which stack orchestrator is in use
func ( cli * DockerCli ) StackOrchestrator ( flagValue string ) ( Orchestrator , error ) {
currentContext := cli . CurrentContext ( )
2019-04-18 09:12:30 -04:00
ctxRaw , err := cli . ContextStore ( ) . GetMetadata ( currentContext )
2019-03-06 09:01:12 -05:00
if store . IsErrContextDoesNotExist ( err ) {
// case where the currentContext has been removed (CLI behavior is to fallback to using DOCKER_HOST based resolution)
return GetStackOrchestrator ( flagValue , "" , cli . ConfigFile ( ) . StackOrchestrator , cli . Err ( ) )
2018-12-17 05:27:07 -05:00
}
2019-03-06 09:01:12 -05:00
if err != nil {
return "" , err
2018-12-17 05:27:07 -05:00
}
2019-03-06 09:01:12 -05:00
ctxMeta , err := GetDockerContext ( ctxRaw )
if err != nil {
return "" , err
}
ctxOrchestrator := string ( ctxMeta . StackOrchestrator )
return GetStackOrchestrator ( flagValue , ctxOrchestrator , cli . ConfigFile ( ) . StackOrchestrator , cli . Err ( ) )
2018-12-17 05:27:07 -05:00
}
2018-11-09 09:10:41 -05:00
// DockerEndpoint returns the current docker endpoint
func ( cli * DockerCli ) DockerEndpoint ( ) docker . Endpoint {
return cli . dockerEndpoint
}
2019-01-28 08:52:58 -05:00
// Apply all the operation on the cli
func ( cli * DockerCli ) Apply ( ops ... DockerCliOption ) error {
for _ , op := range ops {
if err := op ( cli ) ; err != nil {
return err
}
}
return nil
}
2017-03-14 17:53:29 -04:00
// ServerInfo stores details about the supported features and platform of the
// server
type ServerInfo struct {
HasExperimental bool
OSType string
2018-08-06 17:17:03 -04:00
BuildkitVersion types . BuilderVersion
2017-03-14 17:53:29 -04:00
}
2017-12-20 09:04:41 -05:00
// ClientInfo stores details about the supported features of the client
type ClientInfo struct {
HasExperimental bool
DefaultVersion string
2018-01-02 17:56:07 -05:00
}
2019-01-28 08:52:58 -05:00
// NewDockerCli returns a DockerCli instance with all operators applied on it.
2019-11-28 10:54:51 -05:00
// It applies by default the standard streams, and the content trust from
// environment.
2019-01-28 08:52:58 -05:00
func NewDockerCli ( ops ... DockerCliOption ) ( * DockerCli , error ) {
cli := & DockerCli { }
defaultOps := [ ] DockerCliOption {
WithContentTrustFromEnv ( ) ,
}
2019-05-16 09:52:37 -04:00
cli . contextStoreConfig = DefaultContextStoreConfig ( )
2019-01-28 08:52:58 -05:00
ops = append ( defaultOps , ops ... )
if err := cli . Apply ( ops ... ) ; err != nil {
return nil , err
}
if cli . out == nil || cli . in == nil || cli . err == nil {
stdin , stdout , stderr := term . StdStreams ( )
if cli . in == nil {
cli . in = streams . NewIn ( stdin )
}
if cli . out == nil {
cli . out = streams . NewOut ( stdout )
}
if cli . err == nil {
cli . err = stderr
}
}
return cli , nil
2016-09-08 13:11:39 -04:00
}
2018-10-11 22:30:49 -04:00
func getServerHost ( hosts [ ] string , tlsOptions * tlsconfig . Options ) ( string , error ) {
2017-10-12 11:44:03 -04:00
var host string
2016-09-08 13:11:39 -04:00
switch len ( hosts ) {
case 0 :
host = os . Getenv ( "DOCKER_HOST" )
case 1 :
host = hosts [ 0 ]
default :
return "" , errors . New ( "Please specify only one -H" )
}
2018-10-11 22:30:49 -04:00
return dopts . ParseHost ( tlsOptions != nil , host )
2016-09-08 13:11:39 -04:00
}
2016-08-29 14:45:29 -04:00
// UserAgent returns the user agent string used for making API requests
func UserAgent ( ) string {
2019-01-08 10:03:51 -05:00
return "Docker-Client/" + version . Version + " (" + runtime . GOOS + ")"
2016-09-08 13:11:39 -04:00
}
2018-12-17 05:27:07 -05:00
// resolveContextName resolves the current context name with the following rules:
// - setting both --context and --host flags is ambiguous
// - if --context is set, use this value
// - if --host flag or DOCKER_HOST is set, fallbacks to use the same logic as before context-store was added
// for backward compatibility with existing scripts
// - if DOCKER_CONTEXT is set, use this value
// - if Config file has a globally set "CurrentContext", use this value
// - fallbacks to default HOST, uses TLS config from flags/env vars
2019-04-15 06:03:03 -04:00
func resolveContextName ( opts * cliflags . CommonOptions , config * configfile . ConfigFile , contextstore store . Reader ) ( string , error ) {
2018-12-17 05:27:07 -05:00
if opts . Context != "" && len ( opts . Hosts ) > 0 {
2018-11-09 09:10:41 -05:00
return "" , errors . New ( "Conflicting options: either specify --host or --context, not both" )
2018-12-17 05:27:07 -05:00
}
if opts . Context != "" {
return opts . Context , nil
}
if len ( opts . Hosts ) > 0 {
2019-03-06 09:01:12 -05:00
return DefaultContextName , nil
2018-12-17 05:27:07 -05:00
}
if _ , present := os . LookupEnv ( "DOCKER_HOST" ) ; present {
2019-03-06 09:01:12 -05:00
return DefaultContextName , nil
2018-12-17 05:27:07 -05:00
}
if ctxName , ok := os . LookupEnv ( "DOCKER_CONTEXT" ) ; ok {
return ctxName , nil
}
if config != nil && config . CurrentContext != "" {
2019-04-18 09:12:30 -04:00
_ , err := contextstore . GetMetadata ( config . CurrentContext )
2018-11-09 09:10:41 -05:00
if store . IsErrContextDoesNotExist ( err ) {
return "" , errors . Errorf ( "Current context %q is not found on the file system, please check your config file at %s" , config . CurrentContext , config . Filename )
}
return config . CurrentContext , err
2018-12-17 05:27:07 -05:00
}
2019-03-06 09:01:12 -05:00
return DefaultContextName , nil
2018-12-17 05:27:07 -05:00
}
2019-01-21 03:37:20 -05:00
2019-05-16 09:10:57 -04:00
var defaultStoreEndpoints = [ ] store . NamedTypeGetter {
store . EndpointTypeGetter ( docker . DockerEndpoint , func ( ) interface { } { return & docker . EndpointMeta { } } ) ,
}
// RegisterDefaultStoreEndpoints registers a new named endpoint
// metadata type with the default context store config, so that
// endpoint will be supported by stores using the config returned by
// DefaultContextStoreConfig.
func RegisterDefaultStoreEndpoints ( ep ... store . NamedTypeGetter ) {
defaultStoreEndpoints = append ( defaultStoreEndpoints , ep ... )
}
2019-05-16 09:52:37 -04:00
// DefaultContextStoreConfig returns a new store.Config with the default set of endpoints configured.
func DefaultContextStoreConfig ( ) store . Config {
2019-01-21 03:37:20 -05:00
return store . NewConfig (
func ( ) interface { } { return & DockerContext { } } ,
2019-05-16 09:10:57 -04:00
defaultStoreEndpoints ... ,
2019-01-21 03:37:20 -05:00
)
}