Fix Azure login : allow getting a backend when no corresponding context already exists with an explicit call from the login command. Will be usefull next for context creation with azure interactive things

This commit is contained in:
Guillaume Tardif 2020-05-28 17:37:59 +02:00
Родитель 832651b1dc
Коммит 01aaec2dbe
8 изменённых файлов: 84 добавлений и 62 удалений

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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