mirror of https://github.com/docker/compose.git
commit
a6dd996988
|
@ -177,6 +177,9 @@ jobs:
|
|||
if: ${{ matrix.mode == 'plugin' }}
|
||||
run: |
|
||||
make e2e-compose
|
||||
-
|
||||
name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
-
|
||||
name: Test standalone mode
|
||||
if: ${{ matrix.mode == 'standalone' }}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
bin/
|
||||
/.vscode/
|
||||
coverage.out
|
||||
|
|
5
Makefile
5
Makefile
|
@ -33,7 +33,8 @@ ifeq ($(DETECTED_OS),Windows)
|
|||
BINARY_EXT=.exe
|
||||
endif
|
||||
|
||||
TEST_FLAGS?=
|
||||
TEST_COVERAGE_FLAGS = -race -coverprofile=coverage.out -covermode=atomic
|
||||
TEST_FLAGS?= -timeout 15m
|
||||
E2E_TEST?=
|
||||
ifeq ($(E2E_TEST),)
|
||||
else
|
||||
|
@ -61,7 +62,7 @@ install: binary
|
|||
.PHONY: e2e-compose
|
||||
e2e-compose: ## Run end to end local tests in plugin mode. Set E2E_TEST=TestName to run a single test
|
||||
docker compose version
|
||||
go test $(TEST_FLAGS) -count=1 ./pkg/e2e
|
||||
go test $(TEST_FLAGS) $(TEST_COVERAGE_FLAGS) -count=1 ./pkg/e2e
|
||||
|
||||
.PHONY: e2e-compose-standalone
|
||||
e2e-compose-standalone: ## Run End to end local tests in standalone mode. Set E2E_TEST=TestName to run a single test
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
[![PkgGoDev](https://img.shields.io/badge/go.dev-docs-007d9c?style=flat-square&logo=go&logoColor=white)](https://pkg.go.dev/github.com/docker/compose/v2)
|
||||
[![Build Status](https://img.shields.io/github/workflow/status/docker/compose/ci?label=ci&logo=github&style=flat-square)](https://github.com/docker/compose/actions?query=workflow%3Aci)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/docker/compose/v2?style=flat-square)](https://goreportcard.com/report/github.com/docker/compose/v2)
|
||||
[![Codecov](https://codecov.io/gh/docker/compose/branch/master/graph/badge.svg?token=HP3K4Y4ctu)](https://codecov.io/gh/docker/compose)
|
||||
|
||||
![Docker Compose](logo.png?raw=true "Docker Compose Logo")
|
||||
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 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 (
|
||||
"bytes"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type lockedBuffer struct {
|
||||
mu sync.Mutex
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func (l *lockedBuffer) Read(p []byte) (n int, err error) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
return l.buf.Read(p)
|
||||
}
|
||||
|
||||
func (l *lockedBuffer) Write(p []byte) (n int, err error) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
return l.buf.Write(p)
|
||||
}
|
||||
|
||||
func (l *lockedBuffer) String() string {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
return l.buf.String()
|
||||
}
|
||||
|
||||
func (l *lockedBuffer) RequireEventuallyContains(t testing.TB, v string) {
|
||||
t.Helper()
|
||||
var bufContents strings.Builder
|
||||
require.Eventuallyf(t, func() bool {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if _, err := l.buf.WriteTo(&bufContents); err != nil {
|
||||
require.FailNowf(t, "Failed to copy from buffer",
|
||||
"Error: %v", err)
|
||||
}
|
||||
return strings.Contains(bufContents.String(), v)
|
||||
}, 5*time.Second, 20*time.Millisecond,
|
||||
"Buffer did not contain %q\n============\n%s\n============",
|
||||
v, &bufContents)
|
||||
}
|
|
@ -20,7 +20,7 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
@ -28,6 +28,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/icmd"
|
||||
)
|
||||
|
@ -41,8 +42,12 @@ func TestComposeCancel(t *testing.T) {
|
|||
|
||||
// require a separate groupID from the process running tests, in order to simulate ctrl+C from a terminal.
|
||||
// sending kill signal
|
||||
cmd, stdout, stderr, err := StartWithNewGroupID(c.NewDockerComposeCmd(t, "-f", buildProjectPath, "build",
|
||||
"--progress", "plain"))
|
||||
stdout := &utils.SafeBuffer{}
|
||||
stderr := &utils.SafeBuffer{}
|
||||
cmd, err := StartWithNewGroupID(context.Background(),
|
||||
c.NewDockerComposeCmd(t, "-f", buildProjectPath, "build", "--progress", "plain"),
|
||||
stdout,
|
||||
stderr)
|
||||
assert.NilError(t, err)
|
||||
|
||||
c.WaitForCondition(t, func() (bool, string) {
|
||||
|
@ -65,15 +70,16 @@ func TestComposeCancel(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func StartWithNewGroupID(command icmd.Cmd) (*exec.Cmd, *bytes.Buffer, *bytes.Buffer, error) {
|
||||
cmd := exec.Command(command.Command[0], command.Command[1:]...)
|
||||
func StartWithNewGroupID(ctx context.Context, command icmd.Cmd, stdout *utils.SafeBuffer, stderr *utils.SafeBuffer) (*exec.Cmd, error) {
|
||||
cmd := exec.CommandContext(ctx, command.Command[0], command.Command[1:]...)
|
||||
cmd.Env = command.Env
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
if stdout != nil {
|
||||
cmd.Stdout = stdout
|
||||
}
|
||||
if stderr != nil {
|
||||
cmd.Stderr = stderr
|
||||
}
|
||||
err := cmd.Start()
|
||||
return cmd, &stdout, &stderr, err
|
||||
return cmd, err
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gotest.tools/v3/icmd"
|
||||
|
@ -52,9 +53,6 @@ func TestUpDependenciesNotStopped(t *testing.T) {
|
|||
reset()
|
||||
t.Cleanup(reset)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
t.Log("Launching orphan container (background)")
|
||||
c.RunDockerComposeCmd(t,
|
||||
"-f=./fixtures/ups-deps-stop/orphan.yaml",
|
||||
|
@ -66,22 +64,18 @@ func TestUpDependenciesNotStopped(t *testing.T) {
|
|||
RequireServiceState(t, c, "orphan", "running")
|
||||
|
||||
t.Log("Launching app container with implicit dependency")
|
||||
var upOut lockedBuffer
|
||||
var upCmd *exec.Cmd
|
||||
go func() {
|
||||
testCmd := c.NewDockerComposeCmd(t,
|
||||
"-f=./fixtures/ups-deps-stop/compose.yaml",
|
||||
"up",
|
||||
"app",
|
||||
)
|
||||
cmd := exec.CommandContext(ctx, testCmd.Command[0], testCmd.Command[1:]...)
|
||||
cmd.Env = testCmd.Env
|
||||
cmd.Stdout = &upOut
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
upOut := &utils.SafeBuffer{}
|
||||
testCmd := c.NewDockerComposeCmd(t,
|
||||
"-f=./fixtures/ups-deps-stop/compose.yaml",
|
||||
"up",
|
||||
"app",
|
||||
)
|
||||
|
||||
assert.NoError(t, cmd.Start(), "Failed to run compose up")
|
||||
upCmd = cmd
|
||||
}()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
cmd, err := StartWithNewGroupID(ctx, testCmd, upOut, nil)
|
||||
assert.NoError(t, err, "Failed to run compose up")
|
||||
|
||||
t.Log("Waiting for containers to be in running state")
|
||||
upOut.RequireEventuallyContains(t, "hello app")
|
||||
|
@ -89,13 +83,13 @@ func TestUpDependenciesNotStopped(t *testing.T) {
|
|||
RequireServiceState(t, c, "dependency", "running")
|
||||
|
||||
t.Log("Simulating Ctrl-C")
|
||||
require.NoError(t, syscall.Kill(-upCmd.Process.Pid, syscall.SIGINT),
|
||||
require.NoError(t, syscall.Kill(-cmd.Process.Pid, syscall.SIGINT),
|
||||
"Failed to send SIGINT to compose up process")
|
||||
|
||||
time.AfterFunc(5*time.Second, cancel)
|
||||
|
||||
t.Log("Waiting for `compose up` to exit")
|
||||
err := upCmd.Wait()
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
exitErr := err.(*exec.ExitError)
|
||||
require.EqualValues(t, exitErr.ExitCode(), 130)
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
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 utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// SafeBuffer is a thread safe version of bytes.Buffer
|
||||
type SafeBuffer struct {
|
||||
m sync.RWMutex
|
||||
b bytes.Buffer
|
||||
}
|
||||
|
||||
// Read is a thread safe version of bytes.Buffer::Read
|
||||
func (b *SafeBuffer) Read(p []byte) (n int, err error) {
|
||||
b.m.RLock()
|
||||
defer b.m.RUnlock()
|
||||
return b.b.Read(p)
|
||||
}
|
||||
|
||||
// Write is a thread safe version of bytes.Buffer::Write
|
||||
func (b *SafeBuffer) Write(p []byte) (n int, err error) {
|
||||
b.m.Lock()
|
||||
defer b.m.Unlock()
|
||||
return b.b.Write(p)
|
||||
}
|
||||
|
||||
// String is a thread safe version of bytes.Buffer::String
|
||||
func (b *SafeBuffer) String() string {
|
||||
b.m.RLock()
|
||||
defer b.m.RUnlock()
|
||||
return b.b.String()
|
||||
}
|
||||
|
||||
// Bytes is a thread safe version of bytes.Buffer::Bytes
|
||||
func (b *SafeBuffer) Bytes() []byte {
|
||||
b.m.RLock()
|
||||
defer b.m.RUnlock()
|
||||
return b.b.Bytes()
|
||||
}
|
||||
|
||||
// RequireEventuallyContains is a thread safe eventual checker for the buffer content
|
||||
func (b *SafeBuffer) RequireEventuallyContains(t testing.TB, v string) {
|
||||
t.Helper()
|
||||
var bufContents strings.Builder
|
||||
require.Eventuallyf(t, func() bool {
|
||||
b.m.Lock()
|
||||
defer b.m.Unlock()
|
||||
if _, err := b.b.WriteTo(&bufContents); err != nil {
|
||||
require.FailNowf(t, "Failed to copy from buffer",
|
||||
"Error: %v", err)
|
||||
}
|
||||
return strings.Contains(bufContents.String(), v)
|
||||
}, 2*time.Second, 20*time.Millisecond,
|
||||
"Buffer did not contain %q\n============\n%s\n============",
|
||||
v, &bufContents)
|
||||
}
|
Loading…
Reference in New Issue