Merge pull request #654 from dnephin/pr-fix-image-rm

Fix behaviour of `rmi -f` with unexpected errors
This commit is contained in:
Vincent Demeester 2017-10-31 10:38:38 +01:00 committed by GitHub
commit 6e04da39b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 20 deletions

View File

@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
apiclient "github.com/docker/docker/client"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -56,9 +57,13 @@ func runRemove(dockerCli command.Cli, opts removeOptions, images []string) error
} }
var errs []string var errs []string
var fatalErr = false
for _, img := range images { for _, img := range images {
dels, err := client.ImageRemove(ctx, img, options) dels, err := client.ImageRemove(ctx, img, options)
if err != nil { if err != nil {
if !apiclient.IsErrNotFound(err) {
fatalErr = true
}
errs = append(errs, err.Error()) errs = append(errs, err.Error())
} else { } else {
for _, del := range dels { for _, del := range dels {
@ -73,7 +78,7 @@ func runRemove(dockerCli command.Cli, opts removeOptions, images []string) error
if len(errs) > 0 { if len(errs) > 0 {
msg := strings.Join(errs, "\n") msg := strings.Join(errs, "\n")
if !opts.force { if !opts.force || fatalErr {
return errors.New(msg) return errors.New(msg)
} }
fmt.Fprintf(dockerCli.Err(), msg) fmt.Fprintf(dockerCli.Err(), msg)

View File

@ -13,6 +13,18 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
type notFound struct {
imageID string
}
func (n notFound) Error() string {
return fmt.Sprintf("Error: No such image: %s", n.imageID)
}
func (n notFound) NotFound() bool {
return true
}
func TestNewRemoveCommandAlias(t *testing.T) { func TestNewRemoveCommandAlias(t *testing.T) {
cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{})) cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}))
assert.True(t, cmd.HasAlias("rmi")) assert.True(t, cmd.HasAlias("rmi"))
@ -31,6 +43,15 @@ func TestNewRemoveCommandErrors(t *testing.T) {
name: "wrong args", name: "wrong args",
expectedError: "requires at least 1 argument.", expectedError: "requires at least 1 argument.",
}, },
{
name: "ImageRemove fail with force option",
args: []string{"-f", "image1"},
expectedError: "error removing image",
imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) {
assert.Equal(t, "image1", image)
return []types.ImageDeleteResponseItem{}, errors.Errorf("error removing image")
},
},
{ {
name: "ImageRemove fail", name: "ImageRemove fail",
args: []string{"arg1"}, args: []string{"arg1"},
@ -43,12 +64,14 @@ func TestNewRemoveCommandErrors(t *testing.T) {
}, },
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cmd := NewRemoveCommand(test.NewFakeCli(&fakeClient{ cmd := NewRemoveCommand(test.NewFakeCli(&fakeClient{
imageRemoveFunc: tc.imageRemoveFunc, imageRemoveFunc: tc.imageRemoveFunc,
})) }))
cmd.SetOutput(ioutil.Discard) cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args) cmd.SetArgs(tc.args)
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
})
} }
} }
@ -57,7 +80,7 @@ func TestNewRemoveCommandSuccess(t *testing.T) {
name string name string
args []string args []string
imageRemoveFunc func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) imageRemoveFunc func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error)
expectedErrMsg string expectedStderr string
}{ }{
{ {
name: "Image Deleted", name: "Image Deleted",
@ -68,14 +91,16 @@ func TestNewRemoveCommandSuccess(t *testing.T) {
}, },
}, },
{ {
name: "Image Deleted with force option", name: "Image not found with force option",
args: []string{"-f", "image1"}, args: []string{"-f", "image1"},
imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) {
assert.Equal(t, "image1", image) assert.Equal(t, "image1", image)
return []types.ImageDeleteResponseItem{}, errors.Errorf("error removing image") assert.Equal(t, true, options.Force)
return []types.ImageDeleteResponseItem{}, notFound{"image1"}
}, },
expectedErrMsg: "error removing image", expectedStderr: "Error: No such image: image1",
}, },
{ {
name: "Image Untagged", name: "Image Untagged",
args: []string{"image1"}, args: []string{"image1"},
@ -96,14 +121,14 @@ func TestNewRemoveCommandSuccess(t *testing.T) {
}, },
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{imageRemoveFunc: tc.imageRemoveFunc}) cli := test.NewFakeCli(&fakeClient{imageRemoveFunc: tc.imageRemoveFunc})
cmd := NewRemoveCommand(cli) cmd := NewRemoveCommand(cli)
cmd.SetOutput(ioutil.Discard) cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args) cmd.SetArgs(tc.args)
assert.NoError(t, cmd.Execute()) assert.NoError(t, cmd.Execute())
if tc.expectedErrMsg != "" { assert.Equal(t, tc.expectedStderr, cli.ErrBuffer().String())
assert.Equal(t, tc.expectedErrMsg, cli.ErrBuffer().String())
}
golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("remove-command-success.%s.golden", tc.name)) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("remove-command-success.%s.golden", tc.name))
})
} }
} }