Compare commits
52 Commits
v17.11.0-c
...
v17.10.0-c
| Author | SHA1 | Date | |
|---|---|---|---|
| f4ffd2511c | |||
| ec6b6de1c8 | |||
| 8bac3f632b | |||
| 646a76c31b | |||
| af94197a36 | |||
| 2f11502622 | |||
| 24ee573f35 | |||
| 4d73e16a14 | |||
| 1311aaf76e | |||
| 6f56735237 | |||
| e274827392 | |||
| e7f51b183f | |||
| 792e8f9a82 | |||
| 8b13940b3b | |||
| 550052ebae | |||
| b0596beda5 | |||
| 6fc2c6b8c5 | |||
| f26232e8f6 | |||
| 018b6401f5 | |||
| 56bd88c226 | |||
| fc8971da3b | |||
| 9f566ba32b | |||
| 785d4378ad | |||
| e9a538eda6 | |||
| cdee6d83f4 | |||
| c76d10969f | |||
| 02f6ddc6ae | |||
| 125ffb8b1b | |||
| 9c61899f29 | |||
| 76922d8690 | |||
| f437cf754f | |||
| 2adb51e303 | |||
| a761ee3d4d | |||
| e1484d2ff8 | |||
| d82f9fe3f3 | |||
| 73ecdcee10 | |||
| c8e9afef61 | |||
| d866876d86 | |||
| 8ab000a5f4 | |||
| c8ede6fcc6 | |||
| 0d46b8e710 | |||
| 58f592c0d9 | |||
| e58d3abea8 | |||
| 4f818dd6f7 | |||
| d73806305a | |||
| 4e81e4fa4e | |||
| 8a6c4c93b6 | |||
| 7b99808cac | |||
| fab4b40e38 | |||
| 079f5eb5e5 | |||
| db0a220cda | |||
| 71bb1e8c44 |
87
CHANGELOG.md
87
CHANGELOG.md
@ -5,75 +5,50 @@ information on the list of deprecated flags and APIs please have a look at
|
||||
https://docs.docker.com/engine/deprecated/ where target removal dates can also
|
||||
be found.
|
||||
|
||||
## 17.11.0-ce (2017-11-20)
|
||||
## 17.10.0-ce (2017-10-17)
|
||||
|
||||
IMPORTANT: Docker CE 17.11 is the first Docker release based on
|
||||
[containerd 1.0 beta](https://github.com/containerd/containerd/releases/tag/v1.0.0-beta.2).
|
||||
Docker CE 17.11 and later won't recognize containers started with
|
||||
previous Docker versions. If using
|
||||
[Live Restore](https://docs.docker.com/engine/admin/live-restore/#enable-the-live-restore-option),
|
||||
you must stop all containers before upgrading to Docker CE 17.11.
|
||||
If you don't, any containers started by Docker versions that predate
|
||||
17.11 won't be recognized by Docker after the upgrade and will keep
|
||||
running, un-managed, on the system.
|
||||
IMPORTANT: Starting with this release, `docker service create`, `docker service update`,
|
||||
`docker service scale` and `docker service rollback` use non-detached mode as default,
|
||||
use `--detach` to keep the old behaviour.
|
||||
|
||||
### Builder
|
||||
|
||||
* Test & Fix build with rm/force-rm matrix [moby/moby#35139](https://github.com/moby/moby/pull/35139)
|
||||
- Fix build with `--stream` with a large context [moby/moby#35404](https://github.com/moby/moby/pull/35404)
|
||||
* Reset uid/gid to 0 in uploaded build context to share build cache with other clients [docker/cli#513](https://github.com/docker/cli/pull/513)
|
||||
+ Add support for `ADD` urls without any sub path [moby/moby#34217](https://github.com/moby/moby/pull/34217)
|
||||
|
||||
### Client
|
||||
|
||||
* Hide help flag from help output [docker/cli#645](https://github.com/docker/cli/pull/645)
|
||||
* Support parsing of named pipes for compose volumes [docker/cli#560](https://github.com/docker/cli/pull/560)
|
||||
* [Compose] Cast values to expected type after interpolating values [docker/cli#601](https://github.com/docker/cli/pull/601)
|
||||
+ Add output for "secrets" and "configs" on `docker stack deploy` [docker/cli#593](https://github.com/docker/cli/pull/593)
|
||||
- Fix flag description for `--host-add` [docker/cli#648](https://github.com/docker/cli/pull/648)
|
||||
* Do not truncate ID on docker service ps --quiet [docker/cli#579](https://github.com/docker/cli/pull/579)
|
||||
|
||||
### Deprecation
|
||||
|
||||
* Update bash completion and deprecation for synchronous service updates [docker/cli#610](https://github.com/docker/cli/pull/610)
|
||||
|
||||
### Logging
|
||||
|
||||
* copy to log driver's bufsize, fixes #34887 [moby/moby#34888](https://github.com/moby/moby/pull/34888)
|
||||
+ Add TCP support for GELF log driver [moby/moby#34758](https://github.com/moby/moby/pull/34758)
|
||||
+ Add credentials endpoint option for awslogs driver [moby/moby#35055](https://github.com/moby/moby/pull/35055)
|
||||
* Move output of `docker stack rm` to stdout [docker/cli#491](https://github.com/docker/cli/pull/491)
|
||||
* Use natural sort secrets and configs in cli [docker/cli#307](https://github.com/docker/cli/pull/307)
|
||||
* Use non-detached mode as default for `docker service` commands [docker/cli#525](https://github.com/docker/cli/pull/525)
|
||||
* Set APIVersion on the client, even when Ping fails [docker/cli#546](https://github.com/docker/cli/pull/546)
|
||||
- Fix loader error with different build syntax in `docker stack deploy` [docker/cli#544](https://github.com/docker/cli/pull/544)
|
||||
* Change the default output format for `docker container stats` to show `CONTAINER ID` and `NAME` [docker/cli#565](https://github.com/docker/cli/pull/565)
|
||||
+ Add `--no-trunc` flag to `docker container stats` [docker/cli#565](https://github.com/docker/cli/pull/565)
|
||||
+ Add experimental `docker trust`: `view`, `revoke`, `sign` subcommands [docker/cli#472](https://github.com/docker/cli/pull/472)
|
||||
- Various doc and shell completion fixes [docker/cli#610](https://github.com/docker/cli/pull/610) [docker/cli#611](https://github.com/docker/cli/pull/611) [docker/cli#618](https://github.com/docker/cli/pull/618) [docker/cli#580](https://github.com/docker/cli/pull/580) [docker/cli#598](https://github.com/docker/cli/pull/698) [docker/cli#603](https://github.com/docker/cli/pull/603)
|
||||
|
||||
### Networking
|
||||
|
||||
- Fix network name masking network ID on delete [moby/moby#34509](https://github.com/moby/moby/pull/34509)
|
||||
- Fix returned error code for network creation from 500 to 409 [moby/moby#35030](https://github.com/moby/moby/pull/35030)
|
||||
- Fix tasks fail with error "Unable to complete atomic operation, key modified" [docker/libnetwork#2004](https://github.com/docker/libnetwork/pull/2004)
|
||||
* Enabling ILB/ELB on windows using per-node, per-network LB endpoint [moby/moby#34674](https://github.com/moby/moby/pull/34674)
|
||||
* Overlay fix for transient IP reuse [docker/libnetwork#1935](https://github.com/docker/libnetwork/pull/1935)
|
||||
* Serializing bitseq alloc [docker/libnetwork#1788](https://github.com/docker/libnetwork/pull/1788)
|
||||
- Disable hostname lookup on chain exists check [docker/libnetwork#1974](https://github.com/docker/libnetwork/pull/1974)
|
||||
|
||||
### Runtime
|
||||
|
||||
* Switch to Containerd 1.0 client [moby/moby#34895](https://github.com/moby/moby/pull/34895)
|
||||
* Increase container default shutdown timeout on Windows [moby/moby#35184](https://github.com/moby/moby/pull/35184)
|
||||
* LCOW: API: Add `platform` to /images/create and /build [moby/moby#34642](https://github.com/moby/moby/pull/34642)
|
||||
* Stop filtering Windows manifest lists by version [moby/moby#35117](https://github.com/moby/moby/pull/35117)
|
||||
* Use windows console mode constants from Azure/go-ansiterm [moby/moby#35056](https://github.com/moby/moby/pull/35056)
|
||||
* Windows Daemon should respect DOCKER_TMPDIR [moby/moby#35077](https://github.com/moby/moby/pull/35077)
|
||||
* Windows: Fix startup logging [moby/moby#35253](https://github.com/moby/moby/pull/35253)
|
||||
* LCOW: Add UVM debugability by grabbing logs before tear-down [moby/moby#34846](https://github.com/moby/moby/pull/34846)
|
||||
* LCOW: Prepare work for bind mounts [moby/moby#34258](https://github.com/moby/moby/pull/34258)
|
||||
* LCOW: Support for docker cp, ADD/COPY on build [moby/moby#34252](https://github.com/moby/moby/pull/34252)
|
||||
* LCOW: VHDX boot to readonly [moby/moby#34754](https://github.com/moby/moby/pull/34754)
|
||||
* Volume: evaluate symlinks before relabeling mount source [moby/moby#34792](https://github.com/moby/moby/pull/34792)
|
||||
- Fixing ‘docker cp’ to allow new target file name in a host symlinked directory [moby/moby#31993](https://github.com/moby/moby/pull/31993)
|
||||
+ Add support for Windows version filtering on pull [moby/moby#35090](https://github.com/moby/moby/pull/35090)
|
||||
- Fixes LCOW after containerd 1.0 introduced regressions [moby/moby#35320](https://github.com/moby/moby/pull/35320)
|
||||
* ContainerWait on remove: don't stuck on rm fail [moby/moby#34999](https://github.com/moby/moby/pull/34999)
|
||||
* oci: obey CL_UNPRIVILEGED for user namespaced daemon [moby/moby#35205](https://github.com/moby/moby/pull/35205)
|
||||
* Don't abort when setting may_detach_mounts [moby/moby#35172](https://github.com/moby/moby/pull/35172)
|
||||
- Fix panic on get container pid when live restore containers [moby/moby#35157](https://github.com/moby/moby/pull/35157)
|
||||
- Mask `/proc/scsi` path for containers to prevent removal of devices (CVE-2017-16539) [moby/moby#35399](https://github.com/moby/moby/pull/35399)
|
||||
* Update to github.com/vbatts/tar-split@v0.10.2 (CVE-2017-14992) [moby/moby#35424](https://github.com/moby/moby/pull/35424)
|
||||
- Container: protect health monitor channel [moby/moby#35482](https://github.com/moby/moby/pull/35482)
|
||||
- Libcontainerd: fix leaking container/exec state [moby/moby#35484](https://github.com/moby/moby/pull/35484)
|
||||
|
||||
### Swarm Mode
|
||||
### Swarm mode
|
||||
|
||||
* Modifying integration test due to new ipam options in swarmkit [moby/moby#35103](https://github.com/moby/moby/pull/35103)
|
||||
- Fix deadlock on getting swarm info [moby/moby#35388](https://github.com/moby/moby/pull/35388)
|
||||
+ Expand the scope of the `Err` field in `TaskStatus` to also cover non-terminal errors that block the task from progressing [docker/swarmkit#2287](https://github.com/docker/swarmkit/pull/2287)
|
||||
|
||||
### Packaging
|
||||
|
||||
+ Build packages for Debian 10 (Buster) [docker/docker-ce-packaging#50](https://github.com/docker/docker-ce-packaging/pull/50)
|
||||
+ Build packages for Ubuntu 17.10 (Artful) [docker/docker-ce-packaging#55](https://github.com/docker/docker-ce-packaging/pull/55)
|
||||
* Produce an error if `docker swarm init --force-new-cluster` is executed on worker nodes [moby/moby#34881](https://github.com/moby/moby/pull/34881)
|
||||
+ Add support for `.Node.Hostname` templating in swarm services [moby/moby#34686](https://github.com/moby/moby/pull/34686)
|
||||
* Increase gRPC request timeout to 20 seconds for sending snapshots [docker/swarmkit#2391](https://github.com/docker/swarmkit/pull/2391)
|
||||
- Do not filter nodes if logdriver is set to `none` [docker/swarmkit#2396](https://github.com/docker/swarmkit/pull/2396)
|
||||
+ Adding ipam options to ipam driver requests [docker/swarmkit#2324](https://github.com/docker/swarmkit/pull/2324)
|
||||
|
||||
2
Makefile
2
Makefile
@ -1,7 +1,7 @@
|
||||
CLI_DIR:=$(CURDIR)/components/cli
|
||||
ENGINE_DIR:=$(CURDIR)/components/engine
|
||||
PACKAGING_DIR:=$(CURDIR)/components/packaging
|
||||
MOBY_COMPONENTS_SHA=ab7c118272b02d8672dc0255561d0c4015979780
|
||||
MOBY_COMPONENTS_SHA=f79265f1412af0a68aadd11e1d2f374446f3681b
|
||||
MOBY_COMPONENTS_URL=https://raw.githubusercontent.com/shykes/moby-extras/$(MOBY_COMPONENTS_SHA)/cmd/moby-components
|
||||
MOBY_COMPONENTS=.helpers/moby-components-$(MOBY_COMPONENTS_SHA)
|
||||
VERSION=$(shell cat VERSION)
|
||||
|
||||
2
components/cli/.github/CODEOWNERS
vendored
2
components/cli/.github/CODEOWNERS
vendored
@ -1,4 +1,4 @@
|
||||
# GitHub code owners
|
||||
# Github code owners
|
||||
# See https://github.com/blog/2392-introducing-code-owners
|
||||
|
||||
cli/command/stack/** @dnephin @vdemeester
|
||||
|
||||
@ -103,7 +103,7 @@
|
||||
[people.cpuguy83]
|
||||
Name = "Brian Goff"
|
||||
Email = "cpuguy83@gmail.com"
|
||||
GitHub = "cpuguy83"
|
||||
Github = "cpuguy83"
|
||||
|
||||
[people.crosbymichael]
|
||||
Name = "Michael Crosby"
|
||||
|
||||
@ -1 +1 @@
|
||||
17.11.0-ce
|
||||
17.10.0-ce
|
||||
|
||||
@ -17,7 +17,6 @@ func SetupRootCommand(rootCmd *cobra.Command) {
|
||||
cobra.AddTemplateFunc("operationSubCommands", operationSubCommands)
|
||||
cobra.AddTemplateFunc("managementSubCommands", managementSubCommands)
|
||||
cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages)
|
||||
cobra.AddTemplateFunc("useLine", UseLine)
|
||||
|
||||
rootCmd.SetUsageTemplate(usageTemplate)
|
||||
rootCmd.SetHelpTemplate(helpTemplate)
|
||||
@ -26,7 +25,6 @@ func SetupRootCommand(rootCmd *cobra.Command) {
|
||||
|
||||
rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
|
||||
rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help")
|
||||
rootCmd.PersistentFlags().Lookup("help").Hidden = true
|
||||
}
|
||||
|
||||
// FlagErrorFunc prints an error message which matches the format of the
|
||||
@ -99,19 +97,9 @@ func managementSubCommands(cmd *cobra.Command) []*cobra.Command {
|
||||
return cmds
|
||||
}
|
||||
|
||||
// UseLine returns the usage line for a command. This implementation is different
|
||||
// from the default Command.UseLine in that it does not add a `[flags]` to the
|
||||
// end of the line.
|
||||
func UseLine(cmd *cobra.Command) string {
|
||||
if cmd.HasParent() {
|
||||
return cmd.Parent().CommandPath() + " " + cmd.Use
|
||||
}
|
||||
return cmd.Use
|
||||
}
|
||||
|
||||
var usageTemplate = `Usage:
|
||||
|
||||
{{- if not .HasSubCommands}} {{ useLine . }}{{end}}
|
||||
{{- if not .HasSubCommands}} {{.UseLine}}{{end}}
|
||||
{{- if .HasSubCommands}} {{ .CommandPath}} COMMAND{{end}}
|
||||
|
||||
{{ .Short | trim }}
|
||||
|
||||
@ -9,11 +9,11 @@ import (
|
||||
// NewCheckpointCommand returns the `checkpoint` subcommand (only in experimental)
|
||||
func NewCheckpointCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "checkpoint",
|
||||
Short: "Manage checkpoints",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"experimental": "", "version": "1.25"},
|
||||
Use: "checkpoint",
|
||||
Short: "Manage checkpoints",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"experimental": "", "version": "1.25"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCreateCommand(dockerCli),
|
||||
|
||||
@ -18,11 +18,11 @@ import (
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/docker/notary"
|
||||
notaryclient "github.com/docker/notary/client"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/theupdateframework/notary"
|
||||
notaryclient "github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/passphrase"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@ -207,8 +207,7 @@ func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.
|
||||
return client.NewClient(host, verStr, httpClient, customHeaders)
|
||||
}
|
||||
|
||||
func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error) {
|
||||
var host string
|
||||
func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string, err error) {
|
||||
switch len(hosts) {
|
||||
case 0:
|
||||
host = os.Getenv("DOCKER_HOST")
|
||||
@ -218,7 +217,8 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error
|
||||
return "", errors.New("Please specify only one -H")
|
||||
}
|
||||
|
||||
return dopts.ParseHost(tlsOptions != nil, host)
|
||||
host, err = dopts.ParseHost(tlsOptions != nil, host)
|
||||
return
|
||||
}
|
||||
|
||||
func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, error) {
|
||||
|
||||
@ -8,13 +8,14 @@ import (
|
||||
)
|
||||
|
||||
// NewConfigCommand returns a cobra command for `config` subcommands
|
||||
func NewConfigCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// nolint: interfacer
|
||||
func NewConfigCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Manage Docker configs",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.30"},
|
||||
Use: "config",
|
||||
Short: "Manage Docker configs",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.30"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newConfigListCommand(dockerCli),
|
||||
|
||||
@ -7,7 +7,8 @@ import (
|
||||
)
|
||||
|
||||
// NewContainerCommand returns a cobra command for `container` subcommands
|
||||
func NewContainerCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// nolint: interfacer
|
||||
func NewContainerCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "container",
|
||||
Short: "Manage containers",
|
||||
|
||||
@ -22,7 +22,7 @@ type commitOptions struct {
|
||||
}
|
||||
|
||||
// NewCommitCommand creates a new cobra.Command for `docker commit`
|
||||
func NewCommitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewCommitCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var options commitOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -51,7 +51,7 @@ func NewCommitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCommit(dockerCli command.Cli, options *commitOptions) error {
|
||||
func runCommit(dockerCli *command.DockerCli, options *commitOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
name := options.container
|
||||
|
||||
@ -36,7 +36,7 @@ type cpConfig struct {
|
||||
}
|
||||
|
||||
// NewCopyCommand creates a new `docker cp` command
|
||||
func NewCopyCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewCopyCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts copyOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -72,7 +72,7 @@ func NewCopyCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCopy(dockerCli command.Cli, opts copyOptions) error {
|
||||
func runCopy(dockerCli *command.DockerCli, opts copyOptions) error {
|
||||
srcContainer, srcPath := splitCpArg(opts.source)
|
||||
dstContainer, dstPath := splitCpArg(opts.destination)
|
||||
|
||||
@ -104,7 +104,7 @@ func runCopy(dockerCli command.Cli, opts copyOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
func statContainerPath(ctx context.Context, dockerCli command.Cli, containerName, path string) (types.ContainerPathStat, error) {
|
||||
func statContainerPath(ctx context.Context, dockerCli *command.DockerCli, containerName, path string) (types.ContainerPathStat, error) {
|
||||
return dockerCli.Client().ContainerStatPath(ctx, containerName, path)
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ func resolveLocalPath(localPath string) (absPath string, err error) {
|
||||
return archive.PreserveTrailingDotOrSeparator(absPath, localPath, filepath.Separator), nil
|
||||
}
|
||||
|
||||
func copyFromContainer(ctx context.Context, dockerCli command.Cli, srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
|
||||
func copyFromContainer(ctx context.Context, dockerCli *command.DockerCli, srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
|
||||
if dstPath != "-" {
|
||||
// Get an absolute destination path.
|
||||
dstPath, err = resolveLocalPath(dstPath)
|
||||
@ -177,7 +177,7 @@ func copyFromContainer(ctx context.Context, dockerCli command.Cli, srcContainer,
|
||||
return archive.CopyTo(preArchive, srcInfo, dstPath)
|
||||
}
|
||||
|
||||
func copyToContainer(ctx context.Context, dockerCli command.Cli, srcPath, dstContainer, dstPath string, cpParam *cpConfig, copyUIDGID bool) (err error) {
|
||||
func copyToContainer(ctx context.Context, dockerCli *command.DockerCli, srcPath, dstContainer, dstPath string, cpParam *cpConfig, copyUIDGID bool) (err error) {
|
||||
if srcPath != "-" {
|
||||
// Get an absolute source path.
|
||||
srcPath, err = resolveLocalPath(srcPath)
|
||||
|
||||
@ -194,7 +194,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig
|
||||
|
||||
//if image not found try to pull it
|
||||
if err != nil {
|
||||
if apiclient.IsErrNotFound(err) && namedRef != nil {
|
||||
if apiclient.IsErrImageNotFound(err) && namedRef != nil {
|
||||
fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
|
||||
|
||||
// we don't want to write to stdout anything apart from container.ID
|
||||
|
||||
@ -14,7 +14,7 @@ type diffOptions struct {
|
||||
}
|
||||
|
||||
// NewDiffCommand creates a new cobra.Command for `docker diff`
|
||||
func NewDiffCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewDiffCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts diffOptions
|
||||
|
||||
return &cobra.Command{
|
||||
@ -28,7 +28,7 @@ func NewDiffCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func runDiff(dockerCli command.Cli, opts *diffOptions) error {
|
||||
func runDiff(dockerCli *command.DockerCli, opts *diffOptions) error {
|
||||
if opts.container == "" {
|
||||
return errors.New("Container name cannot be empty")
|
||||
}
|
||||
|
||||
@ -122,10 +122,7 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execConfig *typ
|
||||
}
|
||||
|
||||
client := dockerCli.Client()
|
||||
execStartCheck := types.ExecStartCheck{
|
||||
Tty: execConfig.Tty,
|
||||
}
|
||||
resp, err := client.ContainerExecAttach(ctx, execID, execStartCheck)
|
||||
resp, err := client.ContainerExecAttach(ctx, execID, *execConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ type exportOptions struct {
|
||||
}
|
||||
|
||||
// NewExportCommand creates a new `docker export` command
|
||||
func NewExportCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewExportCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts exportOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -36,7 +36,7 @@ func NewExportCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runExport(dockerCli command.Cli, opts exportOptions) error {
|
||||
func runExport(dockerCli *command.DockerCli, opts exportOptions) error {
|
||||
if opts.output == "" && dockerCli.Out().IsTerminal() {
|
||||
return errors.New("cowardly refusing to save to a terminal. Use the -o flag or redirect")
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ type inspectOptions struct {
|
||||
}
|
||||
|
||||
// newInspectCommand creates a new cobra.Command for `docker container inspect`
|
||||
func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts inspectOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -35,7 +35,7 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ type killOptions struct {
|
||||
}
|
||||
|
||||
// NewKillCommand creates a new cobra.Command for `docker kill`
|
||||
func NewKillCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewKillCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts killOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -36,7 +36,7 @@ func NewKillCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runKill(dockerCli command.Cli, opts *killOptions) error {
|
||||
func runKill(dockerCli *command.DockerCli, opts *killOptions) error {
|
||||
var errs []string
|
||||
ctx := context.Background()
|
||||
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
|
||||
|
||||
@ -25,7 +25,7 @@ type psOptions struct {
|
||||
}
|
||||
|
||||
// NewPsCommand creates a new cobra.Command for `docker ps`
|
||||
func NewPsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewPsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
options := psOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -51,7 +51,7 @@ func NewPsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := *NewPsCommand(dockerCli)
|
||||
cmd.Aliases = []string{"ps", "list"}
|
||||
cmd.Use = "ls [OPTIONS]"
|
||||
@ -109,7 +109,7 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func runPs(dockerCli command.Cli, options *psOptions) error {
|
||||
func runPs(dockerCli *command.DockerCli, options *psOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
listOptions, err := buildContainerListOptions(options)
|
||||
|
||||
@ -22,7 +22,7 @@ type logsOptions struct {
|
||||
}
|
||||
|
||||
// NewLogsCommand creates a new cobra.Command for `docker logs`
|
||||
func NewLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewLogsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts logsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -44,7 +44,7 @@ func NewLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runLogs(dockerCli command.Cli, opts *logsOptions) error {
|
||||
func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
options := types.ContainerLogsOptions{
|
||||
|
||||
@ -45,7 +45,10 @@ func TestValidateAttach(t *testing.T) {
|
||||
|
||||
// nolint: unparam
|
||||
func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
||||
flags, copts := setupRunFlags()
|
||||
flags := pflag.NewFlagSet("run", pflag.ContinueOnError)
|
||||
flags.SetOutput(ioutil.Discard)
|
||||
flags.Usage = nil
|
||||
copts := addFlags(flags)
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@ -57,14 +60,6 @@ func parseRun(args []string) (*container.Config, *container.HostConfig, *network
|
||||
return containerConfig.Config, containerConfig.HostConfig, containerConfig.NetworkingConfig, err
|
||||
}
|
||||
|
||||
func setupRunFlags() (*pflag.FlagSet, *containerOptions) {
|
||||
flags := pflag.NewFlagSet("run", pflag.ContinueOnError)
|
||||
flags.SetOutput(ioutil.Discard)
|
||||
flags.Usage = nil
|
||||
copts := addFlags(flags)
|
||||
return flags, copts
|
||||
}
|
||||
|
||||
func parseMustError(t *testing.T, args string) {
|
||||
_, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
||||
assert.Error(t, err, args)
|
||||
@ -232,21 +227,20 @@ func TestParseWithMacAddress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunFlagsParseWithMemory(t *testing.T) {
|
||||
flags, _ := setupRunFlags()
|
||||
args := []string{"--memory=invalid", "img", "cmd"}
|
||||
err := flags.Parse(args)
|
||||
testutil.ErrorContains(t, err, `invalid argument "invalid" for "-m, --memory" flag`)
|
||||
func TestParseWithMemory(t *testing.T) {
|
||||
invalidMemory := "--memory=invalid"
|
||||
_, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"})
|
||||
testutil.ErrorContains(t, err, invalidMemory)
|
||||
|
||||
_, hostconfig := mustParse(t, "--memory=1G")
|
||||
assert.Equal(t, int64(1073741824), hostconfig.Memory)
|
||||
}
|
||||
|
||||
func TestParseWithMemorySwap(t *testing.T) {
|
||||
flags, _ := setupRunFlags()
|
||||
args := []string{"--memory-swap=invalid", "img", "cmd"}
|
||||
err := flags.Parse(args)
|
||||
testutil.ErrorContains(t, err, `invalid argument "invalid" for "--memory-swap" flag`)
|
||||
invalidMemory := "--memory-swap=invalid"
|
||||
|
||||
_, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"})
|
||||
testutil.ErrorContains(t, err, invalidMemory)
|
||||
|
||||
_, hostconfig := mustParse(t, "--memory-swap=1G")
|
||||
assert.Equal(t, int64(1073741824), hostconfig.MemorySwap)
|
||||
@ -371,10 +365,7 @@ func TestParseDevice(t *testing.T) {
|
||||
|
||||
func TestParseModes(t *testing.T) {
|
||||
// pid ko
|
||||
flags, copts := setupRunFlags()
|
||||
args := []string{"--pid=container:", "img", "cmd"}
|
||||
require.NoError(t, flags.Parse(args))
|
||||
_, err := parse(flags, copts)
|
||||
_, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"})
|
||||
testutil.ErrorContains(t, err, "--pid: invalid PID mode")
|
||||
|
||||
// pid ok
|
||||
@ -394,18 +385,14 @@ func TestParseModes(t *testing.T) {
|
||||
if !hostconfig.UTSMode.Valid() {
|
||||
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunFlagsParseShmSize(t *testing.T) {
|
||||
// shm-size ko
|
||||
flags, _ := setupRunFlags()
|
||||
args := []string{"--shm-size=a128m", "img", "cmd"}
|
||||
expectedErr := `invalid argument "a128m" for "--shm-size" flag: invalid size: 'a128m'`
|
||||
err := flags.Parse(args)
|
||||
expectedErr := `invalid argument "a128m" for --shm-size=a128m: invalid size: 'a128m'`
|
||||
_, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"})
|
||||
testutil.ErrorContains(t, err, expectedErr)
|
||||
|
||||
// shm-size ok
|
||||
_, hostconfig, _, err := parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
||||
_, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
||||
require.NoError(t, err)
|
||||
if hostconfig.ShmSize != 134217728 {
|
||||
t.Fatalf("Expected a valid ShmSize, got %d", hostconfig.ShmSize)
|
||||
|
||||
@ -16,7 +16,7 @@ type pauseOptions struct {
|
||||
}
|
||||
|
||||
// NewPauseCommand creates a new cobra.Command for `docker pause`
|
||||
func NewPauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewPauseCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts pauseOptions
|
||||
|
||||
return &cobra.Command{
|
||||
@ -30,7 +30,7 @@ func NewPauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func runPause(dockerCli command.Cli, opts *pauseOptions) error {
|
||||
func runPause(dockerCli *command.DockerCli, opts *pauseOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
|
||||
@ -19,7 +19,7 @@ type portOptions struct {
|
||||
}
|
||||
|
||||
// NewPortCommand creates a new cobra.Command for `docker port`
|
||||
func NewPortCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewPortCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts portOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -37,7 +37,7 @@ func NewPortCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPort(dockerCli command.Cli, opts *portOptions) error {
|
||||
func runPort(dockerCli *command.DockerCli, opts *portOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
|
||||
|
||||
@ -35,7 +35,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
||||
return nil
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@ -52,12 +52,12 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
|
||||
|
||||
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
|
||||
return 0, "", nil
|
||||
return
|
||||
}
|
||||
|
||||
report, err := dockerCli.Client().ContainersPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
return
|
||||
}
|
||||
|
||||
if len(report.ContainersDeleted) > 0 {
|
||||
@ -68,7 +68,7 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
spaceReclaimed = report.SpaceReclaimed
|
||||
}
|
||||
|
||||
return spaceReclaimed, output, nil
|
||||
return
|
||||
}
|
||||
|
||||
// RunPrune calls the Container Prune API
|
||||
|
||||
@ -17,7 +17,7 @@ type renameOptions struct {
|
||||
}
|
||||
|
||||
// NewRenameCommand creates a new cobra.Command for `docker rename`
|
||||
func NewRenameCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewRenameCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts renameOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -33,7 +33,7 @@ func NewRenameCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRename(dockerCli command.Cli, opts *renameOptions) error {
|
||||
func runRename(dockerCli *command.DockerCli, opts *renameOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
oldName := strings.TrimSpace(opts.oldName)
|
||||
|
||||
@ -20,7 +20,7 @@ type restartOptions struct {
|
||||
}
|
||||
|
||||
// NewRestartCommand creates a new cobra.Command for `docker restart`
|
||||
func NewRestartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewRestartCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts restartOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -39,7 +39,7 @@ func NewRestartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRestart(dockerCli command.Cli, opts *restartOptions) error {
|
||||
func runRestart(dockerCli *command.DockerCli, opts *restartOptions) error {
|
||||
ctx := context.Background()
|
||||
var errs []string
|
||||
var timeout *time.Duration
|
||||
|
||||
@ -21,7 +21,7 @@ type rmOptions struct {
|
||||
}
|
||||
|
||||
// NewRmCommand creates a new cobra.Command for `docker rm`
|
||||
func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewRmCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts rmOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -41,7 +41,7 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRm(dockerCli command.Cli, opts *rmOptions) error {
|
||||
func runRm(dockerCli *command.DockerCli, opts *rmOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
|
||||
@ -32,7 +32,7 @@ type runOptions struct {
|
||||
}
|
||||
|
||||
// NewRunCommand create a new `docker run` command
|
||||
func NewRunCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewRunCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts runOptions
|
||||
var copts *containerOptions
|
||||
|
||||
@ -96,7 +96,7 @@ func isLocalhost(ip string) bool {
|
||||
return localhostIPRegexp.MatchString(ip)
|
||||
}
|
||||
|
||||
func runRun(dockerCli command.Cli, flags *pflag.FlagSet, ropts *runOptions, copts *containerOptions) error {
|
||||
func runRun(dockerCli *command.DockerCli, flags *pflag.FlagSet, ropts *runOptions, copts *containerOptions) error {
|
||||
proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), copts.env.GetAll())
|
||||
newEnv := []string{}
|
||||
for k, v := range proxyConfig {
|
||||
@ -117,7 +117,7 @@ func runRun(dockerCli command.Cli, flags *pflag.FlagSet, ropts *runOptions, copt
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptions, containerConfig *containerConfig) error {
|
||||
func runContainer(dockerCli *command.DockerCli, opts *runOptions, copts *containerOptions, containerConfig *containerConfig) error {
|
||||
config := containerConfig.Config
|
||||
hostConfig := containerConfig.HostConfig
|
||||
stdout, stderr := dockerCli.Out(), dockerCli.Err()
|
||||
|
||||
@ -27,7 +27,7 @@ type startOptions struct {
|
||||
}
|
||||
|
||||
// NewStartCommand creates a new cobra.Command for `docker start`
|
||||
func NewStartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewStartCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts startOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -53,7 +53,7 @@ func NewStartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
func runStart(dockerCli command.Cli, opts *startOptions) error {
|
||||
func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
|
||||
ctx, cancelFun := context.WithCancel(context.Background())
|
||||
|
||||
if opts.attach || opts.openStdin {
|
||||
@ -181,7 +181,7 @@ func runStart(dockerCli command.Cli, opts *startOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func startContainersWithoutAttachments(ctx context.Context, dockerCli command.Cli, containers []string) error {
|
||||
func startContainersWithoutAttachments(ctx context.Context, dockerCli *command.DockerCli, containers []string) error {
|
||||
var failedContainers []string
|
||||
for _, container := range containers {
|
||||
if err := dockerCli.Client().ContainerStart(ctx, container, types.ContainerStartOptions{}); err != nil {
|
||||
|
||||
@ -27,7 +27,7 @@ type statsOptions struct {
|
||||
}
|
||||
|
||||
// NewStatsCommand creates a new cobra.Command for `docker stats`
|
||||
func NewStatsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewStatsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts statsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -51,7 +51,7 @@ func NewStatsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// runStats displays a live stream of resource usage statistics for one or more containers.
|
||||
// This shows real-time information on CPU usage, memory usage, and network I/O.
|
||||
// nolint: gocyclo
|
||||
func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
||||
func runStats(dockerCli *command.DockerCli, opts *statsOptions) error {
|
||||
showAll := len(opts.containers) == 0
|
||||
closeChan := make(chan error)
|
||||
|
||||
|
||||
@ -200,8 +200,7 @@ func calculateCPUPercentWindows(v *types.StatsJSON) float64 {
|
||||
return 0.00
|
||||
}
|
||||
|
||||
func calculateBlockIO(blkio types.BlkioStats) (uint64, uint64) {
|
||||
var blkRead, blkWrite uint64
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead uint64, blkWrite uint64) {
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
case "read":
|
||||
@ -210,7 +209,7 @@ func calculateBlockIO(blkio types.BlkioStats) (uint64, uint64) {
|
||||
blkWrite = blkWrite + bioEntry.Value
|
||||
}
|
||||
}
|
||||
return blkRead, blkWrite
|
||||
return
|
||||
}
|
||||
|
||||
func calculateNetwork(network map[string]types.NetworkStats) (float64, float64) {
|
||||
|
||||
@ -20,7 +20,7 @@ type stopOptions struct {
|
||||
}
|
||||
|
||||
// NewStopCommand creates a new cobra.Command for `docker stop`
|
||||
func NewStopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewStopCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts stopOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -39,7 +39,7 @@ func NewStopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStop(dockerCli command.Cli, opts *stopOptions) error {
|
||||
func runStop(dockerCli *command.DockerCli, opts *stopOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var timeout *time.Duration
|
||||
|
||||
@ -18,7 +18,7 @@ type topOptions struct {
|
||||
}
|
||||
|
||||
// NewTopCommand creates a new cobra.Command for `docker top`
|
||||
func NewTopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewTopCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts topOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -38,7 +38,7 @@ func NewTopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runTop(dockerCli command.Cli, opts *topOptions) error {
|
||||
func runTop(dockerCli *command.DockerCli, opts *topOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
procList, err := dockerCli.Client().ContainerTop(ctx, opts.container, opts.args)
|
||||
|
||||
@ -16,7 +16,7 @@ type unpauseOptions struct {
|
||||
}
|
||||
|
||||
// NewUnpauseCommand creates a new cobra.Command for `docker unpause`
|
||||
func NewUnpauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewUnpauseCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts unpauseOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -31,7 +31,7 @@ func NewUnpauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUnpause(dockerCli command.Cli, opts *unpauseOptions) error {
|
||||
func runUnpause(dockerCli *command.DockerCli, opts *unpauseOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
|
||||
@ -35,7 +35,7 @@ type updateOptions struct {
|
||||
}
|
||||
|
||||
// NewUpdateCommand creates a new cobra.Command for `docker update`
|
||||
func NewUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var options updateOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -72,7 +72,7 @@ func NewUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUpdate(dockerCli command.Cli, options *updateOptions) error {
|
||||
func runUpdate(dockerCli *command.DockerCli, options *updateOptions) error {
|
||||
var err error
|
||||
|
||||
if options.nFlag == 0 {
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func waitExitOrRemoved(ctx context.Context, dockerCli command.Cli, containerID string, waitRemove bool) <-chan int {
|
||||
func waitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, containerID string, waitRemove bool) <-chan int {
|
||||
if len(containerID) == 0 {
|
||||
// containerID can never be empty
|
||||
panic("Internal Error: waitExitOrRemoved needs a containerID as parameter")
|
||||
@ -47,7 +47,7 @@ func waitExitOrRemoved(ctx context.Context, dockerCli command.Cli, containerID s
|
||||
return statusC
|
||||
}
|
||||
|
||||
func legacyWaitExitOrRemoved(ctx context.Context, dockerCli command.Cli, containerID string, waitRemove bool) <-chan int {
|
||||
func legacyWaitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, containerID string, waitRemove bool) <-chan int {
|
||||
var removeErr error
|
||||
statusChan := make(chan int)
|
||||
exitCode := 125
|
||||
|
||||
@ -16,7 +16,7 @@ type waitOptions struct {
|
||||
}
|
||||
|
||||
// NewWaitCommand creates a new cobra.Command for `docker wait`
|
||||
func NewWaitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewWaitCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts waitOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -32,7 +32,7 @@ func NewWaitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWait(dockerCli command.Cli, opts *waitOptions) error {
|
||||
func runWait(dockerCli *command.DockerCli, opts *waitOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs []string
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
@ -164,7 +165,7 @@ func (c *containerContext) Image() string {
|
||||
func (c *containerContext) Command() string {
|
||||
command := c.c.Command
|
||||
if c.trunc {
|
||||
command = Ellipsis(command, 20)
|
||||
command = stringutils.Ellipsis(command, 20)
|
||||
}
|
||||
return strconv.Quote(command)
|
||||
}
|
||||
@ -226,7 +227,7 @@ func (c *containerContext) Mounts() string {
|
||||
name = m.Name
|
||||
}
|
||||
if c.trunc {
|
||||
name = Ellipsis(name, 15)
|
||||
name = stringutils.Ellipsis(name, 15)
|
||||
}
|
||||
mounts = append(mounts, name)
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ func TestContainerPsContext(t *testing.T) {
|
||||
Source: "/a/path",
|
||||
},
|
||||
},
|
||||
}, true, "this-is-a-long…", ctx.Mounts},
|
||||
}, true, "this-is-a-lo...", ctx.Mounts},
|
||||
{types.Container{
|
||||
Mounts: []types.MountPoint{
|
||||
{
|
||||
|
||||
@ -118,11 +118,11 @@ func (ctx *DiskUsageContext) Write() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func (ctx *DiskUsageContext) verboseWrite() error {
|
||||
func (ctx *DiskUsageContext) verboseWrite() (err error) {
|
||||
// First images
|
||||
tmpl, err := ctx.startSubsection(defaultDiskUsageImageTableFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Output.Write([]byte("Images space usage:\n\n"))
|
||||
@ -141,14 +141,14 @@ func (ctx *DiskUsageContext) verboseWrite() error {
|
||||
}
|
||||
}
|
||||
|
||||
err := ctx.contextFormat(tmpl, &imageContext{
|
||||
err = ctx.contextFormat(tmpl, &imageContext{
|
||||
repo: repo,
|
||||
tag: tag,
|
||||
trunc: true,
|
||||
i: *i,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.postFormat(tmpl, newImageContext())
|
||||
@ -157,14 +157,17 @@ func (ctx *DiskUsageContext) verboseWrite() error {
|
||||
ctx.Output.Write([]byte("\nContainers space usage:\n\n"))
|
||||
tmpl, err = ctx.startSubsection(defaultDiskUsageContainerTableFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
for _, c := range ctx.Containers {
|
||||
// Don't display the virtual size
|
||||
c.SizeRootFs = 0
|
||||
err := ctx.contextFormat(tmpl, &containerContext{trunc: true, c: *c})
|
||||
err = ctx.contextFormat(tmpl, &containerContext{
|
||||
trunc: true,
|
||||
c: *c,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.postFormat(tmpl, newContainerContext())
|
||||
@ -173,18 +176,21 @@ func (ctx *DiskUsageContext) verboseWrite() error {
|
||||
ctx.Output.Write([]byte("\nLocal Volumes space usage:\n\n"))
|
||||
tmpl, err = ctx.startSubsection(defaultDiskUsageVolumeTableFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
for _, v := range ctx.Volumes {
|
||||
if err := ctx.contextFormat(tmpl, &volumeContext{v: *v}); err != nil {
|
||||
return err
|
||||
err = ctx.contextFormat(tmpl, &volumeContext{
|
||||
v: *v,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.postFormat(tmpl, newVolumeContext())
|
||||
|
||||
// And build cache
|
||||
fmt.Fprintf(ctx.Output, "\nBuild cache usage: %s\n\n", units.HumanSize(float64(ctx.BuilderSize)))
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
type diskUsageImagesContext struct {
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/width"
|
||||
)
|
||||
|
||||
// charWidth returns the number of horizontal positions a character occupies,
|
||||
// and is used to account for wide characters when displaying strings.
|
||||
//
|
||||
// In a broad sense, wide characters include East Asian Wide, East Asian Full-width,
|
||||
// (when not in East Asian context) see http://unicode.org/reports/tr11/.
|
||||
func charWidth(r rune) int {
|
||||
switch width.LookupRune(r).Kind() {
|
||||
case width.EastAsianWide, width.EastAsianFullwidth:
|
||||
return 2
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Ellipsis truncates a string to fit within maxDisplayWidth, and appends ellipsis (…).
|
||||
// For maxDisplayWidth of 1 and lower, no ellipsis is appended.
|
||||
// For maxDisplayWidth of 1, first char of string will return even if its width > 1.
|
||||
func Ellipsis(s string, maxDisplayWidth int) string {
|
||||
if maxDisplayWidth <= 0 {
|
||||
return ""
|
||||
}
|
||||
rs := []rune(s)
|
||||
if maxDisplayWidth == 1 {
|
||||
return string(rs[0])
|
||||
}
|
||||
|
||||
byteLen := len(s)
|
||||
if byteLen == utf8.RuneCountInString(s) {
|
||||
if byteLen <= maxDisplayWidth {
|
||||
return s
|
||||
}
|
||||
return string(rs[:maxDisplayWidth-1]) + "…"
|
||||
}
|
||||
|
||||
var (
|
||||
display []int
|
||||
displayWidth int
|
||||
)
|
||||
for _, r := range rs {
|
||||
cw := charWidth(r)
|
||||
displayWidth += cw
|
||||
display = append(display, displayWidth)
|
||||
}
|
||||
if displayWidth <= maxDisplayWidth {
|
||||
return s
|
||||
}
|
||||
for i := range display {
|
||||
if display[i] <= maxDisplayWidth-1 && display[i+1] > maxDisplayWidth-1 {
|
||||
return string(rs[:i+1]) + "…"
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEllipsis(t *testing.T) {
|
||||
var testcases = []struct {
|
||||
source string
|
||||
width int
|
||||
expected string
|
||||
}{
|
||||
{source: "t🐳ststring", width: 0, expected: ""},
|
||||
{source: "t🐳ststring", width: 1, expected: "t"},
|
||||
{source: "t🐳ststring", width: 2, expected: "t…"},
|
||||
{source: "t🐳ststring", width: 6, expected: "t🐳st…"},
|
||||
{source: "t🐳ststring", width: 20, expected: "t🐳ststring"},
|
||||
{source: "你好世界teststring", width: 0, expected: ""},
|
||||
{source: "你好世界teststring", width: 1, expected: "你"},
|
||||
{source: "你好世界teststring", width: 3, expected: "你…"},
|
||||
{source: "你好世界teststring", width: 6, expected: "你好…"},
|
||||
{source: "你好世界teststring", width: 20, expected: "你好世界teststring"},
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
assert.Equal(t, testcase.expected, Ellipsis(testcase.source, testcase.width))
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
@ -92,7 +93,7 @@ func (c *historyContext) CreatedSince() string {
|
||||
func (c *historyContext) CreatedBy() string {
|
||||
createdBy := strings.Replace(c.h.CreatedBy, "\t", " ", -1)
|
||||
if c.trunc {
|
||||
return Ellipsis(createdBy, 45)
|
||||
return stringutils.Ellipsis(createdBy, 45)
|
||||
}
|
||||
return createdBy
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -95,7 +96,7 @@ func TestHistoryContext_CreatedBy(t *testing.T) {
|
||||
historyContext{
|
||||
h: image.HistoryResponseItem{CreatedBy: withTabs},
|
||||
trunc: true,
|
||||
}, Ellipsis(expected, 45), ctx.CreatedBy,
|
||||
}, stringutils.Ellipsis(expected, 45), ctx.CreatedBy,
|
||||
},
|
||||
}
|
||||
|
||||
@ -190,7 +191,7 @@ imageID3 24 hours ago /bin/bash ls
|
||||
imageID4 24 hours ago /bin/bash grep 183MB Hi
|
||||
`
|
||||
expectedTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT
|
||||
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && kar… 183MB Hi
|
||||
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && k... 183MB Hi
|
||||
imageID2 24 hours ago /bin/bash echo 183MB Hi
|
||||
imageID3 24 hours ago /bin/bash ls 183MB Hi
|
||||
imageID4 24 hours ago /bin/bash grep 183MB Hi
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -79,7 +80,7 @@ func (c *pluginContext) Description() string {
|
||||
desc := strings.Replace(c.p.Config.Description, "\n", "", -1)
|
||||
desc = strings.Replace(desc, "\r", "", -1)
|
||||
if c.trunc {
|
||||
desc = Ellipsis(desc, 45)
|
||||
desc = stringutils.Ellipsis(desc, 45)
|
||||
}
|
||||
|
||||
return desc
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
registry "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -72,7 +73,7 @@ func (c *searchContext) Description() string {
|
||||
desc := strings.Replace(c.s.Description, "\n", " ", -1)
|
||||
desc = strings.Replace(desc, "\r", " ", -1)
|
||||
if c.trunc {
|
||||
desc = Ellipsis(desc, 45)
|
||||
desc = stringutils.Ellipsis(desc, 45)
|
||||
}
|
||||
return desc
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/gotestyourself/gotestyourself/golden"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@ -78,7 +79,7 @@ func TestSearchContextDescription(t *testing.T) {
|
||||
{searchContext{
|
||||
s: registrytypes.SearchResult{Description: longDescription},
|
||||
trunc: true,
|
||||
}, Ellipsis(longDescription, 45), ctx.Description},
|
||||
}, stringutils.Ellipsis(longDescription, 45), ctx.Description},
|
||||
{searchContext{
|
||||
s: registrytypes.SearchResult{Description: descriptionWReturns},
|
||||
trunc: false,
|
||||
@ -86,7 +87,7 @@ func TestSearchContextDescription(t *testing.T) {
|
||||
{searchContext{
|
||||
s: registrytypes.SearchResult{Description: descriptionWReturns},
|
||||
trunc: true,
|
||||
}, Ellipsis(longDescription, 45), ctx.Description},
|
||||
}, stringutils.Ellipsis(longDescription, 45), ctx.Description},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
||||
@ -128,18 +128,18 @@ func getBuildSharedKey(dir string) (string, error) {
|
||||
return hex.EncodeToString(s[:]), nil
|
||||
}
|
||||
|
||||
func tryNodeIdentifier() string {
|
||||
out := cliconfig.Dir() // return config dir as default on permission error
|
||||
func tryNodeIdentifier() (out string) {
|
||||
out = cliconfig.Dir() // return config dir as default on permission error
|
||||
if err := os.MkdirAll(cliconfig.Dir(), 0700); err == nil {
|
||||
sessionFile := filepath.Join(cliconfig.Dir(), ".buildNodeID")
|
||||
if _, err := os.Lstat(sessionFile); err != nil {
|
||||
if os.IsNotExist(err) { // create a new file with stored randomness
|
||||
b := make([]byte, 32)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return out
|
||||
return
|
||||
}
|
||||
if err := ioutil.WriteFile(sessionFile, []byte(hex.EncodeToString(b)), 0600); err != nil {
|
||||
return out
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,5 +149,5 @@ func tryNodeIdentifier() string {
|
||||
return string(dt)
|
||||
}
|
||||
}
|
||||
return out
|
||||
return
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
||||
return nil
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@ -65,12 +65,12 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
warning = allImageWarning
|
||||
}
|
||||
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
|
||||
return 0, "", nil
|
||||
return
|
||||
}
|
||||
|
||||
report, err := dockerCli.Client().ImagesPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
return
|
||||
}
|
||||
|
||||
if len(report.ImagesDeleted) > 0 {
|
||||
@ -85,7 +85,7 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint6
|
||||
spaceReclaimed = report.SpaceReclaimed
|
||||
}
|
||||
|
||||
return spaceReclaimed, output, nil
|
||||
return
|
||||
}
|
||||
|
||||
// RunPrune calls the Image Prune API
|
||||
|
||||
@ -14,11 +14,11 @@ import (
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@ -84,7 +84,7 @@ func PushTrustedReference(streams command.Streams, repoInfo *registry.Repository
|
||||
if err := jsonmessage.DisplayJSONMessagesToStream(in, streams.Out(), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(streams.Err(), "No tag specified, skipping trust metadata push")
|
||||
fmt.Fprintln(streams.Out(), "No tag specified, skipping trust metadata push")
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -97,14 +97,16 @@ func PushTrustedReference(streams command.Streams, repoInfo *registry.Repository
|
||||
}
|
||||
|
||||
if target == nil {
|
||||
return errors.Errorf("no targets found, please provide a specific tag in order to sign it")
|
||||
fmt.Fprintln(streams.Out(), "No targets found, please provide a specific tag in order to sign it")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Fprintln(streams.Out(), "Signing and pushing trust metadata")
|
||||
|
||||
repo, err := trust.GetNotaryRepository(streams.In(), streams.Out(), command.UserAgent(), repoInfo, &authConfig, "push", "pull")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error establishing connection to trust repository")
|
||||
fmt.Fprintf(streams.Out(), "Error establishing connection to notary repository: %s\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// get the latest repository metadata so we can figure out which roles to sign
|
||||
@ -144,11 +146,11 @@ func PushTrustedReference(streams command.Streams, repoInfo *registry.Repository
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to sign %s:%s", repoInfo.Name.Name(), tag)
|
||||
fmt.Fprintf(streams.Out(), "Failed to sign %q:%s - %s\n", repoInfo.Name.Name(), tag, err.Error())
|
||||
return trust.NotaryError(repoInfo.Name.Name(), err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(streams.Out(), "Successfully signed %s:%s\n", repoInfo.Name.Name(), tag)
|
||||
fmt.Fprintf(streams.Out(), "Successfully signed %q:%s\n", repoInfo.Name.Name(), tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -221,7 +223,8 @@ func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.Image
|
||||
func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth) ([]target, error) {
|
||||
notaryRepo, err := cli.NotaryClient(imgRefAndAuth, trust.ActionsPullOnly)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error establishing connection to trust repository")
|
||||
fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ref := imgRefAndAuth.Reference()
|
||||
@ -236,7 +239,7 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth)
|
||||
for _, tgt := range targets {
|
||||
t, err := convertTarget(tgt.Target)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.Err(), "Skipping target for %q\n", reference.FamiliarName(ref))
|
||||
fmt.Fprintf(cli.Out(), "Skipping target for %q\n", reference.FamiliarName(ref))
|
||||
continue
|
||||
}
|
||||
// Only list tags in the top level targets role or the releases delegation role - ignore
|
||||
@ -262,7 +265,7 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth)
|
||||
return nil, trust.NotaryError(ref.Name(), errors.Errorf("No trust data for %s", tagged.Tag()))
|
||||
}
|
||||
|
||||
logrus.Debugf("retrieving target for %s role", t.Role)
|
||||
logrus.Debugf("retrieving target for %s role\n", t.Role)
|
||||
r, err := convertTarget(t.Target)
|
||||
return []target{r}, err
|
||||
}
|
||||
@ -311,7 +314,8 @@ func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedT
|
||||
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, &authConfig, "pull")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error establishing connection to trust repository")
|
||||
fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
|
||||
@ -328,6 +332,7 @@ func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedT
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
return reference.WithDigest(reference.TrimNamed(ref), r.digest)
|
||||
}
|
||||
|
||||
@ -350,7 +355,7 @@ func TagTrusted(ctx context.Context, cli command.Cli, trustedRef reference.Canon
|
||||
familiarRef := reference.FamiliarString(ref)
|
||||
trustedFamiliarRef := reference.FamiliarString(trustedRef)
|
||||
|
||||
fmt.Fprintf(cli.Err(), "Tagging %s as %s\n", trustedFamiliarRef, familiarRef)
|
||||
fmt.Fprintf(cli.Out(), "Tagging %s as %s\n", trustedFamiliarRef, familiarRef)
|
||||
|
||||
return cli.Client().ImageTag(ctx, trustedFamiliarRef, familiarRef)
|
||||
}
|
||||
|
||||
@ -8,11 +8,11 @@ import (
|
||||
"github.com/docker/cli/cli/trust"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustpinning"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/passphrase"
|
||||
"github.com/theupdateframework/notary/trustpinning"
|
||||
)
|
||||
|
||||
func unsetENV() {
|
||||
|
||||
@ -33,7 +33,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@ -50,12 +50,12 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (output string, err e
|
||||
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
|
||||
|
||||
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
|
||||
return "", nil
|
||||
return
|
||||
}
|
||||
|
||||
report, err := dockerCli.Client().NetworksPrune(context.Background(), pruneFilters)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return
|
||||
}
|
||||
|
||||
if len(report.NetworksDeleted) > 0 {
|
||||
@ -65,7 +65,7 @@ func runPrune(dockerCli command.Cli, options pruneOptions) (output string, err e
|
||||
}
|
||||
}
|
||||
|
||||
return output, nil
|
||||
return
|
||||
}
|
||||
|
||||
// RunPrune calls the Network Prune API
|
||||
|
||||
@ -14,11 +14,11 @@ import (
|
||||
// NewNodeCommand returns a cobra command for `node` subcommands
|
||||
func NewNodeCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "node",
|
||||
Short: "Manage Swarm nodes",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.24"},
|
||||
Use: "node",
|
||||
Short: "Manage Swarm nodes",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.24"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newDemoteCommand(dockerCli),
|
||||
|
||||
@ -7,13 +7,14 @@ import (
|
||||
)
|
||||
|
||||
// NewPluginCommand returns a cobra command for `plugin` subcommands
|
||||
func NewPluginCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// nolint: interfacer
|
||||
func NewPluginCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "plugin",
|
||||
Short: "Manage plugins",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
Use: "plugin",
|
||||
Short: "Manage plugins",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
|
||||
@ -63,7 +63,7 @@ type pluginCreateOptions struct {
|
||||
compress bool
|
||||
}
|
||||
|
||||
func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
options := pluginCreateOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -84,7 +84,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(dockerCli command.Cli, options pluginCreateOptions) error {
|
||||
func runCreate(dockerCli *command.DockerCli, options pluginCreateOptions) error {
|
||||
var (
|
||||
createCtx io.ReadCloser
|
||||
err error
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func newDisableCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newDisableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var force bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -27,7 +27,7 @@ func newDisableCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDisable(dockerCli command.Cli, name string, force bool) error {
|
||||
func runDisable(dockerCli *command.DockerCli, name string, force bool) error {
|
||||
if err := dockerCli.Client().PluginDisable(context.Background(), name, types.PluginDisableOptions{Force: force}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ type enableOpts struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func newEnableCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newEnableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts enableOpts
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -34,7 +34,7 @@ func newEnableCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runEnable(dockerCli command.Cli, opts *enableOpts) error {
|
||||
func runEnable(dockerCli *command.DockerCli, opts *enableOpts) error {
|
||||
name := opts.name
|
||||
if opts.timeout < 0 {
|
||||
return errors.Errorf("negative timeout %d is invalid", opts.timeout)
|
||||
|
||||
@ -13,7 +13,7 @@ type inspectOptions struct {
|
||||
format string
|
||||
}
|
||||
|
||||
func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts inspectOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -31,7 +31,7 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
getRef := func(ref string) (interface{}, []byte, error) {
|
||||
|
||||
@ -31,7 +31,7 @@ func loadPullFlags(opts *pluginOptions, flags *pflag.FlagSet) {
|
||||
command.AddTrustVerificationFlags(flags)
|
||||
}
|
||||
|
||||
func newInstallCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var options pluginOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "install [OPTIONS] PLUGIN [KEY=VALUE...]",
|
||||
@ -57,12 +57,12 @@ type pluginRegistryService struct {
|
||||
registry.Service
|
||||
}
|
||||
|
||||
func (s pluginRegistryService) ResolveRepository(name reference.Named) (*registry.RepositoryInfo, error) {
|
||||
repoInfo, err := s.Service.ResolveRepository(name)
|
||||
func (s pluginRegistryService) ResolveRepository(name reference.Named) (repoInfo *registry.RepositoryInfo, err error) {
|
||||
repoInfo, err = s.Service.ResolveRepository(name)
|
||||
if repoInfo != nil {
|
||||
repoInfo.Class = "plugin"
|
||||
}
|
||||
return repoInfo, err
|
||||
return
|
||||
}
|
||||
|
||||
func newRegistryService() (registry.Service, error) {
|
||||
@ -73,7 +73,7 @@ func newRegistryService() (registry.Service, error) {
|
||||
return pluginRegistryService{Service: svc}, nil
|
||||
}
|
||||
|
||||
func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOptions, cmdName string) (types.PluginInstallOptions, error) {
|
||||
func buildPullConfig(ctx context.Context, dockerCli *command.DockerCli, opts pluginOptions, cmdName string) (types.PluginInstallOptions, error) {
|
||||
// Names with both tag and digest will be treated by the daemon
|
||||
// as a pull by digest with a local name for the tag
|
||||
// (if no local name is provided).
|
||||
@ -130,7 +130,7 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func runInstall(dockerCli command.Cli, opts pluginOptions) error {
|
||||
func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
|
||||
var localName string
|
||||
if opts.localName != "" {
|
||||
aref, err := reference.ParseNormalizedNamed(opts.localName)
|
||||
@ -163,7 +163,7 @@ func runInstall(dockerCli command.Cli, opts pluginOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func acceptPrivileges(dockerCli command.Cli, name string) func(privileges types.PluginPrivileges) (bool, error) {
|
||||
func acceptPrivileges(dockerCli *command.DockerCli, name string) func(privileges types.PluginPrivileges) (bool, error) {
|
||||
return func(privileges types.PluginPrivileges) (bool, error) {
|
||||
fmt.Fprintf(dockerCli.Out(), "Plugin %q is requesting the following privileges:\n", name)
|
||||
for _, privilege := range privileges {
|
||||
|
||||
@ -16,7 +16,7 @@ type listOptions struct {
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
options := listOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -39,7 +39,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runList(dockerCli command.Cli, options listOptions) error {
|
||||
func runList(dockerCli *command.DockerCli, options listOptions) error {
|
||||
plugins, err := dockerCli.Client().PluginList(context.Background(), options.filter.Value())
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -16,7 +16,7 @@ type rmOptions struct {
|
||||
plugins []string
|
||||
}
|
||||
|
||||
func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts rmOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -35,7 +35,7 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(dockerCli command.Cli, opts *rmOptions) error {
|
||||
func runRemove(dockerCli *command.DockerCli, opts *rmOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs cli.Errors
|
||||
|
||||
@ -8,7 +8,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newSetCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newSetCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "set PLUGIN KEY=VALUE [KEY=VALUE...]",
|
||||
Short: "Change settings for a plugin",
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newUpgradeCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newUpgradeCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var options pluginOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "upgrade [OPTIONS] PLUGIN [REMOTE]",
|
||||
@ -26,7 +26,7 @@ func newUpgradeCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
return runUpgrade(dockerCli, options)
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.26"},
|
||||
Tags: map[string]string{"version": "1.26"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@ -35,7 +35,7 @@ func newUpgradeCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUpgrade(dockerCli command.Cli, opts pluginOptions) error {
|
||||
func runUpgrade(dockerCli *command.DockerCli, opts pluginOptions) error {
|
||||
ctx := context.Background()
|
||||
p, _, err := dockerCli.Client().PluginInspectWithRaw(ctx, opts.localName)
|
||||
if err != nil {
|
||||
|
||||
@ -8,13 +8,14 @@ import (
|
||||
)
|
||||
|
||||
// NewSecretCommand returns a cobra command for `secret` subcommands
|
||||
func NewSecretCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// nolint: interfacer
|
||||
func NewSecretCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "secret",
|
||||
Short: "Manage Docker secrets",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
Use: "secret",
|
||||
Short: "Manage Docker secrets",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newSecretListCommand(dockerCli),
|
||||
|
||||
@ -14,7 +14,6 @@ type fakeClient struct {
|
||||
serviceInspectWithRawFunc func(ctx context.Context, serviceID string, options types.ServiceInspectOptions) (swarm.Service, []byte, error)
|
||||
serviceUpdateFunc func(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error)
|
||||
serviceListFunc func(context.Context, types.ServiceListOptions) ([]swarm.Service, error)
|
||||
taskListFunc func(context.Context, types.TaskListOptions) ([]swarm.Task, error)
|
||||
infoFunc func(ctx context.Context) (types.Info, error)
|
||||
}
|
||||
|
||||
@ -23,9 +22,6 @@ func (f *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions
|
||||
}
|
||||
|
||||
func (f *fakeClient) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
if f.taskListFunc != nil {
|
||||
return f.taskListFunc(ctx, options)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@ -8,13 +8,14 @@ import (
|
||||
)
|
||||
|
||||
// NewServiceCommand returns a cobra command for `service` subcommands
|
||||
func NewServiceCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// nolint: interfacer
|
||||
func NewServiceCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "service",
|
||||
Short: "Manage services",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.24"},
|
||||
Use: "service",
|
||||
Short: "Manage services",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.24"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCreateCommand(dockerCli),
|
||||
|
||||
@ -54,7 +54,7 @@ func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
getRef := func(ref string) (interface{}, []byte, error) {
|
||||
// Service inspect shows defaults values in empty fields.
|
||||
service, _, err := client.ServiceInspectWithRaw(ctx, ref, types.ServiceInspectOptions{InsertDefaults: true})
|
||||
if err == nil || !apiclient.IsErrNotFound(err) {
|
||||
if err == nil || !apiclient.IsErrServiceNotFound(err) {
|
||||
return service, nil, err
|
||||
}
|
||||
return nil, nil, errors.Errorf("Error: no such service: %s", ref)
|
||||
@ -62,7 +62,7 @@ func runInspect(dockerCli command.Cli, opts inspectOptions) error {
|
||||
|
||||
getNetwork := func(ref string) (interface{}, []byte, error) {
|
||||
network, _, err := client.NetworkInspectWithRaw(ctx, ref, types.NetworkInspectOptions{Scope: "swarm"})
|
||||
if err == nil || !apiclient.IsErrNotFound(err) {
|
||||
if err == nil || !apiclient.IsErrNetworkNotFound(err) {
|
||||
return network, nil, err
|
||||
}
|
||||
return nil, nil, errors.Errorf("Error: no such network: %s", ref)
|
||||
|
||||
@ -37,7 +37,7 @@ type logsOptions struct {
|
||||
target string
|
||||
}
|
||||
|
||||
func newLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newLogsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts logsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -48,7 +48,7 @@ func newLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts.target = args[0]
|
||||
return runLogs(dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.29"},
|
||||
Tags: map[string]string{"version": "1.29"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@ -68,7 +68,7 @@ func newLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runLogs(dockerCli command.Cli, opts *logsOptions) error {
|
||||
func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
options := types.ContainerLogsOptions{
|
||||
@ -97,12 +97,12 @@ func runLogs(dockerCli command.Cli, opts *logsOptions) error {
|
||||
service, _, err := cli.ServiceInspectWithRaw(ctx, opts.target, types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
// if it's any error other than service not found, it's Real
|
||||
if !client.IsErrNotFound(err) {
|
||||
if !client.IsErrServiceNotFound(err) {
|
||||
return err
|
||||
}
|
||||
task, _, err := cli.TaskInspectWithRaw(ctx, opts.target)
|
||||
if err != nil {
|
||||
if client.IsErrNotFound(err) {
|
||||
if client.IsErrTaskNotFound(err) {
|
||||
// if the task isn't found, rewrite the error to be clear
|
||||
// that we looked for services AND tasks and found none
|
||||
err = fmt.Errorf("no such task or service: %v", opts.target)
|
||||
|
||||
@ -354,7 +354,7 @@ func convertNetworks(ctx context.Context, apiClient client.NetworkAPIClient, net
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
netAttach = append(netAttach, swarm.NetworkAttachmentConfig{
|
||||
netAttach = append(netAttach, swarm.NetworkAttachmentConfig{ // nolint: gosimple
|
||||
Target: net.Target,
|
||||
Aliases: net.Aliases,
|
||||
DriverOpts: net.DriverOpts,
|
||||
|
||||
@ -69,9 +69,6 @@ func runPS(dockerCli command.Cli, options psOptions) error {
|
||||
if len(format) == 0 {
|
||||
format = task.DefaultFormat(dockerCli.ConfigFile(), options.quiet)
|
||||
}
|
||||
if options.quiet {
|
||||
options.noTrunc = true
|
||||
}
|
||||
if err := task.Print(ctx, dockerCli, tasks, idresolver.New(client, options.noResolve), !options.noTrunc, options.quiet, format); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -90,22 +90,6 @@ func TestRunPSWarnsOnNotFound(t *testing.T) {
|
||||
assert.EqualError(t, err, "no such service: bar")
|
||||
}
|
||||
|
||||
func TestRunPSQuiet(t *testing.T) {
|
||||
client := &fakeClient{
|
||||
serviceListFunc: func(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{{ID: "foo"}}, nil
|
||||
},
|
||||
taskListFunc: func(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
|
||||
return []swarm.Task{{ID: "sxabyp0obqokwekpun4rjo0b3"}}, nil
|
||||
},
|
||||
}
|
||||
|
||||
cli := test.NewFakeCli(client)
|
||||
err := runPS(cli, psOptions{services: []string{"foo"}, quiet: true, filter: opts.NewFilterOpt()})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "sxabyp0obqokwekpun4rjo0b3\n", cli.OutBuffer().String())
|
||||
}
|
||||
|
||||
func TestUpdateNodeFilter(t *testing.T) {
|
||||
selfNodeID := "foofoo"
|
||||
filter := filters.NewArgs()
|
||||
|
||||
@ -21,7 +21,7 @@ func newRollbackCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runRollback(dockerCli, options, args[0])
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.31"},
|
||||
Tags: map[string]string{"version": "1.31"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
@ -8,10 +8,10 @@ import (
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
|
||||
@ -92,7 +92,7 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.SetAnnotation(flagDNSOptionAdd, "version", []string{"1.25"})
|
||||
flags.Var(&options.dnsSearch, flagDNSSearchAdd, "Add or update a custom DNS search domain")
|
||||
flags.SetAnnotation(flagDNSSearchAdd, "version", []string{"1.25"})
|
||||
flags.Var(&options.hosts, flagHostAdd, "Add a custom host-to-IP mapping (host:ip)")
|
||||
flags.Var(&options.hosts, flagHostAdd, "Add or update a custom host-to-IP mapping (host:ip)")
|
||||
flags.SetAnnotation(flagHostAdd, "version", []string{"1.25"})
|
||||
|
||||
return cmd
|
||||
@ -868,10 +868,6 @@ func updateReplicas(flags *pflag.FlagSet, serviceMode *swarm.ServiceMode) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateHosts performs a diff between existing host entries, entries to be
|
||||
// removed, and entries to be added. Host entries preserve the order in which they
|
||||
// were added, as the specification mentions that in case multiple entries for a
|
||||
// host exist, the first entry should be used (by default).
|
||||
func updateHosts(flags *pflag.FlagSet, hosts *[]string) error {
|
||||
// Combine existing Hosts (in swarmkit format) with the host to add (convert to swarmkit format)
|
||||
if flags.Changed(flagHostAdd) {
|
||||
@ -906,6 +902,9 @@ func updateHosts(flags *pflag.FlagSet, hosts *[]string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Sort so that result is predictable.
|
||||
sort.Strings(newHosts)
|
||||
|
||||
*hosts = newHosts
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/internal/test/testutil"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
mounttypes "github.com/docker/docker/api/types/mount"
|
||||
@ -166,7 +165,7 @@ func TestUpdateDNSConfig(t *testing.T) {
|
||||
// IPv6
|
||||
flags.Set("dns-add", "2001:db8:abc8::1")
|
||||
// Invalid dns record
|
||||
testutil.ErrorContains(t, flags.Set("dns-add", "x.y.z.w"), "x.y.z.w is not an ip address")
|
||||
assert.EqualError(t, flags.Set("dns-add", "x.y.z.w"), "x.y.z.w is not an ip address")
|
||||
|
||||
// domains with duplicates
|
||||
flags.Set("dns-search-add", "example.com")
|
||||
@ -174,7 +173,7 @@ func TestUpdateDNSConfig(t *testing.T) {
|
||||
flags.Set("dns-search-add", "example.org")
|
||||
flags.Set("dns-search-rm", "example.org")
|
||||
// Invalid dns search domain
|
||||
testutil.ErrorContains(t, flags.Set("dns-search-add", "example$com"), "example$com is not a valid domain")
|
||||
assert.EqualError(t, flags.Set("dns-search-add", "example$com"), "example$com is not a valid domain")
|
||||
|
||||
flags.Set("dns-option-add", "ndots:9")
|
||||
flags.Set("dns-option-rm", "timeout:3")
|
||||
@ -363,26 +362,15 @@ func TestUpdateHosts(t *testing.T) {
|
||||
// just hostname should work as well
|
||||
flags.Set("host-rm", "example.net")
|
||||
// bad format error
|
||||
testutil.ErrorContains(t, flags.Set("host-add", "$example.com$"), `bad format for add-host: "$example.com$"`)
|
||||
assert.EqualError(t, flags.Set("host-add", "$example.com$"), `bad format for add-host: "$example.com$"`)
|
||||
|
||||
hosts := []string{"1.2.3.4 example.com", "4.3.2.1 example.org", "2001:db8:abc8::1 example.net"}
|
||||
expected := []string{"1.2.3.4 example.com", "4.3.2.1 example.org", "2001:db8:abc8::1 ipv6.net"}
|
||||
|
||||
err := updateHosts(flags, &hosts)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, hosts)
|
||||
}
|
||||
|
||||
func TestUpdateHostsPreservesOrder(t *testing.T) {
|
||||
flags := newUpdateCommand(nil).Flags()
|
||||
flags.Set("host-add", "foobar:127.0.0.2")
|
||||
flags.Set("host-add", "foobar:127.0.0.1")
|
||||
flags.Set("host-add", "foobar:127.0.0.3")
|
||||
|
||||
hosts := []string{}
|
||||
err := updateHosts(flags, &hosts)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"127.0.0.2 foobar", "127.0.0.1 foobar", "127.0.0.3 foobar"}, hosts)
|
||||
updateHosts(flags, &hosts)
|
||||
require.Len(t, hosts, 3)
|
||||
assert.Equal(t, "1.2.3.4 example.com", hosts[0])
|
||||
assert.Equal(t, "2001:db8:abc8::1 ipv6.net", hosts[1])
|
||||
assert.Equal(t, "4.3.2.1 example.org", hosts[2])
|
||||
}
|
||||
|
||||
func TestUpdatePortsRmWithProtocol(t *testing.T) {
|
||||
|
||||
@ -7,13 +7,14 @@ import (
|
||||
)
|
||||
|
||||
// NewStackCommand returns a cobra command for `stack` subcommands
|
||||
func NewStackCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// nolint: interfacer
|
||||
func NewStackCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "stack",
|
||||
Short: "Manage Docker stacks",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
Use: "stack",
|
||||
Short: "Manage Docker stacks",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newDeployCommand(dockerCli),
|
||||
@ -30,6 +31,6 @@ func NewTopLevelDeployCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := newDeployCommand(dockerCli)
|
||||
// Remove the aliases at the top level
|
||||
cmd.Aliases = []string{}
|
||||
cmd.Annotations = map[string]string{"experimental": "", "version": "1.25"}
|
||||
cmd.Tags = map[string]string{"experimental": "", "version": "1.25"}
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -222,9 +222,8 @@ func createSecrets(
|
||||
if err := client.SecretUpdate(ctx, secret.ID, secret.Meta.Version, secretSpec); err != nil {
|
||||
return errors.Wrapf(err, "failed to update secret %s", secretSpec.Name)
|
||||
}
|
||||
case apiclient.IsErrNotFound(err):
|
||||
case apiclient.IsErrSecretNotFound(err):
|
||||
// secret does not exist, then we create a new one.
|
||||
fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secretSpec.Name)
|
||||
if _, err := client.SecretCreate(ctx, secretSpec); err != nil {
|
||||
return errors.Wrapf(err, "failed to create secret %s", secretSpec.Name)
|
||||
}
|
||||
@ -250,9 +249,8 @@ func createConfigs(
|
||||
if err := client.ConfigUpdate(ctx, config.ID, config.Meta.Version, configSpec); err != nil {
|
||||
errors.Wrapf(err, "failed to update config %s", configSpec.Name)
|
||||
}
|
||||
case apiclient.IsErrNotFound(err):
|
||||
case apiclient.IsErrConfigNotFound(err):
|
||||
// config does not exist, then we create a new one.
|
||||
fmt.Fprintf(dockerCli.Out(), "Creating config %s\n", configSpec.Name)
|
||||
if _, err := client.ConfigCreate(ctx, configSpec); err != nil {
|
||||
errors.Wrapf(err, "failed to create config %s", configSpec.Name)
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ func newCACommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runCA(dockerCli, cmd.Flags(), opts)
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.30"},
|
||||
Tags: map[string]string{"version": "1.30"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
@ -8,13 +8,14 @@ import (
|
||||
)
|
||||
|
||||
// NewSwarmCommand returns a cobra command for `swarm` subcommands
|
||||
// nolint: interfacer
|
||||
func NewSwarmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "swarm",
|
||||
Short: "Manage Swarm",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Annotations: map[string]string{"version": "1.24"},
|
||||
Use: "swarm",
|
||||
Short: "Manage Swarm",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
Tags: map[string]string{"version": "1.24"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newInitCommand(dockerCli),
|
||||
|
||||
@ -8,7 +8,6 @@ Flags:
|
||||
--cert-expiry duration Validity period for node certificates (ns|us|ms|s|m|h) (default 2160h0m0s)
|
||||
--dispatcher-heartbeat duration Dispatcher heartbeat period (ns|us|ms|s|m|h) (default 5s)
|
||||
--external-ca external-ca Specifications of one or more certificate signing endpoints
|
||||
-h, --help help for update
|
||||
--max-snapshots uint Number of additional Raft snapshots to retain
|
||||
--snapshot-interval uint Number of log entries between Raft snapshots (default 10000)
|
||||
--task-history-limit int Task history retention limit (default 5)
|
||||
|
||||
@ -7,7 +7,8 @@ import (
|
||||
)
|
||||
|
||||
// NewSystemCommand returns a cobra command for `system` subcommands
|
||||
func NewSystemCommand(dockerCli command.Cli) *cobra.Command {
|
||||
// nolint: interfacer
|
||||
func NewSystemCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "system",
|
||||
Short: "Manage Docker",
|
||||
|
||||
@ -16,7 +16,7 @@ type diskUsageOptions struct {
|
||||
}
|
||||
|
||||
// newDiskUsageCommand creates a new cobra.Command for `docker df`
|
||||
func newDiskUsageCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newDiskUsageCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts diskUsageOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -26,7 +26,7 @@ func newDiskUsageCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runDiskUsage(dockerCli, opts)
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@ -37,7 +37,7 @@ func newDiskUsageCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDiskUsage(dockerCli command.Cli, opts diskUsageOptions) error {
|
||||
func runDiskUsage(dockerCli *command.DockerCli, opts diskUsageOptions) error {
|
||||
if opts.verbose && len(opts.format) != 0 {
|
||||
return errors.New("the verbose and the format options conflict")
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ type eventsOptions struct {
|
||||
}
|
||||
|
||||
// NewEventsCommand creates a new cobra.Command for `docker events`
|
||||
func NewEventsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewEventsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
options := eventsOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -48,7 +48,7 @@ func NewEventsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runEvents(dockerCli command.Cli, options *eventsOptions) error {
|
||||
func runEvents(dockerCli *command.DockerCli, options *eventsOptions) error {
|
||||
tmpl, err := makeTemplate(options.format)
|
||||
if err != nil {
|
||||
return cli.StatusError{
|
||||
|
||||
@ -22,7 +22,7 @@ type infoOptions struct {
|
||||
}
|
||||
|
||||
// NewInfoCommand creates a new cobra.Command for `docker info`
|
||||
func NewInfoCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func NewInfoCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts infoOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -41,7 +41,7 @@ func NewInfoCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInfo(dockerCli command.Cli, opts *infoOptions) error {
|
||||
func runInfo(dockerCli *command.DockerCli, opts *infoOptions) error {
|
||||
ctx := context.Background()
|
||||
info, err := dockerCli.Client().Info(ctx)
|
||||
if err != nil {
|
||||
@ -357,7 +357,7 @@ func getBackingFs(info types.Info) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func formatInfo(dockerCli command.Cli, info types.Info, format string) error {
|
||||
func formatInfo(dockerCli *command.DockerCli, info types.Info, format string) error {
|
||||
tmpl, err := templates.Parse(format)
|
||||
if err != nil {
|
||||
return cli.StatusError{StatusCode: 64,
|
||||
|
||||
@ -37,7 +37,7 @@ func newPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runPrune(dockerCli, options)
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
@ -2,14 +2,14 @@ package trust
|
||||
|
||||
import (
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/client/changelist"
|
||||
"github.com/theupdateframework/notary/cryptoservice"
|
||||
"github.com/theupdateframework/notary/passphrase"
|
||||
"github.com/theupdateframework/notary/storage"
|
||||
"github.com/theupdateframework/notary/trustmanager"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/signed"
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/client/changelist"
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/storage"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/docker/notary/tuf/signed"
|
||||
)
|
||||
|
||||
// Sample mock CLI interfaces
|
||||
@ -316,39 +316,7 @@ func (l LoadedNotaryRepository) ListRoles() ([]client.RoleWithSignatures, error)
|
||||
Name: data.CanonicalTargetsRole,
|
||||
}
|
||||
|
||||
aliceRole := data.Role{
|
||||
RootRole: data.RootRole{
|
||||
KeyIDs: []string{"A"},
|
||||
Threshold: 1,
|
||||
},
|
||||
Name: data.RoleName("targets/alice"),
|
||||
}
|
||||
|
||||
bobRole := data.Role{
|
||||
RootRole: data.RootRole{
|
||||
KeyIDs: []string{"B"},
|
||||
Threshold: 1,
|
||||
},
|
||||
Name: data.RoleName("targets/bob"),
|
||||
}
|
||||
|
||||
releasesRole := data.Role{
|
||||
RootRole: data.RootRole{
|
||||
KeyIDs: []string{"A", "B"},
|
||||
Threshold: 1,
|
||||
},
|
||||
Name: data.RoleName("targets/releases"),
|
||||
}
|
||||
// have releases only signed off by Alice last
|
||||
releasesSig := []data.Signature{{KeyID: "A"}}
|
||||
|
||||
return []client.RoleWithSignatures{
|
||||
{Role: rootRole},
|
||||
{Role: targetsRole},
|
||||
{Role: aliceRole},
|
||||
{Role: bobRole},
|
||||
{Role: releasesRole, Signatures: releasesSig},
|
||||
}, nil
|
||||
return []client.RoleWithSignatures{{Role: rootRole}, {Role: targetsRole}}, nil
|
||||
}
|
||||
|
||||
func (l LoadedNotaryRepository) ListTargets(roles ...data.RoleName) ([]*client.TargetWithRole, error) {
|
||||
|
||||
@ -18,8 +18,6 @@ func NewTrustCommand(dockerCli command.Cli) *cobra.Command {
|
||||
newViewCommand(dockerCli),
|
||||
newRevokeCommand(dockerCli),
|
||||
newSignCommand(dockerCli),
|
||||
newTrustKeyCommand(dockerCli),
|
||||
newTrustSignerCommand(dockerCli),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -4,20 +4,18 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
)
|
||||
|
||||
const releasedRoleName = "Repo Admin"
|
||||
const releasesRoleTUFName = "targets/releases"
|
||||
|
||||
// isReleasedTarget checks if a role name is "released":
|
||||
// either targets/releases or targets TUF roles
|
||||
// check if a role name is "released": either targets/releases or targets TUF roles
|
||||
func isReleasedTarget(role data.RoleName) bool {
|
||||
return role == data.CanonicalTargetsRole || role == trust.ReleasesRole
|
||||
}
|
||||
|
||||
// notaryRoleToSigner converts TUF role name to a human-understandable signer name
|
||||
// convert TUF role name to a human-understandable signer name
|
||||
func notaryRoleToSigner(tufRole data.RoleName) string {
|
||||
// don't show a signer for "targets" or "targets/releases"
|
||||
if isReleasedTarget(data.RoleName(tufRole.String())) {
|
||||
@ -26,7 +24,6 @@ func notaryRoleToSigner(tufRole data.RoleName) string {
|
||||
return strings.TrimPrefix(tufRole.String(), "targets/")
|
||||
}
|
||||
|
||||
// clearChangelist clears the notary staging changelist.
|
||||
func clearChangeList(notaryRepo client.Repository) error {
|
||||
cl, err := notaryRepo.GetChangelist()
|
||||
if err != nil {
|
||||
@ -34,14 +31,3 @@ func clearChangeList(notaryRepo client.Repository) error {
|
||||
}
|
||||
return cl.Clear("")
|
||||
}
|
||||
|
||||
// getOrGenerateRootKeyAndInitRepo initializes the notary repository
|
||||
// with a remotely managed snapshot key. The initialization will use
|
||||
// an existing root key if one is found, else a new one will be generated.
|
||||
func getOrGenerateRootKeyAndInitRepo(notaryRepo client.Repository) error {
|
||||
rootKey, err := getOrGenerateNotaryKey(notaryRepo, data.CanonicalRootRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return notaryRepo.Initialize([]string{rootKey.ID()}, data.CanonicalSnapshotRole)
|
||||
}
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/passphrase"
|
||||
"github.com/theupdateframework/notary/trustpinning"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetOrGenerateNotaryKeyAndInitRepo(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "notary-test-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
notaryRepo, err := client.NewFileCachedRepository(tmpDir, "gun", "https://localhost", nil, passphrase.ConstantRetriever(passwd), trustpinning.TrustPinConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = getOrGenerateRootKeyAndInitRepo(notaryRepo)
|
||||
assert.EqualError(t, err, "client is offline")
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// newTrustKeyCommand returns a cobra command for `trust key` subcommands
|
||||
func newTrustKeyCommand(dockerCli command.Streams) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "key",
|
||||
Short: "Manage keys for signing Docker images (experimental)",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newKeyGenerateCommand(dockerCli),
|
||||
newKeyLoadCommand(dockerCli),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
@ -1,134 +0,0 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/trustmanager"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
tufutils "github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
type keyGenerateOptions struct {
|
||||
name string
|
||||
directory string
|
||||
}
|
||||
|
||||
func newKeyGenerateCommand(dockerCli command.Streams) *cobra.Command {
|
||||
options := keyGenerateOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "generate NAME",
|
||||
Short: "Generate and load a signing key-pair",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.name = args[0]
|
||||
return setupPassphraseAndGenerateKeys(dockerCli, options)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&options.directory, "dir", "", "Directory to generate key in, defaults to current directory")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// key names can use lowercase alphanumeric + _ + - characters
|
||||
var validKeyName = regexp.MustCompile(`^[a-z0-9][a-z0-9\_\-]*$`).MatchString
|
||||
|
||||
// validate that all of the key names are unique and are alphanumeric + _ + -
|
||||
// and that we do not already have public key files in the target dir on disk
|
||||
func validateKeyArgs(keyName string, targetDir string) error {
|
||||
if !validKeyName(keyName) {
|
||||
return fmt.Errorf("key name \"%s\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", keyName)
|
||||
}
|
||||
|
||||
pubKeyFileName := keyName + ".pub"
|
||||
if _, err := os.Stat(targetDir); err != nil {
|
||||
return fmt.Errorf("public key path does not exist: \"%s\"", targetDir)
|
||||
}
|
||||
targetPath := filepath.Join(targetDir, pubKeyFileName)
|
||||
if _, err := os.Stat(targetPath); err == nil {
|
||||
return fmt.Errorf("public key file already exists: \"%s\"", targetPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupPassphraseAndGenerateKeys(streams command.Streams, opts keyGenerateOptions) error {
|
||||
targetDir := opts.directory
|
||||
if targetDir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetDir = cwd
|
||||
}
|
||||
return validateAndGenerateKey(streams, opts.name, targetDir)
|
||||
}
|
||||
|
||||
func validateAndGenerateKey(streams command.Streams, keyName string, workingDir string) error {
|
||||
freshPassRetGetter := func() notary.PassRetriever { return trust.GetPassphraseRetriever(streams.In(), streams.Out()) }
|
||||
if err := validateKeyArgs(keyName, workingDir); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(streams.Out(), "Generating key for %s...\n", keyName)
|
||||
// Automatically load the private key to local storage for use
|
||||
privKeyFileStore, err := trustmanager.NewKeyFileStore(trust.GetTrustDirectory(), freshPassRetGetter())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pubPEM, err := generateKeyAndOutputPubPEM(keyName, privKeyFileStore)
|
||||
if err != nil {
|
||||
fmt.Fprintf(streams.Out(), err.Error())
|
||||
return errors.Wrapf(err, "failed to generate key for %s", keyName)
|
||||
}
|
||||
|
||||
// Output the public key to a file in the CWD or specified dir
|
||||
writtenPubFile, err := writePubKeyPEMToDir(pubPEM, keyName, workingDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(streams.Out(), "Successfully generated and loaded private key. Corresponding public key available: %s\n", writtenPubFile)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateKeyAndOutputPubPEM(keyName string, privKeyStore trustmanager.KeyStore) (pem.Block, error) {
|
||||
privKey, err := tufutils.GenerateKey(data.ECDSAKey)
|
||||
if err != nil {
|
||||
return pem.Block{}, err
|
||||
}
|
||||
|
||||
privKeyStore.AddKey(trustmanager.KeyInfo{Role: data.RoleName(keyName)}, privKey)
|
||||
if err != nil {
|
||||
return pem.Block{}, err
|
||||
}
|
||||
|
||||
pubKey := data.PublicKeyFromPrivate(privKey)
|
||||
return pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Headers: map[string]string{
|
||||
"role": keyName,
|
||||
},
|
||||
Bytes: pubKey.Public(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func writePubKeyPEMToDir(pubPEM pem.Block, keyName, workingDir string) (string, error) {
|
||||
// Output the public key to a file in the CWD or specified dir
|
||||
pubFileName := strings.Join([]string{keyName, "pub"}, ".")
|
||||
pubFilePath := filepath.Join(workingDir, pubFileName)
|
||||
if err := ioutil.WriteFile(pubFilePath, pem.EncodeToMemory(&pubPEM), notary.PrivNoExecPerms); err != nil {
|
||||
return "", errors.Wrapf(err, "failed to write public key to %s", pubFilePath)
|
||||
}
|
||||
return pubFilePath, nil
|
||||
}
|
||||
@ -1,138 +0,0 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/cli/internal/test/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/passphrase"
|
||||
"github.com/theupdateframework/notary/trustmanager"
|
||||
tufutils "github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
func TestTrustKeyGenerateErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "not-enough-args",
|
||||
expectedError: "requires exactly 1 argument",
|
||||
},
|
||||
{
|
||||
name: "too-many-args",
|
||||
args: []string{"key-1", "key-2"},
|
||||
expectedError: "requires exactly 1 argument",
|
||||
},
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "docker-key-generate-test-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
config.SetDir(tmpDir)
|
||||
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
cmd := newKeyGenerateCommand(cli)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateKeySuccess(t *testing.T) {
|
||||
pubKeyCWD, err := ioutil.TempDir("", "pub-keys-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(pubKeyCWD)
|
||||
|
||||
privKeyStorageDir, err := ioutil.TempDir("", "priv-keys-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(privKeyStorageDir)
|
||||
|
||||
passwd := "password"
|
||||
cannedPasswordRetriever := passphrase.ConstantRetriever(passwd)
|
||||
// generate a single key
|
||||
keyName := "alice"
|
||||
privKeyFileStore, err := trustmanager.NewKeyFileStore(privKeyStorageDir, cannedPasswordRetriever)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pubKeyPEM, err := generateKeyAndOutputPubPEM(keyName, privKeyFileStore)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, keyName, pubKeyPEM.Headers["role"])
|
||||
// the default GUN is empty
|
||||
assert.Equal(t, "", pubKeyPEM.Headers["gun"])
|
||||
// assert public key header
|
||||
assert.Equal(t, "PUBLIC KEY", pubKeyPEM.Type)
|
||||
|
||||
// check that an appropriate ~/<trust_dir>/private/<key_id>.key file exists
|
||||
expectedPrivKeyDir := filepath.Join(privKeyStorageDir, notary.PrivDir)
|
||||
_, err = os.Stat(expectedPrivKeyDir)
|
||||
assert.NoError(t, err)
|
||||
|
||||
keyFiles, err := ioutil.ReadDir(expectedPrivKeyDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, keyFiles, 1)
|
||||
privKeyFilePath := filepath.Join(expectedPrivKeyDir, keyFiles[0].Name())
|
||||
|
||||
// verify the key content
|
||||
privFrom, _ := os.OpenFile(privKeyFilePath, os.O_RDONLY, notary.PrivExecPerms)
|
||||
defer privFrom.Close()
|
||||
fromBytes, _ := ioutil.ReadAll(privFrom)
|
||||
privKeyPEM, _ := pem.Decode(fromBytes)
|
||||
assert.Equal(t, keyName, privKeyPEM.Headers["role"])
|
||||
// the default GUN is empty
|
||||
assert.Equal(t, "", privKeyPEM.Headers["gun"])
|
||||
// assert encrypted header
|
||||
assert.Equal(t, "ENCRYPTED PRIVATE KEY", privKeyPEM.Type)
|
||||
// check that the passphrase matches
|
||||
_, err = tufutils.ParsePKCS8ToTufKey(privKeyPEM.Bytes, []byte(passwd))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// check that the public key exists at the correct path if we use the helper:
|
||||
returnedPath, err := writePubKeyPEMToDir(pubKeyPEM, keyName, pubKeyCWD)
|
||||
assert.NoError(t, err)
|
||||
expectedPubKeyPath := filepath.Join(pubKeyCWD, keyName+".pub")
|
||||
assert.Equal(t, returnedPath, expectedPubKeyPath)
|
||||
_, err = os.Stat(expectedPubKeyPath)
|
||||
assert.NoError(t, err)
|
||||
// check that the public key is the only file output in CWD
|
||||
cwdKeyFiles, err := ioutil.ReadDir(pubKeyCWD)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, cwdKeyFiles, 1)
|
||||
}
|
||||
|
||||
func TestValidateKeyArgs(t *testing.T) {
|
||||
pubKeyCWD, err := ioutil.TempDir("", "pub-keys-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(pubKeyCWD)
|
||||
|
||||
err = validateKeyArgs("a", pubKeyCWD)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = validateKeyArgs("a/b", pubKeyCWD)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err.Error(), "key name \"a/b\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character")
|
||||
|
||||
err = validateKeyArgs("-", pubKeyCWD)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err.Error(), "key name \"-\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character")
|
||||
|
||||
assert.NoError(t, ioutil.WriteFile(filepath.Join(pubKeyCWD, "a.pub"), []byte("abc"), notary.PrivExecPerms))
|
||||
err = validateKeyArgs("a", pubKeyCWD)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err.Error(), fmt.Sprintf("public key file already exists: \"%s/a.pub\"", pubKeyCWD))
|
||||
|
||||
err = validateKeyArgs("a", "/random/dir/")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err.Error(), "public key path does not exist: \"/random/dir/\"")
|
||||
}
|
||||
@ -1,115 +0,0 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/storage"
|
||||
"github.com/theupdateframework/notary/trustmanager"
|
||||
tufutils "github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
nonOwnerReadWriteMask = 0077
|
||||
)
|
||||
|
||||
type keyLoadOptions struct {
|
||||
keyName string
|
||||
}
|
||||
|
||||
func newKeyLoadCommand(dockerCli command.Streams) *cobra.Command {
|
||||
var options keyLoadOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "load [OPTIONS] KEYFILE",
|
||||
Short: "Load a private key file for signing",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return loadPrivKey(dockerCli, args[0], options)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&options.keyName, "name", "signer", "Name for the loaded key")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func loadPrivKey(streams command.Streams, keyPath string, options keyLoadOptions) error {
|
||||
// validate the key name if provided
|
||||
if options.keyName != "" && !validKeyName(options.keyName) {
|
||||
return fmt.Errorf("key name \"%s\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", options.keyName)
|
||||
}
|
||||
trustDir := trust.GetTrustDirectory()
|
||||
keyFileStore, err := storage.NewPrivateKeyFileStorage(trustDir, notary.KeyExtension)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
privKeyImporters := []trustmanager.Importer{keyFileStore}
|
||||
|
||||
fmt.Fprintf(streams.Out(), "Loading key from \"%s\"...\n", keyPath)
|
||||
|
||||
// Always use a fresh passphrase retriever for each import
|
||||
passRet := trust.GetPassphraseRetriever(streams.In(), streams.Out())
|
||||
keyBytes, err := getPrivKeyBytesFromPath(keyPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "refusing to load key from %s", keyPath)
|
||||
}
|
||||
if err := loadPrivKeyBytesToStore(keyBytes, privKeyImporters, keyPath, options.keyName, passRet); err != nil {
|
||||
return errors.Wrapf(err, "error importing key from %s", keyPath)
|
||||
}
|
||||
fmt.Fprintf(streams.Out(), "Successfully imported key from %s\n", keyPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPrivKeyBytesFromPath(keyPath string) ([]byte, error) {
|
||||
fileInfo, err := os.Stat(keyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fileInfo.Mode()&nonOwnerReadWriteMask != 0 {
|
||||
return nil, fmt.Errorf("private key file %s must not be readable or writable by others", keyPath)
|
||||
}
|
||||
|
||||
from, err := os.OpenFile(keyPath, os.O_RDONLY, notary.PrivExecPerms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer from.Close()
|
||||
|
||||
return ioutil.ReadAll(from)
|
||||
}
|
||||
|
||||
func loadPrivKeyBytesToStore(privKeyBytes []byte, privKeyImporters []trustmanager.Importer, keyPath, keyName string, passRet notary.PassRetriever) error {
|
||||
var err error
|
||||
if _, _, err = tufutils.ExtractPrivateKeyAttributes(privKeyBytes); err != nil {
|
||||
return fmt.Errorf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", keyPath)
|
||||
}
|
||||
if privKeyBytes, err = decodePrivKeyIfNecessary(privKeyBytes, passRet); err != nil {
|
||||
return errors.Wrapf(err, "cannot load key from provided file %s", keyPath)
|
||||
}
|
||||
// Make a reader, rewind the file pointer
|
||||
return trustmanager.ImportKeys(bytes.NewReader(privKeyBytes), privKeyImporters, keyName, "", passRet)
|
||||
}
|
||||
|
||||
func decodePrivKeyIfNecessary(privPemBytes []byte, passRet notary.PassRetriever) ([]byte, error) {
|
||||
pemBlock, _ := pem.Decode(privPemBytes)
|
||||
_, containsDEKInfo := pemBlock.Headers["DEK-Info"]
|
||||
if containsDEKInfo || pemBlock.Type == "ENCRYPTED PRIVATE KEY" {
|
||||
// if we do not have enough information to properly import, try to decrypt the key
|
||||
if _, ok := pemBlock.Headers["path"]; !ok {
|
||||
privKey, _, err := trustmanager.GetPasswdDecryptBytes(passRet, privPemBytes, "", "encrypted")
|
||||
if err != nil {
|
||||
return []byte{}, fmt.Errorf("could not decrypt key")
|
||||
}
|
||||
privPemBytes = privKey.Private()
|
||||
}
|
||||
}
|
||||
return privPemBytes, nil
|
||||
}
|
||||
@ -1,244 +0,0 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/cli/internal/test/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/passphrase"
|
||||
"github.com/theupdateframework/notary/storage"
|
||||
"github.com/theupdateframework/notary/trustmanager"
|
||||
tufutils "github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
func TestTrustKeyLoadErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectedError string
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
name: "not-enough-args",
|
||||
expectedError: "exactly 1 argument",
|
||||
expectedOutput: "",
|
||||
},
|
||||
{
|
||||
name: "too-many-args",
|
||||
args: []string{"iamnotakey", "alsonotakey"},
|
||||
expectedError: "exactly 1 argument",
|
||||
expectedOutput: "",
|
||||
},
|
||||
{
|
||||
name: "not-a-key",
|
||||
args: []string{"iamnotakey"},
|
||||
expectedError: "refusing to load key from iamnotakey: stat iamnotakey: no such file or directory",
|
||||
expectedOutput: "Loading key from \"iamnotakey\"...\n",
|
||||
},
|
||||
{
|
||||
name: "bad-key-name",
|
||||
args: []string{"iamnotakey", "--name", "KEYNAME"},
|
||||
expectedError: "key name \"KEYNAME\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character",
|
||||
expectedOutput: "",
|
||||
},
|
||||
}
|
||||
tmpDir, err := ioutil.TempDir("", "docker-key-load-test-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
config.SetDir(tmpDir)
|
||||
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
cmd := newKeyLoadCommand(cli)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
assert.Contains(t, cli.OutBuffer().String(), tc.expectedOutput)
|
||||
}
|
||||
}
|
||||
|
||||
var rsaPrivKeyFixture = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAs7yVMzCw8CBZPoN+QLdx3ZzbVaHnouHIKu+ynX60IZ3stpbb
|
||||
6rowu78OWON252JcYJqe++2GmdIgbBhg+mZDwhX0ZibMVztJaZFsYL+Ch/2J9KqD
|
||||
A5NtE1s/XdhYoX5hsv7W4ok9jLFXRYIMj+T4exJRlR4f4GP9p0fcqPWd9/enPnlJ
|
||||
JFTmu0DXJTZUMVS1UrXUy5t/DPXdrwyl8pM7VCqO3bqK7jqE6mWawdTkEeiku1fJ
|
||||
ydP0285uiYTbj1Q38VVhPwXzMuLbkaUgRJhCI4BcjfQIjtJLbWpS+VdhUEvtgMVx
|
||||
XJMKxCVGG69qjXyj9TjI7pxanb/bWglhovJN9wIDAQABAoIBAQCSnMsLxbUfOxPx
|
||||
RWuwOLN+NZxIvtfnastQEtSdWiRvo5Xa3zYmw5hLHa8DXRC57+cwug/jqr54LQpb
|
||||
gotg1hiBck05In7ezTK2FXTVeoJskal91bUnLpP0DSOkVnz9xszFKNF6Wr7FTEfH
|
||||
IC1FF16Fbcz0mW0hKg9X6+uYOzqPcKpQRwli5LAwhT18Alf9h4/3NCeKotiJyr2J
|
||||
xvcEH1eY2m2c/jQZurBkys7qBC3+i8LJEOW8MBQt7mxajwfbU91wtP2YoqMcoYiS
|
||||
zsPbYp7Ui2t4G9Yn+OJw+uj4RGP1Bo4nSyRxWDtg+8Zug/JYU6/s+8kVRpiGffd3
|
||||
T1GvoxUhAoGBAOnPDWG/g1xlJf65Rh71CxMs638zhYbIloU2K4Rqr05DHe7GryTS
|
||||
9hLVrwhHddK+KwfVbR8HFMPo1DC/NVbuKt8StTAadAu3HsC088gWd28nOiGAWuvH
|
||||
Bo3x/DYQGYwGFfoo4rzCOgMj6DJjXmcWEXNv3NDMoXoYpkxa0g6zZDyHAoGBAMTL
|
||||
t7EUneJT+Mm7wyL1I5bmaT/HFwqoUQB2ccBPVD8p1el62NgLdfhOa8iNlBVhMrlh
|
||||
2aTjrMlSPcjr9sCgKrLcenSWw+2qFsf4+SmV01ntB9kWes2phXpnB0ynXIcbeG05
|
||||
+BLxbqDTVV0Iqh4r/dGeplyV2WyL3mTpkT3hRq8RAoGAZ93degEUICWnHWO9LN97
|
||||
Dge0joua0+ekRoVsC6VBP6k9UOfewqMdQfy/hxQH2Zk1kINVuKTyqp1yNj2bOoUP
|
||||
co3jA/2cc9/jv4QjkE26vRxWDK/ytC90T/aiLno0fyns9XbYUzaNgvuemVPfijgZ
|
||||
hIi7Nd7SFWWB6wWlr3YuH10CgYEAwh7JVa2mh8iZEjVaKTNyJbmmfDjgq6yYKkKr
|
||||
ti0KRzv3O9Xn7ERx27tPaobtWaGFLYQt8g57NCMhuv23aw8Sz1fYmwTUw60Rx7P5
|
||||
42FdF8lOAn/AJvpfJfxXIO+9v7ADPIr//3+TxqRwAdM4K4btWkaKh61wyTe26gfT
|
||||
MxzyYmECgYAnlU5zsGyiZqwoXVktkhtZrE7Qu0SoztzFb8KpvFNmMTPF1kAAYmJY
|
||||
GIhbizeGJ3h4cUdozKmt8ZWIt6uFDEYCqEA7XF4RH75dW25x86mpIPO7iRl9eisY
|
||||
IsLeMYqTIwXAwGx6Ka9v5LOL1kzcHQ2iVj6+QX+yoptSft1dYa9jOA==
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
const rsaPrivKeyID = "ee69e8e07a14756ad5ff0aca2336b37f86b0ac1710d1f3e94440081e080aecd7"
|
||||
|
||||
var ecPrivKeyFixture = []byte(`-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEINfxKtDH3ug7ZIQPDyeAzujCdhw36D+bf9ToPE1A7YEyoAoGCCqGSM49
|
||||
AwEHoUQDQgAEUIH9AYtrcDFzZrFJBdJZkn21d+4cH3nzy2O6Q/ct4BjOBKa+WCdR
|
||||
tPo78bA+C/7t81ADQO8Jqaj59W50rwoqDQ==
|
||||
-----END EC PRIVATE KEY-----`)
|
||||
|
||||
const ecPrivKeyID = "46157cb0becf9c72c3219e11d4692424fef9bf4460812ccc8a71a3dfcafc7e60"
|
||||
|
||||
var testKeys = map[string][]byte{
|
||||
ecPrivKeyID: ecPrivKeyFixture,
|
||||
rsaPrivKeyID: rsaPrivKeyFixture,
|
||||
}
|
||||
|
||||
func TestLoadKeyFromPath(t *testing.T) {
|
||||
for keyID, keyBytes := range testKeys {
|
||||
t.Run(fmt.Sprintf("load-key-id-%s-from-path", keyID), func(t *testing.T) {
|
||||
testLoadKeyFromPath(t, keyID, keyBytes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testLoadKeyFromPath(t *testing.T, privKeyID string, privKeyFixture []byte) {
|
||||
privKeyDir, err := ioutil.TempDir("", "key-load-test-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(privKeyDir)
|
||||
privKeyFilepath := filepath.Join(privKeyDir, "privkey.pem")
|
||||
assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, notary.PrivNoExecPerms))
|
||||
|
||||
keyStorageDir, err := ioutil.TempDir("", "loaded-keys-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(keyStorageDir)
|
||||
|
||||
passwd := "password"
|
||||
cannedPasswordRetriever := passphrase.ConstantRetriever(passwd)
|
||||
keyFileStore, err := storage.NewPrivateKeyFileStorage(keyStorageDir, notary.KeyExtension)
|
||||
assert.NoError(t, err)
|
||||
privKeyImporters := []trustmanager.Importer{keyFileStore}
|
||||
|
||||
// get the privKeyBytes
|
||||
privKeyBytes, err := getPrivKeyBytesFromPath(privKeyFilepath)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// import the key to our keyStorageDir
|
||||
assert.NoError(t, loadPrivKeyBytesToStore(privKeyBytes, privKeyImporters, privKeyFilepath, "signer-name", cannedPasswordRetriever))
|
||||
|
||||
// check that the appropriate ~/<trust_dir>/private/<key_id>.key file exists
|
||||
expectedImportKeyPath := filepath.Join(keyStorageDir, notary.PrivDir, privKeyID+"."+notary.KeyExtension)
|
||||
_, err = os.Stat(expectedImportKeyPath)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// verify the key content
|
||||
from, _ := os.OpenFile(expectedImportKeyPath, os.O_RDONLY, notary.PrivExecPerms)
|
||||
defer from.Close()
|
||||
fromBytes, _ := ioutil.ReadAll(from)
|
||||
keyPEM, _ := pem.Decode(fromBytes)
|
||||
assert.Equal(t, "signer-name", keyPEM.Headers["role"])
|
||||
// the default GUN is empty
|
||||
assert.Equal(t, "", keyPEM.Headers["gun"])
|
||||
// assert encrypted header
|
||||
assert.Equal(t, "ENCRYPTED PRIVATE KEY", keyPEM.Type)
|
||||
|
||||
decryptedKey, err := tufutils.ParsePKCS8ToTufKey(keyPEM.Bytes, []byte(passwd))
|
||||
assert.NoError(t, err)
|
||||
fixturePEM, _ := pem.Decode(privKeyFixture)
|
||||
assert.Equal(t, fixturePEM.Bytes, decryptedKey.Private())
|
||||
}
|
||||
|
||||
func TestLoadKeyTooPermissive(t *testing.T) {
|
||||
for keyID, keyBytes := range testKeys {
|
||||
t.Run(fmt.Sprintf("load-key-id-%s-too-permissive", keyID), func(t *testing.T) {
|
||||
testLoadKeyTooPermissive(t, keyBytes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testLoadKeyTooPermissive(t *testing.T, privKeyFixture []byte) {
|
||||
privKeyDir, err := ioutil.TempDir("", "key-load-test-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(privKeyDir)
|
||||
privKeyFilepath := filepath.Join(privKeyDir, "privkey477.pem")
|
||||
assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0477))
|
||||
|
||||
keyStorageDir, err := ioutil.TempDir("", "loaded-keys-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(keyStorageDir)
|
||||
|
||||
// import the key to our keyStorageDir
|
||||
_, err = getPrivKeyBytesFromPath(privKeyFilepath)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath), err.Error())
|
||||
|
||||
privKeyFilepath = filepath.Join(privKeyDir, "privkey667.pem")
|
||||
assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0677))
|
||||
|
||||
_, err = getPrivKeyBytesFromPath(privKeyFilepath)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath), err.Error())
|
||||
|
||||
privKeyFilepath = filepath.Join(privKeyDir, "privkey777.pem")
|
||||
assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0777))
|
||||
|
||||
_, err = getPrivKeyBytesFromPath(privKeyFilepath)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath), err.Error())
|
||||
|
||||
privKeyFilepath = filepath.Join(privKeyDir, "privkey400.pem")
|
||||
assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0400))
|
||||
|
||||
_, err = getPrivKeyBytesFromPath(privKeyFilepath)
|
||||
assert.NoError(t, err)
|
||||
|
||||
privKeyFilepath = filepath.Join(privKeyDir, "privkey600.pem")
|
||||
assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0600))
|
||||
|
||||
_, err = getPrivKeyBytesFromPath(privKeyFilepath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
var pubKeyFixture = []byte(`-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUIH9AYtrcDFzZrFJBdJZkn21d+4c
|
||||
H3nzy2O6Q/ct4BjOBKa+WCdRtPo78bA+C/7t81ADQO8Jqaj59W50rwoqDQ==
|
||||
-----END PUBLIC KEY-----`)
|
||||
|
||||
func TestLoadPubKeyFailure(t *testing.T) {
|
||||
pubKeyDir, err := ioutil.TempDir("", "key-load-test-pubkey-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(pubKeyDir)
|
||||
pubKeyFilepath := filepath.Join(pubKeyDir, "pubkey.pem")
|
||||
assert.NoError(t, ioutil.WriteFile(pubKeyFilepath, pubKeyFixture, notary.PrivNoExecPerms))
|
||||
keyStorageDir, err := ioutil.TempDir("", "loaded-keys-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(keyStorageDir)
|
||||
|
||||
passwd := "password"
|
||||
cannedPasswordRetriever := passphrase.ConstantRetriever(passwd)
|
||||
keyFileStore, err := storage.NewPrivateKeyFileStorage(keyStorageDir, notary.KeyExtension)
|
||||
assert.NoError(t, err)
|
||||
privKeyImporters := []trustmanager.Importer{keyFileStore}
|
||||
|
||||
pubKeyBytes, err := getPrivKeyBytesFromPath(pubKeyFilepath)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// import the key to our keyStorageDir - it should fail
|
||||
err = loadPrivKeyBytesToStore(pubKeyBytes, privKeyImporters, pubKeyFilepath, "signer-name", cannedPasswordRetriever)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, fmt.Sprintf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", pubKeyFilepath), err.Error())
|
||||
}
|
||||
@ -9,10 +9,10 @@ import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/image"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
type revokeOptions struct {
|
||||
|
||||
@ -7,11 +7,11 @@ import (
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/cli/internal/test/testutil"
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustpinning"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/passphrase"
|
||||
"github.com/theupdateframework/notary/trustpinning"
|
||||
)
|
||||
|
||||
func TestTrustRevokeCommandErrors(t *testing.T) {
|
||||
|
||||
@ -12,10 +12,10 @@ import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/image"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
func newSignCommand(dockerCli command.Cli) *cobra.Command {
|
||||
@ -99,9 +99,9 @@ func signAndPublishToTarget(out io.Writer, imgRefAndAuth trust.ImageRefAndAuth,
|
||||
err = notaryRepo.Publish()
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to sign %s:%s", imgRefAndAuth.RepoInfo().Name.Name(), tag)
|
||||
return errors.Wrapf(err, "failed to sign %q:%s", imgRefAndAuth.RepoInfo().Name.Name(), tag)
|
||||
}
|
||||
fmt.Fprintf(out, "Successfully signed %s:%s\n", imgRefAndAuth.RepoInfo().Name.Name(), tag)
|
||||
fmt.Fprintf(out, "Successfully signed %q:%s\n", imgRefAndAuth.RepoInfo().Name.Name(), tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -183,9 +183,7 @@ func initNotaryRepoWithSigners(notaryRepo client.Repository, newSigner data.Role
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := addStagedSigner(notaryRepo, newSigner, []data.PublicKey{signerKey}); err != nil {
|
||||
return errors.Wrapf(err, "could not add signer to repo: %s", strings.TrimPrefix(newSigner.String(), "targets/"))
|
||||
}
|
||||
addStagedSigner(notaryRepo, newSigner, []data.PublicKey{signerKey})
|
||||
|
||||
return notaryRepo.Publish()
|
||||
}
|
||||
@ -218,21 +216,12 @@ func getOrGenerateNotaryKey(notaryRepo client.Repository, role data.RoleName) (d
|
||||
}
|
||||
|
||||
// stages changes to add a signer with the specified name and key(s). Adds to targets/<name> and targets/releases
|
||||
func addStagedSigner(notaryRepo client.Repository, newSigner data.RoleName, signerKeys []data.PublicKey) error {
|
||||
func addStagedSigner(notaryRepo client.Repository, newSigner data.RoleName, signerKeys []data.PublicKey) {
|
||||
// create targets/<username>
|
||||
if err := notaryRepo.AddDelegationRoleAndKeys(newSigner, signerKeys); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := notaryRepo.AddDelegationPaths(newSigner, []string{""}); err != nil {
|
||||
return err
|
||||
}
|
||||
notaryRepo.AddDelegationRoleAndKeys(newSigner, signerKeys)
|
||||
notaryRepo.AddDelegationPaths(newSigner, []string{""})
|
||||
|
||||
// create targets/releases
|
||||
if err := notaryRepo.AddDelegationRoleAndKeys(trust.ReleasesRole, signerKeys); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := notaryRepo.AddDelegationPaths(trust.ReleasesRole, []string{""}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
notaryRepo.AddDelegationRoleAndKeys(trust.ReleasesRole, signerKeys)
|
||||
notaryRepo.AddDelegationPaths(trust.ReleasesRole, []string{""})
|
||||
}
|
||||
|
||||
@ -12,14 +12,14 @@ import (
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/cli/internal/test/testutil"
|
||||
"github.com/docker/notary"
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/client/changelist"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustpinning"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/client/changelist"
|
||||
"github.com/theupdateframework/notary/passphrase"
|
||||
"github.com/theupdateframework/notary/trustpinning"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
const passwd = "password"
|
||||
@ -140,8 +140,7 @@ func TestAddStageSigners(t *testing.T) {
|
||||
// stage targets/user
|
||||
userRole := data.RoleName("targets/user")
|
||||
userKey := data.NewPublicKey("algoA", []byte("a"))
|
||||
err = addStagedSigner(notaryRepo, userRole, []data.PublicKey{userKey})
|
||||
assert.NoError(t, err)
|
||||
addStagedSigner(notaryRepo, userRole, []data.PublicKey{userKey})
|
||||
// check the changelist for four total changes: two on targets/releases and two on targets/user
|
||||
cl, err := notaryRepo.GetChangelist()
|
||||
assert.NoError(t, err)
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// newTrustSignerCommand returns a cobra command for `trust signer` subcommands
|
||||
func newTrustSignerCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "signer",
|
||||
Short: "Manage entities who can sign Docker images (experimental)",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newSignerAddCommand(dockerCli),
|
||||
newSignerRemoveCommand(dockerCli),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user