mirror of
https://github.com/docker/compose.git
synced 2026-10-12 02:13:08 +00:00
test(e2e): reproduce --project-directory ignored on an oci:// project
TestOciRemoteProjectDirectory publishes a project whose only service mounts a relative volume, then runs `up` on the oci:// artifact with an explicit --project-directory pointing elsewhere. The relative volume must resolve against that directory. It currently fails against the pinned compose-go: LoadConfigFiles defaults the working dir to the downloaded artifact's own cache directory whenever a remote resource loader is involved, silently overriding the explicit --project-directory the same way it would a mere default -- docker/compose#14224. On Docker Desktop this surfaces as a hard failure (the cache directory isn't a shared mount); on Linux it silently mounts the wrong directory, matching the original report. New BindMountSource check (pkg/e2e/checks.go) pins a service's bind mount source to an exact expected path, for tests that need to verify which working directory a relative volume path resolved against. Needs the compose-go fix (github.com/ndeloof/compose-go@a626c70, branch 14224-working-dir) to pass -- not yet reflected in go.mod here. Signed-off-by: Nicolas De Loof <[email protected]>
This commit is contained in:
committed by
Guillaume Lours
parent
34b170c798
commit
207a3eb6f7
@@ -27,6 +27,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"slices"
|
||||
@@ -553,6 +554,53 @@ func ContainerEnv(service, name, value string) Check {
|
||||
}
|
||||
}
|
||||
|
||||
// BindMountSource expects the service's bind mount at target to resolve to
|
||||
// exactly wantSource — pinning down which working directory a relative
|
||||
// volume path was resolved against (e.g. an explicit --project-directory,
|
||||
// as opposed to wherever the compose file itself was loaded from).
|
||||
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 {
|
||||
containers := ctx.curr.service(service)
|
||||
if len(containers) == 0 {
|
||||
return errors.New("service has no container")
|
||||
}
|
||||
wantAbs, err := filepath.Abs(wantSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
Destination string
|
||||
Source string
|
||||
}
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(res.Stdout())), &mounts); err != nil {
|
||||
return err
|
||||
}
|
||||
found := false
|
||||
for _, m := range mounts {
|
||||
if m.Destination != target {
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
if m.Source != wantAbs {
|
||||
return fmt.Errorf("container %s mount %s resolves to %s, want %s", c.Name, target, m.Source, wantAbs)
|
||||
}
|
||||
}
|
||||
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{
|
||||
|
||||
@@ -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")))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
services:
|
||||
app:
|
||||
image: alpine
|
||||
init: true
|
||||
command: sleep infinity
|
||||
volumes:
|
||||
- ./data:/data
|
||||
Reference in New Issue
Block a user