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