mirror of https://github.com/docker/compose.git
Merge pull request #1071 from docker/local_ecs_moby
use compose-in-go code to implement local ECS simulation context
This commit is contained in:
commit
a17e397df3
|
@ -64,7 +64,7 @@ func (cs *aciComposeService) Start(ctx context.Context, project *types.Project,
|
|||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (cs *aciComposeService) Up(ctx context.Context, project *types.Project, detach bool) error {
|
||||
func (cs *aciComposeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
||||
logrus.Debugf("Up on project with name %q", project.Name)
|
||||
|
||||
if err := autocreateFileshares(ctx, project); err != nil {
|
||||
|
@ -102,14 +102,14 @@ func (cs aciComposeService) warnKeepVolumeOnDown(ctx context.Context, projectNam
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cs *aciComposeService) Down(ctx context.Context, project string) error {
|
||||
logrus.Debugf("Down on project with name %q", project)
|
||||
func (cs *aciComposeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
|
||||
logrus.Debugf("Down on projectName with name %q", projectName)
|
||||
|
||||
if err := cs.warnKeepVolumeOnDown(ctx, project); err != nil {
|
||||
if err := cs.warnKeepVolumeOnDown(ctx, projectName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cg, err := deleteACIContainerGroup(ctx, cs.ctx, project)
|
||||
cg, err := deleteACIContainerGroup(ctx, cs.ctx, projectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -198,6 +198,6 @@ func (cs *aciComposeService) Logs(ctx context.Context, projectName string, consu
|
|||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (cs *aciComposeService) Convert(ctx context.Context, project *types.Project, format string) ([]byte, error) {
|
||||
func (cs *aciComposeService) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
|
||||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
|
|
@ -48,11 +48,11 @@ func (c *composeService) Start(ctx context.Context, project *types.Project, cons
|
|||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (c *composeService) Up(context.Context, *types.Project, bool) error {
|
||||
func (c *composeService) Up(context.Context, *types.Project, compose.UpOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (c *composeService) Down(context.Context, string) error {
|
||||
func (c *composeService) Down(context.Context, string, compose.DownOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,6 @@ func (c *composeService) List(context.Context, string) ([]compose.Stack, error)
|
|||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (c *composeService) Convert(context.Context, *types.Project, string) ([]byte, error) {
|
||||
func (c *composeService) Convert(context.Context, *types.Project, compose.ConvertOptions) ([]byte, error) {
|
||||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
|
|
@ -35,9 +35,9 @@ type Service interface {
|
|||
// Start executes the equivalent to a `compose start`
|
||||
Start(ctx context.Context, project *types.Project, consumer LogConsumer) error
|
||||
// Up executes the equivalent to a `compose up`
|
||||
Up(ctx context.Context, project *types.Project, detach bool) error
|
||||
Up(ctx context.Context, project *types.Project, options UpOptions) error
|
||||
// Down executes the equivalent to a `compose down`
|
||||
Down(ctx context.Context, projectName string) error
|
||||
Down(ctx context.Context, projectName string, options DownOptions) error
|
||||
// Logs executes the equivalent to a `compose logs`
|
||||
Logs(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error
|
||||
// Ps executes the equivalent to a `compose ps`
|
||||
|
@ -45,7 +45,25 @@ type Service interface {
|
|||
// List executes the equivalent to a `docker stack ls`
|
||||
List(ctx context.Context, projectName string) ([]Stack, error)
|
||||
// Convert translate compose model into backend's native format
|
||||
Convert(ctx context.Context, project *types.Project, format string) ([]byte, error)
|
||||
Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error)
|
||||
}
|
||||
|
||||
// UpOptions group options of the Up API
|
||||
type UpOptions struct {
|
||||
// Detach will create services and return immediately
|
||||
Detach bool
|
||||
}
|
||||
|
||||
// DownOptions group options of the Down API
|
||||
type DownOptions struct {
|
||||
// RemoveOrphans will cleanup containers that are not declared on the compose model but own the same labels
|
||||
RemoveOrphans bool
|
||||
}
|
||||
|
||||
// ConvertOptions group options of the Convert API
|
||||
type ConvertOptions struct {
|
||||
// Format define the output format used to dump converted application model (json|yaml)
|
||||
Format string
|
||||
}
|
||||
|
||||
// PortPublisher hold status about published port
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
|
@ -61,7 +63,9 @@ func runConvert(ctx context.Context, opts composeOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
json, err = c.ComposeService().Convert(ctx, project, opts.Format)
|
||||
json, err = c.ComposeService().Convert(ctx, project, compose.ConvertOptions{
|
||||
Format: opts.Format,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ package compose
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose-cli/api/client"
|
||||
|
@ -52,7 +54,9 @@ func runDown(ctx context.Context, opts composeOptions) error {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return projectName, c.ComposeService().Down(ctx, projectName)
|
||||
return projectName, c.ComposeService().Down(ctx, projectName, compose.DownOptions{
|
||||
RemoveOrphans: false,
|
||||
})
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ func upCommand(contextType string) *cobra.Command {
|
|||
Short: "Create and start containers",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
switch contextType {
|
||||
case store.LocalContextType, store.DefaultContextType:
|
||||
case store.LocalContextType, store.DefaultContextType, store.EcsLocalSimulationContextType:
|
||||
return runCreateStart(cmd.Context(), opts, args)
|
||||
default:
|
||||
return runUp(cmd.Context(), opts, args)
|
||||
|
@ -68,7 +68,9 @@ func runUp(ctx context.Context, opts composeOptions, services []string) error {
|
|||
}
|
||||
|
||||
_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
|
||||
return "", c.ComposeService().Up(ctx, project, opts.Detach)
|
||||
return "", c.ComposeService().Up(ctx, project, compose.UpOptions{
|
||||
Detach: opts.Detach,
|
||||
})
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
@ -96,7 +98,7 @@ func runCreateStart(ctx context.Context, opts composeOptions, services []string)
|
|||
fmt.Println("Gracefully stopping...")
|
||||
ctx = context.Background()
|
||||
_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
|
||||
return "", c.ComposeService().Down(ctx, project.Name)
|
||||
return "", c.ComposeService().Down(ctx, project.Name, compose.DownOptions{})
|
||||
})
|
||||
}
|
||||
return err
|
||||
|
|
|
@ -23,6 +23,8 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
|
||||
ecsapi "github.com/aws/aws-sdk-go/service/ecs"
|
||||
"github.com/aws/aws-sdk-go/service/elbv2"
|
||||
cloudmapapi "github.com/aws/aws-sdk-go/service/servicediscovery"
|
||||
|
@ -37,13 +39,13 @@ import (
|
|||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
func (b *ecsAPIService) Convert(ctx context.Context, project *types.Project, format string) ([]byte, error) {
|
||||
func (b *ecsAPIService) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
|
||||
template, err := b.convert(ctx, project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return marshall(template, format)
|
||||
return marshall(template, options.Format)
|
||||
}
|
||||
|
||||
func (b *ecsAPIService) convert(ctx context.Context, project *types.Project) (*cloudformation.Template, error) {
|
||||
|
|
12
ecs/down.go
12
ecs/down.go
|
@ -19,11 +19,13 @@ package ecs
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
|
||||
"github.com/docker/compose-cli/progress"
|
||||
)
|
||||
|
||||
func (b *ecsAPIService) Down(ctx context.Context, project string) error {
|
||||
resources, err := b.aws.ListStackResources(ctx, project)
|
||||
func (b *ecsAPIService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
|
||||
resources, err := b.aws.ListStackResources(ctx, projectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -38,16 +40,16 @@ func (b *ecsAPIService) Down(ctx context.Context, project string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
previousEvents, err := b.previousStackEvents(ctx, project)
|
||||
previousEvents, err := b.previousStackEvents(ctx, projectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = b.aws.DeleteStack(ctx, project)
|
||||
err = b.aws.DeleteStack(ctx, projectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.WaitStackCompletion(ctx, project, stackDelete, previousEvents...)
|
||||
return b.WaitStackCompletion(ctx, projectName, stackDelete, previousEvents...)
|
||||
}
|
||||
|
||||
func (b *ecsAPIService) previousStackEvents(ctx context.Context, project string) ([]string, error) {
|
||||
|
|
|
@ -19,6 +19,8 @@ package local
|
|||
import (
|
||||
"context"
|
||||
|
||||
local_compose "github.com/docker/compose-cli/local/compose"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
|
@ -38,17 +40,19 @@ func init() {
|
|||
}
|
||||
|
||||
type ecsLocalSimulation struct {
|
||||
moby *client.Client
|
||||
moby *client.Client
|
||||
compose compose.Service
|
||||
}
|
||||
|
||||
func service(ctx context.Context) (backend.Service, error) {
|
||||
apiClient, err := client.NewClientWithOpts(client.FromEnv)
|
||||
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ecsLocalSimulation{
|
||||
moby: apiClient,
|
||||
moby: apiClient,
|
||||
compose: local_compose.NewComposeService(apiClient),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -17,80 +17,76 @@
|
|||
package local
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
types2 "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sanathkr/go-yaml"
|
||||
"golang.org/x/mod/semver"
|
||||
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
"github.com/docker/compose-cli/errdefs"
|
||||
"github.com/sanathkr/go-yaml"
|
||||
)
|
||||
|
||||
func (e ecsLocalSimulation) Build(ctx context.Context, project *types.Project) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
return e.compose.Build(ctx, project)
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Push(ctx context.Context, project *types.Project) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
return e.compose.Push(ctx, project)
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Pull(ctx context.Context, project *types.Project) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
return e.compose.Pull(ctx, project)
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Create(ctx context.Context, project *types.Project) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Start(ctx context.Context, project *types.Project, consumer compose.LogConsumer) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Up(ctx context.Context, project *types.Project, detach bool) error {
|
||||
|
||||
cmd := exec.Command("docker-compose", "version", "--short")
|
||||
b := bytes.Buffer{}
|
||||
b.WriteString("v")
|
||||
cmd.Stdout = bufio.NewWriter(&b)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ECS simulation mode require Docker-compose 1.27")
|
||||
}
|
||||
version := semver.MajorMinor(strings.TrimSpace(b.String()))
|
||||
if version == "" {
|
||||
return fmt.Errorf("can't parse docker-compose version: %s", b.String())
|
||||
}
|
||||
if semver.Compare(version, "v1.27") < 0 {
|
||||
return fmt.Errorf("ECS simulation mode require Docker-compose 1.27, found %s", version)
|
||||
}
|
||||
|
||||
converted, err := e.Convert(ctx, project, "json")
|
||||
enhanced, err := e.enhanceForLocalSimulation(project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd = exec.Command("docker-compose", "--context", "default", "--project-directory", project.WorkingDir, "--project-name", project.Name, "-f", "-", "up")
|
||||
cmd.Stdin = strings.NewReader(string(converted))
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
return e.compose.Create(ctx, enhanced)
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Convert(ctx context.Context, project *types.Project, format string) ([]byte, error) {
|
||||
func (e ecsLocalSimulation) Start(ctx context.Context, project *types.Project, consumer compose.LogConsumer) error {
|
||||
return e.compose.Start(ctx, project, consumer)
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
||||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
|
||||
enhanced, err := e.enhanceForLocalSimulation(project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(enhanced.Networks, "default")
|
||||
config := map[string]interface{}{
|
||||
"services": enhanced.Services,
|
||||
"networks": enhanced.Networks,
|
||||
"volumes": enhanced.Volumes,
|
||||
"secrets": enhanced.Secrets,
|
||||
"configs": enhanced.Configs,
|
||||
}
|
||||
switch options.Format {
|
||||
case "json":
|
||||
return json.MarshalIndent(config, "", " ")
|
||||
case "yaml":
|
||||
return yaml.Marshal(config)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported format %q", options)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) enhanceForLocalSimulation(project *types.Project) (*types.Project, error) {
|
||||
project.Networks["credentials_network"] = types.NetworkConfig{
|
||||
Name: "credentials_network",
|
||||
Driver: "bridge",
|
||||
Ipam: types.IPAMConfig{
|
||||
Config: []*types.IPAMPool{
|
||||
|
@ -148,68 +144,21 @@ func (e ecsLocalSimulation) Convert(ctx context.Context, project *types.Project,
|
|||
},
|
||||
},
|
||||
})
|
||||
|
||||
delete(project.Networks, "default")
|
||||
config := map[string]interface{}{
|
||||
"services": project.Services,
|
||||
"networks": project.Networks,
|
||||
"volumes": project.Volumes,
|
||||
"secrets": project.Secrets,
|
||||
"configs": project.Configs,
|
||||
}
|
||||
switch format {
|
||||
case "json":
|
||||
return json.MarshalIndent(config, "", " ")
|
||||
case "yaml":
|
||||
return yaml.Marshal(config)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported format %q", format)
|
||||
}
|
||||
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Down(ctx context.Context, projectName string) error {
|
||||
cmd := exec.Command("docker-compose", "--context", "default", "--project-name", projectName, "-f", "-", "down", "--remove-orphans")
|
||||
cmd.Stdin = strings.NewReader(string(`
|
||||
services:
|
||||
ecs-local-endpoints:
|
||||
image: "amazon/amazon-ecs-local-container-endpoints"
|
||||
`))
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
func (e ecsLocalSimulation) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
|
||||
options.RemoveOrphans = true
|
||||
return e.compose.Down(ctx, projectName, options)
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer, options compose.LogOptions) error {
|
||||
list, err := e.moby.ContainerList(ctx, types2.ContainerListOptions{
|
||||
Filters: filters.NewArgs(filters.Arg("label", "com.docker.compose.project="+projectName)),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
services := map[string]types.ServiceConfig{}
|
||||
for _, c := range list {
|
||||
services[c.Labels["com.docker.compose.service"]] = types.ServiceConfig{
|
||||
Image: "unused",
|
||||
}
|
||||
}
|
||||
|
||||
marshal, err := yaml.Marshal(map[string]interface{}{
|
||||
"services": services,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command("docker-compose", "--context", "default", "--project-name", projectName, "-f", "-", "logs", "-f")
|
||||
cmd.Stdin = strings.NewReader(string(marshal))
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
return e.compose.Logs(ctx, projectName, consumer, options)
|
||||
}
|
||||
|
||||
func (e ecsLocalSimulation) Ps(ctx context.Context, projectName string) ([]compose.ContainerSummary, error) {
|
||||
return nil, errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose ps")
|
||||
return e.compose.Ps(ctx, projectName)
|
||||
}
|
||||
func (e ecsLocalSimulation) List(ctx context.Context, projectName string) ([]compose.Stack, error) {
|
||||
return nil, errors.Wrap(errdefs.ErrNotImplemented, "use docker-compose ls")
|
||||
return e.compose.List(ctx, projectName)
|
||||
}
|
||||
|
|
|
@ -49,14 +49,14 @@ func (b *ecsAPIService) Start(ctx context.Context, project *types.Project, consu
|
|||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (b *ecsAPIService) Up(ctx context.Context, project *types.Project, detach bool) error {
|
||||
func (b *ecsAPIService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
||||
|
||||
err := b.aws.CheckRequirements(ctx, b.Region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template, err := b.Convert(ctx, project, "yaml")
|
||||
template, err := b.Convert(ctx, project, compose.ConvertOptions{Format: "yaml"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ func (b *ecsAPIService) Up(ctx context.Context, project *types.Project, detach b
|
|||
return err
|
||||
}
|
||||
}
|
||||
if detach {
|
||||
if options.Detach {
|
||||
return nil
|
||||
}
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
|
@ -90,7 +90,7 @@ func (b *ecsAPIService) Up(ctx context.Context, project *types.Project, detach b
|
|||
go func() {
|
||||
<-signalChan
|
||||
fmt.Println("user interrupted deployment. Deleting stack...")
|
||||
b.Down(ctx, project.Name) // nolint:errcheck
|
||||
b.Down(ctx, project.Name, compose.DownOptions{}) // nolint:errcheck
|
||||
}()
|
||||
|
||||
err = b.WaitStackCompletion(ctx, project.Name, operation)
|
||||
|
|
|
@ -158,14 +158,14 @@ func (cs *composeService) Start(ctx context.Context, project *types.Project, con
|
|||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (cs *composeService) Up(ctx context.Context, project *types.Project, detach bool) error {
|
||||
func (cs *composeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
||||
|
||||
fmt.Printf("Up command on project %q", project.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *composeService) Down(ctx context.Context, project string) error {
|
||||
fmt.Printf("Down command on project %q", project)
|
||||
func (cs *composeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
|
||||
fmt.Printf("Down command on project %q", projectName)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -179,6 +179,6 @@ func (cs *composeService) Logs(ctx context.Context, projectName string, consumer
|
|||
return errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (cs *composeService) Convert(ctx context.Context, project *types.Project, format string) ([]byte, error) {
|
||||
func (cs *composeService) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
|
||||
return nil, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
|
1
go.mod
1
go.mod
|
@ -51,7 +51,6 @@ require (
|
|||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
golang.org/x/mod v0.3.0
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
|
||||
|
|
|
@ -40,7 +40,7 @@ type composeService struct {
|
|||
apiClient *client.Client
|
||||
}
|
||||
|
||||
func (s *composeService) Up(ctx context.Context, project *types.Project, detach bool) error {
|
||||
func (s *composeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
|
||||
return errdefs2.ErrNotImplemented
|
||||
}
|
||||
|
||||
|
@ -54,13 +54,13 @@ func getContainerName(c moby.Container) string {
|
|||
return c.Names[0][1:]
|
||||
}
|
||||
|
||||
func (s *composeService) Convert(ctx context.Context, project *types.Project, format string) ([]byte, error) {
|
||||
switch format {
|
||||
func (s *composeService) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
|
||||
switch options.Format {
|
||||
case "json":
|
||||
return json.MarshalIndent(project, "", " ")
|
||||
case "yaml":
|
||||
return yaml.Marshal(project)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported format %q", format)
|
||||
return nil, fmt.Errorf("unsupported format %q", options)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
|
||||
"github.com/docker/compose-cli/progress"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
|
@ -30,7 +32,7 @@ import (
|
|||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func (s *composeService) Down(ctx context.Context, projectName string) error {
|
||||
func (s *composeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
|
||||
eg, _ := errgroup.WithContext(ctx)
|
||||
w := progress.ContextWriter(ctx)
|
||||
|
||||
|
@ -39,10 +41,27 @@ func (s *composeService) Down(ctx context.Context, projectName string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = InReverseDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
|
||||
filter := filters.NewArgs(projectFilter(project.Name), serviceFilter(service.Name))
|
||||
return s.removeContainers(ctx, w, eg, filter)
|
||||
containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(project.Name)),
|
||||
All: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = InReverseDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
|
||||
serviceContainers, others := split(containers, isService(service.Name))
|
||||
err := s.removeContainers(ctx, w, eg, serviceContainers)
|
||||
containers = others
|
||||
return err
|
||||
})
|
||||
|
||||
if options.RemoveOrphans {
|
||||
err := s.removeContainers(ctx, w, eg, containers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -70,14 +89,7 @@ func (s *composeService) Down(ctx context.Context, projectName string) error {
|
|||
return eg.Wait()
|
||||
}
|
||||
|
||||
func (s *composeService) removeContainers(ctx context.Context, w progress.Writer, eg *errgroup.Group, filter filters.Args) error {
|
||||
containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
|
||||
Filters: filter,
|
||||
All: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (s *composeService) removeContainers(ctx context.Context, w progress.Writer, eg *errgroup.Group, containers []moby.Container) error {
|
||||
for _, container := range containers {
|
||||
eg.Go(func() error {
|
||||
eventName := "Container " + getContainerName(container)
|
||||
|
@ -147,3 +159,25 @@ func loadProjectOptionsFromLabels(c moby.Container) (*cli.ProjectOptions, error)
|
|||
cli.WithWorkingDirectory(c.Labels[workingDirLabel]),
|
||||
cli.WithName(c.Labels[projectLabel]))
|
||||
}
|
||||
|
||||
type containerPredicate func(c moby.Container) bool
|
||||
|
||||
func isService(service string) containerPredicate {
|
||||
return func(c moby.Container) bool {
|
||||
return c.Labels[serviceLabel] == service
|
||||
}
|
||||
}
|
||||
|
||||
// split return a container slice with elements to match predicate
|
||||
func split(containers []moby.Container, predicate containerPredicate) ([]moby.Container, []moby.Container) {
|
||||
var right []moby.Container
|
||||
var left []moby.Container
|
||||
for _, c := range containers {
|
||||
if predicate(c) {
|
||||
right = append(right, c)
|
||||
} else {
|
||||
left = append(left, c)
|
||||
}
|
||||
}
|
||||
return right, left
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ func (s *composeService) Logs(ctx context.Context, projectName string, consumer
|
|||
Filters: filters.NewArgs(
|
||||
projectFilter(projectName),
|
||||
),
|
||||
All: true,
|
||||
})
|
||||
|
||||
ignore := func(string) bool {
|
||||
|
|
|
@ -19,6 +19,8 @@ package proxy
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/compose-cli/api/compose"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
|
||||
|
@ -30,7 +32,8 @@ func (p *proxy) Up(ctx context.Context, request *composev1.ComposeUpRequest) (*c
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &composev1.ComposeUpResponse{ProjectName: project.Name}, Client(ctx).ComposeService().Up(ctx, project, true)
|
||||
err = Client(ctx).ComposeService().Up(ctx, project, compose.UpOptions{Detach: true})
|
||||
return &composev1.ComposeUpResponse{ProjectName: project.Name}, err
|
||||
}
|
||||
|
||||
func (p *proxy) Down(ctx context.Context, request *composev1.ComposeDownRequest) (*composev1.ComposeDownResponse, error) {
|
||||
|
@ -42,7 +45,8 @@ func (p *proxy) Down(ctx context.Context, request *composev1.ComposeDownRequest)
|
|||
}
|
||||
projectName = project.Name
|
||||
}
|
||||
return &composev1.ComposeDownResponse{ProjectName: projectName}, Client(ctx).ComposeService().Down(ctx, projectName)
|
||||
err := Client(ctx).ComposeService().Down(ctx, projectName, compose.DownOptions{})
|
||||
return &composev1.ComposeDownResponse{ProjectName: projectName}, err
|
||||
}
|
||||
|
||||
func (p *proxy) Services(ctx context.Context, request *composev1.ComposeServicesRequest) (*composev1.ComposeServicesResponse, error) {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
services:
|
||||
ping:
|
||||
image: busybox:1.27.2
|
||||
command: ping localhost -c 1
|
||||
hello:
|
||||
image: busybox:1.31.0-uclibc
|
||||
command: echo hello
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
"gotest.tools/v3/icmd"
|
||||
|
||||
. "github.com/docker/compose-cli/tests/framework"
|
||||
)
|
||||
|
||||
func TestLocalComposeLogs(t *testing.T) {
|
||||
c := NewParallelE2eCLI(t, binDir)
|
||||
|
||||
const projectName = "compose-e2e-logs"
|
||||
|
||||
t.Run("up", func(t *testing.T) {
|
||||
c.RunDockerCmd("compose", "up", "-d", "-f", "./fixtures/logs-test/compose.yaml", "--project-name", projectName, "-d")
|
||||
})
|
||||
|
||||
t.Run("logs", func(t *testing.T) {
|
||||
res := c.RunDockerCmd("compose", "logs", "--project-name", projectName)
|
||||
res.Assert(t, icmd.Expected{Out: `PING localhost (127.0.0.1)`})
|
||||
res.Assert(t, icmd.Expected{Out: `hello`})
|
||||
})
|
||||
|
||||
t.Run("logs ping", func(t *testing.T) {
|
||||
res := c.RunDockerCmd("compose", "logs", "--project-name", projectName, "ping")
|
||||
res.Assert(t, icmd.Expected{Out: `PING localhost (127.0.0.1)`})
|
||||
assert.Assert(t, !strings.Contains(res.Stdout(), "hello"))
|
||||
})
|
||||
|
||||
t.Run("logs hello", func(t *testing.T) {
|
||||
res := c.RunDockerCmd("compose", "logs", "--project-name", projectName, "hello", "ping")
|
||||
res.Assert(t, icmd.Expected{Out: `PING localhost (127.0.0.1)`})
|
||||
res.Assert(t, icmd.Expected{Out: `hello`})
|
||||
})
|
||||
|
||||
t.Run("down", func(t *testing.T) {
|
||||
_ = c.RunDockerCmd("compose", "down", "--project-name", projectName)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue