mirror of
https://github.com/docker/compose.git
synced 2026-08-28 12:23:49 +00:00
Move Context & context/store => api/context & api/context/store
Signed-off-by: Guillaume Tardif <guillaume.tardif@gmail.com>
This commit is contained in:
parent
930ae8bdb2
commit
0ea97920c1
47 changed files with 50 additions and 50 deletions
|
|
@ -23,11 +23,11 @@ import (
|
|||
"github.com/docker/compose-cli/api/cloud"
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
"github.com/docker/compose-cli/api/containers"
|
||||
apicontext "github.com/docker/compose-cli/api/context"
|
||||
"github.com/docker/compose-cli/api/context/store"
|
||||
"github.com/docker/compose-cli/api/resources"
|
||||
"github.com/docker/compose-cli/api/secrets"
|
||||
"github.com/docker/compose-cli/api/volumes"
|
||||
apicontext "github.com/docker/compose-cli/context"
|
||||
"github.com/docker/compose-cli/context/store"
|
||||
)
|
||||
|
||||
// New returns a backend client associated with current context
|
||||
|
|
|
|||
36
api/context/context.go
Normal file
36
api/context/context.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
gocontext "context"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type currentContextKey struct{}
|
||||
|
||||
// WithCurrentContext sets the name of the current docker context
|
||||
func WithCurrentContext(ctx gocontext.Context, contextName string) context.Context {
|
||||
return context.WithValue(ctx, currentContextKey{}, contextName)
|
||||
}
|
||||
|
||||
// CurrentContext returns the current context name
|
||||
func CurrentContext(ctx context.Context) string {
|
||||
cc, _ := ctx.Value(currentContextKey{}).(string)
|
||||
return cc
|
||||
}
|
||||
34
api/context/flags.go
Normal file
34
api/context/flags.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// ContextFlags are the global CLI flags
|
||||
// nolint stutter
|
||||
type ContextFlags struct {
|
||||
Context string
|
||||
}
|
||||
|
||||
// AddContextFlags adds persistent (global) flags
|
||||
func (c *ContextFlags) AddContextFlags(flags *pflag.FlagSet) {
|
||||
flags.StringVarP(&c.Context, "context", "c", os.Getenv("DOCKER_CONTEXT"), "context")
|
||||
}
|
||||
109
api/context/store/contextmetadata.go
Normal file
109
api/context/store/contextmetadata.go
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
// EcsContext is the context for the AWS backend
|
||||
type EcsContext struct {
|
||||
CredentialsFromEnv bool `json:",omitempty"`
|
||||
Profile string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// AwsContext is the context for the ecs plugin
|
||||
type AwsContext EcsContext
|
||||
|
||||
// LocalContext is the context for the local backend
|
||||
type LocalContext 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
|
||||
}
|
||||
46
api/context/store/contextmetadata_test.go
Normal file
46
api/context/store/contextmetadata_test.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestDockerContextMetadataKeepAdditionalFields(t *testing.T) {
|
||||
c := ContextMetadata{
|
||||
Description: "test",
|
||||
Type: "aci",
|
||||
StackOrchestrator: "swarm",
|
||||
AdditionalFields: map[string]interface{}{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
jsonBytes, err := json.Marshal(c)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, string(jsonBytes), `{"Description":"test","StackOrchestrator":"swarm","Type":"aci","foo":"bar"}`)
|
||||
|
||||
var c2 ContextMetadata
|
||||
err = json.Unmarshal(jsonBytes, &c2)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, c2.AdditionalFields["foo"], "bar")
|
||||
assert.Equal(t, c2.Type, "aci")
|
||||
assert.Equal(t, c2.StackOrchestrator, "swarm")
|
||||
assert.Equal(t, c2.Description, "test")
|
||||
}
|
||||
338
api/context/store/store.go
Normal file
338
api/context/store/store.go
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/docker/compose-cli/errdefs"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultContextName is an automatically generated local context
|
||||
DefaultContextName = "default"
|
||||
// DefaultContextType is the type for all moby contexts (not associated with cli backend)
|
||||
DefaultContextType = "moby"
|
||||
|
||||
// AwsContextType is the type for aws contexts (currently a CLI plugin, not associated with cli backend)
|
||||
// to be removed with the cli plugin
|
||||
AwsContextType = "aws"
|
||||
|
||||
// EcsContextType is the endpoint key in the context endpoints for an ECS
|
||||
// backend
|
||||
EcsContextType = "ecs"
|
||||
|
||||
// EcsLocalSimulationContextType is the endpoint key in the context endpoints for an ECS backend
|
||||
// running local simulation endpoints
|
||||
EcsLocalSimulationContextType = "ecs-local"
|
||||
|
||||
// AciContextType is the endpoint key in the context endpoints for an ACI
|
||||
// backend
|
||||
AciContextType = "aci"
|
||||
// LocalContextType is the endpoint key in the context endpoints for a new
|
||||
// local backend
|
||||
LocalContextType = "local"
|
||||
// ExampleContextType is the endpoint key in the context endpoints for an
|
||||
// example backend
|
||||
ExampleContextType = "example"
|
||||
)
|
||||
|
||||
const (
|
||||
dockerEndpointKey = "docker"
|
||||
contextsDir = "contexts"
|
||||
metadataDir = "meta"
|
||||
metaFile = "meta.json"
|
||||
)
|
||||
|
||||
type contextStoreKey struct{}
|
||||
|
||||
// WithContextStore adds the store to the context
|
||||
func WithContextStore(ctx context.Context, store Store) context.Context {
|
||||
return context.WithValue(ctx, contextStoreKey{}, store)
|
||||
}
|
||||
|
||||
// ContextStore returns the store from the context
|
||||
func ContextStore(ctx context.Context) Store {
|
||||
s, _ := ctx.Value(contextStoreKey{}).(Store)
|
||||
return s
|
||||
}
|
||||
|
||||
// Store is the context store
|
||||
type Store interface {
|
||||
// Get returns the context with name, it returns an error if the context
|
||||
// doesn't exist
|
||||
Get(name string) (*DockerContext, error)
|
||||
// GetEndpoint sets the `v` parameter to the value of the endpoint for a
|
||||
// particular context type
|
||||
GetEndpoint(name string, v interface{}) error
|
||||
// Create creates a new context, it returns an error if a context with the
|
||||
// same name exists already.
|
||||
Create(name string, contextType string, description string, data interface{}) error
|
||||
// List returns the list of created contexts
|
||||
List() ([]*DockerContext, error)
|
||||
// Remove removes a context by name from the context store
|
||||
Remove(name string) error
|
||||
// ContextExists checks if a context already exists
|
||||
ContextExists(name string) bool
|
||||
}
|
||||
|
||||
// Endpoint holds the Docker or the Kubernetes endpoint, they both have the
|
||||
// `Host` property, only kubernetes will have the `DefaultNamespace`
|
||||
type Endpoint struct {
|
||||
Host string `json:",omitempty"`
|
||||
DefaultNamespace string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type store struct {
|
||||
root string
|
||||
}
|
||||
|
||||
// New returns a configured context store with specified root dir (eg. $HOME/.docker) as root
|
||||
func New(rootDir string) (Store, error) {
|
||||
s := &store{
|
||||
root: rootDir,
|
||||
}
|
||||
|
||||
m := filepath.Join(s.root, contextsDir, metadataDir)
|
||||
if err := createDirIfNotExist(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Get returns the context with the given name
|
||||
func (s *store) Get(name string) (*DockerContext, error) {
|
||||
if name == "default" {
|
||||
return dockerDefaultContext()
|
||||
}
|
||||
meta := filepath.Join(s.root, contextsDir, metadataDir, contextDirOf(name), metaFile)
|
||||
m, err := read(meta)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, errors.Wrap(errdefs.ErrNotFound, objectName(name))
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (s *store) GetEndpoint(name string, data interface{}) error {
|
||||
meta, err := s.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contextType := meta.Type()
|
||||
if _, ok := meta.Endpoints[contextType]; !ok {
|
||||
return errors.Wrapf(errdefs.ErrNotFound, "endpoint of type %q", contextType)
|
||||
}
|
||||
|
||||
dstPtrValue := reflect.ValueOf(data)
|
||||
dstValue := reflect.Indirect(dstPtrValue)
|
||||
|
||||
val := reflect.ValueOf(meta.Endpoints[contextType])
|
||||
valIndirect := reflect.Indirect(val)
|
||||
|
||||
if dstValue.Type() != valIndirect.Type() {
|
||||
return errdefs.ErrWrongContextType
|
||||
}
|
||||
|
||||
dstValue.Set(valIndirect)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func read(meta string) (*DockerContext, error) {
|
||||
bytes, err := ioutil.ReadFile(meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var metadata DockerContext
|
||||
if err := json.Unmarshal(bytes, &metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metadata.Endpoints, err = toTypedEndpoints(metadata.Endpoints)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &metadata, nil
|
||||
}
|
||||
|
||||
func toTypedEndpoints(endpoints map[string]interface{}) (map[string]interface{}, error) {
|
||||
result := map[string]interface{}{}
|
||||
for k, v := range endpoints {
|
||||
bytes, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typeGetters := getters()
|
||||
typeGetter, ok := typeGetters[k]
|
||||
if !ok {
|
||||
typeGetter = func() interface{} {
|
||||
return &Endpoint{}
|
||||
}
|
||||
}
|
||||
|
||||
val := typeGetter()
|
||||
err = json.Unmarshal(bytes, &val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result[k] = val
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *store) ContextExists(name string) bool {
|
||||
if name == DefaultContextName {
|
||||
return true
|
||||
}
|
||||
dir := contextDirOf(name)
|
||||
metaDir := filepath.Join(s.root, contextsDir, metadataDir, dir)
|
||||
if _, err := os.Stat(metaDir); !os.IsNotExist(err) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *store) Create(name string, contextType string, description string, data interface{}) error {
|
||||
if s.ContextExists(name) {
|
||||
return errors.Wrap(errdefs.ErrAlreadyExists, objectName(name))
|
||||
}
|
||||
dir := contextDirOf(name)
|
||||
metaDir := filepath.Join(s.root, contextsDir, metadataDir, dir)
|
||||
|
||||
err := os.Mkdir(metaDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
meta := DockerContext{
|
||||
Name: name,
|
||||
Metadata: ContextMetadata{
|
||||
Type: contextType,
|
||||
Description: description,
|
||||
},
|
||||
Endpoints: map[string]interface{}{
|
||||
(dockerEndpointKey): data,
|
||||
(contextType): data,
|
||||
},
|
||||
}
|
||||
|
||||
bytes, err := json.Marshal(&meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filepath.Join(metaDir, metaFile), bytes, 0644)
|
||||
}
|
||||
|
||||
func (s *store) List() ([]*DockerContext, error) {
|
||||
root := filepath.Join(s.root, contextsDir, metadataDir)
|
||||
c, err := ioutil.ReadDir(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []*DockerContext
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// The default context is not stored in the store, it is in-memory only
|
||||
// so we need a special case for it.
|
||||
dockerDefault, err := dockerDefaultContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = append(result, dockerDefault)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *store) Remove(name string) error {
|
||||
if name == DefaultContextName {
|
||||
return errors.Wrap(errdefs.ErrForbidden, objectName(name))
|
||||
}
|
||||
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 {
|
||||
return digest.FromString(name).Encoded()
|
||||
}
|
||||
|
||||
func objectName(name string) string {
|
||||
return fmt.Sprintf("context %q", name)
|
||||
}
|
||||
|
||||
func createDirIfNotExist(dir string) error {
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Different context types managed by the store.
|
||||
// TODO(rumpl): we should make this extensible in the future if we want to
|
||||
// be able to manage other contexts.
|
||||
func getters() map[string]func() interface{} {
|
||||
return map[string]func() interface{}{
|
||||
AciContextType: func() interface{} {
|
||||
return &AciContext{}
|
||||
},
|
||||
EcsContextType: func() interface{} {
|
||||
return &EcsContext{}
|
||||
},
|
||||
LocalContextType: func() interface{} {
|
||||
return &LocalContext{}
|
||||
},
|
||||
ExampleContextType: func() interface{} {
|
||||
return &ExampleContext{}
|
||||
},
|
||||
}
|
||||
}
|
||||
121
api/context/store/store_test.go
Normal file
121
api/context/store/store_test.go
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
_ "crypto/sha256"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/assert/cmp"
|
||||
|
||||
"github.com/docker/compose-cli/errdefs"
|
||||
)
|
||||
|
||||
func testStore(t *testing.T) Store {
|
||||
d, err := ioutil.TempDir("", "store")
|
||||
assert.NilError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = os.RemoveAll(d)
|
||||
})
|
||||
|
||||
s, err := New(d)
|
||||
assert.NilError(t, err)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
s := testStore(t)
|
||||
err := s.Create("test", "test", "description", ContextMetadata{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = s.Create("test", "test", "descrsiption", ContextMetadata{})
|
||||
assert.Error(t, err, `context "test": already exists`)
|
||||
assert.Assert(t, errdefs.IsAlreadyExistsError(err))
|
||||
}
|
||||
|
||||
func TestGetEndpoint(t *testing.T) {
|
||||
s := testStore(t)
|
||||
err := s.Create("aci", "aci", "description", AciContext{
|
||||
Location: "eu",
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
|
||||
var ctx AciContext
|
||||
err = s.GetEndpoint("aci", &ctx)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, ctx.Location, "eu")
|
||||
|
||||
var exampleCtx ExampleContext
|
||||
err = s.GetEndpoint("aci", &exampleCtx)
|
||||
assert.Error(t, err, "wrong context type")
|
||||
}
|
||||
|
||||
func TestGetUnknown(t *testing.T) {
|
||||
s := testStore(t)
|
||||
meta, err := s.Get("unknown")
|
||||
assert.Assert(t, cmp.Nil(meta))
|
||||
assert.Error(t, err, `context "unknown": not found`)
|
||||
assert.Assert(t, errdefs.IsNotFoundError(err))
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
s := testStore(t)
|
||||
err := s.Create("test", "type", "description", ContextMetadata{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
meta, err := s.Get("test")
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, meta != nil)
|
||||
var m DockerContext
|
||||
if meta != nil {
|
||||
m = *meta
|
||||
}
|
||||
|
||||
assert.Equal(t, m.Name, "test")
|
||||
assert.Equal(t, m.Metadata.Description, "description")
|
||||
assert.Equal(t, m.Type(), "type")
|
||||
}
|
||||
|
||||
func TestRemoveNotFound(t *testing.T) {
|
||||
s := testStore(t)
|
||||
err := s.Remove("notfound")
|
||||
assert.Error(t, err, `context "notfound": not found`)
|
||||
assert.Assert(t, errdefs.IsNotFoundError(err))
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
s := testStore(t)
|
||||
err := s.Create("testremove", "type", "description", ContextMetadata{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
meta, err := s.Get("testremove")
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, meta != nil)
|
||||
|
||||
err = s.Remove("testremove")
|
||||
assert.NilError(t, err)
|
||||
|
||||
meta, err = s.Get("testremove")
|
||||
assert.Error(t, err, `context "testremove": not found`)
|
||||
assert.Assert(t, cmp.Nil(meta))
|
||||
|
||||
}
|
||||
91
api/context/store/storedefault.go
Normal file
91
api/context/store/storedefault.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os/exec"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Represents a context as created by the docker cli
|
||||
type defaultContext struct {
|
||||
Metadata ContextMetadata
|
||||
Endpoints endpoints
|
||||
}
|
||||
|
||||
// Normally (in docker/cli code), the endpoints are mapped as map[string]interface{}
|
||||
// but docker cli contexts always have a "docker" and "kubernetes" key so we
|
||||
// create real types for those to no have to juggle around with interfaces.
|
||||
type endpoints struct {
|
||||
Docker endpoint `json:"docker,omitempty"`
|
||||
Kubernetes endpoint `json:"kubernetes,omitempty"`
|
||||
}
|
||||
|
||||
// Both "docker" and "kubernetes" endpoints in the docker cli created contexts
|
||||
// have a "Host", only kubernetes has the "DefaultNamespace", we put both of
|
||||
// those here for easier manipulation and to not have to create two distinct
|
||||
// structs
|
||||
type endpoint struct {
|
||||
Host string
|
||||
DefaultNamespace string
|
||||
}
|
||||
|
||||
func dockerDefaultContext() (*DockerContext, error) {
|
||||
// ensure we run this using default context, in current context has been damaged / removed in store
|
||||
cmd := exec.Command("com.docker.cli", "--context", "default", "context", "inspect", "default")
|
||||
var stdout bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ctx []defaultContext
|
||||
err = json.Unmarshal(stdout.Bytes(), &ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ctx) != 1 {
|
||||
return nil, errors.New("found more than one default context")
|
||||
}
|
||||
|
||||
defaultCtx := ctx[0]
|
||||
|
||||
meta := DockerContext{
|
||||
Name: "default",
|
||||
Endpoints: map[string]interface{}{
|
||||
"docker": &Endpoint{
|
||||
Host: defaultCtx.Endpoints.Docker.Host,
|
||||
},
|
||||
"kubernetes": &Endpoint{
|
||||
Host: defaultCtx.Endpoints.Kubernetes.Host,
|
||||
DefaultNamespace: defaultCtx.Endpoints.Kubernetes.DefaultNamespace,
|
||||
},
|
||||
},
|
||||
Metadata: ContextMetadata{
|
||||
Type: DefaultContextType,
|
||||
Description: "Current DOCKER_HOST based configuration",
|
||||
StackOrchestrator: defaultCtx.Metadata.StackOrchestrator,
|
||||
},
|
||||
}
|
||||
|
||||
return &meta, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue