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"
|
"os"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/docker/api/context/store"
|
"github.com/docker/api/context/store"
|
||||||
|
@ -48,6 +49,7 @@ func ContextCommand() *cobra.Command {
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
createCommand(),
|
createCommand(),
|
||||||
listCommand(),
|
listCommand(),
|
||||||
|
removeCommand(),
|
||||||
)
|
)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
@ -91,6 +93,17 @@ func listCommand() *cobra.Command {
|
||||||
return cmd
|
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 {
|
func runCreate(ctx context.Context, opts createOpts, name string, contextType string) error {
|
||||||
switch contextType {
|
switch contextType {
|
||||||
case "aci":
|
case "aci":
|
||||||
|
@ -134,3 +147,16 @@ func runList(ctx context.Context) error {
|
||||||
|
|
||||||
return w.Flush()
|
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"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/docker/api/errdefs"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -63,13 +65,15 @@ type Store interface {
|
||||||
// Get returns the context with name, it returns an error if the context
|
// Get returns the context with name, it returns an error if the context
|
||||||
// doesn't exist
|
// doesn't exist
|
||||||
Get(name string, getter func() interface{}) (*Metadata, error)
|
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
|
GetType(meta *Metadata) string
|
||||||
// Create creates a new context, it returns an error if a context with the
|
// Create creates a new context, it returns an error if a context with the
|
||||||
// same name exists already.
|
// same name exists already.
|
||||||
Create(name string, data TypedContext) error
|
Create(name string, data TypedContext) error
|
||||||
// List returns the list of created contexts
|
// List returns the list of created contexts
|
||||||
List() ([]*Metadata, error)
|
List() ([]*Metadata, error)
|
||||||
|
// Remove removes a context by name from the context store
|
||||||
|
Remove(name string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type store struct {
|
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)
|
meta := filepath.Join(s.root, contextsDir, metadataDir, contextdirOf(name), metaFile)
|
||||||
m, err := read(meta, getter)
|
m, err := read(meta, getter)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil, fmt.Errorf("unknown context %q", name)
|
return nil, errors.Wrap(errdefs.ErrNotFound, objectName(name))
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -186,7 +190,7 @@ func (s *store) Create(name string, data TypedContext) error {
|
||||||
dir := contextdirOf(name)
|
dir := contextdirOf(name)
|
||||||
metaDir := filepath.Join(s.root, contextsDir, metadataDir, dir)
|
metaDir := filepath.Join(s.root, contextsDir, metadataDir, dir)
|
||||||
if _, err := os.Stat(metaDir); !os.IsNotExist(err) {
|
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)
|
err := os.Mkdir(metaDir, 0755)
|
||||||
|
@ -237,10 +241,26 @@ func (s *store) List() ([]*Metadata, error) {
|
||||||
return result, nil
|
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 {
|
func contextdirOf(name string) string {
|
||||||
return digest.FromString(name).Encoded()
|
return digest.FromString(name).Encoded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func objectName(name string) string {
|
||||||
|
return fmt.Sprintf("context %q", name)
|
||||||
|
}
|
||||||
|
|
||||||
type dummyContext struct{}
|
type dummyContext struct{}
|
||||||
|
|
||||||
// Metadata represents the docker context metadata
|
// Metadata represents the docker context metadata
|
||||||
|
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/api/errdefs"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
|
@ -62,12 +63,17 @@ func (suite *StoreTestSuite) AfterTest(suiteName, testName string) {
|
||||||
func (suite *StoreTestSuite) TestCreate() {
|
func (suite *StoreTestSuite) TestCreate() {
|
||||||
err := suite.store.Create("test", TypedContext{})
|
err := suite.store.Create("test", TypedContext{})
|
||||||
require.Nil(suite.T(), err)
|
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() {
|
func (suite *StoreTestSuite) TestGetUnknown() {
|
||||||
meta, err := suite.store.Get("unknown", nil)
|
meta, err := suite.store.Get("unknown", nil)
|
||||||
require.Nil(suite.T(), meta)
|
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() {
|
func (suite *StoreTestSuite) TestGet() {
|
||||||
|
@ -101,6 +107,26 @@ func (suite *StoreTestSuite) TestList() {
|
||||||
require.Equal(suite.T(), contexts[1].Name, "test2")
|
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) {
|
func TestExampleTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(StoreTestSuite))
|
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/gobwas/ws v1.0.3
|
||||||
github.com/golang/protobuf v1.4.0
|
github.com/golang/protobuf v1.4.0
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.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/mitchellh/go-homedir v1.1.0
|
||||||
github.com/onsi/gomega v1.9.0
|
github.com/onsi/gomega v1.9.0
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1
|
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 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
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/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/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 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
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()
|
NewDockerCommand("context", "create", "test-example", "example").ExecOrDie()
|
||||||
//Expect(output).To(ContainSubstring("test-example context acitest created"))
|
//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() {
|
It("uses the test context", func() {
|
||||||
currentContext := NewCommand("docker", "context", "use", "test-example").ExecOrDie()
|
currentContext := NewCommand("docker", "context", "use", "test-example").ExecOrDie()
|
||||||
|
|
Loading…
Reference in New Issue