mirror of https://github.com/docker/compose.git
Merge pull request #79 from chris-crone/context-rm
Add context rm command
This commit is contained in:
commit
102116315a
|
@ -33,6 +33,7 @@ import (
|
|||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/api/context/store"
|
||||
|
@ -48,6 +49,7 @@ func ContextCommand() *cobra.Command {
|
|||
cmd.AddCommand(
|
||||
createCommand(),
|
||||
listCommand(),
|
||||
removeCommand(),
|
||||
)
|
||||
|
||||
return cmd
|
||||
|
@ -91,6 +93,17 @@ func listCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func removeCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "rm",
|
||||
Aliases: []string{"remove"},
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runRemove(cmd.Context(), args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runCreate(ctx context.Context, opts createOpts, name string, contextType string) error {
|
||||
switch contextType {
|
||||
case "aci":
|
||||
|
@ -134,3 +147,16 @@ func runList(ctx context.Context) error {
|
|||
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
func runRemove(ctx context.Context, args []string) error {
|
||||
s := store.ContextStore(ctx)
|
||||
var errs *multierror.Error
|
||||
for _, n := range args {
|
||||
if err := s.Remove(n); err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
} else {
|
||||
fmt.Println(n)
|
||||
}
|
||||
}
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
|
|
@ -36,7 +36,9 @@ import (
|
|||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
"github.com/docker/api/errdefs"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -63,13 +65,15 @@ type Store interface {
|
|||
// Get returns the context with name, it returns an error if the context
|
||||
// doesn't exist
|
||||
Get(name string, getter func() interface{}) (*Metadata, error)
|
||||
// GetType reurns the type of the context (docker, aci etc)
|
||||
// GetType returns the type of the context (docker, aci etc)
|
||||
GetType(meta *Metadata) string
|
||||
// Create creates a new context, it returns an error if a context with the
|
||||
// same name exists already.
|
||||
Create(name string, data TypedContext) error
|
||||
// List returns the list of created contexts
|
||||
List() ([]*Metadata, error)
|
||||
// Remove removes a context by name from the context store
|
||||
Remove(name string) error
|
||||
}
|
||||
|
||||
type store struct {
|
||||
|
@ -118,7 +122,7 @@ func (s *store) Get(name string, getter func() interface{}) (*Metadata, error) {
|
|||
meta := filepath.Join(s.root, contextsDir, metadataDir, contextdirOf(name), metaFile)
|
||||
m, err := read(meta, getter)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("unknown context %q", name)
|
||||
return nil, errors.Wrap(errdefs.ErrNotFound, objectName(name))
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -186,7 +190,7 @@ func (s *store) Create(name string, data TypedContext) error {
|
|||
dir := contextdirOf(name)
|
||||
metaDir := filepath.Join(s.root, contextsDir, metadataDir, dir)
|
||||
if _, err := os.Stat(metaDir); !os.IsNotExist(err) {
|
||||
return fmt.Errorf("context %q already exists", name)
|
||||
return errors.Wrap(errdefs.ErrAlreadyExists, objectName(name))
|
||||
}
|
||||
|
||||
err := os.Mkdir(metaDir, 0755)
|
||||
|
@ -237,10 +241,26 @@ func (s *store) List() ([]*Metadata, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func (s *store) Remove(name string) error {
|
||||
dir := filepath.Join(s.root, contextsDir, metadataDir, contextdirOf(name))
|
||||
// Check if directory exists because os.RemoveAll returns nil if it doesn't
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return errors.Wrap(errdefs.ErrNotFound, objectName(name))
|
||||
}
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
return errors.Wrapf(errdefs.ErrUnknown, "unable to remove %s: %s", objectName(name), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func contextdirOf(name string) string {
|
||||
return digest.FromString(name).Encoded()
|
||||
}
|
||||
|
||||
func objectName(name string) string {
|
||||
return fmt.Sprintf("context %q", name)
|
||||
}
|
||||
|
||||
type dummyContext struct{}
|
||||
|
||||
// Metadata represents the docker context metadata
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/api/errdefs"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
@ -62,12 +63,17 @@ func (suite *StoreTestSuite) AfterTest(suiteName, testName string) {
|
|||
func (suite *StoreTestSuite) TestCreate() {
|
||||
err := suite.store.Create("test", TypedContext{})
|
||||
require.Nil(suite.T(), err)
|
||||
|
||||
err = suite.store.Create("test", TypedContext{})
|
||||
require.EqualError(suite.T(), err, `context "test": already exists`)
|
||||
require.True(suite.T(), errdefs.IsAlreadyExistsError(err))
|
||||
}
|
||||
|
||||
func (suite *StoreTestSuite) TestGetUnknown() {
|
||||
meta, err := suite.store.Get("unknown", nil)
|
||||
require.Nil(suite.T(), meta)
|
||||
require.Error(suite.T(), err)
|
||||
require.EqualError(suite.T(), err, `context "unknown": not found`)
|
||||
require.True(suite.T(), errdefs.IsNotFoundError(err))
|
||||
}
|
||||
|
||||
func (suite *StoreTestSuite) TestGet() {
|
||||
|
@ -101,6 +107,26 @@ func (suite *StoreTestSuite) TestList() {
|
|||
require.Equal(suite.T(), contexts[1].Name, "test2")
|
||||
}
|
||||
|
||||
func (suite *StoreTestSuite) TestRemoveNotFound() {
|
||||
err := suite.store.Remove("notfound")
|
||||
require.EqualError(suite.T(), err, `context "notfound": not found`)
|
||||
require.True(suite.T(), errdefs.IsNotFoundError(err))
|
||||
}
|
||||
|
||||
func (suite *StoreTestSuite) TestRemove() {
|
||||
err := suite.store.Create("testremove", TypedContext{})
|
||||
require.Nil(suite.T(), err)
|
||||
contexts, err := suite.store.List()
|
||||
require.Nil(suite.T(), err)
|
||||
require.Equal(suite.T(), len(contexts), 1)
|
||||
|
||||
err = suite.store.Remove("testremove")
|
||||
require.Nil(suite.T(), err)
|
||||
contexts, err = suite.store.List()
|
||||
require.Nil(suite.T(), err)
|
||||
require.Equal(suite.T(), len(contexts), 0)
|
||||
}
|
||||
|
||||
func TestExampleTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(StoreTestSuite))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright (c) 2020 Docker Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package errdefs
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotFound is returned when an object is not found
|
||||
ErrNotFound = errors.New("not found")
|
||||
// ErrAlreadyExists is returned when an object already exists
|
||||
ErrAlreadyExists = errors.New("already exists")
|
||||
// ErrUnknown is returned when the error type is unmapped
|
||||
ErrUnknown = errors.New("unknown")
|
||||
)
|
||||
|
||||
// IsNotFoundError returns true if the unwrapped error is ErrNotFound
|
||||
func IsNotFoundError(err error) bool {
|
||||
return errors.Is(err, ErrNotFound)
|
||||
}
|
||||
|
||||
// IsAlreadyExistsError returns true if the unwrapped error is ErrAlreadyExists
|
||||
func IsAlreadyExistsError(err error) bool {
|
||||
return errors.Is(err, ErrAlreadyExists)
|
||||
}
|
||||
|
||||
// IsUnknownError returns true if the unwrapped error is ErrUnknown
|
||||
func IsUnknownError(err error) bool {
|
||||
return errors.Is(err, ErrUnknown)
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright (c) 2020 Docker Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package errdefs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIsNotFound(t *testing.T) {
|
||||
err := errors.Wrap(ErrNotFound, `object "name"`)
|
||||
require.True(t, IsNotFoundError(err))
|
||||
|
||||
require.False(t, IsNotFoundError(errors.New("another error")))
|
||||
}
|
||||
|
||||
func TestIsAlreadyExists(t *testing.T) {
|
||||
err := errors.Wrap(ErrAlreadyExists, `object "name"`)
|
||||
require.True(t, IsAlreadyExistsError(err))
|
||||
|
||||
require.False(t, IsAlreadyExistsError(errors.New("another error")))
|
||||
}
|
1
go.mod
1
go.mod
|
@ -17,6 +17,7 @@ require (
|
|||
github.com/gobwas/ws v1.0.3
|
||||
github.com/golang/protobuf v1.4.0
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
github.com/hashicorp/go-multierror v1.1.0
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/onsi/gomega v1.9.0
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1
|
||||
|
|
4
go.sum
4
go.sum
|
@ -119,6 +119,10 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg
|
|||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
|
|
|
@ -50,7 +50,7 @@ func main() {
|
|||
NewDockerCommand("context", "create", "test-example", "example").ExecOrDie()
|
||||
//Expect(output).To(ContainSubstring("test-example context acitest created"))
|
||||
})
|
||||
defer NewCommand("docker", "context", "rm", "test-example", "-f").ExecOrDie()
|
||||
defer NewDockerCommand("context", "rm", "test-example").ExecOrDie()
|
||||
|
||||
It("uses the test context", func() {
|
||||
currentContext := NewCommand("docker", "context", "use", "test-example").ExecOrDie()
|
||||
|
|
Loading…
Reference in New Issue