mirror of
https://github.com/docker/compose.git
synced 2026-10-12 02:13:08 +00:00
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]>
122 lines
5.0 KiB
Go
122 lines
5.0 KiB
Go
//go:build e2e
|
|
|
|
/*
|
|
Copyright 2026 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 (
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gotest.tools/v3/poll"
|
|
)
|
|
|
|
// startLocalRegistry runs a throwaway registry container on a random host
|
|
// port for the lifetime of the scenario and returns its host:port address.
|
|
func startLocalRegistry(t *testing.T, s *Scenario) string {
|
|
t.Helper()
|
|
c := s.CLI()
|
|
name := s.Project() + "-registry"
|
|
c.RunDockerCmd(t, "run", "--name", name, "-P", "-d", "registry:3")
|
|
s.Defer(DockerCmd("rm", "--force", name))
|
|
port := c.RunDockerCmd(t, "inspect", "--format", `{{ (index (index .NetworkSettings.Ports "5000/tcp") 0).HostPort }}`, name).Stdout()
|
|
registry := "localhost:" + strings.TrimSpace(port)
|
|
|
|
registryURL := "http://" + registry + "/v2/"
|
|
poll.WaitOn(t, func(l poll.LogT) poll.Result {
|
|
resp, err := http.Get(registryURL) //nolint:gosec,noctx
|
|
if err != nil {
|
|
return poll.Continue("registry not ready: %v", err)
|
|
}
|
|
_ = resp.Body.Close()
|
|
if resp.StatusCode < 500 {
|
|
return poll.Success()
|
|
}
|
|
return poll.Continue("registry not ready, status %d", resp.StatusCode)
|
|
}, poll.WithTimeout(10*time.Second), poll.WithDelay(500*time.Millisecond))
|
|
return registry
|
|
}
|
|
|
|
func TestOciRemoteUp(t *testing.T) {
|
|
s := NewScenario(t, "up on an oci:// artifact must deploy the published project, bundled env files included")
|
|
registry := startLocalRegistry(t, s)
|
|
ref := registry + "/remote-up:v1"
|
|
s.Env("XDG_CACHE_HOME=" + t.TempDir())
|
|
s.Step("publish pushes the project and its env file to the registry",
|
|
ComposeCmd("publish", "--with-env", "--yes", "--insecure-registry", ref))
|
|
s.FromRemote("oci://"+ref, "--insecure-registry", registry)
|
|
s.Step("up pulls the artifact and starts the service",
|
|
ComposeCmd("up", "-d", "--wait", "--yes").Within(60*time.Second),
|
|
ServiceState("app", "running"),
|
|
// the env file travels as an artifact layer: its effect proves the
|
|
// bundle was consumed whole, not just the compose.yaml
|
|
ContainerEnv("app", "FLAVOR", "published"))
|
|
}
|
|
|
|
func TestOciRemoteTagSelection(t *testing.T) {
|
|
s := NewScenario(t, "the tag of an oci:// reference must select which published revision is deployed")
|
|
registry := startLocalRegistry(t, s)
|
|
refV1 := registry + "/remote-tags:v1"
|
|
refV2 := registry + "/remote-tags:v2"
|
|
s.Env("XDG_CACHE_HOME=" + t.TempDir())
|
|
s.Step("publish the v1 revision",
|
|
ComposeCmd("publish", "--with-env", "--yes", "--insecure-registry", refV1))
|
|
// fixture preparation for the second revision, like a git branch: the
|
|
// anchored copy is edited before publishing under the other tag
|
|
if err := os.WriteFile(filepath.Join(s.Dir(), "app.env"), []byte("FLAVOR=v2\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s.Step("publish the v2 revision under another tag",
|
|
ComposeCmd("publish", "--with-env", "--yes", "--insecure-registry", refV2))
|
|
s.FromRemote("oci://"+refV1, "--insecure-registry", registry)
|
|
s.Step("up on the v1 tag deploys the v1 revision, not the latest published one",
|
|
ComposeCmd("up", "-d", "--wait", "--yes").Within(60*time.Second),
|
|
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")))
|
|
}
|