fix: docker proxy probe for podman and containerized runners (#1231)

Podman creates a missing bind source instead of rejecting it, so the Docker proxy probe passed even when the daemon could not see the runner's files. Jobs of Podman runners in a container got an empty directory at `/var/run/docker.sock` (https://gitea.com/gitea/runner/issues/1193#issuecomment-1700501).

The probe now binds the directory and stats its marker through the created container. A runner in a container stats the marker through its own container, so it no longer creates probe containers, which cost up to 2s per job. `make test-dind TARGET=podman` runs the probe against Podman in CI.

Reviewed-on: https://gitea.com/gitea/runner/pulls/1231
Reviewed-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
silverwind
2026-09-17 20:01:45 +00:00
committed by bircni
parent b9c6305dee
commit 5310d8119a
7 changed files with 140 additions and 75 deletions
+2
View File
@@ -53,6 +53,8 @@ jobs:
# after `make test` so the images it needs are already present on the host daemon. # after `make test` so the images it needs are already present on the host daemon.
- name: test against dind image - name: test against dind image
run: make test-dind run: make test-dind
- name: test against podman
run: make test-dind TARGET=podman
- name: coverage report - name: coverage report
run: | run: |
make coverage-report make coverage-report
+7 -5
View File
@@ -8,6 +8,11 @@ XGO_PACKAGE ?= src.techknowlogick.com/xgo@v1.9.0 # renovate: datasource=go
XGO_VERSION := go-1.27.x XGO_VERSION := go-1.27.x
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.16 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.16 # renovate: datasource=go
PODMAN_TEST_IMAGE ?= quay.io/podman/stable:v5.8.4@sha256:d64648d15311e3997df7c747da47cd359f6175655e8b236ca09b32624935c568 # renovate: datasource=docker
E2E_JOB_IMAGE ?= node:24-bookworm@sha256:6dac556d980b7f0e5498d08f08cee0ca67798b4ad6c23964a9214920e67758d0 # renovate: datasource=docker
SERVICE_IMAGE ?= nginx:1.31.4-alpine@sha256:db35bfc6b2951e7f8a72db5db120288c127ffaeeb4a6d4b95a26fead017d5913 # renovate: datasource=docker
E2E_GITEA_IMAGE ?= gitea/gitea:main-nightly
LINUX_ARCHS ?= linux/amd64,linux/arm64 LINUX_ARCHS ?= linux/amd64,linux/arm64
DARWIN_ARCHS ?= darwin-12/amd64,darwin-12/arm64 DARWIN_ARCHS ?= darwin-12/amd64,darwin-12/arm64
WINDOWS_ARCHS ?= windows/amd64 WINDOWS_ARCHS ?= windows/amd64
@@ -158,12 +163,9 @@ coverage-report: ## turn coverage.txt from `make test` into .tmp/coverage.md
@echo "Wrote .tmp/coverage.md" @echo "Wrote .tmp/coverage.md"
.PHONY: test-dind .PHONY: test-dind
test-dind: ## run the daemon-facing tests against the built dind image (TARGET=dind|dind-rootless) test-dind: ## run the daemon-facing tests against the built dind image or podman (TARGET=dind|dind-rootless|podman)
@./scripts/test-dind.sh $(TARGET) @PODMAN_TEST_IMAGE=$(PODMAN_TEST_IMAGE) ./scripts/test-dind.sh $(TARGET)
E2E_JOB_IMAGE ?= node:24-bookworm@sha256:6dac556d980b7f0e5498d08f08cee0ca67798b4ad6c23964a9214920e67758d0 # renovate: datasource=docker
SERVICE_IMAGE ?= nginx:1.31.4-alpine@sha256:db35bfc6b2951e7f8a72db5db120288c127ffaeeb4a6d4b95a26fead017d5913 # renovate: datasource=docker
E2E_GITEA_IMAGE ?= gitea/gitea:main-nightly
E2E_CONCURRENCY ?= 8 E2E_CONCURRENCY ?= 8
.PHONY: test-e2e .PHONY: test-e2e
+12
View File
@@ -9,6 +9,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"path"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@@ -107,6 +109,16 @@ type Info struct {
Mounts map[string]string // container path to its source on the daemon Mounts map[string]string // container path to its source on the daemon
} }
// DaemonPath maps target to its daemon path through the nearest mount with a source, empty without one.
func (info *Info) DaemonPath(target string) string {
for dir := target; dir != "/" && dir != "."; dir = path.Dir(dir) {
if source := info.Mounts[dir]; source != "" {
return path.Join(source, strings.TrimPrefix(target, dir))
}
}
return ""
}
// Container for managing docker run containers // Container for managing docker run containers
type Container interface { type Container interface {
Create(capAdd, capDrop []string) common.Executor Create(capAdd, capDrop []string) common.Executor
+26 -21
View File
@@ -66,15 +66,11 @@ func NewDockerProxy(ctx context.Context, job string) *DockerProxy {
if info, err := os.Stat(daemonSocket); err != nil || info.Mode()&os.ModeSocket == 0 { if info, err := os.Stat(daemonSocket); err != nil || info.Mode()&os.ModeSocket == 0 {
return nil return nil
} }
dir, err := filepath.Abs(os.TempDir()) dir, daemonDir := runnerContainerWorkdir(probeCtx, cli)
if err != nil { seen := daemonDir != ""
common.Logger(ctx).Infof("docker proxy probe failed, jobs get the daemon socket directly: %v", err) if !seen {
return nil if dir, err = filepath.Abs(os.TempDir()); err == nil {
} daemonDir = dir
daemonDir := dir
seen, err := daemonSeesDir(probeCtx, cli, dir, daemonDir)
if err == nil && !seen {
if dir, daemonDir = runnerContainerWorkdir(probeCtx, cli); daemonDir != "" {
seen, err = daemonSeesDir(probeCtx, cli, dir, daemonDir) seen, err = daemonSeesDir(probeCtx, cli, dir, daemonDir)
} }
} }
@@ -109,18 +105,16 @@ func runnerContainerWorkdir(ctx context.Context, cli client.APIClient) (workdir,
if err != nil { if err != nil {
return "", "" return "", ""
} }
destination := "" if daemonDir = containerInfoFromInspect(self.Container).DaemonPath(workdir); daemonDir == "" {
for _, point := range self.Container.Mounts { return "", ""
if rel, err := filepath.Rel(point.Destination, workdir); err == nil && filepath.IsLocal(rel) && len(point.Destination) > len(destination) { }
destination, daemonDir = point.Destination, filepath.Join(point.Source, rel) if seen, _ := containerSeesMarker(ctx, cli, workdir, self.Container.ID, workdir); !seen { // the hostname may name another container
} return "", ""
} }
return workdir, daemonDir return workdir, daemonDir
} }
// daemonSeesDir reports whether the daemon opens the files the runner writes in dir by their path in daemonDir, func containerSeesMarker(ctx context.Context, cli client.APIClient, dir, id, containerDir string) (bool, error) {
// which is what a job's proxy socket mounted from there needs.
func daemonSeesDir(ctx context.Context, cli client.APIClient, dir, daemonDir string) (bool, error) {
marker, err := os.CreateTemp(dir, "gitea-runner-probe-") marker, err := os.CreateTemp(dir, "gitea-runner-probe-")
if err != nil { if err != nil {
return false, err return false, err
@@ -133,6 +127,16 @@ func daemonSeesDir(ctx context.Context, cli client.APIClient, dir, daemonDir str
if err := marker.Close(); err != nil { if err := marker.Close(); err != nil {
return false, err return false, err
} }
_, err = cli.ContainerStatPath(ctx, id, client.ContainerStatPathOptions{Path: path.Join(containerDir, filepath.Base(marker.Name()))})
if cerrdefs.IsNotFound(err) {
return false, nil
}
return err == nil, err
}
// daemonSeesDir reports whether the daemon opens the files the runner writes in dir by their path in daemonDir,
// which is what a job's proxy socket mounted from there needs.
func daemonSeesDir(ctx context.Context, cli client.APIClient, dir, daemonDir string) (bool, error) {
images, err := cli.ImageList(ctx, client.ImageListOptions{}) images, err := cli.ImageList(ctx, client.ImageListOptions{})
if err != nil { if err != nil {
return false, err return false, err
@@ -143,7 +147,7 @@ func daemonSeesDir(ctx context.Context, cli client.APIClient, dir, daemonDir str
created, err := cli.ContainerCreate(ctx, client.ContainerCreateOptions{ created, err := cli.ContainerCreate(ctx, client.ContainerCreateOptions{
Config: &container.Config{Image: images.Items[0].ID, Cmd: []string{"true"}}, Config: &container.Config{Image: images.Items[0].ID, Cmd: []string{"true"}},
HostConfig: &container.HostConfig{Mounts: []mount.Mount{ HostConfig: &container.HostConfig{Mounts: []mount.Mount{
{Type: mount.TypeBind, Source: filepath.Join(daemonDir, filepath.Base(marker.Name())), Target: "/gitea-runner-probe", ReadOnly: true}, {Type: mount.TypeBind, Source: daemonDir, Target: "/gitea-runner-probe", ReadOnly: true}, // not the marker itself, podman creates a missing bind source where docker rejects it
}}, }},
}) })
if cerrdefs.IsInvalidArgument(err) { if cerrdefs.IsInvalidArgument(err) {
@@ -152,12 +156,13 @@ func daemonSeesDir(ctx context.Context, cli client.APIClient, dir, daemonDir str
if err != nil { if err != nil {
return false, err return false, err
} }
seen, err := containerSeesMarker(ctx, cli, dir, created.ID, "/gitea-runner-probe")
cleanupCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), dockerProxyProbeTimeout) cleanupCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), dockerProxyProbeTimeout)
defer cancel() defer cancel()
if _, err := cli.ContainerRemove(cleanupCtx, created.ID, client.ContainerRemoveOptions{Force: true, RemoveVolumes: true}); err != nil { if _, removeErr := cli.ContainerRemove(cleanupCtx, created.ID, client.ContainerRemoveOptions{Force: true, RemoveVolumes: true}); removeErr != nil {
return false, fmt.Errorf("removing the docker proxy probe container failed: %w", err) return false, fmt.Errorf("removing the docker proxy probe container failed: %w", removeErr)
} }
return true, nil return seen, err
} }
// StartDockerProxy serves a job's docker socket in dir, labelling what the job creates through it. // StartDockerProxy serves a job's docker socket in dir, labelling what the job creates through it.
+66 -21
View File
@@ -321,6 +321,9 @@ func TestDockerProxyWithDaemon(t *testing.T) {
seen, err := daemonSeesDir(ctx, direct, dir, dir) seen, err := daemonSeesDir(ctx, direct, dir, dir)
require.NoError(t, err) require.NoError(t, err)
t.Logf("daemon sees the runner's filesystem: %v", seen) t.Logf("daemon sees the runner's filesystem: %v", seen)
seen, err = daemonSeesDir(ctx, direct, dir, shortTempDir(t))
require.NoError(t, err)
assert.False(t, seen)
job := "proxy-test-" + strconv.FormatInt(time.Now().UnixNano(), 36) job := "proxy-test-" + strconv.FormatInt(time.Now().UnixNano(), 36)
proxy, err := StartDockerProxy(daemonSocketPath(t, direct), dir, job) proxy, err := StartDockerProxy(daemonSocketPath(t, direct), dir, job)
@@ -425,6 +428,12 @@ type probeClient struct {
mobyclient.APIClient mobyclient.APIClient
create func(mobyclient.ContainerCreateOptions) (mobyclient.ContainerCreateResult, error) create func(mobyclient.ContainerCreateOptions) (mobyclient.ContainerCreateResult, error)
remove func(context.Context, string, mobyclient.ContainerRemoveOptions) (mobyclient.ContainerRemoveResult, error) remove func(context.Context, string, mobyclient.ContainerRemoveOptions) (mobyclient.ContainerRemoveResult, error)
stat func(string) error
mounts []container.MountPoint
}
func (c *probeClient) ContainerInspect(context.Context, string, mobyclient.ContainerInspectOptions) (mobyclient.ContainerInspectResult, error) {
return mobyclient.ContainerInspectResult{Container: container.InspectResponse{Mounts: c.mounts}}, nil
} }
func (c *probeClient) ImageList(context.Context, mobyclient.ImageListOptions) (mobyclient.ImageListResult, error) { func (c *probeClient) ImageList(context.Context, mobyclient.ImageListOptions) (mobyclient.ImageListResult, error) {
@@ -439,43 +448,47 @@ func (c *probeClient) ContainerRemove(ctx context.Context, id string, opts mobyc
return c.remove(ctx, id, opts) return c.remove(ctx, id, opts)
} }
func (c *probeClient) ContainerStatPath(_ context.Context, _ string, opts mobyclient.ContainerStatPathOptions) (mobyclient.ContainerStatPathResult, error) {
return mobyclient.ContainerStatPathResult{}, c.stat(opts.Path)
}
func TestDaemonSeesDir(t *testing.T) { func TestDaemonSeesDir(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
markers := make(map[string]bool) markers := make(map[string]bool)
for _, testCase := range []struct { for _, testCase := range []struct {
name string name string
private bool daemonDir string
removeErr error removeErr error
seen bool
removed bool
}{ }{
{name: "cleanup after cancellation"}, {name: "cleanup after cancellation", daemonDir: dir, seen: true, removed: true},
{name: "private filesystem with stale marker", private: true}, {name: "daemon without the directory", daemonDir: filepath.Join(dir, "missing")},
{name: "cleanup error after cancellation", removeErr: errors.New("cleanup failed")}, {name: "daemon with a different directory at that path", daemonDir: t.TempDir(), removed: true},
{name: "cleanup error after cancellation", daemonDir: dir, removeErr: errors.New("cleanup failed"), removed: true},
} { } {
t.Run(testCase.name, func(t *testing.T) { t.Run(testCase.name, func(t *testing.T) {
ctx, cancel := context.WithCancel(t.Context()) ctx, cancel := context.WithCancel(t.Context())
defer cancel() defer cancel()
daemonDir := dir
if testCase.private {
daemonDir = t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(daemonDir, "probe"), []byte("stale"), 0o600))
}
removed := false removed := false
cli := &probeClient{ cli := &probeClient{
create: func(opts mobyclient.ContainerCreateOptions) (mobyclient.ContainerCreateResult, error) { create: func(opts mobyclient.ContainerCreateOptions) (mobyclient.ContainerCreateResult, error) {
require.Len(t, opts.HostConfig.Mounts, 1) assert.Equal(t, []mount.Mount{{Type: mount.TypeBind, Source: testCase.daemonDir, Target: "/gitea-runner-probe", ReadOnly: true}}, opts.HostConfig.Mounts)
marker := opts.HostConfig.Mounts[0].Source
assert.Equal(t, mount.Mount{Type: mount.TypeBind, Source: marker, Target: "/gitea-runner-probe", ReadOnly: true}, opts.HostConfig.Mounts[0])
assert.False(t, markers[marker])
markers[marker] = true
info, err := os.Stat(marker)
require.NoError(t, err)
assert.True(t, info.Mode().IsRegular())
cancel() cancel()
if _, err := os.Stat(filepath.Join(daemonDir, filepath.Base(marker))); errors.Is(err, os.ErrNotExist) { if _, err := os.Stat(testCase.daemonDir); err != nil {
return mobyclient.ContainerCreateResult{}, cerrdefs.ErrInvalidArgument return mobyclient.ContainerCreateResult{}, cerrdefs.ErrInvalidArgument
} }
return mobyclient.ContainerCreateResult{ID: "probe"}, nil return mobyclient.ContainerCreateResult{ID: "probe"}, nil
}, },
stat: func(path string) error {
marker := strings.TrimPrefix(path, "/gitea-runner-probe/")
assert.False(t, markers[marker])
markers[marker] = true
if _, err := os.Stat(filepath.Join(testCase.daemonDir, marker)); err != nil {
return cerrdefs.ErrNotFound
}
return nil
},
remove: func(ctx context.Context, id string, opts mobyclient.ContainerRemoveOptions) (mobyclient.ContainerRemoveResult, error) { remove: func(ctx context.Context, id string, opts mobyclient.ContainerRemoveOptions) (mobyclient.ContainerRemoveResult, error) {
removed = true removed = true
assert.Equal(t, "probe", id) assert.Equal(t, "probe", id)
@@ -486,13 +499,45 @@ func TestDaemonSeesDir(t *testing.T) {
return mobyclient.ContainerRemoveResult{}, testCase.removeErr return mobyclient.ContainerRemoveResult{}, testCase.removeErr
}, },
} }
seen, err := daemonSeesDir(ctx, cli, dir, dir) seen, err := daemonSeesDir(ctx, cli, dir, testCase.daemonDir)
require.ErrorIs(t, err, testCase.removeErr) require.ErrorIs(t, err, testCase.removeErr)
assert.Equal(t, !testCase.private && testCase.removeErr == nil, seen) assert.Equal(t, testCase.seen, seen)
assert.Equal(t, !testCase.private, removed) assert.Equal(t, testCase.removed, removed)
entries, err := os.ReadDir(dir) entries, err := os.ReadDir(dir)
require.NoError(t, err) require.NoError(t, err)
assert.Empty(t, entries) assert.Empty(t, entries)
}) })
} }
} }
func TestRunnerContainerWorkdir(t *testing.T) {
workdir := t.TempDir()
t.Chdir(workdir)
volume := container.MountPoint{Type: mount.TypeVolume, Source: "/daemon/_data", Destination: filepath.Dir(workdir)}
for _, testCase := range []struct {
name string
mount container.MountPoint
foreign bool
daemonDir string
}{
{name: "volume above the working directory", mount: volume, daemonDir: filepath.Join(volume.Source, filepath.Base(workdir))},
{name: "tmpfs", mount: container.MountPoint{Type: mount.TypeTmpfs, Destination: volume.Destination}},
{name: "hostname of another container", mount: volume, foreign: true},
} {
cli := &probeClient{
mounts: []container.MountPoint{testCase.mount},
stat: func(path string) error {
if _, err := os.Stat(path); err != nil || testCase.foreign {
return cerrdefs.ErrNotFound
}
return nil
},
}
dir, daemonDir := runnerContainerWorkdir(t.Context(), cli)
assert.Equal(t, testCase.daemonDir, daemonDir, testCase.name)
assert.Equal(t, testCase.daemonDir != "", dir == workdir, testCase.name)
}
entries, err := os.ReadDir(workdir)
require.NoError(t, err)
assert.Empty(t, entries)
}
+2 -7
View File
@@ -18,7 +18,6 @@ import (
maps0 "maps" maps0 "maps"
"net" "net"
"os" "os"
"path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
@@ -995,12 +994,8 @@ func (rc *RunContext) captureJobContainerInfo() common.Executor {
if rc.dockerProxy != nil { if rc.dockerProxy != nil {
rc.dockerProxy.SetMounts(info.Mounts) rc.dockerProxy.SetMounts(info.Mounts)
} }
workspace := rc.githubWorkspace() if source := info.DaemonPath(rc.githubWorkspace()); source != "" {
for dir := workspace; dir != "/" && dir != "."; dir = path.Dir(dir) { rc.Env["GITEA_DOCKER_WORKSPACE"] = source
if source := info.Mounts[dir]; source != "" {
rc.Env["GITEA_DOCKER_WORKSPACE"] = path.Join(source, strings.TrimPrefix(workspace, dir))
break
}
} }
return nil return nil
} }
+25 -21
View File
@@ -8,9 +8,10 @@
# It is deliberately generic: point it at any package/test to exercise the dind daemon. # It is deliberately generic: point it at any package/test to exercise the dind daemon.
# #
# Usage: scripts/test-dind.sh [target] [-- go-test-args...] # Usage: scripts/test-dind.sh [target] [-- go-test-args...]
# target: dind (default) or dind-rootless # target: dind (default), dind-rootless, or podman to run PODMAN_TEST_IMAGE's API service instead
# go-test-args: passed verbatim to `go test`. Defaults cover image env extraction, # go-test-args: passed verbatim to `go test`. Defaults cover image env extraction,
# symlink copying and a mounted Docker job using cached images. # symlink copying and a mounted Docker job using cached images, or the
# Docker proxy probe for podman.
# #
# Env: # Env:
# DIND_TEST_PORT host port for the daemon (default 32375) # DIND_TEST_PORT host port for the daemon (default 32375)
@@ -20,18 +21,13 @@ set -euo pipefail
target="dind" target="dind"
case "${1:-}" in case "${1:-}" in
dind|dind-rootless) target="$1"; shift ;; dind|dind-rootless|podman) target="$1"; shift ;;
esac esac
[ "${1:-}" = "--" ] && shift [ "${1:-}" = "--" ] && shift
default_tests=false default_tests=false
if [ $# -eq 0 ]; then
default_tests=true
set -- -count=1 -race -run '^TestDocker$' ./act/container/
fi
port="${DIND_TEST_PORT:-32375}" port="${DIND_TEST_PORT:-32375}"
name="gitea-runner-dind-test-$$" name="gitea-runner-dind-test-$$"
image="${DIND_TEST_IMAGE:-gitea-runner-${target}:dind-test}"
# The host daemon endpoint, captured before DOCKER_HOST is pointed at the fresh dind daemon. # The host daemon endpoint, captured before DOCKER_HOST is pointed at the fresh dind daemon.
host_docker="${DOCKER_HOST:-$(docker context inspect --format '{{.Endpoints.docker.Host}}')}" host_docker="${DOCKER_HOST:-$(docker context inspect --format '{{.Endpoints.docker.Host}}')}"
test_dir="" test_dir=""
@@ -44,14 +40,25 @@ cleanup() {
} }
trap cleanup EXIT trap cleanup EXIT
if [ -z "${DIND_TEST_IMAGE:-}" ]; then if [ "$target" = podman ]; then
echo "==> Building ${target} image" image="${PODMAN_TEST_IMAGE:?}"
docker build --target "$target" -t "$image" . daemon_args=("$image" podman system service --time=0 tcp://0.0.0.0:2375)
[ $# -gt 0 ] || set -- -count=1 -race -run '^TestDockerProxyWithDaemon$' ./act/container/
else
image="${DIND_TEST_IMAGE:-gitea-runner-${target}:dind-test}"
if [ -z "${DIND_TEST_IMAGE:-}" ]; then
echo "==> Building ${target} image"
docker build --target "$target" -t "$image" .
fi
# Override the image entrypoint (s6) and run only dockerd, exposed over insecure TCP.
# We are testing the daemon the image ships, not the runner supervision tree.
daemon_args=(-e DOCKER_TLS_CERTDIR= --entrypoint dockerd-entrypoint.sh "$image" --host=tcp://0.0.0.0:2375)
if [ $# -eq 0 ]; then
default_tests=true
set -- -count=1 -race -run '^TestDocker$' ./act/container/
fi
fi fi
# Override the image entrypoint (s6) and run only dockerd, exposed over insecure TCP.
# We are testing the daemon the image ships, not the runner supervision tree.
#
# How the test process reaches the daemon depends on where it runs: # How the test process reaches the daemon depends on where it runs:
# - plain host: publish 2375 on loopback and connect to 127.0.0.1. # - plain host: publish 2375 on loopback and connect to 127.0.0.1.
# - inside a container (CI), the daemon is a sibling container, so its published port is on # - inside a container (CI), the daemon is a sibling container, so its published port is on
@@ -66,8 +73,8 @@ if [ -n "$self_container" ]; then
fi fi
# The two cases differ only in how the daemon is exposed and addressed; everything else # The two cases differ only in how the daemon is exposed and addressed; everything else
# (privileged, name, TLS-off entrypoint, image, --host) is shared, so collect just the # (privileged, name, daemon_args) is shared, so collect just the differing run args and the
# differing run args and the resulting DOCKER_HOST here. # resulting DOCKER_HOST here.
if [ -n "$self_network" ]; then if [ -n "$self_network" ]; then
echo "==> Starting ${target} daemon on network ${self_network} (reached as ${name}:2375)" echo "==> Starting ${target} daemon on network ${self_network} (reached as ${name}:2375)"
run_args=(--network "$self_network") run_args=(--network "$self_network")
@@ -79,10 +86,7 @@ else
fi fi
# Create the dind container on the host daemon first, then repoint DOCKER_HOST at it: exporting # Create the dind container on the host daemon first, then repoint DOCKER_HOST at it: exporting
# DOCKER_HOST before `docker run` would make this `docker run` target the not-yet-existent dind. # DOCKER_HOST before `docker run` would make this `docker run` target the not-yet-existent dind.
docker -H "$host_docker" run -d --privileged --name "$name" "${run_args[@]}" \ docker -H "$host_docker" run -d --privileged --name "$name" "${run_args[@]}" "${daemon_args[@]}" >/dev/null
-e DOCKER_TLS_CERTDIR= \
--entrypoint dockerd-entrypoint.sh \
"$image" --host=tcp://0.0.0.0:2375 >/dev/null
export DOCKER_HOST="$daemon_host" export DOCKER_HOST="$daemon_host"
echo "==> Waiting for daemon" echo "==> Waiting for daemon"
@@ -109,7 +113,7 @@ for img in $preload; do
fi fi
done done
echo "==> Running tests against dind daemon" echo "==> Running tests against ${target} daemon"
go test "$@" go test "$@"
if [ "$default_tests" = true ]; then if [ "$default_tests" = true ]; then