Unit tests for cli/commands/image (except build and tag)

Signed-off-by: Ignacio Capurro <icapurrofagian@gmail.com>
This commit is contained in:
Ignacio Capurro 2017-03-30 21:21:14 -03:00
parent 0e9148b1e9
commit b2551c619d
58 changed files with 1446 additions and 124 deletions

View file

@ -39,7 +39,9 @@ type Cli interface {
Out() *OutStream
Err() io.Writer
In() *InStream
SetIn(in *InStream)
ConfigFile() *configfile.ConfigFile
CredentialsStore(serverAddress string) credentials.Store
}
// DockerCli is an instance the docker command line client.
@ -75,6 +77,11 @@ func (cli *DockerCli) Err() io.Writer {
return cli.err
}
// SetIn sets the reader used for stdin
func (cli *DockerCli) SetIn(in *InStream) {
cli.in = in
}
// In returns the reader used for stdin
func (cli *DockerCli) In() *InStream {
return cli.in

View file

@ -13,21 +13,21 @@ type arguments struct {
func TestParseExec(t *testing.T) {
valids := map[*arguments]*types.ExecConfig{
&arguments{
{
execCmd: []string{"command"},
}: {
Cmd: []string{"command"},
AttachStdout: true,
AttachStderr: true,
},
&arguments{
{
execCmd: []string{"command1", "command2"},
}: {
Cmd: []string{"command1", "command2"},
AttachStdout: true,
AttachStderr: true,
},
&arguments{
{
options: execOptions{
interactive: true,
tty: true,
@ -42,7 +42,7 @@ func TestParseExec(t *testing.T) {
Tty: true,
Cmd: []string{"command"},
},
&arguments{
{
options: execOptions{
detach: true,
},
@ -54,7 +54,7 @@ func TestParseExec(t *testing.T) {
Detach: true,
Cmd: []string{"command"},
},
&arguments{
{
options: execOptions{
tty: true,
interactive: true,

View file

@ -0,0 +1,116 @@
package image
import (
"io"
"io/ioutil"
"strings"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
"golang.org/x/net/context"
)
type fakeClient struct {
client.Client
imageTagFunc func(string, string) error
imageSaveFunc func(images []string) (io.ReadCloser, error)
imageRemoveFunc func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error)
imagePushFunc func(ref string, options types.ImagePushOptions) (io.ReadCloser, error)
infoFunc func() (types.Info, error)
imagePullFunc func(ref string, options types.ImagePullOptions) (io.ReadCloser, error)
imagesPruneFunc func(pruneFilter filters.Args) (types.ImagesPruneReport, error)
imageLoadFunc func(input io.Reader, quiet bool) (types.ImageLoadResponse, error)
imageListFunc func(options types.ImageListOptions) ([]types.ImageSummary, error)
imageInspectFunc func(image string) (types.ImageInspect, []byte, error)
imageImportFunc func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)
imageHistoryFunc func(image string) ([]image.HistoryResponseItem, error)
}
func (cli *fakeClient) ImageTag(_ context.Context, image, ref string) error {
if cli.imageTagFunc != nil {
return cli.imageTagFunc(image, ref)
}
return nil
}
func (cli *fakeClient) ImageSave(_ context.Context, images []string) (io.ReadCloser, error) {
if cli.imageSaveFunc != nil {
return cli.imageSaveFunc(images)
}
return ioutil.NopCloser(strings.NewReader("")), nil
}
func (cli *fakeClient) ImageRemove(_ context.Context, image string,
options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) {
if cli.imageRemoveFunc != nil {
return cli.imageRemoveFunc(image, options)
}
return []types.ImageDeleteResponseItem{}, nil
}
func (cli *fakeClient) ImagePush(_ context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
if cli.imagePushFunc != nil {
return cli.imagePushFunc(ref, options)
}
return ioutil.NopCloser(strings.NewReader("")), nil
}
func (cli *fakeClient) Info(_ context.Context) (types.Info, error) {
if cli.infoFunc != nil {
return cli.infoFunc()
}
return types.Info{}, nil
}
func (cli *fakeClient) ImagePull(_ context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
if cli.imagePullFunc != nil {
cli.imagePullFunc(ref, options)
}
return ioutil.NopCloser(strings.NewReader("")), nil
}
func (cli *fakeClient) ImagesPrune(_ context.Context, pruneFilter filters.Args) (types.ImagesPruneReport, error) {
if cli.imagesPruneFunc != nil {
return cli.imagesPruneFunc(pruneFilter)
}
return types.ImagesPruneReport{}, nil
}
func (cli *fakeClient) ImageLoad(_ context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error) {
if cli.imageLoadFunc != nil {
return cli.imageLoadFunc(input, quiet)
}
return types.ImageLoadResponse{}, nil
}
func (cli *fakeClient) ImageList(ctx context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) {
if cli.imageListFunc != nil {
return cli.imageListFunc(options)
}
return []types.ImageSummary{{}}, nil
}
func (cli *fakeClient) ImageInspectWithRaw(_ context.Context, image string) (types.ImageInspect, []byte, error) {
if cli.imageInspectFunc != nil {
return cli.imageInspectFunc(image)
}
return types.ImageInspect{}, nil, nil
}
func (cli *fakeClient) ImageImport(_ context.Context, source types.ImageImportSource, ref string,
options types.ImageImportOptions) (io.ReadCloser, error) {
if cli.imageImportFunc != nil {
return cli.imageImportFunc(source, ref, options)
}
return ioutil.NopCloser(strings.NewReader("")), nil
}
func (cli *fakeClient) ImageHistory(_ context.Context, img string) ([]image.HistoryResponseItem, error) {
if cli.imageHistoryFunc != nil {
return cli.imageHistoryFunc(img)
}
return []image.HistoryResponseItem{{ID: img, Created: time.Now().Unix()}}, nil
}

View file

@ -19,7 +19,7 @@ type historyOptions struct {
}
// NewHistoryCommand creates a new `docker history` command
func NewHistoryCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewHistoryCommand(dockerCli command.Cli) *cobra.Command {
var opts historyOptions
cmd := &cobra.Command{
@ -42,7 +42,7 @@ func NewHistoryCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func runHistory(dockerCli *command.DockerCli, opts historyOptions) error {
func runHistory(dockerCli command.Cli, opts historyOptions) error {
ctx := context.Background()
history, err := dockerCli.Client().ImageHistory(ctx, opts.image)

View file

@ -0,0 +1,108 @@
package image
import (
"bytes"
"fmt"
"io/ioutil"
"regexp"
"testing"
"time"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/docker/docker/pkg/testutil/golden"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestNewHistoryCommandErrors(t *testing.T) {
testCases := []struct {
name string
args []string
expectedError string
imageHistoryFunc func(img string) ([]image.HistoryResponseItem, error)
}{
{
name: "wrong-args",
args: []string{},
expectedError: "requires exactly 1 argument(s).",
},
{
name: "client-error",
args: []string{"image:tag"},
expectedError: "something went wrong",
imageHistoryFunc: func(img string) ([]image.HistoryResponseItem, error) {
return []image.HistoryResponseItem{{}}, errors.Errorf("something went wrong")
},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewHistoryCommand(test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
}
}
func TestNewHistoryCommandSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
outputRegex string
imageHistoryFunc func(img string) ([]image.HistoryResponseItem, error)
}{
{
name: "simple",
args: []string{"image:tag"},
imageHistoryFunc: func(img string) ([]image.HistoryResponseItem, error) {
return []image.HistoryResponseItem{{
ID: "1234567890123456789",
Created: time.Now().Unix(),
}}, nil
},
},
{
name: "quiet",
args: []string{"--quiet", "image:tag"},
},
// TODO: This test is failing since the output does not contain an RFC3339 date
//{
// name: "non-human",
// args: []string{"--human=false", "image:tag"},
// outputRegex: "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}", // RFC3339 date format match
//},
{
name: "non-human-header",
args: []string{"--human=false", "image:tag"},
outputRegex: "CREATED\\sAT",
},
{
name: "quiet-no-trunc",
args: []string{"--quiet", "--no-trunc", "image:tag"},
imageHistoryFunc: func(img string) ([]image.HistoryResponseItem, error) {
return []image.HistoryResponseItem{{
ID: "1234567890123456789",
Created: time.Now().Unix(),
}}, nil
},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewHistoryCommand(test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
assert.NoError(t, err)
actual := buf.String()
if tc.outputRegex == "" {
expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("history-command-success.%s.golden", tc.name))[:])
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected)
} else {
match, _ := regexp.MatchString(tc.outputRegex, actual)
assert.Equal(t, match, true)
}
}
}

View file

@ -23,7 +23,7 @@ type importOptions struct {
}
// NewImportCommand creates a new `docker import` command
func NewImportCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewImportCommand(dockerCli command.Cli) *cobra.Command {
var opts importOptions
cmd := &cobra.Command{
@ -48,7 +48,7 @@ func NewImportCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func runImport(dockerCli *command.DockerCli, opts importOptions) error {
func runImport(dockerCli command.Cli, opts importOptions) error {
var (
in io.Reader
srcName = opts.source

View file

@ -0,0 +1,100 @@
package image
import (
"bytes"
"io"
"io/ioutil"
"strings"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestNewImportCommandErrors(t *testing.T) {
testCases := []struct {
name string
args []string
expectedError string
imageImportFunc func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)
}{
{
name: "wrong-args",
args: []string{},
expectedError: "requires at least 1 argument(s).",
},
{
name: "import-failed",
args: []string{"testdata/import-command-success.input.txt"},
expectedError: "something went wrong",
imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
return nil, errors.Errorf("something went wrong")
},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewImportCommand(test.NewFakeCli(&fakeClient{imageImportFunc: tc.imageImportFunc}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
}
}
func TestNewImportCommandInvalidFile(t *testing.T) {
cmd := NewImportCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer)))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs([]string{"testdata/import-command-success.unexistent-file"})
testutil.ErrorContains(t, cmd.Execute(), "testdata/import-command-success.unexistent-file")
}
func TestNewImportCommandSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
imageImportFunc func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)
}{
{
name: "simple",
args: []string{"testdata/import-command-success.input.txt"},
},
{
name: "terminal-source",
args: []string{"-"},
},
{
name: "double",
args: []string{"-", "image:local"},
imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
assert.Equal(t, ref, "image:local")
return ioutil.NopCloser(strings.NewReader("")), nil
},
},
{
name: "message",
args: []string{"--message", "test message", "-"},
imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
assert.Equal(t, options.Message, "test message")
return ioutil.NopCloser(strings.NewReader("")), nil
},
},
{
name: "change",
args: []string{"--change", "ENV DEBUG true", "-"},
imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
assert.Equal(t, options.Changes[0], "ENV DEBUG true")
return ioutil.NopCloser(strings.NewReader("")), nil
},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewImportCommand(test.NewFakeCli(&fakeClient{imageImportFunc: tc.imageImportFunc}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.NoError(t, cmd.Execute())
}
}

View file

@ -15,7 +15,7 @@ type inspectOptions struct {
}
// newInspectCommand creates a new cobra.Command for `docker image inspect`
func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
func newInspectCommand(dockerCli command.Cli) *cobra.Command {
var opts inspectOptions
cmd := &cobra.Command{
@ -33,7 +33,7 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
func runInspect(dockerCli command.Cli, opts inspectOptions) error {
client := dockerCli.Client()
ctx := context.Background()

View file

@ -0,0 +1,92 @@
package image
import (
"bytes"
"fmt"
"io/ioutil"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/docker/docker/pkg/testutil/golden"
"github.com/stretchr/testify/assert"
)
func TestNewInspectCommandErrors(t *testing.T) {
testCases := []struct {
name string
args []string
expectedError string
}{
{
name: "wrong-args",
args: []string{},
expectedError: "requires at least 1 argument(s).",
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := newInspectCommand(test.NewFakeCli(&fakeClient{}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
}
}
func TestNewInspectCommandSuccess(t *testing.T) {
imageInspectInvocationCount := 0
testCases := []struct {
name string
args []string
imageCount int
imageInspectFunc func(image string) (types.ImageInspect, []byte, error)
}{
{
name: "simple",
args: []string{"image"},
imageCount: 1,
imageInspectFunc: func(image string) (types.ImageInspect, []byte, error) {
imageInspectInvocationCount++
assert.Equal(t, image, "image")
return types.ImageInspect{}, nil, nil
},
},
{
name: "format",
imageCount: 1,
args: []string{"--format='{{.ID}}'", "image"},
imageInspectFunc: func(image string) (types.ImageInspect, []byte, error) {
imageInspectInvocationCount++
return types.ImageInspect{ID: image}, nil, nil
},
},
{
name: "simple-many",
args: []string{"image1", "image2"},
imageCount: 2,
imageInspectFunc: func(image string) (types.ImageInspect, []byte, error) {
imageInspectInvocationCount++
if imageInspectInvocationCount == 1 {
assert.Equal(t, image, "image1")
} else {
assert.Equal(t, image, "image2")
}
return types.ImageInspect{}, nil, nil
},
},
}
for _, tc := range testCases {
imageInspectInvocationCount = 0
buf := new(bytes.Buffer)
cmd := newInspectCommand(test.NewFakeCli(&fakeClient{imageInspectFunc: tc.imageInspectFunc}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
assert.NoError(t, err)
actual := buf.String()
expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("inspect-command-success.%s.golden", tc.name))[:])
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected)
assert.Equal(t, tc.imageCount, imageInspectInvocationCount)
}
}

View file

@ -23,7 +23,7 @@ type imagesOptions struct {
}
// NewImagesCommand creates a new `docker images` command
func NewImagesCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewImagesCommand(dockerCli command.Cli) *cobra.Command {
opts := imagesOptions{filter: opts.NewFilterOpt()}
cmd := &cobra.Command{
@ -50,14 +50,14 @@ func NewImagesCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
func newListCommand(dockerCli command.Cli) *cobra.Command {
cmd := *NewImagesCommand(dockerCli)
cmd.Aliases = []string{"images", "list"}
cmd.Use = "ls [OPTIONS] [REPOSITORY[:TAG]]"
return &cmd
}
func runImages(dockerCli *command.DockerCli, opts imagesOptions) error {
func runImages(dockerCli command.Cli, opts imagesOptions) error {
ctx := context.Background()
filters := opts.filter.Value()

View file

@ -0,0 +1,102 @@
package image
import (
"bytes"
"fmt"
"io/ioutil"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cli/config/configfile"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/docker/docker/pkg/testutil/golden"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestNewImagesCommandErrors(t *testing.T) {
testCases := []struct {
name string
args []string
expectedError string
imageListFunc func(options types.ImageListOptions) ([]types.ImageSummary, error)
}{
{
name: "wrong-args",
args: []string{"arg1", "arg2"},
expectedError: "requires at most 1 argument(s).",
},
{
name: "failed-list",
expectedError: "something went wrong",
imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) {
return []types.ImageSummary{{}}, errors.Errorf("something went wrong")
},
},
}
for _, tc := range testCases {
cmd := NewImagesCommand(test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc}, new(bytes.Buffer)))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.Error(t, cmd.Execute(), tc.expectedError)
}
}
func TestNewImagesCommandSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
imageFormat string
imageListFunc func(options types.ImageListOptions) ([]types.ImageSummary, error)
}{
{
name: "simple",
},
{
name: "format",
imageFormat: "raw",
},
{
name: "quiet-format",
args: []string{"-q"},
imageFormat: "table",
},
{
name: "match-name",
args: []string{"image"},
imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) {
assert.Equal(t, options.Filters.Get("reference")[0], "image")
return []types.ImageSummary{{}}, nil
},
},
{
name: "filters",
args: []string{"--filter", "name=value"},
imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) {
assert.Equal(t, options.Filters.Get("name")[0], "value")
return []types.ImageSummary{{}}, nil
},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cli := test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc}, buf)
cli.SetConfigfile(&configfile.ConfigFile{ImagesFormat: tc.imageFormat})
cmd := NewImagesCommand(cli)
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
assert.NoError(t, err)
actual := buf.String()
expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("list-command-success.%s.golden", tc.name))[:])
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected)
}
}
func TestNewListCommandAlias(t *testing.T) {
cmd := newListCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer)))
assert.Equal(t, cmd.HasAlias("images"), true)
assert.Equal(t, cmd.HasAlias("list"), true)
assert.Equal(t, cmd.HasAlias("other"), false)
}

View file

@ -19,7 +19,7 @@ type loadOptions struct {
}
// NewLoadCommand creates a new `docker load` command
func NewLoadCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewLoadCommand(dockerCli command.Cli) *cobra.Command {
var opts loadOptions
cmd := &cobra.Command{
@ -39,7 +39,7 @@ func NewLoadCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func runLoad(dockerCli *command.DockerCli, opts loadOptions) error {
func runLoad(dockerCli command.Cli, opts loadOptions) error {
var input io.Reader = dockerCli.In()
if opts.input != "" {

View file

@ -0,0 +1,106 @@
package image
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"strings"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/docker/docker/pkg/testutil/golden"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestNewLoadCommandErrors(t *testing.T) {
testCases := []struct {
name string
args []string
isTerminalIn bool
expectedError string
imageLoadFunc func(input io.Reader, quiet bool) (types.ImageLoadResponse, error)
}{
{
name: "wrong-args",
args: []string{"arg"},
expectedError: "accepts no argument(s).",
},
{
name: "input-to-terminal",
isTerminalIn: true,
expectedError: "requested load from stdin, but stdin is empty",
},
{
name: "pull-error",
expectedError: "something went wrong",
imageLoadFunc: func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) {
return types.ImageLoadResponse{}, errors.Errorf("something went wrong")
},
},
}
for _, tc := range testCases {
cli := test.NewFakeCli(&fakeClient{imageLoadFunc: tc.imageLoadFunc}, new(bytes.Buffer))
cli.In().SetIsTerminal(tc.isTerminalIn)
cmd := NewLoadCommand(cli)
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.Error(t, cmd.Execute(), tc.expectedError)
}
}
func TestNewLoadCommandInvalidInput(t *testing.T) {
expectedError := "open *"
cmd := NewLoadCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer)))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs([]string{"--input", "*"})
err := cmd.Execute()
assert.NotNil(t, err)
assert.Contains(t, err.Error(), expectedError)
}
func TestNewLoadCommandSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
imageLoadFunc func(input io.Reader, quiet bool) (types.ImageLoadResponse, error)
}{
{
name: "simple",
imageLoadFunc: func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) {
return types.ImageLoadResponse{Body: ioutil.NopCloser(strings.NewReader("Success"))}, nil
},
},
{
name: "json",
imageLoadFunc: func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) {
json := "{\"ID\": \"1\"}"
return types.ImageLoadResponse{
Body: ioutil.NopCloser(strings.NewReader(json)),
JSON: true,
}, nil
},
},
{
name: "input-file",
args: []string{"--input", "testdata/load-command-success.input.txt"},
imageLoadFunc: func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) {
return types.ImageLoadResponse{Body: ioutil.NopCloser(strings.NewReader("Success"))}, nil
},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewLoadCommand(test.NewFakeCli(&fakeClient{imageLoadFunc: tc.imageLoadFunc}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
assert.NoError(t, err)
actual := buf.String()
expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("load-command-success.%s.golden", tc.name))[:])
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected)
}
}

View file

@ -19,7 +19,7 @@ type pruneOptions struct {
}
// NewPruneCommand returns a new cobra prune command for images
func NewPruneCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
opts := pruneOptions{filter: opts.NewFilterOpt()}
cmd := &cobra.Command{
@ -55,7 +55,7 @@ Are you sure you want to continue?`
Are you sure you want to continue?`
)
func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed uint64, output string, err error) {
func runPrune(dockerCli command.Cli, opts pruneOptions) (spaceReclaimed uint64, output string, err error) {
pruneFilters := opts.filter.Value()
pruneFilters.Add("dangling", fmt.Sprintf("%v", !opts.all))
pruneFilters = command.PruneFilters(dockerCli, pruneFilters)
@ -90,6 +90,6 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed u
// RunPrune calls the Image Prune API
// This returns the amount of space reclaimed and a detailed output string
func RunPrune(dockerCli *command.DockerCli, all bool, filter opts.FilterOpt) (uint64, string, error) {
func RunPrune(dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error) {
return runPrune(dockerCli, pruneOptions{force: true, all: all, filter: filter})
}

View file

@ -0,0 +1,100 @@
package image
import (
"bytes"
"fmt"
"io/ioutil"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/docker/docker/pkg/testutil/golden"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestNewPruneCommandErrors(t *testing.T) {
testCases := []struct {
name string
args []string
expectedError string
imagesPruneFunc func(pruneFilter filters.Args) (types.ImagesPruneReport, error)
}{
{
name: "wrong-args",
args: []string{"something"},
expectedError: "accepts no argument(s).",
},
{
name: "prune-error",
args: []string{"--force"},
expectedError: "something went wrong",
imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) {
return types.ImagesPruneReport{}, errors.Errorf("something went wrong")
},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewPruneCommand(test.NewFakeCli(&fakeClient{
imagesPruneFunc: tc.imagesPruneFunc,
}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.Error(t, cmd.Execute(), tc.expectedError)
}
}
func TestNewPruneCommandSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
imagesPruneFunc func(pruneFilter filters.Args) (types.ImagesPruneReport, error)
}{
{
name: "all",
args: []string{"--all"},
imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) {
assert.Equal(t, pruneFilter.Get("dangling")[0], "false")
return types.ImagesPruneReport{}, nil
},
},
{
name: "force-deleted",
args: []string{"--force"},
imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) {
assert.Equal(t, pruneFilter.Get("dangling")[0], "true")
return types.ImagesPruneReport{
ImagesDeleted: []types.ImageDeleteResponseItem{{Deleted: "image1"}},
SpaceReclaimed: 1,
}, nil
},
},
{
name: "force-untagged",
args: []string{"--force"},
imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) {
assert.Equal(t, pruneFilter.Get("dangling")[0], "true")
return types.ImagesPruneReport{
ImagesDeleted: []types.ImageDeleteResponseItem{{Untagged: "image1"}},
SpaceReclaimed: 2,
}, nil
},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewPruneCommand(test.NewFakeCli(&fakeClient{
imagesPruneFunc: tc.imagesPruneFunc,
}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
assert.NoError(t, err)
actual := buf.String()
expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("prune-command-success.%s.golden", tc.name))[:])
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected)
}
}

View file

@ -19,7 +19,7 @@ type pullOptions struct {
}
// NewPullCommand creates a new `docker pull` command
func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewPullCommand(dockerCli command.Cli) *cobra.Command {
var opts pullOptions
cmd := &cobra.Command{
@ -40,7 +40,7 @@ func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
func runPull(dockerCli command.Cli, opts pullOptions) error {
distributionRef, err := reference.ParseNormalizedNamed(opts.remote)
if err != nil {
return err

View file

@ -0,0 +1,85 @@
package image
import (
"bytes"
"fmt"
"io/ioutil"
"testing"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/docker/docker/pkg/testutil/golden"
"github.com/docker/docker/registry"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
)
func TestNewPullCommandErrors(t *testing.T) {
testCases := []struct {
name string
args []string
expectedError string
trustedPullFunc func(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named,
authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error
}{
{
name: "wrong-args",
expectedError: "requires exactly 1 argument(s).",
args: []string{},
},
{
name: "invalid-name",
expectedError: "invalid reference format: repository name must be lowercase",
args: []string{"UPPERCASE_REPO"},
},
{
name: "all-tags-with-tag",
expectedError: "tag can't be used with --all-tags/-a",
args: []string{"--all-tags", "image:tag"},
},
{
name: "pull-error",
args: []string{"--disable-content-trust=false", "image:tag"},
expectedError: "you are not authorized to perform this operation: server returned 401.",
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewPullCommand(test.NewFakeCli(&fakeClient{}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.Error(t, cmd.Execute(), tc.expectedError)
}
}
func TestNewPullCommandSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
trustedPullFunc func(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named,
authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error
}{
{
name: "simple",
args: []string{"image:tag"},
},
{
name: "simple-no-tag",
args: []string{"image"},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewPullCommand(test.NewFakeCli(&fakeClient{}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
assert.NoError(t, err)
actual := buf.String()
expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("pull-command-success.%s.golden", tc.name))[:])
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected)
}
}

View file

@ -12,7 +12,7 @@ import (
)
// NewPushCommand creates a new `docker push` command
func NewPushCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewPushCommand(dockerCli command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "push [OPTIONS] NAME[:TAG]",
Short: "Push an image or a repository to a registry",
@ -29,7 +29,7 @@ func NewPushCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func runPush(dockerCli *command.DockerCli, remote string) error {
func runPush(dockerCli command.Cli, remote string) error {
ref, err := reference.ParseNormalizedNamed(remote)
if err != nil {
return err

View file

@ -0,0 +1,84 @@
package image
import (
"bytes"
"io"
"io/ioutil"
"strings"
"testing"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/registry"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
)
func TestNewPushCommandErrors(t *testing.T) {
testCases := []struct {
name string
args []string
expectedError string
imagePushFunc func(ref string, options types.ImagePushOptions) (io.ReadCloser, error)
}{
{
name: "wrong-args",
args: []string{},
expectedError: "requires exactly 1 argument(s).",
},
{
name: "invalid-name",
args: []string{"UPPERCASE_REPO"},
expectedError: "invalid reference format: repository name must be lowercase",
},
{
name: "push-failed",
args: []string{"image:repo"},
expectedError: "Failed to push",
imagePushFunc: func(ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
return ioutil.NopCloser(strings.NewReader("")), errors.Errorf("Failed to push")
},
},
{
name: "trust-error",
args: []string{"--disable-content-trust=false", "image:repo"},
expectedError: "you are not authorized to perform this operation: server returned 401.",
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewPushCommand(test.NewFakeCli(&fakeClient{imagePushFunc: tc.imagePushFunc}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.Error(t, cmd.Execute(), tc.expectedError)
}
}
func TestNewPushCommandSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
trustedPushFunc func(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo,
ref reference.Named, authConfig types.AuthConfig,
requestPrivilege types.RequestPrivilegeFunc) error
}{
{
name: "simple",
args: []string{"image:tag"},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewPushCommand(test.NewFakeCli(&fakeClient{
imagePushFunc: func(ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
return ioutil.NopCloser(strings.NewReader("")), nil
},
}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.NoError(t, cmd.Execute())
}
}

View file

@ -19,7 +19,7 @@ type removeOptions struct {
}
// NewRemoveCommand creates a new `docker remove` command
func NewRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewRemoveCommand(dockerCli command.Cli) *cobra.Command {
var opts removeOptions
cmd := &cobra.Command{
@ -39,14 +39,14 @@ func NewRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
cmd := *NewRemoveCommand(dockerCli)
cmd.Aliases = []string{"rmi", "remove"}
cmd.Use = "rm [OPTIONS] IMAGE [IMAGE...]"
return &cmd
}
func runRemove(dockerCli *command.DockerCli, opts removeOptions, images []string) error {
func runRemove(dockerCli command.Cli, opts removeOptions, images []string) error {
client := dockerCli.Client()
ctx := context.Background()

View file

@ -0,0 +1,103 @@
package image
import (
"bytes"
"fmt"
"io/ioutil"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/docker/docker/pkg/testutil/golden"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestNewRemoveCommandAlias(t *testing.T) {
cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer)))
assert.Equal(t, cmd.HasAlias("rmi"), true)
assert.Equal(t, cmd.HasAlias("remove"), true)
assert.Equal(t, cmd.HasAlias("other"), false)
}
func TestNewRemoveCommandErrors(t *testing.T) {
testCases := []struct {
name string
args []string
expectedError string
imageRemoveFunc func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error)
}{
{
name: "wrong args",
expectedError: "requires at least 1 argument(s).",
},
{
name: "ImageRemove fail",
args: []string{"arg1"},
expectedError: "error removing image",
imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) {
assert.Equal(t, options.Force, false)
assert.Equal(t, options.PruneChildren, true)
return []types.ImageDeleteResponseItem{}, errors.Errorf("error removing image")
},
},
}
for _, tc := range testCases {
cmd := NewRemoveCommand(test.NewFakeCli(&fakeClient{
imageRemoveFunc: tc.imageRemoveFunc,
}, new(bytes.Buffer)))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.Error(t, cmd.Execute(), tc.expectedError)
}
}
func TestNewRemoveCommandSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
imageRemoveFunc func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error)
}{
{
name: "Image Deleted",
args: []string{"image1"},
imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) {
assert.Equal(t, image, "image1")
return []types.ImageDeleteResponseItem{{Deleted: image}}, nil
},
},
{
name: "Image Untagged",
args: []string{"image1"},
imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) {
assert.Equal(t, image, "image1")
return []types.ImageDeleteResponseItem{{Untagged: image}}, nil
},
},
{
name: "Image Deleted and Untagged",
args: []string{"image1", "image2"},
imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) {
if image == "image1" {
return []types.ImageDeleteResponseItem{{Untagged: image}}, nil
}
return []types.ImageDeleteResponseItem{{Deleted: image}}, nil
},
},
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewRemoveCommand(test.NewFakeCli(&fakeClient{
imageRemoveFunc: tc.imageRemoveFunc,
}, buf))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.NoError(t, cmd.Execute())
err := cmd.Execute()
assert.NoError(t, err)
actual := buf.String()
expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("remove-command-success.%s.golden", tc.name))[:])
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected)
}
}

View file

@ -16,7 +16,7 @@ type saveOptions struct {
}
// NewSaveCommand creates a new `docker save` command
func NewSaveCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewSaveCommand(dockerCli command.Cli) *cobra.Command {
var opts saveOptions
cmd := &cobra.Command{
@ -36,7 +36,7 @@ func NewSaveCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func runSave(dockerCli *command.DockerCli, opts saveOptions) error {
func runSave(dockerCli command.Cli, opts saveOptions) error {
if opts.output == "" && dockerCli.Out().IsTerminal() {
return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
}

View file

@ -0,0 +1,98 @@
package image
import (
"bytes"
"io"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/docker/docker/cli/internal/test"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestNewSaveCommandErrors(t *testing.T) {
testCases := []struct {
name string
args []string
isTerminal bool
expectedError string
imageSaveFunc func(images []string) (io.ReadCloser, error)
}{
{
name: "wrong args",
args: []string{},
expectedError: "requires at least 1 argument(s).",
},
{
name: "output to terminal",
args: []string{"output", "file", "arg1"},
isTerminal: true,
expectedError: "Cowardly refusing to save to a terminal. Use the -o flag or redirect.",
},
{
name: "ImageSave fail",
args: []string{"arg1"},
isTerminal: false,
expectedError: "error saving image",
imageSaveFunc: func(images []string) (io.ReadCloser, error) {
return ioutil.NopCloser(strings.NewReader("")), errors.Errorf("error saving image")
},
},
}
for _, tc := range testCases {
cli := test.NewFakeCli(&fakeClient{imageSaveFunc: tc.imageSaveFunc}, new(bytes.Buffer))
cli.Out().SetIsTerminal(tc.isTerminal)
cmd := NewSaveCommand(cli)
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.Error(t, cmd.Execute(), tc.expectedError)
}
}
func TestNewSaveCommandSuccess(t *testing.T) {
testCases := []struct {
args []string
isTerminal bool
imageSaveFunc func(images []string) (io.ReadCloser, error)
deferredFunc func()
}{
{
args: []string{"-o", "save_tmp_file", "arg1"},
isTerminal: true,
imageSaveFunc: func(images []string) (io.ReadCloser, error) {
assert.Equal(t, len(images), 1)
assert.Equal(t, images[0], "arg1")
return ioutil.NopCloser(strings.NewReader("")), nil
},
deferredFunc: func() {
os.Remove("save_tmp_file")
},
},
{
args: []string{"arg1", "arg2"},
isTerminal: false,
imageSaveFunc: func(images []string) (io.ReadCloser, error) {
assert.Equal(t, len(images), 2)
assert.Equal(t, images[0], "arg1")
assert.Equal(t, images[1], "arg2")
return ioutil.NopCloser(strings.NewReader("")), nil
},
},
}
for _, tc := range testCases {
cmd := NewSaveCommand(test.NewFakeCli(&fakeClient{
imageSaveFunc: func(images []string) (io.ReadCloser, error) {
return ioutil.NopCloser(strings.NewReader("")), nil
},
}, new(bytes.Buffer)))
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.NoError(t, cmd.Execute())
if tc.deferredFunc != nil {
tc.deferredFunc()
}
}
}

View file

@ -14,7 +14,7 @@ type tagOptions struct {
}
// NewTagCommand creates a new `docker tag` command
func NewTagCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewTagCommand(dockerCli command.Cli) *cobra.Command {
var opts tagOptions
cmd := &cobra.Command{
@ -34,7 +34,7 @@ func NewTagCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func runTag(dockerCli *command.DockerCli, opts tagOptions) error {
func runTag(dockerCli command.Cli, opts tagOptions) error {
ctx := context.Background()
return dockerCli.Client().ImageTag(ctx, opts.image, opts.name)

View file

@ -0,0 +1,43 @@
package image
import (
"bytes"
"io/ioutil"
"testing"
"github.com/docker/docker/cli/internal/test"
"github.com/stretchr/testify/assert"
)
func TestCliNewTagCommandErrors(t *testing.T) {
testCases := [][]string{
{},
{"image1"},
{"image1", "image2", "image3"},
}
expectedError := "\"tag\" requires exactly 2 argument(s)."
buf := new(bytes.Buffer)
for _, args := range testCases {
cmd := NewTagCommand(test.NewFakeCli(&fakeClient{}, buf))
cmd.SetArgs(args)
cmd.SetOutput(ioutil.Discard)
assert.Error(t, cmd.Execute(), expectedError)
}
}
func TestCliNewTagCommand(t *testing.T) {
buf := new(bytes.Buffer)
cmd := NewTagCommand(
test.NewFakeCli(&fakeClient{
imageTagFunc: func(image string, ref string) error {
assert.Equal(t, image, "image1")
assert.Equal(t, ref, "image2")
return nil
},
}, buf))
cmd.SetArgs([]string{"image1", "image2"})
cmd.SetOutput(ioutil.Discard)
assert.NoError(t, cmd.Execute())
value, _ := cmd.Flags().GetBool("interspersed")
assert.Equal(t, value, false)
}

View file

@ -0,0 +1 @@
1234567890123456789

View file

@ -0,0 +1 @@
tag

View file

@ -0,0 +1,2 @@
IMAGE CREATED CREATED BY SIZE COMMENT
123456789012 Less than a second ago 0B

View file

@ -0,0 +1 @@
file input test

View file

@ -0,0 +1 @@
'image'

View file

@ -0,0 +1,50 @@
[
{
"Id": "",
"RepoTags": null,
"RepoDigests": null,
"Parent": "",
"Comment": "",
"Created": "",
"Container": "",
"ContainerConfig": null,
"DockerVersion": "",
"Author": "",
"Config": null,
"Architecture": "",
"Os": "",
"Size": 0,
"VirtualSize": 0,
"GraphDriver": {
"Data": null,
"Name": ""
},
"RootFS": {
"Type": ""
}
},
{
"Id": "",
"RepoTags": null,
"RepoDigests": null,
"Parent": "",
"Comment": "",
"Created": "",
"Container": "",
"ContainerConfig": null,
"DockerVersion": "",
"Author": "",
"Config": null,
"Architecture": "",
"Os": "",
"Size": 0,
"VirtualSize": 0,
"GraphDriver": {
"Data": null,
"Name": ""
},
"RootFS": {
"Type": ""
}
}
]

View file

@ -0,0 +1,26 @@
[
{
"Id": "",
"RepoTags": null,
"RepoDigests": null,
"Parent": "",
"Comment": "",
"Created": "",
"Container": "",
"ContainerConfig": null,
"DockerVersion": "",
"Author": "",
"Config": null,
"Architecture": "",
"Os": "",
"Size": 0,
"VirtualSize": 0,
"GraphDriver": {
"Data": null,
"Name": ""
},
"RootFS": {
"Type": ""
}
}
]

View file

@ -0,0 +1 @@
REPOSITORY TAG IMAGE ID CREATED SIZE

View file

@ -0,0 +1 @@
REPOSITORY TAG IMAGE ID CREATED SIZE

View file

@ -0,0 +1 @@
REPOSITORY TAG IMAGE ID CREATED SIZE

View file

@ -0,0 +1 @@
Success

View file

@ -0,0 +1 @@
file input test

View file

@ -0,0 +1 @@
1:

View file

@ -0,0 +1 @@
Success

View file

@ -0,0 +1,2 @@
WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N] Total reclaimed space: 0B

View file

@ -0,0 +1,4 @@
Deleted Images:
deleted: image1
Total reclaimed space: 1B

View file

@ -0,0 +1,4 @@
Deleted Images:
untagged: image1
Total reclaimed space: 2B

View file

@ -0,0 +1 @@
Using default tag: latest

View file

@ -0,0 +1,4 @@
Untagged: image1
Deleted: image2
Untagged: image1
Deleted: image2

View file

@ -0,0 +1,2 @@
Deleted: image1
Deleted: image1

View file

@ -0,0 +1,2 @@
Untagged: image1
Untagged: image1

View file

@ -29,7 +29,7 @@ type target struct {
}
// trustedPush handles content trust pushing of an image
func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
func trustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
responseBody, err := imagePushPrivileged(ctx, cli, authConfig, ref, requestPrivilege)
if err != nil {
return err
@ -41,7 +41,7 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry
}
// PushTrustedReference pushes a canonical reference to the trust server.
func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, in io.Reader) error {
func PushTrustedReference(cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, in io.Reader) error {
// If it is a trusted push we would like to find the target entry which match the
// tag provided in the function and then do an AddTarget later.
target := &client.Target{}
@ -202,7 +202,7 @@ func addTargetToAllSignableRoles(repo *client.NotaryRepository, target *client.T
}
// imagePushPrivileged push the image
func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig types.AuthConfig, ref reference.Named, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
func imagePushPrivileged(ctx context.Context, cli command.Cli, authConfig types.AuthConfig, ref reference.Named, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
return nil, err
@ -216,7 +216,7 @@ func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig
}
// trustedPull handles content trust pulling of an image
func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
func trustedPull(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
var refs []target
notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull")
@ -295,7 +295,7 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry
}
// imagePullPrivileged pulls the image and displays it to the output
func imagePullPrivileged(ctx context.Context, cli *command.DockerCli, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc, all bool) error {
func imagePullPrivileged(ctx context.Context, cli command.Cli, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc, all bool) error {
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
if err != nil {
@ -317,7 +317,7 @@ func imagePullPrivileged(ctx context.Context, cli *command.DockerCli, authConfig
}
// TrustedReference returns the canonical trusted reference for an image reference
func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged, rs registry.Service) (reference.Canonical, error) {
func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedTagged, rs registry.Service) (reference.Canonical, error) {
var (
repoInfo *registry.RepositoryInfo
err error
@ -371,7 +371,7 @@ func convertTarget(t client.Target) (target, error) {
}
// TagTrusted tags a trusted ref
func TagTrusted(ctx context.Context, cli *command.DockerCli, trustedRef reference.Canonical, ref reference.NamedTagged) error {
func TagTrusted(ctx context.Context, cli command.Cli, trustedRef reference.Canonical, ref reference.NamedTagged) error {
// Use familiar references when interacting with client and output
familiarRef := reference.FamiliarString(ref)
trustedFamiliarRef := reference.FamiliarString(trustedRef)

View file

@ -1,20 +1,16 @@
package command
import (
"io"
"os"
"runtime"
"errors"
"github.com/docker/docker/pkg/term"
"github.com/pkg/errors"
"io"
"runtime"
)
// InStream is an input stream used by the DockerCli to read user input
type InStream struct {
in io.ReadCloser
fd uintptr
isTerminal bool
state *term.State
CommonStream
in io.ReadCloser
}
func (i *InStream) Read(p []byte) (int, error) {
@ -26,32 +22,6 @@ func (i *InStream) Close() error {
return i.in.Close()
}
// FD returns the file descriptor number for this stream
func (i *InStream) FD() uintptr {
return i.fd
}
// IsTerminal returns true if this stream is connected to a terminal
func (i *InStream) IsTerminal() bool {
return i.isTerminal
}
// SetRawTerminal sets raw mode on the input terminal
func (i *InStream) SetRawTerminal() (err error) {
if os.Getenv("NORAW") != "" || !i.isTerminal {
return nil
}
i.state, err = term.SetRawTerminal(i.fd)
return err
}
// RestoreTerminal restores normal mode to the terminal
func (i *InStream) RestoreTerminal() {
if i.state != nil {
term.RestoreTerminal(i.fd, i.state)
}
}
// 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.
func (i *InStream) CheckTty(attachStdin, ttyMode bool) error {
@ -71,5 +41,5 @@ func (i *InStream) CheckTty(attachStdin, ttyMode bool) error {
// NewInStream returns a new InStream object from a ReadCloser
func NewInStream(in io.ReadCloser) *InStream {
fd, isTerminal := term.GetFdInfo(in)
return &InStream{in: in, fd: fd, isTerminal: isTerminal}
return &InStream{CommonStream: CommonStream{fd: fd, isTerminal: isTerminal}, in: in}
}

View file

@ -1,52 +1,22 @@
package command
import (
"io"
"os"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/term"
"io"
)
// OutStream is an output stream used by the DockerCli to write normal program
// output.
type OutStream struct {
out io.Writer
fd uintptr
isTerminal bool
state *term.State
CommonStream
out io.Writer
}
func (o *OutStream) Write(p []byte) (int, error) {
return o.out.Write(p)
}
// FD returns the file descriptor number for this stream
func (o *OutStream) FD() uintptr {
return o.fd
}
// IsTerminal returns true if this stream is connected to a terminal
func (o *OutStream) IsTerminal() bool {
return o.isTerminal
}
// SetRawTerminal sets raw mode on the output terminal
func (o *OutStream) SetRawTerminal() (err error) {
if os.Getenv("NORAW") != "" || !o.isTerminal {
return nil
}
o.state, err = term.SetRawTerminalOutput(o.fd)
return err
}
// RestoreTerminal restores normal mode to the terminal
func (o *OutStream) RestoreTerminal() {
if o.state != nil {
term.RestoreTerminal(o.fd, o.state)
}
}
// GetTtySize returns the height and width in characters of the tty
func (o *OutStream) GetTtySize() (uint, uint) {
if !o.isTerminal {
@ -65,5 +35,5 @@ func (o *OutStream) GetTtySize() (uint, uint) {
// NewOutStream returns a new OutStream object from a Writer
func NewOutStream(out io.Writer) *OutStream {
fd, isTerminal := term.GetFdInfo(out)
return &OutStream{out: out, fd: fd, isTerminal: isTerminal}
return &OutStream{CommonStream: CommonStream{fd: fd, isTerminal: isTerminal}, out: out}
}

View file

@ -21,7 +21,7 @@ import (
)
// ElectAuthServer returns the default registry to use (by asking the daemon)
func ElectAuthServer(ctx context.Context, cli *DockerCli) string {
func ElectAuthServer(ctx context.Context, cli Cli) string {
// The daemon `/info` endpoint informs us of the default registry being
// used. This is essential in cross-platforms environment, where for
// example a Linux client might be interacting with a Windows daemon, hence
@ -46,7 +46,7 @@ func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
// RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info
// for the given command.
func RegistryAuthenticationPrivilegedFunc(cli *DockerCli, index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc {
func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc {
return func() (string, error) {
fmt.Fprintf(cli.Out(), "\nPlease login prior to %s:\n", cmdName)
indexServer := registry.GetAuthConfigKey(index)
@ -62,7 +62,7 @@ func RegistryAuthenticationPrivilegedFunc(cli *DockerCli, index *registrytypes.I
// ResolveAuthConfig is like registry.ResolveAuthConfig, but if using the
// default index, it uses the default index name for the daemon's platform,
// not the client's platform.
func ResolveAuthConfig(ctx context.Context, cli *DockerCli, index *registrytypes.IndexInfo) types.AuthConfig {
func ResolveAuthConfig(ctx context.Context, cli Cli, index *registrytypes.IndexInfo) types.AuthConfig {
configKey := index.Name
if index.Official {
configKey = ElectAuthServer(ctx, cli)
@ -73,10 +73,10 @@ func ResolveAuthConfig(ctx context.Context, cli *DockerCli, index *registrytypes
}
// ConfigureAuth returns an AuthConfig from the specified user, password and server.
func ConfigureAuth(cli *DockerCli, flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
func ConfigureAuth(cli Cli, flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
// On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
if runtime.GOOS == "windows" {
cli.in = NewInStream(os.Stdin)
cli.SetIn(NewInStream(os.Stdin))
}
if !isDefaultRegistry {
@ -160,7 +160,7 @@ func promptWithDefault(out io.Writer, prompt string, configDefault string) {
}
// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete image
func RetrieveAuthTokenFromImage(ctx context.Context, cli *DockerCli, image string) (string, error) {
func RetrieveAuthTokenFromImage(ctx context.Context, cli Cli, image string) (string, error) {
// Retrieve encoded auth token from the image reference
authConfig, err := resolveAuthConfigFromImage(ctx, cli, image)
if err != nil {
@ -174,7 +174,7 @@ func RetrieveAuthTokenFromImage(ctx context.Context, cli *DockerCli, image strin
}
// resolveAuthConfigFromImage retrieves that AuthConfig using the image string
func resolveAuthConfigFromImage(ctx context.Context, cli *DockerCli, image string) (types.AuthConfig, error) {
func resolveAuthConfigFromImage(ctx context.Context, cli Cli, image string) (types.AuthConfig, error) {
registryRef, err := reference.ParseNormalizedNamed(image)
if err != nil {
return types.AuthConfig{}, err

44
cli/command/stream.go Normal file
View file

@ -0,0 +1,44 @@
package command
import (
"github.com/docker/docker/pkg/term"
"os"
)
// CommonStream is an input stream used by the DockerCli to read user input
type CommonStream struct {
fd uintptr
isTerminal bool
state *term.State
}
// FD returns the file descriptor number for this stream
func (s *CommonStream) FD() uintptr {
return s.fd
}
// IsTerminal returns true if this stream is connected to a terminal
func (s *CommonStream) IsTerminal() bool {
return s.isTerminal
}
// SetRawTerminal sets raw mode on the input terminal
func (s *CommonStream) SetRawTerminal() (err error) {
if os.Getenv("NORAW") != "" || !s.isTerminal {
return nil
}
s.state, err = term.SetRawTerminal(s.fd)
return err
}
// RestoreTerminal restores normal mode to the terminal
func (s *CommonStream) RestoreTerminal() {
if s.state != nil {
term.RestoreTerminal(s.fd, s.state)
}
}
// SetIsTerminal sets the boolean used for isTerminal
func (s *CommonStream) SetIsTerminal(isTerminal bool) {
s.isTerminal = isTerminal
}

View file

@ -8,6 +8,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/pkg/errors"
@ -96,7 +97,7 @@ func TestSwarmUnlock(t *testing.T) {
return nil
},
}, buf)
dockerCli.SetIn(ioutil.NopCloser(strings.NewReader(input)))
dockerCli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input))))
cmd := newUnlockCommand(dockerCli)
assert.NoError(t, cmd.Execute())
}

View file

@ -10,6 +10,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/docker/docker/pkg/testutil/golden"
@ -91,7 +92,7 @@ func TestVolumePrunePromptYes(t *testing.T) {
volumePruneFunc: simplePruneFunc,
}, buf)
cli.SetIn(ioutil.NopCloser(strings.NewReader(input)))
cli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input))))
cmd := NewPruneCommand(
cli,
)
@ -113,7 +114,7 @@ func TestVolumePrunePromptNo(t *testing.T) {
volumePruneFunc: simplePruneFunc,
}, buf)
cli.SetIn(ioutil.NopCloser(strings.NewReader(input)))
cli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input))))
cmd := NewPruneCommand(
cli,
)

View file

@ -7,6 +7,7 @@ import (
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/config/configfile"
"github.com/docker/docker/cli/config/credentials"
"github.com/docker/docker/client"
)
@ -15,23 +16,24 @@ type FakeCli struct {
command.DockerCli
client client.APIClient
configfile *configfile.ConfigFile
out io.Writer
out *command.OutStream
err io.Writer
in io.ReadCloser
in *command.InStream
store credentials.Store
}
// NewFakeCli returns a Cli backed by the fakeCli
func NewFakeCli(client client.APIClient, out io.Writer) *FakeCli {
return &FakeCli{
client: client,
out: out,
out: command.NewOutStream(out),
err: ioutil.Discard,
in: ioutil.NopCloser(strings.NewReader("")),
in: command.NewInStream(ioutil.NopCloser(strings.NewReader(""))),
}
}
// SetIn sets the input of the cli to the specified ReadCloser
func (c *FakeCli) SetIn(in io.ReadCloser) {
func (c *FakeCli) SetIn(in *command.InStream) {
c.in = in
}
@ -52,7 +54,7 @@ func (c *FakeCli) Client() client.APIClient {
// Out returns the output stream (stdout) the cli should write on
func (c *FakeCli) Out() *command.OutStream {
return command.NewOutStream(c.out)
return c.out
}
// Err returns the output stream (stderr) the cli should write on
@ -62,10 +64,18 @@ func (c *FakeCli) Err() io.Writer {
// In returns the input stream the cli will use
func (c *FakeCli) In() *command.InStream {
return command.NewInStream(c.in)
return c.in
}
// ConfigFile returns the cli configfile object (to get client configuration)
func (c *FakeCli) ConfigFile() *configfile.ConfigFile {
return c.configfile
}
// CredentialsStore returns the fake store the cli will use
func (c *FakeCli) CredentialsStore(serverAddress string) credentials.Store {
if c.store == nil {
c.store = NewFakeStore()
}
return c.store
}

View file

@ -0,0 +1,74 @@
package test
import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/cli/config/credentials"
)
// fake store implements a credentials.Store that only acts as an in memory map
type fakeStore struct {
store map[string]types.AuthConfig
eraseFunc func(serverAddress string) error
getFunc func(serverAddress string) (types.AuthConfig, error)
getAllFunc func() (map[string]types.AuthConfig, error)
storeFunc func(authConfig types.AuthConfig) error
}
// NewFakeStore creates a new file credentials store.
func NewFakeStore() credentials.Store {
return &fakeStore{store: map[string]types.AuthConfig{}}
}
func (c *fakeStore) SetStore(store map[string]types.AuthConfig) {
c.store = store
}
func (c *fakeStore) SetEraseFunc(eraseFunc func(string) error) {
c.eraseFunc = eraseFunc
}
func (c *fakeStore) SetGetFunc(getFunc func(string) (types.AuthConfig, error)) {
c.getFunc = getFunc
}
func (c *fakeStore) SetGetAllFunc(getAllFunc func() (map[string]types.AuthConfig, error)) {
c.getAllFunc = getAllFunc
}
func (c *fakeStore) SetStoreFunc(storeFunc func(types.AuthConfig) error) {
c.storeFunc = storeFunc
}
// Erase removes the given credentials from the map store
func (c *fakeStore) Erase(serverAddress string) error {
if c.eraseFunc != nil {
return c.eraseFunc(serverAddress)
}
delete(c.store, serverAddress)
return nil
}
// Get retrieves credentials for a specific server from the map store.
func (c *fakeStore) Get(serverAddress string) (types.AuthConfig, error) {
if c.getFunc != nil {
return c.getFunc(serverAddress)
}
authConfig, _ := c.store[serverAddress]
return authConfig, nil
}
func (c *fakeStore) GetAll() (map[string]types.AuthConfig, error) {
if c.getAllFunc != nil {
return c.getAllFunc()
}
return c.store, nil
}
// Store saves the given credentials in the map store.
func (c *fakeStore) Store(authConfig types.AuthConfig) error {
if c.storeFunc != nil {
return c.storeFunc(authConfig)
}
c.store[authConfig.ServerAddress] = authConfig
return nil
}