2016-09-08 13:11:39 -04:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
2018-05-03 21:02:44 -04:00
|
|
|
"context"
|
2022-11-04 10:47:14 -04:00
|
|
|
"fmt"
|
2016-09-08 13:11:39 -04:00
|
|
|
"io"
|
|
|
|
"os"
|
2017-06-15 14:41:54 -04:00
|
|
|
"path/filepath"
|
2016-09-08 13:11:39 -04:00
|
|
|
"runtime"
|
2022-02-24 11:57:47 -05:00
|
|
|
"strconv"
|
2020-04-07 18:57:41 -04:00
|
|
|
"strings"
|
2022-11-04 10:47:14 -04:00
|
|
|
"sync"
|
2020-04-07 18:57:41 -04:00
|
|
|
"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
|
|
|
"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"
|
2022-03-29 04:41:43 -04:00
|
|
|
"github.com/docker/docker/api/types/registry"
|
|
|
|
"github.com/docker/docker/api/types/swarm"
|
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"
|
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
|
|
|
notaryclient "github.com/theupdateframework/notary/client"
|
2016-09-08 13:11:39 -04:00
|
|
|
)
|
|
|
|
|
2022-06-06 12:23:53 -04:00
|
|
|
const defaultInitTimeout = 2 * time.Second
|
|
|
|
|
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-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
|
2022-11-04 08:46:36 -04:00
|
|
|
CurrentVersion() 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
|
2022-02-24 11:57:47 -05:00
|
|
|
BuildKitEnabled() (bool, error)
|
2018-12-17 05:27:07 -05:00
|
|
|
ContextStore() store.Store
|
|
|
|
CurrentContext() string
|
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
|
2022-11-04 08:40:46 -04:00
|
|
|
options *cliflags.ClientOptions
|
2019-11-28 10:54:51 -05:00
|
|
|
in *streams.In
|
|
|
|
out *streams.Out
|
|
|
|
err io.Writer
|
|
|
|
client client.APIClient
|
|
|
|
serverInfo ServerInfo
|
|
|
|
contentTrust bool
|
|
|
|
contextStore store.Store
|
|
|
|
currentContext string
|
2022-11-04 10:47:14 -04:00
|
|
|
init sync.Once
|
|
|
|
initErr error
|
2019-11-28 10:54:51 -05:00
|
|
|
dockerEndpoint docker.Endpoint
|
|
|
|
contextStoreConfig store.Config
|
2022-06-06 12:23:53 -04:00
|
|
|
initTimeout time.Duration
|
2016-10-06 10:09:54 -04:00
|
|
|
}
|
|
|
|
|
2021-06-15 07:03:01 -04:00
|
|
|
// DefaultVersion returns api.defaultVersion.
|
2016-11-02 20:43:32 -04:00
|
|
|
func (cli *DockerCli) DefaultVersion() string {
|
2021-06-15 07:03:01 -04:00
|
|
|
return api.DefaultVersion
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
|
|
|
|
2022-11-04 08:46:36 -04:00
|
|
|
// CurrentVersion returns the API version currently negotiated, or the default
|
|
|
|
// version otherwise.
|
|
|
|
func (cli *DockerCli) CurrentVersion() string {
|
2022-11-04 10:47:14 -04:00
|
|
|
_ = cli.initialize()
|
2022-11-04 08:46:36 -04:00
|
|
|
if cli.client == nil {
|
|
|
|
return api.DefaultVersion
|
|
|
|
}
|
|
|
|
return cli.client.ClientVersion()
|
|
|
|
}
|
|
|
|
|
2016-09-08 13:11:39 -04:00
|
|
|
// Client returns the APIClient
|
|
|
|
func (cli *DockerCli) Client() client.APIClient {
|
2022-11-04 10:47:14 -04:00
|
|
|
if err := cli.initialize(); err != nil {
|
|
|
|
_, _ = fmt.Fprintf(cli.Err(), "Failed to initialize: %s\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2016-09-08 13:11:39 -04:00
|
|
|
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 {
|
2022-11-28 07:24:07 -05:00
|
|
|
// TODO(thaJeztah): when would this happen? Is this only in tests (where cli.Initialize() is not called first?)
|
2019-09-20 11:13:26 -04:00
|
|
|
if cli.configFile == nil {
|
2022-11-28 07:24:07 -05:00
|
|
|
cli.configFile = config.LoadDefaultConfigFile(cli.err)
|
2019-09-20 11:13:26 -04:00
|
|
|
}
|
2016-09-08 13:11:39 -04:00
|
|
|
return cli.configFile
|
|
|
|
}
|
|
|
|
|
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 {
|
2022-11-28 10:16:45 -05:00
|
|
|
_ = cli.initialize()
|
2017-12-20 09:04:41 -05:00
|
|
|
return cli.serverInfo
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-02-24 11:57:47 -05:00
|
|
|
// BuildKitEnabled returns buildkit is enabled or not.
|
|
|
|
func (cli *DockerCli) BuildKitEnabled() (bool, error) {
|
2023-04-19 08:06:01 -04:00
|
|
|
// use DOCKER_BUILDKIT env var value if set and not empty
|
|
|
|
if v := os.Getenv("DOCKER_BUILDKIT"); v != "" {
|
2022-02-24 11:57:47 -05:00
|
|
|
enabled, err := strconv.ParseBool(v)
|
|
|
|
if err != nil {
|
|
|
|
return false, errors.Wrap(err, "DOCKER_BUILDKIT environment variable expects boolean value")
|
|
|
|
}
|
|
|
|
return enabled, nil
|
|
|
|
}
|
|
|
|
// if a builder alias is defined, we are using BuildKit
|
|
|
|
aliasMap := cli.ConfigFile().Aliases
|
|
|
|
if _, ok := aliasMap["builder"]; ok {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
// otherwise, assume BuildKit is enabled but
|
|
|
|
// not if wcow reported from server side
|
|
|
|
return cli.ServerInfo().OSType != "windows", 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 {
|
2022-03-29 04:41:43 -04:00
|
|
|
resolver := func(ctx context.Context, index *registry.IndexInfo) types.AuthConfig {
|
2017-06-15 14:41:54 -04:00
|
|
|
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 {
|
|
|
|
for _, o := range ops {
|
|
|
|
if err := o(cli); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2022-11-04 06:58:11 -04:00
|
|
|
cliflags.SetLogLevel(opts.LogLevel)
|
2018-12-10 09:41:22 -05:00
|
|
|
|
|
|
|
if opts.ConfigDir != "" {
|
2022-03-04 07:24:28 -05:00
|
|
|
config.SetDir(opts.ConfigDir)
|
2018-12-10 09:41:22 -05:00
|
|
|
}
|
|
|
|
|
2022-11-04 06:58:11 -04:00
|
|
|
if opts.Debug {
|
2018-12-10 09:41:22 -05:00
|
|
|
debug.Enable()
|
|
|
|
}
|
2022-11-04 05:33:22 -04:00
|
|
|
if opts.Context != "" && len(opts.Hosts) > 0 {
|
|
|
|
return errors.New("conflicting options: either specify --host or --context, not both")
|
|
|
|
}
|
2018-12-10 09:41:22 -05:00
|
|
|
|
2022-11-04 08:40:46 -04:00
|
|
|
cli.options = opts
|
2022-11-28 07:24:07 -05:00
|
|
|
cli.configFile = config.LoadDefaultConfigFile(cli.err)
|
2022-11-04 08:40:46 -04:00
|
|
|
cli.currentContext = resolveContextName(cli.options, cli.configFile)
|
2019-03-06 09:01:12 -05:00
|
|
|
cli.contextStore = &ContextStoreWithDefault{
|
2022-11-04 08:40:46 -04:00
|
|
|
Store: store.New(config.ContextStoreDir(), cli.contextStoreConfig),
|
2019-03-06 09:01:12 -05:00
|
|
|
Resolver: func() (*DefaultContext, error) {
|
2022-11-04 08:40:46 -04:00
|
|
|
return ResolveDefaultContext(cli.options, cli.contextStoreConfig)
|
2019-03-06 09:01:12 -05:00
|
|
|
},
|
|
|
|
}
|
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
|
2022-11-04 06:58:11 -04:00
|
|
|
func NewAPIClientFromFlags(opts *cliflags.ClientOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
|
2022-11-04 05:33:22 -04:00
|
|
|
if opts.Context != "" && len(opts.Hosts) > 0 {
|
|
|
|
return nil, errors.New("conflicting options: either specify --host or --context, not both")
|
|
|
|
}
|
|
|
|
|
2019-05-16 09:52:37 -04:00
|
|
|
storeConfig := DefaultContextStoreConfig()
|
2021-03-03 06:07:01 -05:00
|
|
|
contextStore := &ContextStoreWithDefault{
|
2022-03-04 07:24:28 -05:00
|
|
|
Store: store.New(config.ContextStoreDir(), storeConfig),
|
2019-03-06 09:01:12 -05:00
|
|
|
Resolver: func() (*DefaultContext, error) {
|
2022-03-03 12:47:36 -05:00
|
|
|
return ResolveDefaultContext(opts, storeConfig)
|
2019-03-06 09:01:12 -05:00
|
|
|
},
|
|
|
|
}
|
2022-11-04 05:33:22 -04:00
|
|
|
endpoint, err := resolveDockerEndpoint(contextStore, resolveContextName(opts, configFile))
|
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) {
|
2022-11-04 10:47:14 -04:00
|
|
|
if s == nil {
|
|
|
|
return docker.Endpoint{}, fmt.Errorf("no context store initialized")
|
|
|
|
}
|
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)
|
2022-11-04 06:58:11 -04:00
|
|
|
func resolveDefaultDockerEndpoint(opts *cliflags.ClientOptions) (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
|
|
|
|
}
|
|
|
|
|
2022-06-06 12:23:53 -04:00
|
|
|
func (cli *DockerCli) getInitTimeout() time.Duration {
|
|
|
|
if cli.initTimeout != 0 {
|
|
|
|
return cli.initTimeout
|
|
|
|
}
|
|
|
|
return defaultInitTimeout
|
|
|
|
}
|
|
|
|
|
2017-12-20 09:04:41 -05:00
|
|
|
func (cli *DockerCli) initializeFromClient() {
|
2020-04-07 18:57:41 -04:00
|
|
|
ctx := context.Background()
|
2022-11-04 10:47:14 -04:00
|
|
|
if !strings.HasPrefix(cli.dockerEndpoint.Host, "ssh://") {
|
2020-04-07 18:57:41 -04:00
|
|
|
// @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()
|
2022-06-06 12:23:53 -04:00
|
|
|
ctx, cancel = context.WithTimeout(ctx, cli.getInitTimeout())
|
2020-04-07 18:57:41 -04:00
|
|
|
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,
|
2022-03-29 04:41:43 -04:00
|
|
|
SwarmStatus: ping.SwarmStatus,
|
2017-09-20 16:13:03 -04:00
|
|
|
}
|
|
|
|
cli.client.NegotiateAPIVersionPing(ping)
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-11-17 18:22:25 -05:00
|
|
|
// CurrentContext returns the current context name, based on flags,
|
|
|
|
// environment variables and the cli configuration file, in the following
|
|
|
|
// order of preference:
|
|
|
|
//
|
|
|
|
// 1. The "--context" command-line option.
|
|
|
|
// 2. The "DOCKER_CONTEXT" environment variable.
|
|
|
|
// 3. The current context as configured through the in "currentContext"
|
|
|
|
// field in the CLI configuration file ("~/.docker/config.json").
|
|
|
|
// 4. If no context is configured, use the "default" context.
|
|
|
|
//
|
|
|
|
// # Fallbacks for backward-compatibility
|
|
|
|
//
|
|
|
|
// To preserve backward-compatibility with the "pre-contexts" behavior,
|
|
|
|
// the "default" context is used if:
|
|
|
|
//
|
|
|
|
// - The "--host" option is set
|
|
|
|
// - The "DOCKER_HOST" ([DefaultContextName]) environment variable is set
|
|
|
|
// to a non-empty value.
|
|
|
|
//
|
|
|
|
// In these cases, the default context is used, which uses the host as
|
|
|
|
// specified in "DOCKER_HOST", and TLS config from flags/env vars.
|
|
|
|
//
|
|
|
|
// Setting both the "--context" and "--host" flags is ambiguous and results
|
|
|
|
// in an error when the cli is started.
|
|
|
|
//
|
|
|
|
// CurrentContext does not validate if the given context exists or if it's
|
|
|
|
// valid; errors may occur when trying to use it.
|
2018-12-17 05:27:07 -05:00
|
|
|
func (cli *DockerCli) CurrentContext() string {
|
|
|
|
return cli.currentContext
|
|
|
|
}
|
|
|
|
|
2022-11-17 18:22:25 -05:00
|
|
|
// CurrentContext returns the current context name, based on flags,
|
|
|
|
// environment variables and the cli configuration file. It does not
|
|
|
|
// validate if the given context exists or if it's valid; errors may
|
|
|
|
// occur when trying to use it.
|
|
|
|
//
|
|
|
|
// Refer to [DockerCli.CurrentContext] above for further details.
|
|
|
|
func resolveContextName(opts *cliflags.ClientOptions, config *configfile.ConfigFile) string {
|
|
|
|
if opts != nil && opts.Context != "" {
|
|
|
|
return opts.Context
|
|
|
|
}
|
|
|
|
if opts != nil && len(opts.Hosts) > 0 {
|
|
|
|
return DefaultContextName
|
|
|
|
}
|
|
|
|
if os.Getenv(client.EnvOverrideHost) != "" {
|
|
|
|
return DefaultContextName
|
|
|
|
}
|
|
|
|
if ctxName := os.Getenv("DOCKER_CONTEXT"); ctxName != "" {
|
|
|
|
return ctxName
|
|
|
|
}
|
|
|
|
if config != nil && config.CurrentContext != "" {
|
|
|
|
// We don't validate if this context exists: errors may occur when trying to use it.
|
|
|
|
return config.CurrentContext
|
|
|
|
}
|
|
|
|
return DefaultContextName
|
|
|
|
}
|
|
|
|
|
2018-11-09 09:10:41 -05:00
|
|
|
// DockerEndpoint returns the current docker endpoint
|
|
|
|
func (cli *DockerCli) DockerEndpoint() docker.Endpoint {
|
2022-11-04 10:47:14 -04:00
|
|
|
if err := cli.initialize(); err != nil {
|
|
|
|
// Note that we're not terminating here, as this function may be used
|
|
|
|
// in cases where we're able to continue.
|
|
|
|
_, _ = fmt.Fprintf(cli.Err(), "%v\n", cli.initErr)
|
|
|
|
}
|
2018-11-09 09:10:41 -05:00
|
|
|
return cli.dockerEndpoint
|
|
|
|
}
|
|
|
|
|
2022-11-04 10:47:14 -04:00
|
|
|
func (cli *DockerCli) getDockerEndPoint() (ep docker.Endpoint, err error) {
|
|
|
|
cn := cli.CurrentContext()
|
|
|
|
if cn == DefaultContextName {
|
|
|
|
return resolveDefaultDockerEndpoint(cli.options)
|
|
|
|
}
|
|
|
|
return resolveDockerEndpoint(cli.contextStore, cn)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cli *DockerCli) initialize() error {
|
|
|
|
cli.init.Do(func() {
|
|
|
|
cli.dockerEndpoint, cli.initErr = cli.getDockerEndPoint()
|
|
|
|
if cli.initErr != nil {
|
|
|
|
cli.initErr = errors.Wrap(cli.initErr, "unable to resolve docker endpoint")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if cli.client == nil {
|
|
|
|
if cli.client, cli.initErr = newAPIClientFromEndpoint(cli.dockerEndpoint, cli.configFile); cli.initErr != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cli.initializeFromClient()
|
|
|
|
})
|
|
|
|
return cli.initErr
|
|
|
|
}
|
|
|
|
|
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
|
2022-03-29 04:41:43 -04:00
|
|
|
|
|
|
|
// SwarmStatus provides information about the current swarm status of the
|
|
|
|
// engine, obtained from the "Swarm" header in the API response.
|
|
|
|
//
|
|
|
|
// It can be a nil struct if the API version does not provide this header
|
|
|
|
// in the ping response, or if an error occurred, in which case the client
|
|
|
|
// should use other ways to get the current swarm status, such as the /swarm
|
|
|
|
// endpoint.
|
|
|
|
SwarmStatus *swarm.Status
|
2017-03-14 17:53:29 -04: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) {
|
|
|
|
defaultOps := []DockerCliOption{
|
|
|
|
WithContentTrustFromEnv(),
|
2021-03-03 06:09:20 -05:00
|
|
|
WithDefaultContextStoreConfig(),
|
cli/command: NewDockerCli(): use WithStandardStreams()
`NewDockerCli` was configuring the standard streams using local code; this patch
instead uses the available `WithStandardStreams()` option to do the same.
There is slight difference in the order of events;
Previously, user-provided options would be applied first, after which NewDockerCli
would check if any of "in", "out", or "err" were nil, and if so set them to the
default stream (or writer) for that output.
The new code unconditionally sets the defaults _before_ applying user-provided
options. In practive, howver, this makes no difference; the fields set are not
exported, and the only functions updating them are `WithStandardStreams`,
`WithInputStream`, and `WithCombinedStream`, neither of which checks the old
value (so always overrides).
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-08-31 11:10:53 -04:00
|
|
|
WithStandardStreams(),
|
2019-01-28 08:52:58 -05:00
|
|
|
}
|
|
|
|
ops = append(defaultOps, ops...)
|
2021-03-03 06:09:20 -05:00
|
|
|
|
|
|
|
cli := &DockerCli{}
|
2019-01-28 08:52:58 -05:00
|
|
|
if err := cli.Apply(ops...); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
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:
|
2022-03-24 19:15:14 -04:00
|
|
|
host = os.Getenv(client.EnvOverrideHost)
|
2016-09-08 13:11:39 -04:00
|
|
|
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
|
|
|
|
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
|
|
|
)
|
|
|
|
}
|