mirror of https://github.com/docker/cli.git
Merge pull request #1633 from silvin-lubecki/refactor-docker-cli-construction
Introduce functional arguments to NewDockerCli for a more stable API.
This commit is contained in:
commit
f95ca8e1ba
|
@ -19,12 +19,15 @@ import (
|
||||||
cliflags "github.com/docker/cli/cli/flags"
|
cliflags "github.com/docker/cli/cli/flags"
|
||||||
manifeststore "github.com/docker/cli/cli/manifest/store"
|
manifeststore "github.com/docker/cli/cli/manifest/store"
|
||||||
registryclient "github.com/docker/cli/cli/registry/client"
|
registryclient "github.com/docker/cli/cli/registry/client"
|
||||||
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/cli/cli/trust"
|
"github.com/docker/cli/cli/trust"
|
||||||
|
"github.com/docker/cli/internal/containerizedengine"
|
||||||
dopts "github.com/docker/cli/opts"
|
dopts "github.com/docker/cli/opts"
|
||||||
clitypes "github.com/docker/cli/types"
|
clitypes "github.com/docker/cli/types"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
registrytypes "github.com/docker/docker/api/types/registry"
|
registrytypes "github.com/docker/docker/api/types/registry"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
"github.com/docker/docker/pkg/term"
|
||||||
"github.com/docker/go-connections/tlsconfig"
|
"github.com/docker/go-connections/tlsconfig"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -35,18 +38,19 @@ import (
|
||||||
|
|
||||||
// Streams is an interface which exposes the standard input and output streams
|
// Streams is an interface which exposes the standard input and output streams
|
||||||
type Streams interface {
|
type Streams interface {
|
||||||
In() *InStream
|
In() *streams.In
|
||||||
Out() *OutStream
|
Out() *streams.Out
|
||||||
Err() io.Writer
|
Err() io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cli represents the docker command line client.
|
// Cli represents the docker command line client.
|
||||||
type Cli interface {
|
type Cli interface {
|
||||||
Client() client.APIClient
|
Client() client.APIClient
|
||||||
Out() *OutStream
|
Out() *streams.Out
|
||||||
Err() io.Writer
|
Err() io.Writer
|
||||||
In() *InStream
|
In() *streams.In
|
||||||
SetIn(in *InStream)
|
SetIn(in *streams.In)
|
||||||
|
Apply(ops ...DockerCliOption) error
|
||||||
ConfigFile() *configfile.ConfigFile
|
ConfigFile() *configfile.ConfigFile
|
||||||
ServerInfo() ServerInfo
|
ServerInfo() ServerInfo
|
||||||
ClientInfo() ClientInfo
|
ClientInfo() ClientInfo
|
||||||
|
@ -66,8 +70,8 @@ type Cli interface {
|
||||||
// Instances of the client can be returned from NewDockerCli.
|
// Instances of the client can be returned from NewDockerCli.
|
||||||
type DockerCli struct {
|
type DockerCli struct {
|
||||||
configFile *configfile.ConfigFile
|
configFile *configfile.ConfigFile
|
||||||
in *InStream
|
in *streams.In
|
||||||
out *OutStream
|
out *streams.Out
|
||||||
err io.Writer
|
err io.Writer
|
||||||
client client.APIClient
|
client client.APIClient
|
||||||
serverInfo ServerInfo
|
serverInfo ServerInfo
|
||||||
|
@ -96,7 +100,7 @@ func (cli *DockerCli) Client() client.APIClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Out returns the writer used for stdout
|
// Out returns the writer used for stdout
|
||||||
func (cli *DockerCli) Out() *OutStream {
|
func (cli *DockerCli) Out() *streams.Out {
|
||||||
return cli.out
|
return cli.out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,12 +110,12 @@ func (cli *DockerCli) Err() io.Writer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetIn sets the reader used for stdin
|
// SetIn sets the reader used for stdin
|
||||||
func (cli *DockerCli) SetIn(in *InStream) {
|
func (cli *DockerCli) SetIn(in *streams.In) {
|
||||||
cli.in = in
|
cli.in = in
|
||||||
}
|
}
|
||||||
|
|
||||||
// In returns the reader used for stdin
|
// In returns the reader used for stdin
|
||||||
func (cli *DockerCli) In() *InStream {
|
func (cli *DockerCli) In() *streams.In {
|
||||||
return cli.in
|
return cli.in
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,6 +397,16 @@ func (cli *DockerCli) DockerEndpoint() docker.Endpoint {
|
||||||
return cli.dockerEndpoint
|
return cli.dockerEndpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply all the operation on the cli
|
||||||
|
func (cli *DockerCli) Apply(ops ...DockerCliOption) error {
|
||||||
|
for _, op := range ops {
|
||||||
|
if err := op(cli); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ServerInfo stores details about the supported features and platform of the
|
// ServerInfo stores details about the supported features and platform of the
|
||||||
// server
|
// server
|
||||||
type ServerInfo struct {
|
type ServerInfo struct {
|
||||||
|
@ -407,9 +421,32 @@ type ClientInfo struct {
|
||||||
DefaultVersion string
|
DefaultVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err.
|
// NewDockerCli returns a DockerCli instance with all operators applied on it.
|
||||||
func NewDockerCli(in io.ReadCloser, out, err io.Writer, isTrusted bool, containerizedFn func(string) (clitypes.ContainerizedClient, error)) *DockerCli {
|
// It applies by default the standard streams, the content trust from
|
||||||
return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err, contentTrust: isTrusted, newContainerizeClient: containerizedFn}
|
// environment and the default containerized client constructor operations.
|
||||||
|
func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) {
|
||||||
|
cli := &DockerCli{}
|
||||||
|
defaultOps := []DockerCliOption{
|
||||||
|
WithContentTrustFromEnv(),
|
||||||
|
WithContainerizedClient(containerizedengine.NewClient),
|
||||||
|
}
|
||||||
|
ops = append(defaultOps, ops...)
|
||||||
|
if err := cli.Apply(ops...); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if cli.out == nil || cli.in == nil || cli.err == nil {
|
||||||
|
stdin, stdout, stderr := term.StdStreams()
|
||||||
|
if cli.in == nil {
|
||||||
|
cli.in = streams.NewIn(stdin)
|
||||||
|
}
|
||||||
|
if cli.out == nil {
|
||||||
|
cli.out = streams.NewOut(stdout)
|
||||||
|
}
|
||||||
|
if cli.err == nil {
|
||||||
|
cli.err = stderr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cli, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error) {
|
func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error) {
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/streams"
|
||||||
|
clitypes "github.com/docker/cli/types"
|
||||||
|
"github.com/docker/docker/pkg/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DockerCliOption applies a modification on a DockerCli.
|
||||||
|
type DockerCliOption func(cli *DockerCli) error
|
||||||
|
|
||||||
|
// WithStandardStreams sets a cli in, out and err streams with the standard streams.
|
||||||
|
func WithStandardStreams() DockerCliOption {
|
||||||
|
return func(cli *DockerCli) error {
|
||||||
|
// Set terminal emulation based on platform as required.
|
||||||
|
stdin, stdout, stderr := term.StdStreams()
|
||||||
|
cli.in = streams.NewIn(stdin)
|
||||||
|
cli.out = streams.NewOut(stdout)
|
||||||
|
cli.err = stderr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCombinedStreams uses the same stream for the output and error streams.
|
||||||
|
func WithCombinedStreams(combined io.Writer) DockerCliOption {
|
||||||
|
return func(cli *DockerCli) error {
|
||||||
|
cli.out = streams.NewOut(combined)
|
||||||
|
cli.err = combined
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithInputStream sets a cli input stream.
|
||||||
|
func WithInputStream(in io.ReadCloser) DockerCliOption {
|
||||||
|
return func(cli *DockerCli) error {
|
||||||
|
cli.in = streams.NewIn(in)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOutputStream sets a cli output stream.
|
||||||
|
func WithOutputStream(out io.Writer) DockerCliOption {
|
||||||
|
return func(cli *DockerCli) error {
|
||||||
|
cli.out = streams.NewOut(out)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithErrorStream sets a cli error stream.
|
||||||
|
func WithErrorStream(err io.Writer) DockerCliOption {
|
||||||
|
return func(cli *DockerCli) error {
|
||||||
|
cli.err = err
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContentTrustFromEnv enables content trust on a cli from environment variable DOCKER_CONTENT_TRUST value.
|
||||||
|
func WithContentTrustFromEnv() DockerCliOption {
|
||||||
|
return func(cli *DockerCli) error {
|
||||||
|
cli.contentTrust = false
|
||||||
|
if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" {
|
||||||
|
if t, err := strconv.ParseBool(e); t || err != nil {
|
||||||
|
// treat any other value as true
|
||||||
|
cli.contentTrust = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContentTrust enables content trust on a cli.
|
||||||
|
func WithContentTrust(enabled bool) DockerCliOption {
|
||||||
|
return func(cli *DockerCli) error {
|
||||||
|
cli.contentTrust = enabled
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContainerizedClient sets the containerized client constructor on a cli.
|
||||||
|
func WithContainerizedClient(containerizedFn func(string) (clitypes.ContainerizedClient, error)) DockerCliOption {
|
||||||
|
return func(cli *DockerCli) error {
|
||||||
|
cli.newContainerizeClient = containerizedFn
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -10,6 +13,7 @@ import (
|
||||||
cliconfig "github.com/docker/cli/cli/config"
|
cliconfig "github.com/docker/cli/cli/config"
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/cli/flags"
|
"github.com/docker/cli/cli/flags"
|
||||||
|
clitypes "github.com/docker/cli/types"
|
||||||
"github.com/docker/docker/api"
|
"github.com/docker/docker/api"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
@ -247,3 +251,44 @@ func TestGetClientWithPassword(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewDockerCliAndOperators(t *testing.T) {
|
||||||
|
// Test default operations and also overriding default ones
|
||||||
|
cli, err := NewDockerCli(
|
||||||
|
WithContentTrust(true),
|
||||||
|
WithContainerizedClient(func(string) (clitypes.ContainerizedClient, error) { return nil, nil }),
|
||||||
|
)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
// Check streams are initialized
|
||||||
|
assert.Check(t, cli.In() != nil)
|
||||||
|
assert.Check(t, cli.Out() != nil)
|
||||||
|
assert.Check(t, cli.Err() != nil)
|
||||||
|
assert.Equal(t, cli.ContentTrustEnabled(), true)
|
||||||
|
client, err := cli.NewContainerizedEngineClient("")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, client, nil)
|
||||||
|
|
||||||
|
// Apply can modify a dockerCli after construction
|
||||||
|
inbuf := bytes.NewBuffer([]byte("input"))
|
||||||
|
outbuf := bytes.NewBuffer(nil)
|
||||||
|
errbuf := bytes.NewBuffer(nil)
|
||||||
|
cli.Apply(
|
||||||
|
WithInputStream(ioutil.NopCloser(inbuf)),
|
||||||
|
WithOutputStream(outbuf),
|
||||||
|
WithErrorStream(errbuf),
|
||||||
|
)
|
||||||
|
// Check input stream
|
||||||
|
inputStream, err := ioutil.ReadAll(cli.In())
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, string(inputStream), "input")
|
||||||
|
// Check output stream
|
||||||
|
fmt.Fprintf(cli.Out(), "output")
|
||||||
|
outputStream, err := ioutil.ReadAll(outbuf)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, string(outputStream), "output")
|
||||||
|
// Check error stream
|
||||||
|
fmt.Fprintf(cli.Err(), "error")
|
||||||
|
errStream, err := ioutil.ReadAll(errbuf)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, string(errStream), "error")
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/streams"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ func TestExportImportPipe(t *testing.T) {
|
||||||
dest: "-",
|
dest: "-",
|
||||||
}))
|
}))
|
||||||
assert.Equal(t, cli.ErrBuffer().String(), "")
|
assert.Equal(t, cli.ErrBuffer().String(), "")
|
||||||
cli.SetIn(command.NewInStream(ioutil.NopCloser(bytes.NewBuffer(cli.OutBuffer().Bytes()))))
|
cli.SetIn(streams.NewIn(ioutil.NopCloser(bytes.NewBuffer(cli.OutBuffer().Bytes()))))
|
||||||
cli.OutBuffer().Reset()
|
cli.OutBuffer().Reset()
|
||||||
cli.ErrBuffer().Reset()
|
cli.ErrBuffer().Reset()
|
||||||
assert.NilError(t, runImport(cli, "test2", "-"))
|
assert.NilError(t, runImport(cli, "test2", "-"))
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
@ -39,7 +39,7 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
|
||||||
FROM alpine:3.6
|
FROM alpine:3.6
|
||||||
COPY foo /
|
COPY foo /
|
||||||
`)
|
`)
|
||||||
cli.SetIn(command.NewInStream(ioutil.NopCloser(dockerfile)))
|
cli.SetIn(streams.NewIn(ioutil.NopCloser(dockerfile)))
|
||||||
|
|
||||||
dir := fs.NewDir(t, t.Name(),
|
dir := fs.NewDir(t, t.Name(),
|
||||||
fs.WithFile("foo", "some content"))
|
fs.WithFile("foo", "some content"))
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/cli/cli/trust"
|
"github.com/docker/cli/cli/trust"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -296,7 +297,7 @@ func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth tru
|
||||||
|
|
||||||
out := cli.Out()
|
out := cli.Out()
|
||||||
if opts.quiet {
|
if opts.quiet {
|
||||||
out = command.NewOutStream(ioutil.Discard)
|
out = streams.NewOut(ioutil.Discard)
|
||||||
}
|
}
|
||||||
return jsonmessage.DisplayJSONMessagesToStream(responseBody, out, nil)
|
return jsonmessage.DisplayJSONMessagesToStream(responseBody, out, nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
package command
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/term"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OutStream is an output stream used by the DockerCli to write normal program
|
|
||||||
// output.
|
|
||||||
type OutStream struct {
|
|
||||||
CommonStream
|
|
||||||
out io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *OutStream) Write(p []byte) (int, error) {
|
|
||||||
return o.out.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRawTerminal sets raw mode on the input terminal
|
|
||||||
func (o *OutStream) SetRawTerminal() (err error) {
|
|
||||||
if os.Getenv("NORAW") != "" || !o.CommonStream.isTerminal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
o.CommonStream.state, err = term.SetRawTerminalOutput(o.CommonStream.fd)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTtySize returns the height and width in characters of the tty
|
|
||||||
func (o *OutStream) GetTtySize() (uint, uint) {
|
|
||||||
if !o.isTerminal {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
ws, err := term.GetWinsize(o.fd)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Debugf("Error getting size: %s", err)
|
|
||||||
if ws == nil {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uint(ws.Height), uint(ws.Width)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewOutStream returns a new OutStream object from a Writer
|
|
||||||
func NewOutStream(out io.Writer) *OutStream {
|
|
||||||
fd, isTerminal := term.GetFdInfo(out)
|
|
||||||
return &OutStream{CommonStream: CommonStream{fd: fd, isTerminal: isTerminal}, out: out}
|
|
||||||
}
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/debug"
|
"github.com/docker/cli/cli/debug"
|
||||||
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
registrytypes "github.com/docker/docker/api/types/registry"
|
registrytypes "github.com/docker/docker/api/types/registry"
|
||||||
|
@ -101,7 +102,7 @@ func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, is
|
||||||
func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *types.AuthConfig, isDefaultRegistry bool) error {
|
func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *types.AuthConfig, isDefaultRegistry bool) error {
|
||||||
// On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
|
// On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
cli.SetIn(NewInStream(os.Stdin))
|
cli.SetIn(streams.NewIn(os.Stdin))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some links documenting this:
|
// Some links documenting this:
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
|
||||||
"github.com/docker/cli/cli/command/stack/options"
|
"github.com/docker/cli/cli/command/stack/options"
|
||||||
composetypes "github.com/docker/cli/cli/compose/types"
|
composetypes "github.com/docker/cli/cli/compose/types"
|
||||||
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/morikuni/aec"
|
"github.com/morikuni/aec"
|
||||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
)
|
)
|
||||||
|
@ -117,7 +117,7 @@ func metaStateFromStatus(status serviceStatus) metaServiceState {
|
||||||
}
|
}
|
||||||
|
|
||||||
type forwardOnlyStatusDisplay struct {
|
type forwardOnlyStatusDisplay struct {
|
||||||
o *command.OutStream
|
o *streams.Out
|
||||||
states map[string]metaServiceState
|
states map[string]metaServiceState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ func (d *forwardOnlyStatusDisplay) OnStatus(status serviceStatus) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type interactiveStatusDisplay struct {
|
type interactiveStatusDisplay struct {
|
||||||
o *command.OutStream
|
o *streams.Out
|
||||||
statuses []serviceStatus
|
statuses []serviceStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ func displayInteractiveServiceStatus(status serviceStatus, o io.Writer) {
|
||||||
status.podsReady, status.podsPending, totalFailed, status.podsTotal)
|
status.podsReady, status.podsPending, totalFailed, status.podsTotal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStatusDisplay(o *command.OutStream) statusDisplay {
|
func newStatusDisplay(o *streams.Out) statusDisplay {
|
||||||
if !o.IsTerminal() {
|
if !o.IsTerminal() {
|
||||||
return &forwardOnlyStatusDisplay{o: o, states: map[string]metaServiceState{}}
|
return &forwardOnlyStatusDisplay{o: o, states: map[string]metaServiceState{}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/cli/cli/streams"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InStream is an input stream used by the DockerCli to read user input
|
||||||
|
// Deprecated: Use github.com/docker/cli/cli/streams.In instead
|
||||||
|
type InStream = streams.In
|
||||||
|
|
||||||
|
// OutStream is an output stream used by the DockerCli to write normal program
|
||||||
|
// output.
|
||||||
|
// Deprecated: Use github.com/docker/cli/cli/streams.Out instead
|
||||||
|
type OutStream = streams.Out
|
||||||
|
|
||||||
|
var (
|
||||||
|
// NewInStream returns a new InStream object from a ReadCloser
|
||||||
|
// Deprecated: Use github.com/docker/cli/cli/streams.NewIn instead
|
||||||
|
NewInStream = streams.NewIn
|
||||||
|
// NewOutStream returns a new OutStream object from a Writer
|
||||||
|
// Deprecated: Use github.com/docker/cli/cli/streams.NewOut instead
|
||||||
|
NewOutStream = streams.NewOut
|
||||||
|
)
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -59,7 +60,7 @@ func runUnlock(dockerCli command.Cli) error {
|
||||||
return client.SwarmUnlock(ctx, req)
|
return client.SwarmUnlock(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readKey(in *command.InStream, prompt string) (string, error) {
|
func readKey(in *streams.In, prompt string) (string, error) {
|
||||||
if in.IsTerminal() {
|
if in.IsTerminal() {
|
||||||
fmt.Print(prompt)
|
fmt.Print(prompt)
|
||||||
dt, err := terminal.ReadPassword(int(in.FD()))
|
dt, err := terminal.ReadPassword(int(in.FD()))
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
@ -92,7 +92,7 @@ func TestSwarmUnlock(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
dockerCli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input))))
|
dockerCli.SetIn(streams.NewIn(ioutil.NopCloser(strings.NewReader(input))))
|
||||||
cmd := newUnlockCommand(dockerCli)
|
cmd := newUnlockCommand(dockerCli)
|
||||||
assert.NilError(t, cmd.Execute())
|
assert.NilError(t, cmd.Execute())
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
@ -80,7 +81,7 @@ func PromptForConfirmation(ins io.Reader, outs io.Writer, message string) bool {
|
||||||
|
|
||||||
// On Windows, force the use of the regular OS stdin stream.
|
// On Windows, force the use of the regular OS stdin stream.
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
ins = NewInStream(os.Stdin)
|
ins = streams.NewIn(os.Stdin)
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := bufio.NewReader(ins)
|
reader := bufio.NewReader(ins)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
@ -86,7 +86,7 @@ func TestVolumePrunePromptYes(t *testing.T) {
|
||||||
volumePruneFunc: simplePruneFunc,
|
volumePruneFunc: simplePruneFunc,
|
||||||
})
|
})
|
||||||
|
|
||||||
cli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input))))
|
cli.SetIn(streams.NewIn(ioutil.NopCloser(strings.NewReader(input))))
|
||||||
cmd := NewPruneCommand(cli)
|
cmd := NewPruneCommand(cli)
|
||||||
assert.NilError(t, cmd.Execute())
|
assert.NilError(t, cmd.Execute())
|
||||||
golden.Assert(t, cli.OutBuffer().String(), "volume-prune-yes.golden")
|
golden.Assert(t, cli.OutBuffer().String(), "volume-prune-yes.golden")
|
||||||
|
@ -102,7 +102,7 @@ func TestVolumePrunePromptNo(t *testing.T) {
|
||||||
volumePruneFunc: simplePruneFunc,
|
volumePruneFunc: simplePruneFunc,
|
||||||
})
|
})
|
||||||
|
|
||||||
cli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input))))
|
cli.SetIn(streams.NewIn(ioutil.NopCloser(strings.NewReader(input))))
|
||||||
cmd := NewPruneCommand(cli)
|
cmd := NewPruneCommand(cli)
|
||||||
assert.NilError(t, cmd.Execute())
|
assert.NilError(t, cmd.Execute())
|
||||||
golden.Assert(t, cli.OutBuffer().String(), "volume-prune-no.golden")
|
golden.Assert(t, cli.OutBuffer().String(), "volume-prune-no.golden")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package command
|
package streams
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -9,33 +9,33 @@ import (
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InStream is an input stream used by the DockerCli to read user input
|
// In is an input stream used by the DockerCli to read user input
|
||||||
type InStream struct {
|
type In struct {
|
||||||
CommonStream
|
commonStream
|
||||||
in io.ReadCloser
|
in io.ReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *InStream) Read(p []byte) (int, error) {
|
func (i *In) Read(p []byte) (int, error) {
|
||||||
return i.in.Read(p)
|
return i.in.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements the Closer interface
|
// Close implements the Closer interface
|
||||||
func (i *InStream) Close() error {
|
func (i *In) Close() error {
|
||||||
return i.in.Close()
|
return i.in.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRawTerminal sets raw mode on the input terminal
|
// SetRawTerminal sets raw mode on the input terminal
|
||||||
func (i *InStream) SetRawTerminal() (err error) {
|
func (i *In) SetRawTerminal() (err error) {
|
||||||
if os.Getenv("NORAW") != "" || !i.CommonStream.isTerminal {
|
if os.Getenv("NORAW") != "" || !i.commonStream.isTerminal {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
i.CommonStream.state, err = term.SetRawTerminal(i.CommonStream.fd)
|
i.commonStream.state, err = term.SetRawTerminal(i.commonStream.fd)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTty checks if we are trying to attach to a container tty
|
// CheckTty checks if we are trying to attach to a container tty
|
||||||
// from a non-tty client input stream, and if so, returns an error.
|
// from a non-tty client input stream, and if so, returns an error.
|
||||||
func (i *InStream) CheckTty(attachStdin, ttyMode bool) error {
|
func (i *In) CheckTty(attachStdin, ttyMode bool) error {
|
||||||
// In order to attach to a container tty, input stream for the client must
|
// In order to attach to a container tty, input stream for the client must
|
||||||
// be a tty itself: redirecting or piping the client standard input is
|
// be a tty itself: redirecting or piping the client standard input is
|
||||||
// incompatible with `docker run -t`, `docker exec -t` or `docker attach`.
|
// incompatible with `docker run -t`, `docker exec -t` or `docker attach`.
|
||||||
|
@ -49,8 +49,8 @@ func (i *InStream) CheckTty(attachStdin, ttyMode bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInStream returns a new InStream object from a ReadCloser
|
// NewIn returns a new In object from a ReadCloser
|
||||||
func NewInStream(in io.ReadCloser) *InStream {
|
func NewIn(in io.ReadCloser) *In {
|
||||||
fd, isTerminal := term.GetFdInfo(in)
|
fd, isTerminal := term.GetFdInfo(in)
|
||||||
return &InStream{CommonStream: CommonStream{fd: fd, isTerminal: isTerminal}, in: in}
|
return &In{commonStream: commonStream{fd: fd, isTerminal: isTerminal}, in: in}
|
||||||
}
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package streams
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/term"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Out is an output stream used by the DockerCli to write normal program
|
||||||
|
// output.
|
||||||
|
type Out struct {
|
||||||
|
commonStream
|
||||||
|
out io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Out) Write(p []byte) (int, error) {
|
||||||
|
return o.out.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRawTerminal sets raw mode on the input terminal
|
||||||
|
func (o *Out) SetRawTerminal() (err error) {
|
||||||
|
if os.Getenv("NORAW") != "" || !o.commonStream.isTerminal {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
o.commonStream.state, err = term.SetRawTerminalOutput(o.commonStream.fd)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTtySize returns the height and width in characters of the tty
|
||||||
|
func (o *Out) GetTtySize() (uint, uint) {
|
||||||
|
if !o.isTerminal {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
ws, err := term.GetWinsize(o.fd)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Error getting size: %s", err)
|
||||||
|
if ws == nil {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uint(ws.Height), uint(ws.Width)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOut returns a new Out object from a Writer
|
||||||
|
func NewOut(out io.Writer) *Out {
|
||||||
|
fd, isTerminal := term.GetFdInfo(out)
|
||||||
|
return &Out{commonStream: commonStream{fd: fd, isTerminal: isTerminal}, out: out}
|
||||||
|
}
|
|
@ -1,34 +1,34 @@
|
||||||
package command
|
package streams
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CommonStream is an input stream used by the DockerCli to read user input
|
// commonStream is an input stream used by the DockerCli to read user input
|
||||||
type CommonStream struct {
|
type commonStream struct {
|
||||||
fd uintptr
|
fd uintptr
|
||||||
isTerminal bool
|
isTerminal bool
|
||||||
state *term.State
|
state *term.State
|
||||||
}
|
}
|
||||||
|
|
||||||
// FD returns the file descriptor number for this stream
|
// FD returns the file descriptor number for this stream
|
||||||
func (s *CommonStream) FD() uintptr {
|
func (s *commonStream) FD() uintptr {
|
||||||
return s.fd
|
return s.fd
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsTerminal returns true if this stream is connected to a terminal
|
// IsTerminal returns true if this stream is connected to a terminal
|
||||||
func (s *CommonStream) IsTerminal() bool {
|
func (s *commonStream) IsTerminal() bool {
|
||||||
return s.isTerminal
|
return s.isTerminal
|
||||||
}
|
}
|
||||||
|
|
||||||
// RestoreTerminal restores normal mode to the terminal
|
// RestoreTerminal restores normal mode to the terminal
|
||||||
func (s *CommonStream) RestoreTerminal() {
|
func (s *commonStream) RestoreTerminal() {
|
||||||
if s.state != nil {
|
if s.state != nil {
|
||||||
term.RestoreTerminal(s.fd, s.state)
|
term.RestoreTerminal(s.fd, s.state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetIsTerminal sets the boolean used for isTerminal
|
// SetIsTerminal sets the boolean used for isTerminal
|
||||||
func (s *CommonStream) SetIsTerminal(isTerminal bool) {
|
func (s *commonStream) SetIsTerminal(isTerminal bool) {
|
||||||
s.isTerminal = isTerminal
|
s.isTerminal = isTerminal
|
||||||
}
|
}
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
|
@ -13,10 +12,8 @@ import (
|
||||||
cliconfig "github.com/docker/cli/cli/config"
|
cliconfig "github.com/docker/cli/cli/config"
|
||||||
"github.com/docker/cli/cli/debug"
|
"github.com/docker/cli/cli/debug"
|
||||||
cliflags "github.com/docker/cli/cli/flags"
|
cliflags "github.com/docker/cli/cli/flags"
|
||||||
"github.com/docker/cli/internal/containerizedengine"
|
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/docker/pkg/term"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
@ -170,17 +167,19 @@ func noArgs(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Set terminal emulation based on platform as required.
|
dockerCli, err := command.NewDockerCli()
|
||||||
stdin, stdout, stderr := term.StdStreams()
|
if err != nil {
|
||||||
logrus.SetOutput(stderr)
|
fmt.Fprintln(dockerCli.Err(), err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
logrus.SetOutput(dockerCli.Err())
|
||||||
|
|
||||||
dockerCli := command.NewDockerCli(stdin, stdout, stderr, contentTrustEnabled(), containerizedengine.NewClient)
|
|
||||||
cmd := newDockerCommand(dockerCli)
|
cmd := newDockerCommand(dockerCli)
|
||||||
|
|
||||||
if err := cmd.Execute(); err != nil {
|
if err := cmd.Execute(); err != nil {
|
||||||
if sterr, ok := err.(cli.StatusError); ok {
|
if sterr, ok := err.(cli.StatusError); ok {
|
||||||
if sterr.Status != "" {
|
if sterr.Status != "" {
|
||||||
fmt.Fprintln(stderr, sterr.Status)
|
fmt.Fprintln(dockerCli.Err(), sterr.Status)
|
||||||
}
|
}
|
||||||
// StatusError should only be used for errors, and all errors should
|
// StatusError should only be used for errors, and all errors should
|
||||||
// have a non-zero exit status, so never exit with 0
|
// have a non-zero exit status, so never exit with 0
|
||||||
|
@ -189,21 +188,11 @@ func main() {
|
||||||
}
|
}
|
||||||
os.Exit(sterr.StatusCode)
|
os.Exit(sterr.StatusCode)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(stderr, err)
|
fmt.Fprintln(dockerCli.Err(), err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func contentTrustEnabled() bool {
|
|
||||||
if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" {
|
|
||||||
if t, err := strconv.ParseBool(e); t || err != nil {
|
|
||||||
// treat any other value as true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func dockerPreRun(opts *cliflags.ClientOptions) {
|
func dockerPreRun(opts *cliflags.ClientOptions) {
|
||||||
cliflags.SetLogLevel(opts.Common.LogLevel)
|
cliflags.SetLogLevel(opts.Common.LogLevel)
|
||||||
|
|
||||||
|
|
|
@ -25,27 +25,33 @@ func TestClientDebugEnabled(t *testing.T) {
|
||||||
assert.Check(t, is.Equal(logrus.DebugLevel, logrus.GetLevel()))
|
assert.Check(t, is.Equal(logrus.DebugLevel, logrus.GetLevel()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var discard = ioutil.NopCloser(bytes.NewBuffer(nil))
|
||||||
|
|
||||||
func TestExitStatusForInvalidSubcommandWithHelpFlag(t *testing.T) {
|
func TestExitStatusForInvalidSubcommandWithHelpFlag(t *testing.T) {
|
||||||
discard := ioutil.Discard
|
cli, err := command.NewDockerCli(command.WithInputStream(discard), command.WithCombinedStreams(ioutil.Discard))
|
||||||
cmd := newDockerCommand(command.NewDockerCli(os.Stdin, discard, discard, false, nil))
|
assert.NilError(t, err)
|
||||||
|
cmd := newDockerCommand(cli)
|
||||||
cmd.SetArgs([]string{"help", "invalid"})
|
cmd.SetArgs([]string{"help", "invalid"})
|
||||||
err := cmd.Execute()
|
err = cmd.Execute()
|
||||||
assert.Error(t, err, "unknown help topic: invalid")
|
assert.Error(t, err, "unknown help topic: invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExitStatusForInvalidSubcommand(t *testing.T) {
|
func TestExitStatusForInvalidSubcommand(t *testing.T) {
|
||||||
discard := ioutil.Discard
|
cli, err := command.NewDockerCli(command.WithInputStream(discard), command.WithCombinedStreams(ioutil.Discard))
|
||||||
cmd := newDockerCommand(command.NewDockerCli(os.Stdin, discard, discard, false, nil))
|
assert.NilError(t, err)
|
||||||
|
cmd := newDockerCommand(cli)
|
||||||
cmd.SetArgs([]string{"invalid"})
|
cmd.SetArgs([]string{"invalid"})
|
||||||
err := cmd.Execute()
|
err = cmd.Execute()
|
||||||
assert.Check(t, is.ErrorContains(err, "docker: 'invalid' is not a docker command."))
|
assert.Check(t, is.ErrorContains(err, "docker: 'invalid' is not a docker command."))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVersion(t *testing.T) {
|
func TestVersion(t *testing.T) {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
cmd := newDockerCommand(command.NewDockerCli(os.Stdin, &b, &b, false, nil))
|
cli, err := command.NewDockerCli(command.WithInputStream(discard), command.WithCombinedStreams(&b))
|
||||||
|
assert.NilError(t, err)
|
||||||
|
cmd := newDockerCommand(cli)
|
||||||
cmd.SetArgs([]string{"--version"})
|
cmd.SetArgs([]string{"--version"})
|
||||||
err := cmd.Execute()
|
err = cmd.Execute()
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Contains(b.String(), "Docker version"))
|
assert.Check(t, is.Contains(b.String(), "Docker version"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/command/commands"
|
"github.com/docker/cli/cli/command/commands"
|
||||||
"github.com/docker/docker/pkg/term"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
@ -18,8 +17,10 @@ import (
|
||||||
const descriptionSourcePath = "docs/reference/commandline/"
|
const descriptionSourcePath = "docs/reference/commandline/"
|
||||||
|
|
||||||
func generateCliYaml(opts *options) error {
|
func generateCliYaml(opts *options) error {
|
||||||
stdin, stdout, stderr := term.StdStreams()
|
dockerCli, err := command.NewDockerCli()
|
||||||
dockerCli := command.NewDockerCli(stdin, stdout, stderr, false, nil)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
cmd := &cobra.Command{Use: "docker"}
|
cmd := &cobra.Command{Use: "docker"}
|
||||||
commands.AddCommands(cmd, dockerCli)
|
commands.AddCommands(cmd, dockerCli)
|
||||||
disableFlagsInUseLine(cmd)
|
disableFlagsInUseLine(cmd)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
@ -24,7 +24,7 @@ func TestPullWithAuthPullFail(t *testing.T) {
|
||||||
}
|
}
|
||||||
imageName := "testnamegoeshere"
|
imageName := "testnamegoeshere"
|
||||||
|
|
||||||
_, err := client.pullWithAuth(ctx, imageName, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
_, err := client.pullWithAuth(ctx, imageName, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.ErrorContains(t, err, "pull failure")
|
assert.ErrorContains(t, err, "pull failure")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,6 @@ func TestPullWithAuthPullPass(t *testing.T) {
|
||||||
}
|
}
|
||||||
imageName := "testnamegoeshere"
|
imageName := "testnamegoeshere"
|
||||||
|
|
||||||
_, err := client.pullWithAuth(ctx, imageName, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
_, err := client.pullWithAuth(ctx, imageName, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/cio"
|
"github.com/containerd/containerd/cio"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/cli/internal/versions"
|
"github.com/docker/cli/internal/versions"
|
||||||
clitypes "github.com/docker/cli/types"
|
clitypes "github.com/docker/cli/types"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -45,21 +45,21 @@ func TestActivateImagePermutations(t *testing.T) {
|
||||||
RuntimeMetadataDir: tmpdir,
|
RuntimeMetadataDir: tmpdir,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.ErrorContains(t, err, expectedError.Error())
|
assert.ErrorContains(t, err, expectedError.Error())
|
||||||
assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage, opts.EngineVersion))
|
assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage, opts.EngineVersion))
|
||||||
|
|
||||||
metadata = clitypes.RuntimeMetadata{EngineImage: clitypes.CommunityEngineImage}
|
metadata = clitypes.RuntimeMetadata{EngineImage: clitypes.CommunityEngineImage}
|
||||||
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.ErrorContains(t, err, expectedError.Error())
|
assert.ErrorContains(t, err, expectedError.Error())
|
||||||
assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage, opts.EngineVersion))
|
assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage, opts.EngineVersion))
|
||||||
|
|
||||||
metadata = clitypes.RuntimeMetadata{EngineImage: clitypes.CommunityEngineImage + "-dm"}
|
metadata = clitypes.RuntimeMetadata{EngineImage: clitypes.CommunityEngineImage + "-dm"}
|
||||||
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
err = versions.WriteRuntimeMetadata(tmpdir, &metadata)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.ErrorContains(t, err, expectedError.Error())
|
assert.ErrorContains(t, err, expectedError.Error())
|
||||||
assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage+"-dm", opts.EngineVersion))
|
assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage+"-dm", opts.EngineVersion))
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ func TestActivateConfigFailure(t *testing.T) {
|
||||||
RuntimeMetadataDir: tmpdir,
|
RuntimeMetadataDir: tmpdir,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.ErrorContains(t, err, "config lookup failure")
|
assert.ErrorContains(t, err, "config lookup failure")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ func TestActivateDoUpdateFail(t *testing.T) {
|
||||||
RuntimeMetadataDir: tmpdir,
|
RuntimeMetadataDir: tmpdir,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.ErrorContains(t, err, "check for image")
|
assert.ErrorContains(t, err, "check for image")
|
||||||
assert.ErrorContains(t, err, "something went wrong")
|
assert.ErrorContains(t, err, "something went wrong")
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,7 @@ func TestDoUpdateNoVersion(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
client := baseClient{}
|
client := baseClient{}
|
||||||
err = client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
err = client.DoUpdate(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.ErrorContains(t, err, "pick the version you")
|
assert.ErrorContains(t, err, "pick the version you")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ func TestDoUpdateImageMiscError(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
err = client.DoUpdate(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.ErrorContains(t, err, "check for image")
|
assert.ErrorContains(t, err, "check for image")
|
||||||
assert.ErrorContains(t, err, "something went wrong")
|
assert.ErrorContains(t, err, "something went wrong")
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,7 @@ func TestDoUpdatePullFail(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
err = client.DoUpdate(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.ErrorContains(t, err, "unable to pull")
|
assert.ErrorContains(t, err, "unable to pull")
|
||||||
assert.ErrorContains(t, err, "pull failure")
|
assert.ErrorContains(t, err, "pull failure")
|
||||||
}
|
}
|
||||||
|
@ -280,7 +280,7 @@ func TestActivateDoUpdateVerifyImageName(t *testing.T) {
|
||||||
RuntimeMetadataDir: tmpdir,
|
RuntimeMetadataDir: tmpdir,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{})
|
err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{})
|
||||||
assert.ErrorContains(t, err, "check for image")
|
assert.ErrorContains(t, err, "check for image")
|
||||||
assert.ErrorContains(t, err, "something went wrong")
|
assert.ErrorContains(t, err, "something went wrong")
|
||||||
expectedImage := fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, opts.EngineImage, opts.EngineVersion)
|
expectedImage := fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, opts.EngineImage, opts.EngineVersion)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/docker/cli/cli/context/store"
|
"github.com/docker/cli/cli/context/store"
|
||||||
manifeststore "github.com/docker/cli/cli/manifest/store"
|
manifeststore "github.com/docker/cli/cli/manifest/store"
|
||||||
registryclient "github.com/docker/cli/cli/registry/client"
|
registryclient "github.com/docker/cli/cli/registry/client"
|
||||||
|
"github.com/docker/cli/cli/streams"
|
||||||
"github.com/docker/cli/cli/trust"
|
"github.com/docker/cli/cli/trust"
|
||||||
clitypes "github.com/docker/cli/types"
|
clitypes "github.com/docker/cli/types"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
@ -29,10 +30,10 @@ type FakeCli struct {
|
||||||
command.DockerCli
|
command.DockerCli
|
||||||
client client.APIClient
|
client client.APIClient
|
||||||
configfile *configfile.ConfigFile
|
configfile *configfile.ConfigFile
|
||||||
out *command.OutStream
|
out *streams.Out
|
||||||
outBuffer *bytes.Buffer
|
outBuffer *bytes.Buffer
|
||||||
err *bytes.Buffer
|
err *bytes.Buffer
|
||||||
in *command.InStream
|
in *streams.In
|
||||||
server command.ServerInfo
|
server command.ServerInfo
|
||||||
clientInfoFunc clientInfoFuncType
|
clientInfoFunc clientInfoFuncType
|
||||||
notaryClientFunc NotaryClientFuncType
|
notaryClientFunc NotaryClientFuncType
|
||||||
|
@ -51,10 +52,10 @@ func NewFakeCli(client client.APIClient, opts ...func(*FakeCli)) *FakeCli {
|
||||||
errBuffer := new(bytes.Buffer)
|
errBuffer := new(bytes.Buffer)
|
||||||
c := &FakeCli{
|
c := &FakeCli{
|
||||||
client: client,
|
client: client,
|
||||||
out: command.NewOutStream(outBuffer),
|
out: streams.NewOut(outBuffer),
|
||||||
outBuffer: outBuffer,
|
outBuffer: outBuffer,
|
||||||
err: errBuffer,
|
err: errBuffer,
|
||||||
in: command.NewInStream(ioutil.NopCloser(strings.NewReader(""))),
|
in: streams.NewIn(ioutil.NopCloser(strings.NewReader(""))),
|
||||||
// Use an empty string for filename so that tests don't create configfiles
|
// Use an empty string for filename so that tests don't create configfiles
|
||||||
// Set cli.ConfigFile().Filename to a tempfile to support Save.
|
// Set cli.ConfigFile().Filename to a tempfile to support Save.
|
||||||
configfile: configfile.New(""),
|
configfile: configfile.New(""),
|
||||||
|
@ -66,7 +67,7 @@ func NewFakeCli(client client.APIClient, opts ...func(*FakeCli)) *FakeCli {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetIn sets the input of the cli to the specified ReadCloser
|
// SetIn sets the input of the cli to the specified ReadCloser
|
||||||
func (c *FakeCli) SetIn(in *command.InStream) {
|
func (c *FakeCli) SetIn(in *streams.In) {
|
||||||
c.in = in
|
c.in = in
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ func (c *FakeCli) SetErr(err *bytes.Buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOut sets the stdout stream for the cli to the specified io.Writer
|
// SetOut sets the stdout stream for the cli to the specified io.Writer
|
||||||
func (c *FakeCli) SetOut(out *command.OutStream) {
|
func (c *FakeCli) SetOut(out *streams.Out) {
|
||||||
c.out = out
|
c.out = out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +107,7 @@ func (c *FakeCli) Client() client.APIClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Out returns the output stream (stdout) the cli should write on
|
// Out returns the output stream (stdout) the cli should write on
|
||||||
func (c *FakeCli) Out() *command.OutStream {
|
func (c *FakeCli) Out() *streams.Out {
|
||||||
return c.out
|
return c.out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +117,7 @@ func (c *FakeCli) Err() io.Writer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// In returns the input stream the cli will use
|
// In returns the input stream the cli will use
|
||||||
func (c *FakeCli) In() *command.InStream {
|
func (c *FakeCli) In() *streams.In {
|
||||||
return c.in
|
return c.in
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/command/commands"
|
"github.com/docker/cli/cli/command/commands"
|
||||||
"github.com/docker/docker/pkg/term"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/cobra/doc"
|
"github.com/spf13/cobra/doc"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
@ -37,8 +36,10 @@ func generateManPages(opts *options) error {
|
||||||
header.Date = &now
|
header.Date = &now
|
||||||
}
|
}
|
||||||
|
|
||||||
stdin, stdout, stderr := term.StdStreams()
|
dockerCli, err := command.NewDockerCli()
|
||||||
dockerCli := command.NewDockerCli(stdin, stdout, stderr, false, nil)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
cmd := &cobra.Command{Use: "docker"}
|
cmd := &cobra.Command{Use: "docker"}
|
||||||
commands.AddCommands(cmd, dockerCli)
|
commands.AddCommands(cmd, dockerCli)
|
||||||
source := filepath.Join(opts.source, descriptionSourcePath)
|
source := filepath.Join(opts.source, descriptionSourcePath)
|
||||||
|
|
Loading…
Reference in New Issue