add //go:build directives to prevent downgrading to go1.16 language
This is a follow-up to 0e73168b7e6d1d029d76d05b843b1aaec46739a8
This repository is not yet a module (i.e., does not have a `go.mod`). This
is not problematic when building the code in GOPATH or "vendor" mode, but
when using the code as a module-dependency (in module-mode), different semantics
are applied since Go1.21, which switches Go _language versions_ on a per-module,
per-package, or even per-file base.
A condensed summary of that logic [is as follows][1]:
- For modules that have a go.mod containing a go version directive; that
version is considered a minimum _required_ version (starting with the
go1.19.13 and go1.20.8 patch releases: before those, it was only a
recommendation).
- For dependencies that don't have a go.mod (not a module), go language
version go1.16 is assumed.
- Likewise, for modules that have a go.mod, but the file does not have a
go version directive, go language version go1.16 is assumed.
- If a go.work file is present, but does not have a go version directive,
language version go1.17 is assumed.
When switching language versions, Go _downgrades_ the language version,
which means that language features (such as generics, and `any`) are not
available, and compilation fails. For example:
# github.com/docker/cli/cli/context/store
/go/pkg/mod/github.com/docker/cli@v25.0.0-beta.2+incompatible/cli/context/store/storeconfig.go:6:24: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
/go/pkg/mod/github.com/docker/cli@v25.0.0-beta.2+incompatible/cli/context/store/store.go:74:12: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
Note that these fallbacks are per-module, per-package, and can even be
per-file, so _(indirect) dependencies_ can still use modern language
features, as long as their respective go.mod has a version specified.
Unfortunately, these failures do not occur when building locally (using
vendor / GOPATH mode), but will affect consumers of the module.
Obviously, this situation is not ideal, and the ultimate solution is to
move to go modules (add a go.mod), but this comes with a non-insignificant
risk in other areas (due to our complex dependency tree).
We can revert to using go1.16 language features only, but this may be
limiting, and may still be problematic when (e.g.) matching signatures
of dependencies.
There is an escape hatch: adding a `//go:build` directive to files that
make use of go language features. From the [go toolchain docs][2]:
> The go line for each module sets the language version the compiler enforces
> when compiling packages in that module. The language version can be changed
> on a per-file basis by using a build constraint.
>
> For example, a module containing code that uses the Go 1.21 language version
> should have a `go.mod` file with a go line such as `go 1.21` or `go 1.21.3`.
> If a specific source file should be compiled only when using a newer Go
> toolchain, adding `//go:build go1.22` to that source file both ensures that
> only Go 1.22 and newer toolchains will compile the file and also changes
> the language version in that file to Go 1.22.
This patch adds `//go:build` directives to those files using recent additions
to the language. It's currently using go1.19 as version to match the version
in our "vendor.mod", but we can consider being more permissive ("any" requires
go1.18 or up), or more "optimistic" (force go1.21, which is the version we
currently use to build).
For completeness sake, note that any file _without_ a `//go:build` directive
will continue to use go1.16 language version when used as a module.
[1]: https://github.com/golang/go/blob/58c28ba286dd0e98fe4cca80f5d64bbcb824a685/src/cmd/go/internal/gover/version.go#L9-L56
[2]; https://go.dev/doc/toolchain#:~:text=The%20go%20line%20for,file%20to%20Go%201.22
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-12-14 07:51:57 -05:00
|
|
|
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
2024-06-18 06:07:25 -04:00
|
|
|
//go:build go1.21
|
add //go:build directives to prevent downgrading to go1.16 language
This is a follow-up to 0e73168b7e6d1d029d76d05b843b1aaec46739a8
This repository is not yet a module (i.e., does not have a `go.mod`). This
is not problematic when building the code in GOPATH or "vendor" mode, but
when using the code as a module-dependency (in module-mode), different semantics
are applied since Go1.21, which switches Go _language versions_ on a per-module,
per-package, or even per-file base.
A condensed summary of that logic [is as follows][1]:
- For modules that have a go.mod containing a go version directive; that
version is considered a minimum _required_ version (starting with the
go1.19.13 and go1.20.8 patch releases: before those, it was only a
recommendation).
- For dependencies that don't have a go.mod (not a module), go language
version go1.16 is assumed.
- Likewise, for modules that have a go.mod, but the file does not have a
go version directive, go language version go1.16 is assumed.
- If a go.work file is present, but does not have a go version directive,
language version go1.17 is assumed.
When switching language versions, Go _downgrades_ the language version,
which means that language features (such as generics, and `any`) are not
available, and compilation fails. For example:
# github.com/docker/cli/cli/context/store
/go/pkg/mod/github.com/docker/cli@v25.0.0-beta.2+incompatible/cli/context/store/storeconfig.go:6:24: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
/go/pkg/mod/github.com/docker/cli@v25.0.0-beta.2+incompatible/cli/context/store/store.go:74:12: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
Note that these fallbacks are per-module, per-package, and can even be
per-file, so _(indirect) dependencies_ can still use modern language
features, as long as their respective go.mod has a version specified.
Unfortunately, these failures do not occur when building locally (using
vendor / GOPATH mode), but will affect consumers of the module.
Obviously, this situation is not ideal, and the ultimate solution is to
move to go modules (add a go.mod), but this comes with a non-insignificant
risk in other areas (due to our complex dependency tree).
We can revert to using go1.16 language features only, but this may be
limiting, and may still be problematic when (e.g.) matching signatures
of dependencies.
There is an escape hatch: adding a `//go:build` directive to files that
make use of go language features. From the [go toolchain docs][2]:
> The go line for each module sets the language version the compiler enforces
> when compiling packages in that module. The language version can be changed
> on a per-file basis by using a build constraint.
>
> For example, a module containing code that uses the Go 1.21 language version
> should have a `go.mod` file with a go line such as `go 1.21` or `go 1.21.3`.
> If a specific source file should be compiled only when using a newer Go
> toolchain, adding `//go:build go1.22` to that source file both ensures that
> only Go 1.22 and newer toolchains will compile the file and also changes
> the language version in that file to Go 1.22.
This patch adds `//go:build` directives to those files using recent additions
to the language. It's currently using go1.19 as version to match the version
in our "vendor.mod", but we can consider being more permissive ("any" requires
go1.18 or up), or more "optimistic" (force go1.21, which is the version we
currently use to build).
For completeness sake, note that any file _without_ a `//go:build` directive
will continue to use go1.16 language version when used as a module.
[1]: https://github.com/golang/go/blob/58c28ba286dd0e98fe4cca80f5d64bbcb824a685/src/cmd/go/internal/gover/version.go#L9-L56
[2]; https://go.dev/doc/toolchain#:~:text=The%20go%20line%20for,file%20to%20Go%201.22
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-12-14 07:51:57 -05:00
|
|
|
|
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"
|
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
|
2024-06-11 10:49:25 -04:00
|
|
|
Err() *streams.Out
|
2016-08-29 14:45:29 -04:00
|
|
|
}
|
|
|
|
|
2016-12-25 16:23:35 -05:00
|
|
|
// Cli represents the docker command line client.
|
|
|
|
type Cli interface {
|
|
|
|
Client() client.APIClient
|
2022-11-04 13:03:42 -04:00
|
|
|
Streams
|
2019-01-28 08:52:58 -05:00
|
|
|
SetIn(in *streams.In)
|
2023-06-20 18:36:25 -04:00
|
|
|
Apply(ops ...CLIOption) 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
|
2024-02-20 10:42:33 -05:00
|
|
|
TelemetryClient
|
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
|
2024-06-11 10:49:25 -04:00
|
|
|
err *streams.Out
|
2019-11-28 10:54:51 -05:00
|
|
|
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
|
2024-02-20 10:42:33 -05:00
|
|
|
res telemetryResource
|
2023-09-09 18:27:44 -04:00
|
|
|
|
|
|
|
// baseCtx is the base context used for internal operations. In the future
|
|
|
|
// this may be replaced by explicitly passing a context to functions that
|
|
|
|
// need it.
|
|
|
|
baseCtx context.Context
|
2024-06-24 08:49:29 -04:00
|
|
|
|
|
|
|
enableGlobalMeter, enableGlobalTracer bool
|
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
|
2024-06-11 10:49:25 -04:00
|
|
|
func (cli *DockerCli) Err() *streams.Out {
|
2016-09-08 13:11:39 -04:00
|
|
|
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
|
|
|
|
}
|
build: allow BuildKit to be used on Windows daemons that advertise it
Commit 6fef143dbcf5fdcb436d05cf8e67d56c24bc4e6e switched the CLI to use
BuildKit by default, but as part of that removed the use of the
BuildkitVersion field as returned by Ping.
Some follow-up changes in commits e38e6c51ffb55405321038dfcf3a477f and
e7a8748b939fbf7798352cff1e0b87ef updated the logic for detecting whether
BuildKit should be used or the legacy builder, but hard-coded using the
legacy builder for Windows daemons.
While Windows / WCOW does not yet support BuildKit by default, there is
work in progress to implement it, so we should not hard-code the assumption
that a Windows daemon cannot support BuildKit.
On the daemon-side, [moby@7b153b9] (Docker v23.0) changed the default as
advertised by the daemon to be BuildKit for Linux daemons. That change
still hardcoded BuildKit to be unsupported for Windows daemons (and does
not yet allow overriding the config), but this may change for future
versions of the daemon, or test-builds.
This patch:
- Re-introduces checks for the BuildkitVersion field in the "Ping" response.
- If the Ping response from the daemon advertises that it supports BuildKit,
the CLI will now use BuildKit as builder.
- If we didn't get a Ping response, or the Ping response did NOT advertise
that the daemon supported BuildKit, we continue to use the current
defaults (BuildKit for Linux daemons, and the legacy builder for Windows)
- Handling of the DOCKER_BUILDKIT environment variable is unchanged; for
CLI.BuildKitEnabled, DOCKER_BUILDKIT always takes precedence, and for
processBuilder the value is taken into account, but will print a warning
when BuildKit is disabled and a Linux daemon is used. For Windows daemons,
no warning is printed.
[moby@7b153b9]: https://github.com/moby/moby/commit/7b153b9e28b53779215de209736310f9266b3f2f
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-19 18:46:30 -04:00
|
|
|
|
|
|
|
si := cli.ServerInfo()
|
|
|
|
if si.BuildkitVersion == types.BuilderBuildKit {
|
|
|
|
// The daemon advertised BuildKit as the preferred builder; this may
|
|
|
|
// be either a Linux daemon or a Windows daemon with experimental
|
|
|
|
// BuildKit support enabled.
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, assume BuildKit is enabled for Linux, but disabled for
|
|
|
|
// Windows / WCOW, which does not yet support BuildKit by default.
|
|
|
|
return si.OSType != "windows", nil
|
2022-02-24 11:57:47 -05:00
|
|
|
}
|
|
|
|
|
2023-07-20 11:25:36 -04:00
|
|
|
// HooksEnabled returns whether plugin hooks are enabled.
|
|
|
|
func (cli *DockerCli) HooksEnabled() bool {
|
|
|
|
// legacy support DOCKER_CLI_HINTS env var
|
|
|
|
if v := os.Getenv("DOCKER_CLI_HINTS"); v != "" {
|
|
|
|
enabled, err := strconv.ParseBool(v)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return enabled
|
|
|
|
}
|
|
|
|
// use DOCKER_CLI_HOOKS env var value if set and not empty
|
|
|
|
if v := os.Getenv("DOCKER_CLI_HOOKS"); v != "" {
|
|
|
|
enabled, err := strconv.ParseBool(v)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return enabled
|
|
|
|
}
|
|
|
|
featuresMap := cli.ConfigFile().Features
|
|
|
|
if v, ok := featuresMap["hooks"]; ok {
|
|
|
|
enabled, err := strconv.ParseBool(v)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return enabled
|
|
|
|
}
|
|
|
|
// default to false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
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 {
|
2023-02-07 20:31:59 -05:00
|
|
|
resolver := func(ctx context.Context, index *registry.IndexInfo) registry.AuthConfig {
|
2023-07-10 11:24:07 -04:00
|
|
|
return ResolveAuthConfig(cli.ConfigFile(), index)
|
2017-06-15 14:41:54 -04:00
|
|
|
}
|
|
|
|
return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure)
|
|
|
|
}
|
|
|
|
|
2019-01-31 12:50:58 -05:00
|
|
|
// WithInitializeClient is passed to DockerCli.Initialize by callers who wish to set a particular API Client for use by the CLI.
|
2023-06-20 18:36:25 -04:00
|
|
|
func WithInitializeClient(makeClient func(dockerCli *DockerCli) (client.APIClient, error)) CLIOption {
|
2019-01-31 12:50:58 -05:00
|
|
|
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.
|
2023-06-20 18:36:25 -04:00
|
|
|
func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption) error {
|
2019-01-31 12:50:58 -05:00
|
|
|
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
|
|
|
},
|
|
|
|
}
|
2024-05-14 11:23:49 -04:00
|
|
|
|
|
|
|
// TODO(krissetto): pass ctx to the funcs instead of using this
|
2024-06-24 08:49:29 -04:00
|
|
|
if cli.enableGlobalMeter {
|
|
|
|
cli.createGlobalMeterProvider(cli.baseCtx)
|
|
|
|
}
|
|
|
|
if cli.enableGlobalTracer {
|
|
|
|
cli.createGlobalTracerProvider(cli.baseCtx)
|
|
|
|
}
|
2024-05-14 11:23:49 -04: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) {
|
2023-06-12 06:39:05 -04:00
|
|
|
opts, err := ep.ClientOpts()
|
2018-12-17 05:27:07 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-06-12 06:39:05 -04:00
|
|
|
if len(configFile.HTTPHeaders) > 0 {
|
|
|
|
opts = append(opts, client.WithHTTPHeaders(configFile.HTTPHeaders))
|
2018-12-17 05:27:07 -05:00
|
|
|
}
|
2024-05-31 07:04:52 -04:00
|
|
|
opts = append(opts, withCustomHeadersFromEnv(), client.WithUserAgent(UserAgent()))
|
2023-06-12 06:39:05 -04:00
|
|
|
return client.NewClientWithOpts(opts...)
|
2018-12-17 05:27:07 -05:00
|
|
|
}
|
|
|
|
|
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 {
|
linting: fmt.Errorf can be replaced with errors.New (perfsprint)
internal/test/cli.go:175:14: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("no notary client available unless defined")
^
cli/command/cli.go:318:29: fmt.Errorf can be replaced with errors.New (perfsprint)
return docker.Endpoint{}, fmt.Errorf("no context store initialized")
^
cli/command/container/attach.go:161:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf(result.Error.Message)
^
cli/command/container/opts.go:577:16: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("--health-start-period cannot be negative")
^
cli/command/container/opts.go:580:16: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("--health-start-interval cannot be negative")
^
cli/command/container/stats.go:221:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("filtering is not supported when specifying a list of containers")
^
cli/command/container/attach_test.go:82:17: fmt.Errorf can be replaced with errors.New (perfsprint)
expectedErr = fmt.Errorf("unexpected error")
^
cli/command/container/create_test.go:234:40: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.CreateResponse{}, fmt.Errorf("shouldn't try to pull image")
^
cli/command/container/list_test.go:150:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("error listing containers")
^
cli/command/container/rm_test.go:40:31: fmt.Errorf can be replaced with errors.New (perfsprint)
return errdefs.NotFound(fmt.Errorf("Error: no such container: " + container))
^
cli/command/container/run_test.go:138:40: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.CreateResponse{}, fmt.Errorf("shouldn't try to pull image")
^
cli/command/image/pull_test.go:115:49: fmt.Errorf can be replaced with errors.New (perfsprint)
return io.NopCloser(strings.NewReader("")), fmt.Errorf("shouldn't try to pull image")
^
cli/command/network/connect.go:88:16: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("invalid key/value pair format in driver options")
^
cli/command/plugin/create_test.go:96:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("Error creating plugin")
^
cli/command/plugin/disable_test.go:32:12: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("Error disabling plugin")
^
cli/command/plugin/enable_test.go:32:12: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("failed to enable plugin")
^
cli/command/plugin/inspect_test.go:55:22: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, nil, fmt.Errorf("error inspecting plugin")
^
cli/command/plugin/install_test.go:43:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("Error installing plugin")
^
cli/command/plugin/install_test.go:51:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("(image) when fetching")
^
cli/command/plugin/install_test.go:95:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("should not try to install plugin")
^
cli/command/plugin/list_test.go:35:41: fmt.Errorf can be replaced with errors.New (perfsprint)
return types.PluginsListResponse{}, fmt.Errorf("error listing plugins")
^
cli/command/plugin/remove_test.go:27:12: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("Error removing plugin")
^
cli/command/registry/login_test.go:36:46: fmt.Errorf can be replaced with errors.New (perfsprint)
return registrytypes.AuthenticateOKBody{}, fmt.Errorf("Invalid Username or Password")
^
cli/command/registry/login_test.go:44:46: fmt.Errorf can be replaced with errors.New (perfsprint)
return registrytypes.AuthenticateOKBody{}, fmt.Errorf(errUnknownUser)
^
cli/command/system/info.go:190:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("errors pretty printing info")
^
cli/command/system/prune.go:77:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf(`ERROR: The "until" filter is not supported with "--volumes"`)
^
cli/command/system/version_test.go:19:28: fmt.Errorf can be replaced with errors.New (perfsprint)
return types.Version{}, fmt.Errorf("no server")
^
cli/command/trust/key_load.go:112:22: fmt.Errorf can be replaced with errors.New (perfsprint)
return []byte{}, fmt.Errorf("could not decrypt key")
^
cli/command/trust/revoke.go:44:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("cannot use a digest reference for IMAGE:TAG")
^
cli/command/trust/revoke.go:105:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("no signed tags to remove")
^
cli/command/trust/signer_add.go:56:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("releases is a reserved keyword, please use a different signer name")
^
cli/command/trust/signer_add.go:60:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("path to a public key must be provided using the `--key` flag")
^
opts/config.go:71:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("source is required")
^
opts/mount.go:168:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("type is required")
^
opts/mount.go:172:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("target is required")
^
opts/network.go:90:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("network name/id is not specified")
^
opts/network.go:129:18: fmt.Errorf can be replaced with errors.New (perfsprint)
return "", "", fmt.Errorf("invalid key value pair format in driver options")
^
opts/opts.go:404:13: fmt.Errorf can be replaced with errors.New (perfsprint)
return 0, fmt.Errorf("value is too precise")
^
opts/opts.go:412:18: fmt.Errorf can be replaced with errors.New (perfsprint)
return "", "", fmt.Errorf("empty string specified for links")
^
opts/parse.go:84:37: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.RestartPolicy{}, fmt.Errorf("invalid restart policy format: no policy provided before colon")
^
opts/parse.go:89:38: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.RestartPolicy{}, fmt.Errorf("invalid restart policy format: maximum retry count must be an integer")
^
opts/port.go:105:13: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("hostip is not supported")
^
opts/secret.go:70:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("source is required")
^
opts/env_test.go:57:11: fmt.Errorf can be replaced with errors.New (perfsprint)
err: fmt.Errorf("invalid environment variable: =a"),
^
opts/env_test.go:93:11: fmt.Errorf can be replaced with errors.New (perfsprint)
err: fmt.Errorf("invalid environment variable: ="),
^
cli-plugins/manager/error_test.go:16:11: fmt.Errorf can be replaced with errors.New (perfsprint)
inner := fmt.Errorf("testing")
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-10 14:47:07 -04:00
|
|
|
return docker.Endpoint{}, errors.New("no context store initialized")
|
2022-11-04 10:47:14 -04:00
|
|
|
}
|
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() {
|
2023-09-09 18:27:44 -04:00
|
|
|
ctx, cancel := context.WithTimeout(cli.baseCtx, cli.getInitTimeout())
|
2023-04-24 04:57:16 -04:00
|
|
|
defer cancel()
|
2020-04-07 18:57:41 -04:00
|
|
|
|
|
|
|
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.
|
2023-06-01 15:45:31 -04:00
|
|
|
// 2. The "DOCKER_CONTEXT" environment variable ([EnvOverrideContext]).
|
2022-11-17 18:22:25 -05:00
|
|
|
// 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
|
2023-06-01 15:42:30 -04:00
|
|
|
// - The "DOCKER_HOST" ([client.EnvOverrideHost]) environment variable is set
|
2022-11-17 18:22:25 -05:00
|
|
|
// 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.
|
2023-11-20 11:38:50 -05:00
|
|
|
func resolveContextName(opts *cliflags.ClientOptions, cfg *configfile.ConfigFile) string {
|
2022-11-17 18:22:25 -05:00
|
|
|
if opts != nil && opts.Context != "" {
|
|
|
|
return opts.Context
|
|
|
|
}
|
|
|
|
if opts != nil && len(opts.Hosts) > 0 {
|
|
|
|
return DefaultContextName
|
|
|
|
}
|
|
|
|
if os.Getenv(client.EnvOverrideHost) != "" {
|
|
|
|
return DefaultContextName
|
|
|
|
}
|
2023-06-01 15:45:31 -04:00
|
|
|
if ctxName := os.Getenv(EnvOverrideContext); ctxName != "" {
|
2022-11-17 18:22:25 -05:00
|
|
|
return ctxName
|
|
|
|
}
|
2023-11-20 11:38:50 -05:00
|
|
|
if cfg != nil && cfg.CurrentContext != "" {
|
2022-11-17 18:22:25 -05:00
|
|
|
// We don't validate if this context exists: errors may occur when trying to use it.
|
2023-11-20 11:38:50 -05:00
|
|
|
return cfg.CurrentContext
|
2022-11-17 18:22:25 -05:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2023-09-09 18:27:44 -04:00
|
|
|
if cli.baseCtx == nil {
|
|
|
|
cli.baseCtx = context.Background()
|
|
|
|
}
|
2022-11-04 10:47:14 -04:00
|
|
|
cli.initializeFromClient()
|
|
|
|
})
|
|
|
|
return cli.initErr
|
|
|
|
}
|
|
|
|
|
2019-01-28 08:52:58 -05:00
|
|
|
// Apply all the operation on the cli
|
2023-06-20 18:36:25 -04:00
|
|
|
func (cli *DockerCli) Apply(ops ...CLIOption) error {
|
2019-01-28 08:52:58 -05:00
|
|
|
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.
|
2023-06-20 18:36:25 -04:00
|
|
|
func NewDockerCli(ops ...CLIOption) (*DockerCli, error) {
|
|
|
|
defaultOps := []CLIOption{
|
2019-01-28 08:52:58 -05:00
|
|
|
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
|
|
|
|
2023-09-09 18:27:44 -04:00
|
|
|
cli := &DockerCli{baseCtx: context.Background()}
|
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:
|
2024-04-26 14:16:51 -04:00
|
|
|
return "", errors.New("Specify only one -H")
|
2016-09-08 13:11:39 -04:00
|
|
|
}
|
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{
|
2023-11-20 12:04:36 -05:00
|
|
|
store.EndpointTypeGetter(docker.DockerEndpoint, func() any { return &docker.EndpointMeta{} }),
|
2019-05-16 09:10:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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(
|
2023-11-20 12:04:36 -05:00
|
|
|
func() any { return &DockerContext{} },
|
2019-05-16 09:10:57 -04:00
|
|
|
defaultStoreEndpoints...,
|
2019-01-21 03:37:20 -05:00
|
|
|
)
|
|
|
|
}
|