Browse code

volume/service: use local driver as default for anonymous volumes

Anonymous volumes get a unique, 64-character name, and intended to be a new
volume (not an existing one). While it's theoretically possible for this name
to exist in other volume drivers, this would be very unlikely, so we should
not need to check other drivers to have this volume.

This patch uses the default ("local") volume-driver for anonymous volumes,
unless the user explicitly asked for a specific driver to use. Setting the
driver skips looking up existing volumes in other drivers.

Before this patch:

DEBU[2024-10-26T15:51:12.681547126Z] container mounted via snapshotter: /var/lib/docker/rootfs/overlayfs/7e947822249514b6239657a0c54d091d90e0fed4b09da472f3f6258f2b4920bc container=7e947822249514b6239657a0c54d091d90e0fed4b09da472f3f6258f2b4920bc
DEBU[2024-10-26T15:51:12.681616084Z] Creating anonymous volume volume-name=fd46d688247c3e7d39d9bae4532d6b2dc69e82e354c4a3bf305c50bbfb9ebc6c
DEBU[2024-10-26T15:51:12.681638959Z] Probing all drivers for volume volume-name=fd46d688247c3e7d39d9bae4532d6b2dc69e82e354c4a3bf305c50bbfb9ebc6c
DEBU[2024-10-26T15:51:12.681688917Z] Registering new volume reference driver=local volume-name=fd46d688247c3e7d39d9bae4532d6b2dc69e82e354c4a3bf305c50bbfb9ebc6c

With this patch:

