mirror of https://github.com/docker/compose.git
Implement context list
This commit is contained in:
parent
3bb4fe163c
commit
e2c7370a82
|
@ -29,6 +29,9 @@ package cmd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/docker/api/context/store"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -45,6 +48,7 @@ func ContextCommand() *cobra.Command {
|
|||
|
||||
cmd.AddCommand(
|
||||
createCommand(),
|
||||
listCommand(),
|
||||
)
|
||||
|
||||
return cmd
|
||||
|
@ -70,6 +74,17 @@ func createCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func listCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runList(cmd.Context())
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(ctx context.Context, opts createOpts, name string, contextType string) error {
|
||||
s := store.ContextStore(ctx)
|
||||
return s.Create(name, store.TypeContext{
|
||||
|
@ -81,3 +96,25 @@ func runCreate(ctx context.Context, opts createOpts, name string, contextType st
|
|||
"docker": CliContext{},
|
||||
})
|
||||
}
|
||||
|
||||
func runList(ctx context.Context) error {
|
||||
s := store.ContextStore(ctx)
|
||||
contexts, err := s.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
|
||||
fmt.Fprintln(w, "NAME\tDESCRIPTION\tTYPE")
|
||||
format := "%s\t%s\t%s\n"
|
||||
|
||||
for _, c := range contexts {
|
||||
meta, ok := c.Metadata.(store.TypeContext)
|
||||
if !ok {
|
||||
return fmt.Errorf("Unable to list contexts, context %q is not valid", c.Name)
|
||||
}
|
||||
fmt.Fprintf(w, format, c.Name, meta.Description, meta.Type)
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
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 context
|
||||
|
||||
type TypeContext struct {
|
||||
Type string
|
||||
}
|
|
@ -56,9 +56,16 @@ func ContextStore(ctx context.Context) Store {
|
|||
return s
|
||||
}
|
||||
|
||||
// Store
|
||||
type Store interface {
|
||||
// Get returns the context with with name, it returns an error if the
|
||||
// context doesn't exist
|
||||
Get(name string) (*Metadata, error)
|
||||
// Create creates a new context, it returns an error if a context with the
|
||||
// same name exists already.
|
||||
Create(name string, data interface{}, endpoints map[string]interface{}) error
|
||||
// List returns the list of created contexts
|
||||
List() ([]*Metadata, error)
|
||||
}
|
||||
|
||||
type store struct {
|
||||
|
@ -92,23 +99,33 @@ func (s *store) Get(name string) (*Metadata, error) {
|
|||
}
|
||||
|
||||
meta := filepath.Join(s.root, contextsDir, metadataDir, contextdirOf(name), metaFile)
|
||||
return read(meta)
|
||||
}
|
||||
|
||||
func read(meta string) (*Metadata, error) {
|
||||
bytes, err := ioutil.ReadFile(meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := &Metadata{
|
||||
Endpoints: make(map[string]interface{}),
|
||||
var r untypedContextMetadata
|
||||
if err := json.Unmarshal(bytes, &r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &Metadata{
|
||||
Name: r.Name,
|
||||
Endpoints: r.Endpoints,
|
||||
}
|
||||
|
||||
typed := getter()
|
||||
if err := json.Unmarshal(bytes, typed); err != nil {
|
||||
return r, err
|
||||
if err := json.Unmarshal(r.Metadata, typed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.Metadata = reflect.ValueOf(typed).Elem().Interface()
|
||||
result.Metadata = reflect.ValueOf(typed).Elem().Interface()
|
||||
|
||||
return r, nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *store) Create(name string, data interface{}, endpoints map[string]interface{}) error {
|
||||
|
@ -137,6 +154,28 @@ func (s *store) Create(name string, data interface{}, endpoints map[string]inter
|
|||
return ioutil.WriteFile(filepath.Join(metaDir, metaFile), bytes, 0644)
|
||||
}
|
||||
|
||||
func (s *store) List() ([]*Metadata, error) {
|
||||
root := filepath.Join(s.root, contextsDir, metadataDir)
|
||||
c, err := ioutil.ReadDir(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []*Metadata
|
||||
for _, fi := range c {
|
||||
if fi.IsDir() {
|
||||
meta := filepath.Join(root, fi.Name(), metaFile)
|
||||
r, err := read(meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, r)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func contextdirOf(name string) string {
|
||||
return digest.FromString(name).Encoded()
|
||||
}
|
||||
|
@ -147,9 +186,15 @@ type Metadata struct {
|
|||
Endpoints map[string]interface{} `json:",omitempty"`
|
||||
}
|
||||
|
||||
type untypedContextMetadata struct {
|
||||
Metadata json.RawMessage `json:"metadata,omitempty"`
|
||||
Endpoints map[string]interface{} `json:"endpoints,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
type TypeContext struct {
|
||||
Type string
|
||||
Description string
|
||||
Type string `json:",omitempty"`
|
||||
Description string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func getter() interface{} {
|
||||
|
|
|
@ -29,6 +29,7 @@ package store
|
|||
|
||||
import (
|
||||
_ "crypto/sha256"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
@ -64,10 +65,38 @@ func TestCreate(t *testing.T) {
|
|||
|
||||
func TestGet(t *testing.T) {
|
||||
setup(t, func(t *testing.T, store Store) {
|
||||
err := store.Create("test", nil, nil)
|
||||
err := store.Create("test", TypeContext{
|
||||
Type: "type",
|
||||
Description: "description",
|
||||
}, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
meta, err := store.Get("test")
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, meta)
|
||||
assert.Equal(t, "test", meta.Name)
|
||||
|
||||
m, ok := meta.Metadata.(TypeContext)
|
||||
assert.Equal(t, ok, true)
|
||||
fmt.Printf("%#v\n", meta)
|
||||
assert.Equal(t, "description", m.Description)
|
||||
assert.Equal(t, "type", m.Type)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
setup(t, func(t *testing.T, store Store) {
|
||||
err := store.Create("test1", TypeContext{}, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = store.Create("test2", TypeContext{}, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
contexts, err := store.List()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, len(contexts), 2)
|
||||
assert.Equal(t, contexts[0].Name, "test1")
|
||||
assert.Equal(t, contexts[1].Name, "test2")
|
||||
})
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -14,7 +14,7 @@ require (
|
|||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/urfave/cli/v2 v2.2.0
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 // indirect
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
google.golang.org/grpc v1.29.1
|
||||
google.golang.org/protobuf v1.21.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -197,6 +197,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowK
|
|||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -213,6 +215,8 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/p
|
|||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
|
|
Loading…
Reference in New Issue