@ -88,41 +88,45 @@ func formatXLCleanupTmpLocalEndpoints(endpoints EndpointList) error {
return nil
}
// Format disks before initialization of object layer.
func waitForFormatXL ( firstDisk bool , endpoints EndpointList , setCount , disksPerSet int ) ( format * formatXLV3 , err error ) {
if len ( endpoints ) == 0 || setCount == 0 || disksPerSet == 0 {
return nil , errInvalidArgument
// validate reference format against list of XL formats.
func validateXLFormats ( format * formatXLV3 , formats [ ] * formatXLV3 , endpoints EndpointList , setCount , drivesPerSet int ) error {
for i := range formats {
if formats [ i ] == nil {
continue
}
if err = formatXLMigrateLocalEndpoints ( endpoints ) ; err != nil {
return nil , err
if err := formatXLV3Check ( format , formats [ i ] ) ; err != nil {
return fmt . Errorf ( "%s format error: %s" , endpoints [ i ] , err )
}
if err = formatXLCleanupTmpLocalEndpoints ( endpoints ) ; err != nil {
return nil , err
}
if len ( format . XL . Sets ) != setCount {
return fmt . Errorf ( "Current backend format is inconsistent with input args (%s), Expected set count %d, got %d" , endpoints , len ( format . XL . Sets ) , setCount )
}
if len ( format . XL . Sets [ 0 ] ) != drivesPerSet {
return fmt . Errorf ( "Current backend format is inconsistent with input args (%s), Expected drive count per set %d, got %d" , endpoints , len ( format . XL . Sets [ 0 ] ) , drivesPerSet )
}
return nil
}
// Done channel is used to close any lingering retry routine, as soon
// as this function returns.
doneCh := make ( chan struct { } )
// Indicate to our retry routine to exit cleanly, upon this function return.
defer close ( doneCh )
// prepare getElapsedTime() to calculate elapsed time since we started trying formatting disks.
// All times are rounded to avoid showing milli, micro and nano seconds
formatStartTime := time . Now ( ) . Round ( time . Second )
getElapsedTime := func ( ) string {
return time . Now ( ) . Round ( time . Second ) . Sub ( formatStartTime ) . String ( )
// Following error message is added to fix a regression in release
// RELEASE.2018-03-16T22-52-12Z after migrating v1 to v2 to v3. This
// migration failed to capture '.This' field properly which indicates
// the disk UUID association. Below error message is returned when
// we see this situation in format.json, for more info refer
// https://github.com/minio/minio/issues/5667
var errXLV3ThisEmpty = fmt . Errorf ( "XL format version 3 has This field empty" )
// connect to list of endpoints and load all XL disk formats, validate the formats are correct
// and are in quorum, if no formats are found attempt to initialize all of them for the first
// time. additionally make sure to close all the disks used in this attempt.
func connectLoadInitFormats ( firstDisk bool , endpoints EndpointList , setCount , drivesPerSet int ) ( * formatXLV3 , error ) {
storageDisks , err := initStorageDisks ( endpoints )
if err != nil {
return nil , err
}
defer closeStorageDisks ( storageDisks )
// Wait on the jitter retry loop.
retryTimerCh := newRetryTimerSimple ( doneCh )
for {
select {
case _ = <- retryTimerCh :
// Attempt to load all `format.json` from all disks.
formatConfigs , sErrs := loadFormatXLAll ( endpoint s)
formatConfigs , sErrs := loadFormatXLAll ( storageDisks )
// Pre-emptively check if one of the formatted disks
// is invalid. This function returns success for the
@ -141,10 +145,9 @@ func waitForFormatXL(firstDisk bool, endpoints EndpointList, setCount, disksPerS
if shouldInitXLDisks ( sErrs ) {
if ! firstDisk {
console . Println ( "Waiting for the first server to format the disks." )
continue
return nil , errNotFirstDisk
}
return initFormatXL ( endpoint s, setCount , disk sPerSet )
return initFormatXL ( storageDisk s, setCount , drive sPerSet )
}
// Following function is added to fix a regressions which was introduced
@ -152,34 +155,81 @@ func waitForFormatXL(firstDisk bool, endpoints EndpointList, setCount, disksPerS
// This migration failed to capture '.This' field properly which indicates
// the disk UUID association. Below function is called to handle and fix
// this regression, for more info refer https://github.com/minio/minio/issues/5667
if err = fixFormatXLV3 ( endpoints , formatConfigs ) ; err != nil {
if err = fixFormatXLV3 ( storageDisks , endpoints , formatConfigs ) ; err != nil {
return nil , err
}
// If any of the .This field is still empty we wait them to be fixed .
// If any of the .This field is still empty, we return error .
if formatXLV3ThisEmpty ( formatConfigs ) {
continue
return nil , errXLV3ThisEmpty
}
format , err = getFormatXLInQuorum ( formatConfigs )
if err == nil {
for i := range formatConfigs {
if formatConfigs [ i ] == nil {
continue
format , err := getFormatXLInQuorum ( formatConfigs )
if err != nil {
return nil , err
}
if err = formatXLV3Check ( format , formatConfigs [ i ] ) ; err != nil {
return nil , fmt . Errorf ( "%s format error: %s" , endpoints [ i ] , err )
// Validate all format configs with reference format.
if err = validateXLFormats ( format , formatConfigs , endpoints , setCount , drivesPerSet ) ; err != nil {
return nil , err
}
return format , nil
}
// Format disks before initialization of object layer.
func waitForFormatXL ( firstDisk bool , endpoints EndpointList , setCount , disksPerSet int ) ( format * formatXLV3 , err error ) {
if len ( endpoints ) == 0 || setCount == 0 || disksPerSet == 0 {
return nil , errInvalidArgument
}
if len ( format . XL . Sets ) != globalXLSetCount {
return nil , fmt . Errorf ( "Current backend format is inconsistent with input args (%s), Expected set count %d, got %d" , endpoints , len ( format . XL . Sets ) , globalXLSetCount )
if err = formatXLMigrateLocalEndpoints ( endpoints ) ; err != nil {
return nil , err
}
if len ( format . XL . Sets [ 0 ] ) != globalXLSetDriveCount {
return nil , fmt . Errorf ( "Current backend format is inconsistent with input args (%s), Expected drive count per set %d, got %d" , endpoints , len ( format . XL . Sets [ 0 ] ) , globalXLSetDriveCount )
if err = formatXLCleanupTmpLocalEndpoints ( endpoints ) ; err != nil {
return nil , err
}
return format , nil
// Done channel is used to close any lingering retry routine, as soon
// as this function returns.
doneCh := make ( chan struct { } )
// Indicate to our retry routine to exit cleanly, upon this function return.
defer close ( doneCh )
// prepare getElapsedTime() to calculate elapsed time since we started trying formatting disks.
// All times are rounded to avoid showing milli, micro and nano seconds
formatStartTime := time . Now ( ) . Round ( time . Second )
getElapsedTime := func ( ) string {
return time . Now ( ) . Round ( time . Second ) . Sub ( formatStartTime ) . String ( )
}
// Wait on the jitter retry loop.
retryTimerCh := newRetryTimerSimple ( doneCh )
for {
select {
case _ = <- retryTimerCh :
format , err := connectLoadInitFormats ( firstDisk , endpoints , setCount , disksPerSet )
if err != nil {
switch err {
case errNotFirstDisk :
// Fresh setup, wait for first server to be up.
console . Println ( "Waiting for the first server to format the disks." )
continue
case errXLReadQuorum :
// no quorum available continue to wait for minimum number of servers.
console . Printf ( "Waiting for a minimum of %d disks to come online (elapsed %s)\n" , len ( endpoints ) / 2 , getElapsedTime ( ) )
continue
case errXLV3ThisEmpty :
// need to wait for this error to be healed, so continue.
continue
default :
// For all other unhandled errors we exit and fail.
return nil , err
}
}
return format , nil
case <- globalOSSignalCh :
return nil , fmt . Errorf ( "Initializing data volumes gracefully stopped" )
}