mirror of
https://github.com/docker/compose.git
synced 2025-07-31 01:24:15 +02:00
Implement context list
This commit is contained in:
parent
3bb4fe163c
commit
e2c7370a82
@ -29,6 +29,9 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/docker/api/context/store"
|
"github.com/docker/api/context/store"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -45,6 +48,7 @@ func ContextCommand() *cobra.Command {
|
|||||||
|
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
createCommand(),
|
createCommand(),
|
||||||
|
listCommand(),
|
||||||
)
|
)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
@ -70,6 +74,17 @@ func createCommand() *cobra.Command {
|
|||||||
return cmd
|
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 {
|
func runCreate(ctx context.Context, opts createOpts, name string, contextType string) error {
|
||||||
s := store.ContextStore(ctx)
|
s := store.ContextStore(ctx)
|
||||||
return s.Create(name, store.TypeContext{
|
return s.Create(name, store.TypeContext{
|
||||||
@ -81,3 +96,25 @@ func runCreate(ctx context.Context, opts createOpts, name string, contextType st
|
|||||||
"docker": CliContext{},
|
"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
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store
|
||||||
type Store interface {
|
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)
|
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
|
Create(name string, data interface{}, endpoints map[string]interface{}) error
|
||||||
|
// List returns the list of created contexts
|
||||||
|
List() ([]*Metadata, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type store struct {
|
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)
|
meta := filepath.Join(s.root, contextsDir, metadataDir, contextdirOf(name), metaFile)
|
||||||
|
return read(meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func read(meta string) (*Metadata, error) {
|
||||||
bytes, err := ioutil.ReadFile(meta)
|
bytes, err := ioutil.ReadFile(meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &Metadata{
|
var r untypedContextMetadata
|
||||||
Endpoints: make(map[string]interface{}),
|
if err := json.Unmarshal(bytes, &r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &Metadata{
|
||||||
|
Name: r.Name,
|
||||||
|
Endpoints: r.Endpoints,
|
||||||
}
|
}
|
||||||
|
|
||||||
typed := getter()
|
typed := getter()
|
||||||
if err := json.Unmarshal(bytes, typed); err != nil {
|
if err := json.Unmarshal(r.Metadata, typed); err != nil {
|
||||||
return r, err
|
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 {
|
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)
|
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 {
|
func contextdirOf(name string) string {
|
||||||
return digest.FromString(name).Encoded()
|
return digest.FromString(name).Encoded()
|
||||||
}
|
}
|
||||||
@ -147,9 +186,15 @@ type Metadata struct {
|
|||||||
Endpoints map[string]interface{} `json:",omitempty"`
|
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 TypeContext struct {
|
||||||
Type string
|
Type string `json:",omitempty"`
|
||||||
Description string
|
Description string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getter() interface{} {
|
func getter() interface{} {
|
||||||
|
@ -29,6 +29,7 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "crypto/sha256"
|
_ "crypto/sha256"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
@ -64,10 +65,38 @@ func TestCreate(t *testing.T) {
|
|||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
setup(t, func(t *testing.T, store Store) {
|
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)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
meta, err := store.Get("test")
|
meta, err := store.Get("test")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotNil(t, meta)
|
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/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.5.1
|
||||||
github.com/urfave/cli/v2 v2.2.0
|
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
|
golang.org/x/text v0.3.2 // indirect
|
||||||
google.golang.org/grpc v1.29.1
|
google.golang.org/grpc v1.29.1
|
||||||
google.golang.org/protobuf v1.21.0
|
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-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 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-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/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-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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-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 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-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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user