mirror of https://github.com/docker/cli.git
Unit tests for cli/commands/image (except build and tag)
Signed-off-by: Ignacio Capurro <icapurrofagian@gmail.com>
This commit is contained in:
parent
2b31a4bf8d
commit
e7793092a2
|
@ -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
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/client"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"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())
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"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)
|
||||
}
|
|
@ -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 != "" {
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"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)
|
||||
}
|
||||
}
|
|
@ -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})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"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())
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"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)
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/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)
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
1234567890123456789
|
|
@ -0,0 +1 @@
|
|||
tag
|
|
@ -0,0 +1,2 @@
|
|||
IMAGE CREATED CREATED BY SIZE COMMENT
|
||||
123456789012 Less than a second ago 0B
|
|
@ -0,0 +1 @@
|
|||
file input test
|
|
@ -0,0 +1 @@
|
|||
'image'
|
|
@ -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": ""
|
||||
}
|
||||
}
|
||||
]
|
|
@ -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": ""
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1 @@
|
|||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
|
@ -0,0 +1 @@
|
|||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
|
@ -0,0 +1 @@
|
|||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
|
@ -0,0 +1 @@
|
|||
Success
|
|
@ -0,0 +1 @@
|
|||
file input test
|
|
@ -0,0 +1 @@
|
|||
1:
|
|
@ -0,0 +1 @@
|
|||
Success
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
Deleted Images:
|
||||
deleted: image1
|
||||
|
||||
Total reclaimed space: 1B
|
|
@ -0,0 +1,4 @@
|
|||
Deleted Images:
|
||||
untagged: image1
|
||||
|
||||
Total reclaimed space: 2B
|
|
@ -0,0 +1 @@
|
|||
Using default tag: latest
|
4
cli/command/image/testdata/remove-command-success.Image Deleted and Untagged.golden
vendored
Normal file
4
cli/command/image/testdata/remove-command-success.Image Deleted and Untagged.golden
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
Untagged: image1
|
||||
Deleted: image2
|
||||
Untagged: image1
|
||||
Deleted: image2
|
|
@ -0,0 +1,2 @@
|
|||
Deleted: image1
|
||||
Deleted: image1
|
|
@ -0,0 +1,2 @@
|
|||
Untagged: image1
|
||||
Untagged: image1
|
|
@ -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
|
||||
|
@ -42,7 +42,7 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry
|
|||
|
||||
// PushTrustedReference pushes a canonical reference to the trust server.
|
||||
// nolint: gocyclo
|
||||
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{}
|
||||
|
@ -203,7 +203,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
|
||||
|
@ -217,7 +217,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")
|
||||
|
@ -296,7 +296,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 {
|
||||
|
@ -318,7 +318,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
|
||||
|
@ -372,7 +372,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)
|
||||
|
|
|
@ -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 {
|
||||
CommonStream
|
||||
in io.ReadCloser
|
||||
fd uintptr
|
||||
isTerminal bool
|
||||
state *term.State
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
CommonStream
|
||||
out io.Writer
|
||||
fd uintptr
|
||||
isTerminal bool
|
||||
state *term.State
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/config/credentials"
|
||||
"github.com/docker/cli/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
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"github.com/docker/cli/cli/config/credentials"
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
Loading…
Reference in New Issue