DEBU[2024-10-27T17:28:28.574956716Z] container mounted via snapshotter: /var/lib/docker/rootfs/overlayfs/7085cb3991b61cbb79edffcb6980ad926f99f6b6b3be617cc3e3b92673cc2eb8 container=7085cb3991b61cbb79edffcb6980ad926f99f6b6b3be617cc3e3b92673cc2eb8
DEBU[2024-10-27T17:28:28.575002549Z] Creating anonymous volume driver=local volume-name=db11c053566362499103213542402af2770a6622fe7a90b9a938a5bed84ca937
DEBU[2024-10-27T17:28:28.575016299Z] Registering new volume reference driver=local volume-name=db11c053566362499103213542402af2770a6622fe7a90b9a938a5bed84ca937

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2024/10/28 02:25:10
Showing 2 changed files
... ...
@@ -14,9 +14,11 @@ import (
14 14
 	"github.com/docker/docker/api/types/network"
15 15
 	"github.com/docker/docker/api/types/versions"
16 16
 	"github.com/docker/docker/client"
17
+	"github.com/docker/docker/errdefs"
17 18
 	"github.com/docker/docker/integration/internal/container"
18 19
 	"github.com/docker/docker/pkg/parsers/kernel"
19 20
 	"github.com/docker/docker/testutil"
21
+	"github.com/docker/docker/volume"
20 22
 	"github.com/moby/sys/mount"
21 23
 	"github.com/moby/sys/mountinfo"
22 24
 	"gotest.tools/v3/assert"
... ...
@@ -26,6 +28,10 @@ import (
26 26
 	"gotest.tools/v3/skip"
27 27
 )
28 28
 
29
+// testNonExistingPlugin is a special plugin-name, which overrides defaultTimeOut in tests.
30
+// this is a copy of https://github.com/moby/moby/blob/9e00a63d65434cdedc444e79a2b33a7c202b10d8/pkg/plugins/client.go#L253-L254
31
+const testNonExistingPlugin = "this-plugin-does-not-exist"
32
+
29 33
 func TestContainerNetworkMountsNoChown(t *testing.T) {
30 34
 	// chown only applies to Linux bind mounted volumes; must be same host to verify
31 35
 	skip.If(t, testEnv.IsRemoteDaemon)
... ...
@@ -402,26 +408,49 @@ func TestContainerVolumeAnonymous(t *testing.T) {
402 402
 	skip.If(t, testEnv.IsRemoteDaemon)
403 403
 
404 404
 	ctx := setupTest(t)
405
-
406
-	mntOpts := mounttypes.Mount{Type: mounttypes.TypeVolume, Target: "/foo"}
407
-
408 405
 	apiClient := testEnv.APIClient()
409
-	cID := container.Create(ctx, t, apiClient, container.WithMount(mntOpts))
410 406
 
411
-	inspect := container.Inspect(ctx, t, apiClient, cID)
412
-	assert.Assert(t, is.Len(inspect.HostConfig.Mounts, 1))
413
-	assert.Check(t, is.Equal(inspect.HostConfig.Mounts[0], mntOpts))
407
+	t.Run("no driver specified", func(t *testing.T) {
408
+		mntOpts := mounttypes.Mount{Type: mounttypes.TypeVolume, Target: "/foo"}
409
+		cID := container.Create(ctx, t, apiClient, container.WithMount(mntOpts))
414 410
 
415
-	assert.Assert(t, is.Len(inspect.Mounts, 1))
416
-	volName := inspect.Mounts[0].Name
417
-	assert.Check(t, is.Len(volName, 64), "volume name should be 64 bytes (from stringid.GenerateRandomID())")
411
+		inspect := container.Inspect(ctx, t, apiClient, cID)
412
+		assert.Assert(t, is.Len(inspect.HostConfig.Mounts, 1))
413
+		assert.Check(t, is.Equal(inspect.HostConfig.Mounts[0], mntOpts))
418 414
 
419
-	volInspect, err := apiClient.VolumeInspect(ctx, volName)
420
-	assert.NilError(t, err)
415
+		assert.Assert(t, is.Len(inspect.Mounts, 1))
416
+		vol := inspect.Mounts[0]
417
+		assert.Check(t, is.Len(vol.Name, 64), "volume name should be 64 bytes (from stringid.GenerateRandomID())")
418
+		assert.Check(t, is.Equal(vol.Driver, volume.DefaultDriverName))
421 419
 
422
-	// see [daemon.AnonymousLabel]; we don't want to import the daemon package here.
423
-	const expectedAnonymousLabel = "com.docker.volume.anonymous"
424
-	assert.Check(t, is.Contains(volInspect.Labels, expectedAnonymousLabel))
420
+		volInspect, err := apiClient.VolumeInspect(ctx, vol.Name)
421
+		assert.NilError(t, err)
422
+
423
+		// see [daemon.AnonymousLabel]; we don't want to import the daemon package here.
424
+		const expectedAnonymousLabel = "com.docker.volume.anonymous"
425
+		assert.Check(t, is.Contains(volInspect.Labels, expectedAnonymousLabel))
426
+		assert.Check(t, is.Equal(volInspect.Driver, volume.DefaultDriverName))
427
+	})
428
+
429
+	// Verify that specifying a custom driver is still taken into account.
430
+	t.Run("custom driver", func(t *testing.T) {
431
+		config := container.NewTestConfig(container.WithMount(mounttypes.Mount{
432
+			Type:   mounttypes.TypeVolume,
433
+			Target: "/foo",
434
+			VolumeOptions: &mounttypes.VolumeOptions{
435
+				DriverConfig: &mounttypes.Driver{
436
+					Name: testNonExistingPlugin,
437
+				},
438
+			},
439
+		}))
440
+		_, err := apiClient.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name)
441
+		// We use [testNonExistingPlugin] for this, which produces an error
442
+		// when used, which we use as indicator that the driver was passed
443
+		// through. We should have a cleaner way for this, but that would
444
+		// require a custom volume plugin to be installed.
445
+		assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
446
+		assert.Check(t, is.ErrorContains(err, fmt.Sprintf(`plugin %q not found`, testNonExistingPlugin)))
447
+	})
425 448
 }
426 449
 
427 450
 // Regression test for #38995 and #43390.
... ...
@@ -72,8 +72,11 @@ const AnonymousLabel = "com.docker.volume.anonymous"
72 72
 func (s *VolumesService) Create(ctx context.Context, name, driverName string, options ...opts.CreateOption) (*volumetypes.Volume, error) {
73 73
 	if name == "" {
74 74
 		name = stringid.GenerateRandomID()
75
+		if driverName == "" {
76
+			driverName = volume.DefaultDriverName
77
+		}
75 78
 		options = append(options, opts.WithCreateLabel(AnonymousLabel, ""))
76
-		log.G(ctx).WithField("volume-name", name).Debug("Creating anonymous volume")
79
+		log.G(ctx).WithFields(log.Fields{"volume-name": name, "driver": driverName}).Debug("Creating anonymous volume")
77 80
 	} else {
78 81
 		log.G(ctx).WithField("volume-name", name).Debug("Creating named volume")
79 82
 	}