Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions pkg/e2e/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"runtime"
"slices"
Expand Down Expand Up @@ -553,6 +554,59 @@ func ContainerEnv(service, name, value string) Check {
}
}

// BindMountSource expects the service's bind mount at target to resolve to
// exactly wantSource (which must be absolute — this check pins down which
// working directory a relative volume path was resolved against, e.g. an
// explicit --project-directory, so a relative wantSource here would silently
// compare against the wrong base: the test process's own cwd, not any
// project directory).
func BindMountSource(service, target, wantSource string) Check {
return Check{
name: fmt.Sprintf("service %q mount %q resolves to %s", service, target, wantSource),
fn: func(ctx *CheckContext) error {
if !filepath.IsAbs(wantSource) {
return fmt.Errorf("BindMountSource: wantSource must be absolute, got %q", wantSource)
}
containers := ctx.curr.service(service)
if len(containers) == 0 {
return errors.New("service has no container")
}
for _, c := range containers {
res := icmd.RunCmd(ctx.scenario.cli.NewDockerCmd(ctx.scenario.t, "inspect", "--format", "{{json .Mounts}}", c.ID))
if res.ExitCode != 0 {
return fmt.Errorf("inspect failed: %s", res.Combined())
}
var mounts []struct {
Type string
Destination string
Source string
}
Comment thread
ndeloof marked this conversation as resolved.
if err := json.Unmarshal([]byte(strings.TrimSpace(res.Stdout())), &mounts); err != nil {
return err
}
found := false
for _, m := range mounts {
if m.Type != "bind" {
continue
}
if m.Destination != target {
continue
}
found = true
Comment thread
ndeloof marked this conversation as resolved.
if m.Source != wantSource {
return fmt.Errorf("container %s mount %s resolves to %s, want %s", c.Name, target, m.Source, wantSource)
}
break
}
if !found {
return fmt.Errorf("container %s has no mount at %s", c.Name, target)
}
}
return nil
},
}
}

// LabelSet expects every container of the service to carry a non-empty label.
func LabelSet(service, key string) Check {
return Check{
Expand Down
28 changes: 28 additions & 0 deletions pkg/e2e/remote_oci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,31 @@ func TestOciRemoteTagSelection(t *testing.T) {
ServiceState("app", "running"),
ContainerEnv("app", "FLAVOR", "v1"))
}

// TestOciRemoteProjectDirectory is the docker/compose#14224 repro: an
// explicit --project-directory must resolve a relative volume path against
// itself, not against the local copy compose downloaded the oci:// artifact
// into (its self-contained-artifact default, otherwise correct for extends
// and bundled env files, but not meant to override an explicit request).
func TestOciRemoteProjectDirectory(t *testing.T) {
s := NewScenario(t, "--project-directory must resolve a relative volume path for an oci:// project, not the artifact's own download directory")
registry := startLocalRegistry(t, s)
ref := registry + "/remote-project-directory:v1"
s.Env("XDG_CACHE_HOME=" + t.TempDir())
s.Step("publish pushes the project to the registry",
ComposeCmd("publish", "--yes", "--insecure-registry", ref))

// distinct from both the anchored testdata copy and the artifact's own
// download cache, so a bind source under it can only come from
// --project-directory being honored
projectDir := t.TempDir()
if err := os.Mkdir(filepath.Join(projectDir, "data"), 0o755); err != nil {
t.Fatal(err)
}

s.FromRemote("oci://"+ref, "--insecure-registry", registry, "--project-directory", projectDir)
s.Step("up resolves the relative volume against --project-directory",
ComposeCmd("up", "-d", "--wait", "--yes").Within(60*time.Second),
ServiceState("app", "running"),
BindMountSource("app", "/data", filepath.Join(projectDir, "data")))
}
7 changes: 7 additions & 0 deletions pkg/e2e/testdata/TestOciRemoteProjectDirectory/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
services:
app:
image: alpine
init: true
command: sleep infinity
volumes:
- ./data:/data
Loading