mirror of
https://github.com/pgsty/minio.git
synced 2026-08-14 10:43:15 +03:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22b7226581 | |||
| f16f715b59 | |||
| 75adb787c4 | |||
| 6123377e66 | |||
| 778cccb15d | |||
| 0256dae657 | |||
| 88a93838de | |||
| 0855988427 | |||
| 84b121bbe1 | |||
| 48fb7b0dd7 | |||
| 01e550a9be | |||
| 63a2e0bab6 | |||
| 24657859a8 | |||
| 67d07e895c | |||
| d7df6bc738 | |||
| a4e1de93a7 | |||
| 41be557f0c | |||
| 9417fd933e | |||
| 067d21d0f2 | |||
| 3882da6ac5 | |||
| 77b780b8ca | |||
| 127e8bf3b6 | |||
| 74faed166a | |||
| dbd05d6e82 | |||
| b5d35c7e09 | |||
| c39eb3bacd | |||
| 57fad9148c | |||
| 0f88cdc80e | |||
| e2a9949b16 | |||
| 38e3c7a8f7 | |||
| 67f166fa02 | |||
| c7df5fb119 | |||
| a4be47d7ad | |||
| aaea94a48d | |||
| c3d9c45f58 | |||
| a2a48cc065 | |||
| cf407f7176 | |||
| d6dd17a483 | |||
| a66071099c | |||
| 9a6e569412 | |||
| 7dfa565d00 | |||
| d2e5f01542 | |||
| f4e373e0d2 | |||
| c8691db2b7 | |||
| affe51cb19 | |||
| 57118919d2 | |||
| 7db05a80dd | |||
| a8ba71edef | |||
| 45a99c3fd3 | |||
| 58e6b83e95 | |||
| f556a72fe2 |
@@ -8,8 +8,10 @@ assignees: ''
|
||||
---
|
||||
|
||||
## NOTE
|
||||
If this case is urgent, please subscribe to [Subnet](https://min.io/pricing) so that our 24/7 support team may help you faster.
|
||||
All GitHub issues are addressed on a best-effort basis at MinIO's sole discretion. There are no Service Level Agreements (SLA) or Objectives (SLO). Remember our [Code of Conduct](https://github.com/minio/minio/blob/master/code_of_conduct.md) when engaging with MinIO Engineers and the larger community.
|
||||
|
||||
For urgent issues (e.g. production down, etc.), subscribe to [SUBNET](https://min.io/pricing?jmp=github) for direct to engineering support.
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
@@ -18,8 +18,7 @@ jobs:
|
||||
- uses: dessant/lock-threads@v3
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '730'
|
||||
issue-inactive-days: '365'
|
||||
exclude-any-issue-labels: 'do-not-close'
|
||||
issue-lock-reason: 'resolved'
|
||||
process-only: 'issues'
|
||||
log-output: false
|
||||
log-output: true
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
FROM minio/minio:latest
|
||||
|
||||
ENV PATH=/opt/bin:$PATH
|
||||
|
||||
COPY ./minio /opt/bin/minio
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
FROM minio/minio:latest
|
||||
|
||||
ENV PATH=/opt/bin:$PATH
|
||||
|
||||
COPY ./minio /opt/bin/minio
|
||||
COPY dockerscripts/docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5
|
||||
|
||||
ARG RELEASE
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5
|
||||
|
||||
ARG TARGETARCH
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5
|
||||
|
||||
ARG TARGETARCH
|
||||
|
||||
|
||||
@@ -98,19 +98,20 @@ hotfix: hotfix-vars install ## builds minio binary with hotfix tags
|
||||
@sha256sum < ./minio.$(VERSION) | sed 's, -,minio.$(VERSION),g' > minio.$(VERSION).sha256sum
|
||||
|
||||
hotfix-push: hotfix
|
||||
@scp -r minio.$(VERSION)* minio@dl-0.minio.io:~/releases/server/minio/hotfixes/linux-amd64/archive/
|
||||
@scp -r minio.$(VERSION)* minio@dl-1.minio.io:~/releases/server/minio/hotfixes/linux-amd64/archive/
|
||||
@scp -q -r minio.$(VERSION)* minio@dl-0.minio.io:~/releases/server/minio/hotfixes/linux-amd64/archive/
|
||||
@scp -q -r minio.$(VERSION)* minio@dl-1.minio.io:~/releases/server/minio/hotfixes/linux-amd64/archive/
|
||||
@echo "Published new hotfix binaries at https://dl.min.io/server/minio/hotfixes/linux-amd64/archive/minio.$(VERSION)"
|
||||
|
||||
docker-hotfix-push: docker-hotfix
|
||||
@docker push $(TAG)
|
||||
@docker push -q $(TAG) && echo "Published new container $(TAG)"
|
||||
|
||||
docker-hotfix: hotfix-push checks ## builds minio docker container with hotfix tags
|
||||
@echo "Building minio docker image '$(TAG)'"
|
||||
@docker build -t $(TAG) --build-arg RELEASE=$(VERSION) . -f Dockerfile.hotfix
|
||||
@docker build -q --no-cache -t $(TAG) --build-arg RELEASE=$(VERSION) . -f Dockerfile.hotfix
|
||||
|
||||
docker: build checks ## builds minio docker container
|
||||
@echo "Building minio docker image '$(TAG)'"
|
||||
@docker build -t $(TAG) . -f Dockerfile
|
||||
@docker build -q --no-cache -t $(TAG) . -f Dockerfile
|
||||
|
||||
install: build ## builds minio and installs it to $GOPATH/bin.
|
||||
@echo "Installing minio binary to '$(GOPATH)/bin/minio'"
|
||||
|
||||
@@ -71,7 +71,7 @@ func (a adminAPIHandlers) PutBucketQuotaConfigHandler(w http.ResponseWriter, r *
|
||||
return
|
||||
}
|
||||
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketQuotaConfigFile, data); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketQuotaConfigFile, data); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -115,7 +115,7 @@ func (a adminAPIHandlers) GetBucketQuotaConfigHandler(w http.ResponseWriter, r *
|
||||
return
|
||||
}
|
||||
|
||||
config, err := globalBucketMetadataSys.GetQuotaConfig(bucket)
|
||||
config, err := globalBucketMetadataSys.GetQuotaConfig(ctx, bucket)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
@@ -246,7 +246,7 @@ func (a adminAPIHandlers) SetRemoteTargetHandler(w http.ResponseWriter, r *http.
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL)
|
||||
return
|
||||
}
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketTargetsFile, tgtBytes); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketTargetsFile, tgtBytes); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -340,7 +340,7 @@ func (a adminAPIHandlers) RemoveRemoteTargetHandler(w http.ResponseWriter, r *ht
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL)
|
||||
return
|
||||
}
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketTargetsFile, tgtBytes); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketTargetsFile, tgtBytes); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,6 +74,10 @@ func (a adminAPIHandlers) DelConfigKVHandler(w http.ResponseWriter, r *http.Requ
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
if err = validateConfig(cfg); err != nil {
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err = saveServerConfig(ctx, objectAPI, cfg); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
|
||||
@@ -253,18 +253,6 @@ func (a adminAPIHandlers) SRPeerReplicateBucketItem(w http.ResponseWriter, r *ht
|
||||
}
|
||||
}
|
||||
|
||||
// SiteReplicationDisable - PUT /minio/admin/v3/site-replication/disable
|
||||
func (a adminAPIHandlers) SiteReplicationDisable(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "SiteReplicationDisable")
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationDisableAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SiteReplicationInfo - GET /minio/admin/v3/site-replication/info
|
||||
func (a adminAPIHandlers) SiteReplicationInfo(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "SiteReplicationInfo")
|
||||
@@ -313,7 +301,6 @@ func parseJSONBody(ctx context.Context, body io.Reader, v interface{}, encryptio
|
||||
Code: ErrSiteReplicationInvalidRequest,
|
||||
}
|
||||
}
|
||||
|
||||
if encryptionKey != "" {
|
||||
data, err = madmin.DecryptData(encryptionKey, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
@@ -324,7 +311,6 @@ func parseJSONBody(ctx context.Context, body io.Reader, v interface{}, encryptio
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
@@ -338,8 +324,16 @@ func (a adminAPIHandlers) SiteReplicationStatus(w http.ResponseWriter, r *http.R
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
info, err := globalSiteReplicationSys.SiteReplicationStatus(ctx, objectAPI)
|
||||
opts := getSRStatusOptions(r)
|
||||
// default options to all if status options are unset for backward compatibility
|
||||
var dfltOpts madmin.SRStatusOptions
|
||||
if opts == dfltOpts {
|
||||
opts.Buckets = true
|
||||
opts.Users = true
|
||||
opts.Policies = true
|
||||
opts.Groups = true
|
||||
}
|
||||
info, err := globalSiteReplicationSys.SiteReplicationStatus(ctx, objectAPI, opts)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
@@ -362,7 +356,8 @@ func (a adminAPIHandlers) SiteReplicationMetaInfo(w http.ResponseWriter, r *http
|
||||
return
|
||||
}
|
||||
|
||||
info, err := globalSiteReplicationSys.SiteReplicationMetaInfo(ctx, objectAPI)
|
||||
opts := getSRStatusOptions(r)
|
||||
info, err := globalSiteReplicationSys.SiteReplicationMetaInfo(ctx, objectAPI, opts)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
@@ -428,3 +423,70 @@ func (a adminAPIHandlers) SRPeerEdit(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getSRStatusOptions(r *http.Request) (opts madmin.SRStatusOptions) {
|
||||
q := r.Form
|
||||
opts.Buckets = q.Get("buckets") == "true"
|
||||
opts.Policies = q.Get("policies") == "true"
|
||||
opts.Groups = q.Get("groups") == "true"
|
||||
opts.Users = q.Get("users") == "true"
|
||||
opts.Entity = madmin.GetSREntityType(q.Get("entity"))
|
||||
opts.EntityValue = q.Get("entityvalue")
|
||||
return
|
||||
}
|
||||
|
||||
// SiteReplicationRemove - PUT /minio/admin/v3/site-replication/remove
|
||||
func (a adminAPIHandlers) SiteReplicationRemove(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "SiteReplicationRemove")
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationRemoveAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
var rreq madmin.SRRemoveReq
|
||||
err := parseJSONBody(ctx, r.Body, &rreq, "")
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
status, err := globalSiteReplicationSys.RemovePeerCluster(ctx, objectAPI, rreq)
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := json.Marshal(status)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
writeSuccessResponseJSON(w, body)
|
||||
}
|
||||
|
||||
// SRPeerRemove - PUT /minio/admin/v3/site-replication/peer/remove
|
||||
//
|
||||
// used internally to tell current cluster to update endpoint for peer
|
||||
func (a adminAPIHandlers) SRPeerRemove(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "SRPeerRemove")
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationRemoveAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var req madmin.SRRemoveReq
|
||||
if err := parseJSONBody(ctx, r.Body, &req, ""); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err := globalSiteReplicationSys.InternalRemoveReq(ctx, objectAPI, req); err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1170,7 +1170,7 @@ func (a adminAPIHandlers) AccountInfoHandler(w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
|
||||
lcfg, _ := globalBucketObjectLockSys.Get(bucket.Name)
|
||||
quota, _ := globalBucketQuotaSys.Get(bucket.Name)
|
||||
quota, _ := globalBucketQuotaSys.Get(ctx, bucket.Name)
|
||||
rcfg, _ := globalBucketMetadataSys.GetReplicationConfig(ctx, bucket.Name)
|
||||
tcfg, _ := globalBucketMetadataSys.GetTaggingConfig(bucket.Name)
|
||||
|
||||
|
||||
+101
-5
@@ -36,6 +36,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
@@ -933,12 +934,17 @@ func (a adminAPIHandlers) BackgroundHealStatusHandler(w http.ResponseWriter, r *
|
||||
}
|
||||
}
|
||||
|
||||
// SpeedtestHandler - reports maximum speed of a cluster by performing PUT and
|
||||
// SpeedtestHandler - Deprecated. See ObjectSpeedtestHandler
|
||||
func (a adminAPIHandlers) SpeedtestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
a.ObjectSpeedtestHandler(w, r)
|
||||
}
|
||||
|
||||
// ObjectSpeedtestHandler - reports maximum speed of a cluster by performing PUT and
|
||||
// GET operations on the server, supports auto tuning by default by automatically
|
||||
// increasing concurrency and stopping when we have reached the limits on the
|
||||
// system.
|
||||
func (a adminAPIHandlers) SpeedtestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "SpeedtestHandler")
|
||||
func (a adminAPIHandlers) ObjectSpeedtestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "ObjectSpeedtestHandler")
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
@@ -978,8 +984,7 @@ func (a adminAPIHandlers) SpeedtestHandler(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
deleteBucket := func() {
|
||||
loc := pathJoin(minioMetaSpeedTestBucket, minioMetaSpeedTestBucketPrefix)
|
||||
objectAPI.DeleteBucket(context.Background(), loc, DeleteBucketOptions{
|
||||
objectAPI.DeleteBucket(context.Background(), pathJoin(minioMetaBucket, "speedtest"), DeleteBucketOptions{
|
||||
Force: true,
|
||||
NoRecreate: true,
|
||||
})
|
||||
@@ -1019,6 +1024,97 @@ func (a adminAPIHandlers) SpeedtestHandler(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
}
|
||||
|
||||
// NetSpeedtestHandler - reports maximum network throughput
|
||||
func (a adminAPIHandlers) NetSpeedtestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "NetSpeedtestHandler")
|
||||
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
}
|
||||
|
||||
// DriveSpeedtestHandler - reports throughput of drives available in the cluster
|
||||
func (a adminAPIHandlers) DriveSpeedtestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "DriveSpeedtestHandler")
|
||||
|
||||
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.HealthInfoAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !globalIsErasure {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Freeze all incoming S3 API calls before running speedtest.
|
||||
globalNotificationSys.ServiceFreeze(ctx, true)
|
||||
|
||||
// unfreeze all incoming S3 API calls after speedtest.
|
||||
defer globalNotificationSys.ServiceFreeze(ctx, false)
|
||||
|
||||
serial := r.Form.Get("serial") == "true"
|
||||
blockSizeStr := r.Form.Get("blocksize")
|
||||
fileSizeStr := r.Form.Get("filesize")
|
||||
|
||||
blockSize, err := strconv.ParseUint(blockSizeStr, 10, 64)
|
||||
if err != nil {
|
||||
blockSize = 4 * humanize.MiByte // default value
|
||||
}
|
||||
|
||||
fileSize, err := strconv.ParseUint(fileSizeStr, 10, 64)
|
||||
if err != nil {
|
||||
fileSize = 1 * humanize.GiByte // default value
|
||||
}
|
||||
|
||||
opts := madmin.DriveSpeedTestOpts{
|
||||
Serial: serial,
|
||||
BlockSize: blockSize,
|
||||
FileSize: fileSize,
|
||||
}
|
||||
|
||||
keepAliveTicker := time.NewTicker(500 * time.Millisecond)
|
||||
defer keepAliveTicker.Stop()
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
ch := globalNotificationSys.DriveSpeedTest(ctx, opts)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
// local driveSpeedTest
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
enc.Encode(driveSpeedTest(ctx, opts))
|
||||
if wf, ok := w.(http.Flusher); ok {
|
||||
wf.Flush()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
goto endloop
|
||||
case <-keepAliveTicker.C:
|
||||
// Write a blank entry to prevent client from disconnecting
|
||||
if err := enc.Encode(madmin.DriveSpeedTestResult{}); err != nil {
|
||||
goto endloop
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
case result, ok := <-ch:
|
||||
if !ok {
|
||||
goto endloop
|
||||
}
|
||||
if err := enc.Encode(result); err != nil {
|
||||
goto endloop
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
}
|
||||
endloop:
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Admin API errors
|
||||
const (
|
||||
AdminUpdateUnexpectedFailure = "XMinioAdminUpdateUnexpectedFailure"
|
||||
|
||||
+5
-1
@@ -197,7 +197,7 @@ func registerAdminRouter(router *mux.Router, enableConfigOps bool) {
|
||||
|
||||
// Cluster Replication APIs
|
||||
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/site-replication/add").HandlerFunc(gz(httpTraceHdrs(adminAPI.SiteReplicationAdd)))
|
||||
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/site-replication/disable").HandlerFunc(gz(httpTraceHdrs(adminAPI.SiteReplicationDisable)))
|
||||
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/site-replication/remove").HandlerFunc(gz(httpTraceHdrs(adminAPI.SiteReplicationRemove)))
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/site-replication/info").HandlerFunc(gz(httpTraceHdrs(adminAPI.SiteReplicationInfo)))
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/site-replication/metainfo").HandlerFunc(gz(httpTraceHdrs(adminAPI.SiteReplicationMetaInfo)))
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/site-replication/status").HandlerFunc(gz(httpTraceHdrs(adminAPI.SiteReplicationStatus)))
|
||||
@@ -209,6 +209,7 @@ func registerAdminRouter(router *mux.Router, enableConfigOps bool) {
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/site-replication/peer/idp-settings").HandlerFunc(gz(httpTraceHdrs(adminAPI.SRPeerGetIDPSettings)))
|
||||
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/site-replication/edit").HandlerFunc(gz(httpTraceHdrs(adminAPI.SiteReplicationEdit)))
|
||||
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/site-replication/peer/edit").HandlerFunc(gz(httpTraceHdrs(adminAPI.SRPeerEdit)))
|
||||
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/site-replication/peer/remove").HandlerFunc(gz(httpTraceHdrs(adminAPI.SRPeerRemove)))
|
||||
}
|
||||
|
||||
if globalIsDistErasure {
|
||||
@@ -220,6 +221,9 @@ func registerAdminRouter(router *mux.Router, enableConfigOps bool) {
|
||||
}
|
||||
|
||||
adminRouter.Methods(http.MethodPost).Path(adminVersion + "/speedtest").HandlerFunc(httpTraceHdrs(adminAPI.SpeedtestHandler))
|
||||
adminRouter.Methods(http.MethodPost).Path(adminVersion + "/speedtest/object").HandlerFunc(httpTraceHdrs(adminAPI.ObjectSpeedtestHandler))
|
||||
adminRouter.Methods(http.MethodPost).Path(adminVersion + "/speedtest/drive").HandlerFunc(httpTraceHdrs(adminAPI.DriveSpeedtestHandler))
|
||||
adminRouter.Methods(http.MethodPost).Path(adminVersion + "/speedtest/net").HandlerFunc(httpTraceHdrs(adminAPI.NetSpeedtestHandler))
|
||||
|
||||
// HTTP Trace
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/trace").HandlerFunc(gz(http.HandlerFunc(adminAPI.TraceHandler)))
|
||||
|
||||
@@ -92,7 +92,7 @@ func (api objectAPIHandlers) PutBucketEncryptionHandler(w http.ResponseWriter, r
|
||||
}
|
||||
|
||||
// Store the bucket encryption configuration in the object layer
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketSSEConfig, configData); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketSSEConfig, configData); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func (api objectAPIHandlers) DeleteBucketEncryptionHandler(w http.ResponseWriter
|
||||
}
|
||||
|
||||
// Delete bucket encryption config from object layer
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketSSEConfig, nil); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketSSEConfig, nil); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -951,6 +951,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
||||
// explicit permissions for the user.
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: cred.AccessKey,
|
||||
Groups: cred.Groups,
|
||||
Action: iampolicy.PutObjectAction,
|
||||
ConditionValues: getConditionValues(r, "", cred.AccessKey, cred.Claims),
|
||||
BucketName: bucket,
|
||||
@@ -1367,7 +1368,7 @@ func (api objectAPIHandlers) PutBucketObjectLockConfigHandler(w http.ResponseWri
|
||||
return
|
||||
}
|
||||
|
||||
if err = globalBucketMetadataSys.Update(bucket, objectLockConfig, configData); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, objectLockConfig, configData); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -1472,7 +1473,7 @@ func (api objectAPIHandlers) PutBucketTaggingHandler(w http.ResponseWriter, r *h
|
||||
return
|
||||
}
|
||||
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketTaggingConfig, configData); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketTaggingConfig, configData); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -1554,7 +1555,7 @@ func (api objectAPIHandlers) DeleteBucketTaggingHandler(w http.ResponseWriter, r
|
||||
return
|
||||
}
|
||||
|
||||
if err := globalBucketMetadataSys.Update(bucket, bucketTaggingConfig, nil); err != nil {
|
||||
if err := globalBucketMetadataSys.Update(ctx, bucket, bucketTaggingConfig, nil); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -1628,7 +1629,7 @@ func (api objectAPIHandlers) PutBucketReplicationConfigHandler(w http.ResponseWr
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketReplicationConfig, configData); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketReplicationConfig, configData); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -1707,7 +1708,7 @@ func (api objectAPIHandlers) DeleteBucketReplicationConfigHandler(w http.Respons
|
||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrReplicationDenyEditError), r.URL)
|
||||
return
|
||||
}
|
||||
if err := globalBucketMetadataSys.Update(bucket, bucketReplicationConfig, nil); err != nil {
|
||||
if err := globalBucketMetadataSys.Update(ctx, bucket, bucketReplicationConfig, nil); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -1860,7 +1861,7 @@ func (api objectAPIHandlers) ResetBucketReplicationStateHandler(w http.ResponseW
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL)
|
||||
return
|
||||
}
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketTargetsFile, tgtBytes); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketTargetsFile, tgtBytes); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ func (api objectAPIHandlers) PutBucketLifecycleHandler(w http.ResponseWriter, r
|
||||
return
|
||||
}
|
||||
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketLifecycleConfig, configData); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketLifecycleConfig, configData); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -168,7 +168,7 @@ func (api objectAPIHandlers) DeleteBucketLifecycleHandler(w http.ResponseWriter,
|
||||
return
|
||||
}
|
||||
|
||||
if err := globalBucketMetadataSys.Update(bucket, bucketLifecycleConfig, nil); err != nil {
|
||||
if err := globalBucketMetadataSys.Update(ctx, bucket, bucketLifecycleConfig, nil); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
+42
-5
@@ -165,6 +165,9 @@ type transitionState struct {
|
||||
killCh chan struct{}
|
||||
|
||||
activeTasks int32
|
||||
|
||||
lastDayMu sync.RWMutex
|
||||
lastDayStats map[string]*lastDayTierStats
|
||||
}
|
||||
|
||||
func (t *transitionState) queueTransitionTask(oi ObjectInfo) {
|
||||
@@ -186,6 +189,7 @@ func newTransitionState(ctx context.Context, objAPI ObjectLayer) *transitionStat
|
||||
ctx: ctx,
|
||||
objAPI: objAPI,
|
||||
killCh: make(chan struct{}),
|
||||
lastDayStats: make(map[string]*lastDayTierStats),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,14 +217,46 @@ func (t *transitionState) worker(ctx context.Context, objectAPI ObjectLayer) {
|
||||
return
|
||||
}
|
||||
atomic.AddInt32(&t.activeTasks, 1)
|
||||
if err := transitionObject(ctx, objectAPI, oi); err != nil {
|
||||
var tier string
|
||||
var err error
|
||||
if tier, err = transitionObject(ctx, objectAPI, oi); err != nil {
|
||||
logger.LogIf(ctx, fmt.Errorf("Transition failed for %s/%s version:%s with %w", oi.Bucket, oi.Name, oi.VersionID, err))
|
||||
}
|
||||
atomic.AddInt32(&t.activeTasks, -1)
|
||||
|
||||
ts := tierStats{
|
||||
TotalSize: uint64(oi.Size),
|
||||
NumVersions: 1,
|
||||
}
|
||||
if oi.IsLatest {
|
||||
ts.NumObjects = 1
|
||||
}
|
||||
t.addLastDayStats(tier, ts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *transitionState) addLastDayStats(tier string, ts tierStats) {
|
||||
t.lastDayMu.Lock()
|
||||
defer t.lastDayMu.Unlock()
|
||||
|
||||
if _, ok := t.lastDayStats[tier]; !ok {
|
||||
t.lastDayStats[tier] = &lastDayTierStats{}
|
||||
}
|
||||
t.lastDayStats[tier].addStats(ts)
|
||||
}
|
||||
|
||||
func (t *transitionState) getDailyAllTierStats() dailyAllTierStats {
|
||||
t.lastDayMu.RLock()
|
||||
defer t.lastDayMu.RUnlock()
|
||||
|
||||
res := make(dailyAllTierStats, len(t.lastDayStats))
|
||||
for tier, st := range t.lastDayStats {
|
||||
res[tier] = st.clone()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// UpdateWorkers at the end of this function leaves n goroutines waiting for
|
||||
// transition tasks
|
||||
func (t *transitionState) UpdateWorkers(n int) {
|
||||
@@ -365,15 +401,16 @@ func genTransitionObjName(bucket string) (string, error) {
|
||||
// storage specified by the transition ARN, the metadata is left behind on source cluster and original content
|
||||
// is moved to the transition tier. Note that in the case of encrypted objects, entire encrypted stream is moved
|
||||
// to the transition tier without decrypting or re-encrypting.
|
||||
func transitionObject(ctx context.Context, objectAPI ObjectLayer, oi ObjectInfo) error {
|
||||
func transitionObject(ctx context.Context, objectAPI ObjectLayer, oi ObjectInfo) (string, error) {
|
||||
lc, err := globalLifecycleSys.Get(oi.Bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
tier := lc.TransitionTier(oi.ToLifecycleOpts())
|
||||
opts := ObjectOptions{
|
||||
Transition: TransitionOptions{
|
||||
Status: lifecycle.TransitionPending,
|
||||
Tier: lc.TransitionTier(oi.ToLifecycleOpts()),
|
||||
Tier: tier,
|
||||
ETag: oi.ETag,
|
||||
},
|
||||
VersionID: oi.VersionID,
|
||||
@@ -381,7 +418,7 @@ func transitionObject(ctx context.Context, objectAPI ObjectLayer, oi ObjectInfo)
|
||||
VersionSuspended: globalBucketVersioningSys.Suspended(oi.Bucket),
|
||||
MTime: oi.ModTime,
|
||||
}
|
||||
return objectAPI.TransitionObject(ctx, oi.Bucket, oi.Name, opts)
|
||||
return tier, objectAPI.TransitionObject(ctx, oi.Bucket, oi.Name, opts)
|
||||
}
|
||||
|
||||
// getTransitionedObjectReader returns a reader from the transitioned tier.
|
||||
|
||||
+19
-19
@@ -74,7 +74,7 @@ func (sys *BucketMetadataSys) Set(bucket string, meta BucketMetadata) {
|
||||
|
||||
// Update update bucket metadata for the specified config file.
|
||||
// The configData data should not be modified after being sent here.
|
||||
func (sys *BucketMetadataSys) Update(bucket string, configFile string, configData []byte) error {
|
||||
func (sys *BucketMetadataSys) Update(ctx context.Context, bucket string, configFile string, configData []byte) error {
|
||||
objAPI := newObjectLayerFn()
|
||||
if objAPI == nil {
|
||||
return errServerNotInitialized
|
||||
@@ -83,13 +83,13 @@ func (sys *BucketMetadataSys) Update(bucket string, configFile string, configDat
|
||||
if globalIsGateway && globalGatewayName != NASBackendGateway {
|
||||
if configFile == bucketPolicyConfig {
|
||||
if configData == nil {
|
||||
return objAPI.DeleteBucketPolicy(GlobalContext, bucket)
|
||||
return objAPI.DeleteBucketPolicy(ctx, bucket)
|
||||
}
|
||||
config, err := policy.ParseConfig(bytes.NewReader(configData), bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return objAPI.SetBucketPolicy(GlobalContext, bucket, config)
|
||||
return objAPI.SetBucketPolicy(ctx, bucket, config)
|
||||
}
|
||||
return NotImplemented{}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func (sys *BucketMetadataSys) Update(bucket string, configFile string, configDat
|
||||
return errInvalidArgument
|
||||
}
|
||||
|
||||
meta, err := loadBucketMetadata(GlobalContext, objAPI, bucket)
|
||||
meta, err := loadBucketMetadata(ctx, objAPI, bucket)
|
||||
if err != nil {
|
||||
if !globalIsErasure && !globalIsDistErasure && errors.Is(err, errVolumeNotFound) {
|
||||
// Only single drive mode needs this fallback.
|
||||
@@ -148,12 +148,12 @@ func (sys *BucketMetadataSys) Update(bucket string, configFile string, configDat
|
||||
return fmt.Errorf("Unknown bucket %s metadata update requested %s", bucket, configFile)
|
||||
}
|
||||
|
||||
if err := meta.Save(GlobalContext, objAPI); err != nil {
|
||||
if err := meta.Save(ctx, objAPI); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sys.Set(bucket, meta)
|
||||
globalNotificationSys.LoadBucketMetadata(GlobalContext, bucket)
|
||||
globalNotificationSys.LoadBucketMetadata(bgContext(ctx), bucket) // Do not use caller context here
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -187,7 +187,7 @@ func (sys *BucketMetadataSys) Get(bucket string) (BucketMetadata, error) {
|
||||
// GetVersioningConfig returns configured versioning config
|
||||
// The returned object may not be modified.
|
||||
func (sys *BucketMetadataSys) GetVersioningConfig(bucket string) (*versioning.Versioning, error) {
|
||||
meta, err := sys.GetConfig(bucket)
|
||||
meta, err := sys.GetConfig(GlobalContext, bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -197,7 +197,7 @@ func (sys *BucketMetadataSys) GetVersioningConfig(bucket string) (*versioning.Ve
|
||||
// GetTaggingConfig returns configured tagging config
|
||||
// The returned object may not be modified.
|
||||
func (sys *BucketMetadataSys) GetTaggingConfig(bucket string) (*tags.Tags, error) {
|
||||
meta, err := sys.GetConfig(bucket)
|
||||
meta, err := sys.GetConfig(GlobalContext, bucket)
|
||||
if err != nil {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
return nil, BucketTaggingNotFound{Bucket: bucket}
|
||||
@@ -213,7 +213,7 @@ func (sys *BucketMetadataSys) GetTaggingConfig(bucket string) (*tags.Tags, error
|
||||
// GetObjectLockConfig returns configured object lock config
|
||||
// The returned object may not be modified.
|
||||
func (sys *BucketMetadataSys) GetObjectLockConfig(bucket string) (*objectlock.Config, error) {
|
||||
meta, err := sys.GetConfig(bucket)
|
||||
meta, err := sys.GetConfig(GlobalContext, bucket)
|
||||
if err != nil {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
return nil, BucketObjectLockConfigNotFound{Bucket: bucket}
|
||||
@@ -245,7 +245,7 @@ func (sys *BucketMetadataSys) GetLifecycleConfig(bucket string) (*lifecycle.Life
|
||||
return meta.lifecycleConfig, nil
|
||||
}
|
||||
|
||||
meta, err := sys.GetConfig(bucket)
|
||||
meta, err := sys.GetConfig(GlobalContext, bucket)
|
||||
if err != nil {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
return nil, BucketLifecycleNotFound{Bucket: bucket}
|
||||
@@ -274,7 +274,7 @@ func (sys *BucketMetadataSys) GetNotificationConfig(bucket string) (*event.Confi
|
||||
return meta.notificationConfig, nil
|
||||
}
|
||||
|
||||
meta, err := sys.GetConfig(bucket)
|
||||
meta, err := sys.GetConfig(GlobalContext, bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -284,7 +284,7 @@ func (sys *BucketMetadataSys) GetNotificationConfig(bucket string) (*event.Confi
|
||||
// GetSSEConfig returns configured SSE config
|
||||
// The returned object may not be modified.
|
||||
func (sys *BucketMetadataSys) GetSSEConfig(bucket string) (*bucketsse.BucketSSEConfig, error) {
|
||||
meta, err := sys.GetConfig(bucket)
|
||||
meta, err := sys.GetConfig(GlobalContext, bucket)
|
||||
if err != nil {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
return nil, BucketSSEConfigNotFound{Bucket: bucket}
|
||||
@@ -308,7 +308,7 @@ func (sys *BucketMetadataSys) GetPolicyConfig(bucket string) (*policy.Policy, er
|
||||
return objAPI.GetBucketPolicy(GlobalContext, bucket)
|
||||
}
|
||||
|
||||
meta, err := sys.GetConfig(bucket)
|
||||
meta, err := sys.GetConfig(GlobalContext, bucket)
|
||||
if err != nil {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
return nil, BucketPolicyNotFound{Bucket: bucket}
|
||||
@@ -323,8 +323,8 @@ func (sys *BucketMetadataSys) GetPolicyConfig(bucket string) (*policy.Policy, er
|
||||
|
||||
// GetQuotaConfig returns configured bucket quota
|
||||
// The returned object may not be modified.
|
||||
func (sys *BucketMetadataSys) GetQuotaConfig(bucket string) (*madmin.BucketQuota, error) {
|
||||
meta, err := sys.GetConfig(bucket)
|
||||
func (sys *BucketMetadataSys) GetQuotaConfig(ctx context.Context, bucket string) (*madmin.BucketQuota, error) {
|
||||
meta, err := sys.GetConfig(ctx, bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -334,7 +334,7 @@ func (sys *BucketMetadataSys) GetQuotaConfig(bucket string) (*madmin.BucketQuota
|
||||
// GetReplicationConfig returns configured bucket replication config
|
||||
// The returned object may not be modified.
|
||||
func (sys *BucketMetadataSys) GetReplicationConfig(ctx context.Context, bucket string) (*replication.Config, error) {
|
||||
meta, err := sys.GetConfig(bucket)
|
||||
meta, err := sys.GetConfig(ctx, bucket)
|
||||
if err != nil {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
return nil, BucketReplicationConfigNotFound{Bucket: bucket}
|
||||
@@ -351,7 +351,7 @@ func (sys *BucketMetadataSys) GetReplicationConfig(ctx context.Context, bucket s
|
||||
// GetBucketTargetsConfig returns configured bucket targets for this bucket
|
||||
// The returned object may not be modified.
|
||||
func (sys *BucketMetadataSys) GetBucketTargetsConfig(bucket string) (*madmin.BucketTargets, error) {
|
||||
meta, err := sys.GetConfig(bucket)
|
||||
meta, err := sys.GetConfig(GlobalContext, bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -377,7 +377,7 @@ func (sys *BucketMetadataSys) GetBucketTarget(bucket string, arn string) (madmin
|
||||
|
||||
// GetConfig returns a specific configuration from the bucket metadata.
|
||||
// The returned object may not be modified.
|
||||
func (sys *BucketMetadataSys) GetConfig(bucket string) (BucketMetadata, error) {
|
||||
func (sys *BucketMetadataSys) GetConfig(ctx context.Context, bucket string) (BucketMetadata, error) {
|
||||
objAPI := newObjectLayerFn()
|
||||
if objAPI == nil {
|
||||
return newBucketMetadata(bucket), errServerNotInitialized
|
||||
@@ -397,7 +397,7 @@ func (sys *BucketMetadataSys) GetConfig(bucket string) (BucketMetadata, error) {
|
||||
if ok {
|
||||
return meta, nil
|
||||
}
|
||||
meta, err := loadBucketMetadata(GlobalContext, objAPI, bucket)
|
||||
meta, err := loadBucketMetadata(ctx, objAPI, bucket)
|
||||
if err != nil {
|
||||
return meta, err
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
|
||||
return
|
||||
}
|
||||
|
||||
if err = globalBucketMetadataSys.Update(bucketName, bucketNotificationConfig, configData); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucketName, bucketNotificationConfig, configData); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
|
||||
return
|
||||
}
|
||||
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketPolicyConfig, configData); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketPolicyConfig, configData); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -148,7 +148,7 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
|
||||
return
|
||||
}
|
||||
|
||||
if err := globalBucketMetadataSys.Update(bucket, bucketPolicyConfig, nil); err != nil {
|
||||
if err := globalBucketMetadataSys.Update(ctx, bucket, bucketPolicyConfig, nil); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
+40
-144
@@ -24,8 +24,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/minio/internal/event"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
)
|
||||
|
||||
// BucketQuotaSys - map of bucket and quota configuration.
|
||||
@@ -34,7 +32,7 @@ type BucketQuotaSys struct {
|
||||
}
|
||||
|
||||
// Get - Get quota configuration.
|
||||
func (sys *BucketQuotaSys) Get(bucketName string) (*madmin.BucketQuota, error) {
|
||||
func (sys *BucketQuotaSys) Get(ctx context.Context, bucketName string) (*madmin.BucketQuota, error) {
|
||||
if globalIsGateway {
|
||||
objAPI := newObjectLayerFn()
|
||||
if objAPI == nil {
|
||||
@@ -43,7 +41,7 @@ func (sys *BucketQuotaSys) Get(bucketName string) (*madmin.BucketQuota, error) {
|
||||
return &madmin.BucketQuota{}, nil
|
||||
}
|
||||
|
||||
return globalBucketMetadataSys.GetQuotaConfig(bucketName)
|
||||
return globalBucketMetadataSys.GetQuotaConfig(ctx, bucketName)
|
||||
}
|
||||
|
||||
// NewBucketQuotaSys returns initialized BucketQuotaSys
|
||||
@@ -51,6 +49,35 @@ func NewBucketQuotaSys() *BucketQuotaSys {
|
||||
return &BucketQuotaSys{}
|
||||
}
|
||||
|
||||
// Init initialize bucket quota.
|
||||
func (sys *BucketQuotaSys) Init(objAPI ObjectLayer) {
|
||||
sys.bucketStorageCache.Once.Do(func() {
|
||||
sys.bucketStorageCache.TTL = 1 * time.Second
|
||||
sys.bucketStorageCache.Update = func() (interface{}, error) {
|
||||
ctx, done := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer done()
|
||||
|
||||
return loadDataUsageFromBackend(ctx, objAPI)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GetBucketUsageInfo return bucket usage info for a given bucket
|
||||
func (sys *BucketQuotaSys) GetBucketUsageInfo(bucket string) (BucketUsageInfo, error) {
|
||||
v, err := sys.bucketStorageCache.Get()
|
||||
if err != nil {
|
||||
return BucketUsageInfo{}, err
|
||||
}
|
||||
|
||||
dui, ok := v.(DataUsageInfo)
|
||||
if !ok {
|
||||
return BucketUsageInfo{}, fmt.Errorf("internal error: Unexpected DUI data type: %T", v)
|
||||
}
|
||||
|
||||
bui := dui.BucketsUsage[bucket]
|
||||
return bui, nil
|
||||
}
|
||||
|
||||
// parseBucketQuota parses BucketQuota from json
|
||||
func parseBucketQuota(bucket string, data []byte) (quotaCfg *madmin.BucketQuota, err error) {
|
||||
quotaCfg = &madmin.BucketQuota{}
|
||||
@@ -63,45 +90,23 @@ func parseBucketQuota(bucket string, data []byte) (quotaCfg *madmin.BucketQuota,
|
||||
return
|
||||
}
|
||||
|
||||
func (sys *BucketQuotaSys) check(ctx context.Context, bucket string, size int64) error {
|
||||
objAPI := newObjectLayerFn()
|
||||
if objAPI == nil {
|
||||
return errServerNotInitialized
|
||||
func (sys *BucketQuotaSys) enforceQuotaHard(ctx context.Context, bucket string, size int64) error {
|
||||
if size < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sys.bucketStorageCache.Once.Do(func() {
|
||||
sys.bucketStorageCache.TTL = 1 * time.Second
|
||||
sys.bucketStorageCache.Update = func() (interface{}, error) {
|
||||
ctx, done := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer done()
|
||||
return loadDataUsageFromBackend(ctx, objAPI)
|
||||
}
|
||||
})
|
||||
|
||||
q, err := sys.Get(bucket)
|
||||
q, err := sys.Get(ctx, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if q != nil && q.Type == madmin.HardQuota && q.Quota > 0 {
|
||||
v, err := sys.bucketStorageCache.Get()
|
||||
bui, err := sys.GetBucketUsageInfo(bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dui, ok := v.(DataUsageInfo)
|
||||
if !ok {
|
||||
return fmt.Errorf("internal error: Unexpected DUI data type: %T", v)
|
||||
}
|
||||
|
||||
bui, ok := dui.BucketsUsage[bucket]
|
||||
if !ok {
|
||||
// bucket not found, cannot enforce quota
|
||||
// call will fail anyways later.
|
||||
return nil
|
||||
}
|
||||
|
||||
if (bui.Size + uint64(size)) >= q.Quota {
|
||||
if bui.Size > 0 && ((bui.Size + uint64(size)) >= q.Quota) {
|
||||
return BucketQuotaExceeded{Bucket: bucket}
|
||||
}
|
||||
}
|
||||
@@ -109,118 +114,9 @@ func (sys *BucketQuotaSys) check(ctx context.Context, bucket string, size int64)
|
||||
return nil
|
||||
}
|
||||
|
||||
func enforceBucketQuota(ctx context.Context, bucket string, size int64) error {
|
||||
if size < 0 {
|
||||
func enforceBucketQuotaHard(ctx context.Context, bucket string, size int64) error {
|
||||
if globalBucketQuotaSys == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return globalBucketQuotaSys.check(ctx, bucket, size)
|
||||
}
|
||||
|
||||
// enforceFIFOQuota deletes objects in FIFO order until sufficient objects
|
||||
// have been deleted so as to bring bucket usage within quota.
|
||||
func enforceFIFOQuotaBucket(ctx context.Context, objectAPI ObjectLayer, bucket string, bui BucketUsageInfo) {
|
||||
// Check if the current bucket has quota restrictions, if not skip it
|
||||
cfg, err := globalBucketQuotaSys.Get(bucket)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cfg.Type != madmin.FIFOQuota {
|
||||
return
|
||||
}
|
||||
|
||||
var toFree uint64
|
||||
if bui.Size > cfg.Quota && cfg.Quota > 0 {
|
||||
toFree = bui.Size - cfg.Quota
|
||||
}
|
||||
|
||||
if toFree <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Allocate new results channel to receive ObjectInfo.
|
||||
objInfoCh := make(chan ObjectInfo)
|
||||
|
||||
versioned := globalBucketVersioningSys.Enabled(bucket)
|
||||
|
||||
// Walk through all objects
|
||||
if err := objectAPI.Walk(ctx, bucket, "", objInfoCh, ObjectOptions{WalkVersions: versioned}); err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
// reuse the fileScorer used by disk cache to score entries by
|
||||
// ModTime to find the oldest objects in bucket to delete. In
|
||||
// the context of bucket quota enforcement - number of hits are
|
||||
// irrelevant.
|
||||
scorer, err := newFileScorer(toFree, UTCNow().Unix(), 1)
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
rcfg, _ := globalBucketObjectLockSys.Get(bucket)
|
||||
for obj := range objInfoCh {
|
||||
// skip objects currently under retention
|
||||
if rcfg.LockEnabled && enforceRetentionForDeletion(ctx, obj) {
|
||||
continue
|
||||
}
|
||||
scorer.addFileWithObjInfo(obj, 1)
|
||||
}
|
||||
|
||||
// If we saw less than quota we are good.
|
||||
if scorer.seenBytes <= cfg.Quota {
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate how much we want to delete now.
|
||||
toFreeNow := scorer.seenBytes - cfg.Quota
|
||||
// We were less over quota than we thought. Adjust so we delete less.
|
||||
// If we are more over, leave it for the next run to pick up.
|
||||
if toFreeNow < toFree {
|
||||
if !scorer.adjustSaveBytes(int64(toFreeNow) - int64(toFree)) {
|
||||
// We got below or at quota.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var objects []ObjectToDelete
|
||||
numKeys := len(scorer.fileObjInfos())
|
||||
for i, obj := range scorer.fileObjInfos() {
|
||||
objects = append(objects, ObjectToDelete{
|
||||
ObjectV: ObjectV{
|
||||
ObjectName: obj.Name,
|
||||
VersionID: obj.VersionID,
|
||||
},
|
||||
})
|
||||
if len(objects) < maxDeleteList && (i < numKeys-1) {
|
||||
// skip deletion until maxDeleteList or end of slice
|
||||
continue
|
||||
}
|
||||
|
||||
if len(objects) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Deletes a list of objects.
|
||||
_, deleteErrs := objectAPI.DeleteObjects(ctx, bucket, objects, ObjectOptions{
|
||||
Versioned: versioned,
|
||||
})
|
||||
for j := range deleteErrs {
|
||||
if deleteErrs[j] != nil {
|
||||
logger.LogIf(ctx, deleteErrs[j])
|
||||
continue
|
||||
}
|
||||
|
||||
// Notify object deleted event.
|
||||
sendEvent(eventArgs{
|
||||
EventName: event.ObjectRemovedDelete,
|
||||
BucketName: bucket,
|
||||
Object: obj,
|
||||
Host: "Internal: [FIFO-QUOTA-EXPIRY]",
|
||||
})
|
||||
}
|
||||
objects = nil
|
||||
}
|
||||
return globalBucketQuotaSys.enforceQuotaHard(ctx, bucket, size)
|
||||
}
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ func (rl ReplicationLatency) merge(other ReplicationLatency) (newReplLatency Rep
|
||||
// Get upload latency of each object size range
|
||||
func (rl ReplicationLatency) getUploadLatency() (ret map[string]uint64) {
|
||||
ret = make(map[string]uint64)
|
||||
avg := rl.UploadHistogram.GetAvg()
|
||||
avg := rl.UploadHistogram.GetAvgData()
|
||||
for k, v := range avg {
|
||||
// Convert nanoseconds to milliseconds
|
||||
ret[sizeTagToString(k)] = v.avg() / uint64(time.Millisecond)
|
||||
|
||||
@@ -95,7 +95,7 @@ func (api objectAPIHandlers) PutBucketVersioningHandler(w http.ResponseWriter, r
|
||||
return
|
||||
}
|
||||
|
||||
if err = globalBucketMetadataSys.Update(bucket, bucketVersioningConfig, configData); err != nil {
|
||||
if err = globalBucketMetadataSys.Update(ctx, bucket, bucketVersioningConfig, configData); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -213,6 +213,9 @@ func minioConfigToConsoleFeatures() {
|
||||
if globalSubnetConfig.APIKey != "" {
|
||||
os.Setenv("CONSOLE_SUBNET_API_KEY", globalSubnetConfig.APIKey)
|
||||
}
|
||||
if globalSubnetConfig.Proxy != "" {
|
||||
os.Setenv("CONSOLE_SUBNET_PROXY", globalSubnetConfig.Proxy)
|
||||
}
|
||||
}
|
||||
|
||||
func initConsoleServer() (*restapi.Server, error) {
|
||||
|
||||
@@ -361,6 +361,10 @@ func validateConfig(s config.Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = subnet.LookupConfig(s[config.SubnetSubSys][config.Default]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return notify.TestNotificationTargets(GlobalContext, s, NewGatewayHTTPTransport(), globalNotificationSys.ConfiguredTargetIDs())
|
||||
}
|
||||
|
||||
|
||||
+16
-12
@@ -22,6 +22,7 @@ import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"math"
|
||||
"math/rand"
|
||||
@@ -33,6 +34,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bits-and-blooms/bloom/v3"
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/minio/internal/bucket/lifecycle"
|
||||
"github.com/minio/minio/internal/bucket/replication"
|
||||
@@ -181,7 +183,8 @@ type folderScanner struct {
|
||||
healFolderInclude uint32 // Include a clean folder one in n cycles.
|
||||
healObjectSelect uint32 // Do a heal check on an object once every n cycles. Must divide into healFolderInclude
|
||||
|
||||
disks []StorageAPI
|
||||
disks []StorageAPI
|
||||
disksQuorum int
|
||||
|
||||
// If set updates will be sent regularly to this channel.
|
||||
// Will not be closed when returned.
|
||||
@@ -247,7 +250,7 @@ var globalScannerStats scannerStats
|
||||
// The returned cache will always be valid, but may not be updated from the existing.
|
||||
// Before each operation sleepDuration is called which can be used to temporarily halt the scanner.
|
||||
// If the supplied context is canceled the function will return at the first chance.
|
||||
func scanDataFolder(ctx context.Context, basePath string, cache dataUsageCache, getSize getSizeFn) (dataUsageCache, error) {
|
||||
func scanDataFolder(ctx context.Context, poolIdx, setIdx int, basePath string, cache dataUsageCache, getSize getSizeFn) (dataUsageCache, error) {
|
||||
t := UTCNow()
|
||||
|
||||
logPrefix := color.Green("data-usage: ")
|
||||
@@ -280,13 +283,15 @@ func scanDataFolder(ctx context.Context, basePath string, cache dataUsageCache,
|
||||
}
|
||||
|
||||
// Add disks for set healing.
|
||||
if len(cache.Disks) > 0 {
|
||||
if poolIdx >= 0 && setIdx >= 0 {
|
||||
objAPI, ok := newObjectLayerFn().(*erasureServerPools)
|
||||
if ok {
|
||||
s.disks = objAPI.GetDisksID(cache.Disks...)
|
||||
if len(s.disks) != len(cache.Disks) {
|
||||
console.Debugf(logPrefix+"Missing disks, want %d, found %d. Cannot heal. %s\n", len(cache.Disks), len(s.disks), logSuffix)
|
||||
s.disks = s.disks[:0]
|
||||
if poolIdx < len(objAPI.serverPools) && setIdx < len(objAPI.serverPools[poolIdx].sets) {
|
||||
// Pass the disks belonging to the set.
|
||||
s.disks = objAPI.serverPools[poolIdx].sets[setIdx].getDisks()
|
||||
s.disksQuorum = len(s.disks) / 2
|
||||
} else {
|
||||
logger.LogIf(ctx, fmt.Errorf("Matching pool %s, set %s not found", humanize.Ordinal(poolIdx+1), humanize.Ordinal(setIdx+1)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -623,7 +628,7 @@ func (f *folderScanner) scanFolder(ctx context.Context, folder cachedFolder, int
|
||||
}
|
||||
|
||||
objAPI, ok := newObjectLayerFn().(*erasureServerPools)
|
||||
if !ok || len(f.disks) == 0 {
|
||||
if !ok || len(f.disks) == 0 || f.disksQuorum == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -645,8 +650,8 @@ func (f *folderScanner) scanFolder(ctx context.Context, folder cachedFolder, int
|
||||
// This means that the next run will not look for it.
|
||||
// How to resolve results.
|
||||
resolver := metadataResolutionParams{
|
||||
dirQuorum: getReadQuorum(len(f.disks)),
|
||||
objQuorum: getReadQuorum(len(f.disks)),
|
||||
dirQuorum: f.disksQuorum,
|
||||
objQuorum: f.disksQuorum,
|
||||
bucket: "",
|
||||
strict: false,
|
||||
}
|
||||
@@ -676,8 +681,7 @@ func (f *folderScanner) scanFolder(ctx context.Context, folder cachedFolder, int
|
||||
path: prefix,
|
||||
recursive: true,
|
||||
reportNotFound: true,
|
||||
minDisks: len(f.disks), // We want full consistency.
|
||||
// Weird, maybe transient error.
|
||||
minDisks: f.disksQuorum,
|
||||
agreed: func(entry metaCacheEntry) {
|
||||
if f.dataUsageScannerDebug {
|
||||
console.Debugf(healObjectsPrefix+" got agreement: %v\n", entry.name)
|
||||
|
||||
@@ -225,7 +225,6 @@ type dataUsageEntryV6 struct {
|
||||
type dataUsageCache struct {
|
||||
Info dataUsageCacheInfo
|
||||
Cache map[string]dataUsageEntry
|
||||
Disks []string
|
||||
}
|
||||
|
||||
//msgp:encode ignore dataUsageCacheV2 dataUsageCacheV3 dataUsageCacheV4 dataUsageCacheV5 dataUsageCacheV6
|
||||
@@ -234,35 +233,30 @@ type dataUsageCache struct {
|
||||
// dataUsageCacheV2 contains a cache of data usage entries version 2.
|
||||
type dataUsageCacheV2 struct {
|
||||
Info dataUsageCacheInfo
|
||||
Disks []string
|
||||
Cache map[string]dataUsageEntryV2
|
||||
}
|
||||
|
||||
// dataUsageCacheV3 contains a cache of data usage entries version 3.
|
||||
type dataUsageCacheV3 struct {
|
||||
Info dataUsageCacheInfo
|
||||
Disks []string
|
||||
Cache map[string]dataUsageEntryV3
|
||||
}
|
||||
|
||||
// dataUsageCacheV4 contains a cache of data usage entries version 4.
|
||||
type dataUsageCacheV4 struct {
|
||||
Info dataUsageCacheInfo
|
||||
Disks []string
|
||||
Cache map[string]dataUsageEntryV4
|
||||
}
|
||||
|
||||
// dataUsageCacheV5 contains a cache of data usage entries version 5.
|
||||
type dataUsageCacheV5 struct {
|
||||
Info dataUsageCacheInfo
|
||||
Disks []string
|
||||
Cache map[string]dataUsageEntryV5
|
||||
}
|
||||
|
||||
// dataUsageCacheV6 contains a cache of data usage entries version 6.
|
||||
type dataUsageCacheV6 struct {
|
||||
Info dataUsageCacheInfo
|
||||
Disks []string
|
||||
Cache map[string]dataUsageEntryV6
|
||||
}
|
||||
|
||||
@@ -804,35 +798,6 @@ func (d *dataUsageCache) bucketsUsageInfo(buckets []BucketInfo) map[string]Bucke
|
||||
return dst
|
||||
}
|
||||
|
||||
// bucketUsageInfo returns the buckets usage info.
|
||||
// If not found all values returned are zero values.
|
||||
func (d *dataUsageCache) bucketUsageInfo(bucket string) BucketUsageInfo {
|
||||
e := d.find(bucket)
|
||||
if e == nil {
|
||||
return BucketUsageInfo{}
|
||||
}
|
||||
flat := d.flatten(*e)
|
||||
bui := BucketUsageInfo{
|
||||
Size: uint64(flat.Size),
|
||||
ObjectsCount: flat.Objects,
|
||||
ObjectSizesHistogram: flat.ObjSizes.toMap(),
|
||||
}
|
||||
if flat.ReplicationStats != nil {
|
||||
bui.ReplicaSize = flat.ReplicationStats.ReplicaSize
|
||||
bui.ReplicationInfo = make(map[string]BucketTargetUsageInfo, len(flat.ReplicationStats.Targets))
|
||||
for arn, stat := range flat.ReplicationStats.Targets {
|
||||
bui.ReplicationInfo[arn] = BucketTargetUsageInfo{
|
||||
ReplicationPendingSize: stat.PendingSize,
|
||||
ReplicatedSize: stat.ReplicatedSize,
|
||||
ReplicationFailedSize: stat.FailedSize,
|
||||
ReplicationPendingCount: stat.PendingCount,
|
||||
ReplicationFailedCount: stat.FailedCount,
|
||||
}
|
||||
}
|
||||
}
|
||||
return bui
|
||||
}
|
||||
|
||||
// sizeRecursive returns the path as a flattened entry.
|
||||
func (d *dataUsageCache) sizeRecursive(path string) *dataUsageEntry {
|
||||
root := d.find(path)
|
||||
@@ -1037,7 +1002,6 @@ func (d *dataUsageCache) deserialize(r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
d.Info = dold.Info
|
||||
d.Disks = dold.Disks
|
||||
d.Cache = make(map[string]dataUsageEntry, len(dold.Cache))
|
||||
for k, v := range dold.Cache {
|
||||
d.Cache[k] = dataUsageEntry{
|
||||
@@ -1061,7 +1025,6 @@ func (d *dataUsageCache) deserialize(r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
d.Info = dold.Info
|
||||
d.Disks = dold.Disks
|
||||
d.Cache = make(map[string]dataUsageEntry, len(dold.Cache))
|
||||
for k, v := range dold.Cache {
|
||||
due := dataUsageEntry{
|
||||
@@ -1101,7 +1064,6 @@ func (d *dataUsageCache) deserialize(r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
d.Info = dold.Info
|
||||
d.Disks = dold.Disks
|
||||
d.Cache = make(map[string]dataUsageEntry, len(dold.Cache))
|
||||
for k, v := range dold.Cache {
|
||||
due := dataUsageEntry{
|
||||
@@ -1153,7 +1115,6 @@ func (d *dataUsageCache) deserialize(r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
d.Info = dold.Info
|
||||
d.Disks = dold.Disks
|
||||
d.Cache = make(map[string]dataUsageEntry, len(dold.Cache))
|
||||
for k, v := range dold.Cache {
|
||||
due := dataUsageEntry{
|
||||
@@ -1205,7 +1166,6 @@ func (d *dataUsageCache) deserialize(r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
d.Info = dold.Info
|
||||
d.Disks = dold.Disks
|
||||
d.Cache = make(map[string]dataUsageEntry, len(dold.Cache))
|
||||
for k, v := range dold.Cache {
|
||||
var replicationStats *replicationAllStats
|
||||
|
||||
+134
-409
@@ -338,25 +338,6 @@ func (z *dataUsageCache) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
}
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
case "Disks":
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0003) {
|
||||
z.Disks = (z.Disks)[:zb0003]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0003)
|
||||
}
|
||||
for za0003 := range z.Disks {
|
||||
z.Disks[za0003], err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0003)
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
@@ -370,9 +351,9 @@ func (z *dataUsageCache) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *dataUsageCache) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 3
|
||||
// map header, size 2
|
||||
// write "Info"
|
||||
err = en.Append(0x83, 0xa4, 0x49, 0x6e, 0x66, 0x6f)
|
||||
err = en.Append(0x82, 0xa4, 0x49, 0x6e, 0x66, 0x6f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -403,32 +384,15 @@ func (z *dataUsageCache) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// write "Disks"
|
||||
err = en.Append(0xa5, 0x44, 0x69, 0x73, 0x6b, 0x73)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteArrayHeader(uint32(len(z.Disks)))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
for za0003 := range z.Disks {
|
||||
err = en.WriteString(z.Disks[za0003])
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0003)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *dataUsageCache) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 3
|
||||
// map header, size 2
|
||||
// string "Info"
|
||||
o = append(o, 0x83, 0xa4, 0x49, 0x6e, 0x66, 0x6f)
|
||||
o = append(o, 0x82, 0xa4, 0x49, 0x6e, 0x66, 0x6f)
|
||||
o, err = z.Info.MarshalMsg(o)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
@@ -445,12 +409,6 @@ func (z *dataUsageCache) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// string "Disks"
|
||||
o = append(o, 0xa5, 0x44, 0x69, 0x73, 0x6b, 0x73)
|
||||
o = msgp.AppendArrayHeader(o, uint32(len(z.Disks)))
|
||||
for za0003 := range z.Disks {
|
||||
o = msgp.AppendString(o, z.Disks[za0003])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -508,25 +466,6 @@ func (z *dataUsageCache) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
}
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
case "Disks":
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0003) {
|
||||
z.Disks = (z.Disks)[:zb0003]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0003)
|
||||
}
|
||||
for za0003 := range z.Disks {
|
||||
z.Disks[za0003], bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0003)
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
@@ -548,10 +487,6 @@ func (z *dataUsageCache) Msgsize() (s int) {
|
||||
s += msgp.StringPrefixSize + len(za0001) + za0002.Msgsize()
|
||||
}
|
||||
}
|
||||
s += 6 + msgp.ArrayHeaderSize
|
||||
for za0003 := range z.Disks {
|
||||
s += msgp.StringPrefixSize + len(z.Disks[za0003])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -811,54 +746,35 @@ func (z *dataUsageCacheV2) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
return
|
||||
}
|
||||
case "Disks":
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0002) {
|
||||
z.Disks = (z.Disks)[:zb0002]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Disks {
|
||||
z.Disks[za0001], err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "Cache":
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadMapHeader()
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
if z.Cache == nil {
|
||||
z.Cache = make(map[string]dataUsageEntryV2, zb0003)
|
||||
z.Cache = make(map[string]dataUsageEntryV2, zb0002)
|
||||
} else if len(z.Cache) > 0 {
|
||||
for key := range z.Cache {
|
||||
delete(z.Cache, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
zb0003--
|
||||
var za0002 string
|
||||
var za0003 dataUsageEntryV2
|
||||
za0002, err = dc.ReadString()
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
var za0001 string
|
||||
var za0002 dataUsageEntryV2
|
||||
za0001, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
err = za0003.DecodeMsg(dc)
|
||||
err = za0002.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache", za0002)
|
||||
err = msgp.WrapError(err, "Cache", za0001)
|
||||
return
|
||||
}
|
||||
z.Cache[za0002] = za0003
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
@@ -895,54 +811,35 @@ func (z *dataUsageCacheV2) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
return
|
||||
}
|
||||
case "Disks":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0002) {
|
||||
z.Disks = (z.Disks)[:zb0002]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Disks {
|
||||
z.Disks[za0001], bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "Cache":
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
if z.Cache == nil {
|
||||
z.Cache = make(map[string]dataUsageEntryV2, zb0003)
|
||||
z.Cache = make(map[string]dataUsageEntryV2, zb0002)
|
||||
} else if len(z.Cache) > 0 {
|
||||
for key := range z.Cache {
|
||||
delete(z.Cache, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
var za0002 string
|
||||
var za0003 dataUsageEntryV2
|
||||
zb0003--
|
||||
za0002, bts, err = msgp.ReadStringBytes(bts)
|
||||
for zb0002 > 0 {
|
||||
var za0001 string
|
||||
var za0002 dataUsageEntryV2
|
||||
zb0002--
|
||||
za0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
bts, err = za0003.UnmarshalMsg(bts)
|
||||
bts, err = za0002.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache", za0002)
|
||||
err = msgp.WrapError(err, "Cache", za0001)
|
||||
return
|
||||
}
|
||||
z.Cache[za0002] = za0003
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
@@ -958,15 +855,11 @@ func (z *dataUsageCacheV2) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *dataUsageCacheV2) Msgsize() (s int) {
|
||||
s = 1 + 5 + z.Info.Msgsize() + 6 + msgp.ArrayHeaderSize
|
||||
for za0001 := range z.Disks {
|
||||
s += msgp.StringPrefixSize + len(z.Disks[za0001])
|
||||
}
|
||||
s += 6 + msgp.MapHeaderSize
|
||||
s = 1 + 5 + z.Info.Msgsize() + 6 + msgp.MapHeaderSize
|
||||
if z.Cache != nil {
|
||||
for za0002, za0003 := range z.Cache {
|
||||
_ = za0003
|
||||
s += msgp.StringPrefixSize + len(za0002) + za0003.Msgsize()
|
||||
for za0001, za0002 := range z.Cache {
|
||||
_ = za0002
|
||||
s += msgp.StringPrefixSize + len(za0001) + za0002.Msgsize()
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -996,54 +889,35 @@ func (z *dataUsageCacheV3) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
return
|
||||
}
|
||||
case "Disks":
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0002) {
|
||||
z.Disks = (z.Disks)[:zb0002]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Disks {
|
||||
z.Disks[za0001], err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "Cache":
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadMapHeader()
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
if z.Cache == nil {
|
||||
z.Cache = make(map[string]dataUsageEntryV3, zb0003)
|
||||
z.Cache = make(map[string]dataUsageEntryV3, zb0002)
|
||||
} else if len(z.Cache) > 0 {
|
||||
for key := range z.Cache {
|
||||
delete(z.Cache, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
zb0003--
|
||||
var za0002 string
|
||||
var za0003 dataUsageEntryV3
|
||||
za0002, err = dc.ReadString()
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
var za0001 string
|
||||
var za0002 dataUsageEntryV3
|
||||
za0001, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
err = za0003.DecodeMsg(dc)
|
||||
err = za0002.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache", za0002)
|
||||
err = msgp.WrapError(err, "Cache", za0001)
|
||||
return
|
||||
}
|
||||
z.Cache[za0002] = za0003
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
@@ -1080,54 +954,35 @@ func (z *dataUsageCacheV3) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
return
|
||||
}
|
||||
case "Disks":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0002) {
|
||||
z.Disks = (z.Disks)[:zb0002]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Disks {
|
||||
z.Disks[za0001], bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "Cache":
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
if z.Cache == nil {
|
||||
z.Cache = make(map[string]dataUsageEntryV3, zb0003)
|
||||
z.Cache = make(map[string]dataUsageEntryV3, zb0002)
|
||||
} else if len(z.Cache) > 0 {
|
||||
for key := range z.Cache {
|
||||
delete(z.Cache, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
var za0002 string
|
||||
var za0003 dataUsageEntryV3
|
||||
zb0003--
|
||||
za0002, bts, err = msgp.ReadStringBytes(bts)
|
||||
for zb0002 > 0 {
|
||||
var za0001 string
|
||||
var za0002 dataUsageEntryV3
|
||||
zb0002--
|
||||
za0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
bts, err = za0003.UnmarshalMsg(bts)
|
||||
bts, err = za0002.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache", za0002)
|
||||
err = msgp.WrapError(err, "Cache", za0001)
|
||||
return
|
||||
}
|
||||
z.Cache[za0002] = za0003
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
@@ -1143,15 +998,11 @@ func (z *dataUsageCacheV3) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *dataUsageCacheV3) Msgsize() (s int) {
|
||||
s = 1 + 5 + z.Info.Msgsize() + 6 + msgp.ArrayHeaderSize
|
||||
for za0001 := range z.Disks {
|
||||
s += msgp.StringPrefixSize + len(z.Disks[za0001])
|
||||
}
|
||||
s += 6 + msgp.MapHeaderSize
|
||||
s = 1 + 5 + z.Info.Msgsize() + 6 + msgp.MapHeaderSize
|
||||
if z.Cache != nil {
|
||||
for za0002, za0003 := range z.Cache {
|
||||
_ = za0003
|
||||
s += msgp.StringPrefixSize + len(za0002) + za0003.Msgsize()
|
||||
for za0001, za0002 := range z.Cache {
|
||||
_ = za0002
|
||||
s += msgp.StringPrefixSize + len(za0001) + za0002.Msgsize()
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -1181,54 +1032,35 @@ func (z *dataUsageCacheV4) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
return
|
||||
}
|
||||
case "Disks":
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0002) {
|
||||
z.Disks = (z.Disks)[:zb0002]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Disks {
|
||||
z.Disks[za0001], err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "Cache":
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadMapHeader()
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
if z.Cache == nil {
|
||||
z.Cache = make(map[string]dataUsageEntryV4, zb0003)
|
||||
z.Cache = make(map[string]dataUsageEntryV4, zb0002)
|
||||
} else if len(z.Cache) > 0 {
|
||||
for key := range z.Cache {
|
||||
delete(z.Cache, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
zb0003--
|
||||
var za0002 string
|
||||
var za0003 dataUsageEntryV4
|
||||
za0002, err = dc.ReadString()
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
var za0001 string
|
||||
var za0002 dataUsageEntryV4
|
||||
za0001, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
err = za0003.DecodeMsg(dc)
|
||||
err = za0002.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache", za0002)
|
||||
err = msgp.WrapError(err, "Cache", za0001)
|
||||
return
|
||||
}
|
||||
z.Cache[za0002] = za0003
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
@@ -1265,54 +1097,35 @@ func (z *dataUsageCacheV4) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
return
|
||||
}
|
||||
case "Disks":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0002) {
|
||||
z.Disks = (z.Disks)[:zb0002]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Disks {
|
||||
z.Disks[za0001], bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "Cache":
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
if z.Cache == nil {
|
||||
z.Cache = make(map[string]dataUsageEntryV4, zb0003)
|
||||
z.Cache = make(map[string]dataUsageEntryV4, zb0002)
|
||||
} else if len(z.Cache) > 0 {
|
||||
for key := range z.Cache {
|
||||
delete(z.Cache, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
var za0002 string
|
||||
var za0003 dataUsageEntryV4
|
||||
zb0003--
|
||||
za0002, bts, err = msgp.ReadStringBytes(bts)
|
||||
for zb0002 > 0 {
|
||||
var za0001 string
|
||||
var za0002 dataUsageEntryV4
|
||||
zb0002--
|
||||
za0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
bts, err = za0003.UnmarshalMsg(bts)
|
||||
bts, err = za0002.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache", za0002)
|
||||
err = msgp.WrapError(err, "Cache", za0001)
|
||||
return
|
||||
}
|
||||
z.Cache[za0002] = za0003
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
@@ -1328,15 +1141,11 @@ func (z *dataUsageCacheV4) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *dataUsageCacheV4) Msgsize() (s int) {
|
||||
s = 1 + 5 + z.Info.Msgsize() + 6 + msgp.ArrayHeaderSize
|
||||
for za0001 := range z.Disks {
|
||||
s += msgp.StringPrefixSize + len(z.Disks[za0001])
|
||||
}
|
||||
s += 6 + msgp.MapHeaderSize
|
||||
s = 1 + 5 + z.Info.Msgsize() + 6 + msgp.MapHeaderSize
|
||||
if z.Cache != nil {
|
||||
for za0002, za0003 := range z.Cache {
|
||||
_ = za0003
|
||||
s += msgp.StringPrefixSize + len(za0002) + za0003.Msgsize()
|
||||
for za0001, za0002 := range z.Cache {
|
||||
_ = za0002
|
||||
s += msgp.StringPrefixSize + len(za0001) + za0002.Msgsize()
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -1366,54 +1175,35 @@ func (z *dataUsageCacheV5) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
return
|
||||
}
|
||||
case "Disks":
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0002) {
|
||||
z.Disks = (z.Disks)[:zb0002]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Disks {
|
||||
z.Disks[za0001], err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "Cache":
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadMapHeader()
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
if z.Cache == nil {
|
||||
z.Cache = make(map[string]dataUsageEntryV5, zb0003)
|
||||
z.Cache = make(map[string]dataUsageEntryV5, zb0002)
|
||||
} else if len(z.Cache) > 0 {
|
||||
for key := range z.Cache {
|
||||
delete(z.Cache, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
zb0003--
|
||||
var za0002 string
|
||||
var za0003 dataUsageEntryV5
|
||||
za0002, err = dc.ReadString()
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
var za0001 string
|
||||
var za0002 dataUsageEntryV5
|
||||
za0001, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
err = za0003.DecodeMsg(dc)
|
||||
err = za0002.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache", za0002)
|
||||
err = msgp.WrapError(err, "Cache", za0001)
|
||||
return
|
||||
}
|
||||
z.Cache[za0002] = za0003
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
@@ -1450,54 +1240,35 @@ func (z *dataUsageCacheV5) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
return
|
||||
}
|
||||
case "Disks":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0002) {
|
||||
z.Disks = (z.Disks)[:zb0002]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Disks {
|
||||
z.Disks[za0001], bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "Cache":
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
if z.Cache == nil {
|
||||
z.Cache = make(map[string]dataUsageEntryV5, zb0003)
|
||||
z.Cache = make(map[string]dataUsageEntryV5, zb0002)
|
||||
} else if len(z.Cache) > 0 {
|
||||
for key := range z.Cache {
|
||||
delete(z.Cache, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
var za0002 string
|
||||
var za0003 dataUsageEntryV5
|
||||
zb0003--
|
||||
za0002, bts, err = msgp.ReadStringBytes(bts)
|
||||
for zb0002 > 0 {
|
||||
var za0001 string
|
||||
var za0002 dataUsageEntryV5
|
||||
zb0002--
|
||||
za0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
bts, err = za0003.UnmarshalMsg(bts)
|
||||
bts, err = za0002.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache", za0002)
|
||||
err = msgp.WrapError(err, "Cache", za0001)
|
||||
return
|
||||
}
|
||||
z.Cache[za0002] = za0003
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
@@ -1513,15 +1284,11 @@ func (z *dataUsageCacheV5) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *dataUsageCacheV5) Msgsize() (s int) {
|
||||
s = 1 + 5 + z.Info.Msgsize() + 6 + msgp.ArrayHeaderSize
|
||||
for za0001 := range z.Disks {
|
||||
s += msgp.StringPrefixSize + len(z.Disks[za0001])
|
||||
}
|
||||
s += 6 + msgp.MapHeaderSize
|
||||
s = 1 + 5 + z.Info.Msgsize() + 6 + msgp.MapHeaderSize
|
||||
if z.Cache != nil {
|
||||
for za0002, za0003 := range z.Cache {
|
||||
_ = za0003
|
||||
s += msgp.StringPrefixSize + len(za0002) + za0003.Msgsize()
|
||||
for za0001, za0002 := range z.Cache {
|
||||
_ = za0002
|
||||
s += msgp.StringPrefixSize + len(za0001) + za0002.Msgsize()
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -1551,54 +1318,35 @@ func (z *dataUsageCacheV6) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
return
|
||||
}
|
||||
case "Disks":
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0002) {
|
||||
z.Disks = (z.Disks)[:zb0002]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Disks {
|
||||
z.Disks[za0001], err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "Cache":
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadMapHeader()
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
if z.Cache == nil {
|
||||
z.Cache = make(map[string]dataUsageEntryV6, zb0003)
|
||||
z.Cache = make(map[string]dataUsageEntryV6, zb0002)
|
||||
} else if len(z.Cache) > 0 {
|
||||
for key := range z.Cache {
|
||||
delete(z.Cache, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
zb0003--
|
||||
var za0002 string
|
||||
var za0003 dataUsageEntryV6
|
||||
za0002, err = dc.ReadString()
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
var za0001 string
|
||||
var za0002 dataUsageEntryV6
|
||||
za0001, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
err = za0003.DecodeMsg(dc)
|
||||
err = za0002.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache", za0002)
|
||||
err = msgp.WrapError(err, "Cache", za0001)
|
||||
return
|
||||
}
|
||||
z.Cache[za0002] = za0003
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
@@ -1635,54 +1383,35 @@ func (z *dataUsageCacheV6) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "Info")
|
||||
return
|
||||
}
|
||||
case "Disks":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks")
|
||||
return
|
||||
}
|
||||
if cap(z.Disks) >= int(zb0002) {
|
||||
z.Disks = (z.Disks)[:zb0002]
|
||||
} else {
|
||||
z.Disks = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Disks {
|
||||
z.Disks[za0001], bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Disks", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "Cache":
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
if z.Cache == nil {
|
||||
z.Cache = make(map[string]dataUsageEntryV6, zb0003)
|
||||
z.Cache = make(map[string]dataUsageEntryV6, zb0002)
|
||||
} else if len(z.Cache) > 0 {
|
||||
for key := range z.Cache {
|
||||
delete(z.Cache, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
var za0002 string
|
||||
var za0003 dataUsageEntryV6
|
||||
zb0003--
|
||||
za0002, bts, err = msgp.ReadStringBytes(bts)
|
||||
for zb0002 > 0 {
|
||||
var za0001 string
|
||||
var za0002 dataUsageEntryV6
|
||||
zb0002--
|
||||
za0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache")
|
||||
return
|
||||
}
|
||||
bts, err = za0003.UnmarshalMsg(bts)
|
||||
bts, err = za0002.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Cache", za0002)
|
||||
err = msgp.WrapError(err, "Cache", za0001)
|
||||
return
|
||||
}
|
||||
z.Cache[za0002] = za0003
|
||||
z.Cache[za0001] = za0002
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
@@ -1698,15 +1427,11 @@ func (z *dataUsageCacheV6) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *dataUsageCacheV6) Msgsize() (s int) {
|
||||
s = 1 + 5 + z.Info.Msgsize() + 6 + msgp.ArrayHeaderSize
|
||||
for za0001 := range z.Disks {
|
||||
s += msgp.StringPrefixSize + len(z.Disks[za0001])
|
||||
}
|
||||
s += 6 + msgp.MapHeaderSize
|
||||
s = 1 + 5 + z.Info.Msgsize() + 6 + msgp.MapHeaderSize
|
||||
if z.Cache != nil {
|
||||
for za0002, za0003 := range z.Cache {
|
||||
_ = za0003
|
||||
s += msgp.StringPrefixSize + len(za0002) + za0003.Msgsize()
|
||||
for za0001, za0002 := range z.Cache {
|
||||
_ = za0002
|
||||
s += msgp.StringPrefixSize + len(za0001) + za0002.Msgsize()
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -67,7 +67,7 @@ func TestDataUsageUpdate(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
got, err := scanDataFolder(context.Background(), base, dataUsageCache{Info: dataUsageCacheInfo{Name: bucket}}, getSize)
|
||||
got, err := scanDataFolder(context.Background(), 0, 0, base, dataUsageCache{Info: dataUsageCacheInfo{Name: bucket}}, getSize)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -178,7 +178,7 @@ func TestDataUsageUpdate(t *testing.T) {
|
||||
}
|
||||
// Changed dir must be picked up in this many cycles.
|
||||
for i := 0; i < dataUsageUpdateDirCycles; i++ {
|
||||
got, err = scanDataFolder(context.Background(), base, got, getSize)
|
||||
got, err = scanDataFolder(context.Background(), 0, 0, base, got, getSize)
|
||||
got.Info.NextCycle++
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -289,7 +289,7 @@ func TestDataUsageUpdatePrefix(t *testing.T) {
|
||||
}
|
||||
return
|
||||
}
|
||||
got, err := scanDataFolder(context.Background(), base, dataUsageCache{Info: dataUsageCacheInfo{Name: "bucket"}}, getSize)
|
||||
got, err := scanDataFolder(context.Background(), 0, 0, base, dataUsageCache{Info: dataUsageCacheInfo{Name: "bucket"}}, getSize)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -423,7 +423,7 @@ func TestDataUsageUpdatePrefix(t *testing.T) {
|
||||
}
|
||||
// Changed dir must be picked up in this many cycles.
|
||||
for i := 0; i < dataUsageUpdateDirCycles; i++ {
|
||||
got, err = scanDataFolder(context.Background(), base, got, getSize)
|
||||
got, err = scanDataFolder(context.Background(), 0, 0, base, got, getSize)
|
||||
got.Info.NextCycle++
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -575,7 +575,7 @@ func TestDataUsageCacheSerialize(t *testing.T) {
|
||||
}
|
||||
return
|
||||
}
|
||||
want, err := scanDataFolder(context.Background(), base, dataUsageCache{Info: dataUsageCacheInfo{Name: bucket}}, getSize)
|
||||
want, err := scanDataFolder(context.Background(), 0, 0, base, dataUsageCache{Info: dataUsageCacheInfo{Name: bucket}}, getSize)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -477,22 +477,6 @@ func (f *fileScorer) trimQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
// fileObjInfos returns all queued file object infos
|
||||
func (f *fileScorer) fileObjInfos() []ObjectInfo {
|
||||
res := make([]ObjectInfo, 0, f.queue.Len())
|
||||
e := f.queue.Front()
|
||||
for e != nil {
|
||||
qfile := e.Value.(queuedFile)
|
||||
res = append(res, ObjectInfo{
|
||||
Name: qfile.name,
|
||||
Size: int64(qfile.size),
|
||||
VersionID: qfile.versionID,
|
||||
})
|
||||
e = e.Next()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (f *fileScorer) purgeFunc(p func(qfile queuedFile)) {
|
||||
e := f.queue.Front()
|
||||
for e != nil {
|
||||
|
||||
+1
-1
@@ -1153,7 +1153,7 @@ func (c *cacheObjects) CompleteMultipartUpload(ctx context.Context, bucket, obje
|
||||
if err == nil {
|
||||
// fill cache in the background
|
||||
go func() {
|
||||
_, err := dcache.CompleteMultipartUpload(ctx, bucket, object, uploadID, uploadedParts, oi, opts)
|
||||
_, err := dcache.CompleteMultipartUpload(bgContext(ctx), bucket, object, uploadID, uploadedParts, oi, opts)
|
||||
if err != nil {
|
||||
// fill cache in the background
|
||||
bReader, bErr := c.InnerGetObjectNInfoFn(GlobalContext, bucket, object, nil, http.Header{}, readLock, ObjectOptions{})
|
||||
|
||||
@@ -368,7 +368,7 @@ func TestHealingDanglingObject(t *testing.T) {
|
||||
orgDisks := append([]StorageAPI{}, disks...)
|
||||
|
||||
// Enable versioning.
|
||||
globalBucketMetadataSys.Update(bucket, bucketVersioningConfig, []byte(`<VersioningConfiguration><Status>Enabled</Status></VersioningConfiguration>`))
|
||||
globalBucketMetadataSys.Update(ctx, bucket, bucketVersioningConfig, []byte(`<VersioningConfiguration><Status>Enabled</Status></VersioningConfiguration>`))
|
||||
|
||||
_, err = objLayer.PutObject(ctx, bucket, object, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), ObjectOptions{
|
||||
Versioned: true,
|
||||
|
||||
@@ -406,11 +406,27 @@ func objectQuorumFromMeta(ctx context.Context, partsMetaData []FileInfo, errs []
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
if latestFileInfo.Deleted {
|
||||
// For delete markers do not use 'defaultParityCount' as it is not expected to be the case.
|
||||
// Use maximum allowed read quorum instead, writeQuorum+1 is returned for compatibility sake
|
||||
// but there are no callers that shall be using this.
|
||||
readQuorum := len(partsMetaData) / 2
|
||||
return readQuorum, readQuorum + 1, nil
|
||||
}
|
||||
|
||||
parityBlocks := globalStorageClass.GetParityForSC(latestFileInfo.Metadata[xhttp.AmzStorageClass])
|
||||
if parityBlocks <= 0 {
|
||||
parityBlocks = defaultParityCount
|
||||
}
|
||||
|
||||
// For erasure code upgraded objects choose the parity
|
||||
// blocks saved internally, instead of 'defaultParityCount'
|
||||
if _, ok := latestFileInfo.Metadata[minIOErasureUpgraded]; ok {
|
||||
if latestFileInfo.Erasure.ParityBlocks != 0 {
|
||||
parityBlocks = latestFileInfo.Erasure.ParityBlocks
|
||||
}
|
||||
}
|
||||
|
||||
dataBlocks := latestFileInfo.Erasure.DataBlocks
|
||||
if dataBlocks == 0 {
|
||||
dataBlocks = len(partsMetaData) - parityBlocks
|
||||
|
||||
@@ -1037,7 +1037,7 @@ func (er erasureObjects) DeleteObjects(ctx context.Context, bucket string, objec
|
||||
// class for objects which have reduced quorum
|
||||
// storage class only needs to be honored for
|
||||
// Read() requests alone which we already do.
|
||||
writeQuorums[i] = er.defaultWQuorum()
|
||||
writeQuorums[i] = len(storageDisks)/2 + 1
|
||||
}
|
||||
|
||||
versionsMap := make(map[string]FileInfoVersions, len(objects))
|
||||
@@ -1153,9 +1153,7 @@ func (er erasureObjects) DeleteObjects(ctx context.Context, bucket string, objec
|
||||
errs[objIndex] = toObjectErr(err, bucket, objects[objIndex].ObjectName)
|
||||
}
|
||||
|
||||
if errs[objIndex] == nil {
|
||||
defer NSUpdated(bucket, objects[objIndex].ObjectName)
|
||||
}
|
||||
defer NSUpdated(bucket, objects[objIndex].ObjectName)
|
||||
}
|
||||
|
||||
// Check failed deletes across multiple objects
|
||||
|
||||
@@ -682,8 +682,8 @@ func (z *erasureServerPools) decommissionPool(ctx context.Context, idx int, pool
|
||||
|
||||
// How to resolve partial results.
|
||||
resolver := metadataResolutionParams{
|
||||
dirQuorum: set.defaultRQuorum(),
|
||||
objQuorum: set.defaultRQuorum(),
|
||||
dirQuorum: len(disks) / 2, // make sure to capture all quorum ratios
|
||||
objQuorum: len(disks) / 2, // make sure to capture all quorum ratios
|
||||
bucket: bName,
|
||||
}
|
||||
|
||||
@@ -692,7 +692,7 @@ func (z *erasureServerPools) decommissionPool(ctx context.Context, idx int, pool
|
||||
bucket: bName,
|
||||
recursive: true,
|
||||
forwardTo: forwardTo,
|
||||
minDisks: len(disks),
|
||||
minDisks: len(disks) / 2, // to capture all quorum ratios
|
||||
reportNotFound: false,
|
||||
agreed: decommissionEntry,
|
||||
partial: func(entries metaCacheEntries, nAgreed int, errs []error) {
|
||||
|
||||
@@ -619,12 +619,6 @@ func (z *erasureServerPools) NSScanner(ctx context.Context, bf *bloomFilter, upd
|
||||
return
|
||||
case v := <-updateCloser:
|
||||
update()
|
||||
// Enforce quotas when all is done.
|
||||
if firstErr == nil {
|
||||
for _, b := range allBuckets {
|
||||
enforceFIFOQuotaBucket(ctx, z, b.Name, allMerged.bucketUsageInfo(b.Name))
|
||||
}
|
||||
}
|
||||
close(v)
|
||||
return
|
||||
case <-updateTicker.C:
|
||||
|
||||
+7
-1
@@ -146,7 +146,7 @@ func connectEndpoint(endpoint Endpoint) (StorageAPI, *formatErasureV3, error) {
|
||||
if errors.Is(err, errUnformattedDisk) {
|
||||
info, derr := disk.DiskInfo(context.TODO())
|
||||
if derr != nil && info.RootDisk {
|
||||
return nil, nil, fmt.Errorf("Disk: %s returned %w", disk, derr) // make sure to '%w' to wrap the error
|
||||
return nil, nil, fmt.Errorf("Disk: %s is a root disk", disk)
|
||||
}
|
||||
}
|
||||
return nil, nil, fmt.Errorf("Disk: %s returned %w", disk, err) // make sure to '%w' to wrap the error
|
||||
@@ -1211,6 +1211,9 @@ func formatsToDrivesInfo(endpoints Endpoints, formats []*formatErasureV3, sErrs
|
||||
// If it is a single node Erasure and all disks are root disks, it is most likely a test setup, else it is a production setup.
|
||||
// On a test setup we allow creation of format.json on root disks to help with dev/testing.
|
||||
func isTestSetup(infos []DiskInfo, errs []error) bool {
|
||||
if globalIsCICD {
|
||||
return true
|
||||
}
|
||||
rootDiskCount := 0
|
||||
for i := range errs {
|
||||
if errs[i] == nil || errs[i] == errUnformattedDisk {
|
||||
@@ -1245,6 +1248,9 @@ func getHealDiskInfos(storageDisks []StorageAPI, errs []error) ([]DiskInfo, []er
|
||||
|
||||
// Mark root disks as down so as not to heal them.
|
||||
func markRootDisksAsDown(storageDisks []StorageAPI, errs []error) {
|
||||
if globalIsCICD {
|
||||
return
|
||||
}
|
||||
var infos []DiskInfo
|
||||
infos, errs = getHealDiskInfos(storageDisks, errs)
|
||||
if !isTestSetup(infos, errs) {
|
||||
|
||||
@@ -357,23 +357,6 @@ func (er erasureObjects) nsScanner(ctx context.Context, buckets []BucketInfo, bf
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect disks for healing.
|
||||
allDisks := er.getDisks()
|
||||
allDiskIDs := make([]string, 0, len(allDisks))
|
||||
for _, disk := range allDisks {
|
||||
if disk == OfflineDisk {
|
||||
// its possible that disk is OfflineDisk
|
||||
continue
|
||||
}
|
||||
id, _ := disk.GetDiskID()
|
||||
if id == "" {
|
||||
// its possible that disk is unformatted
|
||||
// or just went offline
|
||||
continue
|
||||
}
|
||||
allDiskIDs = append(allDiskIDs, id)
|
||||
}
|
||||
|
||||
// Load bucket totals
|
||||
oldCache := dataUsageCache{}
|
||||
if err := oldCache.load(ctx, er, dataUsageCacheName); err != nil {
|
||||
@@ -479,7 +462,6 @@ func (er erasureObjects) nsScanner(ctx context.Context, buckets []BucketInfo, bf
|
||||
}
|
||||
cache.Info.BloomFilter = bloom
|
||||
cache.Info.SkipHealing = healing
|
||||
cache.Disks = allDiskIDs
|
||||
cache.Info.NextCycle = wantCycle
|
||||
if cache.Info.Name != bucket.Name {
|
||||
logger.LogIf(ctx, fmt.Errorf("cache name mismatch: %s != %s", cache.Info.Name, bucket.Name))
|
||||
|
||||
+22
-61
@@ -19,8 +19,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -124,6 +122,13 @@ type formatErasureV3 struct {
|
||||
} `json:"xl"`
|
||||
}
|
||||
|
||||
func (f *formatErasureV3) Drives() (drives int) {
|
||||
for _, set := range f.Erasure.Sets {
|
||||
drives += len(set)
|
||||
}
|
||||
return drives
|
||||
}
|
||||
|
||||
func (f *formatErasureV3) Clone() *formatErasureV3 {
|
||||
b, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
@@ -545,43 +550,36 @@ func formatErasureFixLocalDeploymentID(endpoints Endpoints, storageDisks []Stora
|
||||
|
||||
// Get backend Erasure format in quorum `format.json`.
|
||||
func getFormatErasureInQuorum(formats []*formatErasureV3) (*formatErasureV3, error) {
|
||||
formatHashes := make([]string, len(formats))
|
||||
for i, format := range formats {
|
||||
formatCountMap := make(map[int]int, len(formats))
|
||||
for _, format := range formats {
|
||||
if format == nil {
|
||||
continue
|
||||
}
|
||||
h := sha256.New()
|
||||
for _, set := range format.Erasure.Sets {
|
||||
for _, diskID := range set {
|
||||
h.Write([]byte(diskID))
|
||||
}
|
||||
}
|
||||
formatHashes[i] = hex.EncodeToString(h.Sum(nil))
|
||||
formatCountMap[format.Drives()]++
|
||||
}
|
||||
|
||||
formatCountMap := make(map[string]int)
|
||||
for _, hash := range formatHashes {
|
||||
if hash == "" {
|
||||
continue
|
||||
}
|
||||
formatCountMap[hash]++
|
||||
}
|
||||
|
||||
maxHash := ""
|
||||
maxDrives := 0
|
||||
maxCount := 0
|
||||
for hash, count := range formatCountMap {
|
||||
for drives, count := range formatCountMap {
|
||||
if count > maxCount {
|
||||
maxCount = count
|
||||
maxHash = hash
|
||||
maxDrives = drives
|
||||
}
|
||||
}
|
||||
|
||||
if maxDrives == 0 {
|
||||
return nil, errErasureReadQuorum
|
||||
}
|
||||
|
||||
if maxCount < len(formats)/2 {
|
||||
return nil, errErasureReadQuorum
|
||||
}
|
||||
|
||||
for i, hash := range formatHashes {
|
||||
if hash == maxHash {
|
||||
for i, format := range formats {
|
||||
if format == nil {
|
||||
continue
|
||||
}
|
||||
if format.Drives() == maxDrives {
|
||||
format := formats[i].Clone()
|
||||
format.Erasure.This = ""
|
||||
return format, nil
|
||||
@@ -624,43 +622,6 @@ func formatErasureV3Check(reference *formatErasureV3, format *formatErasureV3) e
|
||||
return fmt.Errorf("Disk ID %s not found in any disk sets %s", this, format.Erasure.Sets)
|
||||
}
|
||||
|
||||
// Initializes meta volume only on local storage disks.
|
||||
func initErasureMetaVolumesInLocalDisks(storageDisks []StorageAPI, formats []*formatErasureV3) error {
|
||||
// Compute the local disks eligible for meta volumes (re)initialization
|
||||
disksToInit := make([]StorageAPI, 0, len(storageDisks))
|
||||
for index := range storageDisks {
|
||||
if formats[index] == nil || storageDisks[index] == nil || !storageDisks[index].IsLocal() {
|
||||
// Ignore create meta volume on disks which are not found or not local.
|
||||
continue
|
||||
}
|
||||
disksToInit = append(disksToInit, storageDisks[index])
|
||||
}
|
||||
|
||||
// Initialize errs to collect errors inside go-routine.
|
||||
g := errgroup.WithNErrs(len(disksToInit))
|
||||
|
||||
// Initialize all disks in parallel.
|
||||
for index := range disksToInit {
|
||||
// Initialize a new index variable in each loop so each
|
||||
// goroutine will return its own instance of index variable.
|
||||
index := index
|
||||
g.Go(func() error {
|
||||
return makeFormatErasureMetaVolumes(disksToInit[index])
|
||||
}, index)
|
||||
}
|
||||
|
||||
// Return upon first error.
|
||||
for _, err := range g.Wait() {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
return toObjectErr(err, minioMetaBucket)
|
||||
}
|
||||
|
||||
// Return success here.
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveFormatErasureAll - populates `format.json` on disks in its order.
|
||||
func saveFormatErasureAll(ctx context.Context, storageDisks []StorageAPI, formats []*formatErasureV3) error {
|
||||
g := errgroup.WithNErrs(len(storageDisks))
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
@@ -54,10 +56,6 @@ func TestFixFormatV3(t *testing.T) {
|
||||
formats[j] = newFormat
|
||||
}
|
||||
|
||||
if err = initErasureMetaVolumesInLocalDisks(storageDisks, formats); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
formats[1] = nil
|
||||
expThis := formats[2].Erasure.This
|
||||
formats[2].Erasure.This = ""
|
||||
@@ -342,6 +340,102 @@ func TestGetFormatErasureInQuorumCheck(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get backend Erasure format in quorum `format.json`.
|
||||
func getFormatErasureInQuorumOld(formats []*formatErasureV3) (*formatErasureV3, error) {
|
||||
formatHashes := make([]string, len(formats))
|
||||
for i, format := range formats {
|
||||
if format == nil {
|
||||
continue
|
||||
}
|
||||
h := sha256.New()
|
||||
for _, set := range format.Erasure.Sets {
|
||||
for _, diskID := range set {
|
||||
h.Write([]byte(diskID))
|
||||
}
|
||||
}
|
||||
formatHashes[i] = hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
formatCountMap := make(map[string]int)
|
||||
for _, hash := range formatHashes {
|
||||
if hash == "" {
|
||||
continue
|
||||
}
|
||||
formatCountMap[hash]++
|
||||
}
|
||||
|
||||
maxHash := ""
|
||||
maxCount := 0
|
||||
for hash, count := range formatCountMap {
|
||||
if count > maxCount {
|
||||
maxCount = count
|
||||
maxHash = hash
|
||||
}
|
||||
}
|
||||
|
||||
if maxCount < len(formats)/2 {
|
||||
return nil, errErasureReadQuorum
|
||||
}
|
||||
|
||||
for i, hash := range formatHashes {
|
||||
if hash == maxHash {
|
||||
format := formats[i].Clone()
|
||||
format.Erasure.This = ""
|
||||
return format, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errErasureReadQuorum
|
||||
}
|
||||
|
||||
func BenchmarkGetFormatErasureInQuorumOld(b *testing.B) {
|
||||
setCount := 200
|
||||
setDriveCount := 15
|
||||
|
||||
format := newFormatErasureV3(setCount, setDriveCount)
|
||||
format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1
|
||||
formats := make([]*formatErasureV3, 15*200)
|
||||
|
||||
for i := 0; i < setCount; i++ {
|
||||
for j := 0; j < setDriveCount; j++ {
|
||||
newFormat := format.Clone()
|
||||
newFormat.Erasure.This = format.Erasure.Sets[i][j]
|
||||
formats[i*setDriveCount+j] = newFormat
|
||||
}
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = getFormatErasureInQuorumOld(formats)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetFormatErasureInQuorum(b *testing.B) {
|
||||
setCount := 200
|
||||
setDriveCount := 15
|
||||
|
||||
format := newFormatErasureV3(setCount, setDriveCount)
|
||||
format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1
|
||||
formats := make([]*formatErasureV3, 15*200)
|
||||
|
||||
for i := 0; i < setCount; i++ {
|
||||
for j := 0; j < setDriveCount; j++ {
|
||||
newFormat := format.Clone()
|
||||
newFormat.Erasure.This = format.Erasure.Sets[i][j]
|
||||
formats[i*setDriveCount+j] = newFormat
|
||||
}
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = getFormatErasureInQuorum(formats)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests formatErasureGetDeploymentID()
|
||||
func TestGetErasureID(t *testing.T) {
|
||||
setCount := 2
|
||||
|
||||
+19
-5
@@ -329,7 +329,7 @@ func (fs *FSObjects) NSScanner(ctx context.Context, bf *bloomFilter, updates cha
|
||||
logger.LogIf(ctx, totalCache.save(ctx, fs, dataUsageCacheName))
|
||||
cloned := totalCache.clone()
|
||||
updates <- cloned.dui(dataUsageRoot, buckets)
|
||||
enforceFIFOQuotaBucket(ctx, fs, b.Name, cloned.bucketUsageInfo(b.Name))
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -352,7 +352,7 @@ func (fs *FSObjects) scanBucket(ctx context.Context, bucket string, cache dataUs
|
||||
}
|
||||
|
||||
// Load bucket info.
|
||||
cache, err = scanDataFolder(ctx, fs.fsPath, cache, func(item scannerItem) (sizeSummary, error) {
|
||||
cache, err = scanDataFolder(ctx, -1, -1, fs.fsPath, cache, func(item scannerItem) (sizeSummary, error) {
|
||||
bucket, object := item.bucket, item.objectPath()
|
||||
fsMetaBytes, err := xioutil.ReadFile(pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fs.metaJSONFile))
|
||||
if err != nil && !osIsNotExist(err) {
|
||||
@@ -1260,9 +1260,23 @@ func (fs *FSObjects) ListObjectVersions(ctx context.Context, bucket, prefix, mar
|
||||
|
||||
// ListObjects - list all objects at prefix upto maxKeys., optionally delimited by '/'. Maintains the list pool
|
||||
// state for future re-entrant list requests.
|
||||
func (fs *FSObjects) ListObjects(ctx context.Context, bucket, prefix, marker, delimiter string, maxKeys int) (loi ListObjectsInfo, e error) {
|
||||
return listObjects(ctx, fs, bucket, prefix, marker, delimiter, maxKeys, fs.listPool,
|
||||
fs.listDirFactory(), fs.isLeaf, fs.isLeafDir, fs.getObjectInfoNoFSLock, fs.getObjectInfoNoFSLock)
|
||||
func (fs *FSObjects) ListObjects(ctx context.Context, bucket, prefix, marker, delimiter string, maxKeys int) (loi ListObjectsInfo, err error) {
|
||||
// listObjects may in rare cases not be able to find any valid results.
|
||||
// Therefore, it cannot set a NextMarker.
|
||||
// In that case we retry the operation, but we add a
|
||||
// max limit, so we never end up in an infinite loop.
|
||||
tries := 50
|
||||
for {
|
||||
loi, err = listObjects(ctx, fs, bucket, prefix, marker, delimiter, maxKeys, fs.listPool,
|
||||
fs.listDirFactory(), fs.isLeaf, fs.isLeafDir, fs.getObjectInfoNoFSLock, fs.getObjectInfoNoFSLock)
|
||||
if err != nil {
|
||||
return loi, err
|
||||
}
|
||||
if !loi.IsTruncated || loi.NextMarker != "" || tries == 0 {
|
||||
return loi, nil
|
||||
}
|
||||
tries--
|
||||
}
|
||||
}
|
||||
|
||||
// GetObjectTags - get object tags from an existing object
|
||||
|
||||
+10
-7
@@ -132,13 +132,13 @@ func authenticateURL(accessKey, secretKey string) (string, error) {
|
||||
// Check if the request is authenticated.
|
||||
// Returns nil if the request is authenticated. errNoAuthToken if token missing.
|
||||
// Returns errAuthentication for all other errors.
|
||||
func webRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, bool, error) {
|
||||
func webRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, []string, bool, error) {
|
||||
token, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(req)
|
||||
if err != nil {
|
||||
if err == jwtreq.ErrNoTokenInRequest {
|
||||
return nil, false, errNoAuthToken
|
||||
return nil, nil, false, errNoAuthToken
|
||||
}
|
||||
return nil, false, err
|
||||
return nil, nil, false, err
|
||||
}
|
||||
claims := xjwt.NewMapClaims()
|
||||
if err := xjwt.ParseWithClaims(token, claims, func(claims *xjwt.MapClaims) ([]byte, error) {
|
||||
@@ -151,20 +151,21 @@ func webRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, bool, error) {
|
||||
}
|
||||
return []byte(cred.SecretKey), nil
|
||||
}); err != nil {
|
||||
return claims, false, errAuthentication
|
||||
return claims, nil, false, errAuthentication
|
||||
}
|
||||
owner := true
|
||||
var groups []string
|
||||
if globalActiveCred.AccessKey != claims.AccessKey {
|
||||
// Check if the access key is part of users credentials.
|
||||
ucred, ok := globalIAMSys.GetUser(req.Context(), claims.AccessKey)
|
||||
if !ok {
|
||||
return nil, false, errInvalidAccessKeyID
|
||||
return nil, nil, false, errInvalidAccessKeyID
|
||||
}
|
||||
|
||||
// get embedded claims
|
||||
eclaims, s3Err := checkClaimsFromToken(req, ucred)
|
||||
if s3Err != ErrNone {
|
||||
return nil, false, errAuthentication
|
||||
return nil, nil, false, errAuthentication
|
||||
}
|
||||
|
||||
for k, v := range eclaims {
|
||||
@@ -177,9 +178,11 @@ func webRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, bool, error) {
|
||||
} else {
|
||||
owner = globalActiveCred.AccessKey == ucred.ParentUser
|
||||
}
|
||||
|
||||
groups = ucred.Groups
|
||||
}
|
||||
|
||||
return claims, owner, nil
|
||||
return claims, groups, owner, nil
|
||||
}
|
||||
|
||||
// newCachedAuthToken returns a token that is cached up to 15 seconds.
|
||||
|
||||
+1
-1
@@ -149,7 +149,7 @@ func TestWebRequestAuthenticate(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
_, _, gotErr := webRequestAuthenticate(testCase.req)
|
||||
_, _, _, gotErr := webRequestAuthenticate(testCase.req)
|
||||
if testCase.expectedErr != gotErr {
|
||||
t.Errorf("Test %d, expected err %s, got %s", i+1, testCase.expectedErr, gotErr)
|
||||
}
|
||||
|
||||
+67
-49
@@ -15,6 +15,8 @@
|
||||
// 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/>.
|
||||
|
||||
//go:generate msgp -file=$GOFILE -unexported
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
@@ -76,34 +78,34 @@ type AccElem struct {
|
||||
N int64
|
||||
}
|
||||
|
||||
// add dur to a as a single element.
|
||||
// Add a duration to a single element.
|
||||
func (a *AccElem) add(dur time.Duration) {
|
||||
a.Total += int64(dur)
|
||||
a.N++
|
||||
}
|
||||
|
||||
// merge b into a.
|
||||
// Merge b into a.
|
||||
func (a *AccElem) merge(b AccElem) {
|
||||
a.N += b.N
|
||||
a.Total += b.Total
|
||||
}
|
||||
|
||||
// avg converts total to average.
|
||||
func (a *AccElem) avg() uint64 {
|
||||
// Avg converts total to average.
|
||||
func (a AccElem) avg() uint64 {
|
||||
if a.N >= 1 && a.Total > 0 {
|
||||
return uint64(a.Total / a.N)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// LastMinuteLatencies keeps track of last minute latencies.
|
||||
type LastMinuteLatencies struct {
|
||||
Totals [60][sizeLastElemMarker]AccElem
|
||||
// lastMinuteLatency keeps track of last minute latency.
|
||||
type lastMinuteLatency struct {
|
||||
Totals [60]AccElem
|
||||
LastSec int64
|
||||
}
|
||||
|
||||
// Merge safely merges two LastMinuteLatencies structures into one
|
||||
func (l LastMinuteLatencies) Merge(o LastMinuteLatencies) (merged LastMinuteLatencies) {
|
||||
// Merge data of two lastMinuteLatency structure
|
||||
func (l lastMinuteLatency) merge(o lastMinuteLatency) (merged lastMinuteLatency) {
|
||||
if l.LastSec > o.LastSec {
|
||||
o.forwardTo(l.LastSec)
|
||||
merged.LastSec = l.LastSec
|
||||
@@ -113,57 +115,73 @@ func (l LastMinuteLatencies) Merge(o LastMinuteLatencies) (merged LastMinuteLate
|
||||
}
|
||||
|
||||
for i := range merged.Totals {
|
||||
for j := range merged.Totals[i] {
|
||||
merged.Totals[i][j] = AccElem{
|
||||
Total: l.Totals[i][j].Total + o.Totals[i][j].Total,
|
||||
N: l.Totals[i][j].N + o.Totals[i][j].N,
|
||||
}
|
||||
merged.Totals[i] = AccElem{
|
||||
Total: l.Totals[i].Total + o.Totals[i].Total,
|
||||
N: l.Totals[i].N + o.Totals[i].N,
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// Add a new duration data
|
||||
func (l *lastMinuteLatency) add(t time.Duration) {
|
||||
sec := time.Now().Unix()
|
||||
l.forwardTo(sec)
|
||||
winIdx := sec % 60
|
||||
l.Totals[winIdx].add(t)
|
||||
l.LastSec = sec
|
||||
}
|
||||
|
||||
// Merge all recorded latencies of last minute into one
|
||||
func (l *lastMinuteLatency) getAvgData() AccElem {
|
||||
var res AccElem
|
||||
sec := time.Now().Unix()
|
||||
l.forwardTo(sec)
|
||||
for _, elem := range l.Totals[:] {
|
||||
res.merge(elem)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// forwardTo time t, clearing any entries in between.
|
||||
func (l *lastMinuteLatency) forwardTo(t int64) {
|
||||
if l.LastSec >= t {
|
||||
return
|
||||
}
|
||||
if t-l.LastSec >= 60 {
|
||||
l.Totals = [60]AccElem{}
|
||||
return
|
||||
}
|
||||
for l.LastSec != t {
|
||||
// Clear next element.
|
||||
idx := (l.LastSec + 1) % 60
|
||||
l.Totals[idx] = AccElem{}
|
||||
l.LastSec++
|
||||
}
|
||||
}
|
||||
|
||||
// LastMinuteLatencies keeps track of last minute latencies.
|
||||
type LastMinuteLatencies [sizeLastElemMarker]lastMinuteLatency
|
||||
|
||||
// Merge safely merges two LastMinuteLatencies structures into one
|
||||
func (l LastMinuteLatencies) Merge(o LastMinuteLatencies) (merged LastMinuteLatencies) {
|
||||
for i := range l {
|
||||
merged[i] = l[i].merge(o[i])
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// Add latency t from object with the specified size.
|
||||
func (l *LastMinuteLatencies) Add(size int64, t time.Duration) {
|
||||
tag := sizeToTag(size)
|
||||
|
||||
// Update...
|
||||
sec := time.Now().Unix()
|
||||
l.forwardTo(sec)
|
||||
|
||||
winIdx := sec % 60
|
||||
l.Totals[winIdx][tag].add(t)
|
||||
|
||||
l.LastSec = sec
|
||||
l[sizeToTag(size)].add(t)
|
||||
}
|
||||
|
||||
// GetAvg will return the average for each bucket from the last time minute.
|
||||
// GetAvgData will return the average for each bucket from the last time minute.
|
||||
// The number of objects is also included.
|
||||
func (l *LastMinuteLatencies) GetAvg() [sizeLastElemMarker]AccElem {
|
||||
func (l *LastMinuteLatencies) GetAvgData() [sizeLastElemMarker]AccElem {
|
||||
var res [sizeLastElemMarker]AccElem
|
||||
sec := time.Now().Unix()
|
||||
l.forwardTo(sec)
|
||||
for _, elems := range l.Totals[:] {
|
||||
for j := range elems {
|
||||
res[j].merge(elems[j])
|
||||
}
|
||||
for i, elem := range l[:] {
|
||||
res[i] = elem.getAvgData()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// forwardTo time t, clearing any entries in between.
|
||||
func (l *LastMinuteLatencies) forwardTo(t int64) {
|
||||
if l.LastSec >= t {
|
||||
return
|
||||
}
|
||||
if t-l.LastSec >= 60 {
|
||||
l.Totals = [60][sizeLastElemMarker]AccElem{}
|
||||
return
|
||||
}
|
||||
for l.LastSec != t {
|
||||
// Clear next element.
|
||||
idx := (l.LastSec + 1) % 60
|
||||
l.Totals[idx] = [sizeLastElemMarker]AccElem{}
|
||||
l.LastSec++
|
||||
}
|
||||
}
|
||||
|
||||
+345
-103
@@ -136,6 +136,282 @@ func (z AccElem) Msgsize() (s int) {
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *LastMinuteLatencies) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != uint32(sizeLastElemMarker) {
|
||||
err = msgp.ArrayError{Wanted: uint32(sizeLastElemMarker), Got: zb0001}
|
||||
return
|
||||
}
|
||||
for za0001 := range z {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001)
|
||||
return
|
||||
}
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Totals":
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals")
|
||||
return
|
||||
}
|
||||
if zb0003 != uint32(60) {
|
||||
err = msgp.ArrayError{Wanted: uint32(60), Got: zb0003}
|
||||
return
|
||||
}
|
||||
for za0002 := range z[za0001].Totals {
|
||||
var zb0004 uint32
|
||||
zb0004, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002)
|
||||
return
|
||||
}
|
||||
for zb0004 > 0 {
|
||||
zb0004--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Total":
|
||||
z[za0001].Totals[za0002].Total, err = dc.ReadInt64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002, "Total")
|
||||
return
|
||||
}
|
||||
case "N":
|
||||
z[za0001].Totals[za0002].N, err = dc.ReadInt64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002, "N")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "LastSec":
|
||||
z[za0001].LastSec, err = dc.ReadInt64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "LastSec")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *LastMinuteLatencies) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = en.WriteArrayHeader(uint32(sizeLastElemMarker))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for za0001 := range z {
|
||||
// map header, size 2
|
||||
// write "Totals"
|
||||
err = en.Append(0x82, 0xa6, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteArrayHeader(uint32(60))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals")
|
||||
return
|
||||
}
|
||||
for za0002 := range z[za0001].Totals {
|
||||
// map header, size 2
|
||||
// write "Total"
|
||||
err = en.Append(0x82, 0xa5, 0x54, 0x6f, 0x74, 0x61, 0x6c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt64(z[za0001].Totals[za0002].Total)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002, "Total")
|
||||
return
|
||||
}
|
||||
// write "N"
|
||||
err = en.Append(0xa1, 0x4e)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt64(z[za0001].Totals[za0002].N)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002, "N")
|
||||
return
|
||||
}
|
||||
}
|
||||
// write "LastSec"
|
||||
err = en.Append(0xa7, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x63)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt64(z[za0001].LastSec)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "LastSec")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *LastMinuteLatencies) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
o = msgp.AppendArrayHeader(o, uint32(sizeLastElemMarker))
|
||||
for za0001 := range z {
|
||||
// map header, size 2
|
||||
// string "Totals"
|
||||
o = append(o, 0x82, 0xa6, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73)
|
||||
o = msgp.AppendArrayHeader(o, uint32(60))
|
||||
for za0002 := range z[za0001].Totals {
|
||||
// map header, size 2
|
||||
// string "Total"
|
||||
o = append(o, 0x82, 0xa5, 0x54, 0x6f, 0x74, 0x61, 0x6c)
|
||||
o = msgp.AppendInt64(o, z[za0001].Totals[za0002].Total)
|
||||
// string "N"
|
||||
o = append(o, 0xa1, 0x4e)
|
||||
o = msgp.AppendInt64(o, z[za0001].Totals[za0002].N)
|
||||
}
|
||||
// string "LastSec"
|
||||
o = append(o, 0xa7, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x63)
|
||||
o = msgp.AppendInt64(o, z[za0001].LastSec)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *LastMinuteLatencies) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != uint32(sizeLastElemMarker) {
|
||||
err = msgp.ArrayError{Wanted: uint32(sizeLastElemMarker), Got: zb0001}
|
||||
return
|
||||
}
|
||||
for za0001 := range z {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001)
|
||||
return
|
||||
}
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Totals":
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals")
|
||||
return
|
||||
}
|
||||
if zb0003 != uint32(60) {
|
||||
err = msgp.ArrayError{Wanted: uint32(60), Got: zb0003}
|
||||
return
|
||||
}
|
||||
for za0002 := range z[za0001].Totals {
|
||||
var zb0004 uint32
|
||||
zb0004, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002)
|
||||
return
|
||||
}
|
||||
for zb0004 > 0 {
|
||||
zb0004--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Total":
|
||||
z[za0001].Totals[za0002].Total, bts, err = msgp.ReadInt64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002, "Total")
|
||||
return
|
||||
}
|
||||
case "N":
|
||||
z[za0001].Totals[za0002].N, bts, err = msgp.ReadInt64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002, "N")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "Totals", za0002)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "LastSec":
|
||||
z[za0001].LastSec, bts, err = msgp.ReadInt64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001, "LastSec")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *LastMinuteLatencies) Msgsize() (s int) {
|
||||
s = msgp.ArrayHeaderSize + (sizeLastElemMarker * (16 + (60 * (9 + msgp.Int64Size + msgp.Int64Size)) + msgp.Int64Size))
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *lastMinuteLatency) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
@@ -165,48 +441,36 @@ func (z *LastMinuteLatencies) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
}
|
||||
for za0001 := range z.Totals {
|
||||
var zb0003 uint32
|
||||
zb0003, err = dc.ReadArrayHeader()
|
||||
zb0003, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001)
|
||||
return
|
||||
}
|
||||
if zb0003 != uint32(sizeLastElemMarker) {
|
||||
err = msgp.ArrayError{Wanted: uint32(sizeLastElemMarker), Got: zb0003}
|
||||
return
|
||||
}
|
||||
for za0002 := range z.Totals[za0001] {
|
||||
var zb0004 uint32
|
||||
zb0004, err = dc.ReadMapHeader()
|
||||
for zb0003 > 0 {
|
||||
zb0003--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002)
|
||||
err = msgp.WrapError(err, "Totals", za0001)
|
||||
return
|
||||
}
|
||||
for zb0004 > 0 {
|
||||
zb0004--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Total":
|
||||
z.Totals[za0001].Total, err = dc.ReadInt64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002)
|
||||
err = msgp.WrapError(err, "Totals", za0001, "Total")
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Total":
|
||||
z.Totals[za0001][za0002].Total, err = dc.ReadInt64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002, "Total")
|
||||
return
|
||||
}
|
||||
case "N":
|
||||
z.Totals[za0001][za0002].N, err = dc.ReadInt64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002, "N")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002)
|
||||
return
|
||||
}
|
||||
case "N":
|
||||
z.Totals[za0001].N, err = dc.ReadInt64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, "N")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,7 +493,7 @@ func (z *LastMinuteLatencies) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *LastMinuteLatencies) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
func (z *lastMinuteLatency) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 2
|
||||
// write "Totals"
|
||||
err = en.Append(0x82, 0xa6, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73)
|
||||
@@ -242,33 +506,26 @@ func (z *LastMinuteLatencies) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
return
|
||||
}
|
||||
for za0001 := range z.Totals {
|
||||
err = en.WriteArrayHeader(uint32(sizeLastElemMarker))
|
||||
// map header, size 2
|
||||
// write "Total"
|
||||
err = en.Append(0x82, 0xa5, 0x54, 0x6f, 0x74, 0x61, 0x6c)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001)
|
||||
return
|
||||
}
|
||||
for za0002 := range z.Totals[za0001] {
|
||||
// map header, size 2
|
||||
// write "Total"
|
||||
err = en.Append(0x82, 0xa5, 0x54, 0x6f, 0x74, 0x61, 0x6c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt64(z.Totals[za0001][za0002].Total)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002, "Total")
|
||||
return
|
||||
}
|
||||
// write "N"
|
||||
err = en.Append(0xa1, 0x4e)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt64(z.Totals[za0001][za0002].N)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002, "N")
|
||||
return
|
||||
}
|
||||
err = en.WriteInt64(z.Totals[za0001].Total)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, "Total")
|
||||
return
|
||||
}
|
||||
// write "N"
|
||||
err = en.Append(0xa1, 0x4e)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt64(z.Totals[za0001].N)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, "N")
|
||||
return
|
||||
}
|
||||
}
|
||||
// write "LastSec"
|
||||
@@ -285,23 +542,20 @@ func (z *LastMinuteLatencies) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *LastMinuteLatencies) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
func (z *lastMinuteLatency) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 2
|
||||
// string "Totals"
|
||||
o = append(o, 0x82, 0xa6, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73)
|
||||
o = msgp.AppendArrayHeader(o, uint32(60))
|
||||
for za0001 := range z.Totals {
|
||||
o = msgp.AppendArrayHeader(o, uint32(sizeLastElemMarker))
|
||||
for za0002 := range z.Totals[za0001] {
|
||||
// map header, size 2
|
||||
// string "Total"
|
||||
o = append(o, 0x82, 0xa5, 0x54, 0x6f, 0x74, 0x61, 0x6c)
|
||||
o = msgp.AppendInt64(o, z.Totals[za0001][za0002].Total)
|
||||
// string "N"
|
||||
o = append(o, 0xa1, 0x4e)
|
||||
o = msgp.AppendInt64(o, z.Totals[za0001][za0002].N)
|
||||
}
|
||||
// map header, size 2
|
||||
// string "Total"
|
||||
o = append(o, 0x82, 0xa5, 0x54, 0x6f, 0x74, 0x61, 0x6c)
|
||||
o = msgp.AppendInt64(o, z.Totals[za0001].Total)
|
||||
// string "N"
|
||||
o = append(o, 0xa1, 0x4e)
|
||||
o = msgp.AppendInt64(o, z.Totals[za0001].N)
|
||||
}
|
||||
// string "LastSec"
|
||||
o = append(o, 0xa7, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x63)
|
||||
@@ -310,7 +564,7 @@ func (z *LastMinuteLatencies) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *LastMinuteLatencies) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
func (z *lastMinuteLatency) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
@@ -340,48 +594,36 @@ func (z *LastMinuteLatencies) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
}
|
||||
for za0001 := range z.Totals {
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001)
|
||||
return
|
||||
}
|
||||
if zb0003 != uint32(sizeLastElemMarker) {
|
||||
err = msgp.ArrayError{Wanted: uint32(sizeLastElemMarker), Got: zb0003}
|
||||
return
|
||||
}
|
||||
for za0002 := range z.Totals[za0001] {
|
||||
var zb0004 uint32
|
||||
zb0004, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
for zb0003 > 0 {
|
||||
zb0003--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002)
|
||||
err = msgp.WrapError(err, "Totals", za0001)
|
||||
return
|
||||
}
|
||||
for zb0004 > 0 {
|
||||
zb0004--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Total":
|
||||
z.Totals[za0001].Total, bts, err = msgp.ReadInt64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002)
|
||||
err = msgp.WrapError(err, "Totals", za0001, "Total")
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Total":
|
||||
z.Totals[za0001][za0002].Total, bts, err = msgp.ReadInt64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002, "Total")
|
||||
return
|
||||
}
|
||||
case "N":
|
||||
z.Totals[za0001][za0002].N, bts, err = msgp.ReadInt64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002, "N")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, za0002)
|
||||
return
|
||||
}
|
||||
case "N":
|
||||
z.Totals[za0001].N, bts, err = msgp.ReadInt64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001, "N")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Totals", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,7 +647,7 @@ func (z *LastMinuteLatencies) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *LastMinuteLatencies) Msgsize() (s int) {
|
||||
s = 1 + 7 + msgp.ArrayHeaderSize + (60 * (sizeLastElemMarker * (9 + msgp.Int64Size + msgp.Int64Size))) + 8 + msgp.Int64Size
|
||||
func (z *lastMinuteLatency) Msgsize() (s int) {
|
||||
s = 1 + 7 + msgp.ArrayHeaderSize + (60 * (9 + msgp.Int64Size + msgp.Int64Size)) + 8 + msgp.Int64Size
|
||||
return
|
||||
}
|
||||
|
||||
@@ -234,3 +234,116 @@ func BenchmarkDecodeLastMinuteLatencies(b *testing.B) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshallastMinuteLatency(t *testing.T) {
|
||||
v := lastMinuteLatency{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsglastMinuteLatency(b *testing.B) {
|
||||
v := lastMinuteLatency{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsglastMinuteLatency(b *testing.B) {
|
||||
v := lastMinuteLatency{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshallastMinuteLatency(b *testing.B) {
|
||||
v := lastMinuteLatency{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodelastMinuteLatency(t *testing.T) {
|
||||
v := lastMinuteLatency{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodelastMinuteLatency Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := lastMinuteLatency{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodelastMinuteLatency(b *testing.B) {
|
||||
v := lastMinuteLatency{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecodelastMinuteLatency(b *testing.B) {
|
||||
v := lastMinuteLatency{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-19
@@ -322,11 +322,10 @@ func (l *localLocker) Refresh(ctx context.Context, args dsync.LockArgs) (refresh
|
||||
}
|
||||
idx := 0
|
||||
for {
|
||||
mapID := formatUUID(args.UID, idx)
|
||||
lris, ok := l.lockMap[resource]
|
||||
if !ok {
|
||||
// Inconsistent. Delete UID.
|
||||
delete(l.lockUID, mapID)
|
||||
delete(l.lockUID, formatUUID(args.UID, idx))
|
||||
return idx > 0, nil
|
||||
}
|
||||
for i := range lris {
|
||||
@@ -335,7 +334,7 @@ func (l *localLocker) Refresh(ctx context.Context, args dsync.LockArgs) (refresh
|
||||
}
|
||||
}
|
||||
idx++
|
||||
resource, ok = l.lockUID[mapID]
|
||||
resource, ok = l.lockUID[formatUUID(args.UID, idx)]
|
||||
if !ok {
|
||||
// No more resources for UID, but we did update at least one.
|
||||
return true, nil
|
||||
@@ -350,25 +349,29 @@ func (l *localLocker) expireOldLocks(interval time.Duration) {
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
for k := range l.lockMap {
|
||||
found := false
|
||||
// Since we mutate the value, remove one per loop.
|
||||
for {
|
||||
lris, ok := l.lockMap[k]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
for _, lri := range lris {
|
||||
if time.Since(lri.TimeLastRefresh) > interval {
|
||||
l.removeEntry(lri.Name, dsync.LockArgs{Owner: lri.Owner, UID: lri.UID}, &lris)
|
||||
found = true
|
||||
for k, lris := range l.lockMap {
|
||||
modified := false
|
||||
for i := 0; i < len(lris); {
|
||||
lri := &lris[i]
|
||||
if time.Since(lri.TimeLastRefresh) > interval {
|
||||
delete(l.lockUID, formatUUID(lri.UID, lri.idx))
|
||||
if len(lris) == 1 {
|
||||
// Remove the write lock.
|
||||
delete(l.lockMap, lri.Name)
|
||||
modified = false
|
||||
break
|
||||
}
|
||||
modified = true
|
||||
// Remove the appropriate lock.
|
||||
lris = append(lris[:i], lris[i+1:]...)
|
||||
// Check same i
|
||||
} else {
|
||||
// Move to next
|
||||
i++
|
||||
}
|
||||
// We did not find any more to expire.
|
||||
if !found {
|
||||
break
|
||||
}
|
||||
}
|
||||
if modified {
|
||||
l.lockMap[k] = lris
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,13 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/minio/minio/internal/dsync"
|
||||
)
|
||||
|
||||
@@ -254,3 +258,89 @@ func TestLocalLockerUnlock(t *testing.T) {
|
||||
t.Fatalf("lockUID len, got %d, want %d + %d", len(l.lockUID), 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_localLocker_expireOldLocksExpire(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(0))
|
||||
// Numbers of unique locks
|
||||
for _, locks := range []int{100, 1000, 1e6} {
|
||||
if testing.Short() && locks > 100 {
|
||||
continue
|
||||
}
|
||||
t.Run(fmt.Sprintf("%d-locks", locks), func(t *testing.T) {
|
||||
// Number of readers per lock...
|
||||
for _, readers := range []int{1, 10, 100} {
|
||||
if locks > 1000 && readers > 1 {
|
||||
continue
|
||||
}
|
||||
if testing.Short() && readers > 10 {
|
||||
continue
|
||||
}
|
||||
t.Run(fmt.Sprintf("%d-read", readers), func(t *testing.T) {
|
||||
l := newLocker()
|
||||
for i := 0; i < locks; i++ {
|
||||
var tmp [16]byte
|
||||
rng.Read(tmp[:])
|
||||
res := []string{hex.EncodeToString(tmp[:])}
|
||||
|
||||
for i := 0; i < readers; i++ {
|
||||
rng.Read(tmp[:])
|
||||
ok, err := l.RLock(context.Background(), dsync.LockArgs{
|
||||
UID: uuid.NewString(),
|
||||
Resources: res,
|
||||
Source: hex.EncodeToString(tmp[:8]),
|
||||
Owner: hex.EncodeToString(tmp[8:]),
|
||||
Quorum: 0,
|
||||
})
|
||||
if !ok || err != nil {
|
||||
t.Fatal("failed:", err, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
start := time.Now()
|
||||
l.expireOldLocks(time.Hour)
|
||||
t.Logf("Scan Took: %v. Left: %d/%d", time.Since(start).Round(time.Millisecond), len(l.lockUID), len(l.lockMap))
|
||||
if len(l.lockMap) != locks {
|
||||
t.Fatalf("objects deleted, want %d != got %d", locks, len(l.lockMap))
|
||||
}
|
||||
if len(l.lockUID) != locks*readers {
|
||||
t.Fatalf("objects deleted, want %d != got %d", locks*readers, len(l.lockUID))
|
||||
}
|
||||
|
||||
// Expire 50%
|
||||
expired := time.Now().Add(-time.Hour * 2)
|
||||
for _, v := range l.lockMap {
|
||||
for i := range v {
|
||||
if rng.Intn(2) == 0 {
|
||||
v[i].TimeLastRefresh = expired
|
||||
}
|
||||
}
|
||||
}
|
||||
start = time.Now()
|
||||
l.expireOldLocks(time.Hour)
|
||||
t.Logf("Expire 50%% took: %v. Left: %d/%d", time.Since(start).Round(time.Millisecond), len(l.lockUID), len(l.lockMap))
|
||||
|
||||
if len(l.lockUID) == locks*readers {
|
||||
t.Fatalf("objects uids all remain, unlikely")
|
||||
}
|
||||
if len(l.lockMap) == 0 {
|
||||
t.Fatalf("objects all deleted, 0 remains")
|
||||
}
|
||||
if len(l.lockUID) == 0 {
|
||||
t.Fatalf("objects uids all deleted, 0 remains")
|
||||
}
|
||||
|
||||
start = time.Now()
|
||||
l.expireOldLocks(-time.Minute)
|
||||
t.Logf("Expire rest took: %v. Left: %d/%d", time.Since(start).Round(time.Millisecond), len(l.lockUID), len(l.lockMap))
|
||||
|
||||
if len(l.lockMap) != 0 {
|
||||
t.Fatalf("objects not deleted, want %d != got %d", 0, len(l.lockMap))
|
||||
}
|
||||
if len(l.lockUID) != 0 {
|
||||
t.Fatalf("objects not deleted, want %d != got %d", 0, len(l.lockUID))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,13 +542,9 @@ func (er *erasureObjects) listPath(ctx context.Context, o listPathOptions, resul
|
||||
var fallbackDisks []StorageAPI
|
||||
|
||||
// Special case: ask all disks if the drive count is 4
|
||||
if askDisks == -1 || er.setDriveCount == 4 {
|
||||
askDisks = len(disks) // with 'strict' quorum list on all online disks.
|
||||
listingQuorum = er.defaultRQuorum()
|
||||
}
|
||||
if askDisks == 0 {
|
||||
askDisks = globalAPIConfig.getListQuorum()
|
||||
listingQuorum = askDisks
|
||||
if askDisks <= 0 || er.setDriveCount == 4 {
|
||||
askDisks = len(disks) // with 'strict' quorum list on all online disks.
|
||||
listingQuorum = len(disks) / 2 // keep this such that we can list all objects with different quorum ratio.
|
||||
}
|
||||
if askDisks > 0 && len(disks) > askDisks {
|
||||
rand.Shuffle(len(disks), func(i, j int) {
|
||||
|
||||
+171
-86
@@ -27,6 +27,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/minio/internal/bucket/lifecycle"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -70,6 +71,7 @@ func init() {
|
||||
|
||||
nodeCollector = newMinioCollectorNode([]*MetricsGroup{
|
||||
getNodeHealthMetrics(),
|
||||
getLocalDiskStorageMetrics(),
|
||||
getCacheMetrics(),
|
||||
getHTTPMetrics(),
|
||||
getNetworkMetrics(),
|
||||
@@ -115,6 +117,7 @@ const (
|
||||
softwareSubsystem MetricSubsystem = "software"
|
||||
sysCallSubsystem MetricSubsystem = "syscall"
|
||||
usageSubsystem MetricSubsystem = "usage"
|
||||
quotaSubsystem MetricSubsystem = "quota"
|
||||
ilmSubsystem MetricSubsystem = "ilm"
|
||||
scannerSubsystem MetricSubsystem = "scanner"
|
||||
)
|
||||
@@ -157,6 +160,8 @@ const (
|
||||
writeBytes MetricName = "write_bytes"
|
||||
wcharBytes MetricName = "wchar_bytes"
|
||||
|
||||
apiLatencyMicroSec MetricName = "latency_us"
|
||||
|
||||
usagePercent MetricName = "update_percent"
|
||||
|
||||
commitInfo MetricName = "commit_info"
|
||||
@@ -315,6 +320,16 @@ func getClusterCapacityUsageFreeBytesMD() MetricDescription {
|
||||
}
|
||||
}
|
||||
|
||||
func getNodeDiskAPILatencyMD() MetricDescription {
|
||||
return MetricDescription{
|
||||
Namespace: nodeMetricNamespace,
|
||||
Subsystem: diskSubsystem,
|
||||
Name: apiLatencyMicroSec,
|
||||
Help: "Average last minute latency in µs for disk API storage operations.",
|
||||
Type: gaugeMetric,
|
||||
}
|
||||
}
|
||||
|
||||
func getNodeDiskUsedBytesMD() MetricDescription {
|
||||
return MetricDescription{
|
||||
Namespace: nodeMetricNamespace,
|
||||
@@ -395,6 +410,16 @@ func getUsageLastScanActivityMD() MetricDescription {
|
||||
}
|
||||
}
|
||||
|
||||
func getBucketUsageQuotaTotalBytesMD() MetricDescription {
|
||||
return MetricDescription{
|
||||
Namespace: bucketMetricNamespace,
|
||||
Subsystem: quotaSubsystem,
|
||||
Name: totalBytes,
|
||||
Help: "Total bucket quota size in bytes",
|
||||
Type: gaugeMetric,
|
||||
}
|
||||
}
|
||||
|
||||
func getBucketUsageTotalBytesMD() MetricDescription {
|
||||
return MetricDescription{
|
||||
Namespace: bucketMetricNamespace,
|
||||
@@ -917,94 +942,109 @@ func getMinioProcMetrics() *MetricsGroup {
|
||||
logger.LogOnceIf(ctx, err, nodeMetricNamespace)
|
||||
return
|
||||
}
|
||||
var openFDs int
|
||||
openFDs, err = p.FileDescriptorsLen()
|
||||
if err != nil {
|
||||
logger.LogOnceIf(ctx, err, getMinioFDOpenMD())
|
||||
return
|
||||
}
|
||||
l, err := p.Limits()
|
||||
if err != nil {
|
||||
logger.LogOnceIf(ctx, err, getMinioFDLimitMD())
|
||||
return
|
||||
}
|
||||
io, err := p.IO()
|
||||
if err != nil {
|
||||
logger.LogOnceIf(ctx, err, ioSubsystem)
|
||||
return
|
||||
}
|
||||
stat, err := p.Stat()
|
||||
if err != nil {
|
||||
logger.LogOnceIf(ctx, err, processSubsystem)
|
||||
return
|
||||
}
|
||||
startTime, err := stat.StartTime()
|
||||
if err != nil {
|
||||
logger.LogOnceIf(ctx, err, startTime)
|
||||
return
|
||||
|
||||
openFDs, _ := p.FileDescriptorsLen()
|
||||
l, _ := p.Limits()
|
||||
io, _ := p.IO()
|
||||
stat, _ := p.Stat()
|
||||
startTime, _ := stat.StartTime()
|
||||
|
||||
if openFDs > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioFDOpenMD(),
|
||||
Value: float64(openFDs),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioFDOpenMD(),
|
||||
Value: float64(openFDs),
|
||||
},
|
||||
)
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioFDLimitMD(),
|
||||
Value: float64(l.OpenFiles),
|
||||
})
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessSysCallRMD(),
|
||||
Value: float64(io.SyscR),
|
||||
})
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessSysCallWMD(),
|
||||
Value: float64(io.SyscW),
|
||||
})
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioProcessIOReadBytesMD(),
|
||||
Value: float64(io.ReadBytes),
|
||||
})
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioProcessIOWriteBytesMD(),
|
||||
Value: float64(io.WriteBytes),
|
||||
})
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioProcessIOReadCachedBytesMD(),
|
||||
Value: float64(io.RChar),
|
||||
})
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioProcessIOWriteCachedBytesMD(),
|
||||
Value: float64(io.WChar),
|
||||
})
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessStartTimeMD(),
|
||||
Value: startTime,
|
||||
})
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessUptimeMD(),
|
||||
Value: time.Since(globalBootTime).Seconds(),
|
||||
})
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessResidentMemory(),
|
||||
Value: float64(stat.ResidentMemory()),
|
||||
})
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessCPUTime(),
|
||||
Value: stat.CPUTime(),
|
||||
})
|
||||
if l.OpenFiles > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioFDLimitMD(),
|
||||
Value: float64(l.OpenFiles),
|
||||
})
|
||||
}
|
||||
|
||||
if io.SyscR > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessSysCallRMD(),
|
||||
Value: float64(io.SyscR),
|
||||
})
|
||||
}
|
||||
|
||||
if io.SyscW > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessSysCallWMD(),
|
||||
Value: float64(io.SyscW),
|
||||
})
|
||||
}
|
||||
|
||||
if io.ReadBytes > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioProcessIOReadBytesMD(),
|
||||
Value: float64(io.ReadBytes),
|
||||
})
|
||||
}
|
||||
|
||||
if io.WriteBytes > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioProcessIOWriteBytesMD(),
|
||||
Value: float64(io.WriteBytes),
|
||||
})
|
||||
}
|
||||
|
||||
if io.RChar > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioProcessIOReadCachedBytesMD(),
|
||||
Value: float64(io.RChar),
|
||||
})
|
||||
}
|
||||
|
||||
if io.WChar > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinioProcessIOWriteCachedBytesMD(),
|
||||
Value: float64(io.WChar),
|
||||
})
|
||||
}
|
||||
|
||||
if startTime > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessStartTimeMD(),
|
||||
Value: startTime,
|
||||
})
|
||||
}
|
||||
|
||||
if !globalBootTime.IsZero() {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessUptimeMD(),
|
||||
Value: time.Since(globalBootTime).Seconds(),
|
||||
})
|
||||
}
|
||||
|
||||
if stat.ResidentMemory() > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessResidentMemory(),
|
||||
Value: float64(stat.ResidentMemory()),
|
||||
})
|
||||
}
|
||||
|
||||
if stat.CPUTime() > 0 {
|
||||
metrics = append(metrics,
|
||||
Metric{
|
||||
Description: getMinIOProcessCPUTime(),
|
||||
Value: stat.CPUTime(),
|
||||
})
|
||||
}
|
||||
return
|
||||
})
|
||||
return mg
|
||||
@@ -1488,6 +1528,8 @@ func getBucketUsageMetrics() *MetricsGroup {
|
||||
for bucket, usage := range dataUsageInfo.BucketsUsage {
|
||||
stats := getLatestReplicationStats(bucket, usage)
|
||||
|
||||
quota, _ := globalBucketQuotaSys.Get(ctx, bucket)
|
||||
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getBucketUsageTotalBytesMD(),
|
||||
Value: float64(usage.Size),
|
||||
@@ -1506,6 +1548,14 @@ func getBucketUsageMetrics() *MetricsGroup {
|
||||
VariableLabels: map[string]string{"bucket": bucket},
|
||||
})
|
||||
|
||||
if quota != nil && quota.Quota > 0 {
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getBucketUsageQuotaTotalBytesMD(),
|
||||
Value: float64(quota.Quota),
|
||||
VariableLabels: map[string]string{"bucket": bucket},
|
||||
})
|
||||
}
|
||||
|
||||
if stats.hasReplicationUsage() {
|
||||
for arn, stat := range stats.Stats {
|
||||
metrics = append(metrics, Metric{
|
||||
@@ -1583,6 +1633,41 @@ func getLocalStorageMetrics() *MetricsGroup {
|
||||
Value: float64(disk.FreeInodes),
|
||||
VariableLabels: map[string]string{"disk": disk.DrivePath},
|
||||
})
|
||||
|
||||
}
|
||||
return
|
||||
})
|
||||
return mg
|
||||
}
|
||||
|
||||
func getLocalDiskStorageMetrics() *MetricsGroup {
|
||||
mg := &MetricsGroup{
|
||||
cacheInterval: 3 * time.Second,
|
||||
}
|
||||
mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
|
||||
objLayer := newObjectLayerFn()
|
||||
// Service not initialized yet
|
||||
if objLayer == nil || globalIsGateway {
|
||||
return
|
||||
}
|
||||
|
||||
storageInfo, _ := objLayer.LocalStorageInfo(ctx)
|
||||
if storageInfo.Backend.Type == madmin.FS {
|
||||
return
|
||||
}
|
||||
metrics = make([]Metric, 0, 50)
|
||||
for _, disk := range storageInfo.Disks {
|
||||
if disk.Metrics == nil {
|
||||
continue
|
||||
}
|
||||
for apiName, latency := range disk.Metrics.APILatencies {
|
||||
val := latency.(uint64)
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getNodeDiskAPILatencyMD(),
|
||||
Value: float64(val / 1000),
|
||||
VariableLabels: map[string]string{"disk": disk.DrivePath, "api": "storage." + apiName},
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
|
||||
+2
-1
@@ -674,7 +674,7 @@ func metricsHandler() http.Handler {
|
||||
// AuthMiddleware checks if the bearer token is valid and authorized.
|
||||
func AuthMiddleware(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
claims, owner, authErr := webRequestAuthenticate(r)
|
||||
claims, groups, owner, authErr := webRequestAuthenticate(r)
|
||||
if authErr != nil || !claims.VerifyIssuer("prometheus", true) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
@@ -682,6 +682,7 @@ func AuthMiddleware(h http.Handler) http.Handler {
|
||||
// For authenticated users apply IAM policy.
|
||||
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||
AccountName: claims.AccessKey,
|
||||
Groups: groups,
|
||||
Action: iampolicy.PrometheusAdminAction,
|
||||
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||
IsOwner: owner,
|
||||
|
||||
@@ -563,6 +563,7 @@ func (sys *NotificationSys) DeleteBucketMetadata(ctx context.Context, bucketName
|
||||
globalReplicationStats.Delete(bucketName)
|
||||
globalBucketMetadataSys.Remove(bucketName)
|
||||
globalBucketTargetSys.Delete(bucketName)
|
||||
globalNotificationSys.RemoveNotification(bucketName)
|
||||
if localMetacacheMgr != nil {
|
||||
localMetacacheMgr.deleteBucketCache(bucketName)
|
||||
}
|
||||
@@ -1611,6 +1612,39 @@ func (sys *NotificationSys) Speedtest(ctx context.Context, size int,
|
||||
return results
|
||||
}
|
||||
|
||||
// DriveSpeedTest - Drive performance information
|
||||
func (sys *NotificationSys) DriveSpeedTest(ctx context.Context, opts madmin.DriveSpeedTestOpts) chan madmin.DriveSpeedTestResult {
|
||||
ch := make(chan madmin.DriveSpeedTestResult)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, client := range sys.peerClients {
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(client *peerRESTClient) {
|
||||
defer wg.Done()
|
||||
resp, err := client.DriveSpeedTest(ctx, opts)
|
||||
if err != nil {
|
||||
resp.Error = err.Error()
|
||||
}
|
||||
|
||||
ch <- resp
|
||||
|
||||
reqInfo := (&logger.ReqInfo{}).AppendTags("remotePeer", client.host.String())
|
||||
ctx := logger.SetReqInfo(GlobalContext, reqInfo)
|
||||
logger.LogIf(ctx, err)
|
||||
}(client)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
// ReloadSiteReplicationConfig - tells all peer minio nodes to reload the
|
||||
// site-replication configuration.
|
||||
func (sys *NotificationSys) ReloadSiteReplicationConfig(ctx context.Context) []error {
|
||||
@@ -1630,3 +1664,31 @@ func (sys *NotificationSys) ReloadSiteReplicationConfig(ctx context.Context) []e
|
||||
wg.Wait()
|
||||
return errs
|
||||
}
|
||||
|
||||
// GetLastDayTierStats fetches per-tier stats of the last 24hrs from all peers
|
||||
func (sys *NotificationSys) GetLastDayTierStats(ctx context.Context) dailyAllTierStats {
|
||||
errs := make([]error, len(sys.allPeerClients))
|
||||
lastDayStats := make([]dailyAllTierStats, len(sys.allPeerClients))
|
||||
var wg sync.WaitGroup
|
||||
for index := range sys.peerClients {
|
||||
if sys.peerClients[index] == nil {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(index int) {
|
||||
defer wg.Done()
|
||||
lastDayStats[index], errs[index] = sys.peerClients[index].GetLastDayTierStats(ctx)
|
||||
}(index)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
merged := globalTransitionState.getDailyAllTierStats()
|
||||
for i, stat := range lastDayStats {
|
||||
if errs[i] != nil {
|
||||
logger.LogIf(ctx, fmt.Errorf("failed to fetch last day tier stats: %w", errs[i]))
|
||||
continue
|
||||
}
|
||||
merged.merge(stat)
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ type ObjectOptions struct {
|
||||
ServerSideEncryption encrypt.ServerSide
|
||||
VersionSuspended bool // indicates if the bucket was previously versioned but is currently suspended.
|
||||
Versioned bool // indicates if the bucket is versioned
|
||||
WalkVersions bool // indicates if the we are interested in walking versions
|
||||
VersionID string // Specifies the versionID which needs to be overwritten or read
|
||||
MTime time.Time // Is only set in POST/PUT operations
|
||||
Expires time.Time // Is only used in POST/PUT operations
|
||||
|
||||
@@ -61,10 +61,7 @@ const (
|
||||
// MinIO tmp meta prefix.
|
||||
minioMetaTmpBucket = minioMetaBucket + "/tmp"
|
||||
// MinIO tmp meta prefix for deleted objects.
|
||||
minioMetaTmpDeletedBucket = minioMetaTmpBucket + "/.trash"
|
||||
minioMetaSpeedTestBucket = minioMetaBucket + "/speedtest"
|
||||
minioMetaSpeedTestBucketPrefix = "objects/"
|
||||
|
||||
minioMetaTmpDeletedBucket = minioMetaTmpBucket + "/.trash"
|
||||
// DNS separator (period), used for bucket name validation.
|
||||
dnsDelimiter = "."
|
||||
// On compressed files bigger than this;
|
||||
|
||||
+12
-5
@@ -1106,7 +1106,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
length := actualSize
|
||||
|
||||
if !cpSrcDstSame {
|
||||
if err := enforceBucketQuota(ctx, dstBucket, actualSize); err != nil {
|
||||
if err := enforceBucketQuotaHard(ctx, dstBucket, actualSize); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -1659,7 +1659,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
}
|
||||
}
|
||||
|
||||
if err := enforceBucketQuota(ctx, bucket, size); err != nil {
|
||||
if err := enforceBucketQuotaHard(ctx, bucket, size); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -1996,7 +1996,7 @@ func (api objectAPIHandlers) PutObjectExtractHandler(w http.ResponseWriter, r *h
|
||||
return
|
||||
}
|
||||
|
||||
if err := enforceBucketQuota(ctx, bucket, size); err != nil {
|
||||
if err := enforceBucketQuotaHard(ctx, bucket, size); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -2418,6 +2418,13 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
|
||||
var parseRangeErr error
|
||||
if rangeHeader := r.Header.Get(xhttp.AmzCopySourceRange); rangeHeader != "" {
|
||||
rs, parseRangeErr = parseCopyPartRangeSpec(rangeHeader)
|
||||
} else {
|
||||
// This check is to see if client specified a header but the value
|
||||
// is empty for 'x-amz-copy-source-range'
|
||||
_, ok := r.Header[xhttp.AmzCopySourceRange]
|
||||
if ok {
|
||||
parseRangeErr = errInvalidRange
|
||||
}
|
||||
}
|
||||
|
||||
checkCopyPartPrecondFn := func(o ObjectInfo) bool {
|
||||
@@ -2465,7 +2472,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
if err := enforceBucketQuota(ctx, dstBucket, actualPartSize); err != nil {
|
||||
if err := enforceBucketQuotaHard(ctx, dstBucket, actualPartSize); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
@@ -2754,7 +2761,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
||||
}
|
||||
}
|
||||
|
||||
if err := enforceBucketQuota(ctx, bucket, size); err != nil {
|
||||
if err := enforceBucketQuotaHard(ctx, bucket, size); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1897,6 +1897,17 @@ func testAPICopyObjectPartHandler(obj ObjectLayer, instanceType, bucketName stri
|
||||
secretKey: credentials.SecretKey,
|
||||
expectedRespStatus: http.StatusNotFound,
|
||||
},
|
||||
// Test case - 16, Test case with ivalid byte range empty value.
|
||||
{
|
||||
bucketName: bucketName,
|
||||
uploadID: uploadID,
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
copySourceRange: "empty",
|
||||
accessKey: credentials.AccessKey,
|
||||
secretKey: credentials.SecretKey,
|
||||
|
||||
expectedRespStatus: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
@@ -1920,7 +1931,11 @@ func testAPICopyObjectPartHandler(obj ObjectLayer, instanceType, bucketName stri
|
||||
req.Header.Set("X-Amz-Copy-Source", testCase.copySourceHeader)
|
||||
}
|
||||
if testCase.copySourceRange != "" {
|
||||
req.Header.Set("X-Amz-Copy-Source-Range", testCase.copySourceRange)
|
||||
if testCase.copySourceRange == "empty" {
|
||||
req.Header.Set("X-Amz-Copy-Source-Range", "") // specifically test for S3 errors in this scenario.
|
||||
} else {
|
||||
req.Header.Set("X-Amz-Copy-Source-Range", testCase.copySourceRange)
|
||||
}
|
||||
}
|
||||
|
||||
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
||||
|
||||
@@ -1067,6 +1067,35 @@ func (client *peerRESTClient) Speedtest(ctx context.Context, size,
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (client *peerRESTClient) DriveSpeedTest(ctx context.Context, opts madmin.DriveSpeedTestOpts) (madmin.DriveSpeedTestResult, error) {
|
||||
queryVals := make(url.Values)
|
||||
if opts.Serial {
|
||||
queryVals.Set("serial", "true")
|
||||
}
|
||||
queryVals.Set("blocksize", strconv.FormatUint(opts.BlockSize, 10))
|
||||
queryVals.Set("filesize", strconv.FormatUint(opts.FileSize, 10))
|
||||
|
||||
respBody, err := client.callWithContext(ctx, peerRESTMethodDriveSpeedTest, queryVals, nil, -1)
|
||||
if err != nil {
|
||||
return madmin.DriveSpeedTestResult{}, err
|
||||
}
|
||||
defer http.DrainBody(respBody)
|
||||
waitReader, err := waitForHTTPResponse(respBody)
|
||||
if err != nil {
|
||||
return madmin.DriveSpeedTestResult{}, err
|
||||
}
|
||||
|
||||
var result madmin.DriveSpeedTestResult
|
||||
err = gob.NewDecoder(waitReader).Decode(&result)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
if result.Error != "" {
|
||||
return result, errors.New(result.Error)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (client *peerRESTClient) ReloadSiteReplicationConfig(ctx context.Context) error {
|
||||
respBody, err := client.callWithContext(context.Background(), peerRESTMethodReloadSiteReplicationConfig, nil, nil, -1)
|
||||
if err != nil {
|
||||
@@ -1075,3 +1104,18 @@ func (client *peerRESTClient) ReloadSiteReplicationConfig(ctx context.Context) e
|
||||
defer http.DrainBody(respBody)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *peerRESTClient) GetLastDayTierStats(ctx context.Context) (dailyAllTierStats, error) {
|
||||
var result map[string]lastDayTierStats
|
||||
respBody, err := client.callWithContext(context.Background(), peerRESTMethodGetLastDayTierStats, nil, nil, -1)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
defer http.DrainBody(respBody)
|
||||
|
||||
err = gob.NewDecoder(respBody).Decode(&result)
|
||||
if err != nil {
|
||||
return dailyAllTierStats{}, err
|
||||
}
|
||||
return dailyAllTierStats(result), nil
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package cmd
|
||||
|
||||
const (
|
||||
peerRESTVersion = "v18" // Add LoadPoolMeta
|
||||
peerRESTVersion = "v20" // Add drivespeedtest
|
||||
peerRESTVersionPrefix = SlashSeparator + peerRESTVersion
|
||||
peerRESTPrefix = minioReservedBucketPath + "/peer"
|
||||
peerRESTPath = peerRESTPrefix + peerRESTVersionPrefix
|
||||
@@ -66,8 +66,10 @@ const (
|
||||
peerRESTMethodGetPeerMetrics = "/peermetrics"
|
||||
peerRESTMethodLoadTransitionTierConfig = "/loadtransitiontierconfig"
|
||||
peerRESTMethodSpeedtest = "/speedtest"
|
||||
peerRESTMethodDriveSpeedTest = "/drivespeedtest"
|
||||
peerRESTMethodReloadSiteReplicationConfig = "/reloadsitereplicationconfig"
|
||||
peerRESTMethodReloadPoolMeta = "/reloadpoolmeta"
|
||||
peerRESTMethodGetLastDayTierStats = "/getlastdaytierstats"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+61
-4
@@ -1168,7 +1168,6 @@ func selfSpeedtest(ctx context.Context, size, concurrent int, duration time.Dura
|
||||
var totalBytesWritten uint64
|
||||
var totalBytesRead uint64
|
||||
|
||||
bucket := minioMetaSpeedTestBucket
|
||||
objCountPerThread := make([]uint64, concurrent)
|
||||
uploadsCtx, uploadsCancel := context.WithCancel(context.Background())
|
||||
defer uploadsCancel()
|
||||
@@ -1178,7 +1177,7 @@ func selfSpeedtest(ctx context.Context, size, concurrent int, duration time.Dura
|
||||
uploadsCancel()
|
||||
}()
|
||||
|
||||
objNamePrefix := minioMetaSpeedTestBucketPrefix + uuid.New().String()
|
||||
objNamePrefix := "speedtest/objects/" + uuid.New().String()
|
||||
|
||||
wg.Add(concurrent)
|
||||
for i := 0; i < concurrent; i++ {
|
||||
@@ -1198,7 +1197,7 @@ func selfSpeedtest(ctx context.Context, size, concurrent int, duration time.Dura
|
||||
return
|
||||
}
|
||||
reader := NewPutObjReader(hashReader)
|
||||
objInfo, err := objAPI.PutObject(uploadsCtx, bucket, fmt.Sprintf("%s.%d.%d",
|
||||
objInfo, err := objAPI.PutObject(uploadsCtx, minioMetaBucket, fmt.Sprintf("%s.%d.%d",
|
||||
objNamePrefix, i, objCountPerThread[i]), reader, ObjectOptions{
|
||||
UserDefined: map[string]string{
|
||||
xhttp.AmzStorageClass: storageClass,
|
||||
@@ -1245,7 +1244,7 @@ func selfSpeedtest(ctx context.Context, size, concurrent int, duration time.Dura
|
||||
if objCountPerThread[i] == j {
|
||||
j = 0
|
||||
}
|
||||
r, err := objAPI.GetObjectNInfo(downloadsCtx, bucket, fmt.Sprintf("%s.%d.%d",
|
||||
r, err := objAPI.GetObjectNInfo(downloadsCtx, minioMetaBucket, fmt.Sprintf("%s.%d.%d",
|
||||
objNamePrefix, i, j), nil, nil, noLock, ObjectOptions{})
|
||||
if err != nil {
|
||||
if !contextCanceled(downloadsCtx) {
|
||||
@@ -1327,6 +1326,62 @@ func (s *peerRESTServer) SpeedtestHandler(w http.ResponseWriter, r *http.Request
|
||||
logger.LogIf(r.Context(), gob.NewEncoder(w).Encode(result))
|
||||
}
|
||||
|
||||
// GetLastDayTierStatsHandler - returns per-tier stats in the last 24hrs for this server
|
||||
func (s *peerRESTServer) GetLastDayTierStatsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.IsValid(w, r) {
|
||||
s.writeErrorResponse(w, errors.New("invalid request"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx := newContext(r, w, "GetLastDayTierStats")
|
||||
if objAPI := newObjectLayerFn(); objAPI == nil || globalTransitionState == nil {
|
||||
s.writeErrorResponse(w, errServerNotInitialized)
|
||||
return
|
||||
}
|
||||
|
||||
result := globalTransitionState.getDailyAllTierStats()
|
||||
logger.LogIf(ctx, gob.NewEncoder(w).Encode(result))
|
||||
}
|
||||
|
||||
func (s *peerRESTServer) DriveSpeedTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.IsValid(w, r) {
|
||||
s.writeErrorResponse(w, errors.New("invalid request"))
|
||||
return
|
||||
}
|
||||
|
||||
objAPI := newObjectLayerFn()
|
||||
if objAPI == nil {
|
||||
s.writeErrorResponse(w, errServerNotInitialized)
|
||||
return
|
||||
}
|
||||
|
||||
serial := r.Form.Get("serial") == "true"
|
||||
blockSizeStr := r.Form.Get("blocksize")
|
||||
fileSizeStr := r.Form.Get("filesize")
|
||||
|
||||
blockSize, err := strconv.ParseUint(blockSizeStr, 10, 64)
|
||||
if err != nil {
|
||||
blockSize = 4 * humanize.MiByte // default value
|
||||
}
|
||||
|
||||
fileSize, err := strconv.ParseUint(fileSizeStr, 10, 64)
|
||||
if err != nil {
|
||||
fileSize = 1 * humanize.GiByte // default value
|
||||
}
|
||||
|
||||
opts := madmin.DriveSpeedTestOpts{
|
||||
Serial: serial,
|
||||
BlockSize: blockSize,
|
||||
FileSize: fileSize,
|
||||
}
|
||||
|
||||
done := keepHTTPResponseAlive(w)
|
||||
result := driveSpeedTest(r.Context(), opts)
|
||||
done(nil)
|
||||
|
||||
logger.LogIf(r.Context(), gob.NewEncoder(w).Encode(result))
|
||||
}
|
||||
|
||||
// registerPeerRESTHandlers - register peer rest router.
|
||||
func registerPeerRESTHandlers(router *mux.Router) {
|
||||
server := &peerRESTServer{}
|
||||
@@ -1373,6 +1428,8 @@ func registerPeerRESTHandlers(router *mux.Router) {
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodGetPeerMetrics).HandlerFunc(httpTraceHdrs(server.GetPeerMetrics))
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadTransitionTierConfig).HandlerFunc(httpTraceHdrs(server.LoadTransitionTierConfigHandler))
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodSpeedtest).HandlerFunc(httpTraceHdrs(server.SpeedtestHandler))
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDriveSpeedTest).HandlerFunc(httpTraceHdrs(server.DriveSpeedTestHandler))
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodReloadSiteReplicationConfig).HandlerFunc(httpTraceHdrs(server.ReloadSiteReplicationConfigHandler))
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodReloadPoolMeta).HandlerFunc(httpTraceHdrs(server.ReloadPoolMetaHandler))
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodGetLastDayTierStats).HandlerFunc(httpTraceHdrs(server.GetLastDayTierStatsHandler))
|
||||
}
|
||||
|
||||
+10
-5
@@ -185,7 +185,7 @@ func connectLoadInitFormats(retryCount int, firstDisk bool, endpoints Endpoints,
|
||||
|
||||
for i, err := range errs {
|
||||
if err != nil {
|
||||
if err == errDiskNotFound && retryCount >= 5 {
|
||||
if err == errDiskNotFound && retryCount >= 10 {
|
||||
logger.Error("Unable to connect to %s: %v", endpoints[i], isServerResolvable(endpoints[i], time.Second))
|
||||
} else {
|
||||
logger.Error("Unable to use the drive %s: %v", endpoints[i], err)
|
||||
@@ -202,7 +202,7 @@ func connectLoadInitFormats(retryCount int, firstDisk bool, endpoints Endpoints,
|
||||
// Check if we have
|
||||
for i, sErr := range sErrs {
|
||||
// print the error, nonetheless, which is perhaps unhandled
|
||||
if sErr != errUnformattedDisk && sErr != errDiskNotFound && retryCount >= 5 {
|
||||
if sErr != errUnformattedDisk && sErr != errDiskNotFound && retryCount >= 10 {
|
||||
if sErr != nil {
|
||||
logger.Error("Unable to read 'format.json' from %s: %v\n", endpoints[i], sErr)
|
||||
}
|
||||
@@ -315,23 +315,28 @@ func waitForFormatErasure(firstDisk bool, endpoints Endpoints, poolCount, setCou
|
||||
tries++ // tried already once
|
||||
|
||||
// Wait on each try for an update.
|
||||
ticker := time.NewTicker(250 * time.Millisecond)
|
||||
ticker := time.NewTicker(150 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if tries == 10 {
|
||||
// Reset the tries count such that we log only for every 10 retries.
|
||||
tries = 1
|
||||
}
|
||||
|
||||
storageDisks, format, err := connectLoadInitFormats(tries, firstDisk, endpoints, poolCount, setCount, setDriveCount, deploymentID, distributionAlgo)
|
||||
if err != nil {
|
||||
tries++
|
||||
switch err {
|
||||
case errNotFirstDisk:
|
||||
// Fresh setup, wait for first server to be up.
|
||||
logger.Info("Waiting for the first server to format the disks.")
|
||||
logger.Info("Waiting for the first server to format the disks (elapsed %s)\n", getElapsedTime())
|
||||
continue
|
||||
case errFirstDiskWait:
|
||||
// Fresh setup, wait for other servers to come up.
|
||||
logger.Info("Waiting for all other servers to be online to format the disks.")
|
||||
logger.Info("Waiting for all other servers to be online to format the disks (elapses %s)\n", getElapsedTime())
|
||||
continue
|
||||
case errErasureReadQuorum:
|
||||
// no quorum available continue to wait for minimum number of servers.
|
||||
|
||||
+17
-13
@@ -531,10 +531,24 @@ func serverMain(ctx *cli.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if globalBrowserEnabled {
|
||||
srv, err := initConsoleServer()
|
||||
if err != nil {
|
||||
logger.FatalIf(err, "Unable to initialize console service")
|
||||
}
|
||||
|
||||
setConsoleSrv(srv)
|
||||
|
||||
go func() {
|
||||
logger.FatalIf(newConsoleServerFn().Serve(), "Unable to initialize console server")
|
||||
}()
|
||||
}
|
||||
|
||||
newObject, err := newObjectLayer(GlobalContext, globalEndpoints)
|
||||
if err != nil {
|
||||
logFatalErrs(err, Endpoint{}, true)
|
||||
}
|
||||
|
||||
logger.SetDeploymentID(globalDeploymentID)
|
||||
|
||||
// Enable background operations for erasure coding
|
||||
@@ -577,6 +591,9 @@ func serverMain(ctx *cli.Context) {
|
||||
// Initialize site replication manager.
|
||||
globalSiteReplicationSys.Init(GlobalContext, newObject)
|
||||
|
||||
// Initialize quota manager.
|
||||
globalBucketQuotaSys.Init(newObject)
|
||||
|
||||
// Initialize users credentials and policies in background right after config has initialized.
|
||||
go globalIAMSys.Init(GlobalContext, newObject, globalEtcdClient, globalNotificationSys, globalRefreshIAMInterval)
|
||||
|
||||
@@ -621,19 +638,6 @@ func serverMain(ctx *cli.Context) {
|
||||
logStartupMessage(color.RedBold("WARNING: Strict AWS S3 compatible incoming PUT, POST content payload validation is turned off, caution is advised do not use in production"))
|
||||
}
|
||||
|
||||
if globalBrowserEnabled {
|
||||
srv, err := initConsoleServer()
|
||||
if err != nil {
|
||||
logger.FatalIf(err, "Unable to initialize console service")
|
||||
}
|
||||
|
||||
setConsoleSrv(srv)
|
||||
|
||||
go func() {
|
||||
logger.FatalIf(newConsoleServerFn().Serve(), "Unable to initialize console server")
|
||||
}()
|
||||
}
|
||||
|
||||
if serverDebugLog {
|
||||
logger.Info("== DEBUG Mode enabled ==")
|
||||
logger.Info("Currently set environment settings:")
|
||||
|
||||
+783
-304
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,217 @@
|
||||
// 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"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/minio/dperf/pkg/dperf"
|
||||
"github.com/minio/madmin-go"
|
||||
)
|
||||
|
||||
var dperfUUID = mustGetUUID()
|
||||
|
||||
type speedTestOpts struct {
|
||||
throughputSize int
|
||||
concurrencyStart int
|
||||
duration time.Duration
|
||||
autotune bool
|
||||
storageClass string
|
||||
}
|
||||
|
||||
// Get the max throughput and iops numbers.
|
||||
func objectSpeedTest(ctx context.Context, opts speedTestOpts) chan madmin.SpeedTestResult {
|
||||
ch := make(chan madmin.SpeedTestResult, 1)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
|
||||
concurrency := opts.concurrencyStart
|
||||
|
||||
throughputHighestGet := uint64(0)
|
||||
throughputHighestPut := uint64(0)
|
||||
var throughputHighestResults []SpeedtestResult
|
||||
|
||||
sendResult := func() {
|
||||
var result madmin.SpeedTestResult
|
||||
|
||||
durationSecs := opts.duration.Seconds()
|
||||
|
||||
result.GETStats.ThroughputPerSec = throughputHighestGet / uint64(durationSecs)
|
||||
result.GETStats.ObjectsPerSec = throughputHighestGet / uint64(opts.throughputSize) / uint64(durationSecs)
|
||||
result.PUTStats.ThroughputPerSec = throughputHighestPut / uint64(durationSecs)
|
||||
result.PUTStats.ObjectsPerSec = throughputHighestPut / uint64(opts.throughputSize) / uint64(durationSecs)
|
||||
for i := 0; i < len(throughputHighestResults); i++ {
|
||||
errStr := ""
|
||||
if throughputHighestResults[i].Error != "" {
|
||||
errStr = throughputHighestResults[i].Error
|
||||
}
|
||||
result.PUTStats.Servers = append(result.PUTStats.Servers, madmin.SpeedTestStatServer{
|
||||
Endpoint: throughputHighestResults[i].Endpoint,
|
||||
ThroughputPerSec: throughputHighestResults[i].Uploads / uint64(durationSecs),
|
||||
ObjectsPerSec: throughputHighestResults[i].Uploads / uint64(opts.throughputSize) / uint64(durationSecs),
|
||||
Err: errStr,
|
||||
})
|
||||
result.GETStats.Servers = append(result.GETStats.Servers, madmin.SpeedTestStatServer{
|
||||
Endpoint: throughputHighestResults[i].Endpoint,
|
||||
ThroughputPerSec: throughputHighestResults[i].Downloads / uint64(durationSecs),
|
||||
ObjectsPerSec: throughputHighestResults[i].Downloads / uint64(opts.throughputSize) / uint64(durationSecs),
|
||||
Err: errStr,
|
||||
})
|
||||
}
|
||||
|
||||
result.Size = opts.throughputSize
|
||||
result.Disks = globalEndpoints.NEndpoints()
|
||||
result.Servers = len(globalNotificationSys.peerClients) + 1
|
||||
result.Version = Version
|
||||
result.Concurrent = concurrency
|
||||
|
||||
ch <- result
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// If the client got disconnected stop the speedtest.
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
results := globalNotificationSys.Speedtest(ctx,
|
||||
opts.throughputSize, concurrency,
|
||||
opts.duration, opts.storageClass)
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return results[i].Endpoint < results[j].Endpoint
|
||||
})
|
||||
|
||||
totalPut := uint64(0)
|
||||
totalGet := uint64(0)
|
||||
for _, result := range results {
|
||||
totalPut += result.Uploads
|
||||
totalGet += result.Downloads
|
||||
}
|
||||
|
||||
if totalGet < throughputHighestGet {
|
||||
// Following check is for situations
|
||||
// when Writes() scale higher than Reads()
|
||||
// - practically speaking this never happens
|
||||
// and should never happen - however it has
|
||||
// been seen recently due to hardware issues
|
||||
// causes Reads() to go slower than Writes().
|
||||
//
|
||||
// Send such results anyways as this shall
|
||||
// expose a problem underneath.
|
||||
if totalPut > throughputHighestPut {
|
||||
throughputHighestResults = results
|
||||
throughputHighestPut = totalPut
|
||||
// let the client see lower value as well
|
||||
throughputHighestGet = totalGet
|
||||
}
|
||||
sendResult()
|
||||
break
|
||||
}
|
||||
|
||||
doBreak := float64(totalGet-throughputHighestGet)/float64(totalGet) < 0.025
|
||||
|
||||
throughputHighestGet = totalGet
|
||||
throughputHighestResults = results
|
||||
throughputHighestPut = totalPut
|
||||
|
||||
if doBreak {
|
||||
sendResult()
|
||||
break
|
||||
}
|
||||
|
||||
for _, result := range results {
|
||||
if result.Error != "" {
|
||||
// Break out on errors.
|
||||
sendResult()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sendResult()
|
||||
if !opts.autotune {
|
||||
break
|
||||
}
|
||||
|
||||
// Try with a higher concurrency to see if we get better throughput
|
||||
concurrency += (concurrency + 1) / 2
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// speedTest - Deprecated. See objectSpeedTest
|
||||
func speedTest(ctx context.Context, opts speedTestOpts) chan madmin.SpeedTestResult {
|
||||
return objectSpeedTest(ctx, opts)
|
||||
}
|
||||
|
||||
func driveSpeedTest(ctx context.Context, opts madmin.DriveSpeedTestOpts) madmin.DriveSpeedTestResult {
|
||||
perf := &dperf.DrivePerf{
|
||||
Serial: opts.Serial,
|
||||
BlockSize: opts.BlockSize,
|
||||
FileSize: opts.FileSize,
|
||||
}
|
||||
paths := func() []string {
|
||||
toRet := []string{}
|
||||
paths := globalEndpoints.LocalDisksPaths()
|
||||
for _, p := range paths {
|
||||
dperfFilename := filepath.Join(p, ".minio.sys", "tmp", dperfUUID)
|
||||
if _, err := os.Stat(dperfFilename); err == nil {
|
||||
// file exists, so remove it
|
||||
os.Remove(dperfFilename)
|
||||
}
|
||||
toRet = append(toRet, dperfFilename)
|
||||
}
|
||||
return toRet
|
||||
}()
|
||||
|
||||
perfs, err := perf.Run(ctx, paths...)
|
||||
return madmin.DriveSpeedTestResult{
|
||||
Endpoint: globalLocalNodeName,
|
||||
Version: Version,
|
||||
DrivePerf: func() []madmin.DrivePerf {
|
||||
results := []madmin.DrivePerf{}
|
||||
for _, r := range perfs {
|
||||
result := madmin.DrivePerf{
|
||||
Path: r.Path,
|
||||
ReadThroughput: r.ReadThroughput,
|
||||
WriteThroughput: r.WriteThroughput,
|
||||
Error: func() string {
|
||||
if r.Error != nil {
|
||||
return r.Error.Error()
|
||||
}
|
||||
return ""
|
||||
}(),
|
||||
}
|
||||
results = append(results, result)
|
||||
}
|
||||
return results
|
||||
}(),
|
||||
Error: func() string {
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
}(),
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ type DiskInfo struct {
|
||||
// the number of calls of each API and the moving average of
|
||||
// the duration of each API.
|
||||
type DiskMetrics struct {
|
||||
APILatencies map[string]string `json:"apiLatencies,omitempty"`
|
||||
APILatencies map[string]uint64 `json:"apiLatencies,omitempty"`
|
||||
APICalls map[string]uint64 `json:"apiCalls,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -299,7 +299,7 @@ func (z *DiskMetrics) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
return
|
||||
}
|
||||
if z.APILatencies == nil {
|
||||
z.APILatencies = make(map[string]string, zb0002)
|
||||
z.APILatencies = make(map[string]uint64, zb0002)
|
||||
} else if len(z.APILatencies) > 0 {
|
||||
for key := range z.APILatencies {
|
||||
delete(z.APILatencies, key)
|
||||
@@ -308,13 +308,13 @@ func (z *DiskMetrics) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
var za0001 string
|
||||
var za0002 string
|
||||
var za0002 uint64
|
||||
za0001, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "APILatencies")
|
||||
return
|
||||
}
|
||||
za0002, err = dc.ReadString()
|
||||
za0002, err = dc.ReadUint64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "APILatencies", za0001)
|
||||
return
|
||||
@@ -381,7 +381,7 @@ func (z *DiskMetrics) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = msgp.WrapError(err, "APILatencies")
|
||||
return
|
||||
}
|
||||
err = en.WriteString(za0002)
|
||||
err = en.WriteUint64(za0002)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "APILatencies", za0001)
|
||||
return
|
||||
@@ -421,7 +421,7 @@ func (z *DiskMetrics) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.AppendMapHeader(o, uint32(len(z.APILatencies)))
|
||||
for za0001, za0002 := range z.APILatencies {
|
||||
o = msgp.AppendString(o, za0001)
|
||||
o = msgp.AppendString(o, za0002)
|
||||
o = msgp.AppendUint64(o, za0002)
|
||||
}
|
||||
// string "APICalls"
|
||||
o = append(o, 0xa8, 0x41, 0x50, 0x49, 0x43, 0x61, 0x6c, 0x6c, 0x73)
|
||||
@@ -459,7 +459,7 @@ func (z *DiskMetrics) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
return
|
||||
}
|
||||
if z.APILatencies == nil {
|
||||
z.APILatencies = make(map[string]string, zb0002)
|
||||
z.APILatencies = make(map[string]uint64, zb0002)
|
||||
} else if len(z.APILatencies) > 0 {
|
||||
for key := range z.APILatencies {
|
||||
delete(z.APILatencies, key)
|
||||
@@ -467,14 +467,14 @@ func (z *DiskMetrics) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
}
|
||||
for zb0002 > 0 {
|
||||
var za0001 string
|
||||
var za0002 string
|
||||
var za0002 uint64
|
||||
zb0002--
|
||||
za0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "APILatencies")
|
||||
return
|
||||
}
|
||||
za0002, bts, err = msgp.ReadStringBytes(bts)
|
||||
za0002, bts, err = msgp.ReadUint64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "APILatencies", za0001)
|
||||
return
|
||||
@@ -529,7 +529,7 @@ func (z *DiskMetrics) Msgsize() (s int) {
|
||||
if z.APILatencies != nil {
|
||||
for za0001, za0002 := range z.APILatencies {
|
||||
_ = za0002
|
||||
s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002)
|
||||
s += msgp.StringPrefixSize + len(za0001) + msgp.Uint64Size
|
||||
}
|
||||
}
|
||||
s += 9 + msgp.MapHeaderSize
|
||||
|
||||
+25
-19
@@ -132,24 +132,23 @@ func registerSTSRouter(router *mux.Router) {
|
||||
}
|
||||
|
||||
func checkAssumeRoleAuth(ctx context.Context, r *http.Request) (user auth.Credentials, isErrCodeSTS bool, stsErr STSErrorCode) {
|
||||
switch getRequestAuthType(r) {
|
||||
default:
|
||||
if !isRequestSignatureV4(r) {
|
||||
return user, true, ErrSTSAccessDenied
|
||||
case authTypeSigned:
|
||||
s3Err := isReqAuthenticated(ctx, r, globalSite.Region, serviceSTS)
|
||||
if s3Err != ErrNone {
|
||||
return user, false, STSErrorCode(s3Err)
|
||||
}
|
||||
}
|
||||
|
||||
user, _, s3Err = getReqAccessKeyV4(r, globalSite.Region, serviceSTS)
|
||||
if s3Err != ErrNone {
|
||||
return user, false, STSErrorCode(s3Err)
|
||||
}
|
||||
s3Err := isReqAuthenticated(ctx, r, globalSite.Region, serviceSTS)
|
||||
if s3Err != ErrNone {
|
||||
return user, false, STSErrorCode(s3Err)
|
||||
}
|
||||
|
||||
// Temporary credentials or Service accounts cannot generate further temporary credentials.
|
||||
if user.IsTemp() || user.IsServiceAccount() {
|
||||
return user, true, ErrSTSAccessDenied
|
||||
}
|
||||
user, _, s3Err = getReqAccessKeyV4(r, globalSite.Region, serviceSTS)
|
||||
if s3Err != ErrNone {
|
||||
return user, false, STSErrorCode(s3Err)
|
||||
}
|
||||
|
||||
// Temporary credentials or Service accounts cannot generate further temporary credentials.
|
||||
if user.IsTemp() || user.IsServiceAccount() {
|
||||
return user, true, ErrSTSAccessDenied
|
||||
}
|
||||
|
||||
// Session tokens are not allowed in STS AssumeRole requests.
|
||||
@@ -178,11 +177,11 @@ func parseForm(r *http.Request) error {
|
||||
func (sts *stsAPIHandlers) AssumeRole(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "AssumeRole")
|
||||
|
||||
// Check auth here (otherwise r.Form will have unexpected values from
|
||||
// the call to `parseForm` below), but return failure only after we are
|
||||
// able to validate that it is a valid STS request, so that we are able
|
||||
// to send an appropriate audit log.
|
||||
user, isErrCodeSTS, stsErr := checkAssumeRoleAuth(ctx, r)
|
||||
if stsErr != ErrSTSNone {
|
||||
writeSTSErrorResponse(ctx, w, isErrCodeSTS, stsErr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err := parseForm(r); err != nil {
|
||||
writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, err)
|
||||
@@ -205,6 +204,13 @@ func (sts *stsAPIHandlers) AssumeRole(w http.ResponseWriter, r *http.Request) {
|
||||
ctx = newContext(r, w, action)
|
||||
defer logger.AuditLog(ctx, w, r, nil)
|
||||
|
||||
// Validate the authentication result here so that failures will be
|
||||
// audit-logged.
|
||||
if stsErr != ErrSTSNone {
|
||||
writeSTSErrorResponse(ctx, w, isErrCodeSTS, stsErr, nil)
|
||||
return
|
||||
}
|
||||
|
||||
sessionPolicyStr := r.Form.Get(stsPolicy)
|
||||
// https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
|
||||
// The plain text that you use for both inline and managed session
|
||||
|
||||
@@ -227,7 +227,11 @@ func (api adminAPIHandlers) TierStatsHandler(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.Marshal(dui.tierStats())
|
||||
tierStats := dui.tierStats()
|
||||
dailyStats := globalNotificationSys.GetLastDayTierStats(ctx)
|
||||
tierStats = dailyStats.addToTierInfo(tierStats)
|
||||
|
||||
data, err := json.Marshal(tierStats)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2022 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 (
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go"
|
||||
)
|
||||
|
||||
type lastDayTierStats struct {
|
||||
Bins [24]tierStats
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (l *lastDayTierStats) addStats(ts tierStats) {
|
||||
now := time.Now()
|
||||
l.forwardTo(now)
|
||||
|
||||
nowIdx := now.Hour()
|
||||
l.Bins[nowIdx] = l.Bins[nowIdx].add(ts)
|
||||
}
|
||||
|
||||
// forwardTo moves time to t, clearing entries between last update and t.
|
||||
func (l *lastDayTierStats) forwardTo(t time.Time) {
|
||||
since := t.Sub(l.UpdatedAt).Hours()
|
||||
// within the hour since l.UpdatedAt
|
||||
if since < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
idx, lastIdx := t.Hour(), l.UpdatedAt.Hour()
|
||||
l.UpdatedAt = t
|
||||
|
||||
if since >= 24 {
|
||||
l.Bins = [24]tierStats{}
|
||||
return
|
||||
}
|
||||
|
||||
for ; lastIdx != idx; lastIdx++ {
|
||||
l.Bins[(lastIdx+1)%24] = tierStats{}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lastDayTierStats) clone() lastDayTierStats {
|
||||
clone := lastDayTierStats{
|
||||
UpdatedAt: l.UpdatedAt,
|
||||
}
|
||||
copy(clone.Bins[:], l.Bins[:])
|
||||
return clone
|
||||
}
|
||||
|
||||
func (l lastDayTierStats) merge(m lastDayTierStats) (merged lastDayTierStats) {
|
||||
cl := l.clone()
|
||||
cm := m.clone()
|
||||
|
||||
if cl.UpdatedAt.After(cm.UpdatedAt) {
|
||||
cm.forwardTo(cl.UpdatedAt)
|
||||
merged.UpdatedAt = cl.UpdatedAt
|
||||
} else {
|
||||
cl.forwardTo(cm.UpdatedAt)
|
||||
merged.UpdatedAt = cm.UpdatedAt
|
||||
}
|
||||
|
||||
for i := range cl.Bins {
|
||||
merged.Bins[i] = cl.Bins[i].add(cm.Bins[i])
|
||||
}
|
||||
|
||||
return merged
|
||||
}
|
||||
|
||||
// dailyAllTierStats is used to aggregate last day tier stats across MinIO servers
|
||||
type dailyAllTierStats map[string]lastDayTierStats
|
||||
|
||||
func (l dailyAllTierStats) merge(m dailyAllTierStats) {
|
||||
for tier, st := range m {
|
||||
l[tier] = l[tier].merge(st)
|
||||
}
|
||||
}
|
||||
|
||||
func (l dailyAllTierStats) addToTierInfo(tierInfos []madmin.TierInfo) []madmin.TierInfo {
|
||||
for i := range tierInfos {
|
||||
var lst lastDayTierStats
|
||||
var ok bool
|
||||
if lst, ok = l[tierInfos[i].Name]; !ok {
|
||||
continue
|
||||
}
|
||||
for hr, st := range lst.Bins {
|
||||
tierInfos[i].DailyStats.Bins[hr] = madmin.TierStats{
|
||||
TotalSize: st.TotalSize,
|
||||
NumVersions: st.NumVersions,
|
||||
NumObjects: st.NumObjects,
|
||||
}
|
||||
}
|
||||
tierInfos[i].DailyStats.UpdatedAt = lst.UpdatedAt
|
||||
}
|
||||
return tierInfos
|
||||
}
|
||||
+3
-3
@@ -124,7 +124,7 @@ func GetCurrentReleaseTime() (releaseTime time.Time, err error) {
|
||||
// "/.dockerenv": "file",
|
||||
//
|
||||
func IsDocker() bool {
|
||||
if env.Get("MINIO_CI_CD", "") == "" {
|
||||
if !globalIsCICD {
|
||||
_, err := os.Stat("/.dockerenv")
|
||||
if osIsNotExist(err) {
|
||||
return false
|
||||
@@ -140,7 +140,7 @@ func IsDocker() bool {
|
||||
|
||||
// IsDCOS returns true if minio is running in DCOS.
|
||||
func IsDCOS() bool {
|
||||
if env.Get("MINIO_CI_CD", "") == "" {
|
||||
if !globalIsCICD {
|
||||
// http://mesos.apache.org/documentation/latest/docker-containerizer/
|
||||
// Mesos docker containerizer sets this value
|
||||
return env.Get("MESOS_CONTAINER_NAME", "") != ""
|
||||
@@ -150,7 +150,7 @@ func IsDCOS() bool {
|
||||
|
||||
// IsKubernetes returns true if minio is running in kubernetes.
|
||||
func IsKubernetes() bool {
|
||||
if env.Get("MINIO_CI_CD", "") == "" {
|
||||
if !globalIsCICD {
|
||||
// Kubernetes env used to validate if we are
|
||||
// indeed running inside a kubernetes pod
|
||||
// is KUBERNETES_SERVICE_HOST
|
||||
|
||||
-135
@@ -37,7 +37,6 @@ import (
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"runtime/trace"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -113,10 +112,6 @@ func path2BucketObject(s string) (bucket, prefix string) {
|
||||
return path2BucketObjectWithBasePath("", s)
|
||||
}
|
||||
|
||||
func getReadQuorum(drive int) int {
|
||||
return drive - getDefaultParityBlocks(drive)
|
||||
}
|
||||
|
||||
func getWriteQuorum(drive int) int {
|
||||
parity := getDefaultParityBlocks(drive)
|
||||
quorum := drive - parity
|
||||
@@ -1001,136 +996,6 @@ func auditLogInternal(ctx context.Context, bucket, object string, opts AuditLogO
|
||||
logger.AuditLog(ctx, nil, nil, nil)
|
||||
}
|
||||
|
||||
type speedTestOpts struct {
|
||||
throughputSize int
|
||||
concurrencyStart int
|
||||
duration time.Duration
|
||||
autotune bool
|
||||
storageClass string
|
||||
}
|
||||
|
||||
// Get the max throughput and iops numbers.
|
||||
func speedTest(ctx context.Context, opts speedTestOpts) chan madmin.SpeedTestResult {
|
||||
ch := make(chan madmin.SpeedTestResult, 1)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
|
||||
concurrency := opts.concurrencyStart
|
||||
|
||||
throughputHighestGet := uint64(0)
|
||||
throughputHighestPut := uint64(0)
|
||||
var throughputHighestResults []SpeedtestResult
|
||||
|
||||
sendResult := func() {
|
||||
var result madmin.SpeedTestResult
|
||||
|
||||
durationSecs := opts.duration.Seconds()
|
||||
|
||||
result.GETStats.ThroughputPerSec = throughputHighestGet / uint64(durationSecs)
|
||||
result.GETStats.ObjectsPerSec = throughputHighestGet / uint64(opts.throughputSize) / uint64(durationSecs)
|
||||
result.PUTStats.ThroughputPerSec = throughputHighestPut / uint64(durationSecs)
|
||||
result.PUTStats.ObjectsPerSec = throughputHighestPut / uint64(opts.throughputSize) / uint64(durationSecs)
|
||||
for i := 0; i < len(throughputHighestResults); i++ {
|
||||
errStr := ""
|
||||
if throughputHighestResults[i].Error != "" {
|
||||
errStr = throughputHighestResults[i].Error
|
||||
}
|
||||
result.PUTStats.Servers = append(result.PUTStats.Servers, madmin.SpeedTestStatServer{
|
||||
Endpoint: throughputHighestResults[i].Endpoint,
|
||||
ThroughputPerSec: throughputHighestResults[i].Uploads / uint64(durationSecs),
|
||||
ObjectsPerSec: throughputHighestResults[i].Uploads / uint64(opts.throughputSize) / uint64(durationSecs),
|
||||
Err: errStr,
|
||||
})
|
||||
result.GETStats.Servers = append(result.GETStats.Servers, madmin.SpeedTestStatServer{
|
||||
Endpoint: throughputHighestResults[i].Endpoint,
|
||||
ThroughputPerSec: throughputHighestResults[i].Downloads / uint64(durationSecs),
|
||||
ObjectsPerSec: throughputHighestResults[i].Downloads / uint64(opts.throughputSize) / uint64(durationSecs),
|
||||
Err: errStr,
|
||||
})
|
||||
}
|
||||
|
||||
result.Size = opts.throughputSize
|
||||
result.Disks = globalEndpoints.NEndpoints()
|
||||
result.Servers = len(globalNotificationSys.peerClients) + 1
|
||||
result.Version = Version
|
||||
result.Concurrent = concurrency
|
||||
|
||||
ch <- result
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// If the client got disconnected stop the speedtest.
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
results := globalNotificationSys.Speedtest(ctx,
|
||||
opts.throughputSize, concurrency,
|
||||
opts.duration, opts.storageClass)
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return results[i].Endpoint < results[j].Endpoint
|
||||
})
|
||||
|
||||
totalPut := uint64(0)
|
||||
totalGet := uint64(0)
|
||||
for _, result := range results {
|
||||
totalPut += result.Uploads
|
||||
totalGet += result.Downloads
|
||||
}
|
||||
|
||||
if totalGet < throughputHighestGet {
|
||||
// Following check is for situations
|
||||
// when Writes() scale higher than Reads()
|
||||
// - practically speaking this never happens
|
||||
// and should never happen - however it has
|
||||
// been seen recently due to hardware issues
|
||||
// causes Reads() to go slower than Writes().
|
||||
//
|
||||
// Send such results anyways as this shall
|
||||
// expose a problem underneath.
|
||||
if totalPut > throughputHighestPut {
|
||||
throughputHighestResults = results
|
||||
throughputHighestPut = totalPut
|
||||
// let the client see lower value as well
|
||||
throughputHighestGet = totalGet
|
||||
}
|
||||
sendResult()
|
||||
break
|
||||
}
|
||||
|
||||
doBreak := float64(totalGet-throughputHighestGet)/float64(totalGet) < 0.025
|
||||
|
||||
throughputHighestGet = totalGet
|
||||
throughputHighestResults = results
|
||||
throughputHighestPut = totalPut
|
||||
|
||||
if doBreak {
|
||||
sendResult()
|
||||
break
|
||||
}
|
||||
|
||||
for _, result := range results {
|
||||
if result.Error != "" {
|
||||
// Break out on errors.
|
||||
sendResult()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sendResult()
|
||||
if !opts.autotune {
|
||||
break
|
||||
}
|
||||
|
||||
// Try with a higher concurrency to see if we get better throughput
|
||||
concurrency += (concurrency + 1) / 2
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func newTLSConfig(getCert certs.GetCertificateFunc) *tls.Config {
|
||||
if getCert == nil {
|
||||
return nil
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/VividCortex/ewma"
|
||||
"github.com/minio/madmin-go"
|
||||
)
|
||||
|
||||
@@ -71,18 +70,18 @@ type xlStorageDiskIDCheck struct {
|
||||
// please use `fieldalignment ./...` to check
|
||||
// if your changes are not causing any problems.
|
||||
storage StorageAPI
|
||||
apiLatencies [storageMetricLast]ewma.MovingAverage
|
||||
apiLatencies [storageMetricLast]*lockedLastMinuteLatency
|
||||
diskID string
|
||||
apiCalls [storageMetricLast]uint64
|
||||
}
|
||||
|
||||
func (p *xlStorageDiskIDCheck) getMetrics() DiskMetrics {
|
||||
diskMetric := DiskMetrics{
|
||||
APILatencies: make(map[string]string),
|
||||
APILatencies: make(map[string]uint64),
|
||||
APICalls: make(map[string]uint64),
|
||||
}
|
||||
for i, v := range p.apiLatencies {
|
||||
diskMetric.APILatencies[storageMetric(i).String()] = time.Duration(v.Value()).String()
|
||||
diskMetric.APILatencies[storageMetric(i).String()] = v.value()
|
||||
}
|
||||
for i := range p.apiCalls {
|
||||
diskMetric.APICalls[storageMetric(i).String()] = atomic.LoadUint64(&p.apiCalls[i])
|
||||
@@ -90,28 +89,21 @@ func (p *xlStorageDiskIDCheck) getMetrics() DiskMetrics {
|
||||
return diskMetric
|
||||
}
|
||||
|
||||
type lockedSimpleEWMA struct {
|
||||
sync.RWMutex
|
||||
*ewma.SimpleEWMA
|
||||
type lockedLastMinuteLatency struct {
|
||||
sync.Mutex
|
||||
lastMinuteLatency
|
||||
}
|
||||
|
||||
func (e *lockedSimpleEWMA) Add(value float64) {
|
||||
func (e *lockedLastMinuteLatency) add(value time.Duration) {
|
||||
e.Lock()
|
||||
defer e.Unlock()
|
||||
e.SimpleEWMA.Add(value)
|
||||
e.lastMinuteLatency.add(value)
|
||||
}
|
||||
|
||||
func (e *lockedSimpleEWMA) Set(value float64) {
|
||||
func (e *lockedLastMinuteLatency) value() uint64 {
|
||||
e.Lock()
|
||||
defer e.Unlock()
|
||||
|
||||
e.SimpleEWMA.Set(value)
|
||||
}
|
||||
|
||||
func (e *lockedSimpleEWMA) Value() float64 {
|
||||
e.RLock()
|
||||
defer e.RUnlock()
|
||||
return e.SimpleEWMA.Value()
|
||||
return e.lastMinuteLatency.getAvgData().avg()
|
||||
}
|
||||
|
||||
func newXLStorageDiskIDCheck(storage *xlStorage) *xlStorageDiskIDCheck {
|
||||
@@ -119,9 +111,7 @@ func newXLStorageDiskIDCheck(storage *xlStorage) *xlStorageDiskIDCheck {
|
||||
storage: storage,
|
||||
}
|
||||
for i := range xl.apiLatencies[:] {
|
||||
xl.apiLatencies[i] = &lockedSimpleEWMA{
|
||||
SimpleEWMA: new(ewma.SimpleEWMA),
|
||||
}
|
||||
xl.apiLatencies[i] = &lockedLastMinuteLatency{}
|
||||
}
|
||||
return &xl
|
||||
}
|
||||
@@ -582,7 +572,7 @@ func (p *xlStorageDiskIDCheck) updateStorageMetrics(s storageMetric, paths ...st
|
||||
duration := time.Since(startTime)
|
||||
|
||||
atomic.AddUint64(&p.apiCalls[s], 1)
|
||||
p.apiLatencies[s].Add(float64(duration))
|
||||
p.apiLatencies[s].add(duration)
|
||||
|
||||
if trace {
|
||||
globalTrace.Publish(storageTrace(s, startTime, duration, strings.Join(paths, " ")))
|
||||
|
||||
+20
-10
@@ -216,14 +216,10 @@ func newXLStorage(ep Endpoint) (s *xlStorage, err error) {
|
||||
if globalIsCICD {
|
||||
rootDisk = true
|
||||
} else {
|
||||
rootDisk, err = disk.IsRootDisk(path, SlashSeparator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !rootDisk && globalRootDiskThreshold > 0 {
|
||||
// If for some reason we couldn't detect the root disk
|
||||
// use - MINIO_ROOTDISK_THRESHOLD_SIZE to figure out if
|
||||
// this disk is a root disk.
|
||||
if globalRootDiskThreshold > 0 {
|
||||
// When you do not want rely on automatic verification
|
||||
// of rejecting root disks, we need to add this threshold
|
||||
// to ensure that root disks are ignored properly.
|
||||
info, err := disk.GetInfo(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -232,6 +228,14 @@ func newXLStorage(ep Endpoint) (s *xlStorage, err error) {
|
||||
// treat those disks with size less than or equal to the
|
||||
// threshold as rootDisks.
|
||||
rootDisk = info.Total <= globalRootDiskThreshold
|
||||
} else {
|
||||
// When root disk threshold is not set, we rely
|
||||
// on automatic detection - does not work in
|
||||
// container environments.
|
||||
rootDisk, err = disk.IsRootDisk(path, SlashSeparator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +455,9 @@ func (s *xlStorage) NSScanner(ctx context.Context, cache dataUsageCache, updates
|
||||
|
||||
cache.Info.updates = updates
|
||||
|
||||
dataUsageInfo, err := scanDataFolder(ctx, s.diskPath, cache, func(item scannerItem) (sizeSummary, error) {
|
||||
poolIdx, setIdx, _ := s.GetDiskLoc()
|
||||
|
||||
dataUsageInfo, err := scanDataFolder(ctx, poolIdx, setIdx, s.diskPath, cache, func(item scannerItem) (sizeSummary, error) {
|
||||
// Look for `xl.meta/xl.json' at the leaf.
|
||||
if !strings.HasSuffix(item.Path, SlashSeparator+xlStorageFormatFile) &&
|
||||
!strings.HasSuffix(item.Path, SlashSeparator+xlStorageFormatFileV1) {
|
||||
@@ -1713,7 +1719,11 @@ func (s *xlStorage) ReadFileStream(ctx context.Context, volume, path string, off
|
||||
}{Reader: io.LimitReader(or, length), Closer: closeWrapper(func() error {
|
||||
if !alignment || offset+length%xioutil.DirectioAlignSize != 0 {
|
||||
// invalidate page-cache for unaligned reads.
|
||||
disk.FadviseDontNeed(file)
|
||||
if !globalAPIConfig.isDisableODirect() {
|
||||
// skip removing from page-cache only
|
||||
// if O_DIRECT was disabled.
|
||||
disk.FadviseDontNeed(file)
|
||||
}
|
||||
}
|
||||
return or.Close()
|
||||
})}
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
|
||||

|
||||
|
||||
Buckets can be configured to have one of two types of quota configuration - FIFO and Hard quota.
|
||||
|
||||
- `Hard` quota disallows writes to the bucket after configured quota limit is reached.
|
||||
- `FIFO` quota automatically deletes oldest content until bucket usage falls within configured limit while permitting writes.
|
||||
Buckets can be configured to have `Hard` quota - it disallows writes to the bucket after configured quota limit is reached.
|
||||
|
||||
> NOTE: Bucket quotas are not supported under gateway or standalone single disk deployments.
|
||||
|
||||
@@ -21,12 +18,6 @@ Buckets can be configured to have one of two types of quota configuration - FIFO
|
||||
$ mc admin bucket quota myminio/mybucket --hard 1gb
|
||||
```
|
||||
|
||||
### Set FIFO quota of 5GB for a bucket "mybucket" on MinIO to allow automatic deletion of older content to ensure bucket usage remains within 5GB
|
||||
|
||||
```sh
|
||||
$ mc admin bucket quota myminio/mybucket --fifo 5gb
|
||||
```
|
||||
|
||||
### Verify the quota configured on `mybucket` on MinIO
|
||||
|
||||
```sh
|
||||
|
||||
@@ -14,6 +14,7 @@ These metrics can be from any MinIO server once per collection.
|
||||
| `minio_bucket_replication_failed_count` | Total number of replication foperations failed for this bucket. |
|
||||
| `minio_bucket_usage_object_total` | Total number of objects |
|
||||
| `minio_bucket_usage_total_bytes` | Total bucket size in bytes |
|
||||
| `minio_bucket_quota_total_bytes` | Total bucket quota size in bytes |
|
||||
| `minio_cache_hits_total` | Total number of disk cache hits |
|
||||
| `minio_cache_missed_total` | Total number of disk cache misses |
|
||||
| `minio_cache_sent_bytes` | Total number of bytes served from cache |
|
||||
|
||||
@@ -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.2022-01-08T03-11-54Z
|
||||
image: quay.io/minio/minio:RELEASE.2022-02-01T18-00-14Z
|
||||
command: server --console-address ":9001" http://minio{1...4}/data{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
|
||||
+12
-2
@@ -35,12 +35,13 @@ In this document we will explain in detail on how to configure all the prerequis
|
||||
> NOTE: If you are interested in AssumeRole API only, skip to [here](https://github.com/minio/minio/blob/master/docs/sts/assume-role.md)
|
||||
|
||||
### Prerequisites
|
||||
- [Configuring keycloak](https://github.com/minio/minio/blob/master/docs/sts/keycloak.md)
|
||||
- [Configuring keycloak](https://github.com/minio/minio/blob/master/docs/sts/keycloak.md) or [Configuring Casdoor](https://github.com/minio/minio/blob/master/docs/sts/casdoor.md)
|
||||
- [Configuring etcd (optional needed only in gateway or federation mode)](https://github.com/minio/minio/blob/master/docs/sts/etcd.md)
|
||||
|
||||
### Setup MinIO with Keycloak
|
||||
### Setup MinIO with Identity Provider
|
||||
Make sure we have followed the previous step and configured each software independently, once done we can now proceed to use MinIO STS API and MinIO server to use these credentials to perform object API operations.
|
||||
|
||||
#### KeyCloak
|
||||
```
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
@@ -49,6 +50,15 @@ export MINIO_IDENTITY_OPENID_CLIENT_ID="843351d4-1080-11ea-aa20-271ecba3924a"
|
||||
minio server /mnt/data
|
||||
```
|
||||
|
||||
#### Casdoor
|
||||
```
|
||||
export MINIO_ROOT_USER=minio
|
||||
export MINIO_ROOT_PASSWORD=minio123
|
||||
export MINIO_IDENTITY_OPENID_CONFIG_URL=http://CASDOOR_ENDPOINT/.well-known/openid-configuration
|
||||
export MINIO_IDENTITY_OPENID_CLIENT_ID="843351d4-1080-11ea-aa20-271ecba3924a"
|
||||
minio server /mnt/data
|
||||
```
|
||||
|
||||
### Setup MinIO Gateway with Keycloak and Etcd
|
||||
Make sure we have followed the previous step and configured each software independently, once done we can now proceed to use MinIO STS API and MinIO gateway to use these credentials to perform object API operations.
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# Casdoor Quickstart Guide [](https://slack.min.io)
|
||||
|
||||
Casdoor is a UI-first centralized authentication / Single-Sign-On (SSO) platform supporting OAuth 2.0, OIDC and SAML, integrated with Casbin RBAC and ABAC permission management. This document covers configuring Casdoor identity provider support with MinIO.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Configure and install casdoor server by following [Casdoor Server Installation](https://casdoor.org/docs/basic/server-installation).
|
||||
For a quick installation, docker-compose reference configs are also available on the [Casdoor Try with Docker](https://casdoor.org/docs/basic/try-with-docker).
|
||||
|
||||
### Configure Casdoor
|
||||
- Go to Applications
|
||||
- Create or use an existing Casdoor application
|
||||
- Edit the application
|
||||
- Copy `Client ID` and `Client secret`
|
||||
- Add your redirect url (callback url) to `Redirect URLs`
|
||||
- Save
|
||||
|
||||
- Go to Users
|
||||
- Edit the user
|
||||
- Add your MinIO policy (ex: `readwrite`) in `Tag`
|
||||
- Save
|
||||
|
||||
- Open your favorite browser and visit: **http://`CASDOOR_ENDPOINT`/.well-known/openid-configuration**, you will see the OIDC configure of Casdoor.
|
||||
|
||||
### Configure MinIO
|
||||
```
|
||||
$ export MINIO_ROOT_USER=minio
|
||||
$ export MINIO_ROOT_PASSWORD=minio123
|
||||
$ minio server /mnt/export
|
||||
```
|
||||
|
||||
Here are all the available options to configure OpenID connect
|
||||
```
|
||||
mc admin config set myminio/ identity_openid
|
||||
|
||||
KEY:
|
||||
identity_openid enable OpenID SSO support
|
||||
|
||||
ARGS:
|
||||
config_url* (url) openid discovery document e.g. "https://accounts.google.com/.well-known/openid-configuration"
|
||||
client_id (string) unique public identifier for apps e.g. "292085223830.apps.googleusercontent.com"
|
||||
claim_name (string) JWT canned policy claim name, defaults to "policy"
|
||||
claim_prefix (string) JWT claim namespace prefix e.g. "customer1/"
|
||||
scopes (csv) Comma separated list of OpenID scopes for server, defaults to advertised scopes from discovery document e.g. "email,admin"
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
and ENV based options
|
||||
```
|
||||
mc admin config set myminio/ identity_openid --env
|
||||
|
||||
KEY:
|
||||
identity_openid enable OpenID SSO support
|
||||
|
||||
ARGS:
|
||||
MINIO_IDENTITY_OPENID_CONFIG_URL* (url) openid discovery document e.g. "https://accounts.google.com/.well-known/openid-configuration"
|
||||
MINIO_IDENTITY_OPENID_CLIENT_ID (string) unique public identifier for apps e.g. "292085223830.apps.googleusercontent.com"
|
||||
MINIO_IDENTITY_OPENID_CLAIM_NAME (string) JWT canned policy claim name, defaults to "policy"
|
||||
MINIO_IDENTITY_OPENID_CLAIM_PREFIX (string) JWT claim namespace prefix e.g. "customer1/"
|
||||
MINIO_IDENTITY_OPENID_SCOPES (csv) Comma separated list of OpenID scopes for server, defaults to advertised scopes from discovery document e.g. "email,admin"
|
||||
MINIO_IDENTITY_OPENID_COMMENT (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
Set `identity_openid` config with `config_url`, `client_id` and restart MinIO
|
||||
```
|
||||
~ mc admin config set myminio identity_openid config_url="http://CASDOOR_ENDPOINT/.well-known/openid-configuration" client_id=<client id> client_secret=<client secret> claim_name="tag"
|
||||
```
|
||||
> NOTE: As MinIO needs to use a claim attribute in JWT for its policy, you should configure it in casdoor as well. Currently, casdoor uses `tag` as a workaround for configuring MinIO's policy.
|
||||
|
||||
Once successfully set restart the MinIO instance.
|
||||
```
|
||||
mc admin service restart myminio
|
||||
```
|
||||
|
||||
### Using WebIdentiy API
|
||||
On another terminal run `web-identity.go` a sample client application which obtains JWT id_tokens from an identity provider, in our case its Keycloak. Uses the returned id_token response to get new temporary credentials from the MinIO server using the STS API call `AssumeRoleWithWebIdentity`.
|
||||
|
||||
```
|
||||
$ go run docs/sts/web-identity.go -cid account -csec 072e7f00-4289-469c-9ab2-bbe843c7f5a8 -config-ep "http://CASDOOR_ENDPOINT/.well-known/openid-configuration" -port 8888
|
||||
2018/12/26 17:49:36 listening on http://localhost:8888/
|
||||
```
|
||||
|
||||
This will open the login page of Casdoor, upon successful login, STS credentials along with any buckets discovered using the credentials will be printed on the screen, for example:
|
||||
|
||||
```
|
||||
{
|
||||
buckets: [ ],
|
||||
credentials: {
|
||||
AccessKeyID: "EJOLVY3K3G4BF37YD1A0",
|
||||
SecretAccessKey: "1b+w8LlDqMQOquKxIlZ2ggP+bgE51iwNG7SUVPJJ",
|
||||
SessionToken: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJFSk9MVlkzSzNHNEJGMzdZRDFBMCIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IiIsImFwcGxlIjoiIiwiYXVkIjpbIjI0YTI1ZWEwNzE0ZDkyZTc4NTk1Il0sImF2YXRhciI6Imh0dHBzOi8vY2FzYmluLm9yZy9pbWcvY2FzYmluLnN2ZyIsImF6dXJlYWQiOiIiLCJiaW8iOiIiLCJiaXJ0aGRheSI6IiIsImNyZWF0ZWRJcCI6IiIsImNyZWF0ZWRUaW1lIjoiMjAyMS0xMi0wNlQyMzo1ODo0MyswODowMCIsImRpbmd0YWxrIjoiIiwiZGlzcGxheU5hbWUiOiJjYmMiLCJlZHVjYXRpb24iOiIiLCJlbWFpbCI6IjE5OTkwNjI2LmxvdmVAMTYzLmNvbSIsImV4cCI6MTY0MzIwMjIyMCwiZmFjZWJvb2siOiIiLCJnZW5kZXIiOiIiLCJnaXRlZSI6IiIsImdpdGh1YiI6IiIsImdpdGxhYiI6IiIsImdvb2dsZSI6IiIsImhhc2giOiIiLCJob21lcGFnZSI6IiIsImlhdCI6MTY0MzE5MjEwMSwiaWQiOiIxYzU1NTgxZS01ZmEyLTQ4NTEtOWM2NC04MjNhNjYyZDBkY2IiLCJpZENhcmQiOiIiLCJpZENhcmRUeXBlIjoiIiwiaXNBZG1pbiI6dHJ1ZSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNEZWxldGVkIjpmYWxzZSwiaXNGb3JiaWRkZW4iOmZhbHNlLCJpc0dsb2JhbEFkbWluIjp0cnVlLCJpc09ubGluZSI6ZmFsc2UsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMCIsImxhbmd1YWdlIjoiIiwibGFyayI6IiIsImxhc3RTaWduaW5JcCI6IiIsImxhc3RTaWduaW5UaW1lIjoiIiwibGRhcCI6IiIsImxpbmtlZGluIjoiIiwibG9jYXRpb24iOiIiLCJuYW1lIjoiY2JjIiwibmJmIjoxNjQzMTkyMTAxLCJub25jZSI6Im51bGwiLCJvd25lciI6ImJ1aWx0LWluIiwicGFzc3dvcmQiOiIiLCJwYXNzd29yZFNhbHQiOiIiLCJwZXJtYW5lbnRBdmF0YXIiOiIiLCJwaG9uZSI6IjE4ODE3NTgzMjA3IiwicHJlSGFzaCI6IjAwY2JiNGEyOTBjZDBjZDgwZmZkZWMyZjBhOWJlM2E2IiwicHJvcGVydGllcyI6e30sInFxIjoiIiwicmFua2luZyI6MCwicmVnaW9uIjoiIiwic2NvcmUiOjIwMDAsInNpZ251cEFwcGxpY2F0aW9uIjoiYXBwLWJ1aWx0LWluIiwic2xhY2siOiIiLCJzdWIiOiIxYzU1NTgxZS01ZmEyLTQ4NTEtOWM2NC04MjNhNjYyZDBkY2IiLCJ0YWciOiJyZWFkd3JpdGUiLCJ0aXRsZSI6IiIsInR5cGUiOiJub3JtYWwtdXNlciIsInVwZGF0ZWRUaW1lIjoiIiwid2VjaGF0IjoiIiwid2Vjb20iOiIiLCJ3ZWlibyI6IiJ9.C5ZoJrojpRSePg_Ef9O-JTnc9BgoDNC5JX5AxlE9npd2tNl3ftudhny47pG6GgNDeiCMiaxueNyb_HPEPltJTw",
|
||||
SignerType: 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using MinIO Console
|
||||
|
||||
- Open MinIO URL on the browser, lets say http://localhost:9000/
|
||||
- Click on `Login with SSO`
|
||||
- User will be redirected to the Casdoor user login page, upon successful login the user will be redirected to MinIO page and logged in automatically,
|
||||
the user should see now the buckets and objects they have access to.
|
||||
|
||||
## Explore Further
|
||||
|
||||
- [Casdoor MinIO Integration](https://casdoor.org/docs/integration/minio)
|
||||
- [MinIO STS Quickstart Guide](https://docs.min.io/docs/minio-sts-quickstart-guide)
|
||||
- [The MinIO documentation website](https://docs.min.io)
|
||||
+1
-1
@@ -37,7 +37,7 @@ ARGS:
|
||||
MINIO_IDENTITY_LDAP_SERVER_ADDR* (address) AD/LDAP server address e.g. "myldapserver.com:636"
|
||||
MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN (string) DN for LDAP read-only service account used to perform DN and group lookups
|
||||
MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD (string) Password for LDAP read-only service account used to perform DN and group lookups
|
||||
MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN (string) Base LDAP DN to search for user DN
|
||||
MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN (string) ";" separated list of user search base DNs e.g. "dc=myldapserver,dc=com"
|
||||
MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER (string) Search filter to lookup user DN
|
||||
MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER (string) search filter for groups e.g. "(&(objectclass=groupOfNames)(memberUid=%s))"
|
||||
MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN (list) ";" separated list of group search base DNs e.g. "dc=myldapserver,dc=com"
|
||||
|
||||
@@ -7,7 +7,6 @@ require (
|
||||
github.com/Azure/azure-pipeline-go v0.2.2
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0
|
||||
github.com/Shopify/sarama v1.27.2
|
||||
github.com/VividCortex/ewma v1.1.1
|
||||
github.com/alecthomas/participle v0.2.1
|
||||
github.com/bcicen/jstream v1.0.1
|
||||
github.com/beevik/ntp v0.3.0
|
||||
@@ -45,11 +44,12 @@ require (
|
||||
github.com/lib/pq v1.9.0
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/minio/cli v1.22.0
|
||||
github.com/minio/console v0.13.3
|
||||
github.com/minio/console v0.14.3
|
||||
github.com/minio/csvparser v1.0.0
|
||||
github.com/minio/dperf v0.2.1-0.20220126071331-eefa6468e289
|
||||
github.com/minio/highwayhash v1.0.2
|
||||
github.com/minio/kes v0.14.0
|
||||
github.com/minio/madmin-go v1.2.7
|
||||
github.com/minio/madmin-go v1.3.1
|
||||
github.com/minio/minio-go/v7 v7.0.21
|
||||
github.com/minio/parquet-go v1.1.0
|
||||
github.com/minio/pkg v1.1.15
|
||||
@@ -74,20 +74,20 @@ require (
|
||||
github.com/rs/cors v1.7.0
|
||||
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417
|
||||
github.com/secure-io/sio-go v0.3.1
|
||||
github.com/shirou/gopsutil/v3 v3.21.9
|
||||
github.com/shirou/gopsutil/v3 v3.21.12
|
||||
github.com/streadway/amqp v1.0.0
|
||||
github.com/tinylib/msgp v1.1.7-0.20211026165309-e818a1881b0e
|
||||
github.com/valyala/bytebufferpool v1.0.0
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c
|
||||
github.com/yargevad/filepathx v1.0.0
|
||||
github.com/zeebo/xxh3 v1.0.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.0
|
||||
go.etcd.io/etcd/client/v3 v3.5.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.1
|
||||
go.etcd.io/etcd/client/v3 v3.5.1
|
||||
go.uber.org/atomic v1.9.0
|
||||
go.uber.org/zap v1.19.1
|
||||
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b
|
||||
golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
|
||||
google.golang.org/api v0.58.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
@@ -98,36 +98,39 @@ require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/StackExchange/wmi v1.2.1 // indirect
|
||||
github.com/apache/thrift v0.15.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.0 // indirect
|
||||
github.com/briandowns/spinner v1.16.0 // indirect
|
||||
github.com/charmbracelet/bubbles v0.10.0 // indirect
|
||||
github.com/charmbracelet/bubbletea v0.19.3 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.4.1-0.20220204041308-bf2912e703f6 // indirect
|
||||
github.com/containerd/console v1.0.2 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/eapache/go-resiliency v1.2.0 // indirect
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
|
||||
github.com/eapache/queue v1.1.0 // indirect
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/frankban/quicktest v1.14.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect
|
||||
github.com/go-logr/logr v0.4.0 // indirect
|
||||
github.com/go-logr/logr v1.2.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/analysis v0.20.1 // indirect
|
||||
github.com/go-openapi/errors v0.20.1 // indirect
|
||||
github.com/go-openapi/analysis v0.21.2 // indirect
|
||||
github.com/go-openapi/errors v0.20.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/runtime v0.21.0 // indirect
|
||||
github.com/go-openapi/runtime v0.21.1 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.1 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-openapi/validate v0.20.3 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/goccy/go-json v0.7.9 // indirect
|
||||
github.com/goccy/go-json v0.8.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
||||
@@ -136,6 +139,7 @@ require (
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
|
||||
github.com/googleapis/gnostic v0.5.1 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
@@ -153,10 +157,12 @@ require (
|
||||
github.com/lestrrat-go/blackmagic v1.0.0 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.0 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.1 // indirect
|
||||
github.com/lestrrat-go/jwx v1.2.7 // indirect
|
||||
github.com/lestrrat-go/jwx v1.2.14 // indirect
|
||||
github.com/lestrrat-go/option v1.0.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.10 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-ieproxy v0.0.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
@@ -166,28 +172,34 @@ require (
|
||||
github.com/minio/colorjson v1.0.1 // indirect
|
||||
github.com/minio/direct-csi v1.3.5-0.20210601185811-f7776f7961bf // indirect
|
||||
github.com/minio/filepath v1.0.0 // indirect
|
||||
github.com/minio/mc v0.0.0-20211207230606-23a05f5a17f2 // indirect
|
||||
github.com/minio/mc v0.0.0-20220204044644-e048c85d71a7 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/operator v0.0.0-20220110040724-a5d59a342b7f // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.0.2 // indirect
|
||||
github.com/nats-io/nats-streaming-server v0.21.2 // indirect
|
||||
github.com/nats-io/nkeys v0.3.0 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pkg/profile v1.6.0 // indirect
|
||||
github.com/pkg/xattr v0.4.3 // indirect
|
||||
github.com/pkg/xattr v0.4.4 // indirect
|
||||
github.com/posener/complete v1.2.3 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect
|
||||
github.com/prometheus/common v0.31.1 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rjeczalik/notify v0.9.2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1 // indirect
|
||||
github.com/rs/xid v1.3.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/tidwall/gjson v1.11.0 // indirect
|
||||
github.com/tidwall/gjson v1.12.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/sjson v1.2.3 // indirect
|
||||
@@ -195,21 +207,22 @@ require (
|
||||
github.com/tklauser/numcpus v0.3.0 // indirect
|
||||
github.com/unrolled/secure v1.0.9 // indirect
|
||||
github.com/xdg/stringprep v1.0.0 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
|
||||
go.mongodb.org/mongo-driver v1.7.5 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210928142010-c7af6a1a74c9 // indirect
|
||||
google.golang.org/grpc v1.41.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect
|
||||
google.golang.org/grpc v1.43.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/h2non/filetype.v1 v1.0.5 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.63.2 // indirect
|
||||
gopkg.in/ini.v1 v1.66.3 // indirect
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect
|
||||
@@ -219,7 +232,7 @@ require (
|
||||
k8s.io/api v0.21.1 // indirect
|
||||
k8s.io/apimachinery v0.21.1 // indirect
|
||||
k8s.io/client-go v0.21.1 // indirect
|
||||
k8s.io/klog/v2 v2.8.0 // indirect
|
||||
k8s.io/klog/v2 v2.30.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 // indirect
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
|
||||
maze.io/x/duration v0.0.0-20160924141736-faac084b6075 // indirect
|
||||
@@ -227,3 +240,5 @@ require (
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/gomodule/redigo v2.0.0+incompatible => github.com/gomodule/redigo v1.8.5
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
apiVersion: v1
|
||||
description: Multi-Cloud Object Storage
|
||||
name: minio
|
||||
version: 3.4.6
|
||||
appVersion: RELEASE.2022-01-08T03-11-54Z
|
||||
version: 3.5.1
|
||||
appVersion: RELEASE.2022-02-01T18-00-14Z
|
||||
keywords:
|
||||
- minio
|
||||
- storage
|
||||
|
||||
@@ -81,6 +81,10 @@ spec:
|
||||
subPath: "{{ .Values.persistence.subPath }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: extra-secret
|
||||
mountPath: "/tmp/minio-config-env"
|
||||
{{- end }}
|
||||
{{- include "minio.tlsKeysVolumeMount" . | indent 12 }}
|
||||
ports:
|
||||
- name: {{ $scheme }}
|
||||
@@ -98,6 +102,10 @@ spec:
|
||||
secretKeyRef:
|
||||
name: {{ template "minio.secretName" . }}
|
||||
key: rootPassword
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: MINIO_CONFIG_ENV_FILE
|
||||
value: "/tmp/minio-config-env/config.env"
|
||||
{{- end}}
|
||||
{{- if .Values.metrics.serviceMonitor.public }}
|
||||
- name: MINIO_PROMETHEUS_AUTH_TYPE
|
||||
value: "public"
|
||||
@@ -149,6 +157,11 @@ spec:
|
||||
{{- else }}
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: extra-secret
|
||||
secret:
|
||||
secretName: {{ .Values.extraSecret }}
|
||||
{{- end }}
|
||||
- name: minio-user
|
||||
secret:
|
||||
secretName: {{ template "minio.secretName" . }}
|
||||
|
||||
@@ -86,6 +86,10 @@ spec:
|
||||
subPath: "{{ .Values.persistence.subPath }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: extra-secret
|
||||
mountPath: "/tmp/minio-config-env"
|
||||
{{- end }}
|
||||
{{- include "minio.tlsKeysVolumeMount" . | indent 12 }}
|
||||
ports:
|
||||
- name: {{ $scheme }}
|
||||
@@ -103,6 +107,10 @@ spec:
|
||||
secretKeyRef:
|
||||
name: {{ template "minio.secretName" . }}
|
||||
key: rootPassword
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: MINIO_CONFIG_ENV_FILE
|
||||
value: "/tmp/minio-config-env/config.env"
|
||||
{{- end}}
|
||||
{{- if eq .Values.gateway.type "gcs" }}
|
||||
- name: GOOGLE_APPLICATION_CREDENTIALS
|
||||
value: "/tmp/credentials/service-account-file.json"
|
||||
@@ -161,5 +169,10 @@ spec:
|
||||
- name: minio-user
|
||||
secret:
|
||||
secretName: {{ template "minio.secretName" . }}
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: extra-secret
|
||||
secret:
|
||||
secretName: {{ .Values.extraSecret }}
|
||||
{{- end }}
|
||||
{{- include "minio.tlsKeysVolume" . | indent 8 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -114,6 +114,10 @@ spec:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: extra-secret
|
||||
mountPath: "/tmp/minio-config-env"
|
||||
{{- end }}
|
||||
{{- include "minio.tlsKeysVolumeMount" . | indent 12 }}
|
||||
ports:
|
||||
- name: {{ $scheme }}
|
||||
@@ -131,6 +135,10 @@ spec:
|
||||
secretKeyRef:
|
||||
name: {{ template "minio.secretName" . }}
|
||||
key: rootPassword
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: MINIO_CONFIG_ENV_FILE
|
||||
value: "/tmp/minio-config-env/config.env"
|
||||
{{- end}}
|
||||
{{- if .Values.metrics.serviceMonitor.public }}
|
||||
- name: MINIO_PROMETHEUS_AUTH_TYPE
|
||||
value: "public"
|
||||
@@ -158,6 +166,11 @@ spec:
|
||||
- name: minio-user
|
||||
secret:
|
||||
secretName: {{ template "minio.secretName" . }}
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: extra-secret
|
||||
secret:
|
||||
secretName: {{ .Values.extraSecret }}
|
||||
{{- end }}
|
||||
{{- include "minio.tlsKeysVolume" . | indent 8 }}
|
||||
{{- if .Values.persistence.enabled }}
|
||||
volumeClaimTemplates:
|
||||
|
||||
@@ -315,6 +315,12 @@ environment:
|
||||
## MINIO_SUBNET_LICENSE: "License key obtained from https://subnet.min.io"
|
||||
## MINIO_BROWSER: "off"
|
||||
|
||||
## The name of a secret in the same kubernetes namespace which contain secret values
|
||||
## This can be useful for LDAP password, etc
|
||||
## The key in the secret must be 'config.env'
|
||||
##
|
||||
# extraSecret: minio-extraenv
|
||||
|
||||
networkPolicy:
|
||||
enabled: false
|
||||
allowExternal: true
|
||||
|
||||
+123
-35
@@ -1,9 +1,97 @@
|
||||
apiVersion: v1
|
||||
entries:
|
||||
minio:
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-01T18-00-14Z
|
||||
created: "2022-02-04T14:54:01.883917227-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: a3e855ed0f31233b989fffd775a29d6fbfa0590089010ff16783fd7f142ef6e7
|
||||
home: https://min.io
|
||||
icon: https://min.io/resources/img/logo/MINIO_wordmark.png
|
||||
keywords:
|
||||
- minio
|
||||
- storage
|
||||
- object-storage
|
||||
- s3
|
||||
- cluster
|
||||
maintainers:
|
||||
- email: dev@minio.io
|
||||
name: MinIO, Inc
|
||||
name: minio
|
||||
sources:
|
||||
- https://github.com/minio/minio
|
||||
urls:
|
||||
- https://charts.min.io/helm-releases/minio-3.5.1.tgz
|
||||
version: 3.5.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-01T18-00-14Z
|
||||
created: "2022-02-04T14:54:01.882747497-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: b1b0ae3c54b4260a698753e11d7781bb8ddc67b7e3fbf0af82796e4cd4ef92a3
|
||||
home: https://min.io
|
||||
icon: https://min.io/resources/img/logo/MINIO_wordmark.png
|
||||
keywords:
|
||||
- minio
|
||||
- storage
|
||||
- object-storage
|
||||
- s3
|
||||
- cluster
|
||||
maintainers:
|
||||
- email: dev@minio.io
|
||||
name: MinIO, Inc
|
||||
name: minio
|
||||
sources:
|
||||
- https://github.com/minio/minio
|
||||
urls:
|
||||
- https://charts.min.io/helm-releases/minio-3.5.0.tgz
|
||||
version: 3.5.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-01-28T02-28-16Z
|
||||
created: "2022-02-04T14:54:01.881714534-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: fecf25d2d3fb208c6f894fed642a60780a570b7f6d0adddde846af7236dc80aa
|
||||
home: https://min.io
|
||||
icon: https://min.io/resources/img/logo/MINIO_wordmark.png
|
||||
keywords:
|
||||
- minio
|
||||
- storage
|
||||
- object-storage
|
||||
- s3
|
||||
- cluster
|
||||
maintainers:
|
||||
- email: dev@minio.io
|
||||
name: MinIO, Inc
|
||||
name: minio
|
||||
sources:
|
||||
- https://github.com/minio/minio
|
||||
urls:
|
||||
- https://charts.min.io/helm-releases/minio-3.4.8.tgz
|
||||
version: 3.4.8
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-01-25T19-56-04Z
|
||||
created: "2022-02-04T14:54:01.880681748-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: c78008caa5ce98f64c887630f59d0cbd481cb3f19a7d4e9d3e81bf4e1e45cadc
|
||||
home: https://min.io
|
||||
icon: https://min.io/resources/img/logo/MINIO_wordmark.png
|
||||
keywords:
|
||||
- minio
|
||||
- storage
|
||||
- object-storage
|
||||
- s3
|
||||
- cluster
|
||||
maintainers:
|
||||
- email: dev@minio.io
|
||||
name: MinIO, Inc
|
||||
name: minio
|
||||
sources:
|
||||
- https://github.com/minio/minio
|
||||
urls:
|
||||
- https://charts.min.io/helm-releases/minio-3.4.7.tgz
|
||||
version: 3.4.7
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-01-08T03-11-54Z
|
||||
created: "2022-01-07T22:23:31.017276397-08:00"
|
||||
created: "2022-02-04T14:54:01.879748851-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 8f2e2691bf897f74ff094dd370ec56ba9d417e5e8926710c14c2ba346330238d
|
||||
home: https://min.io
|
||||
@@ -25,7 +113,7 @@ entries:
|
||||
version: 3.4.6
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-01-04T07-41-07Z
|
||||
created: "2022-01-07T22:23:31.016291288-08:00"
|
||||
created: "2022-02-04T14:54:01.878686506-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: bacd140f0016fab35f516bde787da6449b3a960c071fad9e4b6563118033ac84
|
||||
home: https://min.io
|
||||
@@ -47,7 +135,7 @@ entries:
|
||||
version: 3.4.5
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-29T06-49-06Z
|
||||
created: "2022-01-07T22:23:31.015267411-08:00"
|
||||
created: "2022-02-04T14:54:01.877412539-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 48a453ea5ffeef25933904caefd9470bfb26224dfc2d1096bd0031467ba53007
|
||||
home: https://min.io
|
||||
@@ -69,7 +157,7 @@ entries:
|
||||
version: 3.4.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-20T22-07-16Z
|
||||
created: "2022-01-07T22:23:31.013914449-08:00"
|
||||
created: "2022-02-04T14:54:01.876201383-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 47ef4a930713b98f9438ceca913c6e700f85bb25dba5624b056486254b5f0c60
|
||||
home: https://min.io
|
||||
@@ -91,7 +179,7 @@ entries:
|
||||
version: 3.4.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-20T22-07-16Z
|
||||
created: "2022-01-07T22:23:31.012618149-08:00"
|
||||
created: "2022-02-04T14:54:01.875010103-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: d6763f7e2ea66810bd55eb225579a9c3b968f9ae1256f45fd469362e55d846ff
|
||||
home: https://min.io
|
||||
@@ -113,7 +201,7 @@ entries:
|
||||
version: 3.4.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-10T23-03-39Z
|
||||
created: "2022-01-07T22:23:31.011313126-08:00"
|
||||
created: "2022-02-04T14:54:01.873865714-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 2fb822c87216ba3fc2ae51a54a0a3e239aa560d86542991504a841cc2a2b9a37
|
||||
home: https://min.io
|
||||
@@ -135,7 +223,7 @@ entries:
|
||||
version: 3.4.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-18T04-42-33Z
|
||||
created: "2022-01-07T22:23:31.010415945-08:00"
|
||||
created: "2022-02-04T14:54:01.872574251-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: fa8ba1aeb1a15316c6be8403416a5e6b5e6139b7166592087e7bddc9e6db5453
|
||||
home: https://min.io
|
||||
@@ -157,7 +245,7 @@ entries:
|
||||
version: 3.4.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-10T23-03-39Z
|
||||
created: "2022-01-07T22:23:31.009538439-08:00"
|
||||
created: "2022-02-04T14:54:01.871540757-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: b9b0af9ca50b8d00868e1f1b989dca275829d9110af6de91bb9b3a398341e894
|
||||
home: https://min.io
|
||||
@@ -179,7 +267,7 @@ entries:
|
||||
version: 3.3.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-10T23-03-39Z
|
||||
created: "2022-01-07T22:23:31.008600244-08:00"
|
||||
created: "2022-02-04T14:54:01.870553195-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: f8b22a5b8fe95a7ddf61b825e17d11c9345fb10e4c126b0d78381608aa300a08
|
||||
home: https://min.io
|
||||
@@ -201,7 +289,7 @@ entries:
|
||||
version: 3.3.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-10T23-03-39Z
|
||||
created: "2022-01-07T22:23:31.007685729-08:00"
|
||||
created: "2022-02-04T14:54:01.868933933-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: c48d474f269427abe5ab446f00687d0625b3d1adfc5c73bdb4b21ca9e42853fb
|
||||
home: https://min.io
|
||||
@@ -223,7 +311,7 @@ entries:
|
||||
version: 3.3.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-11-24T23-19-33Z
|
||||
created: "2022-01-07T22:23:31.006668152-08:00"
|
||||
created: "2022-02-04T14:54:01.867987898-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 7c3da39d9b0090cbf5efedf0cc163a1e2df05becc5152c3add8e837384690bc4
|
||||
home: https://min.io
|
||||
@@ -245,7 +333,7 @@ entries:
|
||||
version: 3.3.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-11-24T23-19-33Z
|
||||
created: "2022-01-07T22:23:31.005220606-08:00"
|
||||
created: "2022-02-04T14:54:01.866909733-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 50d6590b4cc779c40f81cc13b1586fbe508aa7f3230036c760bfc5f4154fbce4
|
||||
home: https://min.io
|
||||
@@ -267,7 +355,7 @@ entries:
|
||||
version: 3.3.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-10-13T00-23-17Z
|
||||
created: "2022-01-07T22:23:31.004333024-08:00"
|
||||
created: "2022-02-04T14:54:01.865773884-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 5b797b7208cd904c11a76cd72938c8652160cb5fcd7f09fa41e4e703e6d64054
|
||||
home: https://min.io
|
||||
@@ -289,7 +377,7 @@ entries:
|
||||
version: 3.2.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-10-10T16-53-30Z
|
||||
created: "2022-01-07T22:23:31.003383269-08:00"
|
||||
created: "2022-02-04T14:54:01.864870666-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: e084ac4bb095f071e59f8f08bd092e4ab2404c1ddadacfdce7dbe248f1bafff8
|
||||
home: https://min.io
|
||||
@@ -311,7 +399,7 @@ entries:
|
||||
version: 3.1.9
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-10-06T23-36-31Z
|
||||
created: "2022-01-07T22:23:31.00245312-08:00"
|
||||
created: "2022-02-04T14:54:01.863907095-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 2890430a8d9487d1fa5508c26776e4881d0086b2c052aa6bdc65c0e4423b9159
|
||||
home: https://min.io
|
||||
@@ -333,7 +421,7 @@ entries:
|
||||
version: 3.1.8
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-10-02T16-31-05Z
|
||||
created: "2022-01-07T22:23:31.001470508-08:00"
|
||||
created: "2022-02-04T14:54:01.862644497-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 01a92196af6c47e3a01e1c68d7cf693a8bc487cba810c2cecff155071e4d6a11
|
||||
home: https://min.io
|
||||
@@ -355,7 +443,7 @@ entries:
|
||||
version: 3.1.7
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-18T18-09-59Z
|
||||
created: "2022-01-07T22:23:31.000439575-08:00"
|
||||
created: "2022-02-04T14:54:01.861450804-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: e779d73f80b75f33b9c9d995ab10fa455c9c57ee575ebc54e06725a64cd04310
|
||||
home: https://min.io
|
||||
@@ -377,7 +465,7 @@ entries:
|
||||
version: 3.1.6
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-18T18-09-59Z
|
||||
created: "2022-01-07T22:23:30.999005495-08:00"
|
||||
created: "2022-02-04T14:54:01.860470785-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 19de4bbc8a400f0c2a94c5e85fc25c9bfc666e773fb3e368dd621d5a57dd1c2a
|
||||
home: https://min.io
|
||||
@@ -399,7 +487,7 @@ entries:
|
||||
version: 3.1.5
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-18T18-09-59Z
|
||||
created: "2022-01-07T22:23:30.998030319-08:00"
|
||||
created: "2022-02-04T14:54:01.859520772-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: f789d93a171296dd01af0105a5ce067c663597afbb2432faeda293b752b355c0
|
||||
home: https://min.io
|
||||
@@ -421,7 +509,7 @@ entries:
|
||||
version: 3.1.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-09T21-37-07Z
|
||||
created: "2022-01-07T22:23:30.997073512-08:00"
|
||||
created: "2022-02-04T14:54:01.858553776-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: e2eb34d31560b012ef6581f0ff6004ea4376c968cbe0daed2d8f3a614a892afb
|
||||
home: https://min.io
|
||||
@@ -443,7 +531,7 @@ entries:
|
||||
version: 3.1.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-09T21-37-07Z
|
||||
created: "2022-01-07T22:23:30.996141707-08:00"
|
||||
created: "2022-02-04T14:54:01.857464842-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 8d7e0cc46b3583abd71b97dc0c071f98321101f90eca17348f1e9e0831be64cd
|
||||
home: https://min.io
|
||||
@@ -465,7 +553,7 @@ entries:
|
||||
version: 3.1.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-09T21-37-07Z
|
||||
created: "2022-01-07T22:23:30.995177995-08:00"
|
||||
created: "2022-02-04T14:54:01.856105308-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 50dcbf366b1b21f4a6fc429d0b884c0c7ff481d0fb95c5e9b3ae157c348dd124
|
||||
home: https://min.io
|
||||
@@ -487,7 +575,7 @@ entries:
|
||||
version: 3.1.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-09T21-37-07Z
|
||||
created: "2022-01-07T22:23:30.994243802-08:00"
|
||||
created: "2022-02-04T14:54:01.854632618-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 6c01af55d2e2e5f716eabf6fef3a92a8464d0674529e9bacab292e5478a73b7a
|
||||
home: https://min.io
|
||||
@@ -509,7 +597,7 @@ entries:
|
||||
version: 3.1.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-03T03-56-13Z
|
||||
created: "2022-01-07T22:23:30.993250641-08:00"
|
||||
created: "2022-02-04T14:54:01.853699758-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 18e10be4d0458bc590ca9abf753227e0c70f60511495387b8d4fb15a4daf932e
|
||||
home: https://min.io
|
||||
@@ -531,7 +619,7 @@ entries:
|
||||
version: 3.0.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-31T05-46-54Z
|
||||
created: "2022-01-07T22:23:30.991794257-08:00"
|
||||
created: "2022-02-04T14:54:01.852776944-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: f5b6e7f6272a9e71aef3b75555f6f756a39eef65cb78873f26451dba79b19906
|
||||
home: https://min.io
|
||||
@@ -553,7 +641,7 @@ entries:
|
||||
version: 3.0.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-31T05-46-54Z
|
||||
created: "2022-01-07T22:23:30.99054488-08:00"
|
||||
created: "2022-02-04T14:54:01.85188399-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 6d2ee1336c412affaaf209fdb80215be2a6ebb23ab2443adbaffef9e7df13fab
|
||||
home: https://min.io
|
||||
@@ -575,7 +663,7 @@ entries:
|
||||
version: 3.0.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-31T05-46-54Z
|
||||
created: "2022-01-07T22:23:30.989246019-08:00"
|
||||
created: "2022-02-04T14:54:01.850952702-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 0a004aaf5bb61deed6a5c88256d1695ebe2f9ff1553874a93e4acfd75e8d339b
|
||||
home: https://min.io
|
||||
@@ -595,7 +683,7 @@ entries:
|
||||
version: 2.0.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-25T00-41-18Z
|
||||
created: "2022-01-07T22:23:30.988375668-08:00"
|
||||
created: "2022-02-04T14:54:01.849982885-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: fcd944e837ee481307de6aa3d387ea18c234f995a84c15abb211aab4a4054afc
|
||||
home: https://min.io
|
||||
@@ -615,7 +703,7 @@ entries:
|
||||
version: 2.0.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-25T00-41-18Z
|
||||
created: "2022-01-07T22:23:30.986818536-08:00"
|
||||
created: "2022-02-04T14:54:01.848851323-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 7b6c033d43a856479eb493ab8ca05b230f77c3e42e209e8f298fac6af1a9796f
|
||||
home: https://min.io
|
||||
@@ -635,7 +723,7 @@ entries:
|
||||
version: 1.0.5
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-25T00-41-18Z
|
||||
created: "2022-01-07T22:23:30.983755023-08:00"
|
||||
created: "2022-02-04T14:54:01.847362247-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: abd221245ace16c8e0c6c851cf262d1474a5219dcbf25c4b2e7b77142f9c59ed
|
||||
home: https://min.io
|
||||
@@ -655,7 +743,7 @@ entries:
|
||||
version: 1.0.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-20T18-32-01Z
|
||||
created: "2022-01-07T22:23:30.982736376-08:00"
|
||||
created: "2022-02-04T14:54:01.846307751-08:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 922a333f5413d1042f7aa81929f43767f6ffca9b260c46713f04ce1dda86d57d
|
||||
home: https://min.io
|
||||
@@ -675,7 +763,7 @@ entries:
|
||||
version: 1.0.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-20T18-32-01Z
|
||||
created: "2022-01-07T22:23:30.981247281-08:00"
|
||||
created: "2022-02-04T14:54:01.845383032-08:00"
|
||||
description: High Performance, Kubernetes Native Object Storage
|
||||
digest: 10e22773506bbfb1c66442937956534cf4057b94f06a977db78b8cd223588388
|
||||
home: https://min.io
|
||||
@@ -695,7 +783,7 @@ entries:
|
||||
version: 1.0.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-20T18-32-01Z
|
||||
created: "2022-01-07T22:23:30.979933759-08:00"
|
||||
created: "2022-02-04T14:54:01.844449754-08:00"
|
||||
description: High Performance, Kubernetes Native Object Storage
|
||||
digest: ef86ab6df23d6942705da9ef70991b649638c51bc310587d37a425268ba4a06c
|
||||
home: https://min.io
|
||||
@@ -715,7 +803,7 @@ entries:
|
||||
version: 1.0.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-17T20-53-08Z
|
||||
created: "2022-01-07T22:23:30.978416915-08:00"
|
||||
created: "2022-02-04T14:54:01.843426903-08:00"
|
||||
description: High Performance, Kubernetes Native Object Storage
|
||||
digest: 1add7608692cbf39aaf9b1252530e566f7b2f306a14e390b0f49b97a20f2b188
|
||||
home: https://min.io
|
||||
@@ -733,4 +821,4 @@ entries:
|
||||
urls:
|
||||
- https://charts.min.io/helm-releases/minio-1.0.0.tgz
|
||||
version: 1.0.0
|
||||
generated: "2022-01-07T22:23:30.97673974-08:00"
|
||||
generated: "2022-02-04T14:54:01.841997075-08:00"
|
||||
|
||||
@@ -85,6 +85,9 @@ func LoadX509KeyPair(certFile, keyFile string) (tls.Certificate, error) {
|
||||
if len(rest) > 0 {
|
||||
return tls.Certificate{}, ErrSSLUnexpectedData(nil).Msg("The private key contains additional data")
|
||||
}
|
||||
if key == nil {
|
||||
return tls.Certificate{}, ErrSSLUnexpectedData(nil).Msg("The private key is not readable")
|
||||
}
|
||||
if x509.IsEncryptedPEMBlock(key) {
|
||||
password := env.Get(EnvCertPassword, "")
|
||||
if len(password) == 0 {
|
||||
|
||||
@@ -213,6 +213,32 @@ var loadX509KeyPairTests = []struct {
|
||||
privateKey, certificate string
|
||||
shouldFail bool
|
||||
}{
|
||||
{
|
||||
password: "",
|
||||
privateKey: "",
|
||||
certificate: `-----BEGIN CERTIFICATE-----
|
||||
MIIDiTCCAnGgAwIBAgIJAK5m5S7EE46kMA0GCSqGSIb3DQEBCwUAMFsxCzAJBgNV
|
||||
BAYTAlVTMQ4wDAYDVQQIDAVzdGF0ZTERMA8GA1UEBwwIbG9jYXRpb24xFTATBgNV
|
||||
BAoMDG9yZ2FuaXphdGlvbjESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE3MTIxODE4
|
||||
MDUyOFoXDTI3MTIxNjE4MDUyOFowWzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBXN0
|
||||
YXRlMREwDwYDVQQHDAhsb2NhdGlvbjEVMBMGA1UECgwMb3JnYW5pemF0aW9uMRIw
|
||||
EAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDPJfYY5Dhsntrqwyu7ZgKM/zrlKEjCwGHhWJBdZdeZCHQlY8ISrtDxxp2XMmI6
|
||||
HsszalEhNF9fk3vSXWclTuomG03fgGzP4R6QpcwGUCxhRF1J+0b64Yi8pw2uEGsR
|
||||
GuMwLhGorcWalNoihgHc0BQ4vO8aaTNTX7iD06olesP6vGNu/S8h0VomE+0v9qYc
|
||||
VF66Zaiv/6OmxAtDpElJjVd0mY7G85BlDlFrVwzd7zhRiuJZ4iDg749Xt9GuuKla
|
||||
Dvr14glHhP4dQgUbhluJmIHMdx2ZPjk+5FxaDK6I9IUpxczFDe4agDE6lKzU1eLd
|
||||
cCXRWFOf6q9lTB1hUZfmWfTxAgMBAAGjUDBOMB0GA1UdDgQWBBTQh7lDTq+8salD
|
||||
0HBNILochiiNaDAfBgNVHSMEGDAWgBTQh7lDTq+8salD0HBNILochiiNaDAMBgNV
|
||||
HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAqi9LycxcXKNSDXaPkCKvw7RQy
|
||||
iMBDGm1kIY++p3tzbUGuaeu85TsswKnqd50AullEU+aQxRRJGfR8eSKzQJMBXLMQ
|
||||
b4ptYCc5OrZtRHT8NaZ/df2tc6I88kN8dBu6ybcNGsevXA/iNX3kKLW7naxdr5jj
|
||||
KUudWSuqDCjCmQa5bYb9H6DreLH2lUItSWBa/YmeZ3VSezDCd+XYO53QKwZVj8Jb
|
||||
bulZmoo7e7HO1qecEzWKL10UYyEbG3UDPtw+NZc142ZYeEhXQ0dsstGAO5hf3hEl
|
||||
kQyKGUTpDbKLuyYMFsoH73YLjBqNe+UEhPwE+FWpcky1Sp9RTx/oMLpiZaPR
|
||||
-----END CERTIFICATE-----`,
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
password: "foobar",
|
||||
privateKey: `-----BEGIN RSA PRIVATE KEY-----
|
||||
|
||||
@@ -62,6 +62,7 @@ const (
|
||||
SecretKey = "secret_key"
|
||||
License = "license" // Deprecated Dec 2021
|
||||
APIKey = "api_key"
|
||||
Proxy = "proxy"
|
||||
)
|
||||
|
||||
// Top level config constants.
|
||||
|
||||
@@ -55,6 +55,7 @@ const (
|
||||
|
||||
EnvMinIOSubnetLicense = "MINIO_SUBNET_LICENSE" // Deprecated Dec 2021
|
||||
EnvMinIOSubnetAPIKey = "MINIO_SUBNET_API_KEY"
|
||||
EnvMinIOSubnetProxy = "MINIO_SUBNET_PROXY"
|
||||
EnvMinIOServerURL = "MINIO_SERVER_URL"
|
||||
EnvMinIOBrowserRedirectURL = "MINIO_BROWSER_REDIRECT_URL"
|
||||
EnvRootDiskThresholdSize = "MINIO_ROOTDISK_THRESHOLD_SIZE"
|
||||
|
||||
@@ -51,8 +51,9 @@ type Config struct {
|
||||
ServerAddr string `json:"serverAddr"`
|
||||
|
||||
// User DN search parameters
|
||||
UserDNSearchBaseDN string `json:"userDNSearchBaseDN"`
|
||||
UserDNSearchFilter string `json:"userDNSearchFilter"`
|
||||
UserDNSearchBaseDistName string `json:"userDNSearchBaseDN"`
|
||||
UserDNSearchBaseDistNames []string `json:"-"`
|
||||
UserDNSearchFilter string `json:"userDNSearchFilter"`
|
||||
|
||||
// Group search parameters
|
||||
GroupSearchBaseDistName string `json:"groupSearchBaseDN"`
|
||||
@@ -187,25 +188,32 @@ func (l *Config) lookupBind(conn *ldap.Conn) error {
|
||||
// search result in at most one result.
|
||||
func (l *Config) lookupUserDN(conn *ldap.Conn, username string) (string, error) {
|
||||
filter := strings.ReplaceAll(l.UserDNSearchFilter, "%s", ldap.EscapeFilter(username))
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
l.UserDNSearchBaseDN,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
filter,
|
||||
[]string{}, // only need DN, so no pass no attributes here
|
||||
nil,
|
||||
)
|
||||
var foundDistNames []string
|
||||
for _, userSearchBase := range l.UserDNSearchBaseDistNames {
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
userSearchBase,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
filter,
|
||||
[]string{}, // only need DN, so no pass no attributes here
|
||||
nil,
|
||||
)
|
||||
|
||||
searchResult, err := conn.Search(searchRequest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
searchResult, err := conn.Search(searchRequest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, entry := range searchResult.Entries {
|
||||
foundDistNames = append(foundDistNames, entry.DN)
|
||||
}
|
||||
}
|
||||
if len(searchResult.Entries) == 0 {
|
||||
if len(foundDistNames) == 0 {
|
||||
return "", fmt.Errorf("User DN for %s not found", username)
|
||||
}
|
||||
if len(searchResult.Entries) != 1 {
|
||||
if len(foundDistNames) != 1 {
|
||||
return "", fmt.Errorf("Multiple DNs for %s found - please fix the search filter", username)
|
||||
}
|
||||
return searchResult.Entries[0].DN, nil
|
||||
return foundDistNames[0], nil
|
||||
}
|
||||
|
||||
func (l *Config) searchForUserGroups(conn *ldap.Conn, username, bindDN string) ([]string, error) {
|
||||
@@ -375,7 +383,12 @@ func (l Config) testConnection() error {
|
||||
|
||||
// IsLDAPUserDN determines if the given string could be a user DN from LDAP.
|
||||
func (l Config) IsLDAPUserDN(user string) bool {
|
||||
return strings.HasSuffix(user, ","+l.UserDNSearchBaseDN)
|
||||
for _, baseDN := range l.UserDNSearchBaseDistNames {
|
||||
if strings.HasSuffix(user, ","+baseDN) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetNonEligibleUserDistNames - find user accounts (DNs) that are no longer
|
||||
@@ -505,15 +518,6 @@ func Lookup(kvs config.KVS, rootCAs *x509.CertPool) (l Config, err error) {
|
||||
if lookupBindDN != "" {
|
||||
l.LookupBindDN = lookupBindDN
|
||||
l.LookupBindPassword = lookupBindPassword
|
||||
|
||||
// User DN search configuration
|
||||
userDNSearchBaseDN := env.Get(EnvUserDNSearchBaseDN, kvs.Get(UserDNSearchBaseDN))
|
||||
userDNSearchFilter := env.Get(EnvUserDNSearchFilter, kvs.Get(UserDNSearchFilter))
|
||||
if userDNSearchFilter == "" || userDNSearchBaseDN == "" {
|
||||
return l, errors.New("In lookup bind mode, userDN search base DN and userDN search filter are both required")
|
||||
}
|
||||
l.UserDNSearchBaseDN = userDNSearchBaseDN
|
||||
l.UserDNSearchFilter = userDNSearchFilter
|
||||
}
|
||||
|
||||
// Test connection to LDAP server.
|
||||
@@ -521,6 +525,16 @@ func Lookup(kvs config.KVS, rootCAs *x509.CertPool) (l Config, err error) {
|
||||
return l, fmt.Errorf("Connection test for LDAP server failed: %w", err)
|
||||
}
|
||||
|
||||
// User DN search configuration
|
||||
userDNSearchBaseDN := env.Get(EnvUserDNSearchBaseDN, kvs.Get(UserDNSearchBaseDN))
|
||||
userDNSearchFilter := env.Get(EnvUserDNSearchFilter, kvs.Get(UserDNSearchFilter))
|
||||
if userDNSearchFilter == "" || userDNSearchBaseDN == "" {
|
||||
return l, errors.New("UserDN search base DN and UserDN search filter are both required")
|
||||
}
|
||||
l.UserDNSearchBaseDistName = userDNSearchBaseDN
|
||||
l.UserDNSearchBaseDistNames = strings.Split(userDNSearchBaseDN, dnDelimiter)
|
||||
l.UserDNSearchFilter = userDNSearchFilter
|
||||
|
||||
// Group search params configuration
|
||||
grpSearchFilter := env.Get(EnvGroupSearchFilter, kvs.Get(GroupSearchFilter))
|
||||
grpSearchBaseDN := env.Get(EnvGroupSearchBaseDN, kvs.Get(GroupSearchBaseDN))
|
||||
|
||||
@@ -44,9 +44,9 @@ var (
|
||||
},
|
||||
config.HelpKV{
|
||||
Key: UserDNSearchBaseDN,
|
||||
Description: `Base LDAP DN to search for user DN`,
|
||||
Description: `";" separated list of user search base DNs e.g. "dc=myldapserver,dc=com"`,
|
||||
Optional: true,
|
||||
Type: "string",
|
||||
Type: "list",
|
||||
},
|
||||
config.HelpKV{
|
||||
Key: UserDNSearchFilter,
|
||||
|
||||
@@ -82,7 +82,7 @@ func RegisterNotificationTargets(ctx context.Context, cfg config.Config, transpo
|
||||
for _, targetID := range targetIDs {
|
||||
if !targetList.Exists(targetID) {
|
||||
return nil, config.Errorf(
|
||||
"Unable to disable configured targets '%v'",
|
||||
"Unable to disable currently configured targets '%v'",
|
||||
targetID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
package subnet
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/minio/minio/internal/config"
|
||||
"github.com/minio/pkg/env"
|
||||
)
|
||||
@@ -33,6 +35,10 @@ var (
|
||||
Key: config.APIKey,
|
||||
Value: "",
|
||||
},
|
||||
config.KV{
|
||||
Key: config.Proxy,
|
||||
Value: "",
|
||||
},
|
||||
}
|
||||
|
||||
// HelpSubnet - provides help for subnet api key config
|
||||
@@ -49,6 +55,12 @@ var (
|
||||
Description: "Subnet api key for the cluster",
|
||||
Optional: true,
|
||||
},
|
||||
config.HelpKV{
|
||||
Key: config.Proxy,
|
||||
Type: "string",
|
||||
Description: "HTTP(S) proxy URL to use for connecting to SUBNET",
|
||||
Optional: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -59,6 +71,9 @@ type Config struct {
|
||||
|
||||
// The subnet api key
|
||||
APIKey string `json:"api_key"`
|
||||
|
||||
// The HTTP(S) proxy URL to use for connecting to SUBNET
|
||||
Proxy string `json:"proxy"`
|
||||
}
|
||||
|
||||
// LookupConfig - lookup config and override with valid environment settings if any.
|
||||
@@ -67,6 +82,12 @@ func LookupConfig(kvs config.KVS) (cfg Config, err error) {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
cfg.Proxy = env.Get(config.EnvMinIOSubnetProxy, kvs.Get(config.Proxy))
|
||||
_, err = url.Parse(cfg.Proxy)
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
cfg.License = env.Get(config.EnvMinIOSubnetLicense, kvs.Get(config.License))
|
||||
cfg.APIKey = env.Get(config.EnvMinIOSubnetAPIKey, kvs.Get(config.APIKey))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user