1
0
Djordje Lukic 5 жил өмнө
parent
commit
e2c7370a82

+ 37 - 0
cli/cmd/context.go

@@ -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()
+}

+ 0 - 32
context/context.go

@@ -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
-}

+ 53 - 8
context/store/store.go

@@ -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{} {

+ 30 - 1
context/store/store_test.go

@@ -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")
 	})
 }

+ 1 - 1
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 - 0
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=