diff --git a/azure/backend.go b/azure/backend.go index 4b249d5da..99e34a888 100644 --- a/azure/backend.go +++ b/azure/backend.go @@ -31,58 +31,53 @@ const singleContainerName = "single--container--aci" var ErrNoSuchContainer = errors.New("no such container") func init() { - backend.Register("aci", "aci", func(ctx context.Context) (backend.Service, error) { - return New(ctx) - }) + backend.Register("aci", "aci", service, getCloudService) } -// New creates a backend that can manage containers -func New(ctx context.Context) (backend.Service, error) { - currentContext := apicontext.CurrentContext(ctx) +func service(ctx context.Context) (backend.Service, error) { contextStore := store.ContextStore(ctx) - + currentContext := apicontext.CurrentContext(ctx) var aciContext store.AciContext + if err := contextStore.GetEndpoint(currentContext, &aciContext); err != nil { return nil, err } - return getAciAPIService(aciContext) + return getAciAPIService(aciContext), nil } -func getAciAPIService(aciCtx store.AciContext) (*aciAPIService, error) { +func getCloudService() (cloud.Service, error) { service, err := login.NewAzureLoginService() if err != nil { return nil, err } - return &aciAPIService{ - aciContainerService: aciContainerService{ - ctx: aciCtx, - }, - aciComposeService: aciComposeService{ - ctx: aciCtx, - }, - aciCloudService: aciCloudService{ - loginService: service, - }, + return &aciCloudService{ + loginService: service, }, nil } +func getAciAPIService(aciCtx store.AciContext) *aciAPIService { + return &aciAPIService{ + aciContainerService: &aciContainerService{ + ctx: aciCtx, + }, + aciComposeService: &aciComposeService{ + ctx: aciCtx, + }, + } +} + type aciAPIService struct { - aciContainerService - aciComposeService - aciCloudService + *aciContainerService + *aciComposeService } func (a *aciAPIService) ContainerService() containers.Service { - return &a.aciContainerService + return a.aciContainerService } func (a *aciAPIService) ComposeService() compose.Service { - return &a.aciComposeService -} - -func (a *aciAPIService) CloudService() cloud.Service { - return &a.aciCloudService + return a.aciComposeService } type aciContainerService struct { diff --git a/azure/backend_test.go b/azure/backend_test.go index e63f65ca6..ef2ab4d10 100644 --- a/azure/backend_test.go +++ b/azure/backend_test.go @@ -3,13 +3,16 @@ package azure import ( "testing" + "github.com/stretchr/testify/suite" + . "github.com/onsi/gomega" ) -// TestGetContainerName ensures we can read container group name / container name from a containerID -func TestGetContainerName(t *testing.T) { - RegisterTestingT(t) +type BackendSuiteTest struct { + suite.Suite +} +func (suite *BackendSuiteTest) TestGetContainerName() { group, container := getGroupAndContainerName("docker1234") Expect(group).To(Equal("docker1234")) Expect(container).To(Equal(singleContainerName)) @@ -22,3 +25,8 @@ func TestGetContainerName(t *testing.T) { Expect(group).To(Equal("compose_stack")) Expect(container).To(Equal("service1")) } + +func TestBackendSuite(t *testing.T) { + RegisterTestingT(t) + suite.Run(t, new(BackendSuiteTest)) +} diff --git a/backend/backend.go b/backend/backend.go index b76ae6f58..fd179f32c 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -19,11 +19,13 @@ var ( ) type initFunc func(context.Context) (Service, error) +type getCloudServiceFunc func() (cloud.Service, error) type registeredBackend struct { - name string - backendType string - init initFunc + name string + backendType string + init initFunc + getCloudService getCloudServiceFunc } var backends = struct { @@ -34,11 +36,10 @@ var backends = struct { type Service interface { ContainerService() containers.Service ComposeService() compose.Service - CloudService() cloud.Service } // Register adds a typed backend to the registry -func Register(name string, backendType string, init initFunc) { +func Register(name string, backendType string, init initFunc, getCoudService getCloudServiceFunc) { if name == "" { logrus.Fatal(errNoName) } @@ -55,6 +56,7 @@ func Register(name string, backendType string, init initFunc) { name, backendType, init, + getCoudService, }) } @@ -69,3 +71,15 @@ func Get(ctx context.Context, backendType string) (Service, error) { return nil, fmt.Errorf("backend not found for context %q", backendType) } + +// GetCloudService returns the backend registered for a particular type, it returns +// an error if there is no registered backends for the given type. +func GetCloudService(ctx context.Context, backendType string) (cloud.Service, error) { + for _, b := range backends.r { + if b.backendType == backendType { + return b.getCloudService() + } + } + + return nil, fmt.Errorf("backend not found for backend type %s", backendType) +} diff --git a/cli/cmd/context/login/login.go b/cli/cmd/context/login/login.go index 4cbd6a7cf..9e10c667a 100644 --- a/cli/cmd/context/login/login.go +++ b/cli/cmd/context/login/login.go @@ -5,7 +5,6 @@ import ( "github.com/spf13/cobra" "github.com/docker/api/client" - apicontext "github.com/docker/api/context" ) // Command returns the compose command with its child commands @@ -24,12 +23,12 @@ func azureLoginCommand() *cobra.Command { azureLoginCmd := &cobra.Command{ Use: "azure", RunE: func(cmd *cobra.Command, args []string) error { - ctx := apicontext.WithCurrentContext(cmd.Context(), "aci") - c, err := client.New(ctx) + ctx := cmd.Context() + cs, err := client.GetCloudService(ctx, "aci") if err != nil { return errors.Wrap(err, "cannot connect to backend") } - return c.CloudService().Login(ctx, nil) + return cs.Login(ctx, nil) }, } diff --git a/client/client.go b/client/client.go index 44c05a77f..8e37dc5b5 100644 --- a/client/client.go +++ b/client/client.go @@ -39,7 +39,7 @@ import ( "github.com/docker/api/context/store" ) -// New returns a backend client +// New returns a backend client associated with current context func New(ctx context.Context) (*Client, error) { currentContext := apicontext.CurrentContext(ctx) s := store.ContextStore(ctx) @@ -58,7 +58,11 @@ func New(ctx context.Context) (*Client, error) { backendType: cc.Type, bs: service, }, nil +} +// GetCloudService returns a backend CloudService (typically login, create context) +func GetCloudService(ctx context.Context, backendType string) (cloud.Service, error) { + return backend.GetCloudService(ctx, backendType) } // Client is a multi-backend client @@ -76,8 +80,3 @@ func (c *Client) ContainerService() containers.Service { func (c *Client) ComposeService() compose.Service { return c.bs.ComposeService() } - -// CloudService returns the backend service for the current context -func (c *Client) CloudService() cloud.Service { - return c.bs.CloudService() -} diff --git a/context/cloud/api.go b/context/cloud/api.go index 448c6537c..a00d3db7a 100644 --- a/context/cloud/api.go +++ b/context/cloud/api.go @@ -1,9 +1,25 @@ package cloud -import "context" +import ( + "context" + + "github.com/docker/api/errdefs" +) // Service cloud specific services type Service interface { // Login login to cloud provider Login(ctx context.Context, params map[string]string) error } + +// NotImplementedCloudService to use for backend that don't provide cloud services +func NotImplementedCloudService() (Service, error) { + return notImplementedCloudService{}, nil +} + +type notImplementedCloudService struct { +} + +func (cs notImplementedCloudService) Login(ctx context.Context, params map[string]string) error { + return errdefs.ErrNotImplemented +} diff --git a/example/backend.go b/example/backend.go index 0fb2edb53..1819b4fb6 100644 --- a/example/backend.go +++ b/example/backend.go @@ -26,14 +26,12 @@ func (a *apiService) ComposeService() compose.Service { return &a.composeService } -func (a *apiService) CloudService() cloud.Service { - return nil +func init() { + backend.Register("example", "example", service, cloud.NotImplementedCloudService) } -func init() { - backend.Register("example", "example", func(ctx context.Context) (backend.Service, error) { - return &apiService{}, nil - }) +func service(ctx context.Context) (backend.Service, error) { + return &apiService{}, nil } type containerService struct{} diff --git a/moby/backend.go b/moby/backend.go index 5c6616a7d..11f5696f3 100644 --- a/moby/backend.go +++ b/moby/backend.go @@ -27,13 +27,10 @@ type mobyService struct { } func init() { - backend.Register("moby", "moby", func(ctx context.Context) (backend.Service, error) { - return New() - }) + backend.Register("moby", "moby", service, cloud.NotImplementedCloudService) } -// New returns a moby backend implementation -func New() (backend.Service, error) { +func service(ctx context.Context) (backend.Service, error) { apiClient, err := client.NewClientWithOpts(client.FromEnv) if err != nil { return nil, err @@ -52,10 +49,6 @@ func (ms *mobyService) ComposeService() compose.Service { return nil } -func (ms *mobyService) CloudService() cloud.Service { - return nil -} - func (ms *mobyService) List(ctx context.Context, all bool) ([]containers.Container, error) { css, err := ms.apiClient.ContainerList(ctx, types.ContainerListOptions{ All: all,