integration/prune: Run in a separate daemon
- depends on: https://github.com/moby/moby/pull/46894
- addresses https://github.com/moby/moby/issues/46760

Isolate the prune effects by running the test in a separate daemon.
This minimizes the impact of/on other integration tests.

**- How to verify it**


**- Description for the changelog**
TestPruneDontDeleteUsedDangling in CI


**- A picture of a cute animal (not mandatory but encouraged)**


integration/image/prune_test.go
package image

import (
	"testing"

	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/integration/internal/container"
	"github.com/docker/docker/internal/testutils/specialimage"
	"gotest.tools/v3/assert"
	is "gotest.tools/v3/assert/cmp"
	"gotest.tools/v3/skip"
)

// Regression test for: https://github.com/moby/moby/issues/45732
func TestPruneDontDeleteUsedDangling(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME: hack/make/.build-empty-images doesn't run on Windows")

	ctx := setupTest(t)
	client := testEnv.APIClient()

	danglingID := specialimage.Load(ctx, t, client, specialimage.Dangling)

	container.Create(ctx, t, client,
		container.WithImage(danglingID),
		container.WithCmd("sleep", "60"))

	pruned, err := client.ImagesPrune(ctx, filters.NewArgs(filters.Arg("dangling", "true")))

	assert.NilError(t, err)
	assert.Check(t, is.Len(pruned.ImagesDeleted, 0))
}

package image

import (
	"strings"
	"testing"

	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/integration/internal/container"
	"github.com/docker/docker/internal/testutils/specialimage"
	"github.com/docker/docker/testutil/daemon"
	"gotest.tools/v3/assert"
	"gotest.tools/v3/skip"
)

// Regression test for: https://github.com/moby/moby/issues/45732
func TestPruneDontDeleteUsedDangling(t *testing.T) {
	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "cannot start multiple daemons on windows")
	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")

	ctx := setupTest(t)

	d := daemon.New(t)
	d.Start(t)
	defer d.Stop(t)

	client := d.NewClientT(t)
	defer client.Close()

	danglingID := specialimage.Load(ctx, t, client, specialimage.Dangling)

	_, _, err := client.ImageInspectWithRaw(ctx, danglingID)
	assert.NilError(t, err, "Test dangling image doesn't exist")

	container.Create(ctx, t, client,
		container.WithImage(danglingID),
		container.WithCmd("sleep", "60"))

	pruned, err := client.ImagesPrune(ctx, filters.NewArgs(filters.Arg("dangling", "true")))
	assert.NilError(t, err)

	for _, deleted := range pruned.ImagesDeleted {
		if strings.Contains(deleted.Deleted, danglingID) || strings.Contains(deleted.Untagged, danglingID) {
			t.Errorf("used dangling image %s shouldn't be deleted", danglingID)
		}
	}

	_, _, err = client.ImageInspectWithRaw(ctx, danglingID)
	assert.NilError(t, err, "Test dangling image should still exist")
}

This one could probably be `t.Errorf` (as we're looping over a range?) or do we want to fail fast always?