diff --git a/volume/local/local_linux_test.go b/volume/local/local_linux_test.go index c66ecc9095..c947a929cd 100644 --- a/volume/local/local_linux_test.go +++ b/volume/local/local_linux_test.go @@ -6,8 +6,10 @@ package local // import "github.com/docker/docker/volume/local" import ( "os" "path/filepath" + "strconv" "testing" + "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/quota" "gotest.tools/v3/assert" @@ -95,3 +97,115 @@ func testVolQuotaUnsupported(t *testing.T, mountPoint, backingFsDev, testDir str _, err = vol.Mount("1234") assert.ErrorContains(t, err, "no quota support") } + +func TestVolCreateValidation(t *testing.T) { + r, err := New(t.TempDir(), idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()}) + if err != nil { + t.Fatal(err) + } + + mandatoryOpts = map[string][]string{ + "device": {"type"}, + "type": {"device"}, + "o": {"device", "type"}, + } + + tests := []struct { + doc string + name string + opts map[string]string + expectedErr string + }{ + { + doc: "invalid: name too short", + name: "a", + opts: map[string]string{ + "type": "foo", + "device": "foo", + }, + expectedErr: `volume name is too short, names should be at least two alphanumeric characters`, + }, + { + doc: "invalid: name invalid characters", + name: "hello world", + opts: map[string]string{ + "type": "foo", + "device": "foo", + }, + expectedErr: `"hello world" includes invalid characters for a local volume name, only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed. If you intended to pass a host directory, use absolute path`, + }, + { + doc: "invalid: size, but no quotactl", + opts: map[string]string{"size": "1234"}, + expectedErr: `quota size requested but no quota support`, + }, + { + doc: "invalid: device without type", + opts: map[string]string{ + "device": "foo", + }, + expectedErr: `missing required option: "type"`, + }, + { + doc: "invalid: type without device", + opts: map[string]string{ + "type": "foo", + }, + expectedErr: `missing required option: "device"`, + }, + { + doc: "invalid: o without device", + opts: map[string]string{ + "o": "foo", + "type": "foo", + }, + expectedErr: `missing required option: "device"`, + }, + { + doc: "invalid: o without type", + opts: map[string]string{ + "o": "foo", + "device": "foo", + }, + expectedErr: `missing required option: "type"`, + }, + { + doc: "valid: short name, no options", + name: "ab", + }, + { + doc: "valid: device and type", + opts: map[string]string{ + "type": "foo", + "device": "foo", + }, + }, + { + doc: "valid: device, type, and o", + opts: map[string]string{ + "type": "foo", + "device": "foo", + "o": "foo", + }, + }, + } + + for i, tc := range tests { + tc := tc + t.Run(tc.doc, func(t *testing.T) { + if tc.name == "" { + tc.name = "vol-" + strconv.Itoa(i) + } + v, err := r.Create(tc.name, tc.opts) + if v != nil { + defer assert.Check(t, r.Remove(v)) + } + if tc.expectedErr == "" { + assert.NilError(t, err) + } else { + assert.Check(t, errdefs.IsInvalidParameter(err), "got: %T", err) + assert.ErrorContains(t, err, tc.expectedErr) + } + }) + } +}