storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/format-erasure_test.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "encoding/json" 21 "errors" 22 "io/ioutil" 23 "os" 24 "reflect" 25 "testing" 26 ) 27 28 // tests fixFormatErasureV3 - fix format.json on all disks. 29 func TestFixFormatV3(t *testing.T) { 30 erasureDirs, err := getRandomDisks(8) 31 if err != nil { 32 t.Fatal(err) 33 } 34 for _, erasureDir := range erasureDirs { 35 defer os.RemoveAll(erasureDir) 36 } 37 endpoints := mustGetNewEndpoints(erasureDirs...) 38 39 storageDisks, errs := initStorageDisksWithErrors(endpoints) 40 for _, err := range errs { 41 if err != nil && err != errDiskNotFound { 42 t.Fatal(err) 43 } 44 } 45 46 format := newFormatErasureV3(1, 8) 47 format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1 48 formats := make([]*formatErasureV3, 8) 49 50 for j := 0; j < 8; j++ { 51 newFormat := format.Clone() 52 newFormat.Erasure.This = format.Erasure.Sets[0][j] 53 formats[j] = newFormat 54 } 55 56 if err = initErasureMetaVolumesInLocalDisks(storageDisks, formats); err != nil { 57 t.Fatal(err) 58 } 59 60 formats[1] = nil 61 expThis := formats[2].Erasure.This 62 formats[2].Erasure.This = "" 63 if err := fixFormatErasureV3(storageDisks, endpoints, formats); err != nil { 64 t.Fatal(err) 65 } 66 67 newFormats, errs := loadFormatErasureAll(storageDisks, false) 68 for _, err := range errs { 69 if err != nil && err != errUnformattedDisk { 70 t.Fatal(err) 71 } 72 } 73 gotThis := newFormats[2].Erasure.This 74 if expThis != gotThis { 75 t.Fatalf("expected uuid %s, got %s", expThis, gotThis) 76 } 77 } 78 79 // tests formatErasureV3ThisEmpty conditions. 80 func TestFormatErasureEmpty(t *testing.T) { 81 format := newFormatErasureV3(1, 16) 82 format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1 83 formats := make([]*formatErasureV3, 16) 84 85 for j := 0; j < 16; j++ { 86 newFormat := format.Clone() 87 newFormat.Erasure.This = format.Erasure.Sets[0][j] 88 formats[j] = newFormat 89 } 90 91 // empty format to indicate disk not found, but this 92 // empty should return false. 93 formats[0] = nil 94 95 if ok := formatErasureV3ThisEmpty(formats); ok { 96 t.Fatalf("expected value false, got %t", ok) 97 } 98 99 formats[2].Erasure.This = "" 100 if ok := formatErasureV3ThisEmpty(formats); !ok { 101 t.Fatalf("expected value true, got %t", ok) 102 } 103 } 104 105 // Tests xl format migration. 106 func TestFormatErasureMigrate(t *testing.T) { 107 // Get test root. 108 rootPath, err := getTestRoot() 109 if err != nil { 110 t.Fatal(err) 111 } 112 defer os.RemoveAll(rootPath) 113 114 m := &formatErasureV1{} 115 m.Format = formatBackendErasure 116 m.Version = formatMetaVersionV1 117 m.Erasure.Version = formatErasureVersionV1 118 m.Erasure.Disk = mustGetUUID() 119 m.Erasure.JBOD = []string{m.Erasure.Disk, mustGetUUID(), mustGetUUID(), mustGetUUID()} 120 121 b, err := json.Marshal(m) 122 if err != nil { 123 t.Fatal(err) 124 } 125 126 if err = os.MkdirAll(pathJoin(rootPath, minioMetaBucket), os.FileMode(0755)); err != nil { 127 t.Fatal(err) 128 } 129 130 if err = ioutil.WriteFile(pathJoin(rootPath, minioMetaBucket, formatConfigFile), b, os.FileMode(0644)); err != nil { 131 t.Fatal(err) 132 } 133 134 if err = formatErasureMigrate(rootPath); err != nil { 135 t.Fatal(err) 136 } 137 138 migratedVersion, err := formatGetBackendErasureVersion(pathJoin(rootPath, minioMetaBucket, formatConfigFile)) 139 if err != nil { 140 t.Fatal(err) 141 } 142 143 if migratedVersion != formatErasureVersionV3 { 144 t.Fatalf("expected version: %s, got: %s", formatErasureVersionV3, migratedVersion) 145 } 146 147 b, err = ioutil.ReadFile(pathJoin(rootPath, minioMetaBucket, formatConfigFile)) 148 if err != nil { 149 t.Fatal(err) 150 } 151 formatV3 := &formatErasureV3{} 152 if err = json.Unmarshal(b, formatV3); err != nil { 153 t.Fatal(err) 154 } 155 if formatV3.Erasure.This != m.Erasure.Disk { 156 t.Fatalf("expected disk uuid: %s, got: %s", m.Erasure.Disk, formatV3.Erasure.This) 157 } 158 if len(formatV3.Erasure.Sets) != 1 { 159 t.Fatalf("expected single set after migrating from v1 to v3, but found %d", len(formatV3.Erasure.Sets)) 160 } 161 if !reflect.DeepEqual(formatV3.Erasure.Sets[0], m.Erasure.JBOD) { 162 t.Fatalf("expected disk uuid: %v, got: %v", m.Erasure.JBOD, formatV3.Erasure.Sets[0]) 163 } 164 165 m = &formatErasureV1{} 166 m.Format = "unknown" 167 m.Version = formatMetaVersionV1 168 m.Erasure.Version = formatErasureVersionV1 169 m.Erasure.Disk = mustGetUUID() 170 m.Erasure.JBOD = []string{m.Erasure.Disk, mustGetUUID(), mustGetUUID(), mustGetUUID()} 171 172 b, err = json.Marshal(m) 173 if err != nil { 174 t.Fatal(err) 175 } 176 177 if err = ioutil.WriteFile(pathJoin(rootPath, minioMetaBucket, formatConfigFile), b, os.FileMode(0644)); err != nil { 178 t.Fatal(err) 179 } 180 181 if err = formatErasureMigrate(rootPath); err == nil { 182 t.Fatal("Expected to fail with unexpected backend format") 183 } 184 185 m = &formatErasureV1{} 186 m.Format = formatBackendErasure 187 m.Version = formatMetaVersionV1 188 m.Erasure.Version = "30" 189 m.Erasure.Disk = mustGetUUID() 190 m.Erasure.JBOD = []string{m.Erasure.Disk, mustGetUUID(), mustGetUUID(), mustGetUUID()} 191 192 b, err = json.Marshal(m) 193 if err != nil { 194 t.Fatal(err) 195 } 196 197 if err = ioutil.WriteFile(pathJoin(rootPath, minioMetaBucket, formatConfigFile), b, os.FileMode(0644)); err != nil { 198 t.Fatal(err) 199 } 200 201 if err = formatErasureMigrate(rootPath); err == nil { 202 t.Fatal("Expected to fail with unexpected backend format version number") 203 } 204 } 205 206 // Tests check format xl value. 207 func TestCheckFormatErasureValue(t *testing.T) { 208 testCases := []struct { 209 format *formatErasureV3 210 success bool 211 }{ 212 // Invalid Erasure format version "2". 213 { 214 &formatErasureV3{ 215 formatMetaV1: formatMetaV1{ 216 Version: "2", 217 Format: "Erasure", 218 }, 219 Erasure: struct { 220 Version string `json:"version"` 221 This string `json:"this"` 222 Sets [][]string `json:"sets"` 223 DistributionAlgo string `json:"distributionAlgo"` 224 }{ 225 Version: "2", 226 }, 227 }, 228 false, 229 }, 230 // Invalid Erasure format "Unknown". 231 { 232 &formatErasureV3{ 233 formatMetaV1: formatMetaV1{ 234 Version: "1", 235 Format: "Unknown", 236 }, 237 Erasure: struct { 238 Version string `json:"version"` 239 This string `json:"this"` 240 Sets [][]string `json:"sets"` 241 DistributionAlgo string `json:"distributionAlgo"` 242 }{ 243 Version: "2", 244 }, 245 }, 246 false, 247 }, 248 // Invalid Erasure format version "0". 249 { 250 &formatErasureV3{ 251 formatMetaV1: formatMetaV1{ 252 Version: "1", 253 Format: "Erasure", 254 }, 255 Erasure: struct { 256 Version string `json:"version"` 257 This string `json:"this"` 258 Sets [][]string `json:"sets"` 259 DistributionAlgo string `json:"distributionAlgo"` 260 }{ 261 Version: "0", 262 }, 263 }, 264 false, 265 }, 266 } 267 268 // Valid all test cases. 269 for i, testCase := range testCases { 270 if err := checkFormatErasureValue(testCase.format, nil); err != nil && testCase.success { 271 t.Errorf("Test %d: Expected failure %s", i+1, err) 272 } 273 } 274 } 275 276 // Tests getFormatErasureInQuorum() 277 func TestGetFormatErasureInQuorumCheck(t *testing.T) { 278 setCount := 2 279 setDriveCount := 16 280 281 format := newFormatErasureV3(setCount, setDriveCount) 282 format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1 283 formats := make([]*formatErasureV3, 32) 284 285 for i := 0; i < setCount; i++ { 286 for j := 0; j < setDriveCount; j++ { 287 newFormat := format.Clone() 288 newFormat.Erasure.This = format.Erasure.Sets[i][j] 289 formats[i*setDriveCount+j] = newFormat 290 } 291 } 292 293 // Return a format from list of formats in quorum. 294 quorumFormat, err := getFormatErasureInQuorum(formats) 295 if err != nil { 296 t.Fatal(err) 297 } 298 299 // Check if the reference format and input formats are same. 300 if err = formatErasureV3Check(quorumFormat, formats[0]); err != nil { 301 t.Fatal(err) 302 } 303 304 // QuorumFormat has .This field empty on purpose, expect a failure. 305 if err = formatErasureV3Check(formats[0], quorumFormat); err == nil { 306 t.Fatal("Unexpected success") 307 } 308 309 formats[0] = nil 310 quorumFormat, err = getFormatErasureInQuorum(formats) 311 if err != nil { 312 t.Fatal(err) 313 } 314 315 badFormat := *quorumFormat 316 badFormat.Erasure.Sets = nil 317 if err = formatErasureV3Check(quorumFormat, &badFormat); err == nil { 318 t.Fatal("Unexpected success") 319 } 320 321 badFormatUUID := *quorumFormat 322 badFormatUUID.Erasure.Sets[0][0] = "bad-uuid" 323 if err = formatErasureV3Check(quorumFormat, &badFormatUUID); err == nil { 324 t.Fatal("Unexpected success") 325 } 326 327 badFormatSetSize := *quorumFormat 328 badFormatSetSize.Erasure.Sets[0] = nil 329 if err = formatErasureV3Check(quorumFormat, &badFormatSetSize); err == nil { 330 t.Fatal("Unexpected success") 331 } 332 333 for i := range formats { 334 if i < 17 { 335 formats[i] = nil 336 } 337 } 338 if _, err = getFormatErasureInQuorum(formats); err == nil { 339 t.Fatal("Unexpected success") 340 } 341 } 342 343 // Tests formatErasureGetDeploymentID() 344 func TestGetErasureID(t *testing.T) { 345 setCount := 2 346 setDriveCount := 8 347 348 format := newFormatErasureV3(setCount, setDriveCount) 349 format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1 350 formats := make([]*formatErasureV3, 16) 351 352 for i := 0; i < setCount; i++ { 353 for j := 0; j < setDriveCount; j++ { 354 newFormat := format.Clone() 355 newFormat.Erasure.This = format.Erasure.Sets[i][j] 356 formats[i*setDriveCount+j] = newFormat 357 } 358 } 359 360 // Return a format from list of formats in quorum. 361 quorumFormat, err := getFormatErasureInQuorum(formats) 362 if err != nil { 363 t.Fatal(err) 364 } 365 366 // Check if the reference format and input formats are same. 367 var id string 368 if id, err = formatErasureGetDeploymentID(quorumFormat, formats); err != nil { 369 t.Fatal(err) 370 } 371 372 if id == "" { 373 t.Fatal("ID cannot be empty.") 374 } 375 376 formats[0] = nil 377 if id, err = formatErasureGetDeploymentID(quorumFormat, formats); err != nil { 378 t.Fatal(err) 379 } 380 if id == "" { 381 t.Fatal("ID cannot be empty.") 382 } 383 384 formats[1].Erasure.Sets[0][0] = "bad-uuid" 385 if id, err = formatErasureGetDeploymentID(quorumFormat, formats); err != nil { 386 t.Fatal(err) 387 } 388 389 if id == "" { 390 t.Fatal("ID cannot be empty.") 391 } 392 393 formats[2].ID = "bad-id" 394 if _, err = formatErasureGetDeploymentID(quorumFormat, formats); !errors.Is(err, errCorruptedFormat) { 395 t.Fatalf("Unexpect error %s", err) 396 } 397 } 398 399 // Initialize new format sets. 400 func TestNewFormatSets(t *testing.T) { 401 setCount := 2 402 setDriveCount := 16 403 404 format := newFormatErasureV3(setCount, setDriveCount) 405 format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1 406 formats := make([]*formatErasureV3, 32) 407 errs := make([]error, 32) 408 409 for i := 0; i < setCount; i++ { 410 for j := 0; j < setDriveCount; j++ { 411 newFormat := format.Clone() 412 newFormat.Erasure.This = format.Erasure.Sets[i][j] 413 formats[i*setDriveCount+j] = newFormat 414 } 415 } 416 417 quorumFormat, err := getFormatErasureInQuorum(formats) 418 if err != nil { 419 t.Fatal(err) 420 } 421 422 // 16th disk is unformatted. 423 errs[15] = errUnformattedDisk 424 425 newFormats := newHealFormatSets(quorumFormat, setCount, setDriveCount, formats, errs) 426 if newFormats == nil { 427 t.Fatal("Unexpected failure") 428 } 429 430 // Check if deployment IDs are preserved. 431 for i := range newFormats { 432 for j := range newFormats[i] { 433 if newFormats[i][j] == nil { 434 continue 435 } 436 if newFormats[i][j].ID != quorumFormat.ID { 437 t.Fatal("Deployment id in the new format is lost") 438 } 439 } 440 } 441 } 442 443 func BenchmarkInitStorageDisks256(b *testing.B) { 444 benchmarkInitStorageDisksN(b, 256) 445 } 446 447 func BenchmarkInitStorageDisks1024(b *testing.B) { 448 benchmarkInitStorageDisksN(b, 1024) 449 } 450 451 func BenchmarkInitStorageDisks2048(b *testing.B) { 452 benchmarkInitStorageDisksN(b, 2048) 453 } 454 455 func BenchmarkInitStorageDisksMax(b *testing.B) { 456 benchmarkInitStorageDisksN(b, 32*204) 457 } 458 459 func benchmarkInitStorageDisksN(b *testing.B, nDisks int) { 460 b.ResetTimer() 461 b.ReportAllocs() 462 463 fsDirs, err := getRandomDisks(nDisks) 464 if err != nil { 465 b.Fatal(err) 466 } 467 endpoints := mustGetNewEndpoints(fsDirs...) 468 b.RunParallel(func(pb *testing.PB) { 469 endpoints := endpoints 470 for pb.Next() { 471 initStorageDisksWithErrors(endpoints) 472 } 473 }) 474 }