diff --git a/cmd/config-migrate.go b/cmd/config-migrate.go index d6a07b045..e38d1bf4c 100644 --- a/cmd/config-migrate.go +++ b/cmd/config-migrate.go @@ -74,6 +74,10 @@ func migrateConfig() error { if err := migrateV12ToV13(); err != nil { return err } + // Migration version '13' to '14'. + if err := migrateV13ToV14(); err != nil { + return err + } return nil } @@ -937,3 +941,105 @@ func migrateV12ToV13() error { ) return nil } + +// Version '13' to '14' migration. Add support for custom webhook endpoint. +func migrateV13ToV14() error { + cv13, err := loadConfigV13() + if err != nil { + if os.IsNotExist(err) { + return nil + } + return fmt.Errorf("Unable to load config version ‘13’. %v", err) + } + if cv13.Version != "13" { + return nil + } + + // Copy over fields from V13 into V14 config struct + srvConfig := &serverConfigV14{ + Logger: &logger{}, + Notify: ¬ifier{}, + } + srvConfig.Version = "14" + srvConfig.Credential = cv13.Credential + srvConfig.Region = cv13.Region + if srvConfig.Region == "" { + // Region needs to be set for AWS Signature Version 4. + srvConfig.Region = globalMinioDefaultRegion + } + srvConfig.Logger.Console = cv13.Logger.Console + srvConfig.Logger.File = cv13.Logger.File + + // check and set notifiers config + if len(cv13.Notify.AMQP) == 0 { + srvConfig.Notify.AMQP = make(map[string]amqpNotify) + srvConfig.Notify.AMQP["1"] = amqpNotify{} + } else { + srvConfig.Notify.AMQP = cv13.Notify.AMQP + } + if len(cv13.Notify.ElasticSearch) == 0 { + srvConfig.Notify.ElasticSearch = make(map[string]elasticSearchNotify) + srvConfig.Notify.ElasticSearch["1"] = elasticSearchNotify{} + } else { + srvConfig.Notify.ElasticSearch = cv13.Notify.ElasticSearch + } + if len(cv13.Notify.Redis) == 0 { + srvConfig.Notify.Redis = make(map[string]redisNotify) + srvConfig.Notify.Redis["1"] = redisNotify{} + } else { + srvConfig.Notify.Redis = cv13.Notify.Redis + } + if len(cv13.Notify.PostgreSQL) == 0 { + srvConfig.Notify.PostgreSQL = make(map[string]postgreSQLNotify) + srvConfig.Notify.PostgreSQL["1"] = postgreSQLNotify{} + } else { + srvConfig.Notify.PostgreSQL = cv13.Notify.PostgreSQL + } + if len(cv13.Notify.Kafka) == 0 { + srvConfig.Notify.Kafka = make(map[string]kafkaNotify) + srvConfig.Notify.Kafka["1"] = kafkaNotify{} + } else { + srvConfig.Notify.Kafka = cv13.Notify.Kafka + } + if len(cv13.Notify.NATS) == 0 { + srvConfig.Notify.NATS = make(map[string]natsNotify) + srvConfig.Notify.NATS["1"] = natsNotify{} + } else { + srvConfig.Notify.NATS = cv13.Notify.NATS + } + if len(cv13.Notify.Webhook) == 0 { + srvConfig.Notify.Webhook = make(map[string]webhookNotify) + srvConfig.Notify.Webhook["1"] = webhookNotify{} + } else { + srvConfig.Notify.Webhook = cv13.Notify.Webhook + } + + // Set the new browser parameter to true by default + srvConfig.Browser = "on" + + qc, err := quick.New(srvConfig) + if err != nil { + return fmt.Errorf("Unable to initialize the quick config. %v", + err) + } + configFile, err := getConfigFile() + if err != nil { + return fmt.Errorf("Unable to get config file. %v", err) + } + + err = qc.Save(configFile) + if err != nil { + return fmt.Errorf( + "Failed to migrate config from ‘"+ + cv13.Version+"’ to ‘"+srvConfig.Version+ + "’ failed. %v", err, + ) + } + + console.Println( + "Migration from version ‘" + + cv13.Version + "’ to ‘" + srvConfig.Version + + "’ completed successfully.", + ) + return nil +} diff --git a/cmd/config-migrate_test.go b/cmd/config-migrate_test.go index b1fb6e24e..35f2d802d 100644 --- a/cmd/config-migrate_test.go +++ b/cmd/config-migrate_test.go @@ -50,7 +50,7 @@ func TestServerConfigMigrateV1(t *testing.T) { } // Initialize server config and check again if everything is fine - if err := loadConfig(credential{}); err != nil { + if err := loadConfig(envParams{}); err != nil { t.Fatalf("Unable to initialize from updated config file %s", err) } } @@ -106,10 +106,13 @@ func TestServerConfigMigrateInexistentConfig(t *testing.T) { if err := migrateV12ToV13(); err != nil { t.Fatal("migrate v12 to v13 should succeed when no config file is found") } + if err := migrateV13ToV14(); err != nil { + t.Fatal("migrate v13 to v14 should succeed when no config file is found") + } } // Test if a config migration from v2 to v12 is successfully done -func TestServerConfigMigrateV2toV12(t *testing.T) { +func TestServerConfigMigrateV2toV14(t *testing.T) { rootPath, err := newTestConfig(globalMinioDefaultRegion) if err != nil { t.Fatalf("Init Test config failed") @@ -143,7 +146,7 @@ func TestServerConfigMigrateV2toV12(t *testing.T) { } // Initialize server config and check again if everything is fine - if err := loadConfig(credential{}); err != nil { + if err := loadConfig(envParams{}); err != nil { t.Fatalf("Unable to initialize from updated config file %s", err) } @@ -213,4 +216,7 @@ func TestServerConfigMigrateFaultyConfig(t *testing.T) { if err := migrateV12ToV13(); err == nil { t.Fatal("migrateConfigV12ToV13() should fail with a corrupted json") } + if err := migrateV13ToV14(); err == nil { + t.Fatal("migrateConfigV13ToV14() should fail with a corrupted json") + } } diff --git a/cmd/config-old.go b/cmd/config-old.go index c3cc6113c..d8fa5867f 100644 --- a/cmd/config-old.go +++ b/cmd/config-old.go @@ -522,3 +522,31 @@ func loadConfigV12() (*serverConfigV12, error) { } return config.(*serverConfigV12), err } + +// serverConfigV13 server configuration version '13' which is like +// version '12' except it adds support for webhook notification. +type serverConfigV13 struct { + Version string `json:"version"` + + // S3 API configuration. + Credential credential `json:"credential"` + Region string `json:"region"` + + // Additional error logging configuration. + Logger *logger `json:"logger"` + + // Notification queue configuration. + Notify *notifier `json:"notify"` +} + +func loadConfigV13() (*serverConfigV13, error) { + configFile, err := getConfigFile() + if err != nil { + return nil, err + } + config, err := loadOldConfig(configFile, &serverConfigV13{Version: "13"}) + if config == nil { + return nil, err + } + return config.(*serverConfigV13), err +} diff --git a/cmd/config-v13.go b/cmd/config-v14.go similarity index 73% rename from cmd/config-v13.go rename to cmd/config-v14.go index f1ee32b90..1ae69405b 100644 --- a/cmd/config-v13.go +++ b/cmd/config-v14.go @@ -18,6 +18,7 @@ package cmd import ( "os" + "strings" "sync" "github.com/minio/minio/pkg/quick" @@ -26,14 +27,15 @@ import ( // Read Write mutex for safe access to ServerConfig. var serverConfigMu sync.RWMutex -// serverConfigV13 server configuration version '13' which is like -// version '12' except it adds support for webhook notification. -type serverConfigV13 struct { +// serverConfigV14 server configuration version '14' which is like +// version '13' except it adds support of browser param. +type serverConfigV14 struct { Version string `json:"version"` // S3 API configuration. Credential credential `json:"credential"` Region string `json:"region"` + Browser string `json:"browser"` // Additional error logging configuration. Logger *logger `json:"logger"` @@ -42,12 +44,11 @@ type serverConfigV13 struct { Notify *notifier `json:"notify"` } -// newConfig - initialize a new server config, saves creds from env -// if globalIsEnvCreds is set otherwise generates a new set of keys -// and those are saved. -func newConfig(envCreds credential) error { +// newConfig - initialize a new server config, saves env parameters if +// found, otherwise use default parameters +func newConfig(envParams envParams) error { // Initialize server config. - srvCfg := &serverConfigV13{ + srvCfg := &serverConfigV14{ Logger: &logger{}, Notify: ¬ifier{}, } @@ -56,11 +57,17 @@ func newConfig(envCreds credential) error { // If env is set for a fresh start, save them to config file. if globalIsEnvCreds { - srvCfg.SetCredential(envCreds) + srvCfg.SetCredential(envParams.creds) } else { srvCfg.SetCredential(newCredential()) } + if globalIsEnvBrowser { + srvCfg.SetBrowser(envParams.browser) + } else { + srvCfg.SetBrowser("on") + } + // Enable console logger by default on a fresh run. srvCfg.Logger.Console = consoleLogger{ Enable: true, @@ -99,10 +106,9 @@ func newConfig(envCreds credential) error { return serverConfig.Save() } -// loadConfig - loads a new config from disk, overrides creds from env -// if globalIsEnvCreds is set otherwise serves the creds from loaded -// from the disk. -func loadConfig(envCreds credential) error { +// loadConfig - loads a new config from disk, overrides params from env +// if found and valid +func loadConfig(envParams envParams) error { configFile, err := getConfigFile() if err != nil { return err @@ -111,7 +117,7 @@ func loadConfig(envCreds credential) error { if _, err = os.Stat(configFile); err != nil { return err } - srvCfg := &serverConfigV13{} + srvCfg := &serverConfigV14{} srvCfg.Version = globalMinioConfigVersion qc, err := quick.New(srvCfg) if err != nil { @@ -124,11 +130,19 @@ func loadConfig(envCreds credential) error { // If env is set override the credentials from config file. if globalIsEnvCreds { - srvCfg.SetCredential(envCreds) + srvCfg.SetCredential(envParams.creds) } else { srvCfg.SetCredential(srvCfg.Credential) } + if globalIsEnvBrowser { + srvCfg.SetBrowser(envParams.browser) + } + + if strings.ToLower(srvCfg.GetBrowser()) == "off" { + globalIsBrowserEnabled = false + } + // hold the mutex lock before a new config is assigned. serverConfigMu.Lock() // Save the loaded config globally. @@ -141,10 +155,10 @@ func loadConfig(envCreds credential) error { } // serverConfig server config. -var serverConfig *serverConfigV13 +var serverConfig *serverConfigV14 // GetVersion get current config version. -func (s serverConfigV13) GetVersion() string { +func (s serverConfigV14) GetVersion() string { serverConfigMu.RLock() defer serverConfigMu.RUnlock() @@ -152,7 +166,7 @@ func (s serverConfigV13) GetVersion() string { } // SetRegion set new region. -func (s *serverConfigV13) SetRegion(region string) { +func (s *serverConfigV14) SetRegion(region string) { serverConfigMu.Lock() defer serverConfigMu.Unlock() @@ -160,7 +174,7 @@ func (s *serverConfigV13) SetRegion(region string) { } // GetRegion get current region. -func (s serverConfigV13) GetRegion() string { +func (s serverConfigV14) GetRegion() string { serverConfigMu.RLock() defer serverConfigMu.RUnlock() @@ -168,7 +182,7 @@ func (s serverConfigV13) GetRegion() string { } // SetCredentials set new credentials. -func (s *serverConfigV13) SetCredential(creds credential) { +func (s *serverConfigV14) SetCredential(creds credential) { serverConfigMu.Lock() defer serverConfigMu.Unlock() @@ -177,15 +191,32 @@ func (s *serverConfigV13) SetCredential(creds credential) { } // GetCredentials get current credentials. -func (s serverConfigV13) GetCredential() credential { +func (s serverConfigV14) GetCredential() credential { serverConfigMu.RLock() defer serverConfigMu.RUnlock() return s.Credential } +// SetBrowser set if browser is enabled. +func (s *serverConfigV14) SetBrowser(v string) { + serverConfigMu.Lock() + defer serverConfigMu.Unlock() + + // Set browser param + s.Browser = v +} + +// GetCredentials get current credentials. +func (s serverConfigV14) GetBrowser() string { + serverConfigMu.RLock() + defer serverConfigMu.RUnlock() + + return s.Browser +} + // Save config. -func (s serverConfigV13) Save() error { +func (s serverConfigV14) Save() error { serverConfigMu.RLock() defer serverConfigMu.RUnlock() diff --git a/cmd/config-v13_test.go b/cmd/config-v14_test.go similarity index 78% rename from cmd/config-v13_test.go rename to cmd/config-v14_test.go index 4b4e420f9..bbf786c3a 100644 --- a/cmd/config-v13_test.go +++ b/cmd/config-v14_test.go @@ -17,6 +17,7 @@ package cmd import ( + "os" "reflect" "testing" ) @@ -114,7 +115,55 @@ func TestServerConfig(t *testing.T) { setGlobalConfigPath(rootPath) // Initialize server config. - if err := loadConfig(credential{}); err != nil { + if err := loadConfig(envParams{}); err != nil { t.Fatalf("Unable to initialize from updated config file %s", err) } } + +func TestServerConfigWithEnvs(t *testing.T) { + + os.Setenv("MINIO_BROWSER", "off") + defer os.Unsetenv("MINIO_BROWSER") + + os.Setenv("MINIO_ACCESS_KEY", "minio") + defer os.Unsetenv("MINIO_ACCESS_KEY") + + os.Setenv("MINIO_SECRET_KEY", "minio123") + defer os.Unsetenv("MINIO_SECRET_KEY") + + defer func() { + globalIsEnvBrowser = false + globalIsEnvCreds = false + }() + + // Get test root. + rootPath, err := getTestRoot() + if err != nil { + t.Error(err) + } + + // Do this only once here. + setGlobalConfigPath(rootPath) + + // Init config + initConfig() + + // remove the root directory after the test ends. + defer removeAll(rootPath) + + // Check if serverConfig has + if serverConfig.GetBrowser() != "off" { + t.Errorf("Expecting browser `off` found %s", serverConfig.GetBrowser()) + } + + // Check if serverConfig has + cred := serverConfig.GetCredential() + + if cred.AccessKey != "minio" { + t.Errorf("Expecting access key to be `minio` found %s", cred.AccessKey) + } + + if cred.SecretKey != "minio123" { + t.Errorf("Expecting access key to be `minio123` found %s", cred.SecretKey) + } +} diff --git a/cmd/globals.go b/cmd/globals.go index 56439f635..deff7ae90 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -19,9 +19,7 @@ package cmd import ( "crypto/x509" "net/url" - "os" "runtime" - "strings" "time" humanize "github.com/dustin/go-humanize" @@ -74,9 +72,12 @@ var ( // Indicates if the running minio server is an erasure-code backend. globalIsXL = false - // This flag is set to 'true' by default, it is set to `false` - // when MINIO_BROWSER env is set to 'off'. - globalIsBrowserEnabled = !strings.EqualFold(os.Getenv("MINIO_BROWSER"), "off") + // This flag is set to 'true' by default + globalIsBrowserEnabled = true + // This flag is set to 'true' when MINIO_BROWSER env is set. + globalIsEnvBrowser = false + // Set to true if credentials were passed from env, default is false. + globalIsEnvCreds = false // Maximum cache size. Defaults to disabled. // Caching is enabled only for RAM size > 8GiB. @@ -113,9 +114,6 @@ var ( // Minio server user agent string. globalServerUserAgent = "Minio/" + ReleaseTag + " (" + runtime.GOOS + "; " + runtime.GOARCH + ")" - // Set to true if credentials were passed from env, default is false. - globalIsEnvCreds = false - // url.URL endpoints of disks that belong to the object storage. globalEndpoints = []*url.URL{} diff --git a/cmd/main.go b/cmd/main.go index 1516cd086..18d3ab20a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -159,14 +159,24 @@ func checkUpdate() { } } +// envParams holds all env parameters +type envParams struct { + creds credential + browser string +} + // Initializes a new config if it doesn't exist, else migrates any old config // to newer config and finally loads the config to memory. func initConfig() { - envCreds := mustGetCredentialFromEnv() + + envs := envParams{ + creds: mustGetCredentialFromEnv(), + browser: mustGetBrowserFromEnv(), + } // Config file does not exist, we create it fresh and return upon success. if !isConfigFileExists() { - if err := newConfig(envCreds); err != nil { + if err := newConfig(envs); err != nil { console.Fatalf("Unable to initialize minio config for the first time. Err: %s.\n", err) } console.Println("Created minio configuration file successfully at " + mustGetConfigPath()) @@ -177,7 +187,7 @@ func initConfig() { migrate() // Once we have migrated all the old config, now load them. - if err := loadConfig(envCreds); err != nil { + if err := loadConfig(envs); err != nil { console.Fatalf("Unable to initialize minio config. Err: %s.\n", err) } } diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index 7ef2f25b2..cb1d8d1e2 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -533,7 +533,7 @@ func newTestConfig(bucketLocation string) (rootPath string, err error) { setGlobalConfigPath(rootPath) // Initialize server config. - if err = newConfig(credential{}); err != nil { + if err = newConfig(envParams{}); err != nil { return "", err } diff --git a/cmd/utils.go b/cmd/utils.go index a6a158d48..46166147c 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -23,11 +23,13 @@ import ( "io" "net/http" "net/url" + "os" "strings" "encoding/json" humanize "github.com/dustin/go-humanize" + "github.com/minio/mc/pkg/console" "github.com/pkg/profile" ) @@ -238,3 +240,25 @@ func dumpRequest(r *http.Request) string { } return string(jsonBytes) } + +// Variant of getBrowserFromEnv but upon error fails right here. +func mustGetBrowserFromEnv() string { + browser, err := getBrowserFromEnv() + if err != nil { + console.Fatalf("Unable to load MINIO_BROWSER value from environment. Err: %s.\n", err) + } + return browser +} + +// +func getBrowserFromEnv() (string, error) { + b := os.Getenv("MINIO_BROWSER") + if strings.TrimSpace(b) == "" { + return "", nil + } + if !strings.EqualFold(b, "off") && !strings.EqualFold(b, "on") { + return "", errInvalidArgument + } + globalIsEnvBrowser = true + return strings.ToLower(b), nil +}