Persist MINIO_WORM as part of config.json (#6022)

This commit is contained in:
Harshavardhana 2018-06-06 18:10:51 -07:00 committed by kannappanr
parent 113570b514
commit 6138cae8e7
16 changed files with 333 additions and 112 deletions

View File

@ -42,7 +42,7 @@ export class ChangePasswordModal extends React.Component {
const { serverInfo } = this.props const { serverInfo } = this.props
// Check environment variables first. // Check environment variables first.
if (serverInfo.info.isEnvCreds) { if (serverInfo.info.isEnvCreds || serverInfo.info.isWorm) {
this.setState({ this.setState({
accessKey: "xxxxxxxxx", accessKey: "xxxxxxxxx",
secretKey: "xxxxxxxxx", secretKey: "xxxxxxxxx",

File diff suppressed because one or more lines are too long

View File

@ -68,7 +68,7 @@ var (
func (a adminAPIHandlers) VersionHandler(w http.ResponseWriter, r *http.Request) { func (a adminAPIHandlers) VersionHandler(w http.ResponseWriter, r *http.Request) {
adminAPIErr := checkAdminRequestAuthType(r, globalServerConfig.GetRegion()) adminAPIErr := checkAdminRequestAuthType(r, globalServerConfig.GetRegion())
if adminAPIErr != ErrNone { if adminAPIErr != ErrNone {
writeErrorResponse(w, adminAPIErr, r.URL) writeErrorResponseJSON(w, adminAPIErr, r.URL)
return return
} }
@ -669,6 +669,12 @@ func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Reques
return return
} }
// Deny if WORM is enabled
if globalWORMEnabled {
writeErrorResponseJSON(w, ErrMethodNotAllowed, r.URL)
return
}
// Validate request signature. // Validate request signature.
adminAPIErr := checkAdminRequestAuthType(r, globalServerConfig.GetRegion()) adminAPIErr := checkAdminRequestAuthType(r, globalServerConfig.GetRegion())
if adminAPIErr != ErrNone { if adminAPIErr != ErrNone {
@ -681,12 +687,12 @@ func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Reques
n, err := io.ReadFull(r.Body, configBuf) n, err := io.ReadFull(r.Body, configBuf)
if err == nil { if err == nil {
// More than maxConfigSize bytes were available // More than maxConfigSize bytes were available
writeErrorResponse(w, ErrAdminConfigTooLarge, r.URL) writeErrorResponseJSON(w, ErrAdminConfigTooLarge, r.URL)
return return
} }
if err != io.ErrUnexpectedEOF { if err != io.ErrUnexpectedEOF {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponse(w, toAPIErrorCode(err), r.URL) writeErrorResponseJSON(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -696,7 +702,7 @@ func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Reques
// client has not sent JSON objects with duplicate keys. // client has not sent JSON objects with duplicate keys.
if err = quick.CheckDuplicateKeys(string(configBytes)); err != nil { if err = quick.CheckDuplicateKeys(string(configBytes)); err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponse(w, ErrAdminConfigBadJSON, r.URL) writeErrorResponseJSON(w, ErrAdminConfigBadJSON, r.URL)
return return
} }
@ -704,7 +710,7 @@ func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Reques
err = json.Unmarshal(configBytes, &config) err = json.Unmarshal(configBytes, &config)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponse(w, toAPIErrorCode(err), r.URL) writeErrorResponseJSON(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -769,14 +775,14 @@ func (a adminAPIHandlers) UpdateCredentialsHandler(w http.ResponseWriter,
// Authenticate request // Authenticate request
adminAPIErr := checkAdminRequestAuthType(r, globalServerConfig.GetRegion()) adminAPIErr := checkAdminRequestAuthType(r, globalServerConfig.GetRegion())
if adminAPIErr != ErrNone { if adminAPIErr != ErrNone {
writeErrorResponse(w, adminAPIErr, r.URL) writeErrorResponseJSON(w, adminAPIErr, r.URL)
return return
} }
// Avoid setting new credentials when they are already passed // Avoid setting new credentials when they are already passed
// by the environment. // by the environment. Deny if WORM is enabled.
if globalIsEnvCreds { if globalIsEnvCreds || globalWORMEnabled {
writeErrorResponse(w, ErrMethodNotAllowed, r.URL) writeErrorResponseJSON(w, ErrMethodNotAllowed, r.URL)
return return
} }
@ -791,7 +797,7 @@ func (a adminAPIHandlers) UpdateCredentialsHandler(w http.ResponseWriter,
creds, err := auth.CreateCredentials(req.AccessKey, req.SecretKey) creds, err := auth.CreateCredentials(req.AccessKey, req.SecretKey)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(err), r.URL) writeErrorResponseJSON(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -811,7 +817,7 @@ func (a adminAPIHandlers) UpdateCredentialsHandler(w http.ResponseWriter,
// Update local credentials in memory. // Update local credentials in memory.
globalServerConfig.SetCredential(creds) globalServerConfig.SetCredential(creds)
if err = globalServerConfig.Save(); err != nil { if err = globalServerConfig.Save(); err != nil {
writeErrorResponse(w, ErrInternalError, r.URL) writeErrorResponseJSON(w, ErrInternalError, r.URL)
return return
} }

View File

@ -1,5 +1,5 @@
/* /*
* Minio Cloud Storage, (C) 2017 Minio, Inc. * Minio Cloud Storage, (C) 2017, 2018 Minio, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,11 +21,11 @@ import (
"fmt" "fmt"
) )
// BrowserFlag - wrapper bool type. // BoolFlag - wrapper bool type.
type BrowserFlag bool type BoolFlag bool
// String - returns string of BrowserFlag. // String - returns string of BoolFlag.
func (bf BrowserFlag) String() string { func (bf BoolFlag) String() string {
if bf { if bf {
return "on" return "on"
} }
@ -33,20 +33,20 @@ func (bf BrowserFlag) String() string {
return "off" return "off"
} }
// MarshalJSON - converts BrowserFlag into JSON data. // MarshalJSON - converts BoolFlag into JSON data.
func (bf BrowserFlag) MarshalJSON() ([]byte, error) { func (bf BoolFlag) MarshalJSON() ([]byte, error) {
return json.Marshal(bf.String()) return json.Marshal(bf.String())
} }
// UnmarshalJSON - parses given data into BrowserFlag. // UnmarshalJSON - parses given data into BoolFlag.
func (bf *BrowserFlag) UnmarshalJSON(data []byte) (err error) { func (bf *BoolFlag) UnmarshalJSON(data []byte) (err error) {
var s string var s string
if err = json.Unmarshal(data, &s); err == nil { if err = json.Unmarshal(data, &s); err == nil {
b := BrowserFlag(true) b := BoolFlag(true)
if s == "" { if s == "" {
// Empty string is treated as valid. // Empty string is treated as valid.
*bf = b *bf = b
} else if b, err = ParseBrowserFlag(s); err == nil { } else if b, err = ParseBoolFlag(s); err == nil {
*bf = b *bf = b
} }
} }
@ -54,14 +54,14 @@ func (bf *BrowserFlag) UnmarshalJSON(data []byte) (err error) {
return err return err
} }
// ParseBrowserFlag - parses string into BrowserFlag. // ParseBoolFlag - parses string into BoolFlag.
func ParseBrowserFlag(s string) (bf BrowserFlag, err error) { func ParseBoolFlag(s string) (bf BoolFlag, err error) {
if s == "on" { if s == "on" {
bf = true bf = true
} else if s == "off" { } else if s == "off" {
bf = false bf = false
} else { } else {
err = fmt.Errorf("invalid value %s for BrowserFlag", s) err = fmt.Errorf("invalid value %s for BoolFlag", s)
} }
return bf, err return bf, err

View File

@ -21,17 +21,17 @@ import (
"testing" "testing"
) )
// Test BrowserFlag.String() // Test BoolFlag.String()
func TestBrowserFlagString(t *testing.T) { func TestBoolFlagString(t *testing.T) {
var bf BrowserFlag var bf BoolFlag
testCases := []struct { testCases := []struct {
flag BrowserFlag flag BoolFlag
expectedResult string expectedResult string
}{ }{
{bf, "off"}, {bf, "off"},
{BrowserFlag(true), "on"}, {BoolFlag(true), "on"},
{BrowserFlag(false), "off"}, {BoolFlag(false), "off"},
} }
for _, testCase := range testCases { for _, testCase := range testCases {
@ -42,17 +42,17 @@ func TestBrowserFlagString(t *testing.T) {
} }
} }
// Test BrowserFlag.MarshalJSON() // Test BoolFlag.MarshalJSON()
func TestBrowserFlagMarshalJSON(t *testing.T) { func TestBoolFlagMarshalJSON(t *testing.T) {
var bf BrowserFlag var bf BoolFlag
testCases := []struct { testCases := []struct {
flag BrowserFlag flag BoolFlag
expectedResult string expectedResult string
}{ }{
{bf, `"off"`}, {bf, `"off"`},
{BrowserFlag(true), `"on"`}, {BoolFlag(true), `"on"`},
{BrowserFlag(false), `"off"`}, {BoolFlag(false), `"off"`},
} }
for _, testCase := range testCases { for _, testCase := range testCases {
@ -63,27 +63,27 @@ func TestBrowserFlagMarshalJSON(t *testing.T) {
} }
} }
// Test BrowserFlag.UnmarshalJSON() // Test BoolFlag.UnmarshalJSON()
func TestBrowserFlagUnmarshalJSON(t *testing.T) { func TestBoolFlagUnmarshalJSON(t *testing.T) {
testCases := []struct { testCases := []struct {
data []byte data []byte
expectedResult BrowserFlag expectedResult BoolFlag
expectedErr error expectedErr error
}{ }{
{[]byte(`{}`), BrowserFlag(false), errors.New("json: cannot unmarshal object into Go value of type string")}, {[]byte(`{}`), BoolFlag(false), errors.New("json: cannot unmarshal object into Go value of type string")},
{[]byte(`["on"]`), BrowserFlag(false), errors.New("json: cannot unmarshal array into Go value of type string")}, {[]byte(`["on"]`), BoolFlag(false), errors.New("json: cannot unmarshal array into Go value of type string")},
{[]byte(`"junk"`), BrowserFlag(false), errors.New("invalid value junk for BrowserFlag")}, {[]byte(`"junk"`), BoolFlag(false), errors.New("invalid value junk for BoolFlag")},
{[]byte(`"true"`), BrowserFlag(false), errors.New("invalid value true for BrowserFlag")}, {[]byte(`"true"`), BoolFlag(false), errors.New("invalid value true for BoolFlag")},
{[]byte(`"false"`), BrowserFlag(false), errors.New("invalid value false for BrowserFlag")}, {[]byte(`"false"`), BoolFlag(false), errors.New("invalid value false for BoolFlag")},
{[]byte(`"ON"`), BrowserFlag(false), errors.New("invalid value ON for BrowserFlag")}, {[]byte(`"ON"`), BoolFlag(false), errors.New("invalid value ON for BoolFlag")},
{[]byte(`"OFF"`), BrowserFlag(false), errors.New("invalid value OFF for BrowserFlag")}, {[]byte(`"OFF"`), BoolFlag(false), errors.New("invalid value OFF for BoolFlag")},
{[]byte(`""`), BrowserFlag(true), nil}, {[]byte(`""`), BoolFlag(true), nil},
{[]byte(`"on"`), BrowserFlag(true), nil}, {[]byte(`"on"`), BoolFlag(true), nil},
{[]byte(`"off"`), BrowserFlag(false), nil}, {[]byte(`"off"`), BoolFlag(false), nil},
} }
for _, testCase := range testCases { for _, testCase := range testCases {
var flag BrowserFlag var flag BoolFlag
err := (&flag).UnmarshalJSON(testCase.data) err := (&flag).UnmarshalJSON(testCase.data)
if testCase.expectedErr == nil { if testCase.expectedErr == nil {
if err != nil { if err != nil {
@ -101,25 +101,25 @@ func TestBrowserFlagUnmarshalJSON(t *testing.T) {
} }
} }
// Test ParseBrowserFlag() // Test ParseBoolFlag()
func TestParseBrowserFlag(t *testing.T) { func TestParseBoolFlag(t *testing.T) {
testCases := []struct { testCases := []struct {
flagStr string flagStr string
expectedResult BrowserFlag expectedResult BoolFlag
expectedErr error expectedErr error
}{ }{
{"", BrowserFlag(false), errors.New("invalid value for BrowserFlag")}, {"", BoolFlag(false), errors.New("invalid value for BoolFlag")},
{"junk", BrowserFlag(false), errors.New("invalid value junk for BrowserFlag")}, {"junk", BoolFlag(false), errors.New("invalid value junk for BoolFlag")},
{"true", BrowserFlag(false), errors.New("invalid value true for BrowserFlag")}, {"true", BoolFlag(false), errors.New("invalid value true for BoolFlag")},
{"false", BrowserFlag(false), errors.New("invalid value false for BrowserFlag")}, {"false", BoolFlag(false), errors.New("invalid value false for BoolFlag")},
{"ON", BrowserFlag(false), errors.New("invalid value ON for BrowserFlag")}, {"ON", BoolFlag(false), errors.New("invalid value ON for BoolFlag")},
{"OFF", BrowserFlag(false), errors.New("invalid value OFF for BrowserFlag")}, {"OFF", BoolFlag(false), errors.New("invalid value OFF for BoolFlag")},
{"on", BrowserFlag(true), nil}, {"on", BoolFlag(true), nil},
{"off", BrowserFlag(false), nil}, {"off", BoolFlag(false), nil},
} }
for _, testCase := range testCases { for _, testCase := range testCases {
bf, err := ParseBrowserFlag(testCase.flagStr) bf, err := ParseBoolFlag(testCase.flagStr)
if testCase.expectedErr == nil { if testCase.expectedErr == nil {
if err != nil { if err != nil {
t.Fatalf("error: expected = <nil>, got = %v", err) t.Fatalf("error: expected = <nil>, got = %v", err)

View File

@ -105,7 +105,7 @@ func handleCommonEnvVars() {
} }
if browser := os.Getenv("MINIO_BROWSER"); browser != "" { if browser := os.Getenv("MINIO_BROWSER"); browser != "" {
browserFlag, err := ParseBrowserFlag(browser) browserFlag, err := ParseBoolFlag(browser)
if err != nil { if err != nil {
logger.Fatal(uiErrInvalidBrowserValue(nil).Msg("Unknown value `%s`", browser), "Unable to validate MINIO_BROWSER environment variable") logger.Fatal(uiErrInvalidBrowserValue(nil).Msg("Unknown value `%s`", browser), "Unable to validate MINIO_BROWSER environment variable")
} }
@ -123,10 +123,7 @@ func handleCommonEnvVars() {
logger.FatalIf(err, "error opening file %s", traceFile) logger.FatalIf(err, "error opening file %s", traceFile)
} }
globalDomainName = os.Getenv("MINIO_DOMAIN") globalDomainName, globalIsEnvDomainName = os.LookupEnv("MINIO_DOMAIN")
if globalDomainName != "" {
globalIsEnvDomainName = true
}
if drives := os.Getenv("MINIO_CACHE_DRIVES"); drives != "" { if drives := os.Getenv("MINIO_CACHE_DRIVES"); drives != "" {
driveList, err := parseCacheDrives(strings.Split(drives, cacheEnvDelimiter)) driveList, err := parseCacheDrives(strings.Split(drives, cacheEnvDelimiter))
@ -189,5 +186,15 @@ func handleCommonEnvVars() {
} }
// Get WORM environment variable. // Get WORM environment variable.
globalWORMEnabled = strings.EqualFold(os.Getenv("MINIO_WORM"), "on") if worm := os.Getenv("MINIO_WORM"); worm != "" {
wormFlag, err := ParseBoolFlag(worm)
if err != nil {
logger.Fatal(uiErrInvalidWormValue(nil).Msg("Unknown value `%s`", worm), "Unable to validate MINIO_WORM environment variable")
}
// worm Envs are set globally, this does not represent
// if worm is turned off or on.
globalIsEnvWORM = true
globalWORMEnabled = bool(wormFlag)
}
} }

View File

@ -39,9 +39,9 @@ import (
// 6. Make changes in config-current_test.go for any test change // 6. Make changes in config-current_test.go for any test change
// Config version // Config version
const serverConfigVersion = "24" const serverConfigVersion = "25"
type serverConfig = serverConfigV24 type serverConfig = serverConfigV25
var ( var (
// globalServerConfig server config. // globalServerConfig server config.
@ -85,7 +85,13 @@ func (s *serverConfig) GetCredential() auth.Credentials {
// SetBrowser set if browser is enabled. // SetBrowser set if browser is enabled.
func (s *serverConfig) SetBrowser(b bool) { func (s *serverConfig) SetBrowser(b bool) {
// Set the new value. // Set the new value.
s.Browser = BrowserFlag(b) s.Browser = BoolFlag(b)
}
// SetWorm set if worm is enabled.
func (s *serverConfig) SetWorm(b bool) {
// Set the new value.
s.Worm = BoolFlag(b)
} }
func (s *serverConfig) SetStorageClass(standardClass, rrsClass storageClass) { func (s *serverConfig) SetStorageClass(standardClass, rrsClass storageClass) {
@ -99,11 +105,16 @@ func (s *serverConfig) GetStorageClass() (storageClass, storageClass) {
return s.StorageClass.Standard, s.StorageClass.RRS return s.StorageClass.Standard, s.StorageClass.RRS
} }
// GetCredentials get current credentials. // GetBrowser get current credentials.
func (s *serverConfig) GetBrowser() bool { func (s *serverConfig) GetBrowser() bool {
return bool(s.Browser) return bool(s.Browser)
} }
// GetWorm get current credentials.
func (s *serverConfig) GetWorm() bool {
return bool(s.Worm)
}
// SetCacheConfig sets the current cache config // SetCacheConfig sets the current cache config
func (s *serverConfig) SetCacheConfig(drives, exclude []string, expiry int) { func (s *serverConfig) SetCacheConfig(drives, exclude []string, expiry int) {
s.Cache.Drives = drives s.Cache.Drives = drives
@ -230,6 +241,10 @@ func newConfig() error {
srvCfg.SetBrowser(globalIsBrowserEnabled) srvCfg.SetBrowser(globalIsBrowserEnabled)
} }
if globalIsEnvWORM {
srvCfg.SetWorm(globalWORMEnabled)
}
if globalIsEnvRegion { if globalIsEnvRegion {
srvCfg.SetRegion(globalServerRegion) srvCfg.SetRegion(globalServerRegion)
} }
@ -324,6 +339,9 @@ func loadConfig() error {
if !globalIsEnvBrowser { if !globalIsEnvBrowser {
globalIsBrowserEnabled = globalServerConfig.GetBrowser() globalIsBrowserEnabled = globalServerConfig.GetBrowser()
} }
if !globalIsEnvWORM {
globalWORMEnabled = globalServerConfig.GetWorm()
}
if !globalIsEnvRegion { if !globalIsEnvRegion {
globalServerRegion = globalServerConfig.GetRegion() globalServerRegion = globalServerConfig.GetRegion()
} }

View File

@ -177,6 +177,11 @@ func migrateConfig() error {
return err return err
} }
fallthrough fallthrough
case "24":
if err = migrateV24ToV25(); err != nil {
return err
}
fallthrough
case serverConfigVersion: case serverConfigVersion:
// No migration needed. this always points to current version. // No migration needed. this always points to current version.
err = nil err = nil
@ -2069,3 +2074,121 @@ func migrateV23ToV24() error {
logger.Info(configMigrateMSGTemplate, configFile, cv23.Version, srvConfig.Version) logger.Info(configMigrateMSGTemplate, configFile, cv23.Version, srvConfig.Version)
return nil return nil
} }
func migrateV24ToV25() error {
configFile := getConfigFile()
cv24 := &serverConfigV24{}
_, err := quick.Load(configFile, cv24)
if os.IsNotExist(err) {
return nil
} else if err != nil {
return fmt.Errorf("Unable to load config version 24. %v", err)
}
if cv24.Version != "24" {
return nil
}
// Copy over fields from V24 into V25 config struct
srvConfig := &serverConfigV25{
Notify: notifier{},
}
srvConfig.Version = "25"
srvConfig.Credential = cv24.Credential
srvConfig.Region = cv24.Region
if srvConfig.Region == "" {
// Region needs to be set for AWS Signature Version 4.
srvConfig.Region = globalMinioDefaultRegion
}
if len(cv24.Notify.AMQP) == 0 {
srvConfig.Notify.AMQP = make(map[string]target.AMQPArgs)
srvConfig.Notify.AMQP["1"] = target.AMQPArgs{}
} else {
srvConfig.Notify.AMQP = cv24.Notify.AMQP
}
if len(cv24.Notify.Elasticsearch) == 0 {
srvConfig.Notify.Elasticsearch = make(map[string]target.ElasticsearchArgs)
srvConfig.Notify.Elasticsearch["1"] = target.ElasticsearchArgs{
Format: event.NamespaceFormat,
}
} else {
srvConfig.Notify.Elasticsearch = cv24.Notify.Elasticsearch
}
if len(cv24.Notify.Redis) == 0 {
srvConfig.Notify.Redis = make(map[string]target.RedisArgs)
srvConfig.Notify.Redis["1"] = target.RedisArgs{
Format: event.NamespaceFormat,
}
} else {
srvConfig.Notify.Redis = cv24.Notify.Redis
}
if len(cv24.Notify.PostgreSQL) == 0 {
srvConfig.Notify.PostgreSQL = make(map[string]target.PostgreSQLArgs)
srvConfig.Notify.PostgreSQL["1"] = target.PostgreSQLArgs{
Format: event.NamespaceFormat,
}
} else {
srvConfig.Notify.PostgreSQL = cv24.Notify.PostgreSQL
}
if len(cv24.Notify.Kafka) == 0 {
srvConfig.Notify.Kafka = make(map[string]target.KafkaArgs)
srvConfig.Notify.Kafka["1"] = target.KafkaArgs{}
} else {
srvConfig.Notify.Kafka = cv24.Notify.Kafka
}
if len(cv24.Notify.NATS) == 0 {
srvConfig.Notify.NATS = make(map[string]target.NATSArgs)
srvConfig.Notify.NATS["1"] = target.NATSArgs{}
} else {
srvConfig.Notify.NATS = cv24.Notify.NATS
}
if len(cv24.Notify.Webhook) == 0 {
srvConfig.Notify.Webhook = make(map[string]target.WebhookArgs)
srvConfig.Notify.Webhook["1"] = target.WebhookArgs{}
} else {
srvConfig.Notify.Webhook = cv24.Notify.Webhook
}
if len(cv24.Notify.MySQL) == 0 {
srvConfig.Notify.MySQL = make(map[string]target.MySQLArgs)
srvConfig.Notify.MySQL["1"] = target.MySQLArgs{
Format: event.NamespaceFormat,
}
} else {
srvConfig.Notify.MySQL = cv24.Notify.MySQL
}
if len(cv24.Notify.MQTT) == 0 {
srvConfig.Notify.MQTT = make(map[string]target.MQTTArgs)
srvConfig.Notify.MQTT["1"] = target.MQTTArgs{}
} else {
srvConfig.Notify.MQTT = cv24.Notify.MQTT
}
// Load browser config from existing config in the file.
srvConfig.Browser = cv24.Browser
// New field should be turned-off by default.
srvConfig.Worm = false // cv25.Worm should be used here
// for the next migration from v25 to v26 to persist
// local config value.
// Load domain config from existing config in the file.
srvConfig.Domain = cv24.Domain
// Load storage class config from existing storage class config in the file.
srvConfig.StorageClass.RRS = cv24.StorageClass.RRS
srvConfig.StorageClass.Standard = cv24.StorageClass.Standard
// Load cache config from existing cache config in the file.
srvConfig.Cache.Drives = cv24.Cache.Drives
srvConfig.Cache.Exclude = cv24.Cache.Exclude
srvConfig.Cache.Expiry = cv24.Cache.Expiry
if err = quick.Save(configFile, srvConfig); err != nil {
return fmt.Errorf("Failed to migrate config from %s to %s. %v", cv24.Version, srvConfig.Version, err)
}
logger.Info(configMigrateMSGTemplate, configFile, cv24.Version, srvConfig.Version)
return nil
}

View File

@ -405,7 +405,7 @@ type serverConfigV14 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
// Additional error logging configuration. // Additional error logging configuration.
Logger *loggerV7 `json:"logger"` Logger *loggerV7 `json:"logger"`
@ -422,7 +422,7 @@ type serverConfigV15 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
// Additional error logging configuration. // Additional error logging configuration.
Logger *loggerV7 `json:"logger"` Logger *loggerV7 `json:"logger"`
@ -460,7 +460,7 @@ type serverConfigV16 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
// Additional error logging configuration. // Additional error logging configuration.
Logger *loggers `json:"logger"` Logger *loggers `json:"logger"`
@ -479,7 +479,7 @@ type serverConfigV17 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
// Additional error logging configuration. // Additional error logging configuration.
Logger *loggers `json:"logger"` Logger *loggers `json:"logger"`
@ -498,7 +498,7 @@ type serverConfigV18 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
// Additional error logging configuration. // Additional error logging configuration.
Logger *loggers `json:"logger"` Logger *loggers `json:"logger"`
@ -516,7 +516,7 @@ type serverConfigV19 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
// Additional error logging configuration. // Additional error logging configuration.
Logger *loggers `json:"logger"` Logger *loggers `json:"logger"`
@ -534,7 +534,7 @@ type serverConfigV20 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
Domain string `json:"domain"` Domain string `json:"domain"`
// Additional error logging configuration. // Additional error logging configuration.
@ -552,7 +552,7 @@ type serverConfigV21 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
Domain string `json:"domain"` Domain string `json:"domain"`
// Notification queue configuration. // Notification queue configuration.
@ -570,7 +570,7 @@ type serverConfigV22 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
Domain string `json:"domain"` Domain string `json:"domain"`
// Storage class configuration // Storage class configuration
@ -590,7 +590,7 @@ type serverConfigV23 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
Domain string `json:"domain"` Domain string `json:"domain"`
// Storage class configuration // Storage class configuration
@ -614,7 +614,32 @@ type serverConfigV24 struct {
// S3 API configuration. // S3 API configuration.
Credential auth.Credentials `json:"credential"` Credential auth.Credentials `json:"credential"`
Region string `json:"region"` Region string `json:"region"`
Browser BrowserFlag `json:"browser"` Browser BoolFlag `json:"browser"`
Domain string `json:"domain"`
// Storage class configuration
StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration
Cache CacheConfig `json:"cache"`
// Notification queue configuration.
Notify notifier `json:"notify"`
}
// serverConfigV25 is just like version '24', stores additionally
// worm variable.
//
// IMPORTANT NOTE: When updating this struct make sure that
// serverConfig.ConfigDiff() is updated as necessary.
type serverConfigV25 struct {
Version string `json:"version"`
// S3 API configuration.
Credential auth.Credentials `json:"credential"`
Region string `json:"region"`
Browser BoolFlag `json:"browser"`
Worm BoolFlag `json:"worm"`
Domain string `json:"domain"` Domain string `json:"domain"`
// Storage class configuration // Storage class configuration

View File

@ -174,6 +174,7 @@ var (
// Set to store standard storage class // Set to store standard storage class
globalStandardStorageClass storageClass globalStandardStorageClass storageClass
globalIsEnvWORM bool
globalWORMEnabled bool globalWORMEnabled bool
// Is Disk Caching set up // Is Disk Caching set up
@ -217,6 +218,7 @@ func getGlobalInfo() (globalInfo map[string]interface{}) {
"isDistXL": globalIsDistXL, "isDistXL": globalIsDistXL,
"isXL": globalIsXL, "isXL": globalIsXL,
"isBrowserEnabled": globalIsBrowserEnabled, "isBrowserEnabled": globalIsBrowserEnabled,
"isWorm": globalWORMEnabled,
"isEnvBrowser": globalIsEnvBrowser, "isEnvBrowser": globalIsEnvBrowser,
"isEnvCreds": globalIsEnvCreds, "isEnvCreds": globalIsEnvCreds,
"isEnvRegion": globalIsEnvRegion, "isEnvRegion": globalIsEnvRegion,

View File

@ -82,7 +82,7 @@ ENVIRONMENT VARIABLES:
MINIO_CACHE_DRIVES: List of mounted drives or directories delimited by ";". MINIO_CACHE_DRIVES: List of mounted drives or directories delimited by ";".
MINIO_CACHE_EXCLUDE: List of cache exclusion patterns delimited by ";". MINIO_CACHE_EXCLUDE: List of cache exclusion patterns delimited by ";".
MINIO_CACHE_EXPIRY: Cache expiry duration in days. MINIO_CACHE_EXPIRY: Cache expiry duration in days.
DOMAIN: DOMAIN:
MINIO_DOMAIN: To enable virtual-host-style requests, set this value to Minio host domain name. MINIO_DOMAIN: To enable virtual-host-style requests, set this value to Minio host domain name.

View File

@ -17,7 +17,6 @@
package cmd package cmd
import ( import (
"context"
"io" "io"
"path" "path"
@ -223,7 +222,9 @@ func registerStorageRPCRouters(router *mux.Router, endpoints EndpointList) {
for _, endpoint := range endpoints { for _, endpoint := range endpoints {
if endpoint.IsLocal { if endpoint.IsLocal {
rpcServer, err := NewStorageRPCServer(endpoint.Path) rpcServer, err := NewStorageRPCServer(endpoint.Path)
logger.CriticalIf(context.Background(), err) if err != nil {
logger.Fatal(uiErrUnableToWriteInBackend(err), "Unable to configure one of server's RPC services")
}
subrouter := router.PathPrefix(minioReservedBucketPath).Subrouter() subrouter := router.PathPrefix(minioReservedBucketPath).Subrouter()
subrouter.Path(path.Join(storageServiceSubPath, endpoint.Path)).Handler(rpcServer) subrouter.Path(path.Join(storageServiceSubPath, endpoint.Path)).Handler(rpcServer)
} }

View File

@ -23,6 +23,9 @@ import (
// errInvalidArgument means that input argument is invalid. // errInvalidArgument means that input argument is invalid.
var errInvalidArgument = errors.New("Invalid arguments specified") var errInvalidArgument = errors.New("Invalid arguments specified")
// errMethodNotAllowed means that method is not allowed.
var errMethodNotAllowed = errors.New("Method not allowed")
// errSignatureMismatch means signature did not match. // errSignatureMismatch means signature did not match.
var errSignatureMismatch = errors.New("Signature does not match") var errSignatureMismatch = errors.New("Signature does not match")

View File

@ -29,6 +29,12 @@ var (
"Browser can only accept `on` and `off` values. To disable web browser access, set this value to `off`", "Browser can only accept `on` and `off` values. To disable web browser access, set this value to `off`",
) )
uiErrInvalidWormValue = newUIErrFn(
"Invalid WORM value",
"Please check the passed value",
"WORM can only accept `on` and `off` values. To enable WORM, set this value to `on`",
)
uiErrInvalidCacheDrivesValue = newUIErrFn( uiErrInvalidCacheDrivesValue = newUIErrFn(
"Invalid cache drive value", "Invalid cache drive value",
"Please check the value in this ENV variable", "Please check the value in this ENV variable",

View File

@ -353,6 +353,13 @@ next:
for _, objectName := range args.Objects { for _, objectName := range args.Objects {
// If not a directory, remove the object. // If not a directory, remove the object.
if !hasSuffix(objectName, slashSeparator) && objectName != "" { if !hasSuffix(objectName, slashSeparator) && objectName != "" {
// Deny if WORM is enabled
if globalWORMEnabled {
if _, err = objectAPI.GetObjectInfo(context.Background(), args.BucketName, objectName); err == nil {
return toJSONError(errMethodNotAllowed)
}
}
if err = deleteObject(nil, objectAPI, web.CacheAPI(), args.BucketName, objectName, r); err != nil { if err = deleteObject(nil, objectAPI, web.CacheAPI(), args.BucketName, objectName, r); err != nil {
break next break next
} }
@ -457,7 +464,7 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se
} }
// If creds are set through ENV disallow changing credentials. // If creds are set through ENV disallow changing credentials.
if globalIsEnvCreds { if globalIsEnvCreds || globalWORMEnabled {
return toJSONError(errChangeCredNotAllowed) return toJSONError(errChangeCredNotAllowed)
} }
@ -594,6 +601,14 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
return return
} }
// Deny if WORM is enabled
if globalWORMEnabled {
if _, err = objectAPI.GetObjectInfo(context.Background(), bucket, object); err == nil {
writeWebErrorResponse(w, errMethodNotAllowed)
return
}
}
objInfo, err := putObject(context.Background(), bucket, object, hashReader, metadata) objInfo, err := putObject(context.Background(), bucket, object, hashReader, metadata)
if err != nil { if err != nil {
writeWebErrorResponse(w, err) writeWebErrorResponse(w, err)
@ -1103,7 +1118,10 @@ func toWebAPIError(err error) APIError {
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
Description: err.Error(), Description: err.Error(),
} }
} else if err == errMethodNotAllowed {
return getAPIError(ErrMethodNotAllowed)
} }
// Convert error type to api error code. // Convert error type to api error code.
switch err.(type) { switch err.(type) {
case StorageFull: case StorageFull:

View File

@ -69,6 +69,18 @@ export MINIO_BROWSER=off
minio server /data minio server /data
``` ```
#### Worm
|Field|Type|Description|
|:---|:---|:---|
|``worm``| _string_ | Enable this to turn on Write-Once-Read-Many. By default it is set to `off`. You may override this field with ``MINIO_WORM`` environment variable.|
Example:
```sh
export MINIO_WORM=on
minio server /data
```
### Domain ### Domain
|Field|Type|Description| |Field|Type|Description|
|:---|:---|:---| |:---|:---|:---|