github.com/intel/goresctrl@v0.5.0/pkg/cgroups/cgroupblkio_test.go (about) 1 // Copyright 2020-2021 Intel Corporation. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cgroups 16 17 import ( 18 "bytes" 19 "io" 20 "syscall" 21 "testing" 22 23 "github.com/intel/goresctrl/pkg/testutils" 24 ) 25 26 var mountDir = "/sys/fs/cgroup" 27 28 func TestUpdateAppend(t *testing.T) { 29 tcases := []struct { 30 name string 31 inputMajMinVals [][]int64 32 inputItem []int64 33 expectedMajMinVal [][]int64 34 expectedErrorCount int 35 expectedErrorSubstrings []string 36 }{ 37 { 38 name: "update empty list", 39 inputItem: []int64{1, 2, 3}, 40 expectedMajMinVal: [][]int64{{1, 2, 3}}, 41 }, 42 { 43 name: "update appends non-existing element", 44 inputMajMinVals: [][]int64{{10, 20, 30}, {40, 50, 60}}, 45 inputItem: []int64{1, 2, 3}, 46 expectedMajMinVal: [][]int64{{10, 20, 30}, {40, 50, 60}, {1, 2, 3}}, 47 }, 48 { 49 name: "update the first existing element", 50 inputMajMinVals: [][]int64{{10, 20, 30}, {40, 50, 60}, {40, 50, 60}}, 51 inputItem: []int64{40, 50, 66}, 52 expectedMajMinVal: [][]int64{{10, 20, 30}, {40, 50, 66}, {40, 50, 60}}, 53 }, 54 } 55 for _, tc := range tcases { 56 t.Run(tc.name, func(t *testing.T) { 57 devWeights := DeviceWeights{} 58 devRates := DeviceRates{} 59 expDevWeights := DeviceWeights{} 60 expDevRates := DeviceRates{} 61 for _, item := range tc.inputMajMinVals { 62 devWeights.Append(item[0], item[1], item[2]) 63 devRates.Append(item[0], item[1], item[2]) 64 } 65 devWeights.Update(tc.inputItem[0], tc.inputItem[1], tc.inputItem[2]) 66 devRates.Update(tc.inputItem[0], tc.inputItem[1], tc.inputItem[2]) 67 for _, item := range tc.expectedMajMinVal { 68 expDevWeights = append(expDevWeights, DeviceWeight{item[0], item[1], item[2]}) 69 expDevRates = append(expDevRates, DeviceRate{item[0], item[1], item[2]}) 70 } 71 testutils.VerifyDeepEqual(t, "device weights", expDevWeights, devWeights) 72 testutils.VerifyDeepEqual(t, "device rates", expDevRates, devRates) 73 }) 74 } 75 } 76 77 var fsBlkioUtFiles map[string]mockFile = map[string]mockFile{ 78 mountDir + "/blkio/mockpods/clean/blkio.bfq.weight": {data: []byte("100\n")}, 79 mountDir + "/blkio/mockpods/clean/blkio.bfq.weight_device": {}, 80 mountDir + "/blkio/mockpods/clean/blkio.throttle.read_bps_device": {}, 81 mountDir + "/blkio/mockpods/clean/blkio.throttle.write_bps_device": {}, 82 mountDir + "/blkio/mockpods/clean/blkio.throttle.read_iops_device": {}, 83 mountDir + "/blkio/mockpods/clean/blkio.throttle.write_iops_device": {}, 84 85 mountDir + "/blkio/mockpods/no-blkio-bfq-weight/blkio.weight": {data: []byte("100\n")}, 86 87 mountDir + "/blkio/mockpods/reset/blkio.bfq.weight": {data: []byte("200\n")}, 88 mountDir + "/blkio/mockpods/reset/blkio.bfq.weight_device": {data: []byte("default 200\n1:2 3\n4:5 6\n")}, 89 mountDir + "/blkio/mockpods/reset/blkio.throttle.read_bps_device": {data: []byte("11:12 13\n14:15 16\n")}, 90 mountDir + "/blkio/mockpods/reset/blkio.throttle.write_bps_device": {data: []byte("21:22 23\n")}, 91 mountDir + "/blkio/mockpods/reset/blkio.throttle.read_iops_device": {data: []byte("31:32 33\n")}, 92 mountDir + "/blkio/mockpods/reset/blkio.throttle.write_iops_device": {data: []byte("41:42 43\n")}, 93 94 mountDir + "/blkio/mockpods/merge/blkio.bfq.weight": {data: []byte("200\n")}, 95 mountDir + "/blkio/mockpods/merge/blkio.bfq.weight_device": {data: []byte("default 200\n1:2 3\n4:5 6\n7:8 9")}, 96 mountDir + "/blkio/mockpods/merge/blkio.throttle.read_bps_device": {data: []byte("11:12 13\n14:15 16\n")}, 97 mountDir + "/blkio/mockpods/merge/blkio.throttle.write_bps_device": {data: []byte("21:22 23\n24:25 26\n")}, 98 mountDir + "/blkio/mockpods/merge/blkio.throttle.read_iops_device": {data: []byte("31:32 33\n331:332 333\n")}, 99 mountDir + "/blkio/mockpods/merge/blkio.throttle.write_iops_device": {data: []byte("41:42 43\n441:442 443\n")}, 100 101 // parseok: 102 // test weight without linefeed 103 // test weight_device file with real "default" line 104 // test parsing two lines and skipping empty lines 105 // test single line file 106 // test single line, missing LF at the end 107 // test small and large values 108 mountDir + "/blkio/parseok/blkio.bfq.weight": {data: []byte("1")}, 109 mountDir + "/blkio/parseok/blkio.bfq.weight_device": {data: []byte("default 10\n1:2 3\n")}, 110 mountDir + "/blkio/parseok/blkio.throttle.read_bps_device": {data: []byte("\n11:22 33\n\n111:222 333\n")}, 111 mountDir + "/blkio/parseok/blkio.throttle.write_bps_device": {data: []byte("1111:2222 3333\n")}, 112 mountDir + "/blkio/parseok/blkio.throttle.read_iops_device": {data: []byte("11111:22222 33333")}, 113 mountDir + "/blkio/parseok/blkio.throttle.write_iops_device": {data: []byte("0:0 0\n4294967296:4294967297 9223372036854775807\n")}, 114 115 // parse-err: 116 // weight: not a number 117 // weight_device: test bad line in the middle 118 // read_bps_device: test no spaces 119 // write_bps_device: test too many spaces 120 // read_iobps_device: test no colons 121 // write_iobps_device: test missing number 122 mountDir + "/blkio/parse-err/blkio.bfq.weight": {data: []byte("xyz")}, 123 mountDir + "/blkio/parse-err/blkio.bfq.weight_device": {data: []byte("default 10\n1:2 3\nbad\n4:5 6\n")}, 124 mountDir + "/blkio/parse-err/blkio.throttle.read_bps_device": {data: []byte("11:22:33")}, 125 mountDir + "/blkio/parse-err/blkio.throttle.write_bps_device": {data: []byte("1111 2222 3333 \n")}, 126 mountDir + "/blkio/parse-err/blkio.throttle.read_iops_device": {data: []byte("1111122222 33333")}, 127 mountDir + "/blkio/parse-err/blkio.throttle.write_iops_device": {data: []byte("0: 0\n")}, 128 129 mountDir + "/blkio/write-enodev/blkio.bfq.weight": {write: func([]byte) (int, error) { return 0, syscall.ENODEV }}, 130 mountDir + "/blkio/write-enodev/blkio.bfq.weight_device": {write: func([]byte) (int, error) { return 0, syscall.ENODEV }}, 131 mountDir + "/blkio/write-enodev/blkio.throttle.read_bps_device": {write: func([]byte) (int, error) { return 0, syscall.ENODEV }}, 132 mountDir + "/blkio/write-enodev/blkio.throttle.write_bps_device": {write: func([]byte) (int, error) { return 0, syscall.ENODEV }}, 133 mountDir + "/blkio/write-enodev/blkio.throttle.read_iops_device": {write: func([]byte) (int, error) { return 0, syscall.ENODEV }}, 134 mountDir + "/blkio/write-enodev/blkio.throttle.write_iops_device": {write: func([]byte) (int, error) { return 0, syscall.ENODEV }}, 135 } 136 137 // TestResetBlkioParameters: unit test for ResetBlkioParameters() 138 func TestResetBlkioParameters(t *testing.T) { 139 tcases := []struct { 140 name string 141 fsi fsiIface 142 cntnrDir string 143 blockIO BlockIOParameters 144 expectedFsWrites map[string][][]byte 145 expectedBlockIO *BlockIOParameters 146 expectedErrorCount int 147 expectedErrorSubstrings []string 148 }{ 149 { 150 name: "write to clean cgroups", 151 fsi: NewFsiMock(fsBlkioUtFiles), 152 cntnrDir: "mockpods/clean", 153 blockIO: BlockIOParameters{ 154 Weight: 222, 155 WeightDevice: DeviceWeights{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, 156 ThrottleReadBpsDevice: DeviceRates{{11, 12, 13}, {111, 112, 113}}, 157 ThrottleWriteBpsDevice: DeviceRates{{21, 22, 23}, {221, 222, 223}}, 158 ThrottleReadIOPSDevice: DeviceRates{{31, 32, 33}, {331, 332, 333}}, 159 ThrottleWriteIOPSDevice: DeviceRates{{41, 42, 43}, {441, 442, 443}}, 160 }, 161 expectedFsWrites: map[string][][]byte{ 162 mountDir + "/blkio/mockpods/clean/blkio.bfq.weight": {[]byte("222")}, 163 mountDir + "/blkio/mockpods/clean/blkio.bfq.weight_device": {[]byte("1:2 3"), []byte("4:5 6"), []byte("7:8 9")}, 164 mountDir + "/blkio/mockpods/clean/blkio.throttle.read_bps_device": {[]byte("11:12 13"), []byte("111:112 113")}, 165 mountDir + "/blkio/mockpods/clean/blkio.throttle.write_bps_device": {[]byte("21:22 23"), []byte("221:222 223")}, 166 mountDir + "/blkio/mockpods/clean/blkio.throttle.read_iops_device": {[]byte("31:32 33"), []byte("331:332 333")}, 167 mountDir + "/blkio/mockpods/clean/blkio.throttle.write_iops_device": {[]byte("41:42 43"), []byte("441:442 443")}, 168 }, 169 }, 170 { 171 name: "reset all existing", 172 fsi: NewFsiMock(fsBlkioUtFiles), 173 cntnrDir: "mockpods/reset", 174 blockIO: NewBlockIOParameters(), 175 expectedFsWrites: map[string][][]byte{ 176 mountDir + "/blkio/mockpods/reset/blkio.bfq.weight_device": {[]byte("1:2 0"), []byte("4:5 0")}, 177 mountDir + "/blkio/mockpods/reset/blkio.throttle.read_bps_device": {[]byte("11:12 0"), []byte("14:15 0")}, 178 mountDir + "/blkio/mockpods/reset/blkio.throttle.write_bps_device": {[]byte("21:22 0")}, 179 mountDir + "/blkio/mockpods/reset/blkio.throttle.read_iops_device": {[]byte("31:32 0")}, 180 mountDir + "/blkio/mockpods/reset/blkio.throttle.write_iops_device": {[]byte("41:42 0")}, 181 }, 182 }, 183 { 184 name: "merge", 185 fsi: NewFsiMock(fsBlkioUtFiles), 186 cntnrDir: "mockpods/merge", 187 blockIO: BlockIOParameters{ 188 Weight: 80, 189 WeightDevice: DeviceWeights{{1, 2, 1113}, {7, 8, 9}}, // drop middle, update first, keep last 190 ThrottleReadBpsDevice: DeviceRates{{11, 12, 13}}, // keep the first entry 191 ThrottleWriteBpsDevice: DeviceRates{{24, 25, 26}}, // keep the last entry 192 ThrottleReadIOPSDevice: DeviceRates{{31, 32, 33}, {331, 332, 333}}, // keep all 193 ThrottleWriteIOPSDevice: DeviceRates{{41, 42, 430}, {441, 442, 4430}}, // change all 194 }, 195 expectedFsWrites: map[string][][]byte{ 196 mountDir + "/blkio/mockpods/merge/blkio.bfq.weight": {[]byte("80")}, 197 mountDir + "/blkio/mockpods/merge/blkio.bfq.weight_device": {[]byte("1:2 1113"), []byte("7:8 9"), []byte("4:5 0")}, 198 mountDir + "/blkio/mockpods/merge/blkio.throttle.read_bps_device": {[]byte("11:12 13"), []byte("14:15 0")}, 199 mountDir + "/blkio/mockpods/merge/blkio.throttle.write_bps_device": {[]byte("24:25 26"), []byte("21:22 0")}, 200 mountDir + "/blkio/mockpods/merge/blkio.throttle.read_iops_device": {[]byte("31:32 33"), []byte("331:332 333")}, 201 mountDir + "/blkio/mockpods/merge/blkio.throttle.write_iops_device": {[]byte("41:42 430"), []byte("441:442 4430")}, 202 }, 203 }, 204 } 205 for _, tc := range tcases { 206 t.Run(tc.name, func(t *testing.T) { 207 fsi = tc.fsi 208 err := ResetBlkioParameters(tc.cntnrDir, tc.blockIO) 209 testutils.VerifyError(t, err, tc.expectedErrorCount, tc.expectedErrorSubstrings) 210 validateWriteHistory(t, tc.expectedFsWrites, fsi.(*fsMock).files) 211 }) 212 } 213 } 214 215 // validateWriteHistory compares expected writes to filesystem to all 216 // observed writes. 217 func validateWriteHistory(t *testing.T, expected map[string][][]byte, filesystem map[string]*mockFile) { 218 for expFilename, expWrites := range expected { 219 mf, ok := filesystem[expFilename] 220 if !ok { 221 t.Errorf("expected writes to %q, but file is missing", expFilename) 222 return 223 } 224 obsWrites := mf.writeHistory 225 if len(expWrites) != len(obsWrites) { 226 t.Errorf("unexpected number of writes to %q: expected %v, observed %v", expFilename, expWrites, obsWrites) 227 return 228 } 229 for i, expWrite := range expWrites { 230 if !bytes.Equal(expWrite, obsWrites[i]) { 231 t.Errorf("write at index %d differs: expected %v, observed %v", i, expWrites, obsWrites) 232 } 233 } 234 } 235 for obsFilename, mf := range filesystem { 236 if mf.writeHistory != nil { 237 if _, ok := expected[obsFilename]; !ok { 238 t.Errorf("writes to unexpected file %q, observed: %v", obsFilename, mf.writeHistory) 239 } 240 } 241 } 242 } 243 244 // TestGetBlkioParameters: unit test for GetBlkioParameters() 245 func TestGetBlkioParameters(t *testing.T) { 246 tcases := []struct { 247 name string 248 fsi fsiIface 249 fsFuncs map[string]mockFile 250 cntnrDir string 251 readsFail int 252 fsContent map[string]string 253 expectedBlockIO *BlockIOParameters 254 expectedErrorCount int 255 expectedErrorSubstrings []string 256 }{ 257 { 258 name: "all clean and empty", 259 fsi: NewFsiMock(fsBlkioUtFiles), 260 fsFuncs: map[string]mockFile{ 261 // reuse clean directory, but force weight file empty 262 mountDir + "/blkio/mockpods/clean/blkio.bfq.weight": { 263 read: func([]byte) (int, error) { return 0, io.EOF }, 264 }, 265 }, 266 cntnrDir: "mockpods/clean", 267 expectedBlockIO: &BlockIOParameters{Weight: -1}, 268 expectedErrorCount: 1, // weight is not expected to be empty 269 expectedErrorSubstrings: []string{"parsing weight"}, 270 }, 271 { 272 name: "everything defined", 273 fsi: NewFsiMock(fsBlkioUtFiles), 274 cntnrDir: "/parseok", 275 expectedBlockIO: &BlockIOParameters{ 276 Weight: 1, 277 WeightDevice: DeviceWeights{{1, 2, 3}}, 278 ThrottleReadBpsDevice: DeviceRates{{11, 22, 33}, {111, 222, 333}}, 279 ThrottleWriteBpsDevice: DeviceRates{{1111, 2222, 3333}}, 280 ThrottleReadIOPSDevice: DeviceRates{{11111, 22222, 33333}}, 281 ThrottleWriteIOPSDevice: DeviceRates{{0, 0, 0}, {4294967296, 4294967297, 9223372036854775807}}, 282 }, 283 }, 284 { 285 name: "test bad files", 286 fsi: NewFsiMock(fsBlkioUtFiles), 287 cntnrDir: "/parse-err", 288 expectedErrorCount: 6, 289 expectedErrorSubstrings: []string{"bad", "xyz", "11:22:33", "1111 2222 3333 ", "1111122222 33333", "0: 0"}, 290 expectedBlockIO: &BlockIOParameters{ 291 Weight: -1, 292 WeightDevice: DeviceWeights{{1, 2, 3}, {4, 5, 6}}, 293 }, 294 }, 295 { 296 name: "all files missing", 297 fsi: NewFsiMock(fsBlkioUtFiles), 298 cntnrDir: "/this/container/does/not/exist", 299 expectedBlockIO: &BlockIOParameters{Weight: -1}, 300 expectedErrorCount: 6, 301 expectedErrorSubstrings: []string{ 302 "file not found", 303 "blkio.bfq.weight", 304 "blkio.bfq.weight_device", 305 "blkio.throttle.read_bps_device", 306 "blkio.throttle.write_bps_device", 307 "blkio.throttle.read_iops_device", 308 "blkio.throttle.write_iops_device", 309 }, 310 }, 311 } 312 313 for _, tc := range tcases { 314 t.Run(tc.name, func(t *testing.T) { 315 fsi = tc.fsi 316 overrideFsFuncs(fsi.(*fsMock), tc.fsFuncs) 317 blockIO, err := GetBlkioParameters(tc.cntnrDir) 318 testutils.VerifyError(t, err, tc.expectedErrorCount, tc.expectedErrorSubstrings) 319 if tc.expectedBlockIO != nil { 320 testutils.VerifyDeepEqual(t, "blockio parameters", *tc.expectedBlockIO, blockIO) 321 } 322 }) 323 } 324 } 325 326 // overrideFsFuncs (re)sets user overrides of file-specific functions 327 // in the mock filesystem. 328 func overrideFsFuncs(fsm *fsMock, fsFuncs map[string]mockFile) { 329 for filename, mf := range fsFuncs { 330 if mf.open != nil { 331 fsm.files[filename].open = mf.open 332 } 333 if mf.read != nil { 334 fsm.files[filename].read = mf.read 335 } 336 if mf.write != nil { 337 fsm.files[filename].write = mf.write 338 } 339 } 340 } 341 342 // TestSetBlkioParameters: unit test for SetBlkioParameters() 343 func TestSetBlkioParameters(t *testing.T) { 344 tcases := []struct { 345 name string 346 fsi fsiIface 347 fsFuncs map[string]mockFile 348 cntnrDir string 349 blockIO BlockIOParameters 350 writesFail int 351 expectedFsWrites map[string][][]byte 352 expectedErrorCount int 353 expectedErrorSubstrings []string 354 }{ 355 { 356 name: "write full OCI struct", 357 fsi: NewFsiMock(fsBlkioUtFiles), 358 cntnrDir: "/mockpods/clean", 359 blockIO: BlockIOParameters{ 360 Weight: 10, 361 WeightDevice: DeviceWeights{{Major: 1, Minor: 2, Weight: 3}}, 362 ThrottleReadBpsDevice: DeviceRates{{Major: 11, Minor: 12, Rate: 13}}, 363 ThrottleWriteBpsDevice: DeviceRates{{Major: 21, Minor: 22, Rate: 23}}, 364 ThrottleReadIOPSDevice: DeviceRates{{Major: 31, Minor: 32, Rate: 33}}, 365 ThrottleWriteIOPSDevice: DeviceRates{{Major: 41, Minor: 42, Rate: 43}}, 366 }, 367 expectedFsWrites: map[string][][]byte{ 368 mountDir + "/blkio/mockpods/clean/blkio.bfq.weight": {[]byte("10")}, 369 mountDir + "/blkio/mockpods/clean/blkio.bfq.weight_device": {[]byte("1:2 3")}, 370 mountDir + "/blkio/mockpods/clean/blkio.throttle.read_bps_device": {[]byte("11:12 13")}, 371 mountDir + "/blkio/mockpods/clean/blkio.throttle.write_bps_device": {[]byte("21:22 23")}, 372 mountDir + "/blkio/mockpods/clean/blkio.throttle.read_iops_device": {[]byte("31:32 33")}, 373 mountDir + "/blkio/mockpods/clean/blkio.throttle.write_iops_device": {[]byte("41:42 43")}, 374 }, 375 }, 376 { 377 name: "write empty struct", 378 fsi: NewFsiMock(fsBlkioUtFiles), 379 cntnrDir: "/mockpods/clean", 380 blockIO: BlockIOParameters{}, 381 expectedFsWrites: map[string][][]byte{ 382 mountDir + "/blkio/mockpods/clean/blkio.bfq.weight": {[]byte("0")}, 383 }, 384 }, 385 { 386 name: "multidevice weight and throttling, no weight write on -1", 387 fsi: NewFsiMock(fsBlkioUtFiles), 388 cntnrDir: "/mockpods/clean", 389 blockIO: BlockIOParameters{ 390 Weight: -1, 391 WeightDevice: DeviceWeights{{1, 2, 3}, {4, 5, 6}}, 392 ThrottleReadBpsDevice: DeviceRates{{11, 12, 13}, {111, 112, 113}}, 393 ThrottleWriteBpsDevice: DeviceRates{{21, 22, 23}, {221, 222, 223}}, 394 ThrottleReadIOPSDevice: DeviceRates{{31, 32, 33}, {331, 332, 333}}, 395 ThrottleWriteIOPSDevice: DeviceRates{{41, 42, 43}, {441, 442, 443}}, 396 }, 397 expectedFsWrites: map[string][][]byte{ 398 mountDir + "/blkio/mockpods/clean/blkio.bfq.weight_device": {[]byte("1:2 3"), []byte("4:5 6")}, 399 mountDir + "/blkio/mockpods/clean/blkio.throttle.read_bps_device": {[]byte("11:12 13"), []byte("111:112 113")}, 400 mountDir + "/blkio/mockpods/clean/blkio.throttle.write_bps_device": {[]byte("21:22 23"), []byte("221:222 223")}, 401 mountDir + "/blkio/mockpods/clean/blkio.throttle.read_iops_device": {[]byte("31:32 33"), []byte("331:332 333")}, 402 mountDir + "/blkio/mockpods/clean/blkio.throttle.write_iops_device": {[]byte("41:42 43"), []byte("441:442 443")}, 403 }, 404 }, 405 { 406 name: "no bfq.weight", 407 fsi: NewFsiMock(fsBlkioUtFiles), 408 cntnrDir: "/mockpods/no-blkio-bfq-weight", 409 blockIO: BlockIOParameters{Weight: 100}, 410 writesFail: 1, 411 expectedFsWrites: map[string][][]byte{ 412 mountDir + "/blkio/mockpods/no-blkio-bfq-weight/blkio.weight": {[]byte("100")}, 413 }, 414 }, 415 { 416 name: "all writes fail", 417 fsi: NewFsiMock(fsBlkioUtFiles), 418 cntnrDir: "write-enodev", 419 blockIO: BlockIOParameters{ 420 Weight: -1, 421 WeightDevice: DeviceWeights{{1, 0, 100}}, 422 ThrottleReadBpsDevice: DeviceRates{{11, 12, 13}}, 423 }, 424 expectedErrorCount: 2, 425 expectedErrorSubstrings: []string{ 426 "\"1:0 100\"", 427 "\"11:12 13\"", 428 "\"blkio.bfq.weight_device\"", 429 "\"blkio.weight_device\"", 430 "read_bps_device", 431 "no such device", 432 "file not found", 433 }, 434 }, 435 { 436 name: "all files missing", 437 fsi: NewFsiMock(fsBlkioUtFiles), 438 cntnrDir: "/this/container/does/not/exist", 439 blockIO: BlockIOParameters{ 440 Weight: 10, 441 WeightDevice: DeviceWeights{{Major: 1, Minor: 2, Weight: 3}}, 442 ThrottleReadBpsDevice: DeviceRates{{Major: 11, Minor: 12, Rate: 13}}, 443 ThrottleWriteBpsDevice: DeviceRates{{Major: 21, Minor: 22, Rate: 23}}, 444 ThrottleReadIOPSDevice: DeviceRates{{Major: 31, Minor: 32, Rate: 33}}, 445 ThrottleWriteIOPSDevice: DeviceRates{{Major: 41, Minor: 42, Rate: 43}}, 446 }, 447 expectedErrorCount: 6, 448 expectedErrorSubstrings: []string{ 449 "file not found", 450 "blkio.bfq.weight", 451 "blkio.bfq.weight_device", 452 "blkio.throttle.read_bps_device", 453 "blkio.throttle.write_bps_device", 454 "blkio.throttle.read_iops_device", 455 "blkio.throttle.write_iops_device", 456 }, 457 }, 458 } 459 for _, tc := range tcases { 460 t.Run(tc.name, func(t *testing.T) { 461 fsi = tc.fsi 462 overrideFsFuncs(fsi.(*fsMock), tc.fsFuncs) 463 err := SetBlkioParameters(tc.cntnrDir, tc.blockIO) 464 testutils.VerifyError(t, err, tc.expectedErrorCount, tc.expectedErrorSubstrings) 465 validateWriteHistory(t, tc.expectedFsWrites, fsi.(*fsMock).files) 466 }) 467 } 468 }