From 434423de89279e64efcba59020e64c67ec6a747e Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Sat, 7 May 2016 00:59:43 -0700 Subject: [PATCH] xl: Move format detection inside xl objects. (#1515) Fixes #1449 --- config.go | 23 --------- format-config-v1.go | 115 ++++++++++++++++++++------------------------ globals.go | 13 +++-- routers.go | 22 --------- xl-objects.go | 52 ++++++++++++++++++++ 5 files changed, 111 insertions(+), 114 deletions(-) diff --git a/config.go b/config.go index 43274a41d..be7203d36 100644 --- a/config.go +++ b/config.go @@ -86,26 +86,3 @@ func getConfigFile() (string, error) { } return filepath.Join(configPath, globalMinioConfigFile), nil } - -// isFormatConfigFileExists - returns true if format config file exists. -func isFormatConfigFileExists() bool { - st, err := os.Stat(mustGetFormatConfigFile()) - return (err == nil && st.Mode().IsRegular()) -} - -// mustGetFormatConfigFile must get format config file. -func mustGetFormatConfigFile() string { - configFile, err := getFormatConfigFile() - fatalIf(err, "Unable to get format config file.", nil) - - return configFile -} - -// getFormatConfigFile get format config file. -func getFormatConfigFile() (string, error) { - configPath, err := getConfigPath() - if err != nil { - return "", err - } - return filepath.Join(configPath, globalMinioFormatConfigFile), nil -} diff --git a/format-config-v1.go b/format-config-v1.go index dabb75b0e..c77939241 100644 --- a/format-config-v1.go +++ b/format-config-v1.go @@ -16,7 +16,10 @@ package main -import "github.com/minio/minio/pkg/quick" +import ( + "encoding/json" + "fmt" +) type fsFormat struct { Version string `json:"version"` @@ -28,84 +31,72 @@ type xlFormat struct { } type formatConfigV1 struct { - // must have "Version" to "quick" to work - Version string `json:"version"` - Format string `json:"format"` - FS fsFormat `json:"fs,omitempty"` - XL xlFormat `json:"xl,omitempty"` + Version string `json:"version"` + Format string `json:"format"` + FS *fsFormat `json:"fs,omitempty"` + XL *xlFormat `json:"xl,omitempty"` } -func (f formatConfigV1) Save() error { - configFile, err := getFormatConfigFile() - if err != nil { - return err - } +// FIXME: currently we don't check single exportPath which uses FS layer. - // initialize quick. - qc, err := quick.New(&f) +// loadFormatXL - load XL format.json. +func loadFormatXL(storage StorageAPI) (xl *xlFormat, err error) { + offset := int64(0) + r, err := storage.ReadFile(minioMetaBucket, formatConfigFile, offset) if err != nil { - return err + return nil, err } - - // Save config file. - return qc.Save(configFile) -} - -func (f *formatConfigV1) Load() error { - configFile, err := getFormatConfigFile() + decoder := json.NewDecoder(r) + formatXL := formatConfigV1{} + err = decoder.Decode(&formatXL) if err != nil { - return err + return nil, err } + if err = r.Close(); err != nil { + return nil, err + } + if formatXL.Version != "1" { + return nil, fmt.Errorf("Unsupported version of backend format [%s] found.", formatXL.Version) + } + if formatXL.Format != "xl" { + return nil, fmt.Errorf("Unsupported backend format [%s] found.", formatXL.Format) + } + return formatXL.XL, nil +} - f.Version = globalMinioConfigVersion - qc, err := quick.New(f) +// checkFormat - validates if format.json file exists. +func checkFormat(storage StorageAPI) error { + _, err := storage.StatFile(minioMetaBucket, formatConfigFile) if err != nil { return err } - if err := qc.Load(configFile); err != nil { - return err - } - return nil } -// saveFormatFS - save FS format configuration -func saveFormatFS(fs fsFormat) error { - config := formatConfigV1{Version: globalMinioConfigVersion, Format: "fs", FS: fs} - return config.Save() -} - // saveFormatXL - save XL format configuration -func saveFormatXL(xl xlFormat) error { - config := formatConfigV1{Version: globalMinioConfigVersion, Format: "xl", XL: xl} - return config.Save() -} - -// getSavedFormatConfig - get saved format configuration -func getSavedFormatConfig() (formatConfigV1, error) { - config := formatConfigV1{Version: globalMinioConfigVersion} - - if err := config.Load(); err != nil { - return config, err - } - - return config, nil -} - -// getFormatFS - get saved FS format configuration -func getFormatFS() (fsFormat, error) { - config, err := getSavedFormatConfig() +func saveFormatXL(storage StorageAPI, xl *xlFormat) error { + w, err := storage.CreateFile(minioMetaBucket, formatConfigFile) if err != nil { - return fsFormat{}, err + return err } - return config.FS, nil -} - -// getFormatXL - get saved XL format configuration -func getFormatXL() (xlFormat, error) { - config, err := getSavedFormatConfig() + formatXL := formatConfigV1{ + Version: "1", + Format: "xl", + XL: xl, + } + encoder := json.NewEncoder(w) + err = encoder.Encode(&formatXL) if err != nil { - return xlFormat{}, err + if clErr := safeCloseAndRemove(w); clErr != nil { + return clErr + } + return err + } + if err = w.Close(); err != nil { + if clErr := safeCloseAndRemove(w); clErr != nil { + return clErr + } + return err } - return config.XL, nil + return nil } diff --git a/globals.go b/globals.go index ad3b147f7..bb65c27bc 100644 --- a/globals.go +++ b/globals.go @@ -29,13 +29,12 @@ const ( // minio configuration related constants. const ( - globalMinioConfigVersion = "4" - globalMinioConfigDir = ".minio" - globalMinioCertsDir = ".minio/certs" - globalMinioCertFile = "public.crt" - globalMinioKeyFile = "private.key" - globalMinioConfigFile = "config.json" - globalMinioFormatConfigFile = "format.json" + globalMinioConfigVersion = "4" + globalMinioConfigDir = ".minio" + globalMinioCertsDir = ".minio/certs" + globalMinioCertFile = "public.crt" + globalMinioKeyFile = "private.key" + globalMinioConfigFile = "config.json" ) var ( diff --git a/routers.go b/routers.go index 5f5d9a7f3..39afda7f7 100644 --- a/routers.go +++ b/routers.go @@ -17,9 +17,7 @@ package main import ( - "fmt" "net/http" - "reflect" router "github.com/gorilla/mux" ) @@ -38,26 +36,6 @@ func newObjectLayer(exportPaths ...string) (ObjectLayer, error) { // configureServer handler returns final handler for the http server. func configureServerHandler(srvCmdConfig serverCmdConfig) http.Handler { - // FIXME: currently we don't check single exportPath which uses FS layer. - if len(srvCmdConfig.exportPaths) > 1 { - if isFormatConfigFileExists() { - format, err := getFormatXL() - if err != nil { - fatalIf(err, "Failed to read format.json", nil) - } - - if !reflect.DeepEqual(format.Disks, srvCmdConfig.exportPaths) { - err = fmt.Errorf("Number of export paths from command-line did not match the backend configuration. Backend is configured with [%s] exports.", format.Disks) - fatalIf(err, "", nil) - } - } else { - // First run: save disk configuration - if err := saveFormatXL(xlFormat{Version: "1", Disks: srvCmdConfig.exportPaths}); err != nil { - fatalIf(err, "Unable to save 'format.json'", nil) - } - } - } - objAPI, err := newObjectLayer(srvCmdConfig.exportPaths...) fatalIf(err, "Initializing object layer failed.", nil) diff --git a/xl-objects.go b/xl-objects.go index e43693982..c3ba7ebe6 100644 --- a/xl-objects.go +++ b/xl-objects.go @@ -18,6 +18,7 @@ package main import ( "encoding/json" + "fmt" "io" "path/filepath" "strings" @@ -29,6 +30,7 @@ import ( const ( multipartSuffix = ".minio.multipart" multipartMetaFile = "00000" + multipartSuffix + formatConfigFile = "format.json" ) // xlObjects - Implements fs object layer. @@ -43,11 +45,37 @@ func isLeafDirectory(disk StorageAPI, volume, leafPath string) bool { return err == nil } +// isValidFormat - validates input arguments with backend 'format.json' +func isValidFormat(storage StorageAPI, exportPaths ...string) bool { + // Load saved XL format.json and validate. + xl, err := loadFormatXL(storage) + if err != nil { + log.Errorf("loadFormatXL failed with %s", err) + return false + } + if xl.Version != "1" { + log.Errorf("Unsupported XL backend format found [%s]", xl.Version) + return false + } + if len(exportPaths) != len(xl.Disks) { + log.Errorf("Number of disks %d passed at the command-line did not match the backend format %d", len(exportPaths), len(xl.Disks)) + return false + } + for index, disk := range xl.Disks { + if exportPaths[index] != disk { + log.Errorf("Invalid order of disks detected %s. Required order is %s.", exportPaths, xl.Disks) + return false + } + } + return true +} + // FIXME: constructor should return a pointer. // newXLObjects - initialize new xl object layer. func newXLObjects(exportPaths ...string) (ObjectLayer, error) { storage, err := newXL(exportPaths...) if err != nil { + log.Errorf("newXL failed with %s", err) return nil, err } @@ -55,6 +83,30 @@ func newXLObjects(exportPaths ...string) (ObjectLayer, error) { // cleaning up tmp files etc. initObjectLayer(storage) + err = checkFormat(storage) + if err != nil { + if err == errFileNotFound { + // Save new XL format. + errSave := saveFormatXL(storage, &xlFormat{ + Version: "1", + Disks: exportPaths, + }) + if errSave != nil { + log.Errorf("saveFormatXL failed with %s", errSave) + return nil, errSave + } + } else { + log.Errorf("Unable to check backend format %s", err) + return nil, err + } + } + + // Validate if format exists and input arguments are validated + // with backend format. + if !isValidFormat(storage, exportPaths...) { + return nil, fmt.Errorf("Command-line arguments %s is not valid.", exportPaths) + } + // Return successfully initialized object layer. return xlObjects{ storage: storage,