瀏覽代碼

Merge pull request #41 from ulyssessouza/add-up

Add compose up and down
Ulysses Souza 5 年之前
父節點
當前提交
a4e54e9b5d
共有 9 個文件被更改,包括 190 次插入31 次删除
  1. 8 0
      azure/aci.go
  2. 74 16
      azure/backend.go
  3. 14 3
      backend/backend.go
  4. 58 0
      cli/cmd/compose/compose.go
  5. 5 4
      cli/main.go
  6. 11 5
      client/client.go
  7. 13 0
      compose/api.go
  8. 5 1
      compose/project.go
  9. 2 2
      containers/api.go

+ 8 - 0
azure/aci.go

@@ -96,6 +96,14 @@ func createACIContainers(ctx context.Context, aciContext store.AciContext, group
 	return containerGroup, err
 }
 
+func deleteACIContainerGroup(ctx context.Context, aciContext store.AciContext, containerGroupName string) (c containerinstance.ContainerGroup, err error) {
+	containerGroupsClient, err := getContainerGroupsClient(aciContext.SubscriptionID)
+	if err != nil {
+		return c, fmt.Errorf("cannot get container group client: %v", err)
+	}
+	return containerGroupsClient.Delete(ctx, aciContext.ResourceGroup, containerGroupName)
+}
+
 func execACIContainer(ctx context.Context, aciContext store.AciContext, command, containerGroup string, containerName string) (c containerinstance.ContainerExecResponse, err error) {
 	containerClient, err := getContainerClient(aciContext.SubscriptionID)
 	if err != nil {

+ 74 - 16
azure/backend.go

@@ -21,11 +21,6 @@ import (
 	"github.com/docker/api/context/store"
 )
 
-type containerService struct {
-	containerGroupsClient containerinstance.ContainerGroupsClient
-	ctx                   store.AciContext
-}
-
 func init() {
 	backend.Register("aci", "aci", func(ctx context.Context) (interface{}, error) {
 		return New(ctx)
@@ -36,8 +31,8 @@ func getter() interface{} {
 	return &store.AciContext{}
 }
 
-// New creates a backend that can manage containers on ACI
-func New(ctx context.Context) (containers.ContainerService, error) {
+// New creates a backend that can manage containers
+func New(ctx context.Context) (backend.Service, error) {
 	currentContext := apicontext.CurrentContext(ctx)
 	contextStore, err := store.New()
 	if err != nil {
@@ -53,13 +48,47 @@ func New(ctx context.Context) (containers.ContainerService, error) {
 	containerGroupsClient := containerinstance.NewContainerGroupsClient(aciContext.SubscriptionID)
 	containerGroupsClient.Authorizer = auth
 
-	return &containerService{
-		containerGroupsClient: containerGroupsClient,
-		ctx:                   aciContext,
-	}, nil
+	return getAciApiService(containerGroupsClient, aciContext), nil
+}
+
+func getAciApiService(cgc containerinstance.ContainerGroupsClient, aciCtx store.AciContext) *aciApiService {
+	return &aciApiService{
+		container: aciContainerService{
+			containerGroupsClient: cgc,
+			ctx:                   aciCtx,
+		},
+		compose: aciComposeService{
+			containerGroupsClient: cgc,
+			ctx:                   aciCtx,
+		},
+	}
+}
+
+type aciApiService struct {
+	container aciContainerService
+	compose   aciComposeService
+}
+
+func (a *aciApiService) ContainerService() containers.Service {
+	return &aciContainerService{
+		containerGroupsClient: a.container.containerGroupsClient,
+		ctx:                   a.container.ctx,
+	}
+}
+
+func (a *aciApiService) ComposeService() compose.Service {
+	return &aciComposeService{
+		containerGroupsClient: a.compose.containerGroupsClient,
+		ctx:                   a.compose.ctx,
+	}
+}
+
+type aciContainerService struct {
+	containerGroupsClient containerinstance.ContainerGroupsClient
+	ctx                   store.AciContext
 }
 
-func (cs *containerService) List(ctx context.Context) ([]containers.Container, error) {
+func (cs *aciContainerService) List(ctx context.Context) ([]containers.Container, error) {
 	var containerGroups []containerinstance.ContainerGroup
 	result, err := cs.containerGroupsClient.ListByResourceGroup(ctx, cs.ctx.ResourceGroup)
 	if err != nil {
@@ -73,7 +102,7 @@ func (cs *containerService) List(ctx context.Context) ([]containers.Container, e
 		}
 	}
 
-	res := []containers.Container{}
+	var res []containers.Container
 	for _, containerGroup := range containerGroups {
 		group, err := cs.containerGroupsClient.Get(ctx, cs.ctx.ResourceGroup, *containerGroup.Name)
 		if err != nil {
@@ -96,7 +125,7 @@ func (cs *containerService) List(ctx context.Context) ([]containers.Container, e
 	return res, nil
 }
 
-func (cs *containerService) Run(ctx context.Context, r containers.ContainerConfig) error {
+func (cs *aciContainerService) Run(ctx context.Context, r containers.ContainerConfig) error {
 	var ports []types.ServicePortConfig
 	for _, p := range r.Ports {
 		ports = append(ports, types.ServicePortConfig{
@@ -127,7 +156,7 @@ func (cs *containerService) Run(ctx context.Context, r containers.ContainerConfi
 	return err
 }
 
-func (cs *containerService) Exec(ctx context.Context, name string, command string, reader io.Reader, writer io.Writer) error {
+func (cs *aciContainerService) Exec(ctx context.Context, name string, command string, reader io.Reader, writer io.Writer) error {
 	containerExecResponse, err := execACIContainer(ctx, cs.ctx, command, name, name)
 	if err != nil {
 		return err
@@ -142,7 +171,7 @@ func (cs *containerService) Exec(ctx context.Context, name string, command strin
 	)
 }
 
-func (cs *containerService) Logs(ctx context.Context, containerName string, req containers.LogsRequest) error {
+func (cs *aciContainerService) Logs(ctx context.Context, containerName string, req containers.LogsRequest) error {
 	logs, err := getACIContainerLogs(ctx, cs.ctx, containerName, containerName)
 	if err != nil {
 		return err
@@ -163,3 +192,32 @@ func (cs *containerService) Logs(ctx context.Context, containerName string, req
 	_, err = fmt.Fprint(req.Writer, logs)
 	return err
 }
+
+type aciComposeService struct {
+	containerGroupsClient containerinstance.ContainerGroupsClient
+	ctx                   store.AciContext
+}
+
+func (cs *aciComposeService) Up(ctx context.Context, opts compose.ProjectOptions) error {
+	project, err := compose.ProjectFromOptions(&opts)
+	if err != nil {
+		return err
+	}
+	logrus.Debugf("Up on project with name %q\n", project.Name)
+	groupDefinition, err := convert.ToContainerGroup(cs.ctx, *project)
+	if err != nil {
+		return err
+	}
+	_, err = createACIContainers(ctx, cs.ctx, groupDefinition)
+	return err
+}
+
+func (cs *aciComposeService) Down(ctx context.Context, opts compose.ProjectOptions) error {
+	project, err := compose.ProjectFromOptions(&opts)
+	if err != nil {
+		return err
+	}
+	logrus.Debugf("Down on project with name %q\n", project.Name)
+	_, err = deleteACIContainerGroup(ctx, cs.ctx, project.Name)
+	return err
+}

+ 14 - 3
backend/backend.go

@@ -4,6 +4,11 @@ import (
 	"context"
 	"errors"
 	"fmt"
+
+	"github.com/sirupsen/logrus"
+
+	"github.com/docker/api/compose"
+	"github.com/docker/api/containers"
 )
 
 var (
@@ -24,17 +29,23 @@ var backends = struct {
 	r []*registeredBackend
 }{}
 
+// Aggregation of service interfaces
+type Service interface {
+	ContainerService() containers.Service
+	ComposeService() compose.Service
+}
+
 // Register adds a typed backend to the registry
 func Register(name string, backendType string, init initFunc) {
 	if name == "" {
-		panic(errNoName)
+		logrus.Fatal(errNoName)
 	}
 	if backendType == "" {
-		panic(errNoType)
+		logrus.Fatal(errNoType)
 	}
 	for _, b := range backends.r {
 		if b.backendType == backendType {
-			panic(errTypeRegistered)
+			logrus.Fatal(errTypeRegistered)
 		}
 	}
 

+ 58 - 0
cli/cmd/compose/compose.go

@@ -0,0 +1,58 @@
+package compose
+
+import (
+	"github.com/spf13/cobra"
+
+	"github.com/docker/api/client"
+	"github.com/docker/api/compose"
+)
+
+func Command() *cobra.Command {
+	command := &cobra.Command{
+		Short: "Docker Compose",
+		Use:   "compose",
+	}
+	command.AddCommand(
+		upCommand(),
+		downCommand(),
+	)
+	return command
+}
+
+func upCommand() *cobra.Command {
+	opts := &compose.ProjectOptions{}
+	upCmd := &cobra.Command{
+		Use: "up",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			c, err := client.New(cmd.Context())
+			if err != nil {
+				return err
+			}
+			return c.ComposeService().Up(cmd.Context(), *opts)
+		},
+	}
+	upCmd.Flags().StringVar(&opts.Name, "name", "", "Project name")
+	upCmd.Flags().StringVar(&opts.WorkDir, "workdir", ".", "Work dir")
+	upCmd.Flags().StringArrayVarP(&opts.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
+	upCmd.Flags().StringArrayVarP(&opts.Environment, "environment", "e", []string{}, "Environment variables")
+
+	return upCmd
+}
+
+func downCommand() *cobra.Command {
+	opts := &compose.ProjectOptions{}
+	downCmd := &cobra.Command{
+		Use: "down",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			c, err := client.New(cmd.Context())
+			if err != nil {
+				return err
+			}
+			return c.ComposeService().Down(cmd.Context(), *opts)
+		},
+	}
+	downCmd.Flags().StringVar(&opts.Name, "name", "", "Project name")
+	downCmd.Flags().StringVar(&opts.WorkDir, "workdir", ".", "Work dir")
+
+	return downCmd
+}

+ 5 - 4
cli/main.go

@@ -35,14 +35,14 @@ import (
 	"os/exec"
 	"path/filepath"
 
-	// Backend registrations
-	_ "github.com/docker/api/azure"
-	_ "github.com/docker/api/example"
-
 	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
+	_ "github.com/docker/api/azure"
+	_ "github.com/docker/api/example"
+
 	"github.com/docker/api/cli/cmd"
+	"github.com/docker/api/cli/cmd/compose"
 	"github.com/docker/api/cli/cmd/run"
 	apicontext "github.com/docker/api/context"
 	"github.com/docker/api/context/store"
@@ -101,6 +101,7 @@ func main() {
 		run.Command(),
 		cmd.ExecCommand(),
 		cmd.LogsCommand(),
+		compose.Command(),
 	)
 
 	helpFunc := root.HelpFunc()

+ 11 - 5
client/client.go

@@ -34,6 +34,7 @@ import (
 	"github.com/docker/api/backend"
 	backendv1 "github.com/docker/api/backend/v1"
 	cliv1 "github.com/docker/api/cli/v1"
+	"github.com/docker/api/compose"
 	composev1 "github.com/docker/api/compose/v1"
 	"github.com/docker/api/containers"
 	containersv1 "github.com/docker/api/containers/v1"
@@ -57,13 +58,13 @@ func New(ctx context.Context) (*Client, error) {
 		return nil, err
 	}
 
-	ba, ok := b.(containers.ContainerService)
+	service, ok := b.(backend.Service)
 	if !ok {
 		return nil, errors.New("backend not found")
 	}
 	return &Client{
 		backendType: contextType,
-		cc:          ba,
+		bs:          service,
 	}, nil
 
 }
@@ -76,10 +77,15 @@ type Client struct {
 	composev1.ComposeClient
 
 	backendType string
-	cc          containers.ContainerService
+	bs          backend.Service
 }
 
 // ContainerService returns the backend service for the current context
-func (c *Client) ContainerService() containers.ContainerService {
-	return c.cc
+func (c *Client) ContainerService() containers.Service {
+	return c.bs.ContainerService()
+}
+
+// ComposeService returns the backend service for the current context
+func (c *Client) ComposeService() compose.Service {
+	return c.bs.ComposeService()
 }

+ 13 - 0
compose/api.go

@@ -0,0 +1,13 @@
+package compose
+
+import (
+	"context"
+)
+
+// Service manages a compose project
+type Service interface {
+	// Up executes the equivalent to a `compose up`
+	Up(ctx context.Context, opts ProjectOptions) error
+	// Down executes the equivalent to a `compose down`
+	Down(ctx context.Context, opts ProjectOptions) error
+}

+ 5 - 1
compose/project.go

@@ -51,7 +51,11 @@ func ProjectFromOptions(options *ProjectOptions) (*Project, error) {
 	name := options.Name
 	if name == "" {
 		r := regexp.MustCompile(`[^a-z0-9\\-_]+`)
-		name = r.ReplaceAllString(strings.ToLower(filepath.Base(options.WorkDir)), "")
+		absPath, err := filepath.Abs(options.WorkDir)
+		if err != nil {
+			return nil, err
+		}
+		name = r.ReplaceAllString(strings.ToLower(filepath.Base(absPath)), "")
 	}
 
 	return newProject(types.ConfigDetails{

+ 2 - 2
containers/api.go

@@ -44,8 +44,8 @@ type LogsRequest struct {
 	Writer io.Writer
 }
 
-// ContainerService interacts with the underlying container backend
-type ContainerService interface {
+// Service interacts with the underlying container backend
+type Service interface {
 	// List returns all the containers
 	List(ctx context.Context) ([]Container, error)
 	// Run creates and starts a container