Merge pull request #41 from ulyssessouza/add-up

Add compose up and down
This commit is contained in:
Ulysses Souza 2020-05-05 16:00:44 +02:00 committed by GitHub
commit a4e54e9b5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 190 additions and 31 deletions

View File

@ -96,6 +96,14 @@ func createACIContainers(ctx context.Context, aciContext store.AciContext, group
return containerGroup, err 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) { func execACIContainer(ctx context.Context, aciContext store.AciContext, command, containerGroup string, containerName string) (c containerinstance.ContainerExecResponse, err error) {
containerClient, err := getContainerClient(aciContext.SubscriptionID) containerClient, err := getContainerClient(aciContext.SubscriptionID)
if err != nil { if err != nil {

View File

@ -21,11 +21,6 @@ import (
"github.com/docker/api/context/store" "github.com/docker/api/context/store"
) )
type containerService struct {
containerGroupsClient containerinstance.ContainerGroupsClient
ctx store.AciContext
}
func init() { func init() {
backend.Register("aci", "aci", func(ctx context.Context) (interface{}, error) { backend.Register("aci", "aci", func(ctx context.Context) (interface{}, error) {
return New(ctx) return New(ctx)
@ -36,8 +31,8 @@ func getter() interface{} {
return &store.AciContext{} return &store.AciContext{}
} }
// New creates a backend that can manage containers on ACI // New creates a backend that can manage containers
func New(ctx context.Context) (containers.ContainerService, error) { func New(ctx context.Context) (backend.Service, error) {
currentContext := apicontext.CurrentContext(ctx) currentContext := apicontext.CurrentContext(ctx)
contextStore, err := store.New() contextStore, err := store.New()
if err != nil { if err != nil {
@ -53,13 +48,47 @@ func New(ctx context.Context) (containers.ContainerService, error) {
containerGroupsClient := containerinstance.NewContainerGroupsClient(aciContext.SubscriptionID) containerGroupsClient := containerinstance.NewContainerGroupsClient(aciContext.SubscriptionID)
containerGroupsClient.Authorizer = auth containerGroupsClient.Authorizer = auth
return &containerService{ return getAciApiService(containerGroupsClient, aciContext), nil
containerGroupsClient: containerGroupsClient,
ctx: aciContext,
}, nil
} }
func (cs *containerService) List(ctx context.Context) ([]containers.Container, error) { 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 *aciContainerService) List(ctx context.Context) ([]containers.Container, error) {
var containerGroups []containerinstance.ContainerGroup var containerGroups []containerinstance.ContainerGroup
result, err := cs.containerGroupsClient.ListByResourceGroup(ctx, cs.ctx.ResourceGroup) result, err := cs.containerGroupsClient.ListByResourceGroup(ctx, cs.ctx.ResourceGroup)
if err != nil { 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 { for _, containerGroup := range containerGroups {
group, err := cs.containerGroupsClient.Get(ctx, cs.ctx.ResourceGroup, *containerGroup.Name) group, err := cs.containerGroupsClient.Get(ctx, cs.ctx.ResourceGroup, *containerGroup.Name)
if err != nil { if err != nil {
@ -96,7 +125,7 @@ func (cs *containerService) List(ctx context.Context) ([]containers.Container, e
return res, nil 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 var ports []types.ServicePortConfig
for _, p := range r.Ports { for _, p := range r.Ports {
ports = append(ports, types.ServicePortConfig{ ports = append(ports, types.ServicePortConfig{
@ -127,7 +156,7 @@ func (cs *containerService) Run(ctx context.Context, r containers.ContainerConfi
return err 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) containerExecResponse, err := execACIContainer(ctx, cs.ctx, command, name, name)
if err != nil { if err != nil {
return err 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) logs, err := getACIContainerLogs(ctx, cs.ctx, containerName, containerName)
if err != nil { if err != nil {
return err return err
@ -163,3 +192,32 @@ func (cs *containerService) Logs(ctx context.Context, containerName string, req
_, err = fmt.Fprint(req.Writer, logs) _, err = fmt.Fprint(req.Writer, logs)
return err 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
}

View File

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

View File

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

View File

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

View File

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

13
compose/api.go Normal file
View File

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

View File

@ -51,7 +51,11 @@ func ProjectFromOptions(options *ProjectOptions) (*Project, error) {
name := options.Name name := options.Name
if name == "" { if name == "" {
r := regexp.MustCompile(`[^a-z0-9\\-_]+`) 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{ return newProject(types.ConfigDetails{

View File

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