mirror of
https://github.com/pgsty/minio.git
synced 2026-08-10 08:13:28 +03:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2dc917e87f | |||
| 0a284a1a10 | |||
| 5c8339e1e8 | |||
| fd37418da2 | |||
| bbfea29c2b | |||
| aa703dc903 | |||
| 8cd80fec8c | |||
| 780882efcf | |||
| c5636143c6 | |||
| ba6218b354 | |||
| 8e32de3ba9 | |||
| e37508fb8f | |||
| b46a717425 | |||
| 7926df0b80 | |||
| 557df666fd | |||
| f91b257f50 | |||
| 28a2d1eb3d | |||
| a0ae1489e5 | |||
| edfb310a59 | |||
| a2312028b9 |
@@ -1001,7 +1001,7 @@ func (a adminAPIHandlers) HealHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Message: hr.errBody,
|
||||
Resource: r.URL.Path,
|
||||
RequestID: w.Header().Get(xhttp.AmzRequestID),
|
||||
HostID: globalDeploymentID,
|
||||
HostID: globalDeploymentID(),
|
||||
})
|
||||
}
|
||||
if !started {
|
||||
@@ -1982,7 +1982,7 @@ func getServerInfo(ctx context.Context, poolsInfoEnabled bool, r *http.Request)
|
||||
Domain: domain,
|
||||
Region: globalSite.Region,
|
||||
SQSARN: globalEventNotifier.GetARNList(false),
|
||||
DeploymentID: globalDeploymentID,
|
||||
DeploymentID: globalDeploymentID(),
|
||||
Buckets: buckets,
|
||||
Objects: objects,
|
||||
Versions: versions,
|
||||
@@ -2392,7 +2392,7 @@ func (a adminAPIHandlers) HealthInfoHandler(w http.ResponseWriter, r *http.Reque
|
||||
Version: madmin.HealthInfoVersion,
|
||||
Minio: madmin.MinioHealthInfo{
|
||||
Info: madmin.MinioInfo{
|
||||
DeploymentID: globalDeploymentID,
|
||||
DeploymentID: globalDeploymentID(),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -2690,7 +2690,7 @@ func getClusterMetaInfo(ctx context.Context) []byte {
|
||||
ci.Info.NoOfBuckets = dataUsageInfo.BucketsCount
|
||||
ci.Info.NoOfObjects = dataUsageInfo.ObjectsTotalCount
|
||||
|
||||
ci.DeploymentID = globalDeploymentID
|
||||
ci.DeploymentID = globalDeploymentID()
|
||||
ci.ClusterName = fmt.Sprintf("%d-servers-%d-disks-%s", ci.Info.NoOfServers, ci.Info.NoOfDrives, ci.Info.MinioVersion)
|
||||
|
||||
select {
|
||||
|
||||
@@ -310,6 +310,7 @@ const (
|
||||
ErrSiteReplicationBucketMetaError
|
||||
ErrSiteReplicationIAMError
|
||||
ErrSiteReplicationConfigMissing
|
||||
ErrSiteReplicationIAMConfigMismatch
|
||||
|
||||
// Pool rebalance errors
|
||||
ErrAdminRebalanceAlreadyStarted
|
||||
@@ -1512,6 +1513,11 @@ var errorCodes = errorCodeMap{
|
||||
Description: "Site not found in site replication configuration",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrSiteReplicationIAMConfigMismatch: {
|
||||
Code: "XMinioSiteReplicationIAMConfigMismatch",
|
||||
Description: "IAM configuration mismatch between sites",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrAdminRebalanceAlreadyStarted: {
|
||||
Code: "XMinioAdminRebalanceAlreadyStarted",
|
||||
Description: "Pool rebalance is already started",
|
||||
|
||||
+1
-1
@@ -978,7 +978,7 @@ func writeCustomErrorResponseJSON(ctx context.Context, w http.ResponseWriter, er
|
||||
BucketName: reqInfo.BucketName,
|
||||
Key: reqInfo.ObjectName,
|
||||
RequestID: w.Header().Get(xhttp.AmzRequestID),
|
||||
HostID: globalDeploymentID,
|
||||
HostID: globalDeploymentID(),
|
||||
}
|
||||
encodedErrorResponse := encodeResponseJSON(errorResponse)
|
||||
writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeJSON)
|
||||
|
||||
+111
-110
File diff suppressed because one or more lines are too long
@@ -444,7 +444,7 @@ func genTransitionObjName(bucket string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
us := u.String()
|
||||
obj := fmt.Sprintf("%s/%s/%s/%s/%s", globalDeploymentID, bucket, us[0:2], us[2:4], us)
|
||||
obj := fmt.Sprintf("%s/%s/%s/%s/%s", globalDeploymentID(), bucket, us[0:2], us[2:4], us)
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -131,7 +131,7 @@ func performCallhome(ctx context.Context) {
|
||||
Version: madmin.HealthInfoVersion,
|
||||
Minio: madmin.MinioHealthInfo{
|
||||
Info: madmin.MinioInfo{
|
||||
DeploymentID: globalDeploymentID,
|
||||
DeploymentID: globalDeploymentID(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
+3
-5
@@ -122,8 +122,8 @@ func init() {
|
||||
const consolePrefix = "CONSOLE_"
|
||||
|
||||
func minioConfigToConsoleFeatures() {
|
||||
os.Setenv("CONSOLE_PBKDF_SALT", globalDeploymentID)
|
||||
os.Setenv("CONSOLE_PBKDF_PASSPHRASE", globalDeploymentID)
|
||||
os.Setenv("CONSOLE_PBKDF_SALT", globalDeploymentID())
|
||||
os.Setenv("CONSOLE_PBKDF_PASSPHRASE", globalDeploymentID())
|
||||
if globalMinioEndpoint != "" {
|
||||
os.Setenv("CONSOLE_MINIO_SERVER", globalMinioEndpoint)
|
||||
} else {
|
||||
@@ -191,7 +191,7 @@ func buildOpenIDConsoleConfig() consoleoauth2.OpenIDPCfg {
|
||||
DisplayName: cfg.DisplayName,
|
||||
ClientID: cfg.ClientID,
|
||||
ClientSecret: cfg.ClientSecret,
|
||||
HMACSalt: globalDeploymentID,
|
||||
HMACSalt: globalDeploymentID(),
|
||||
HMACPassphrase: cfg.ClientID,
|
||||
Scopes: strings.Join(cfg.DiscoveryDoc.ScopesSupported, ","),
|
||||
Userinfo: cfg.ClaimUserinfo,
|
||||
@@ -619,8 +619,6 @@ func loadEnvVarsFromFiles() {
|
||||
}
|
||||
|
||||
func handleCommonEnvVars() {
|
||||
loadEnvVarsFromFiles()
|
||||
|
||||
var err error
|
||||
globalBrowserEnabled, err = config.ParseBool(env.Get(config.EnvBrowser, config.EnableOn))
|
||||
if err != nil {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,336 +0,0 @@
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test if config v1 is purged
|
||||
func TestServerConfigMigrateV1(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
objLayer, fsDir, err := prepareFS(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(fsDir)
|
||||
err = newTestConfig(globalMinioDefaultRegion, objLayer)
|
||||
if err != nil {
|
||||
t.Fatalf("Init Test config failed")
|
||||
}
|
||||
rootPath := t.TempDir()
|
||||
globalConfigDir = &ConfigDir{path: rootPath}
|
||||
|
||||
globalObjLayerMutex.Lock()
|
||||
globalObjectAPI = objLayer
|
||||
globalObjLayerMutex.Unlock()
|
||||
|
||||
// Create a V1 config json file and store it
|
||||
configJSON := "{ \"version\":\"1\", \"accessKeyId\":\"abcde\", \"secretAccessKey\":\"abcdefgh\"}"
|
||||
configPath := rootPath + "/fsUsers.json"
|
||||
if err := os.WriteFile(configPath, []byte(configJSON), 0o644); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
// Fire a migrateConfig()
|
||||
if err := migrateConfig(); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
// Check if config v1 is removed from filesystem
|
||||
if _, err := os.Stat(configPath); err == nil || !osIsNotExist(err) {
|
||||
t.Fatal("Config V1 file is not purged")
|
||||
}
|
||||
|
||||
// Initialize server config and check again if everything is fine
|
||||
if err := loadConfig(objLayer, nil); err != nil {
|
||||
t.Fatalf("Unable to initialize from updated config file %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test if all migrate code returns nil when config file does not
|
||||
// exist
|
||||
func TestServerConfigMigrateInexistentConfig(t *testing.T) {
|
||||
globalConfigDir = &ConfigDir{path: t.TempDir()}
|
||||
|
||||
if err := migrateV2ToV3(); err != nil {
|
||||
t.Fatal("migrate v2 to v3 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV3ToV4(); err != nil {
|
||||
t.Fatal("migrate v3 to v4 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV4ToV5(); err != nil {
|
||||
t.Fatal("migrate v4 to v5 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV5ToV6(); err != nil {
|
||||
t.Fatal("migrate v5 to v6 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV6ToV7(); err != nil {
|
||||
t.Fatal("migrate v6 to v7 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV7ToV8(); err != nil {
|
||||
t.Fatal("migrate v7 to v8 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV8ToV9(); err != nil {
|
||||
t.Fatal("migrate v8 to v9 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV9ToV10(); err != nil {
|
||||
t.Fatal("migrate v9 to v10 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV10ToV11(); err != nil {
|
||||
t.Fatal("migrate v10 to v11 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV11ToV12(); err != nil {
|
||||
t.Fatal("migrate v11 to v12 should succeed when no config file is found")
|
||||
}
|
||||
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")
|
||||
}
|
||||
if err := migrateV14ToV15(); err != nil {
|
||||
t.Fatal("migrate v14 to v15 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV15ToV16(); err != nil {
|
||||
t.Fatal("migrate v15 to v16 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV16ToV17(); err != nil {
|
||||
t.Fatal("migrate v16 to v17 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV17ToV18(); err != nil {
|
||||
t.Fatal("migrate v17 to v18 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV18ToV19(); err != nil {
|
||||
t.Fatal("migrate v18 to v19 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV19ToV20(); err != nil {
|
||||
t.Fatal("migrate v19 to v20 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV20ToV21(); err != nil {
|
||||
t.Fatal("migrate v20 to v21 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV21ToV22(); err != nil {
|
||||
t.Fatal("migrate v21 to v22 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV22ToV23(); err != nil {
|
||||
t.Fatal("migrate v22 to v23 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV23ToV24(); err != nil {
|
||||
t.Fatal("migrate v23 to v24 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV24ToV25(); err != nil {
|
||||
t.Fatal("migrate v24 to v25 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV25ToV26(); err != nil {
|
||||
t.Fatal("migrate v25 to v26 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV26ToV27(); err != nil {
|
||||
t.Fatal("migrate v26 to v27 should succeed when no config file is found")
|
||||
}
|
||||
if err := migrateV27ToV28(); err != nil {
|
||||
t.Fatal("migrate v27 to v28 should succeed when no config file is found")
|
||||
}
|
||||
}
|
||||
|
||||
// Test if a config migration from v2 to v33 is successfully done
|
||||
func TestServerConfigMigrateV2toV33(t *testing.T) {
|
||||
rootPath := t.TempDir()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
globalConfigDir = &ConfigDir{path: rootPath}
|
||||
|
||||
objLayer, fsDir, err := prepareFS(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(fsDir)
|
||||
|
||||
configPath := rootPath + SlashSeparator + minioConfigFile
|
||||
|
||||
// Create a corrupted config file
|
||||
if err := os.WriteFile(configPath, []byte("{ \"version\":\"2\","), 0o644); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
// Fire a migrateConfig()
|
||||
if err := migrateConfig(); err == nil {
|
||||
t.Fatal("migration should fail with corrupted config file")
|
||||
}
|
||||
|
||||
accessKey := "accessfoo"
|
||||
secretKey := "secretfoo"
|
||||
|
||||
// Create a V2 config json file and store it
|
||||
configJSON := "{ \"version\":\"2\", \"credentials\": {\"accessKeyId\":\"" + accessKey + "\", \"secretAccessKey\":\"" + secretKey + "\", \"region\":\"us-east-1\"}, \"mongoLogger\":{\"addr\":\"127.0.0.1:3543\", \"db\":\"foodb\", \"collection\":\"foo\"}, \"syslogLogger\":{\"network\":\"127.0.0.1:543\", \"addr\":\"addr\"}, \"fileLogger\":{\"filename\":\"log.out\"}}"
|
||||
if err := os.WriteFile(configPath, []byte(configJSON), 0o644); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
// Fire a migrateConfig()
|
||||
if err := migrateConfig(); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
if err := migrateConfigToMinioSys(objLayer); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
srvCfg, err := readConfigWithoutMigrate(context.Background(), objLayer)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
if err = saveServerConfig(GlobalContext, objLayer, srvCfg); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
// Initialize server config and check again if everything is fine
|
||||
if err := loadConfig(objLayer, nil); err != nil {
|
||||
t.Fatalf("Unable to initialize from updated config file %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test if all migrate code returns error with corrupted config files
|
||||
func TestServerConfigMigrateFaultyConfig(t *testing.T) {
|
||||
rootPath := t.TempDir()
|
||||
|
||||
globalConfigDir = &ConfigDir{path: rootPath}
|
||||
configPath := rootPath + SlashSeparator + minioConfigFile
|
||||
|
||||
// Create a corrupted config file
|
||||
if err := os.WriteFile(configPath, []byte("{ \"version\":\"2\", \"test\":"), 0o644); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
// Test different migrate versions and be sure they are returning an error
|
||||
if err := migrateV2ToV3(); err == nil {
|
||||
t.Fatal("migrateConfigV2ToV3() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV3ToV4(); err == nil {
|
||||
t.Fatal("migrateConfigV3ToV4() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV4ToV5(); err == nil {
|
||||
t.Fatal("migrateConfigV4ToV5() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV5ToV6(); err == nil {
|
||||
t.Fatal("migrateConfigV5ToV6() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV6ToV7(); err == nil {
|
||||
t.Fatal("migrateConfigV6ToV7() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV7ToV8(); err == nil {
|
||||
t.Fatal("migrateConfigV7ToV8() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV8ToV9(); err == nil {
|
||||
t.Fatal("migrateConfigV8ToV9() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV9ToV10(); err == nil {
|
||||
t.Fatal("migrateConfigV9ToV10() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV10ToV11(); err == nil {
|
||||
t.Fatal("migrateConfigV10ToV11() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV11ToV12(); err == nil {
|
||||
t.Fatal("migrateConfigV11ToV12() should fail with a corrupted json")
|
||||
}
|
||||
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")
|
||||
}
|
||||
if err := migrateV14ToV15(); err == nil {
|
||||
t.Fatal("migrateConfigV14ToV15() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV15ToV16(); err == nil {
|
||||
t.Fatal("migrateConfigV15ToV16() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV16ToV17(); err == nil {
|
||||
t.Fatal("migrateConfigV16ToV17() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV17ToV18(); err == nil {
|
||||
t.Fatal("migrateConfigV17ToV18() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV18ToV19(); err == nil {
|
||||
t.Fatal("migrateConfigV18ToV19() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV19ToV20(); err == nil {
|
||||
t.Fatal("migrateConfigV19ToV20() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV20ToV21(); err == nil {
|
||||
t.Fatal("migrateConfigV20ToV21() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV21ToV22(); err == nil {
|
||||
t.Fatal("migrateConfigV21ToV22() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV22ToV23(); err == nil {
|
||||
t.Fatal("migrateConfigV22ToV23() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV23ToV24(); err == nil {
|
||||
t.Fatal("migrateConfigV23ToV24() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV24ToV25(); err == nil {
|
||||
t.Fatal("migrateConfigV24ToV25() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV25ToV26(); err == nil {
|
||||
t.Fatal("migrateConfigV25ToV26() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV26ToV27(); err == nil {
|
||||
t.Fatal("migrateConfigV26ToV27() should fail with a corrupted json")
|
||||
}
|
||||
if err := migrateV27ToV28(); err == nil {
|
||||
t.Fatal("migrateConfigV27ToV28() should fail with a corrupted json")
|
||||
}
|
||||
}
|
||||
|
||||
// Test if all migrate code returns error with corrupted config files
|
||||
func TestServerConfigMigrateCorruptedConfig(t *testing.T) {
|
||||
rootPath := t.TempDir()
|
||||
|
||||
globalConfigDir = &ConfigDir{path: rootPath}
|
||||
configPath := rootPath + SlashSeparator + minioConfigFile
|
||||
|
||||
for i := 3; i <= 17; i++ {
|
||||
// Create a corrupted config file
|
||||
if err := os.WriteFile(configPath, []byte(fmt.Sprintf("{ \"version\":\"%d\", \"credential\": { \"accessKey\": 1 } }", i)),
|
||||
0o644); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
// Test different migrate versions and be sure they are returning an error
|
||||
if err := migrateConfig(); err == nil {
|
||||
t.Fatal("migrateConfig() should fail with a corrupted json")
|
||||
}
|
||||
}
|
||||
|
||||
// Create a corrupted config file for version '2'.
|
||||
if err := os.WriteFile(configPath, []byte("{ \"version\":\"2\", \"credentials\": { \"accessKeyId\": 1 } }"), 0o644); err != nil {
|
||||
t.Fatal("Unexpected error: ", err)
|
||||
}
|
||||
|
||||
// Test different migrate versions and be sure they are returning an error
|
||||
if err := migrateConfig(); err == nil {
|
||||
t.Fatal("migrateConfig() should fail with a corrupted json")
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/minio/internal/config"
|
||||
"github.com/minio/minio/internal/config/compress"
|
||||
@@ -28,407 +26,10 @@ import (
|
||||
"github.com/minio/minio/internal/config/notify"
|
||||
"github.com/minio/minio/internal/config/policy/opa"
|
||||
"github.com/minio/minio/internal/config/storageclass"
|
||||
"github.com/minio/minio/internal/event/target"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/pkg/v2/quick"
|
||||
)
|
||||
|
||||
type configV1 struct {
|
||||
Version string `json:"version"`
|
||||
AccessKey string `json:"accessKeyId"`
|
||||
SecretKey string `json:"secretAccessKey"`
|
||||
}
|
||||
|
||||
type configV2 struct {
|
||||
Version string `json:"version"`
|
||||
Credentials struct {
|
||||
AccessKey string `json:"accessKeyId"`
|
||||
SecretKey string `json:"secretAccessKey"`
|
||||
Region string `json:"region"`
|
||||
} `json:"credentials"`
|
||||
MongoLogger struct {
|
||||
Addr string `json:"addr"`
|
||||
DB string `json:"db"`
|
||||
Collection string `json:"collection"`
|
||||
} `json:"mongoLogger"`
|
||||
SyslogLogger struct {
|
||||
Network string `json:"network"`
|
||||
Addr string `json:"addr"`
|
||||
} `json:"syslogLogger"`
|
||||
FileLogger struct {
|
||||
Filename string `json:"filename"`
|
||||
} `json:"fileLogger"`
|
||||
}
|
||||
|
||||
// backendV3 type.
|
||||
type backendV3 struct {
|
||||
Type string `json:"type"`
|
||||
Disk string `json:"disk,omitempty"`
|
||||
Disks []string `json:"disks,omitempty"`
|
||||
}
|
||||
|
||||
// syslogLogger v3
|
||||
type syslogLoggerV3 struct {
|
||||
Enable bool `json:"enable"`
|
||||
Addr string `json:"address"`
|
||||
Level string `json:"level"`
|
||||
}
|
||||
|
||||
// loggerV3 type.
|
||||
type loggerV3 struct {
|
||||
Console struct {
|
||||
Enable bool `json:"enable"`
|
||||
Level string `json:"level"`
|
||||
}
|
||||
File struct {
|
||||
Enable bool `json:"enable"`
|
||||
Filename string `json:"fileName"`
|
||||
Level string `json:"level"`
|
||||
}
|
||||
Syslog struct {
|
||||
Enable bool `json:"enable"`
|
||||
Addr string `json:"address"`
|
||||
Level string `json:"level"`
|
||||
} `json:"syslog"`
|
||||
// Add new loggers here.
|
||||
}
|
||||
|
||||
// configV3 server configuration version '3'.
|
||||
type configV3 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// Backend configuration.
|
||||
Backend backendV3 `json:"backend"`
|
||||
|
||||
// http Server configuration.
|
||||
Addr string `json:"address"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger loggerV3 `json:"logger"`
|
||||
}
|
||||
|
||||
// logger type representing version '4' logger config.
|
||||
type loggerV4 struct {
|
||||
Console struct {
|
||||
Enable bool `json:"enable"`
|
||||
Level string `json:"level"`
|
||||
} `json:"console"`
|
||||
File struct {
|
||||
Enable bool `json:"enable"`
|
||||
Filename string `json:"fileName"`
|
||||
Level string `json:"level"`
|
||||
} `json:"file"`
|
||||
Syslog struct {
|
||||
Enable bool `json:"enable"`
|
||||
Addr string `json:"address"`
|
||||
Level string `json:"level"`
|
||||
} `json:"syslog"`
|
||||
}
|
||||
|
||||
// configV4 server configuration version '4'.
|
||||
type configV4 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger loggerV4 `json:"logger"`
|
||||
}
|
||||
|
||||
// logger type representing version '5' logger config.
|
||||
type loggerV5 struct {
|
||||
Console struct {
|
||||
Enable bool `json:"enable"`
|
||||
Level string `json:"level"`
|
||||
} `json:"console"`
|
||||
File struct {
|
||||
Enable bool `json:"enable"`
|
||||
Filename string `json:"fileName"`
|
||||
Level string `json:"level"`
|
||||
} `json:"file"`
|
||||
Syslog struct {
|
||||
Enable bool `json:"enable"`
|
||||
Addr string `json:"address"`
|
||||
Level string `json:"level"`
|
||||
} `json:"syslog"`
|
||||
AMQP struct {
|
||||
Enable bool `json:"enable"`
|
||||
Level string `json:"level"`
|
||||
URL string `json:"url"`
|
||||
Exchange string `json:"exchange"`
|
||||
RoutingKey string `json:"routingKey"`
|
||||
ExchangeType string `json:"exchangeType"`
|
||||
Mandatory bool `json:"mandatory"`
|
||||
Immediate bool `json:"immediate"`
|
||||
Durable bool `json:"durable"`
|
||||
Internal bool `json:"internal"`
|
||||
NoWait bool `json:"noWait"`
|
||||
AutoDeleted bool `json:"autoDeleted"`
|
||||
} `json:"amqp"`
|
||||
ElasticSearch struct {
|
||||
Enable bool `json:"enable"`
|
||||
Level string `json:"level"`
|
||||
URL string `json:"url"`
|
||||
Index string `json:"index"`
|
||||
} `json:"elasticsearch"`
|
||||
Redis struct {
|
||||
Enable bool `json:"enable"`
|
||||
Level string `json:"level"`
|
||||
Addr string `json:"address"`
|
||||
Password string `json:"password"`
|
||||
Key string `json:"key"`
|
||||
} `json:"redis"`
|
||||
}
|
||||
|
||||
// configV5 server configuration version '5'.
|
||||
type configV5 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger loggerV5 `json:"logger"`
|
||||
}
|
||||
|
||||
// consoleLogger - default logger if not other logging is enabled.
|
||||
type consoleLoggerV1 struct {
|
||||
Enable bool `json:"enable"`
|
||||
Level string `json:"level"`
|
||||
}
|
||||
|
||||
type fileLoggerV1 struct {
|
||||
Enable bool `json:"enable"`
|
||||
Filename string `json:"fileName"`
|
||||
Level string `json:"level"`
|
||||
}
|
||||
|
||||
type loggerV6 struct {
|
||||
Console consoleLoggerV1 `json:"console"`
|
||||
File fileLoggerV1 `json:"file"`
|
||||
Syslog syslogLoggerV3 `json:"syslog"`
|
||||
}
|
||||
|
||||
// configV6 server configuration version '6'.
|
||||
type configV6 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger loggerV6 `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV1 `json:"notify"`
|
||||
}
|
||||
|
||||
// Notifier represents collection of supported notification queues in version
|
||||
// 1 without NATS streaming.
|
||||
type notifierV1 struct {
|
||||
AMQP map[string]target.AMQPArgs `json:"amqp"`
|
||||
NATS map[string]natsNotifyV1 `json:"nats"`
|
||||
ElasticSearch map[string]target.ElasticsearchArgs `json:"elasticsearch"`
|
||||
Redis map[string]target.RedisArgs `json:"redis"`
|
||||
PostgreSQL map[string]target.PostgreSQLArgs `json:"postgresql"`
|
||||
Kafka map[string]target.KafkaArgs `json:"kafka"`
|
||||
}
|
||||
|
||||
// Notifier represents collection of supported notification queues in version 2
|
||||
// with NATS streaming but without webhook.
|
||||
type notifierV2 struct {
|
||||
AMQP map[string]target.AMQPArgs `json:"amqp"`
|
||||
NATS map[string]target.NATSArgs `json:"nats"`
|
||||
ElasticSearch map[string]target.ElasticsearchArgs `json:"elasticsearch"`
|
||||
Redis map[string]target.RedisArgs `json:"redis"`
|
||||
PostgreSQL map[string]target.PostgreSQLArgs `json:"postgresql"`
|
||||
Kafka map[string]target.KafkaArgs `json:"kafka"`
|
||||
}
|
||||
|
||||
// configV7 server configuration version '7'.
|
||||
type serverConfigV7 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger loggerV6 `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV1 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV8 server configuration version '8'. Adds NATS notify.Config
|
||||
// configuration.
|
||||
type serverConfigV8 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger loggerV6 `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV1 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV9 server configuration version '9'. Adds PostgreSQL
|
||||
// notify.Config configuration.
|
||||
type serverConfigV9 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger loggerV6 `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV1 `json:"notify"`
|
||||
}
|
||||
|
||||
type loggerV7 struct {
|
||||
sync.RWMutex
|
||||
Console consoleLoggerV1 `json:"console"`
|
||||
File fileLoggerV1 `json:"file"`
|
||||
}
|
||||
|
||||
// serverConfigV10 server configuration version '10' which is like
|
||||
// version '9' except it drops support of syslog config, and makes the
|
||||
// RWMutex global (so it does not exist in this struct).
|
||||
type serverConfigV10 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger loggerV7 `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV1 `json:"notify"`
|
||||
}
|
||||
|
||||
// natsNotifyV1 - structure was valid until config V 11
|
||||
type natsNotifyV1 struct {
|
||||
Enable bool `json:"enable"`
|
||||
Address string `json:"address"`
|
||||
Subject string `json:"subject"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Token string `json:"token"`
|
||||
Secure bool `json:"secure"`
|
||||
PingInterval int64 `json:"pingInterval"`
|
||||
}
|
||||
|
||||
// serverConfigV11 server configuration version '11' which is like
|
||||
// version '10' except it adds support for Kafka notifications.
|
||||
type serverConfigV11 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger loggerV7 `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV1 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV12 server configuration version '12' which is like
|
||||
// version '11' except it adds support for NATS streaming notifications.
|
||||
type serverConfigV12 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger loggerV7 `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV2 `json:"notify"`
|
||||
}
|
||||
|
||||
type notifierV3 struct {
|
||||
AMQP map[string]target.AMQPArgs `json:"amqp"`
|
||||
Elasticsearch map[string]target.ElasticsearchArgs `json:"elasticsearch"`
|
||||
Kafka map[string]target.KafkaArgs `json:"kafka"`
|
||||
MQTT map[string]target.MQTTArgs `json:"mqtt"`
|
||||
MySQL map[string]target.MySQLArgs `json:"mysql"`
|
||||
NATS map[string]target.NATSArgs `json:"nats"`
|
||||
PostgreSQL map[string]target.PostgreSQLArgs `json:"postgresql"`
|
||||
Redis map[string]target.RedisArgs `json:"redis"`
|
||||
Webhook map[string]target.WebhookArgs `json:"webhook"`
|
||||
}
|
||||
|
||||
// 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 auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger *loggerV7 `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify *notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// 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 auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger *loggerV7 `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify *notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV15 server configuration version '15' which is like
|
||||
// version '14' except it adds mysql support
|
||||
type serverConfigV15 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger *loggerV7 `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify *notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// FileLogger is introduced to workaround the dependency about logrus
|
||||
type FileLogger struct {
|
||||
Enable bool `json:"enable"`
|
||||
@@ -440,264 +41,6 @@ type ConsoleLogger struct {
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
|
||||
// Loggers struct is defined with FileLogger and ConsoleLogger
|
||||
// although they are removed from logging logic. They are
|
||||
// kept here just to workaround the dependency migration
|
||||
// code/logic has on them.
|
||||
type loggers struct {
|
||||
sync.RWMutex
|
||||
Console ConsoleLogger `json:"console"`
|
||||
File FileLogger `json:"file"`
|
||||
}
|
||||
|
||||
// serverConfigV16 server configuration version '16' which is like
|
||||
// version '15' except it makes a change to logging configuration.
|
||||
type serverConfigV16 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger *loggers `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify *notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV17 server configuration version '17' which is like
|
||||
// version '16' except it adds support for "format" parameter in
|
||||
// database event notification targets: PostgreSQL, MySQL, Redis and
|
||||
// Elasticsearch.
|
||||
type serverConfigV17 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger *loggers `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify *notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV18 server configuration version '18' which is like
|
||||
// version '17' except it adds support for "deliveryMode" parameter in
|
||||
// the AMQP notification target.
|
||||
type serverConfigV18 struct {
|
||||
sync.RWMutex
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger *loggers `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify *notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV19 server configuration version '19' which is like
|
||||
// version '18' except it adds support for MQTT notifications.
|
||||
type serverConfigV19 struct {
|
||||
sync.RWMutex
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger *loggers `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify *notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV20 server configuration version '20' which is like
|
||||
// version '19' except it adds support for VirtualHostDomain
|
||||
type serverConfigV20 struct {
|
||||
sync.RWMutex
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
Domain string `json:"domain"`
|
||||
|
||||
// Additional error logging configuration.
|
||||
Logger *loggers `json:"logger"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify *notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV21 is just like version '20' without logger field
|
||||
type serverConfigV21 struct {
|
||||
sync.RWMutex
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
Domain string `json:"domain"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify *notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV22 is just like version '21' with added support
|
||||
// for StorageClass.
|
||||
type serverConfigV22 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
Domain string `json:"domain"`
|
||||
|
||||
// Storage class configuration
|
||||
StorageClass storageclass.Config `json:"storageclass"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV23 is just like version '22' with addition of cache field.
|
||||
type serverConfigV23 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
Domain string `json:"domain"`
|
||||
|
||||
// Storage class configuration
|
||||
StorageClass storageclass.Config `json:"storageclass"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV24 is just like version '23', we had to revert
|
||||
// the changes which were made in 6fb06045028b7a57c37c60a612c8e50735279ab4
|
||||
type serverConfigV24 struct {
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
Domain string `json:"domain"`
|
||||
|
||||
// Storage class configuration
|
||||
StorageClass storageclass.Config `json:"storageclass"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV25 is just like version '24', stores additionally
|
||||
// worm variable.
|
||||
type serverConfigV25 struct {
|
||||
quick.Config `json:"-"` // ignore interfaces
|
||||
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
Worm config.BoolFlag `json:"worm"`
|
||||
Domain string `json:"domain"`
|
||||
|
||||
// Storage class configuration
|
||||
StorageClass storageclass.Config `json:"storageclass"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV26 is just like version '25', stores additionally
|
||||
// cache max use value in 'cache.Config'.
|
||||
type serverConfigV26 struct {
|
||||
quick.Config `json:"-"` // ignore interfaces
|
||||
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
Worm config.BoolFlag `json:"worm"`
|
||||
Domain string `json:"domain"`
|
||||
|
||||
// Storage class configuration
|
||||
StorageClass storageclass.Config `json:"storageclass"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV3 `json:"notify"`
|
||||
}
|
||||
|
||||
// serverConfigV27 is just like version '26', stores additionally
|
||||
// the logger field
|
||||
type serverConfigV27 struct {
|
||||
quick.Config `json:"-"` // ignore interfaces
|
||||
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Browser config.BoolFlag `json:"browser"`
|
||||
Worm config.BoolFlag `json:"worm"`
|
||||
Domain string `json:"domain"`
|
||||
|
||||
// Storage class configuration
|
||||
StorageClass storageclass.Config `json:"storageclass"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV3 `json:"notify"`
|
||||
|
||||
// Logger configuration
|
||||
Logger logger.Config `json:"logger"`
|
||||
}
|
||||
|
||||
// serverConfigV28 is just like version '27', additionally
|
||||
// storing KMS config
|
||||
type serverConfigV28 struct {
|
||||
quick.Config `json:"-"` // ignore interfaces
|
||||
|
||||
Version string `json:"version"`
|
||||
|
||||
// S3 API configuration.
|
||||
Credential auth.Credentials `json:"credential"`
|
||||
Region string `json:"region"`
|
||||
Worm config.BoolFlag `json:"worm"`
|
||||
|
||||
// Storage class configuration
|
||||
StorageClass storageclass.Config `json:"storageclass"`
|
||||
|
||||
// Notification queue configuration.
|
||||
Notify notifierV3 `json:"notify"`
|
||||
|
||||
// Logger configuration
|
||||
Logger logger.Config `json:"logger"`
|
||||
}
|
||||
|
||||
// serverConfigV33 is just like version '32', removes clientID from NATS and MQTT, and adds queueDir, queueLimit in all notification targets.
|
||||
type serverConfigV33 struct {
|
||||
quick.Config `json:"-"` // ignore interfaces
|
||||
|
||||
@@ -1142,7 +1142,7 @@ func (er erasureObjects) HealObject(ctx context.Context, bucket, object, version
|
||||
if reqInfo != nil {
|
||||
newReqInfo = logger.NewReqInfo(reqInfo.RemoteHost, reqInfo.UserAgent, reqInfo.DeploymentID, reqInfo.RequestID, reqInfo.API, bucket, object)
|
||||
} else {
|
||||
newReqInfo = logger.NewReqInfo("", "", globalDeploymentID, "", "Heal", bucket, object)
|
||||
newReqInfo = logger.NewReqInfo("", "", globalDeploymentID(), "", "Heal", bucket, object)
|
||||
}
|
||||
healCtx := logger.SetReqInfo(GlobalContext, newReqInfo)
|
||||
|
||||
|
||||
+12
-11
@@ -320,7 +320,7 @@ func (er erasureObjects) ListMultipartUploads(ctx context.Context, bucket, objec
|
||||
populatedUploadIds.Add(uploadID)
|
||||
uploads = append(uploads, MultipartInfo{
|
||||
Object: object,
|
||||
UploadID: base64.RawURLEncoding.EncodeToString([]byte(fmt.Sprintf("%s.%s", globalDeploymentID, uploadID))),
|
||||
UploadID: base64.RawURLEncoding.EncodeToString([]byte(fmt.Sprintf("%s.%s", globalDeploymentID(), uploadID))),
|
||||
Initiated: fi.ModTime,
|
||||
})
|
||||
}
|
||||
@@ -481,7 +481,7 @@ func (er erasureObjects) newMultipartUpload(ctx context.Context, bucket string,
|
||||
partsMetadata[index].Metadata = userDefined
|
||||
}
|
||||
uploadUUID := mustGetUUID()
|
||||
uploadID := base64.RawURLEncoding.EncodeToString([]byte(fmt.Sprintf("%s.%s", globalDeploymentID, uploadUUID)))
|
||||
uploadID := base64.RawURLEncoding.EncodeToString([]byte(fmt.Sprintf("%s.%s", globalDeploymentID(), uploadUUID)))
|
||||
uploadIDPath := er.getUploadIDDir(bucket, object, uploadUUID)
|
||||
|
||||
// Write updated `xl.meta` to all disks.
|
||||
@@ -1169,6 +1169,16 @@ func (er erasureObjects) CompleteMultipartUpload(ctx context.Context, bucket str
|
||||
return oi, err
|
||||
}
|
||||
}
|
||||
|
||||
// Hold namespace to complete the transaction
|
||||
lk := er.NewNSLock(bucket, object)
|
||||
lkctx, err := lk.GetLock(ctx, globalOperationTimeout)
|
||||
if err != nil {
|
||||
return oi, err
|
||||
}
|
||||
ctx = lkctx.Context()
|
||||
defer lk.Unlock(lkctx)
|
||||
|
||||
if checksumType.IsSet() {
|
||||
checksumType |= hash.ChecksumMultipart | hash.ChecksumIncludesMultipart
|
||||
cs := hash.NewChecksumFromData(checksumType, checksumCombined)
|
||||
@@ -1212,15 +1222,6 @@ func (er erasureObjects) CompleteMultipartUpload(ctx context.Context, bucket str
|
||||
}
|
||||
}
|
||||
|
||||
// Hold namespace to complete the transaction
|
||||
lk := er.NewNSLock(bucket, object)
|
||||
lkctx, err := lk.GetLock(ctx, globalOperationTimeout)
|
||||
if err != nil {
|
||||
return oi, err
|
||||
}
|
||||
ctx = lkctx.Context()
|
||||
defer lk.Unlock(lkctx)
|
||||
|
||||
// Remove parts that weren't present in CompleteMultipartUpload request.
|
||||
for _, curpart := range currentFI.Parts {
|
||||
// Remove part.meta which is not needed anymore.
|
||||
|
||||
@@ -188,14 +188,6 @@ func (er erasureObjects) CopyObject(ctx context.Context, srcBucket, srcObject, d
|
||||
func (er erasureObjects) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
auditObjectErasureSet(ctx, object, &er)
|
||||
|
||||
// This is a special call attempted first to check for SOS-API calls.
|
||||
gr, err = veeamSOSAPIGetObject(ctx, bucket, object, rs, opts)
|
||||
if err == nil {
|
||||
return gr, nil
|
||||
}
|
||||
// reset any error to 'nil'
|
||||
err = nil
|
||||
|
||||
var unlockOnDefer bool
|
||||
nsUnlocker := func() {}
|
||||
defer func() {
|
||||
@@ -451,14 +443,6 @@ func (er erasureObjects) getObjectWithFileInfo(ctx context.Context, bucket, obje
|
||||
func (er erasureObjects) GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (info ObjectInfo, err error) {
|
||||
auditObjectErasureSet(ctx, object, &er)
|
||||
|
||||
// This is a special call attempted first to check for SOS-API calls.
|
||||
info, err = veeamSOSAPIHeadObject(ctx, bucket, object, opts)
|
||||
if err == nil {
|
||||
return info, nil
|
||||
}
|
||||
// reset any error to 'nil'
|
||||
err = nil
|
||||
|
||||
if !opts.NoLock {
|
||||
// Lock the object before reading.
|
||||
lk := er.NewNSLock(bucket, object)
|
||||
|
||||
@@ -795,6 +795,16 @@ func (z *erasureServerPools) GetObjectNInfo(ctx context.Context, bucket, object
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// This is a special call attempted first to check for SOS-API calls.
|
||||
gr, err = veeamSOSAPIGetObject(ctx, bucket, object, rs, opts)
|
||||
if err == nil {
|
||||
return gr, nil
|
||||
}
|
||||
|
||||
// reset any error to 'nil' and any reader to be 'nil'
|
||||
gr = nil
|
||||
err = nil
|
||||
|
||||
object = encodeDirObject(object)
|
||||
|
||||
if z.SinglePool() {
|
||||
@@ -924,6 +934,16 @@ func (z *erasureServerPools) GetObjectInfo(ctx context.Context, bucket, object s
|
||||
return objInfo, err
|
||||
}
|
||||
|
||||
// This is a special call attempted first to check for SOS-API calls.
|
||||
objInfo, err = veeamSOSAPIHeadObject(ctx, bucket, object, opts)
|
||||
if err == nil {
|
||||
return objInfo, nil
|
||||
}
|
||||
|
||||
// reset any error to 'nil', and object info to be empty.
|
||||
err = nil
|
||||
objInfo = ObjectInfo{}
|
||||
|
||||
object = encodeDirObject(object)
|
||||
|
||||
if z.SinglePool() {
|
||||
|
||||
@@ -210,6 +210,9 @@ type eventArgs struct {
|
||||
func (args eventArgs) ToEvent(escape bool) event.Event {
|
||||
eventTime := UTCNow()
|
||||
uniqueID := fmt.Sprintf("%X", eventTime.UnixNano())
|
||||
if !args.Object.ModTime.IsZero() {
|
||||
uniqueID = fmt.Sprintf("%X", args.Object.ModTime.UnixNano())
|
||||
}
|
||||
|
||||
respElements := map[string]string{
|
||||
"x-amz-request-id": args.RespElements["requestId"],
|
||||
@@ -223,7 +226,7 @@ func (args eventArgs) ToEvent(escape bool) event.Event {
|
||||
}
|
||||
|
||||
// Add deployment as part of response elements.
|
||||
respElements["x-minio-deployment-id"] = globalDeploymentID
|
||||
respElements["x-minio-deployment-id"] = globalDeploymentID()
|
||||
if args.RespElements["content-length"] != "" {
|
||||
respElements["content-length"] = args.RespElements["content-length"]
|
||||
}
|
||||
|
||||
+9
-2
@@ -23,7 +23,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
@@ -35,6 +34,7 @@ import (
|
||||
"github.com/minio/minio/internal/config"
|
||||
"github.com/minio/minio/internal/handlers"
|
||||
"github.com/minio/minio/internal/kms"
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
@@ -313,7 +313,14 @@ var (
|
||||
globalAuthZPlugin *polplugin.AuthZPlugin
|
||||
|
||||
// Deployment ID - unique per deployment
|
||||
globalDeploymentID string
|
||||
globalDeploymentIDPtr atomic.Pointer[string]
|
||||
globalDeploymentID = func() string {
|
||||
ptr := globalDeploymentIDPtr.Load()
|
||||
if ptr == nil {
|
||||
return ""
|
||||
}
|
||||
return *ptr
|
||||
}
|
||||
|
||||
globalAllHealState *allHealState
|
||||
|
||||
|
||||
+4
-14
@@ -112,14 +112,9 @@ func saveIAMFormat(ctx context.Context, store IAMStorageAPI) error {
|
||||
bootstrapTraceMsg("Load IAM format file")
|
||||
var iamFmt iamFormat
|
||||
path := getIAMFormatFilePath()
|
||||
if err := store.loadIAMConfig(ctx, &iamFmt, path); err != nil {
|
||||
switch err {
|
||||
case errConfigNotFound:
|
||||
// Need to migrate to V1.
|
||||
default:
|
||||
// if IAM format
|
||||
return err
|
||||
}
|
||||
if err := store.loadIAMConfig(ctx, &iamFmt, path); err != nil && !errors.Is(err, errConfigNotFound) {
|
||||
// if IAM format
|
||||
return err
|
||||
}
|
||||
|
||||
if iamFmt.Version >= iamFormatVersion1 {
|
||||
@@ -129,12 +124,7 @@ func saveIAMFormat(ctx context.Context, store IAMStorageAPI) error {
|
||||
|
||||
bootstrapTraceMsg("Write IAM format file")
|
||||
// Save iam format to version 1.
|
||||
if err := store.saveIAMConfig(ctx, newIAMFormatVersion1(), path); err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return store.saveIAMConfig(ctx, newIAMFormatVersion1(), path)
|
||||
}
|
||||
|
||||
func getGroupInfoPath(group string) string {
|
||||
|
||||
+2
-1
@@ -293,6 +293,7 @@ func (sys *IAMSys) Init(ctx context.Context, objAPI ObjectLayer, etcdClient *etc
|
||||
if err := saveIAMFormat(retryCtx, sys.store); err != nil {
|
||||
if configRetriableErrors(err) {
|
||||
logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. possible cause (%v)", err)
|
||||
time.Sleep(time.Duration(r.Float64() * float64(time.Second)))
|
||||
continue
|
||||
}
|
||||
logger.LogIf(ctx, fmt.Errorf("IAM sub-system is partially initialized, unable to write the IAM format: %w", err))
|
||||
@@ -307,7 +308,7 @@ func (sys *IAMSys) Init(ctx context.Context, objAPI ObjectLayer, etcdClient *etc
|
||||
if err := sys.Load(retryCtx, true); err != nil {
|
||||
if configRetriableErrors(err) {
|
||||
logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. possible cause (%v)", err)
|
||||
time.Sleep(time.Duration(r.Float64() * float64(5*time.Second)))
|
||||
time.Sleep(time.Duration(r.Float64() * float64(time.Second)))
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
+12
-6
@@ -74,7 +74,8 @@ const (
|
||||
var (
|
||||
resourceCollector *minioResourceCollector
|
||||
// resourceMetricsMap is a map of subsystem to its metrics
|
||||
resourceMetricsMap map[MetricSubsystem]ResourceMetrics
|
||||
resourceMetricsMap map[MetricSubsystem]ResourceMetrics
|
||||
resourceMetricsMapMu sync.RWMutex
|
||||
// resourceMetricsHelpMap maps metric name to its help string
|
||||
resourceMetricsHelpMap map[MetricName]string
|
||||
resourceMetricsGroups []*MetricsGroup
|
||||
@@ -159,6 +160,8 @@ func init() {
|
||||
}
|
||||
|
||||
func updateResourceMetrics(subSys MetricSubsystem, name MetricName, val float64, labels map[string]string, isCumulative bool) {
|
||||
resourceMetricsMapMu.Lock()
|
||||
defer resourceMetricsMapMu.Unlock()
|
||||
subsysMetrics, found := resourceMetricsMap[subSys]
|
||||
if !found {
|
||||
subsysMetrics = ResourceMetrics{}
|
||||
@@ -320,7 +323,9 @@ func collectLocalResourceMetrics() {
|
||||
|
||||
// startResourceMetricsCollection - starts the job for collecting resource metrics
|
||||
func startResourceMetricsCollection() {
|
||||
resourceMetricsMapMu.Lock()
|
||||
resourceMetricsMap = map[MetricSubsystem]ResourceMetrics{}
|
||||
resourceMetricsMapMu.Unlock()
|
||||
metricsTimer := time.NewTimer(resourceMetricsCollectionInterval)
|
||||
defer metricsTimer.Stop()
|
||||
|
||||
@@ -382,12 +387,11 @@ func newMinioResourceCollector(metricsGroups []*MetricsGroup) *minioResourceColl
|
||||
func prepareResourceMetrics(rm ResourceMetric, subSys MetricSubsystem, requireAvgMax bool) []Metric {
|
||||
help := resourceMetricsHelpMap[rm.Name]
|
||||
name := rm.Name
|
||||
|
||||
metrics := []Metric{}
|
||||
metrics := make([]Metric, 0, 3)
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getResourceMetricDescription(subSys, name, help),
|
||||
Value: rm.Current,
|
||||
VariableLabels: rm.Labels,
|
||||
VariableLabels: cloneMSS(rm.Labels),
|
||||
})
|
||||
|
||||
if requireAvgMax {
|
||||
@@ -396,7 +400,7 @@ func prepareResourceMetrics(rm ResourceMetric, subSys MetricSubsystem, requireAv
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getResourceMetricDescription(subSys, avgName, avgHelp),
|
||||
Value: math.Round(rm.Avg*100) / 100,
|
||||
VariableLabels: rm.Labels,
|
||||
VariableLabels: cloneMSS(rm.Labels),
|
||||
})
|
||||
|
||||
maxName := MetricName(fmt.Sprintf("%s_max", name))
|
||||
@@ -404,7 +408,7 @@ func prepareResourceMetrics(rm ResourceMetric, subSys MetricSubsystem, requireAv
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getResourceMetricDescription(subSys, maxName, maxHelp),
|
||||
Value: rm.Max,
|
||||
VariableLabels: rm.Labels,
|
||||
VariableLabels: cloneMSS(rm.Labels),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -429,6 +433,8 @@ func getResourceMetrics() *MetricsGroup {
|
||||
metrics := []Metric{}
|
||||
|
||||
subSystems := []MetricSubsystem{interfaceSubsystem, memSubsystem, driveSubsystem, cpuSubsystem}
|
||||
resourceMetricsMapMu.RLock()
|
||||
defer resourceMetricsMapMu.RUnlock()
|
||||
for _, subSys := range subSystems {
|
||||
stats, found := resourceMetricsMap[subSys]
|
||||
if found {
|
||||
|
||||
@@ -194,8 +194,6 @@ func delOpts(ctx context.Context, r *http.Request, bucket, object string) (opts
|
||||
Err: fmt.Errorf("Unable to parse %s, failed with %w", xhttp.MinIOSourceMTime, err),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
opts.MTime = UTCNow()
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
@@ -225,7 +223,7 @@ func putOpts(ctx context.Context, r *http.Request, bucket, object string, metada
|
||||
}
|
||||
|
||||
mtimeStr := strings.TrimSpace(r.Header.Get(xhttp.MinIOSourceMTime))
|
||||
mtime := UTCNow()
|
||||
var mtime time.Time
|
||||
if mtimeStr != "" {
|
||||
mtime, err = time.Parse(time.RFC3339Nano, mtimeStr)
|
||||
if err != nil {
|
||||
@@ -237,7 +235,7 @@ func putOpts(ctx context.Context, r *http.Request, bucket, object string, metada
|
||||
}
|
||||
}
|
||||
retaintimeStr := strings.TrimSpace(r.Header.Get(xhttp.MinIOSourceObjectRetentionTimestamp))
|
||||
retaintimestmp := mtime
|
||||
var retaintimestmp time.Time
|
||||
if retaintimeStr != "" {
|
||||
retaintimestmp, err = time.Parse(time.RFC3339, retaintimeStr)
|
||||
if err != nil {
|
||||
@@ -250,7 +248,7 @@ func putOpts(ctx context.Context, r *http.Request, bucket, object string, metada
|
||||
}
|
||||
|
||||
lholdtimeStr := strings.TrimSpace(r.Header.Get(xhttp.MinIOSourceObjectLegalHoldTimestamp))
|
||||
lholdtimestmp := mtime
|
||||
var lholdtimestmp time.Time
|
||||
if lholdtimeStr != "" {
|
||||
lholdtimestmp, err = time.Parse(time.RFC3339, lholdtimeStr)
|
||||
if err != nil {
|
||||
@@ -262,7 +260,7 @@ func putOpts(ctx context.Context, r *http.Request, bucket, object string, metada
|
||||
}
|
||||
}
|
||||
tagtimeStr := strings.TrimSpace(r.Header.Get(xhttp.MinIOSourceTaggingTimestamp))
|
||||
taggingtimestmp := mtime
|
||||
var taggingtimestmp time.Time
|
||||
if tagtimeStr != "" {
|
||||
taggingtimestmp, err = time.Parse(time.RFC3339, tagtimeStr)
|
||||
if err != nil {
|
||||
@@ -353,7 +351,7 @@ func copySrcOpts(ctx context.Context, r *http.Request, bucket, object string) (O
|
||||
// get ObjectOptions for CompleteMultipart calls
|
||||
func completeMultipartOpts(ctx context.Context, r *http.Request, bucket, object string) (opts ObjectOptions, err error) {
|
||||
mtimeStr := strings.TrimSpace(r.Header.Get(xhttp.MinIOSourceMTime))
|
||||
mtime := UTCNow()
|
||||
var mtime time.Time
|
||||
if mtimeStr != "" {
|
||||
mtime, err = time.Parse(time.RFC3339Nano, mtimeStr)
|
||||
if err != nil {
|
||||
|
||||
@@ -245,7 +245,7 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
|
||||
Key: object,
|
||||
Resource: r.URL.Path,
|
||||
RequestID: w.Header().Get(xhttp.AmzRequestID),
|
||||
HostID: globalDeploymentID,
|
||||
HostID: globalDeploymentID(),
|
||||
})
|
||||
writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, mimeXML)
|
||||
} else {
|
||||
@@ -264,7 +264,7 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
|
||||
Key: object,
|
||||
Resource: r.URL.Path,
|
||||
RequestID: w.Header().Get(xhttp.AmzRequestID),
|
||||
HostID: globalDeploymentID,
|
||||
HostID: globalDeploymentID(),
|
||||
})
|
||||
writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, mimeXML)
|
||||
} else {
|
||||
@@ -1375,7 +1375,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
srcTimestamp := dstOpts.ReplicationSourceLegalholdTimestamp
|
||||
if !srcTimestamp.IsZero() {
|
||||
ondiskTimestamp, err := time.Parse(lastLegalHoldTimestamp, time.RFC3339Nano)
|
||||
// update legalhold metadata only if replica timestamp is newer than what's on disk
|
||||
// update legalhold metadata only if replica timestamp is newer than what's on disk
|
||||
if err != nil || (err == nil && ondiskTimestamp.Before(srcTimestamp)) {
|
||||
srcInfo.UserDefined[strings.ToLower(xhttp.AmzObjectLockLegalHold)] = string(legalHold.Status)
|
||||
srcInfo.UserDefined[ReservedMetadataPrefixLower+ObjectLockRetentionTimestamp] = srcTimestamp.Format(time.RFC3339Nano)
|
||||
@@ -3141,7 +3141,7 @@ func (api objectAPIHandlers) PostRestoreObjectHandler(w http.ResponseWriter, r *
|
||||
Key: object,
|
||||
Resource: r.URL.Path,
|
||||
RequestID: w.Header().Get(xhttp.AmzRequestID),
|
||||
HostID: globalDeploymentID,
|
||||
HostID: globalDeploymentID(),
|
||||
})
|
||||
writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, mimeXML)
|
||||
} else {
|
||||
|
||||
+4
-4
@@ -80,11 +80,11 @@ func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error)
|
||||
continue
|
||||
}
|
||||
|
||||
var typ os.FileMode = 0 // regular file
|
||||
var typ os.FileMode // regular file
|
||||
switch {
|
||||
case data.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0:
|
||||
// Reparse point is a symlink
|
||||
fi, err := os.Stat(pathJoin(dirPath, string(name)))
|
||||
fi, err := os.Stat(pathJoin(dirPath, name))
|
||||
if err != nil {
|
||||
// It got deleted in the meantime, not found
|
||||
// or returns too many symlinks ignore this
|
||||
@@ -153,7 +153,7 @@ func readDirWithOpts(dirPath string, opts readDirOpts) (entries []string, err er
|
||||
switch {
|
||||
case data.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0:
|
||||
// Reparse point is a symlink
|
||||
fi, err := os.Stat(pathJoin(dirPath, string(name)))
|
||||
fi, err := os.Stat(pathJoin(dirPath, name))
|
||||
if err != nil {
|
||||
// It got deleted in the meantime, not found
|
||||
// or returns too many symlinks ignore this
|
||||
@@ -170,7 +170,7 @@ func readDirWithOpts(dirPath string, opts readDirOpts) (entries []string, err er
|
||||
continue
|
||||
}
|
||||
case data.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0:
|
||||
name = name + SlashSeparator
|
||||
name += SlashSeparator
|
||||
}
|
||||
|
||||
count--
|
||||
|
||||
+14
-7
@@ -131,7 +131,6 @@ func (sys *S3PeerSys) ListBuckets(ctx context.Context, opts BucketOptions) ([]Bu
|
||||
g := errgroup.WithNErrs(len(sys.peerClients))
|
||||
|
||||
nodeBuckets := make([][]BucketInfo, len(sys.peerClients))
|
||||
errs := []error{nil}
|
||||
|
||||
for idx, client := range sys.peerClients {
|
||||
idx := idx
|
||||
@@ -149,7 +148,7 @@ func (sys *S3PeerSys) ListBuckets(ctx context.Context, opts BucketOptions) ([]Bu
|
||||
}, idx)
|
||||
}
|
||||
|
||||
errs = append(errs, g.Wait()...)
|
||||
errs := g.Wait()
|
||||
|
||||
// The list of buckets in a map to avoid duplication
|
||||
resultMap := make(map[string]BucketInfo)
|
||||
@@ -161,7 +160,7 @@ func (sys *S3PeerSys) ListBuckets(ctx context.Context, opts BucketOptions) ([]Bu
|
||||
perPoolErrs = append(perPoolErrs, errs[i])
|
||||
}
|
||||
}
|
||||
quorum := len(perPoolErrs)/2 + 1
|
||||
quorum := len(perPoolErrs) / 2
|
||||
if poolErr := reduceWriteQuorumErrs(ctx, perPoolErrs, bucketOpIgnoredErrs, quorum); poolErr != nil {
|
||||
return nil, poolErr
|
||||
}
|
||||
@@ -181,7 +180,7 @@ func (sys *S3PeerSys) ListBuckets(ctx context.Context, opts BucketOptions) ([]Bu
|
||||
continue
|
||||
}
|
||||
bucketsMap[bi.Name]++
|
||||
if bucketsMap[bi.Name] == quorum {
|
||||
if bucketsMap[bi.Name] >= quorum {
|
||||
resultMap[bi.Name] = bi
|
||||
}
|
||||
}
|
||||
@@ -223,9 +222,17 @@ func (sys *S3PeerSys) GetBucketInfo(ctx context.Context, bucket string, opts Buc
|
||||
|
||||
errs := g.Wait()
|
||||
|
||||
quorum := len(sys.peerClients)/2 + 1
|
||||
if err = reduceReadQuorumErrs(ctx, errs, bucketOpIgnoredErrs, quorum); err != nil {
|
||||
return BucketInfo{}, toObjectErr(err, bucket)
|
||||
for poolIdx := 0; poolIdx < sys.poolsCount; poolIdx++ {
|
||||
perPoolErrs := make([]error, 0, len(sys.peerClients))
|
||||
for i, client := range sys.peerClients {
|
||||
if slices.Contains(client.GetPools(), poolIdx) {
|
||||
perPoolErrs = append(perPoolErrs, errs[i])
|
||||
}
|
||||
}
|
||||
quorum := len(perPoolErrs) / 2
|
||||
if poolErr := reduceWriteQuorumErrs(ctx, perPoolErrs, bucketOpIgnoredErrs, quorum); poolErr != nil {
|
||||
return BucketInfo{}, poolErr
|
||||
}
|
||||
}
|
||||
|
||||
for i, err := range errs {
|
||||
|
||||
+1
-1
@@ -351,7 +351,7 @@ func siteNetperf(ctx context.Context, duration time.Duration) madmin.SiteNetPerf
|
||||
|
||||
for _, info := range clusterInfos.Sites {
|
||||
// skip self
|
||||
if globalDeploymentID == info.DeploymentID {
|
||||
if globalDeploymentID() == info.DeploymentID {
|
||||
continue
|
||||
}
|
||||
info := info
|
||||
|
||||
@@ -225,9 +225,9 @@ func connectLoadInitFormats(verboseLogging bool, firstDisk bool, endpoints Endpo
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Assign globalDeploymentID on first run for the
|
||||
// Assign globalDeploymentID() on first run for the
|
||||
// minio server managing the first disk
|
||||
globalDeploymentID = format.ID
|
||||
globalDeploymentIDPtr.Store(&format.ID)
|
||||
return storageDisks, format, nil
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ func connectLoadInitFormats(verboseLogging bool, firstDisk bool, endpoints Endpo
|
||||
}
|
||||
}
|
||||
|
||||
globalDeploymentID = format.ID
|
||||
globalDeploymentIDPtr.Store(&format.ID)
|
||||
|
||||
if err = formatErasureFixLocalDeploymentID(endpoints, storageDisks, format); err != nil {
|
||||
logger.LogIf(GlobalContext, err)
|
||||
|
||||
+28
-7
@@ -372,6 +372,12 @@ func initAllSubsystems(ctx context.Context) {
|
||||
}
|
||||
|
||||
func configRetriableErrors(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
notInitialized := err.Error() == "Server not initialized, please try again"
|
||||
|
||||
// Initializing sub-systems needs a retry mechanism for
|
||||
// the following reasons:
|
||||
// - Read quorum is lost just after the initialization
|
||||
@@ -392,7 +398,8 @@ func configRetriableErrors(err error) bool {
|
||||
errors.As(err, &wquorum) ||
|
||||
isErrObjectNotFound(err) ||
|
||||
isErrBucketNotFound(err) ||
|
||||
errors.Is(err, os.ErrDeadlineExceeded)
|
||||
errors.Is(err, os.ErrDeadlineExceeded) ||
|
||||
notInitialized
|
||||
}
|
||||
|
||||
func bootstrapTraceMsg(msg string) {
|
||||
@@ -579,6 +586,9 @@ func serverMain(ctx *cli.Context) {
|
||||
|
||||
setDefaultProfilerRates()
|
||||
|
||||
// Always load ENV variables from files first.
|
||||
loadEnvVarsFromFiles()
|
||||
|
||||
// Handle all server command args.
|
||||
bootstrapTrace("serverHandleCmdArgs", func() {
|
||||
serverHandleCmdArgs(ctx)
|
||||
@@ -710,7 +720,7 @@ func serverMain(ctx *cli.Context) {
|
||||
}
|
||||
})
|
||||
|
||||
xhttp.SetDeploymentID(globalDeploymentID)
|
||||
xhttp.SetDeploymentID(globalDeploymentID())
|
||||
xhttp.SetMinIOVersion(Version)
|
||||
|
||||
for _, n := range globalNodes {
|
||||
@@ -718,7 +728,7 @@ func serverMain(ctx *cli.Context) {
|
||||
if n.IsLocal {
|
||||
nodeName = globalLocalNodeName
|
||||
}
|
||||
nodeNameSum := sha256.Sum256([]byte(nodeName + globalDeploymentID))
|
||||
nodeNameSum := sha256.Sum256([]byte(nodeName + globalDeploymentID()))
|
||||
globalNodeNamesHex[hex.EncodeToString(nodeNameSum[:])] = struct{}{}
|
||||
}
|
||||
|
||||
@@ -810,10 +820,12 @@ func serverMain(ctx *cli.Context) {
|
||||
}()
|
||||
|
||||
go func() {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
if !globalDisableFreezeOnBoot {
|
||||
defer bootstrapTrace("unfreezeServices", unfreezeServices)
|
||||
t := time.AfterFunc(5*time.Minute, func() {
|
||||
logger.Info(color.Yellow("WARNING: Taking more time to initialize the config subsystem. Please set '_MINIO_DISABLE_API_FREEZE_ON_BOOT=true' to not freeze the APIs"))
|
||||
logger.Info(color.Yellow("WARNING: Initializing the config subsystem is taking longer than 5 minutes. Please set '_MINIO_DISABLE_API_FREEZE_ON_BOOT=true' to not freeze the APIs"))
|
||||
})
|
||||
defer t.Stop()
|
||||
}
|
||||
@@ -861,9 +873,18 @@ func serverMain(ctx *cli.Context) {
|
||||
var buckets []BucketInfo
|
||||
// List buckets to initialize bucket metadata sub-sys.
|
||||
bootstrapTrace("listBuckets", func() {
|
||||
buckets, err = newObject.ListBuckets(GlobalContext, BucketOptions{})
|
||||
if err != nil {
|
||||
logger.LogIf(GlobalContext, fmt.Errorf("Unable to list buckets to initialize bucket metadata sub-system: %w", err))
|
||||
for {
|
||||
buckets, err = newObject.ListBuckets(GlobalContext, BucketOptions{})
|
||||
if err != nil {
|
||||
if configRetriableErrors(err) {
|
||||
logger.Info("Waiting for list buckets to succeed to initialize buckets.. possible cause (%v)", err)
|
||||
time.Sleep(time.Duration(r.Float64() * float64(time.Second)))
|
||||
continue
|
||||
}
|
||||
logger.LogIf(GlobalContext, fmt.Errorf("Unable to list buckets to initialize bucket metadata sub-system: %w", err))
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -273,15 +273,5 @@ func checkMetaHeaders(signedHeadersMap http.Header, r *http.Request) APIErrorCod
|
||||
}
|
||||
}
|
||||
|
||||
// check values from url, if no http header
|
||||
for k, val := range r.Form {
|
||||
if stringsHasPrefixFold(k, "x-amz-meta-") {
|
||||
if signedHeadersMap.Get(http.CanonicalHeaderKey(k)) == val[0] {
|
||||
continue
|
||||
}
|
||||
return ErrUnsignedHeaders
|
||||
}
|
||||
}
|
||||
|
||||
return ErrNone
|
||||
}
|
||||
|
||||
@@ -394,17 +394,4 @@ func TestCheckMetaHeaders(t *testing.T) {
|
||||
if errCode != ErrNone {
|
||||
t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrNone, errCode)
|
||||
}
|
||||
|
||||
// Add extra metadata in url values
|
||||
r, err = http.NewRequest(http.MethodPut, "http://play.min.io:9000?x-amz-meta-test=test&x-amz-meta-extension=png&x-amz-meta-name=imagepng&x-amz-meta-clone=fail", nil)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to create http.Request :", err)
|
||||
}
|
||||
|
||||
r.ParseForm()
|
||||
// calling the function being tested.
|
||||
errCode = checkMetaHeaders(signedHeadersMap, r)
|
||||
if errCode != ErrUnsignedHeaders {
|
||||
t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrUnsignedHeaders, errCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ func (sm *siteResyncMetrics) load(ctx context.Context, objAPI ObjectLayer) error
|
||||
return nil
|
||||
}
|
||||
for _, peer := range info.Sites {
|
||||
if peer.DeploymentID == globalDeploymentID {
|
||||
if peer.DeploymentID == globalDeploymentID() {
|
||||
continue
|
||||
}
|
||||
rs, err := loadSiteResyncMetadata(ctx, objAPI, peer.DeploymentID)
|
||||
|
||||
+54
-49
@@ -154,6 +154,13 @@ func errSRConfigMissingError(err error) SRError {
|
||||
}
|
||||
}
|
||||
|
||||
func errSRIAMConfigMismatch(peer1, peer2 string, s1, s2 madmin.IDPSettings) SRError {
|
||||
return SRError{
|
||||
Cause: fmt.Errorf("IAM/IDP settings mismatch between %s and %s: %#v vs %#v", peer1, peer2, s1, s2),
|
||||
Code: ErrSiteReplicationIAMConfigMismatch,
|
||||
}
|
||||
}
|
||||
|
||||
var errSRObjectLayerNotReady = SRError{
|
||||
Cause: fmt.Errorf("object layer not ready"),
|
||||
Code: ErrServerNotInitialized,
|
||||
@@ -365,7 +372,7 @@ func (c *SiteReplicationSys) getSiteStatuses(ctx context.Context, sites ...madmi
|
||||
PeerSite: v,
|
||||
DeploymentID: info.DeploymentID,
|
||||
Empty: len(buckets) == 0,
|
||||
self: info.DeploymentID == globalDeploymentID,
|
||||
self: info.DeploymentID == globalDeploymentID(),
|
||||
})
|
||||
}
|
||||
return
|
||||
@@ -410,7 +417,7 @@ func (c *SiteReplicationSys) AddPeerClusters(ctx context.Context, psites []madmi
|
||||
}
|
||||
}
|
||||
if selfIdx == -1 {
|
||||
return madmin.ReplicateAddStatus{}, errSRBackendIssue(fmt.Errorf("global deployment ID %s mismatch, expected one of %s", globalDeploymentID, deploymentIDsSet))
|
||||
return madmin.ReplicateAddStatus{}, errSRBackendIssue(fmt.Errorf("global deployment ID %s mismatch, expected one of %s", globalDeploymentID(), deploymentIDsSet))
|
||||
}
|
||||
if !currDeploymentIDsSet.IsEmpty() {
|
||||
// If current cluster is already SR enabled and no new site being added ,fail.
|
||||
@@ -424,13 +431,10 @@ func (c *SiteReplicationSys) AddPeerClusters(ctx context.Context, psites []madmi
|
||||
}
|
||||
|
||||
// validate that all clusters are using the same IDP settings.
|
||||
pass, err := c.validateIDPSettings(ctx, sites)
|
||||
err = c.validateIDPSettings(ctx, sites)
|
||||
if err != nil {
|
||||
return madmin.ReplicateAddStatus{}, err
|
||||
}
|
||||
if !pass {
|
||||
return madmin.ReplicateAddStatus{}, errSRInvalidRequest(errors.New("all cluster sites must have the same IAM/IDP settings"))
|
||||
}
|
||||
|
||||
// For this `add` API, either all clusters must be empty or the local
|
||||
// cluster must be the only one having some buckets.
|
||||
@@ -569,7 +573,7 @@ func (c *SiteReplicationSys) AddPeerClusters(ctx context.Context, psites []madmi
|
||||
func (c *SiteReplicationSys) PeerJoinReq(ctx context.Context, arg madmin.SRPeerJoinReq) error {
|
||||
var ourName string
|
||||
for d, p := range arg.Peers {
|
||||
if d == globalDeploymentID {
|
||||
if d == globalDeploymentID() {
|
||||
ourName = p.Name
|
||||
break
|
||||
}
|
||||
@@ -619,7 +623,7 @@ func (c *SiteReplicationSys) GetIDPSettings(ctx context.Context) madmin.IDPSetti
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *SiteReplicationSys) validateIDPSettings(ctx context.Context, peers []PeerSiteInfo) (bool, error) {
|
||||
func (c *SiteReplicationSys) validateIDPSettings(ctx context.Context, peers []PeerSiteInfo) error {
|
||||
s := make([]madmin.IDPSettings, 0, len(peers))
|
||||
for _, v := range peers {
|
||||
if v.self {
|
||||
@@ -629,22 +633,23 @@ func (c *SiteReplicationSys) validateIDPSettings(ctx context.Context, peers []Pe
|
||||
|
||||
admClient, err := getAdminClient(v.Endpoint, v.AccessKey, v.SecretKey)
|
||||
if err != nil {
|
||||
return false, errSRPeerResp(fmt.Errorf("unable to create admin client for %s: %w", v.Name, err))
|
||||
return errSRPeerResp(fmt.Errorf("unable to create admin client for %s: %w", v.Name, err))
|
||||
}
|
||||
|
||||
is, err := admClient.SRPeerGetIDPSettings(ctx)
|
||||
if err != nil {
|
||||
return false, errSRPeerResp(fmt.Errorf("unable to fetch IDP settings from %s: %v", v.Name, err))
|
||||
return errSRPeerResp(fmt.Errorf("unable to fetch IDP settings from %s: %v", v.Name, err))
|
||||
}
|
||||
s = append(s, is)
|
||||
}
|
||||
|
||||
for i := 1; i < len(s); i++ {
|
||||
if !reflect.DeepEqual(s[i], s[0]) {
|
||||
return false, nil
|
||||
return errSRIAMConfigMismatch(peers[0].Name, peers[i].Name, s[0], s[i])
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Netperf for site-replication net perf
|
||||
@@ -658,7 +663,7 @@ func (c *SiteReplicationSys) Netperf(ctx context.Context, duration time.Duration
|
||||
for _, info := range infos.Sites {
|
||||
info := info
|
||||
// will call siteNetperf, means call others's adminAPISiteReplicationDevNull
|
||||
if globalDeploymentID == info.DeploymentID {
|
||||
if globalDeploymentID() == info.DeploymentID {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
@@ -1090,7 +1095,7 @@ func (c *SiteReplicationSys) PeerBucketConfigureReplHandler(ctx context.Context,
|
||||
defer c.RUnlock()
|
||||
errMap := make(map[string]error, len(c.state.Peers))
|
||||
for d, peer := range c.state.Peers {
|
||||
if d == globalDeploymentID {
|
||||
if d == globalDeploymentID() {
|
||||
continue
|
||||
}
|
||||
errMap[d] = configurePeerFn(d, peer)
|
||||
@@ -2112,7 +2117,7 @@ func (c *SiteReplicationSys) concDo(selfActionFn func() error, peerActionFn func
|
||||
for i := range depIDs {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
if depIDs[i] == globalDeploymentID {
|
||||
if depIDs[i] == globalDeploymentID() {
|
||||
if selfActionFn != nil {
|
||||
errs[i] = selfActionFn()
|
||||
}
|
||||
@@ -2193,11 +2198,11 @@ func (c *SiteReplicationSys) RemovePeerCluster(ctx context.Context, objectAPI Ob
|
||||
|
||||
for _, v := range info.Sites {
|
||||
wg.Add(1)
|
||||
if v.DeploymentID == globalDeploymentID {
|
||||
if v.DeploymentID == globalDeploymentID() {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
err := c.RemoveRemoteTargetsForEndpoint(ctx, objectAPI, rmvEndpoints, false)
|
||||
errs[globalDeploymentID] = err
|
||||
errs[globalDeploymentID()] = err
|
||||
}()
|
||||
continue
|
||||
}
|
||||
@@ -2209,7 +2214,7 @@ func (c *SiteReplicationSys) RemovePeerCluster(ctx context.Context, objectAPI Ob
|
||||
return
|
||||
}
|
||||
// set the requesting site's deploymentID for verification of peer request
|
||||
rreq.RequestingDepID = globalDeploymentID
|
||||
rreq.RequestingDepID = globalDeploymentID()
|
||||
if _, err = admClient.SRPeerRemove(ctx, rreq); err != nil {
|
||||
if errors.Is(err, errMissingSRConfig) {
|
||||
// ignore if peer is already removed.
|
||||
@@ -2223,7 +2228,7 @@ func (c *SiteReplicationSys) RemovePeerCluster(ctx context.Context, objectAPI Ob
|
||||
wg.Wait()
|
||||
|
||||
errdID := ""
|
||||
selfTgtsDeleted := errs[globalDeploymentID] == nil // true if all remote targets and replication config cleared successfully on local cluster
|
||||
selfTgtsDeleted := errs[globalDeploymentID()] == nil // true if all remote targets and replication config cleared successfully on local cluster
|
||||
|
||||
for dID, err := range errs {
|
||||
if err != nil {
|
||||
@@ -2311,7 +2316,7 @@ func (c *SiteReplicationSys) InternalRemoveReq(ctx context.Context, objectAPI Ob
|
||||
|
||||
for _, p := range c.state.Peers {
|
||||
peerMap[p.Name] = p
|
||||
if p.DeploymentID == globalDeploymentID {
|
||||
if p.DeploymentID == globalDeploymentID() {
|
||||
ourName = p.Name
|
||||
}
|
||||
updatedPeers[p.DeploymentID] = p
|
||||
@@ -2327,7 +2332,7 @@ func (c *SiteReplicationSys) InternalRemoveReq(ctx context.Context, objectAPI Ob
|
||||
if !ok {
|
||||
return errMissingSRConfig
|
||||
}
|
||||
if info.DeploymentID == globalDeploymentID {
|
||||
if info.DeploymentID == globalDeploymentID() {
|
||||
unlinkSelf = true
|
||||
continue
|
||||
}
|
||||
@@ -2608,7 +2613,7 @@ func (c *SiteReplicationSys) siteReplicationStatus(ctx context.Context, objAPI O
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sris[depIdx[globalDeploymentID]] = srInfo
|
||||
sris[depIdx[globalDeploymentID()]] = srInfo
|
||||
return nil
|
||||
},
|
||||
func(deploymentID string, p madmin.PeerInfo) error {
|
||||
@@ -3354,7 +3359,7 @@ func (c *SiteReplicationSys) SiteReplicationMetaInfo(ctx context.Context, objAPI
|
||||
if !c.enabled {
|
||||
return info, nil
|
||||
}
|
||||
info.DeploymentID = globalDeploymentID
|
||||
info.DeploymentID = globalDeploymentID()
|
||||
if opts.Buckets || opts.Entity == madmin.SRBucketEntity {
|
||||
var (
|
||||
buckets []BucketInfo
|
||||
@@ -3611,7 +3616,7 @@ func (c *SiteReplicationSys) EditPeerCluster(ctx context.Context, peer madmin.Pe
|
||||
admClient *madmin.AdminClient
|
||||
)
|
||||
|
||||
if globalDeploymentID == peer.DeploymentID && !peer.SyncState.Empty() && !peer.DefaultBandwidth.IsSet {
|
||||
if globalDeploymentID() == peer.DeploymentID && !peer.SyncState.Empty() && !peer.DefaultBandwidth.IsSet {
|
||||
return madmin.ReplicateEditStatus{}, errSRInvalidRequest(fmt.Errorf("a peer cluster, rather than the local cluster (endpoint=%s, deployment-id=%s) needs to be specified while setting a 'sync' replication mode", peer.Endpoint, peer.DeploymentID))
|
||||
}
|
||||
|
||||
@@ -3665,7 +3670,7 @@ func (c *SiteReplicationSys) EditPeerCluster(ctx context.Context, peer madmin.Pe
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for dID, v := range state.Peers {
|
||||
if v.DeploymentID == globalDeploymentID {
|
||||
if v.DeploymentID == globalDeploymentID() {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
@@ -3792,7 +3797,7 @@ func (c *SiteReplicationSys) PeerEditReq(ctx context.Context, arg madmin.PeerInf
|
||||
}
|
||||
c.state.Peers[arg.DeploymentID] = p
|
||||
}
|
||||
if p.DeploymentID == globalDeploymentID {
|
||||
if p.DeploymentID == globalDeploymentID() {
|
||||
ourName = p.Name
|
||||
}
|
||||
}
|
||||
@@ -3993,7 +3998,7 @@ func (c *SiteReplicationSys) healTagMetadata(ctx context.Context, objAPI ObjectL
|
||||
if isBucketMetadataEqual(latestTaggingConfig, bStatus.meta.Tags) {
|
||||
continue
|
||||
}
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
if _, err := globalBucketMetadataSys.Update(ctx, bucket, bucketTaggingConfig, latestTaggingConfigBytes); err != nil {
|
||||
logger.LogIf(ctx, fmt.Errorf("Unable to heal tagging metadata from peer site %s : %w", latestPeerName, err))
|
||||
}
|
||||
@@ -4057,7 +4062,7 @@ func (c *SiteReplicationSys) healBucketPolicies(ctx context.Context, objAPI Obje
|
||||
if strings.EqualFold(string(latestIAMPolicy), string(bStatus.meta.Policy)) {
|
||||
continue
|
||||
}
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
if _, err := globalBucketMetadataSys.Update(ctx, bucket, bucketPolicyConfig, latestIAMPolicy); err != nil {
|
||||
logger.LogIf(ctx, fmt.Errorf("Unable to heal bucket policy metadata from peer site %s : %w", latestPeerName, err))
|
||||
}
|
||||
@@ -4132,7 +4137,7 @@ func (c *SiteReplicationSys) healBucketQuotaConfig(ctx context.Context, objAPI O
|
||||
if isBucketMetadataEqual(latestQuotaConfig, bStatus.meta.QuotaConfig) {
|
||||
continue
|
||||
}
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
if _, err := globalBucketMetadataSys.Update(ctx, bucket, bucketQuotaConfigFile, latestQuotaConfigBytes); err != nil {
|
||||
logger.LogIf(ctx, fmt.Errorf("Unable to heal quota metadata from peer site %s : %w", latestPeerName, err))
|
||||
}
|
||||
@@ -4207,7 +4212,7 @@ func (c *SiteReplicationSys) healVersioningMetadata(ctx context.Context, objAPI
|
||||
if isBucketMetadataEqual(latestVersioningConfig, bStatus.meta.Versioning) {
|
||||
continue
|
||||
}
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
if _, err := globalBucketMetadataSys.Update(ctx, bucket, bucketVersioningConfig, latestVersioningConfigBytes); err != nil {
|
||||
logger.LogIf(ctx, fmt.Errorf("Unable to heal versioning metadata from peer site %s : %w", latestPeerName, err))
|
||||
}
|
||||
@@ -4282,7 +4287,7 @@ func (c *SiteReplicationSys) healSSEMetadata(ctx context.Context, objAPI ObjectL
|
||||
if isBucketMetadataEqual(latestSSEConfig, bStatus.meta.SSEConfig) {
|
||||
continue
|
||||
}
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
if _, err := globalBucketMetadataSys.Update(ctx, bucket, bucketSSEConfig, latestSSEConfigBytes); err != nil {
|
||||
logger.LogIf(ctx, fmt.Errorf("Unable to heal sse metadata from peer site %s : %w", latestPeerName, err))
|
||||
}
|
||||
@@ -4357,7 +4362,7 @@ func (c *SiteReplicationSys) healOLockConfigMetadata(ctx context.Context, objAPI
|
||||
if isBucketMetadataEqual(latestObjLockConfig, bStatus.meta.ObjectLockConfig) {
|
||||
continue
|
||||
}
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
if _, err := globalBucketMetadataSys.Update(ctx, bucket, objectLockConfig, latestObjLockConfigBytes); err != nil {
|
||||
logger.LogIf(ctx, fmt.Errorf("Unable to heal objectlock config metadata from peer site %s : %w", latestPeerName, err))
|
||||
}
|
||||
@@ -4445,7 +4450,7 @@ func (c *SiteReplicationSys) healBucket(ctx context.Context, objAPI ObjectLayer,
|
||||
bStatus := info.BucketStats[bucket][latestID].meta
|
||||
isMakeBucket := len(missingB) > 0
|
||||
deleteOp := NoOp
|
||||
if latestID != globalDeploymentID {
|
||||
if latestID != globalDeploymentID() {
|
||||
return nil
|
||||
}
|
||||
if lastUpdate.Equal(bStatus.DeletedAt) {
|
||||
@@ -4479,7 +4484,7 @@ func (c *SiteReplicationSys) healBucket(ctx context.Context, objAPI ObjectLayer,
|
||||
}
|
||||
for _, dID := range missingB {
|
||||
peerName := info.Sites[dID].Name
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
err := c.PeerBucketMakeWithVersioningHandler(ctx, bucket, opts)
|
||||
if err != nil {
|
||||
return c.annotateErr(makeBucketWithVersion, fmt.Errorf("error healing bucket for site replication %w from %s -> %s",
|
||||
@@ -4512,7 +4517,7 @@ func (c *SiteReplicationSys) healBucket(ctx context.Context, objAPI ObjectLayer,
|
||||
if deleteOp == Purge {
|
||||
for _, dID := range missingB {
|
||||
peerName := info.Sites[dID].Name
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
c.purgeDeletedBucket(ctx, objAPI, bucket)
|
||||
} else {
|
||||
admClient, err := c.getAdminClient(ctx, dID)
|
||||
@@ -4529,7 +4534,7 @@ func (c *SiteReplicationSys) healBucket(ctx context.Context, objAPI ObjectLayer,
|
||||
if deleteOp == MarkDelete {
|
||||
for _, dID := range withB {
|
||||
peerName := info.Sites[dID].Name
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
err := c.PeerBucketDeleteHandler(ctx, bucket, DeleteBucketOptions{
|
||||
Force: true,
|
||||
})
|
||||
@@ -4685,14 +4690,14 @@ func (c *SiteReplicationSys) healPolicies(ctx context.Context, objAPI ObjectLaye
|
||||
latestPolicyStat = ss
|
||||
}
|
||||
}
|
||||
if latestID != globalDeploymentID {
|
||||
if latestID != globalDeploymentID() {
|
||||
// heal only from the site with latest info.
|
||||
return nil
|
||||
}
|
||||
latestPeerName = info.Sites[latestID].Name
|
||||
// heal policy of peers if peer does not have it.
|
||||
for dID, pStatus := range ps {
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
continue
|
||||
}
|
||||
if !pStatus.PolicyMismatch && pStatus.HasPolicy {
|
||||
@@ -4739,14 +4744,14 @@ func (c *SiteReplicationSys) healUserPolicies(ctx context.Context, objAPI Object
|
||||
latestUserStat = ss
|
||||
}
|
||||
}
|
||||
if latestID != globalDeploymentID {
|
||||
if latestID != globalDeploymentID() {
|
||||
// heal only from the site with latest info.
|
||||
return nil
|
||||
}
|
||||
latestPeerName = info.Sites[latestID].Name
|
||||
// heal policy of peers if peer does not have it.
|
||||
for dID, pStatus := range us {
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
continue
|
||||
}
|
||||
if !pStatus.PolicyMismatch && pStatus.HasPolicyMapping {
|
||||
@@ -4800,14 +4805,14 @@ func (c *SiteReplicationSys) healGroupPolicies(ctx context.Context, objAPI Objec
|
||||
latestGroupStat = ss
|
||||
}
|
||||
}
|
||||
if latestID != globalDeploymentID {
|
||||
if latestID != globalDeploymentID() {
|
||||
// heal only from the site with latest info.
|
||||
return nil
|
||||
}
|
||||
latestPeerName = info.Sites[latestID].Name
|
||||
// heal policy of peers if peer does not have it.
|
||||
for dID, pStatus := range gs {
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
continue
|
||||
}
|
||||
if !pStatus.PolicyMismatch && pStatus.HasPolicyMapping {
|
||||
@@ -4862,13 +4867,13 @@ func (c *SiteReplicationSys) healUsers(ctx context.Context, objAPI ObjectLayer,
|
||||
latestUserStat = ss
|
||||
}
|
||||
}
|
||||
if latestID != globalDeploymentID {
|
||||
if latestID != globalDeploymentID() {
|
||||
// heal only from the site with latest info.
|
||||
return nil
|
||||
}
|
||||
latestPeerName = info.Sites[latestID].Name
|
||||
for dID, uStatus := range us {
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
continue
|
||||
}
|
||||
if !uStatus.UserInfoMismatch {
|
||||
@@ -5008,13 +5013,13 @@ func (c *SiteReplicationSys) healGroups(ctx context.Context, objAPI ObjectLayer,
|
||||
latestGroupStat = ss
|
||||
}
|
||||
}
|
||||
if latestID != globalDeploymentID {
|
||||
if latestID != globalDeploymentID() {
|
||||
// heal only from the site with latest info.
|
||||
return nil
|
||||
}
|
||||
latestPeerName = info.Sites[latestID].Name
|
||||
for dID, gStatus := range gs {
|
||||
if dID == globalDeploymentID {
|
||||
if dID == globalDeploymentID() {
|
||||
continue
|
||||
}
|
||||
if !gStatus.GroupDescMismatch {
|
||||
@@ -5113,7 +5118,7 @@ func (c *SiteReplicationSys) getPeerForUpload(deplID string) (pi srPeerInfo, loc
|
||||
PeerInfo: site,
|
||||
EndpointURL: ep,
|
||||
}
|
||||
return pi, site.DeploymentID == globalDeploymentID
|
||||
return pi, site.DeploymentID == globalDeploymentID()
|
||||
}
|
||||
}
|
||||
return pi, true
|
||||
@@ -5130,7 +5135,7 @@ func (c *SiteReplicationSys) startResync(ctx context.Context, objAPI ObjectLayer
|
||||
return res, errSRObjectLayerNotReady
|
||||
}
|
||||
|
||||
if peer.DeploymentID == globalDeploymentID {
|
||||
if peer.DeploymentID == globalDeploymentID() {
|
||||
return res, errSRResyncToSelf
|
||||
}
|
||||
if _, ok := c.state.Peers[peer.DeploymentID]; !ok {
|
||||
@@ -5244,7 +5249,7 @@ func (c *SiteReplicationSys) cancelResync(ctx context.Context, objAPI ObjectLaye
|
||||
if objAPI == nil {
|
||||
return res, errSRObjectLayerNotReady
|
||||
}
|
||||
if peer.DeploymentID == globalDeploymentID {
|
||||
if peer.DeploymentID == globalDeploymentID() {
|
||||
return res, errSRResyncToSelf
|
||||
}
|
||||
if _, ok := c.state.Peers[peer.DeploymentID]; !ok {
|
||||
|
||||
+54
-22
@@ -144,7 +144,8 @@ type storageRESTClient struct {
|
||||
// Indexes, will be -1 until assigned a set.
|
||||
poolIndex, setIndex, diskIndex int
|
||||
|
||||
diskInfoCache timedValue
|
||||
diskInfoCache timedValue
|
||||
diskInfoCacheMetrics timedValue
|
||||
}
|
||||
|
||||
// Retrieve location indexes.
|
||||
@@ -289,30 +290,61 @@ func (client *storageRESTClient) DiskInfo(_ context.Context, metrics bool) (info
|
||||
}
|
||||
// Do not cache results from atomic variables
|
||||
scanning := atomic.LoadInt32(&client.scanning) == 1
|
||||
client.diskInfoCache.Once.Do(func() {
|
||||
client.diskInfoCache.TTL = time.Second
|
||||
client.diskInfoCache.Update = func() (interface{}, error) {
|
||||
var info DiskInfo
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if metrics {
|
||||
client.diskInfoCacheMetrics.Once.Do(func() {
|
||||
client.diskInfoCacheMetrics.TTL = time.Second
|
||||
client.diskInfoCacheMetrics.Update = func() (interface{}, error) {
|
||||
var info DiskInfo
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
vals := make(url.Values)
|
||||
vals.Set(storageRESTMetrics, strconv.FormatBool(metrics))
|
||||
respBody, err := client.call(ctx, storageRESTMethodDiskInfo, vals, nil, -1)
|
||||
if err != nil {
|
||||
return info, err
|
||||
vals := make(url.Values)
|
||||
vals.Set(storageRESTMetrics, "true")
|
||||
respBody, err := client.call(ctx, storageRESTMethodDiskInfo, vals, nil, -1)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
defer xhttp.DrainBody(respBody)
|
||||
if err = msgp.Decode(respBody, &info); err != nil {
|
||||
return info, err
|
||||
}
|
||||
if info.Error != "" {
|
||||
return info, toStorageErr(errors.New(info.Error))
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
defer xhttp.DrainBody(respBody)
|
||||
if err = msgp.Decode(respBody, &info); err != nil {
|
||||
return info, err
|
||||
})
|
||||
} else {
|
||||
client.diskInfoCache.Once.Do(func() {
|
||||
client.diskInfoCache.TTL = time.Second
|
||||
client.diskInfoCache.Update = func() (interface{}, error) {
|
||||
var info DiskInfo
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
vals := make(url.Values)
|
||||
respBody, err := client.call(ctx, storageRESTMethodDiskInfo, vals, nil, -1)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
defer xhttp.DrainBody(respBody)
|
||||
if err = msgp.Decode(respBody, &info); err != nil {
|
||||
return info, err
|
||||
}
|
||||
if info.Error != "" {
|
||||
return info, toStorageErr(errors.New(info.Error))
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
if info.Error != "" {
|
||||
return info, toStorageErr(errors.New(info.Error))
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
})
|
||||
val, err := client.diskInfoCache.Get()
|
||||
})
|
||||
}
|
||||
|
||||
var val interface{}
|
||||
if metrics {
|
||||
val, err = client.diskInfoCacheMetrics.Get()
|
||||
} else {
|
||||
val, err = client.diskInfoCache.Get()
|
||||
}
|
||||
if val != nil {
|
||||
info = val.(DiskInfo)
|
||||
}
|
||||
|
||||
+2
-2
@@ -814,7 +814,7 @@ func newContext(r *http.Request, w http.ResponseWriter, api string) context.Cont
|
||||
bucket := vars["bucket"]
|
||||
object := likelyUnescapeGeneric(vars["object"], url.PathUnescape)
|
||||
reqInfo := &logger.ReqInfo{
|
||||
DeploymentID: globalDeploymentID,
|
||||
DeploymentID: globalDeploymentID(),
|
||||
RequestID: reqID,
|
||||
RemoteHost: handlers.GetSourceIP(r),
|
||||
Host: getHostName(r),
|
||||
@@ -1048,7 +1048,7 @@ func auditLogInternal(ctx context.Context, opts AuditLogOptions) {
|
||||
if len(logger.AuditTargets()) == 0 {
|
||||
return
|
||||
}
|
||||
entry := audit.NewEntry(globalDeploymentID)
|
||||
entry := audit.NewEntry(globalDeploymentID())
|
||||
entry.Trigger = opts.Event
|
||||
entry.Event = opts.Event
|
||||
entry.Error = opts.Error
|
||||
|
||||
+24
-7
@@ -22,6 +22,8 @@ import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
// From Veeam-SOSAPI_1.0_Document_v1.02d.pdf
|
||||
@@ -143,18 +145,33 @@ func veeamSOSAPIGetObject(ctx context.Context, bucket, object string, rs *HTTPRa
|
||||
if objAPI == nil {
|
||||
return nil, errServerNotInitialized
|
||||
}
|
||||
info := objAPI.StorageInfo(ctx)
|
||||
info.Backend = objAPI.BackendInfo()
|
||||
|
||||
usableTotal := int64(GetTotalUsableCapacity(info.Disks, info))
|
||||
usableFree := int64(GetTotalUsableCapacityFree(info.Disks, info))
|
||||
q, _ := globalBucketQuotaSys.Get(ctx, bucket)
|
||||
binfo, _ := globalBucketQuotaSys.GetBucketUsageInfo(bucket)
|
||||
|
||||
ci := capacityInfo{
|
||||
Capacity: usableTotal,
|
||||
Available: usableFree,
|
||||
Used: usableTotal - usableFree,
|
||||
Used: int64(binfo.Size),
|
||||
}
|
||||
|
||||
var quotaSize int64
|
||||
if q != nil && q.Type == madmin.HardQuota {
|
||||
if q.Size > 0 {
|
||||
quotaSize = int64(q.Size)
|
||||
} else if q.Quota > 0 {
|
||||
quotaSize = int64(q.Quota)
|
||||
}
|
||||
}
|
||||
|
||||
if quotaSize == 0 {
|
||||
info := objAPI.StorageInfo(ctx)
|
||||
info.Backend = objAPI.BackendInfo()
|
||||
|
||||
ci.Capacity = int64(GetTotalUsableCapacity(info.Disks, info))
|
||||
} else {
|
||||
ci.Capacity = quotaSize
|
||||
}
|
||||
ci.Available = ci.Capacity - ci.Used
|
||||
|
||||
buf = encodeResponse(&ci)
|
||||
default:
|
||||
return nil, errFileNotFound
|
||||
|
||||
@@ -86,6 +86,15 @@ type xlStorageDiskIDCheck struct {
|
||||
diskID string
|
||||
storage *xlStorage
|
||||
health *diskHealthTracker
|
||||
|
||||
// diskStartChecking is a threshold above which we will start to check
|
||||
// the state of disks, generally this value is less than diskMaxConcurrent
|
||||
diskStartChecking int
|
||||
|
||||
// diskMaxConcurrent represents maximum number of running concurrent
|
||||
// operations for local and (incoming) remote disk operations.
|
||||
diskMaxConcurrent int
|
||||
|
||||
metricsCache timedValue
|
||||
diskCtx context.Context
|
||||
cancel context.CancelFunc
|
||||
@@ -168,10 +177,45 @@ func (e *lockedLastMinuteLatency) total() AccElem {
|
||||
return e.lastMinuteLatency.getTotal()
|
||||
}
|
||||
|
||||
var maxConcurrentOnce sync.Once
|
||||
|
||||
func newXLStorageDiskIDCheck(storage *xlStorage, healthCheck bool) *xlStorageDiskIDCheck {
|
||||
// diskMaxConcurrent represents maximum number of running concurrent
|
||||
// operations for local and (incoming) remote disk operations.
|
||||
//
|
||||
// this value is a placeholder it is overridden via ENV for custom settings
|
||||
// or this default value is used to pick the correct value HDDs v/s NVMe's
|
||||
diskMaxConcurrent := -1
|
||||
maxConcurrentOnce.Do(func() {
|
||||
s := env.Get("_MINIO_DRIVE_MAX_CONCURRENT", "")
|
||||
if s == "" {
|
||||
s = env.Get("_MINIO_DISK_MAX_CONCURRENT", "")
|
||||
}
|
||||
if s != "" {
|
||||
diskMaxConcurrent, _ = strconv.Atoi(s)
|
||||
}
|
||||
})
|
||||
|
||||
if diskMaxConcurrent <= 0 {
|
||||
diskMaxConcurrent = 512
|
||||
if storage.rotational {
|
||||
diskMaxConcurrent = int(storage.nrRequests) / 2
|
||||
if diskMaxConcurrent == 0 {
|
||||
diskMaxConcurrent = 32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diskStartChecking := 16 + diskMaxConcurrent/8
|
||||
if diskStartChecking > diskMaxConcurrent {
|
||||
diskStartChecking = diskMaxConcurrent
|
||||
}
|
||||
|
||||
xl := xlStorageDiskIDCheck{
|
||||
storage: storage,
|
||||
health: newDiskHealthTracker(),
|
||||
storage: storage,
|
||||
health: newDiskHealthTracker(diskMaxConcurrent),
|
||||
diskMaxConcurrent: diskMaxConcurrent,
|
||||
diskStartChecking: diskStartChecking,
|
||||
}
|
||||
xl.diskCtx, xl.cancel = context.WithCancel(context.TODO())
|
||||
for i := range xl.apiLatencies[:] {
|
||||
@@ -709,14 +753,6 @@ const (
|
||||
diskHealthFaulty
|
||||
)
|
||||
|
||||
// diskMaxConcurrent is the maximum number of running concurrent operations
|
||||
// for local and (incoming) remote disk ops respectively.
|
||||
var diskMaxConcurrent = 512
|
||||
|
||||
// diskStartChecking is a threshold above which we will start to check
|
||||
// the state of disks.
|
||||
var diskStartChecking = 32
|
||||
|
||||
// diskMaxTimeoutOperation maximum wait time before we consider a drive
|
||||
// offline under active monitoring.
|
||||
var diskMaxTimeout = 2 * time.Minute
|
||||
@@ -725,18 +761,6 @@ var diskMaxTimeout = 2 * time.Minute
|
||||
var diskActiveMonitoring = true
|
||||
|
||||
func init() {
|
||||
s := env.Get("_MINIO_DRIVE_MAX_CONCURRENT", "")
|
||||
if s == "" {
|
||||
s = env.Get("_MINIO_DISK_MAX_CONCURRENT", "")
|
||||
}
|
||||
if s != "" {
|
||||
diskMaxConcurrent, _ = strconv.Atoi(s)
|
||||
if diskMaxConcurrent <= 0 {
|
||||
logger.Info("invalid _MINIO_DISK_MAX_CONCURRENT value: %s, defaulting to '512'", s)
|
||||
diskMaxConcurrent = 512
|
||||
}
|
||||
}
|
||||
|
||||
d := env.Get("_MINIO_DRIVE_MAX_TIMEOUT", "")
|
||||
if d == "" {
|
||||
d = env.Get("_MINIO_DISK_MAX_TIMEOUT", "")
|
||||
@@ -752,11 +776,6 @@ func init() {
|
||||
|
||||
diskActiveMonitoring = (env.Get("_MINIO_DRIVE_ACTIVE_MONITORING", config.EnableOn) == config.EnableOn) ||
|
||||
(env.Get("_MINIO_DISK_ACTIVE_MONITORING", config.EnableOn) == config.EnableOn)
|
||||
|
||||
diskStartChecking = 16 + diskMaxConcurrent/8
|
||||
if diskStartChecking > diskMaxConcurrent {
|
||||
diskStartChecking = diskMaxConcurrent
|
||||
}
|
||||
}
|
||||
|
||||
type diskHealthTracker struct {
|
||||
@@ -777,7 +796,7 @@ type diskHealthTracker struct {
|
||||
}
|
||||
|
||||
// newDiskHealthTracker creates a new disk health tracker.
|
||||
func newDiskHealthTracker() *diskHealthTracker {
|
||||
func newDiskHealthTracker(diskMaxConcurrent int) *diskHealthTracker {
|
||||
d := diskHealthTracker{
|
||||
lastSuccess: time.Now().UnixNano(),
|
||||
lastStarted: time.Now().UnixNano(),
|
||||
@@ -912,7 +931,7 @@ func (p *xlStorageDiskIDCheck) checkHealth(ctx context.Context) (err error) {
|
||||
return errFaultyDisk
|
||||
}
|
||||
// Check if there are tokens.
|
||||
if diskMaxConcurrent-len(p.health.tokens) < diskStartChecking {
|
||||
if p.diskMaxConcurrent-len(p.health.tokens) < p.diskStartChecking {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:RELEASE.2023-10-07T15-07-38Z
|
||||
image: quay.io/minio/minio:RELEASE.2023-10-16T04-13-43Z
|
||||
command: server --console-address ":9001" http://minio{1...4}/data{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
|
||||
@@ -59,7 +59,7 @@ require (
|
||||
github.com/minio/xxml v0.0.3
|
||||
github.com/minio/zipindex v0.3.0
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/nats-io/nats-server/v2 v2.9.20
|
||||
github.com/nats-io/nats-server/v2 v2.9.23
|
||||
github.com/nats-io/nats.go v1.30.0
|
||||
github.com/nats-io/stan.go v0.10.4
|
||||
github.com/ncw/directio v1.0.5
|
||||
@@ -192,7 +192,7 @@ require (
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.4.1 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.5.0 // indirect
|
||||
github.com/nats-io/nats-streaming-server v0.24.3 // indirect
|
||||
github.com/nats-io/nkeys v0.4.5 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
|
||||
@@ -529,11 +529,11 @@ github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nats-io/jwt/v2 v2.2.1-0.20220113022732-58e87895b296/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
|
||||
github.com/nats-io/jwt/v2 v2.4.1 h1:Y35W1dgbbz2SQUYDPCaclXcuqleVmpbRa7646Jf2EX4=
|
||||
github.com/nats-io/jwt/v2 v2.4.1/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI=
|
||||
github.com/nats-io/jwt/v2 v2.5.0 h1:WQQ40AAlqqfx+f6ku+i0pOVm+ASirD4fUh+oQsiE9Ak=
|
||||
github.com/nats-io/jwt/v2 v2.5.0/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI=
|
||||
github.com/nats-io/nats-server/v2 v2.7.4/go.mod h1:1vZ2Nijh8tcyNe8BDVyTviCd9NYzRbubQYiEHsvOQWc=
|
||||
github.com/nats-io/nats-server/v2 v2.9.20 h1:bt1dW6xsL1hWWwv7Hovm+EJt5L6iplyqlgEFkoEUk0k=
|
||||
github.com/nats-io/nats-server/v2 v2.9.20/go.mod h1:aTb/xtLCGKhfTFLxP591CMWfkdgBmcUUSkiSOe5A3gw=
|
||||
github.com/nats-io/nats-server/v2 v2.9.23 h1:6Wj6H6QpP9FMlpCyWUaNu2yeZ/qGj+mdRkZ1wbikExU=
|
||||
github.com/nats-io/nats-server/v2 v2.9.23/go.mod h1:wEjrEy9vnqIGE4Pqz4/c75v9Pmaq7My2IgFmnykc4C0=
|
||||
github.com/nats-io/nats-streaming-server v0.24.3 h1:uZez8jBkXscua++jaDsK7DhpSAkizdetar6yWbPMRco=
|
||||
github.com/nats-io/nats-streaming-server v0.24.3/go.mod h1:rqWfyCbxlhKj//fAp8POdQzeADwqkVhZcoWlbhkuU5w=
|
||||
github.com/nats-io/nats.go v1.13.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
// Copyright (c) 2015-2023 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
@@ -60,7 +60,7 @@ type ARN struct {
|
||||
|
||||
// Allows english letters, numbers, '.', '-', '_' and '/'. Starts with a
|
||||
// letter or digit. At least 1 character long.
|
||||
var validResourceIDRegex = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_/\.-]*$`)
|
||||
var validResourceIDRegex = regexp.MustCompile(`[A-Za-z0-9_/\.-]+$`)
|
||||
|
||||
// NewIAMRoleARN - returns an ARN for a role in MinIO.
|
||||
func NewIAMRoleARN(resourceID, serverRegion string) (ARN, error) {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2015-2023 MinIO, Inc.
|
||||
//
|
||||
// # This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package arn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewIAMRoleARN(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resourceID string
|
||||
serverRegion string
|
||||
expectedARN string
|
||||
isErrExpected bool
|
||||
}{
|
||||
{
|
||||
resourceID: "myrole",
|
||||
serverRegion: "us-east-1",
|
||||
expectedARN: "arn:minio:iam:us-east-1::role/myrole",
|
||||
isErrExpected: false,
|
||||
},
|
||||
{
|
||||
resourceID: "myrole",
|
||||
serverRegion: "",
|
||||
expectedARN: "arn:minio:iam:::role/myrole",
|
||||
isErrExpected: false,
|
||||
},
|
||||
{
|
||||
// Resource ID can start with a hyphen
|
||||
resourceID: "-myrole",
|
||||
serverRegion: "",
|
||||
expectedARN: "arn:minio:iam:::role/-myrole",
|
||||
isErrExpected: false,
|
||||
},
|
||||
{
|
||||
resourceID: "",
|
||||
serverRegion: "",
|
||||
expectedARN: "",
|
||||
isErrExpected: true,
|
||||
},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
arn, err := NewIAMRoleARN(testCase.resourceID, testCase.serverRegion)
|
||||
fmt.Println(arn, err)
|
||||
if err != nil {
|
||||
if !testCase.isErrExpected {
|
||||
t.Errorf("Test %d: Unexpected error: %v", i+1, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if testCase.isErrExpected {
|
||||
t.Errorf("Test %d: Expected error but got none", i+1)
|
||||
}
|
||||
if arn.String() != testCase.expectedARN {
|
||||
t.Errorf("Test %d: Expected ARN %s but got %s", i+1, testCase.expectedARN, arn.String())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,8 @@ func (lc Lifecycle) HasActiveRules(prefix string) bool {
|
||||
}
|
||||
|
||||
if len(prefix) > 0 && len(rule.GetPrefix()) > 0 {
|
||||
// If recursive, we can skip this rule if it doesn't match the tested prefix.
|
||||
// we can skip this rule if it doesn't match the tested
|
||||
// prefix.
|
||||
if !strings.HasPrefix(prefix, rule.GetPrefix()) && !strings.HasPrefix(rule.GetPrefix(), prefix) {
|
||||
continue
|
||||
}
|
||||
@@ -171,15 +172,15 @@ func (lc Lifecycle) HasActiveRules(prefix string) bool {
|
||||
if !rule.NoncurrentVersionTransition.IsNull() {
|
||||
return true
|
||||
}
|
||||
if rule.Expiration.IsNull() && rule.Transition.IsNull() {
|
||||
continue
|
||||
}
|
||||
if !rule.Expiration.IsDateNull() && rule.Expiration.Date.Before(time.Now().UTC()) {
|
||||
return true
|
||||
}
|
||||
if !rule.Expiration.IsDaysNull() {
|
||||
return true
|
||||
}
|
||||
if rule.Expiration.DeleteMarker.val {
|
||||
return true
|
||||
}
|
||||
if !rule.Transition.IsDateNull() && rule.Transition.Date.Before(time.Now().UTC()) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -575,7 +575,7 @@ func TestHasActiveRules(t *testing.T) {
|
||||
want: true,
|
||||
},
|
||||
{ // empty prefix
|
||||
inputConfig: `<LifecycleConfiguration><Rule><Status>Enabled</Status><Expiration><Days>5</Days></Expiration></Rule></LifecycleConfiguration>`,
|
||||
inputConfig: `<LifecycleConfiguration><Rule><Status>Enabled</Status><Filter></Filter><Expiration><Days>5</Days></Expiration></Rule></LifecycleConfiguration>`,
|
||||
prefix: "foodir/foobject/foo.txt",
|
||||
want: true,
|
||||
},
|
||||
@@ -600,15 +600,25 @@ func TestHasActiveRules(t *testing.T) {
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
inputConfig: `<LifecycleConfiguration><Rule><Status>Enabled</Status><Transition><StorageClass>S3TIER-1</StorageClass></Transition></Rule></LifecycleConfiguration>`,
|
||||
inputConfig: `<LifecycleConfiguration><Rule><Status>Enabled</Status><Filter></Filter><Transition><StorageClass>S3TIER-1</StorageClass></Transition></Rule></LifecycleConfiguration>`,
|
||||
prefix: "foodir/foobject/foo.txt",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
inputConfig: `<LifecycleConfiguration><Rule><Status>Enabled</Status><NoncurrentVersionTransition><StorageClass>S3TIER-1</StorageClass></NoncurrentVersionTransition></Rule></LifecycleConfiguration>`,
|
||||
inputConfig: `<LifecycleConfiguration><Rule><Status>Enabled</Status><Filter></Filter><NoncurrentVersionTransition><StorageClass>S3TIER-1</StorageClass></NoncurrentVersionTransition></Rule></LifecycleConfiguration>`,
|
||||
prefix: "foodir/foobject/foo.txt",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
inputConfig: `<LifecycleConfiguration><Rule><Status>Enabled</Status><Filter></Filter><Expiration><ExpiredObjectDeleteMarker>true</ExpiredObjectDeleteMarker></Expiration></Rule></LifecycleConfiguration>`,
|
||||
prefix: "",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
inputConfig: `<LifecycleConfiguration><Rule><Status>Enabled</Status><Filter></Filter><Expiration><Days>42</Days><ExpiredObjectAllVersions>true</ExpiredObjectAllVersions></Expiration></Rule></LifecycleConfiguration>`,
|
||||
prefix: "",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
@@ -618,8 +628,12 @@ func TestHasActiveRules(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Got unexpected error: %v", err)
|
||||
}
|
||||
// To ensure input lifecycle configurations are valid
|
||||
if err := lc.Validate(); err != nil {
|
||||
t.Fatalf("Invalid test case: %d %v", i+1, err)
|
||||
}
|
||||
if got := lc.HasActiveRules(tc.prefix); got != tc.want {
|
||||
t.Fatalf("Expected result with recursive set to false: `%v`, got: `%v`", tc.want, got)
|
||||
t.Fatalf("Expected result: `%v`, got: `%v`", tc.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1002,3 +1016,63 @@ func TestFilterAndSetPredictionHeaders(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterRules(t *testing.T) {
|
||||
lc := Lifecycle{
|
||||
Rules: []Rule{
|
||||
{
|
||||
ID: "rule-1",
|
||||
Status: "Enabled",
|
||||
Filter: Filter{
|
||||
Tag: Tag{
|
||||
Key: "key1",
|
||||
Value: "val1",
|
||||
},
|
||||
},
|
||||
Expiration: Expiration{
|
||||
Days: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
tests := []struct {
|
||||
opts ObjectOpts
|
||||
wantRule string
|
||||
}{
|
||||
{ // Delete marker should match filter without tags
|
||||
opts: ObjectOpts{
|
||||
DeleteMarker: true,
|
||||
IsLatest: true,
|
||||
Name: "obj-1",
|
||||
},
|
||||
wantRule: "rule-1",
|
||||
},
|
||||
{ // PUT version with no matching tags
|
||||
opts: ObjectOpts{
|
||||
IsLatest: true,
|
||||
Name: "obj-1",
|
||||
},
|
||||
wantRule: "",
|
||||
},
|
||||
{ // PUT version with matching tags
|
||||
opts: ObjectOpts{
|
||||
IsLatest: true,
|
||||
UserTags: "key1=val1",
|
||||
Name: "obj-1",
|
||||
},
|
||||
wantRule: "rule-1",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
t.Run(fmt.Sprintf("test-%d", i+1), func(t *testing.T) {
|
||||
rules := lc.FilterRules(tc.opts)
|
||||
if tc.wantRule != "" && len(rules) == 0 {
|
||||
t.Fatalf("%d: Expected rule match %s but none matched", i+1, tc.wantRule)
|
||||
}
|
||||
if tc.wantRule == "" && len(rules) > 0 {
|
||||
t.Fatalf("%d: Expected no rules to match but got matches %v", i+1, rules)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,6 @@ func DisableDirectIO(f *os.File) error {
|
||||
|
||||
// AlignedBlock simply returns an unaligned buffer
|
||||
// for systems that do not support DirectIO.
|
||||
func AlignedBlock(BlockSize int) []byte {
|
||||
return make([]byte, BlockSize)
|
||||
func AlignedBlock(blockSize int) []byte {
|
||||
return make([]byte, blockSize)
|
||||
}
|
||||
|
||||
@@ -81,11 +81,10 @@ func lockedOpenFile(path string, flag int, perm os.FileMode, lockType uint32) (*
|
||||
// acquire a write lock.
|
||||
func TryLockedOpenFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
var lockType uint32 = lockFileFailImmediately | lockFileExclusiveLock
|
||||
switch flag {
|
||||
case syscall.O_RDONLY:
|
||||
if flag == syscall.O_RDONLY {
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-lockfileex
|
||||
//lint:ignore SA4016 Reasons
|
||||
lockType = lockFileFailImmediately | 0 // Set this to enable shared lock and fail immediately.
|
||||
lockType = lockFileFailImmediately // Set this to enable shared lock and fail immediately.
|
||||
}
|
||||
return lockedOpenFile(path, flag, perm, lockType)
|
||||
}
|
||||
@@ -94,8 +93,7 @@ func TryLockedOpenFile(path string, flag int, perm os.FileMode) (*LockedFile, er
|
||||
// the file from concurrent access.
|
||||
func LockedOpenFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
var lockType uint32 = lockFileExclusiveLock
|
||||
switch flag {
|
||||
case syscall.O_RDONLY:
|
||||
if flag == syscall.O_RDONLY {
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-lockfileex
|
||||
lockType = 0 // Set this to enable shared lock.
|
||||
}
|
||||
@@ -239,15 +237,13 @@ func lockFile(fd syscall.Handle, flags uint32) error {
|
||||
}
|
||||
|
||||
err := lockFileEx(fd, flags, 1, 0, &syscall.Overlapped{})
|
||||
if err == nil {
|
||||
if err == nil || err == errLockViolation {
|
||||
return nil
|
||||
} else if err.Error() == "The process cannot access the file because another process has locked a portion of the file." {
|
||||
return ErrAlreadyLocked
|
||||
} else if err != errLockViolation {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
if err.Error() == "The process cannot access the file because another process has locked a portion of the file." {
|
||||
return ErrAlreadyLocked
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func lockFileEx(h syscall.Handle, flags, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
|
||||
|
||||
@@ -51,10 +51,10 @@ func TestFixLongPath(t *testing.T) {
|
||||
{`\\?\c:\long\foo.txt`, `\\?\c:\long\foo.txt`},
|
||||
{`\\?\c:\long/foo.txt`, `\\?\c:\long/foo.txt`},
|
||||
} {
|
||||
in := strings.Replace(test.in, "long", veryLong, -1)
|
||||
want := strings.Replace(test.want, "long", veryLong, -1)
|
||||
in := strings.ReplaceAll(test.in, "long", veryLong)
|
||||
want := strings.ReplaceAll(test.want, "long", veryLong)
|
||||
if got := fixLongPath(in); got != want {
|
||||
got = strings.Replace(got, veryLong, "long", -1)
|
||||
got = strings.ReplaceAll(got, veryLong, "long")
|
||||
t.Errorf("fixLongPath(%q) = %q; want %q", test.in, got, test.want)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user