github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/volume/local/local_linux_test.go (about) 1 //go:build linux 2 // +build linux 3 4 package local // import "github.com/docker/docker/volume/local" 5 6 import ( 7 "os" 8 "path/filepath" 9 "strconv" 10 "testing" 11 12 "github.com/docker/docker/errdefs" 13 "github.com/docker/docker/pkg/idtools" 14 "github.com/docker/docker/quota" 15 "gotest.tools/v3/assert" 16 is "gotest.tools/v3/assert/cmp" 17 ) 18 19 const quotaSize = 1024 * 1024 20 const quotaSizeLiteral = "1M" 21 22 func TestQuota(t *testing.T) { 23 if msg, ok := quota.CanTestQuota(); !ok { 24 t.Skip(msg) 25 } 26 27 // get sparse xfs test image 28 imageFileName, err := quota.PrepareQuotaTestImage(t) 29 if err != nil { 30 t.Fatal(err) 31 } 32 defer os.Remove(imageFileName) 33 34 t.Run("testVolWithQuota", quota.WrapMountTest(imageFileName, true, testVolWithQuota)) 35 t.Run("testVolQuotaUnsupported", quota.WrapMountTest(imageFileName, false, testVolQuotaUnsupported)) 36 } 37 38 func testVolWithQuota(t *testing.T, mountPoint, backingFsDev, testDir string) { 39 r, err := New(testDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()}) 40 if err != nil { 41 t.Fatal(err) 42 } 43 assert.Assert(t, r.quotaCtl != nil) 44 45 vol, err := r.Create("testing", map[string]string{"size": quotaSizeLiteral}) 46 if err != nil { 47 t.Fatal(err) 48 } 49 50 dir, err := vol.Mount("1234") 51 if err != nil { 52 t.Fatal(err) 53 } 54 defer func() { 55 if err := vol.Unmount("1234"); err != nil { 56 t.Fatal(err) 57 } 58 }() 59 60 testfile := filepath.Join(dir, "testfile") 61 62 // test writing file smaller than quota 63 assert.NilError(t, os.WriteFile(testfile, make([]byte, quotaSize/2), 0644)) 64 assert.NilError(t, os.Remove(testfile)) 65 66 // test writing fiel larger than quota 67 err = os.WriteFile(testfile, make([]byte, quotaSize+1), 0644) 68 assert.ErrorContains(t, err, "") 69 if _, err := os.Stat(testfile); err == nil { 70 assert.NilError(t, os.Remove(testfile)) 71 } 72 } 73 74 func testVolQuotaUnsupported(t *testing.T, mountPoint, backingFsDev, testDir string) { 75 r, err := New(testDir, idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()}) 76 if err != nil { 77 t.Fatal(err) 78 } 79 assert.Assert(t, is.Nil(r.quotaCtl)) 80 81 _, err = r.Create("testing", map[string]string{"size": quotaSizeLiteral}) 82 assert.ErrorContains(t, err, "no quota support") 83 84 vol, err := r.Create("testing", nil) 85 if err != nil { 86 t.Fatal(err) 87 } 88 89 // this could happen if someone moves volumes from storage with 90 // quota support to some place without 91 lv, ok := vol.(*localVolume) 92 assert.Assert(t, ok) 93 lv.opts = &optsConfig{ 94 Quota: quota.Quota{Size: quotaSize}, 95 } 96 97 _, err = vol.Mount("1234") 98 assert.ErrorContains(t, err, "no quota support") 99 } 100 101 func TestVolCreateValidation(t *testing.T) { 102 r, err := New(t.TempDir(), idtools.Identity{UID: os.Geteuid(), GID: os.Getegid()}) 103 if err != nil { 104 t.Fatal(err) 105 } 106 107 mandatoryOpts = map[string][]string{ 108 "device": {"type"}, 109 "type": {"device"}, 110 "o": {"device", "type"}, 111 } 112 113 tests := []struct { 114 doc string 115 name string 116 opts map[string]string 117 expectedErr string 118 }{ 119 { 120 doc: "invalid: name too short", 121 name: "a", 122 opts: map[string]string{ 123 "type": "foo", 124 "device": "foo", 125 }, 126 expectedErr: `volume name is too short, names should be at least two alphanumeric characters`, 127 }, 128 { 129 doc: "invalid: name invalid characters", 130 name: "hello world", 131 opts: map[string]string{ 132 "type": "foo", 133 "device": "foo", 134 }, 135 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`, 136 }, 137 { 138 doc: "invalid: unknown option", 139 opts: map[string]string{"hello": "world"}, 140 expectedErr: `invalid option: "hello"`, 141 }, 142 { 143 doc: "invalid: invalid size", 144 opts: map[string]string{"size": "hello"}, 145 expectedErr: `invalid size: 'hello'`, 146 }, 147 { 148 doc: "invalid: size, but no quotactl", 149 opts: map[string]string{"size": "1234"}, 150 expectedErr: `quota size requested but no quota support`, 151 }, 152 { 153 doc: "invalid: device without type", 154 opts: map[string]string{ 155 "device": "foo", 156 }, 157 expectedErr: `missing required option: "type"`, 158 }, 159 { 160 doc: "invalid: type without device", 161 opts: map[string]string{ 162 "type": "foo", 163 }, 164 expectedErr: `missing required option: "device"`, 165 }, 166 { 167 doc: "invalid: o without device", 168 opts: map[string]string{ 169 "o": "foo", 170 "type": "foo", 171 }, 172 expectedErr: `missing required option: "device"`, 173 }, 174 { 175 doc: "invalid: o without type", 176 opts: map[string]string{ 177 "o": "foo", 178 "device": "foo", 179 }, 180 expectedErr: `missing required option: "type"`, 181 }, 182 { 183 doc: "valid: short name, no options", 184 name: "ab", 185 }, 186 { 187 doc: "valid: device and type", 188 opts: map[string]string{ 189 "type": "foo", 190 "device": "foo", 191 }, 192 }, 193 { 194 doc: "valid: device, type, and o", 195 opts: map[string]string{ 196 "type": "foo", 197 "device": "foo", 198 "o": "foo", 199 }, 200 }, 201 } 202 203 for i, tc := range tests { 204 tc := tc 205 t.Run(tc.doc, func(t *testing.T) { 206 if tc.name == "" { 207 tc.name = "vol-" + strconv.Itoa(i) 208 } 209 v, err := r.Create(tc.name, tc.opts) 210 if v != nil { 211 defer assert.Check(t, r.Remove(v)) 212 } 213 if tc.expectedErr == "" { 214 assert.NilError(t, err) 215 } else { 216 assert.Check(t, errdefs.IsInvalidParameter(err), "got: %T", err) 217 assert.ErrorContains(t, err, tc.expectedErr) 218 } 219 }) 220 } 221 }