docker context rm: allow --force to ignore non-existing contexts

Before this change, running `docker context rm --force` would fail if the context
did not exist. This behavior was different from other commands, which allowed
ignoring non-existing objects.

For example; when trying to remove a non-existing volume, the command would
fail without "force":

```bash
docker volume rm nosuchvolume
Error: No such volume: nosuchvolume
echo $?
1
```

But using the `-f` / `--force` option would make the command complete successfully
(the error itself is still printed for informational purposes);

```bash
docker volume rm -f nosuchvolume
nosuchvolume
echo $?
0
```

With this patch, `docker context rm` behaves the same:

```bash
docker context rm nosuchcontext
context "nosuchcontext" does not exist
echo $?
1
```

```bash
docker context rm -f nosuchcontext
nosuchcontext
echo $?
0
```

This patch also simplifies how we check if the context exists; previously we
would try to read the context's metadata; this could fail if a context was
corrupted, or if an empty directory was present. This patch now only checks
if the directory exists, without first validating the context's data.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2022-09-29 02:01:09 +02:00
parent 3dad26ca2d
commit 9a493b1bf7
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
2 changed files with 24 additions and 4 deletions

View File

@ -1,12 +1,14 @@
package context package context
import ( import (
"errors"
"fmt" "fmt"
"os"
"strings" "strings"
"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/errdefs"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -50,9 +52,6 @@ func RunRemove(dockerCli command.Cli, opts RemoveOptions, names []string) error
} }
func doRemove(dockerCli command.Cli, name string, isCurrent, force bool) error { func doRemove(dockerCli command.Cli, name string, isCurrent, force bool) error {
if _, err := dockerCli.ContextStore().GetMetadata(name); err != nil {
return err
}
if isCurrent { if isCurrent {
if !force { if !force {
return errors.New("context is in use, set -f flag to force remove") return errors.New("context is in use, set -f flag to force remove")
@ -64,5 +63,23 @@ func doRemove(dockerCli command.Cli, name string, isCurrent, force bool) error {
return err return err
} }
} }
if !force {
if err := checkContextExists(dockerCli, name); err != nil {
return err
}
}
return dockerCli.ContextStore().Remove(name) return dockerCli.ContextStore().Remove(name)
} }
// checkContextExists returns an error if the context directory does not exist.
func checkContextExists(dockerCli command.Cli, name string) error {
contextDir := dockerCli.ContextStore().GetStorageInfo(name).MetadataPath
_, err := os.Stat(contextDir)
if os.IsNotExist(err) {
return errdefs.NotFound(errors.Errorf("context %q does not exist", name))
}
// Ignore other errors; if relevant, they will produce an error when
// performing the actual delete.
return nil
}

View File

@ -27,6 +27,9 @@ func TestRemoveNotAContext(t *testing.T) {
createTestContext(t, cli, "other") createTestContext(t, cli, "other")
err := RunRemove(cli, RemoveOptions{}, []string{"not-a-context"}) err := RunRemove(cli, RemoveOptions{}, []string{"not-a-context"})
assert.ErrorContains(t, err, `context "not-a-context" does not exist`) assert.ErrorContains(t, err, `context "not-a-context" does not exist`)
err = RunRemove(cli, RemoveOptions{Force: true}, []string{"not-a-context"})
assert.NilError(t, err)
} }
func TestRemoveCurrent(t *testing.T) { func TestRemoveCurrent(t *testing.T) {