Browse Source

Add compose up and down

Signed-off-by: Ulysses Souza <[email protected]>
Ulysses Souza 5 years ago
parent
commit
03e418cbbb
12 changed files with 135 additions and 21 deletions
  1. 8 0
      azure/aci.go
  2. 37 8
      azure/backend.go
  3. 58 0
      cli/cmd/compose/compose.go
  4. 1 1
      cli/cmd/exec.go
  5. 1 1
      cli/cmd/logs.go
  6. 1 1
      cli/cmd/ps.go
  7. 1 1
      cli/cmd/run/run.go
  8. 2 0
      cli/main.go
  9. 6 6
      client/client.go
  10. 13 0
      compose/api.go
  11. 5 1
      compose/project.go
  12. 2 2
      server/proxy/proxy.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 {

+ 37 - 8
azure/backend.go

@@ -21,7 +21,7 @@ import (
 	"github.com/docker/api/context/store"
 )
 
-type containerService struct {
+type aciApiService struct {
 	containerGroupsClient containerinstance.ContainerGroupsClient
 	ctx                   store.AciContext
 }
@@ -36,8 +36,13 @@ func getter() interface{} {
 	return &store.AciContext{}
 }
 
+type AciService interface {
+	containers.ContainerService
+	compose.Service
+}
+
 // New creates a backend that can manage containers on ACI
-func New(ctx context.Context) (containers.ContainerService, error) {
+func New(ctx context.Context) (AciService, error) {
 	currentContext := apicontext.CurrentContext(ctx)
 	contextStore, err := store.New()
 	if err != nil {
@@ -53,13 +58,13 @@ func New(ctx context.Context) (containers.ContainerService, error) {
 	containerGroupsClient := containerinstance.NewContainerGroupsClient(aciContext.SubscriptionID)
 	containerGroupsClient.Authorizer = auth
 
-	return &containerService{
+	return &aciApiService{
 		containerGroupsClient: containerGroupsClient,
 		ctx:                   aciContext,
 	}, nil
 }
 
-func (cs *containerService) List(ctx context.Context) ([]containers.Container, error) {
+func (cs *aciApiService) 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 +78,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 +101,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 *aciApiService) Run(ctx context.Context, r containers.ContainerConfig) error {
 	var ports []types.ServicePortConfig
 	for _, p := range r.Ports {
 		ports = append(ports, types.ServicePortConfig{
@@ -127,7 +132,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 *aciApiService) 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 +147,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 *aciApiService) 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 +168,27 @@ func (cs *containerService) Logs(ctx context.Context, containerName string, req
 	_, err = fmt.Fprint(req.Writer, logs)
 	return err
 }
+
+func (cs *aciApiService) 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 *aciApiService) 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
+}

+ 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.AciService().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.AciService().Down(cmd.Context(), *opts)
+		},
+	}
+	downCmd.Flags().StringVar(&opts.Name, "name",  "", "Project name")
+	downCmd.Flags().StringVar(&opts.WorkDir, "workdir",  ".", "Work dir")
+
+	return downCmd
+}

+ 1 - 1
cli/cmd/exec.go

@@ -62,5 +62,5 @@ func runExec(ctx context.Context, opts execOpts, name string, command string) er
 		stdout = con
 	}
 
-	return c.ContainerService().Exec(ctx, name, command, os.Stdin, stdout)
+	return c.AciService().Exec(ctx, name, command, os.Stdin, stdout)
 }

+ 1 - 1
cli/cmd/logs.go

@@ -46,5 +46,5 @@ func runLogs(ctx context.Context, containerName string, opts logsOpts) error {
 		Writer: os.Stdout,
 	}
 
-	return c.ContainerService().Logs(ctx, containerName, req)
+	return c.AciService().Logs(ctx, containerName, req)
 }

+ 1 - 1
cli/cmd/ps.go

@@ -23,7 +23,7 @@ var PsCommand = cobra.Command{
 			return errors.Wrap(err, "cannot connect to backend")
 		}
 
-		containers, err := c.ContainerService().List(ctx)
+		containers, err := c.AciService().List(ctx)
 		if err != nil {
 			return errors.Wrap(err, "fetch containers")
 		}

+ 1 - 1
cli/cmd/run/run.go

@@ -65,5 +65,5 @@ func runRun(ctx context.Context, image string, opts runOpts) error {
 		return err
 	}
 
-	return c.ContainerService().Run(ctx, project)
+	return c.AciService().Run(ctx, project)
 }

+ 2 - 0
cli/main.go

@@ -38,6 +38,7 @@ import (
 
 	// Backend registrations
 	_ "github.com/docker/api/azure"
+	"github.com/docker/api/cli/cmd/compose"
 	_ "github.com/docker/api/example"
 
 	"github.com/sirupsen/logrus"
@@ -102,6 +103,7 @@ func main() {
 		run.Command(),
 		cmd.ExecCommand(),
 		cmd.LogsCommand(),
+		compose.Command(),
 	)
 
 	helpFunc := root.HelpFunc()

+ 6 - 6
client/client.go

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

+ 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
server/proxy/proxy.go

@@ -31,7 +31,7 @@ type proxyContainerAPI struct{}
 func (p *proxyContainerAPI) List(ctx context.Context, _ *v1.ListRequest) (*v1.ListResponse, error) {
 	client := Client(ctx)
 
-	c, err := client.ContainerService().List(ctx)
+	c, err := client.AciService().List(ctx)
 	if err != nil {
 		return &v1.ListResponse{}, nil
 	}
@@ -52,7 +52,7 @@ func (p *proxyContainerAPI) List(ctx context.Context, _ *v1.ListRequest) (*v1.Li
 func (p *proxyContainerAPI) Create(ctx context.Context, request *v1.CreateRequest) (*v1.CreateResponse, error) {
 	client := Client(ctx)
 
-	err := client.ContainerService().Run(ctx, containers.ContainerConfig{
+	err := client.AciService().Run(ctx, containers.ContainerConfig{
 		ID:    request.Id,
 		Image: request.Image,
 	})