mirror of https://github.com/docker/compose.git
Merge pull request #200 from docker/context_store_type
Store context type in metadata to make retrocompatibility with previous contexts easier (potentially switching back and forth)
This commit is contained in:
commit
93623dc5aa
|
@ -79,7 +79,7 @@ func runList(ctx context.Context) error {
|
||||||
fmt.Fprintf(w,
|
fmt.Fprintf(w,
|
||||||
format,
|
format,
|
||||||
contextName,
|
contextName,
|
||||||
c.Type,
|
c.Type(),
|
||||||
c.Metadata.Description,
|
c.Metadata.Description,
|
||||||
getEndpoint("docker", c.Endpoints),
|
getEndpoint("docker", c.Endpoints),
|
||||||
getEndpoint("kubernetes", c.Endpoints),
|
getEndpoint("kubernetes", c.Endpoints),
|
||||||
|
|
|
@ -49,13 +49,13 @@ func New(ctx context.Context) (*Client, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
service, err := backend.Get(ctx, cc.Type)
|
service, err := backend.Get(ctx, cc.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
backendType: cc.Type,
|
backendType: cc.Type(),
|
||||||
bs: service,
|
bs: service,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// DockerContext represents the docker context metadata
|
||||||
|
type DockerContext struct {
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Metadata ContextMetadata `json:",omitempty"`
|
||||||
|
Endpoints map[string]interface{} `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type the context type
|
||||||
|
func (m *DockerContext) Type() string {
|
||||||
|
if m.Metadata.Type == "" {
|
||||||
|
return defaultContextType
|
||||||
|
}
|
||||||
|
return m.Metadata.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextMetadata is represtentation of the data we put in a context
|
||||||
|
// metadata
|
||||||
|
type ContextMetadata struct {
|
||||||
|
Type string
|
||||||
|
Description string
|
||||||
|
StackOrchestrator string
|
||||||
|
AdditionalFields map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AciContext is the context for the ACI backend
|
||||||
|
type AciContext struct {
|
||||||
|
SubscriptionID string `json:",omitempty"`
|
||||||
|
Location string `json:",omitempty"`
|
||||||
|
ResourceGroup string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MobyContext is the context for the moby backend
|
||||||
|
type MobyContext struct{}
|
||||||
|
|
||||||
|
// ExampleContext is the context for the example backend
|
||||||
|
type ExampleContext struct{}
|
||||||
|
|
||||||
|
// MarshalJSON implements custom JSON marshalling
|
||||||
|
func (dc ContextMetadata) MarshalJSON() ([]byte, error) {
|
||||||
|
s := map[string]interface{}{}
|
||||||
|
if dc.Description != "" {
|
||||||
|
s["Description"] = dc.Description
|
||||||
|
}
|
||||||
|
if dc.StackOrchestrator != "" {
|
||||||
|
s["StackOrchestrator"] = dc.StackOrchestrator
|
||||||
|
}
|
||||||
|
if dc.Type != "" {
|
||||||
|
s["Type"] = dc.Type
|
||||||
|
}
|
||||||
|
if dc.AdditionalFields != nil {
|
||||||
|
for k, v := range dc.AdditionalFields {
|
||||||
|
s[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json.Marshal(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements custom JSON marshalling
|
||||||
|
func (dc *ContextMetadata) UnmarshalJSON(payload []byte) error {
|
||||||
|
var data map[string]interface{}
|
||||||
|
if err := json.Unmarshal(payload, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for k, v := range data {
|
||||||
|
switch k {
|
||||||
|
case "Description":
|
||||||
|
dc.Description = v.(string)
|
||||||
|
case "StackOrchestrator":
|
||||||
|
dc.StackOrchestrator = v.(string)
|
||||||
|
case "Type":
|
||||||
|
dc.Type = v.(string)
|
||||||
|
default:
|
||||||
|
if dc.AdditionalFields == nil {
|
||||||
|
dc.AdditionalFields = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
dc.AdditionalFields[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContextTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ContextTestSuite) TestDockerContextMetadataKeepAdditionalFields() {
|
||||||
|
c := ContextMetadata{
|
||||||
|
Description: "test",
|
||||||
|
Type: "aci",
|
||||||
|
StackOrchestrator: "swarm",
|
||||||
|
AdditionalFields: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
jsonBytes, err := json.Marshal(c)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(string(jsonBytes)).To(Equal(`{"Description":"test","StackOrchestrator":"swarm","Type":"aci","foo":"bar"}`))
|
||||||
|
|
||||||
|
var c2 ContextMetadata
|
||||||
|
err = json.Unmarshal(jsonBytes, &c2)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(c2.AdditionalFields["foo"]).To(Equal("bar"))
|
||||||
|
Expect(c2.Type).To(Equal("aci"))
|
||||||
|
Expect(c2.StackOrchestrator).To(Equal("swarm"))
|
||||||
|
Expect(c2.Description).To(Equal("test"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPs(t *testing.T) {
|
||||||
|
RegisterTestingT(t)
|
||||||
|
suite.Run(t, new(ContextTestSuite))
|
||||||
|
}
|
|
@ -72,7 +72,7 @@ func ContextStore(ctx context.Context) Store {
|
||||||
type Store interface {
|
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) (*Metadata, error)
|
Get(name string) (*DockerContext, error)
|
||||||
// GetEndpoint sets the `v` parameter to the value of the endpoint for a
|
// GetEndpoint sets the `v` parameter to the value of the endpoint for a
|
||||||
// particular context type
|
// particular context type
|
||||||
GetEndpoint(name string, v interface{}) error
|
GetEndpoint(name string, v interface{}) error
|
||||||
|
@ -80,7 +80,7 @@ type Store interface {
|
||||||
// same name exists already.
|
// same name exists already.
|
||||||
Create(name string, contextType string, description string, data interface{}) error
|
Create(name string, contextType string, description string, data interface{}) error
|
||||||
// List returns the list of created contexts
|
// List returns the list of created contexts
|
||||||
List() ([]*Metadata, error)
|
List() ([]*DockerContext, error)
|
||||||
// Remove removes a context by name from the context store
|
// Remove removes a context by name from the context store
|
||||||
Remove(name string) error
|
Remove(name string) error
|
||||||
}
|
}
|
||||||
|
@ -104,34 +104,6 @@ const (
|
||||||
ExampleContextType = "example"
|
ExampleContextType = "example"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Metadata represents the docker context metadata
|
|
||||||
type Metadata struct {
|
|
||||||
Name string `json:",omitempty"`
|
|
||||||
Type string `json:",omitempty"`
|
|
||||||
Metadata ContextMetadata `json:",omitempty"`
|
|
||||||
Endpoints map[string]interface{} `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContextMetadata is represtentation of the data we put in a context
|
|
||||||
// metadata
|
|
||||||
type ContextMetadata struct {
|
|
||||||
Description string `json:",omitempty"`
|
|
||||||
StackOrchestrator string `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AciContext is the context for the ACI backend
|
|
||||||
type AciContext struct {
|
|
||||||
SubscriptionID string `json:",omitempty"`
|
|
||||||
Location string `json:",omitempty"`
|
|
||||||
ResourceGroup string `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MobyContext is the context for the moby backend
|
|
||||||
type MobyContext struct{}
|
|
||||||
|
|
||||||
// ExampleContext is the context for the example backend
|
|
||||||
type ExampleContext struct{}
|
|
||||||
|
|
||||||
type store struct {
|
type store struct {
|
||||||
root string
|
root string
|
||||||
}
|
}
|
||||||
|
@ -175,7 +147,7 @@ func New(opts ...Opt) (Store, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the context with the given name
|
// Get returns the context with the given name
|
||||||
func (s *store) Get(name string) (*Metadata, error) {
|
func (s *store) Get(name string) (*DockerContext, 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)
|
m, err := read(meta)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -192,14 +164,15 @@ func (s *store) GetEndpoint(name string, data interface{}) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, ok := meta.Endpoints[meta.Type]; !ok {
|
contextType := meta.Type()
|
||||||
return errors.Wrapf(errdefs.ErrNotFound, "endpoint of type %q", meta.Type)
|
if _, ok := meta.Endpoints[contextType]; !ok {
|
||||||
|
return errors.Wrapf(errdefs.ErrNotFound, "endpoint of type %q", contextType)
|
||||||
}
|
}
|
||||||
|
|
||||||
dstPtrValue := reflect.ValueOf(data)
|
dstPtrValue := reflect.ValueOf(data)
|
||||||
dstValue := reflect.Indirect(dstPtrValue)
|
dstValue := reflect.Indirect(dstPtrValue)
|
||||||
|
|
||||||
val := reflect.ValueOf(meta.Endpoints[meta.Type])
|
val := reflect.ValueOf(meta.Endpoints[contextType])
|
||||||
valIndirect := reflect.Indirect(val)
|
valIndirect := reflect.Indirect(val)
|
||||||
|
|
||||||
if dstValue.Type() != valIndirect.Type() {
|
if dstValue.Type() != valIndirect.Type() {
|
||||||
|
@ -211,13 +184,13 @@ func (s *store) GetEndpoint(name string, data interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func read(meta string) (*Metadata, error) {
|
func read(meta string) (*DockerContext, error) {
|
||||||
bytes, err := ioutil.ReadFile(meta)
|
bytes, err := ioutil.ReadFile(meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var metadata Metadata
|
var metadata DockerContext
|
||||||
if err := json.Unmarshal(bytes, &metadata); err != nil {
|
if err := json.Unmarshal(bytes, &metadata); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -270,10 +243,10 @@ func (s *store) Create(name string, contextType string, description string, data
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
meta := Metadata{
|
meta := DockerContext{
|
||||||
Name: name,
|
Name: name,
|
||||||
Type: contextType,
|
|
||||||
Metadata: ContextMetadata{
|
Metadata: ContextMetadata{
|
||||||
|
Type: contextType,
|
||||||
Description: description,
|
Description: description,
|
||||||
},
|
},
|
||||||
Endpoints: map[string]interface{}{
|
Endpoints: map[string]interface{}{
|
||||||
|
@ -290,14 +263,14 @@ func (s *store) Create(name string, contextType string, description string, data
|
||||||
return ioutil.WriteFile(filepath.Join(metaDir, metaFile), bytes, 0644)
|
return ioutil.WriteFile(filepath.Join(metaDir, metaFile), bytes, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *store) List() ([]*Metadata, error) {
|
func (s *store) List() ([]*DockerContext, error) {
|
||||||
root := filepath.Join(s.root, contextsDir, metadataDir)
|
root := filepath.Join(s.root, contextsDir, metadataDir)
|
||||||
c, err := ioutil.ReadDir(root)
|
c, err := ioutil.ReadDir(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var result []*Metadata
|
var result []*DockerContext
|
||||||
for _, fi := range c {
|
for _, fi := range c {
|
||||||
if fi.IsDir() {
|
if fi.IsDir() {
|
||||||
meta := filepath.Join(root, fi.Name(), metaFile)
|
meta := filepath.Join(root, fi.Name(), metaFile)
|
||||||
|
|
|
@ -104,7 +104,7 @@ func (suite *StoreTestSuite) TestGet() {
|
||||||
require.Equal(suite.T(), "test", meta.Name)
|
require.Equal(suite.T(), "test", meta.Name)
|
||||||
|
|
||||||
require.Equal(suite.T(), "description", meta.Metadata.Description)
|
require.Equal(suite.T(), "description", meta.Metadata.Description)
|
||||||
require.Equal(suite.T(), "type", meta.Type)
|
require.Equal(suite.T(), "type", meta.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *StoreTestSuite) TestRemoveNotFound() {
|
func (suite *StoreTestSuite) TestRemoveNotFound() {
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultContextType = "docker"
|
||||||
|
|
||||||
// Represents a context as created by the docker cli
|
// Represents a context as created by the docker cli
|
||||||
type defaultContext struct {
|
type defaultContext struct {
|
||||||
Metadata ContextMetadata
|
Metadata ContextMetadata
|
||||||
|
@ -31,7 +33,7 @@ type endpoint struct {
|
||||||
DefaultNamespace string
|
DefaultNamespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
func dockerDefaultContext() (*Metadata, error) {
|
func dockerDefaultContext() (*DockerContext, error) {
|
||||||
cmd := exec.Command("docker-classic", "context", "inspect", "default")
|
cmd := exec.Command("docker-classic", "context", "inspect", "default")
|
||||||
var stdout bytes.Buffer
|
var stdout bytes.Buffer
|
||||||
cmd.Stdout = &stdout
|
cmd.Stdout = &stdout
|
||||||
|
@ -52,9 +54,8 @@ func dockerDefaultContext() (*Metadata, error) {
|
||||||
|
|
||||||
defaultCtx := ctx[0]
|
defaultCtx := ctx[0]
|
||||||
|
|
||||||
meta := Metadata{
|
meta := DockerContext{
|
||||||
Name: "default",
|
Name: "default",
|
||||||
Type: "docker",
|
|
||||||
Endpoints: map[string]interface{}{
|
Endpoints: map[string]interface{}{
|
||||||
"docker": Endpoint{
|
"docker": Endpoint{
|
||||||
Host: defaultCtx.Endpoints.Docker.Host,
|
Host: defaultCtx.Endpoints.Docker.Host,
|
||||||
|
@ -65,6 +66,7 @@ func dockerDefaultContext() (*Metadata, error) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Metadata: ContextMetadata{
|
Metadata: ContextMetadata{
|
||||||
|
Type: defaultContextType,
|
||||||
Description: "Current DOCKER_HOST based configuration",
|
Description: "Current DOCKER_HOST based configuration",
|
||||||
StackOrchestrator: defaultCtx.Metadata.StackOrchestrator,
|
StackOrchestrator: defaultCtx.Metadata.StackOrchestrator,
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (cp *contextsProxy) List(ctx context.Context, request *contextsv1.ListReque
|
||||||
for _, c := range contexts {
|
for _, c := range contexts {
|
||||||
result.Contexts = append(result.Contexts, &contextsv1.Context{
|
result.Contexts = append(result.Contexts, &contextsv1.Context{
|
||||||
Name: c.Name,
|
Name: c.Name,
|
||||||
ContextType: c.Type,
|
ContextType: c.Type(),
|
||||||
Current: c.Name == configFile.CurrentContext,
|
Current: c.Name == configFile.CurrentContext,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue