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:
Guillaume Tardif 2020-12-17 18:34:48 +01:00 committed by GitHub
commit a17e397df3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 243 additions and 153 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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