mirror of
https://github.com/docker/compose.git
synced 2025-07-12 16:24:32 +02:00
commit
a6dd996988
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -177,6 +177,9 @@ jobs:
|
|||||||
if: ${{ matrix.mode == 'plugin' }}
|
if: ${{ matrix.mode == 'plugin' }}
|
||||||
run: |
|
run: |
|
||||||
make e2e-compose
|
make e2e-compose
|
||||||
|
-
|
||||||
|
name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v3
|
||||||
-
|
-
|
||||||
name: Test standalone mode
|
name: Test standalone mode
|
||||||
if: ${{ matrix.mode == 'standalone' }}
|
if: ${{ matrix.mode == 'standalone' }}
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
bin/
|
bin/
|
||||||
/.vscode/
|
/.vscode/
|
||||||
|
coverage.out
|
||||||
|
5
Makefile
5
Makefile
@ -33,7 +33,8 @@ ifeq ($(DETECTED_OS),Windows)
|
|||||||
BINARY_EXT=.exe
|
BINARY_EXT=.exe
|
||||||
endif
|
endif
|
||||||
|
|
||||||
TEST_FLAGS?=
|
TEST_COVERAGE_FLAGS = -race -coverprofile=coverage.out -covermode=atomic
|
||||||
|
TEST_FLAGS?= -timeout 15m
|
||||||
E2E_TEST?=
|
E2E_TEST?=
|
||||||
ifeq ($(E2E_TEST),)
|
ifeq ($(E2E_TEST),)
|
||||||
else
|
else
|
||||||
@ -61,7 +62,7 @@ install: binary
|
|||||||
.PHONY: e2e-compose
|
.PHONY: e2e-compose
|
||||||
e2e-compose: ## Run end to end local tests in plugin mode. Set E2E_TEST=TestName to run a single test
|
e2e-compose: ## Run end to end local tests in plugin mode. Set E2E_TEST=TestName to run a single test
|
||||||
docker compose version
|
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
|
.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
|
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 @@
|
|||||||
[](https://pkg.go.dev/github.com/docker/compose/v2)
|
[](https://pkg.go.dev/github.com/docker/compose/v2)
|
||||||
[](https://github.com/docker/compose/actions?query=workflow%3Aci)
|
[](https://github.com/docker/compose/actions?query=workflow%3Aci)
|
||||||
[](https://goreportcard.com/report/github.com/docker/compose/v2)
|
[](https://goreportcard.com/report/github.com/docker/compose/v2)
|
||||||
|
[](https://codecov.io/gh/docker/compose)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@ -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
|
package e2e
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
@ -28,6 +28,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/compose/v2/pkg/utils"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
"gotest.tools/v3/icmd"
|
"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.
|
// require a separate groupID from the process running tests, in order to simulate ctrl+C from a terminal.
|
||||||
// sending kill signal
|
// sending kill signal
|
||||||
cmd, stdout, stderr, err := StartWithNewGroupID(c.NewDockerComposeCmd(t, "-f", buildProjectPath, "build",
|
stdout := &utils.SafeBuffer{}
|
||||||
"--progress", "plain"))
|
stderr := &utils.SafeBuffer{}
|
||||||
|
cmd, err := StartWithNewGroupID(context.Background(),
|
||||||
|
c.NewDockerComposeCmd(t, "-f", buildProjectPath, "build", "--progress", "plain"),
|
||||||
|
stdout,
|
||||||
|
stderr)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
c.WaitForCondition(t, func() (bool, string) {
|
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) {
|
func StartWithNewGroupID(ctx context.Context, command icmd.Cmd, stdout *utils.SafeBuffer, stderr *utils.SafeBuffer) (*exec.Cmd, error) {
|
||||||
cmd := exec.Command(command.Command[0], command.Command[1:]...)
|
cmd := exec.CommandContext(ctx, command.Command[0], command.Command[1:]...)
|
||||||
cmd.Env = command.Env
|
cmd.Env = command.Env
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||||
|
if stdout != nil {
|
||||||
var stdout bytes.Buffer
|
cmd.Stdout = stdout
|
||||||
var stderr bytes.Buffer
|
}
|
||||||
cmd.Stdout = &stdout
|
if stderr != nil {
|
||||||
cmd.Stderr = &stderr
|
cmd.Stderr = stderr
|
||||||
|
}
|
||||||
err := cmd.Start()
|
err := cmd.Start()
|
||||||
return cmd, &stdout, &stderr, err
|
return cmd, err
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/compose/v2/pkg/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gotest.tools/v3/icmd"
|
"gotest.tools/v3/icmd"
|
||||||
@ -52,9 +53,6 @@ func TestUpDependenciesNotStopped(t *testing.T) {
|
|||||||
reset()
|
reset()
|
||||||
t.Cleanup(reset)
|
t.Cleanup(reset)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
t.Log("Launching orphan container (background)")
|
t.Log("Launching orphan container (background)")
|
||||||
c.RunDockerComposeCmd(t,
|
c.RunDockerComposeCmd(t,
|
||||||
"-f=./fixtures/ups-deps-stop/orphan.yaml",
|
"-f=./fixtures/ups-deps-stop/orphan.yaml",
|
||||||
@ -66,22 +64,18 @@ func TestUpDependenciesNotStopped(t *testing.T) {
|
|||||||
RequireServiceState(t, c, "orphan", "running")
|
RequireServiceState(t, c, "orphan", "running")
|
||||||
|
|
||||||
t.Log("Launching app container with implicit dependency")
|
t.Log("Launching app container with implicit dependency")
|
||||||
var upOut lockedBuffer
|
upOut := &utils.SafeBuffer{}
|
||||||
var upCmd *exec.Cmd
|
testCmd := c.NewDockerComposeCmd(t,
|
||||||
go func() {
|
"-f=./fixtures/ups-deps-stop/compose.yaml",
|
||||||
testCmd := c.NewDockerComposeCmd(t,
|
"up",
|
||||||
"-f=./fixtures/ups-deps-stop/compose.yaml",
|
"app",
|
||||||
"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}
|
|
||||||
|
|
||||||
assert.NoError(t, cmd.Start(), "Failed to run compose up")
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
upCmd = cmd
|
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")
|
t.Log("Waiting for containers to be in running state")
|
||||||
upOut.RequireEventuallyContains(t, "hello app")
|
upOut.RequireEventuallyContains(t, "hello app")
|
||||||
@ -89,13 +83,13 @@ func TestUpDependenciesNotStopped(t *testing.T) {
|
|||||||
RequireServiceState(t, c, "dependency", "running")
|
RequireServiceState(t, c, "dependency", "running")
|
||||||
|
|
||||||
t.Log("Simulating Ctrl-C")
|
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")
|
"Failed to send SIGINT to compose up process")
|
||||||
|
|
||||||
time.AfterFunc(5*time.Second, cancel)
|
time.AfterFunc(5*time.Second, cancel)
|
||||||
|
|
||||||
t.Log("Waiting for `compose up` to exit")
|
t.Log("Waiting for `compose up` to exit")
|
||||||
err := upCmd.Wait()
|
err = cmd.Wait()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exitErr := err.(*exec.ExitError)
|
exitErr := err.(*exec.ExitError)
|
||||||
require.EqualValues(t, exitErr.ExitCode(), 130)
|
require.EqualValues(t, exitErr.ExitCode(), 130)
|
||||||
|
78
pkg/utils/safebuffer.go
Normal file
78
pkg/utils/safebuffer.go
Normal file
@ -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…
x
Reference in New Issue
Block a user