mirror of
https://github.com/pgsty/minio.git
synced 2026-08-14 02:33:16 +03:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c61c4b71b2 | |||
| 84f31ed45d | |||
| 8a81e317d6 | |||
| 224d9a752f | |||
| 0db34e4b85 | |||
| 8a9b9832fd | |||
| f66625be67 | |||
| 6825bd7e75 | |||
| 18515a4e3b | |||
| 839b9c9271 | |||
| dd9ed85e22 | |||
| e96c88e914 | |||
| 6c1410f7f5 | |||
| f92450d8b3 | |||
| c133979b8e | |||
| a9269cee29 | |||
| cf42ede92c | |||
| 958a480e53 | |||
| 62151a751d | |||
| f1ab9df2ee | |||
| a42650c065 | |||
| bdad3730f7 | |||
| a5835cecbf | |||
| b19620b324 | |||
| cd6dec49c0 | |||
| d350654aee | |||
| 6877578bbc | |||
| 10693fddfa | |||
| f3682b6149 | |||
| 056ca0c68e | |||
| 25f7a8e406 | |||
| 1f1c267b6c | |||
| eab1dc927b | |||
| fc94ea1ced | |||
| 67d2cf8f30 | |||
| 2c85b84cbc | |||
| 09a25ea7b7 |
@@ -37,6 +37,10 @@ check-gen: ## check for updated autogenerated files
|
||||
@(! git diff --name-only | grep '_gen.go$$') || (echo "Non-committed changes in auto-generated code is detected, please commit them to proceed." && false)
|
||||
|
||||
lint: getdeps ## runs golangci-lint suite of linters
|
||||
@echo "Running $@ check"
|
||||
@$(GOLANGCI) run --build-tags kqueue --timeout=10m --config ./.golangci.yml
|
||||
|
||||
lint-fix: getdeps ## runs golangci-lint suite of linters with automatic fixes
|
||||
@echo "Running $@ check"
|
||||
@$(GOLANGCI) run --build-tags kqueue --timeout=10m --config ./.golangci.yml --fix
|
||||
|
||||
|
||||
@@ -86,8 +86,6 @@ chmod +x minio
|
||||
./minio server /data
|
||||
```
|
||||
|
||||
Replace ``/data`` with the path to the drive or directory in which you want MinIO to store data.
|
||||
|
||||
The following table lists supported architectures. Replace the `wget` URL with the architecture for your Linux host.
|
||||
|
||||
| Architecture | URL |
|
||||
|
||||
@@ -75,11 +75,11 @@ check_minimum_version() {
|
||||
|
||||
assert_is_supported_arch() {
|
||||
case "${ARCH}" in
|
||||
x86_64 | amd64 | aarch64 | ppc64le | arm* | s390x )
|
||||
x86_64 | amd64 | aarch64 | ppc64le | arm* | s390x | loong64 | loongarch64 )
|
||||
return
|
||||
;;
|
||||
*)
|
||||
echo "Arch '${ARCH}' is not supported. Supported Arch: [x86_64, amd64, aarch64, ppc64le, arm*, s390x]"
|
||||
echo "Arch '${ARCH}' is not supported. Supported Arch: [x86_64, amd64, aarch64, ppc64le, arm*, s390x, loong64, loongarch64]"
|
||||
exit 1
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -85,11 +85,6 @@ func (a adminAPIHandlers) PutBucketQuotaConfigHandler(w http.ResponseWriter, r *
|
||||
return
|
||||
}
|
||||
|
||||
if quotaConfig.Type == "fifo" {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
updatedAt, err := globalBucketMetadataSys.Update(ctx, bucket, bucketQuotaConfigFile, data)
|
||||
if err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
@@ -1032,11 +1027,6 @@ func (a adminAPIHandlers) ImportBucketMetadataHandler(w http.ResponseWriter, r *
|
||||
continue
|
||||
}
|
||||
|
||||
if quotaConfig.Type == "fifo" {
|
||||
rpt.SetStatus(bucket, fileName, fmt.Errorf("Detected older 'fifo' quota config, 'fifo' feature is removed and not supported anymore"))
|
||||
continue
|
||||
}
|
||||
|
||||
updatedAt, err := globalBucketMetadataSys.Update(ctx, bucket, bucketQuotaConfigFile, data)
|
||||
if err != nil {
|
||||
rpt.SetStatus(bucket, fileName, err)
|
||||
|
||||
@@ -869,13 +869,8 @@ func (h *healSequence) healBucket(objAPI ObjectLayer, bucket string, bucketsOnly
|
||||
|
||||
if !h.settings.Recursive {
|
||||
if h.object != "" {
|
||||
// Check if an object named as the objPrefix exists,
|
||||
// and if so heal it.
|
||||
oi, err := objAPI.GetObjectInfo(h.ctx, bucket, h.object, ObjectOptions{})
|
||||
if err == nil {
|
||||
if err = h.healObject(bucket, h.object, oi.VersionID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.healObject(bucket, h.object, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2097,6 +2097,8 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) {
|
||||
apiErr = ErrObjectLockInvalidHeaders
|
||||
case objectlock.ErrMalformedXML:
|
||||
apiErr = ErrMalformedXML
|
||||
case errInvalidMaxParts:
|
||||
apiErr = ErrInvalidMaxParts
|
||||
}
|
||||
|
||||
// Compression errors
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
@@ -43,7 +44,8 @@ const (
|
||||
|
||||
// healingTracker is used to persist healing information during a heal.
|
||||
type healingTracker struct {
|
||||
disk StorageAPI `msg:"-"`
|
||||
disk StorageAPI `msg:"-"`
|
||||
mu *sync.RWMutex `msg:"-"`
|
||||
|
||||
ID string
|
||||
PoolIndex int
|
||||
@@ -112,22 +114,76 @@ func loadHealingTracker(ctx context.Context, disk StorageAPI) (*healingTracker,
|
||||
}
|
||||
h.disk = disk
|
||||
h.ID = diskID
|
||||
h.mu = &sync.RWMutex{}
|
||||
return &h, nil
|
||||
}
|
||||
|
||||
// newHealingTracker will create a new healing tracker for the disk.
|
||||
func newHealingTracker(disk StorageAPI, healID string) *healingTracker {
|
||||
diskID, _ := disk.GetDiskID()
|
||||
h := healingTracker{
|
||||
disk: disk,
|
||||
ID: diskID,
|
||||
HealID: healID,
|
||||
Path: disk.String(),
|
||||
Endpoint: disk.Endpoint().String(),
|
||||
Started: time.Now().UTC(),
|
||||
func newHealingTracker() *healingTracker {
|
||||
return &healingTracker{
|
||||
mu: &sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func initHealingTracker(disk StorageAPI, healID string) *healingTracker {
|
||||
h := newHealingTracker()
|
||||
diskID, _ := disk.GetDiskID()
|
||||
h.disk = disk
|
||||
h.ID = diskID
|
||||
h.HealID = healID
|
||||
h.Path = disk.String()
|
||||
h.Endpoint = disk.Endpoint().String()
|
||||
h.Started = time.Now().UTC()
|
||||
h.PoolIndex, h.SetIndex, h.DiskIndex = disk.GetDiskLoc()
|
||||
return &h
|
||||
return h
|
||||
}
|
||||
|
||||
func (h healingTracker) getLastUpdate() time.Time {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
|
||||
return h.LastUpdate
|
||||
}
|
||||
|
||||
func (h healingTracker) getBucket() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
|
||||
return h.Bucket
|
||||
}
|
||||
|
||||
func (h *healingTracker) setBucket(bucket string) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
h.Bucket = bucket
|
||||
}
|
||||
|
||||
func (h healingTracker) getObject() string {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
|
||||
return h.Object
|
||||
}
|
||||
|
||||
func (h *healingTracker) setObject(object string) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
h.Object = object
|
||||
}
|
||||
|
||||
func (h *healingTracker) updateProgress(success bool, bytes uint64) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
if success {
|
||||
h.ItemsHealed++
|
||||
h.BytesDone += bytes
|
||||
} else {
|
||||
h.ItemsFailed++
|
||||
h.BytesFailed += bytes
|
||||
}
|
||||
}
|
||||
|
||||
// update will update the tracker on the disk.
|
||||
@@ -136,15 +192,18 @@ func (h *healingTracker) update(ctx context.Context) error {
|
||||
if h.disk.Healing() == nil {
|
||||
return fmt.Errorf("healingTracker: drive %q is not marked as healing", h.ID)
|
||||
}
|
||||
h.mu.Lock()
|
||||
if h.ID == "" || h.PoolIndex < 0 || h.SetIndex < 0 || h.DiskIndex < 0 {
|
||||
h.ID, _ = h.disk.GetDiskID()
|
||||
h.PoolIndex, h.SetIndex, h.DiskIndex = h.disk.GetDiskLoc()
|
||||
}
|
||||
h.mu.Unlock()
|
||||
return h.save(ctx)
|
||||
}
|
||||
|
||||
// save will unconditionally save the tracker and will be created if not existing.
|
||||
func (h *healingTracker) save(ctx context.Context) error {
|
||||
h.mu.Lock()
|
||||
if h.PoolIndex < 0 || h.SetIndex < 0 || h.DiskIndex < 0 {
|
||||
// Attempt to get location.
|
||||
if api := newObjectLayerFn(); api != nil {
|
||||
@@ -155,6 +214,7 @@ func (h *healingTracker) save(ctx context.Context) error {
|
||||
}
|
||||
h.LastUpdate = time.Now().UTC()
|
||||
htrackerBytes, err := h.MarshalMsg(nil)
|
||||
h.mu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -176,6 +236,8 @@ func (h *healingTracker) delete(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (h *healingTracker) isHealed(bucket string) bool {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
for _, v := range h.HealedBuckets {
|
||||
if v == bucket {
|
||||
return true
|
||||
@@ -186,6 +248,9 @@ func (h *healingTracker) isHealed(bucket string) bool {
|
||||
|
||||
// resume will reset progress to the numbers at the start of the bucket.
|
||||
func (h *healingTracker) resume() {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
h.ItemsHealed = h.ResumeItemsHealed
|
||||
h.ItemsFailed = h.ResumeItemsFailed
|
||||
h.BytesDone = h.ResumeBytesDone
|
||||
@@ -195,6 +260,9 @@ func (h *healingTracker) resume() {
|
||||
// bucketDone should be called when a bucket is done healing.
|
||||
// Adds the bucket to the list of healed buckets and updates resume numbers.
|
||||
func (h *healingTracker) bucketDone(bucket string) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
h.ResumeItemsHealed = h.ItemsHealed
|
||||
h.ResumeItemsFailed = h.ItemsFailed
|
||||
h.ResumeBytesDone = h.BytesDone
|
||||
@@ -211,6 +279,9 @@ func (h *healingTracker) bucketDone(bucket string) {
|
||||
// setQueuedBuckets will add buckets, but exclude any that is already in h.HealedBuckets.
|
||||
// Order is preserved.
|
||||
func (h *healingTracker) setQueuedBuckets(buckets []BucketInfo) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
s := set.CreateStringSet(h.HealedBuckets...)
|
||||
h.QueuedBuckets = make([]string, 0, len(buckets))
|
||||
for _, b := range buckets {
|
||||
@@ -221,6 +292,9 @@ func (h *healingTracker) setQueuedBuckets(buckets []BucketInfo) {
|
||||
}
|
||||
|
||||
func (h *healingTracker) printTo(writer io.Writer) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
|
||||
b, err := json.MarshalIndent(h, "", " ")
|
||||
if err != nil {
|
||||
writer.Write([]byte(err.Error()))
|
||||
@@ -230,6 +304,9 @@ func (h *healingTracker) printTo(writer io.Writer) {
|
||||
|
||||
// toHealingDisk converts the information to madmin.HealingDisk
|
||||
func (h *healingTracker) toHealingDisk() madmin.HealingDisk {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
|
||||
return madmin.HealingDisk{
|
||||
ID: h.ID,
|
||||
HealID: h.HealID,
|
||||
@@ -336,7 +413,7 @@ func healFreshDisk(ctx context.Context, z *erasureServerPools, endpoint Endpoint
|
||||
return nil
|
||||
}
|
||||
logger.LogIf(ctx, fmt.Errorf("Unable to load healing tracker on '%s': %w, re-initializing..", disk, err))
|
||||
tracker = newHealingTracker(disk, mustGetUUID())
|
||||
tracker = initHealingTracker(disk, mustGetUUID())
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Healing drive '%s' - 'mc admin heal alias/ --verbose' to check the current status.", endpoint))
|
||||
|
||||
@@ -694,7 +694,7 @@ func (r *BatchJobReplicateV1) ReplicateToTarget(ctx context.Context, api ObjectL
|
||||
Versioned: versioned,
|
||||
VersionSuspended: versionSuspended,
|
||||
}
|
||||
rd, err := api.GetObjectNInfo(ctx, srcBucket, srcObject, nil, http.Header{}, readLock, opts)
|
||||
rd, err := api.GetObjectNInfo(ctx, srcBucket, srcObject, nil, http.Header{}, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+27
-1
@@ -32,6 +32,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/minio-go/v7/pkg/tags"
|
||||
"github.com/minio/minio/internal/amztime"
|
||||
sse "github.com/minio/minio/internal/bucket/encryption"
|
||||
@@ -70,6 +71,30 @@ func NewLifecycleSys() *LifecycleSys {
|
||||
return &LifecycleSys{}
|
||||
}
|
||||
|
||||
func ilmTrace(startTime time.Time, duration time.Duration, oi ObjectInfo, event string) madmin.TraceInfo {
|
||||
return madmin.TraceInfo{
|
||||
TraceType: madmin.TraceILM,
|
||||
Time: startTime,
|
||||
NodeName: globalLocalNodeName,
|
||||
FuncName: event,
|
||||
Duration: duration,
|
||||
Path: pathJoin(oi.Bucket, oi.Name),
|
||||
Error: "",
|
||||
Message: getSource(4),
|
||||
Custom: map[string]string{"version-id": oi.VersionID},
|
||||
}
|
||||
}
|
||||
|
||||
func (sys *LifecycleSys) trace(oi ObjectInfo) func(event string) {
|
||||
startTime := time.Now()
|
||||
return func(event string) {
|
||||
duration := time.Since(startTime)
|
||||
if globalTrace.NumSubscribers(madmin.TraceILM) > 0 {
|
||||
globalTrace.Publish(ilmTrace(startTime, duration, oi, event))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type expiryTask struct {
|
||||
objInfo ObjectInfo
|
||||
versionExpiry bool
|
||||
@@ -360,6 +385,7 @@ const (
|
||||
// 1. when a restored (via PostRestoreObject API) object expires.
|
||||
// 2. when a transitioned object expires (based on an ILM rule).
|
||||
func expireTransitionedObject(ctx context.Context, objectAPI ObjectLayer, oi *ObjectInfo, lcOpts lifecycle.ObjectOpts, action expireAction) error {
|
||||
traceFn := globalLifecycleSys.trace(*oi)
|
||||
var opts ObjectOptions
|
||||
opts.Versioned = globalBucketVersioningSys.PrefixEnabled(oi.Bucket, oi.Name)
|
||||
opts.VersionID = lcOpts.VersionID
|
||||
@@ -385,7 +411,7 @@ func expireTransitionedObject(ctx context.Context, objectAPI ObjectLayer, oi *Ob
|
||||
}
|
||||
|
||||
// Send audit for the lifecycle delete operation
|
||||
auditLogLifecycle(ctx, *oi, ILMExpiry)
|
||||
defer auditLogLifecycle(ctx, *oi, ILMExpiry, traceFn)
|
||||
|
||||
eventName := event.ObjectRemovedDelete
|
||||
if lcOpts.DeleteMarker {
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ func parseBucketQuota(bucket string, data []byte) (quotaCfg *madmin.BucketQuota,
|
||||
if !quotaCfg.IsValid() {
|
||||
if quotaCfg.Type == "fifo" {
|
||||
logger.LogIf(GlobalContext, errors.New("Detected older 'fifo' quota config, 'fifo' feature is removed and not supported anymore. Please clear your quota configs using 'mc admin bucket quota alias/bucket --clear' and use 'mc ilm add' for expiration of objects"))
|
||||
return quotaCfg, nil
|
||||
return quotaCfg, fmt.Errorf("invalid quota type 'fifo'")
|
||||
}
|
||||
return quotaCfg, fmt.Errorf("Invalid quota config %#v", quotaCfg)
|
||||
}
|
||||
|
||||
@@ -1085,7 +1085,7 @@ func (ri ReplicateObjectInfo) replicateObject(ctx context.Context, objectAPI Obj
|
||||
versioned := globalBucketVersioningSys.PrefixEnabled(bucket, object)
|
||||
versionSuspended := globalBucketVersioningSys.PrefixSuspended(bucket, object)
|
||||
|
||||
gr, err := objectAPI.GetObjectNInfo(ctx, bucket, object, nil, http.Header{}, readLock, ObjectOptions{
|
||||
gr, err := objectAPI.GetObjectNInfo(ctx, bucket, object, nil, http.Header{}, ObjectOptions{
|
||||
VersionID: objInfo.VersionID,
|
||||
Versioned: versioned,
|
||||
VersionSuspended: versionSuspended,
|
||||
@@ -1233,7 +1233,7 @@ func (ri ReplicateObjectInfo) replicateAll(ctx context.Context, objectAPI Object
|
||||
versioned := globalBucketVersioningSys.PrefixEnabled(bucket, object)
|
||||
versionSuspended := globalBucketVersioningSys.PrefixSuspended(bucket, object)
|
||||
|
||||
gr, err := objectAPI.GetObjectNInfo(ctx, bucket, object, nil, http.Header{}, readLock, ObjectOptions{
|
||||
gr, err := objectAPI.GetObjectNInfo(ctx, bucket, object, nil, http.Header{}, ObjectOptions{
|
||||
VersionID: objInfo.VersionID,
|
||||
Versioned: versioned,
|
||||
VersionSuspended: versionSuspended,
|
||||
|
||||
@@ -208,7 +208,7 @@ func (sys *BucketTargetSys) SetTarget(ctx context.Context, bucket string, tgt *m
|
||||
}
|
||||
clnt, err := sys.getRemoteTargetClient(tgt)
|
||||
if err != nil {
|
||||
return BucketRemoteTargetNotFound{Bucket: tgt.TargetBucket}
|
||||
return BucketRemoteTargetNotFound{Bucket: tgt.TargetBucket, Err: err}
|
||||
}
|
||||
// validate if target credentials are ok
|
||||
exists, err := clnt.BucketExists(ctx, tgt.TargetBucket)
|
||||
|
||||
@@ -30,12 +30,7 @@ import (
|
||||
var errConfigNotFound = errors.New("config file not found")
|
||||
|
||||
func readConfigWithMetadata(ctx context.Context, store objectIO, configFile string, opts ObjectOptions) ([]byte, ObjectInfo, error) {
|
||||
lockType := readLock
|
||||
if opts.NoLock {
|
||||
lockType = noLock // erasureObjects.GetObjectNInfo honors lockType argument but not opts.NoLock.
|
||||
}
|
||||
|
||||
r, err := store.GetObjectNInfo(ctx, minioMetaBucket, configFile, nil, http.Header{}, lockType, opts)
|
||||
r, err := store.GetObjectNInfo(ctx, minioMetaBucket, configFile, nil, http.Header{}, opts)
|
||||
if err != nil {
|
||||
if isErrObjectNotFound(err) {
|
||||
return nil, ObjectInfo{}, errConfigNotFound
|
||||
|
||||
+6
-3
@@ -957,6 +957,7 @@ func (i *scannerItem) applyLifecycle(ctx context.Context, o ObjectLayer, oi Obje
|
||||
// applyTierObjSweep removes remote object pending deletion and the free-version
|
||||
// tracking this information.
|
||||
func (i *scannerItem) applyTierObjSweep(ctx context.Context, o ObjectLayer, oi ObjectInfo) {
|
||||
traceFn := globalLifecycleSys.trace(oi)
|
||||
if !oi.TransitionedObject.FreeVersion {
|
||||
// nothing to be done
|
||||
return
|
||||
@@ -982,7 +983,7 @@ func (i *scannerItem) applyTierObjSweep(ctx context.Context, o ObjectLayer, oi O
|
||||
InclFreeVersions: true,
|
||||
})
|
||||
if err == nil {
|
||||
auditLogLifecycle(ctx, oi, ILMFreeVersionDelete)
|
||||
auditLogLifecycle(ctx, oi, ILMFreeVersionDelete, traceFn)
|
||||
}
|
||||
if ignoreNotFoundErr(err) != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
@@ -1177,6 +1178,7 @@ func applyExpiryOnTransitionedObject(ctx context.Context, objLayer ObjectLayer,
|
||||
}
|
||||
|
||||
func applyExpiryOnNonTransitionedObjects(ctx context.Context, objLayer ObjectLayer, obj ObjectInfo, applyOnVersion bool) bool {
|
||||
traceFn := globalLifecycleSys.trace(obj)
|
||||
opts := ObjectOptions{
|
||||
Expiration: ExpirationOptions{Expire: true},
|
||||
}
|
||||
@@ -1200,7 +1202,7 @@ func applyExpiryOnNonTransitionedObjects(ctx context.Context, objLayer ObjectLay
|
||||
}
|
||||
|
||||
// Send audit for the lifecycle delete operation
|
||||
auditLogLifecycle(ctx, obj, ILMExpiry)
|
||||
auditLogLifecycle(ctx, obj, ILMExpiry, traceFn)
|
||||
|
||||
eventName := event.ObjectRemovedDelete
|
||||
if obj.DeleteMarker {
|
||||
@@ -1443,7 +1445,7 @@ const (
|
||||
ILMTransition = " ilm:transition"
|
||||
)
|
||||
|
||||
func auditLogLifecycle(ctx context.Context, oi ObjectInfo, event string) {
|
||||
func auditLogLifecycle(ctx context.Context, oi ObjectInfo, event string, traceFn func(event string)) {
|
||||
var apiName string
|
||||
switch event {
|
||||
case ILMExpiry:
|
||||
@@ -1460,4 +1462,5 @@ func auditLogLifecycle(ctx context.Context, oi ObjectInfo, event string) {
|
||||
Object: oi.Name,
|
||||
VersionID: oi.VersionID,
|
||||
})
|
||||
traceFn(event)
|
||||
}
|
||||
|
||||
@@ -873,7 +873,7 @@ func (d *dataUsageCache) merge(other dataUsageCache) {
|
||||
}
|
||||
|
||||
type objectIO interface {
|
||||
GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (reader *GetObjectReader, err error)
|
||||
GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (reader *GetObjectReader, err error)
|
||||
PutObject(ctx context.Context, bucket, object string, data *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error)
|
||||
}
|
||||
|
||||
@@ -889,7 +889,7 @@ func (d *dataUsageCache) load(ctx context.Context, store objectIO, name string)
|
||||
// Caches are read+written without locks,
|
||||
retries := 0
|
||||
for retries < 5 {
|
||||
r, err := store.GetObjectNInfo(ctx, dataUsageBucket, name, nil, http.Header{}, noLock, ObjectOptions{NoLock: true})
|
||||
r, err := store.GetObjectNInfo(ctx, dataUsageBucket, name, nil, http.Header{}, ObjectOptions{NoLock: true})
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case ObjectNotFound, BucketNotFound:
|
||||
|
||||
+15
-15
@@ -80,7 +80,7 @@ type CacheStorageInfo struct {
|
||||
// CacheObjectLayer implements primitives for cache object API layer.
|
||||
type CacheObjectLayer interface {
|
||||
// Object operations.
|
||||
GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error)
|
||||
GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (gr *GetObjectReader, err error)
|
||||
GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error)
|
||||
DeleteObject(ctx context.Context, bucket, object string, opts ObjectOptions) (ObjectInfo, error)
|
||||
DeleteObjects(ctx context.Context, bucket string, objects []ObjectToDelete, opts ObjectOptions) ([]DeletedObject, []error)
|
||||
@@ -117,7 +117,7 @@ type cacheObjects struct {
|
||||
// Cache stats
|
||||
cacheStats *CacheStats
|
||||
|
||||
InnerGetObjectNInfoFn func(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error)
|
||||
InnerGetObjectNInfoFn func(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (gr *GetObjectReader, err error)
|
||||
InnerGetObjectInfoFn func(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error)
|
||||
InnerDeleteObjectFn func(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error)
|
||||
InnerPutObjectFn func(ctx context.Context, bucket, object string, data *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error)
|
||||
@@ -231,16 +231,16 @@ func (c *cacheObjects) incCacheStats(size int64) {
|
||||
c.cacheStats.incBytesServed(size)
|
||||
}
|
||||
|
||||
func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
if c.isCacheExclude(bucket, object) || c.skipCache() {
|
||||
return c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts)
|
||||
return c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, opts)
|
||||
}
|
||||
var cc *cacheControl
|
||||
var cacheObjSize int64
|
||||
// fetch diskCache if object is currently cached or nearest available cache drive
|
||||
dcache, err := c.getCacheToLoc(ctx, bucket, object)
|
||||
if err != nil {
|
||||
return c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts)
|
||||
return c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, opts)
|
||||
}
|
||||
|
||||
cacheReader, numCacheHits, cacheErr := dcache.Get(ctx, bucket, object, rs, h, opts)
|
||||
@@ -269,7 +269,7 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
if cc != nil && cc.noStore {
|
||||
cacheReader.Close()
|
||||
c.cacheStats.incMiss()
|
||||
bReader, err := c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts)
|
||||
bReader, err := c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, opts)
|
||||
bReader.ObjInfo.CacheLookupStatus = CacheHit
|
||||
bReader.ObjInfo.CacheStatus = CacheMiss
|
||||
return bReader, err
|
||||
@@ -304,7 +304,7 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
cacheReader.Close()
|
||||
}
|
||||
c.cacheStats.incMiss()
|
||||
return c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts)
|
||||
return c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, opts)
|
||||
}
|
||||
// skip cache for objects with locks
|
||||
objRetention := objectlock.GetObjectRetentionMeta(objInfo.UserDefined)
|
||||
@@ -314,7 +314,7 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
cacheReader.Close()
|
||||
}
|
||||
c.cacheStats.incMiss()
|
||||
return c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts)
|
||||
return c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, opts)
|
||||
}
|
||||
if cacheErr == nil {
|
||||
// if ETag matches for stale cache entry, serve from cache
|
||||
@@ -332,7 +332,7 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
// Reaching here implies cache miss
|
||||
c.cacheStats.incMiss()
|
||||
|
||||
bkReader, bkErr := c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts)
|
||||
bkReader, bkErr := c.InnerGetObjectNInfoFn(ctx, bucket, object, rs, h, opts)
|
||||
|
||||
if bkErr != nil {
|
||||
return bkReader, bkErr
|
||||
@@ -359,7 +359,7 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
// if range caching is disabled, download entire object.
|
||||
rs = nil
|
||||
// fill cache in the background for range GET requests
|
||||
bReader, bErr := c.InnerGetObjectNInfoFn(GlobalContext, bucket, object, rs, h, lockType, opts)
|
||||
bReader, bErr := c.InnerGetObjectNInfoFn(GlobalContext, bucket, object, rs, h, opts)
|
||||
if bErr != nil {
|
||||
return
|
||||
}
|
||||
@@ -713,7 +713,7 @@ func (c *cacheObjects) PutObject(ctx context.Context, bucket, object string, r *
|
||||
if err == nil {
|
||||
go func() {
|
||||
// fill cache in the background
|
||||
bReader, bErr := c.InnerGetObjectNInfoFn(GlobalContext, bucket, object, nil, http.Header{}, readLock, ObjectOptions{})
|
||||
bReader, bErr := c.InnerGetObjectNInfoFn(GlobalContext, bucket, object, nil, http.Header{}, ObjectOptions{})
|
||||
if bErr != nil {
|
||||
return
|
||||
}
|
||||
@@ -857,8 +857,8 @@ func newServerCacheObjects(ctx context.Context, config cache.Config) (CacheObjec
|
||||
InnerGetObjectInfoFn: func(ctx context.Context, bucket, object string, opts ObjectOptions) (ObjectInfo, error) {
|
||||
return newObjectLayerFn().GetObjectInfo(ctx, bucket, object, opts)
|
||||
},
|
||||
InnerGetObjectNInfoFn: func(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
return newObjectLayerFn().GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts)
|
||||
InnerGetObjectNInfoFn: func(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
return newObjectLayerFn().GetObjectNInfo(ctx, bucket, object, rs, h, opts)
|
||||
},
|
||||
InnerDeleteObjectFn: func(ctx context.Context, bucket, object string, opts ObjectOptions) (ObjectInfo, error) {
|
||||
return newObjectLayerFn().DeleteObject(ctx, bucket, object, opts)
|
||||
@@ -1143,7 +1143,7 @@ func (c *cacheObjects) CopyObjectPart(ctx context.Context, srcBucket, srcObject,
|
||||
End: startOffset + length,
|
||||
}
|
||||
// fill cache in the background
|
||||
bReader, bErr := c.InnerGetObjectNInfoFn(GlobalContext, srcBucket, srcObject, rs, http.Header{}, readLock, ObjectOptions{})
|
||||
bReader, bErr := c.InnerGetObjectNInfoFn(GlobalContext, srcBucket, srcObject, rs, http.Header{}, ObjectOptions{})
|
||||
if bErr != nil {
|
||||
return
|
||||
}
|
||||
@@ -1176,7 +1176,7 @@ func (c *cacheObjects) CompleteMultipartUpload(ctx context.Context, bucket, obje
|
||||
_, 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{})
|
||||
bReader, bErr := c.InnerGetObjectNInfoFn(GlobalContext, bucket, object, nil, http.Header{}, ObjectOptions{})
|
||||
if bErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -655,3 +655,133 @@ func TestDisksWithAllParts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommonParities(t *testing.T) {
|
||||
// This test uses two FileInfo values that represent the same object but
|
||||
// have different parities. They occur in equal number of drives, but only
|
||||
// one has read quorum. commonParity should pick the parity corresponding to
|
||||
// the FileInfo which has read quorum.
|
||||
fi1 := FileInfo{
|
||||
Volume: "mybucket",
|
||||
Name: "myobject",
|
||||
VersionID: "",
|
||||
IsLatest: true,
|
||||
Deleted: false,
|
||||
ExpireRestored: false,
|
||||
DataDir: "4a01d9dd-0c5e-4103-88f8-b307c57d212e",
|
||||
XLV1: false,
|
||||
ModTime: time.Date(2023, time.March, 15, 11, 18, 4, 989906961, time.UTC),
|
||||
Size: 329289, Mode: 0x0, WrittenByVersion: 0x63c77756,
|
||||
Metadata: map[string]string{
|
||||
"content-type": "application/octet-stream", "etag": "f205307ef9f50594c4b86d9c246bee86", "x-minio-internal-erasure-upgraded": "5->6", "x-minio-internal-inline-data": "true",
|
||||
},
|
||||
Parts: []ObjectPartInfo{
|
||||
{
|
||||
ETag: "",
|
||||
Number: 1,
|
||||
Size: 329289,
|
||||
ActualSize: 329289,
|
||||
ModTime: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
Index: []uint8(nil),
|
||||
Checksums: map[string]string(nil),
|
||||
},
|
||||
},
|
||||
Erasure: ErasureInfo{
|
||||
Algorithm: "ReedSolomon",
|
||||
DataBlocks: 6,
|
||||
ParityBlocks: 6,
|
||||
BlockSize: 1048576,
|
||||
Index: 1,
|
||||
Distribution: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
|
||||
Checksums: []ChecksumInfo{{PartNumber: 1, Algorithm: 0x3, Hash: []uint8{}}},
|
||||
},
|
||||
NumVersions: 1,
|
||||
Idx: 0,
|
||||
}
|
||||
|
||||
fi2 := FileInfo{
|
||||
Volume: "mybucket",
|
||||
Name: "myobject",
|
||||
VersionID: "",
|
||||
IsLatest: true,
|
||||
Deleted: false,
|
||||
DataDir: "6f5c106d-9d28-4c85-a7f4-eac56225876b",
|
||||
ModTime: time.Date(2023, time.March, 15, 19, 57, 30, 492530160, time.UTC),
|
||||
Size: 329289,
|
||||
Mode: 0x0,
|
||||
WrittenByVersion: 0x63c77756,
|
||||
Metadata: map[string]string{"content-type": "application/octet-stream", "etag": "f205307ef9f50594c4b86d9c246bee86", "x-minio-internal-inline-data": "true"},
|
||||
Parts: []ObjectPartInfo{
|
||||
{
|
||||
ETag: "",
|
||||
Number: 1,
|
||||
Size: 329289,
|
||||
ActualSize: 329289,
|
||||
ModTime: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
Index: []uint8(nil),
|
||||
Checksums: map[string]string(nil),
|
||||
},
|
||||
},
|
||||
Erasure: ErasureInfo{
|
||||
Algorithm: "ReedSolomon",
|
||||
DataBlocks: 7,
|
||||
ParityBlocks: 5,
|
||||
BlockSize: 1048576,
|
||||
Index: 2,
|
||||
Distribution: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
|
||||
Checksums: []ChecksumInfo{
|
||||
{PartNumber: 1, Algorithm: 0x3, Hash: []uint8{}},
|
||||
},
|
||||
},
|
||||
NumVersions: 1,
|
||||
Idx: 0,
|
||||
}
|
||||
|
||||
fiDel := FileInfo{
|
||||
Volume: "mybucket",
|
||||
Name: "myobject",
|
||||
VersionID: "",
|
||||
IsLatest: true,
|
||||
Deleted: true,
|
||||
ModTime: time.Date(2023, time.March, 15, 19, 57, 30, 492530160, time.UTC),
|
||||
Mode: 0x0,
|
||||
WrittenByVersion: 0x63c77756,
|
||||
NumVersions: 1,
|
||||
Idx: 0,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
fi1, fi2 FileInfo
|
||||
}{
|
||||
{
|
||||
fi1: fi1,
|
||||
fi2: fi2,
|
||||
},
|
||||
{
|
||||
fi1: fi1,
|
||||
fi2: fiDel,
|
||||
},
|
||||
}
|
||||
for idx, test := range tests {
|
||||
var metaArr []FileInfo
|
||||
for i := 0; i < 12; i++ {
|
||||
fi := test.fi1
|
||||
if i%2 == 0 {
|
||||
fi = test.fi2
|
||||
}
|
||||
metaArr = append(metaArr, fi)
|
||||
}
|
||||
|
||||
parities := listObjectParities(metaArr, make([]error, len(metaArr)))
|
||||
parity := commonParity(parities, 5)
|
||||
var match int
|
||||
for _, fi := range metaArr {
|
||||
if fi.Erasure.ParityBlocks == parity {
|
||||
match++
|
||||
}
|
||||
}
|
||||
if match < len(metaArr)-parity {
|
||||
t.Fatalf("Test %d: Expected %d drives with parity=%d, but got %d", idx, len(metaArr)-parity, parity, match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,6 +294,26 @@ func shouldHealObjectOnDisk(erErr, dataErr error, meta FileInfo, latestMeta File
|
||||
return false
|
||||
}
|
||||
|
||||
const xMinIOHealing = ReservedMetadataPrefix + "healing"
|
||||
|
||||
// SetHealing marks object (version) as being healed.
|
||||
// Note: this is to be used only from healObject
|
||||
func (fi *FileInfo) SetHealing() {
|
||||
if fi.Metadata == nil {
|
||||
fi.Metadata = make(map[string]string)
|
||||
}
|
||||
fi.Metadata[xMinIOHealing] = "true"
|
||||
}
|
||||
|
||||
// Healing returns true if object is being healed (i.e fi is being passed down
|
||||
// from healObject)
|
||||
func (fi FileInfo) Healing() bool {
|
||||
if _, ok := fi.Metadata[xMinIOHealing]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Heals an object by re-writing corrupt/missing erasure blocks.
|
||||
func (er *erasureObjects) healObject(ctx context.Context, bucket string, object string, versionID string, opts madmin.HealOpts) (result madmin.HealResultItem, err error) {
|
||||
dryRun := opts.DryRun
|
||||
@@ -664,6 +684,7 @@ func (er *erasureObjects) healObject(ctx context.Context, bucket string, object
|
||||
partsMetadata[i].Erasure.Index = i + 1
|
||||
|
||||
// Attempt a rename now from healed data to final location.
|
||||
partsMetadata[i].SetHealing()
|
||||
if _, err = disk.RenameData(ctx, minioMetaTmpBucket, tmpID, partsMetadata[i], bucket, object); err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
return result, err
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/dustin/go-humanize"
|
||||
uuid2 "github.com/google/uuid"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/minio/internal/config/storageclass"
|
||||
)
|
||||
|
||||
// Tests isObjectDangling function
|
||||
@@ -563,6 +564,17 @@ func TestHealingDanglingObject(t *testing.T) {
|
||||
resetGlobalHealState()
|
||||
defer resetGlobalHealState()
|
||||
|
||||
// Set globalStoragClass.STANDARD to EC:4 for this test
|
||||
saveSC := globalStorageClass
|
||||
defer func() {
|
||||
globalStorageClass = saveSC
|
||||
}()
|
||||
globalStorageClass = storageclass.Config{
|
||||
Standard: storageclass.StorageClass{
|
||||
Parity: 4,
|
||||
},
|
||||
}
|
||||
|
||||
nDisks := 16
|
||||
fsDirs, err := getRandomDisks(nDisks)
|
||||
if err != nil {
|
||||
@@ -577,6 +589,8 @@ func TestHealingDanglingObject(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
setObjectLayer(objLayer)
|
||||
|
||||
bucket := getRandomBucketName()
|
||||
object := getRandomObjectName()
|
||||
data := bytes.Repeat([]byte("a"), 128*1024)
|
||||
@@ -1620,7 +1634,7 @@ func TestHealLastDataShard(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
firstGr, err := obj.GetObjectNInfo(ctx, bucket, object, nil, nil, noLock, ObjectOptions{})
|
||||
firstGr, err := obj.GetObjectNInfo(ctx, bucket, object, nil, nil, ObjectOptions{NoLock: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1650,7 +1664,7 @@ func TestHealLastDataShard(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
secondGr, err := obj.GetObjectNInfo(ctx, bucket, object, nil, nil, noLock, ObjectOptions{})
|
||||
secondGr, err := obj.GetObjectNInfo(ctx, bucket, object, nil, nil, ObjectOptions{NoLock: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
+18
-5
@@ -234,6 +234,7 @@ func (fi FileInfo) ToObjectInfo(bucket, object string, versioned bool) ObjectInf
|
||||
}
|
||||
}
|
||||
objInfo.Checksum = fi.Checksum
|
||||
objInfo.Inlined = fi.InlineData()
|
||||
// Success.
|
||||
return objInfo
|
||||
}
|
||||
@@ -444,20 +445,33 @@ func writeUniqueFileInfo(ctx context.Context, disks []StorageAPI, bucket, prefix
|
||||
return evalDisks(disks, mErrs), err
|
||||
}
|
||||
|
||||
func commonParity(parities []int) int {
|
||||
func commonParity(parities []int, defaultParityCount int) int {
|
||||
N := len(parities)
|
||||
|
||||
occMap := make(map[int]int)
|
||||
for _, p := range parities {
|
||||
occMap[p]++
|
||||
}
|
||||
|
||||
var maxOcc, commonParity int
|
||||
|
||||
for parity, occ := range occMap {
|
||||
if parity == -1 {
|
||||
// Ignore non defined parity
|
||||
continue
|
||||
}
|
||||
if occ >= maxOcc {
|
||||
|
||||
readQuorum := N - parity
|
||||
if defaultParityCount > 0 && parity == 0 {
|
||||
// In this case, parity == 0 implies that this object version is a
|
||||
// delete marker
|
||||
readQuorum = N/2 + 1
|
||||
}
|
||||
if occ < readQuorum {
|
||||
// Ignore this parity since we don't have enough shards for read quorum
|
||||
continue
|
||||
}
|
||||
|
||||
if occ > maxOcc {
|
||||
maxOcc = occ
|
||||
commonParity = parity
|
||||
}
|
||||
@@ -510,8 +524,7 @@ func objectQuorumFromMeta(ctx context.Context, partsMetaData []FileInfo, errs []
|
||||
}
|
||||
|
||||
parities := listObjectParities(partsMetaData, errs)
|
||||
parityBlocks := commonParity(parities)
|
||||
|
||||
parityBlocks := commonParity(parities, defaultParityCount)
|
||||
if parityBlocks < 0 {
|
||||
return -1, -1, errErasureReadQuorum
|
||||
}
|
||||
|
||||
@@ -1137,8 +1137,9 @@ func (er erasureObjects) CompleteMultipartUpload(ctx context.Context, bucket str
|
||||
}
|
||||
}
|
||||
if checksumType.IsSet() {
|
||||
checksumType |= hash.ChecksumMultipart
|
||||
cs := hash.NewChecksumFromData(checksumType, checksumCombined)
|
||||
fi.Checksum = cs.AppendTo(nil)
|
||||
fi.Checksum = cs.AppendTo(nil, len(fi.Parts))
|
||||
if opts.EncryptFn != nil {
|
||||
fi.Checksum = opts.EncryptFn("object-checksum", fi.Checksum)
|
||||
}
|
||||
@@ -1226,7 +1227,7 @@ func (er erasureObjects) CompleteMultipartUpload(ctx context.Context, bucket str
|
||||
if disk != nil && disk.IsOnline() {
|
||||
continue
|
||||
}
|
||||
er.addPartial(bucket, object, fi.VersionID, fi.Size)
|
||||
er.addPartial(bucket, object, fi.VersionID)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
+39
-30
@@ -29,6 +29,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/klauspost/readahead"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
@@ -183,7 +184,7 @@ func (er erasureObjects) CopyObject(ctx context.Context, srcBucket, srcObject, d
|
||||
|
||||
// GetObjectNInfo - returns object info and an object
|
||||
// Read(Closer). When err != nil, the returned reader is always nil.
|
||||
func (er erasureObjects) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
func (er erasureObjects) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
auditObjectErasureSet(ctx, object, &er)
|
||||
|
||||
// This is a special call attempted first to check for SOS-API calls.
|
||||
@@ -203,25 +204,26 @@ func (er erasureObjects) GetObjectNInfo(ctx context.Context, bucket, object stri
|
||||
}()
|
||||
|
||||
// Acquire lock
|
||||
if lockType != noLock {
|
||||
if !opts.NoLock {
|
||||
lock := er.NewNSLock(bucket, object)
|
||||
switch lockType {
|
||||
case writeLock:
|
||||
lkctx, err := lock.GetLock(ctx, globalOperationTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = lkctx.Context()
|
||||
nsUnlocker = func() { lock.Unlock(lkctx) }
|
||||
case readLock:
|
||||
lkctx, err := lock.GetRLock(ctx, globalOperationTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = lkctx.Context()
|
||||
nsUnlocker = func() { lock.RUnlock(lkctx) }
|
||||
lkctx, err := lock.GetRLock(ctx, globalOperationTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = lkctx.Context()
|
||||
|
||||
// Release lock when the metadata is verified, and reader
|
||||
// is ready to be read.
|
||||
//
|
||||
// This is possible to be lock free because
|
||||
// - xl.meta for inlined objects has already read the data
|
||||
// into memory, any mutation on xl.meta subsequently is
|
||||
// inconsequential to the overall read operation.
|
||||
// - xl.meta metadata is still verified for quorum under lock()
|
||||
// however writing the response doesn't need to serialize
|
||||
// concurrent writers
|
||||
unlockOnDefer = true
|
||||
nsUnlocker = func() { lock.RUnlock(lkctx) }
|
||||
}
|
||||
|
||||
fi, metaArr, onlineDisks, err := er.getObjectFileInfo(ctx, bucket, object, opts, true)
|
||||
@@ -265,6 +267,7 @@ func (er erasureObjects) GetObjectNInfo(ctx context.Context, bucket, object stri
|
||||
ObjInfo: objInfo,
|
||||
}, toObjectErr(errMethodNotAllowed, bucket, object)
|
||||
}
|
||||
|
||||
if objInfo.IsRemote() {
|
||||
gr, err := getTransitionedObjectReader(ctx, bucket, object, rs, h, objInfo, opts)
|
||||
if err != nil {
|
||||
@@ -278,7 +281,10 @@ func (er erasureObjects) GetObjectNInfo(ctx context.Context, bucket, object stri
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unlockOnDefer = false
|
||||
|
||||
if unlockOnDefer {
|
||||
unlockOnDefer = fi.InlineData()
|
||||
}
|
||||
|
||||
pr, pw := xioutil.WaitPipe()
|
||||
go func() {
|
||||
@@ -291,7 +297,11 @@ func (er erasureObjects) GetObjectNInfo(ctx context.Context, bucket, object stri
|
||||
pr.CloseWithError(nil)
|
||||
}
|
||||
|
||||
return fn(pr, h, pipeCloser, nsUnlocker)
|
||||
if !unlockOnDefer {
|
||||
return fn(pr, h, pipeCloser, nsUnlocker)
|
||||
}
|
||||
|
||||
return fn(pr, h, pipeCloser)
|
||||
}
|
||||
|
||||
func (er erasureObjects) getObjectWithFileInfo(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, fi FileInfo, metaArr []FileInfo, onlineDisks []StorageAPI) error {
|
||||
@@ -1081,7 +1091,7 @@ func (er erasureObjects) putObject(ctx context.Context, bucket string, object st
|
||||
}
|
||||
|
||||
fi.DataDir = mustGetUUID()
|
||||
fi.Checksum = opts.WantChecksum.AppendTo(nil)
|
||||
fi.Checksum = opts.WantChecksum.AppendTo(nil, 0)
|
||||
if opts.EncryptFn != nil {
|
||||
fi.Checksum = opts.EncryptFn("object-checksum", fi.Checksum)
|
||||
}
|
||||
@@ -1290,7 +1300,7 @@ func (er erasureObjects) putObject(ctx context.Context, bucket string, object st
|
||||
continue
|
||||
}
|
||||
|
||||
er.addPartial(bucket, object, fi.VersionID, fi.Size)
|
||||
er.addPartial(bucket, object, fi.VersionID)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1482,7 +1492,7 @@ func (er erasureObjects) DeleteObjects(ctx context.Context, bucket string, objec
|
||||
if errs[i] != nil && !isErrVersionNotFound(errs[i]) && !isErrObjectNotFound(errs[i]) {
|
||||
// all other direct versionId references we should
|
||||
// ensure no dangling file is left over.
|
||||
er.addPartial(bucket, dobj.ObjectName, dobj.VersionID, -1)
|
||||
er.addPartial(bucket, dobj.ObjectName, dobj.VersionID)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1495,7 +1505,7 @@ func (er erasureObjects) DeleteObjects(ctx context.Context, bucket string, objec
|
||||
|
||||
// all other direct versionId references we should
|
||||
// ensure no dangling file is left over.
|
||||
er.addPartial(bucket, dobj.ObjectName, dobj.VersionID, -1)
|
||||
er.addPartial(bucket, dobj.ObjectName, dobj.VersionID)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1658,7 +1668,7 @@ func (er erasureObjects) DeleteObject(ctx context.Context, bucket, object string
|
||||
if disk != nil && disk.IsOnline() {
|
||||
continue
|
||||
}
|
||||
er.addPartial(bucket, object, opts.VersionID, -1)
|
||||
er.addPartial(bucket, object, opts.VersionID)
|
||||
break
|
||||
}
|
||||
}()
|
||||
@@ -1715,14 +1725,12 @@ func (er erasureObjects) DeleteObject(ctx context.Context, bucket, object string
|
||||
|
||||
// Send the successful but partial upload/delete, however ignore
|
||||
// if the channel is blocked by other items.
|
||||
func (er erasureObjects) addPartial(bucket, object, versionID string, size int64) {
|
||||
func (er erasureObjects) addPartial(bucket, object, versionID string) {
|
||||
globalMRFState.addPartialOp(partialOperation{
|
||||
bucket: bucket,
|
||||
object: object,
|
||||
versionID: versionID,
|
||||
size: size,
|
||||
setIndex: er.setIndex,
|
||||
poolIndex: er.poolIndex,
|
||||
queued: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1953,6 +1961,7 @@ func (er erasureObjects) TransitionObject(ctx context.Context, bucket, object st
|
||||
return toObjectErr(err, bucket, object)
|
||||
}
|
||||
}
|
||||
traceFn := globalLifecycleSys.trace(fi.ToObjectInfo(bucket, object, opts.Versioned || opts.VersionSuspended))
|
||||
|
||||
destObj, err := genTransitionObjName(bucket)
|
||||
if err != nil {
|
||||
@@ -1988,7 +1997,7 @@ func (er erasureObjects) TransitionObject(ctx context.Context, bucket, object st
|
||||
if disk != nil && disk.IsOnline() {
|
||||
continue
|
||||
}
|
||||
er.addPartial(bucket, object, opts.VersionID, -1)
|
||||
er.addPartial(bucket, object, opts.VersionID)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2000,7 +2009,7 @@ func (er erasureObjects) TransitionObject(ctx context.Context, bucket, object st
|
||||
UserAgent: "Internal: [ILM-Transition]",
|
||||
Host: globalLocalNodeName,
|
||||
})
|
||||
auditLogLifecycle(ctx, objInfo, ILMTransition)
|
||||
auditLogLifecycle(ctx, objInfo, ILMTransition, traceFn)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -532,7 +532,7 @@ func TestGetObjectNoQuorum(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
gr, err := xl.GetObjectNInfo(ctx, bucket, object, nil, nil, readLock, opts)
|
||||
gr, err := xl.GetObjectNInfo(ctx, bucket, object, nil, nil, opts)
|
||||
if err != nil {
|
||||
if err != toObjectErr(errErasureReadQuorum, bucket, object) {
|
||||
t.Errorf("Expected GetObject to fail with %v, but failed with %v", toObjectErr(errErasureReadQuorum, bucket, object), err)
|
||||
@@ -577,7 +577,7 @@ func TestGetObjectNoQuorum(t *testing.T) {
|
||||
}
|
||||
z.serverPools[0].erasureDisksMu.Unlock()
|
||||
// Fetch object from store.
|
||||
gr, err := xl.GetObjectNInfo(ctx, bucket, object, nil, nil, readLock, opts)
|
||||
gr, err := xl.GetObjectNInfo(ctx, bucket, object, nil, nil, opts)
|
||||
if err != nil {
|
||||
if err != toObjectErr(errErasureReadQuorum, bucket, object) {
|
||||
t.Errorf("Expected GetObject to fail with %v, but failed with %v", toObjectErr(errErasureReadQuorum, bucket, object), err)
|
||||
@@ -831,7 +831,7 @@ func TestPutObjectSmallInlineData(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gr, err := obj.GetObjectNInfo(ctx, bucket, object, nil, nil, readLock, ObjectOptions{})
|
||||
gr, err := obj.GetObjectNInfo(ctx, bucket, object, nil, nil, ObjectOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Expected GetObject to succeed, but failed with %v", err)
|
||||
}
|
||||
@@ -855,7 +855,7 @@ func TestPutObjectSmallInlineData(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gr, err = obj.GetObjectNInfo(ctx, bucket, object, nil, nil, readLock, ObjectOptions{})
|
||||
gr, err = obj.GetObjectNInfo(ctx, bucket, object, nil, nil, ObjectOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Expected GetObject to succeed, but failed with %v", err)
|
||||
}
|
||||
@@ -1099,7 +1099,7 @@ func TestGetObjectInlineNotInline(t *testing.T) {
|
||||
}
|
||||
|
||||
// Try to read the object and check its md5sum
|
||||
gr, err := objLayer.GetObjectNInfo(ctx, "testbucket", "file", nil, nil, readLock, ObjectOptions{})
|
||||
gr, err := objLayer.GetObjectNInfo(ctx, "testbucket", "file", nil, nil, ObjectOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Expected GetObject to succeed, but failed with %v", err)
|
||||
}
|
||||
@@ -1193,7 +1193,7 @@ func TestGetObjectWithOutdatedDisks(t *testing.T) {
|
||||
sets.erasureDisksMu.Lock()
|
||||
xl.getDisks = func() []StorageAPI { return origErasureDisks }
|
||||
sets.erasureDisksMu.Unlock()
|
||||
gr, err := z.GetObjectNInfo(ctx, testCase.bucket, testCase.object, nil, nil, readLock, ObjectOptions{VersionID: got.VersionID})
|
||||
gr, err := z.GetObjectNInfo(ctx, testCase.bucket, testCase.object, nil, nil, ObjectOptions{VersionID: got.VersionID})
|
||||
if err != nil {
|
||||
t.Fatalf("Expected GetObject to succeed, but failed with %v", err)
|
||||
}
|
||||
|
||||
@@ -838,10 +838,10 @@ func (z *erasureServerPools) decommissionPool(ctx context.Context, idx int, pool
|
||||
encodeDirObject(version.Name),
|
||||
nil,
|
||||
http.Header{},
|
||||
noLock, // all mutations are blocked reads are safe without locks.
|
||||
ObjectOptions{
|
||||
VersionID: version.VersionID,
|
||||
NoDecryption: true,
|
||||
NoLock: true,
|
||||
})
|
||||
if isErrObjectNotFound(err) || isErrVersionNotFound(err) {
|
||||
// object deleted by the application, nothing to do here we move on.
|
||||
|
||||
@@ -550,10 +550,10 @@ func (z *erasureServerPools) rebalanceBucket(ctx context.Context, bucket string,
|
||||
encodeDirObject(version.Name),
|
||||
nil,
|
||||
http.Header{},
|
||||
noLock, // all mutations are blocked reads are safe without locks.
|
||||
ObjectOptions{
|
||||
VersionID: version.VersionID,
|
||||
NoDecryption: true,
|
||||
NoLock: true,
|
||||
})
|
||||
if isErrObjectNotFound(err) || isErrVersionNotFound(err) {
|
||||
// object deleted by the application, nothing to do here we move on.
|
||||
|
||||
+35
-35
@@ -432,30 +432,37 @@ func (z *erasureServerPools) getPoolInfoExistingWithOpts(ctx context.Context, bu
|
||||
for _, pinfo := range poolObjInfos {
|
||||
// skip all objects from suspended pools if asked by the
|
||||
// caller.
|
||||
if z.IsSuspended(pinfo.Index) && opts.SkipDecommissioned {
|
||||
if opts.SkipDecommissioned && z.IsSuspended(pinfo.Index) {
|
||||
continue
|
||||
}
|
||||
// Skip object if it's from pools participating in a rebalance operation.
|
||||
if opts.SkipRebalancing && z.IsPoolRebalancing(pinfo.Index) {
|
||||
continue
|
||||
}
|
||||
|
||||
if pinfo.Err != nil && !isErrObjectNotFound(pinfo.Err) {
|
||||
return pinfo, pinfo.Err
|
||||
if pinfo.Err == nil {
|
||||
// found a pool
|
||||
return pinfo, nil
|
||||
}
|
||||
if isErrReadQuorum(pinfo.Err) {
|
||||
// read quorum is returned when the object is visibly
|
||||
// present but its unreadable, we simply ask the writes to
|
||||
// schedule to this pool instead. If there is no quorum
|
||||
// it will fail anyways, however if there is quorum available
|
||||
// with enough disks online but sufficiently inconsistent to
|
||||
// break parity threshold, allow them to be overwritten
|
||||
// or allow new versions to be added.
|
||||
return pinfo, nil
|
||||
}
|
||||
defPool = pinfo
|
||||
if isErrObjectNotFound(pinfo.Err) {
|
||||
// No object exists or its a delete marker,
|
||||
// check objInfo to confirm.
|
||||
if pinfo.ObjInfo.DeleteMarker && pinfo.ObjInfo.Name != "" {
|
||||
return pinfo, nil
|
||||
}
|
||||
// objInfo is not valid, truly the object doesn't
|
||||
// exist proceed to next pool.
|
||||
continue
|
||||
if !isErrObjectNotFound(pinfo.Err) {
|
||||
return pinfo, pinfo.Err
|
||||
}
|
||||
|
||||
return pinfo, nil
|
||||
// No object exists or its a delete marker,
|
||||
// check objInfo to confirm.
|
||||
if pinfo.ObjInfo.DeleteMarker && pinfo.ObjInfo.Name != "" {
|
||||
return pinfo, nil
|
||||
}
|
||||
}
|
||||
if opts.ReplicationRequest && opts.DeleteMarker && defPool.Index >= 0 {
|
||||
// If the request is a delete marker replication request, return a default pool
|
||||
@@ -766,7 +773,7 @@ func (z *erasureServerPools) MakeBucket(ctx context.Context, bucket string, opts
|
||||
return nil
|
||||
}
|
||||
|
||||
func (z *erasureServerPools) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
func (z *erasureServerPools) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
if err = checkGetObjArgs(ctx, bucket, object); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -774,7 +781,7 @@ func (z *erasureServerPools) GetObjectNInfo(ctx context.Context, bucket, object
|
||||
object = encodeDirObject(object)
|
||||
|
||||
if z.SinglePool() {
|
||||
return z.serverPools[0].GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts)
|
||||
return z.serverPools[0].GetObjectNInfo(ctx, bucket, object, rs, h, opts)
|
||||
}
|
||||
|
||||
var unlockOnDefer bool
|
||||
@@ -786,24 +793,14 @@ func (z *erasureServerPools) GetObjectNInfo(ctx context.Context, bucket, object
|
||||
}()
|
||||
|
||||
// Acquire lock
|
||||
if lockType != noLock {
|
||||
if !opts.NoLock {
|
||||
lock := z.NewNSLock(bucket, object)
|
||||
switch lockType {
|
||||
case writeLock:
|
||||
lkctx, err := lock.GetLock(ctx, globalOperationTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = lkctx.Context()
|
||||
nsUnlocker = func() { lock.Unlock(lkctx) }
|
||||
case readLock:
|
||||
lkctx, err := lock.GetRLock(ctx, globalOperationTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = lkctx.Context()
|
||||
nsUnlocker = func() { lock.RUnlock(lkctx) }
|
||||
lkctx, err := lock.GetRLock(ctx, globalOperationTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = lkctx.Context()
|
||||
nsUnlocker = func() { lock.RUnlock(lkctx) }
|
||||
unlockOnDefer = true
|
||||
}
|
||||
|
||||
@@ -831,14 +828,17 @@ func (z *erasureServerPools) GetObjectNInfo(ctx context.Context, bucket, object
|
||||
return nil, PreConditionFailed{}
|
||||
}
|
||||
|
||||
lockType = noLock // do not take locks at lower levels for GetObjectNInfo()
|
||||
gr, err = z.serverPools[zIdx].GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts)
|
||||
opts.NoLock = true
|
||||
gr, err = z.serverPools[zIdx].GetObjectNInfo(ctx, bucket, object, rs, h, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if unlockOnDefer {
|
||||
unlockOnDefer = false
|
||||
unlockOnDefer = gr.ObjInfo.Inlined
|
||||
}
|
||||
|
||||
if !unlockOnDefer {
|
||||
return gr.WithCleanupFuncs(nsUnlocker), nil
|
||||
}
|
||||
return gr, nil
|
||||
|
||||
+2
-15
@@ -190,7 +190,6 @@ func (s *erasureSets) connectDisks() {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
diskMap := s.getDiskMap()
|
||||
setsJustConnected := make([]bool, s.setCount)
|
||||
for _, endpoint := range s.endpoints.Endpoints {
|
||||
cdisk := diskMap[endpoint]
|
||||
if cdisk != nil && cdisk.IsOnline() {
|
||||
@@ -203,8 +202,6 @@ func (s *erasureSets) connectDisks() {
|
||||
// putting it back into the s.erasureDisks by re-placing the disk again.
|
||||
_, setIndex, _ := cdisk.GetDiskLoc()
|
||||
if setIndex != -1 {
|
||||
// Recently disconnected disks must go to MRF
|
||||
setsJustConnected[setIndex] = cdisk.LastConn().After(s.lastConnectDisksOpTime)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -260,21 +257,11 @@ func (s *erasureSets) connectDisks() {
|
||||
s.erasureDisks[setIndex][diskIndex] = disk
|
||||
}
|
||||
disk.SetDiskLoc(s.poolIndex, setIndex, diskIndex)
|
||||
setsJustConnected[setIndex] = true // disk just went online we treat it is as MRF event
|
||||
s.erasureDisksMu.Unlock()
|
||||
}(endpoint)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
go func() {
|
||||
for setIndex, justConnected := range setsJustConnected {
|
||||
if !justConnected {
|
||||
continue
|
||||
}
|
||||
globalMRFState.newSetReconnected(s.poolIndex, setIndex)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// monitorAndConnectEndpoints this is a monitoring loop to keep track of disconnected
|
||||
@@ -756,9 +743,9 @@ func listDeletedBuckets(ctx context.Context, storageDisks []StorageAPI, delBucke
|
||||
// --- Object Operations ---
|
||||
|
||||
// GetObjectNInfo - returns object info and locked object ReadCloser
|
||||
func (s *erasureSets) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
func (s *erasureSets) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (gr *GetObjectReader, err error) {
|
||||
set := s.getHashedSet(object)
|
||||
return set.GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts)
|
||||
return set.GetObjectNInfo(ctx, bucket, object, rs, h, opts)
|
||||
}
|
||||
|
||||
// PutObject - writes an object to hashedSet based on the object name.
|
||||
|
||||
@@ -207,6 +207,7 @@ func getDisksInfo(disks []StorageAPI, endpoints []Endpoint) (disksInfo []madmin.
|
||||
Scanning: info.Scanning,
|
||||
State: diskErrToDriveState(err),
|
||||
FreeInodes: info.FreeInodes,
|
||||
UsedInodes: info.UsedInodes,
|
||||
}
|
||||
di.PoolIndex, di.SetIndex, di.DiskIndex = disks[index].GetDiskLoc()
|
||||
if info.Healing {
|
||||
|
||||
@@ -380,7 +380,7 @@ func saveFormatErasure(disk StorageAPI, format *formatErasureV3, healID string)
|
||||
disk.SetDiskID(diskID)
|
||||
if healID != "" {
|
||||
ctx := context.Background()
|
||||
ht := newHealingTracker(disk, healID)
|
||||
ht := initHealingTracker(disk, healID)
|
||||
return ht.save(ctx)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,505 @@
|
||||
// Copyright (c) 2015-2023 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
ftp "goftp.io/server/v2"
|
||||
)
|
||||
|
||||
var _ ftp.Driver = &ftpDriver{}
|
||||
|
||||
// ftpDriver implements ftpDriver to store files in minio
|
||||
type ftpDriver struct {
|
||||
endpoint string
|
||||
}
|
||||
|
||||
// NewFTPDriver implements ftp.Driver interface
|
||||
func NewFTPDriver() ftp.Driver {
|
||||
return &ftpDriver{endpoint: fmt.Sprintf("127.0.0.1:%s", globalMinioPort)}
|
||||
}
|
||||
|
||||
func buildMinioPath(p string) string {
|
||||
return strings.TrimPrefix(p, SlashSeparator)
|
||||
}
|
||||
|
||||
func buildMinioDir(p string) string {
|
||||
v := buildMinioPath(p)
|
||||
if !strings.HasSuffix(v, SlashSeparator) {
|
||||
return v + SlashSeparator
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
type minioFileInfo struct {
|
||||
p string
|
||||
info minio.ObjectInfo
|
||||
isDir bool
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) Name() string {
|
||||
return m.p
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) Size() int64 {
|
||||
return m.info.Size
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) Mode() os.FileMode {
|
||||
if m.isDir {
|
||||
return os.ModeDir
|
||||
}
|
||||
return os.ModePerm
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) ModTime() time.Time {
|
||||
return m.info.LastModified
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) IsDir() bool {
|
||||
return m.isDir
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
//msgp:ignore ftpMetrics
|
||||
type ftpMetrics struct{}
|
||||
|
||||
var globalFtpMetrics ftpMetrics
|
||||
|
||||
func ftpTrace(s *ftp.Context, startTime time.Time, source, path string, err error) madmin.TraceInfo {
|
||||
var errStr string
|
||||
if err != nil {
|
||||
errStr = err.Error()
|
||||
}
|
||||
return madmin.TraceInfo{
|
||||
TraceType: madmin.TraceFTP,
|
||||
Time: startTime,
|
||||
NodeName: globalLocalNodeName,
|
||||
FuncName: fmt.Sprintf("ftp USER=%s COMMAND=%s PARAM=%s ISLOGIN=%t, Source=%s", s.Sess.LoginUser(), s.Cmd, s.Param, s.Sess.IsLogin(), source),
|
||||
Duration: time.Since(startTime),
|
||||
Path: path,
|
||||
Error: errStr,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ftpMetrics) log(s *ftp.Context, paths ...string) func(err error) {
|
||||
startTime := time.Now()
|
||||
source := getSource(2)
|
||||
return func(err error) {
|
||||
globalTrace.Publish(ftpTrace(s, startTime, source, strings.Join(paths, " "), err))
|
||||
}
|
||||
}
|
||||
|
||||
// Stat implements ftpDriver
|
||||
func (driver *ftpDriver) Stat(ctx *ftp.Context, path string) (fi os.FileInfo, err error) {
|
||||
stopFn := globalFtpMetrics.log(ctx, path)
|
||||
defer stopFn(err)
|
||||
|
||||
if path == SlashSeparator {
|
||||
return &minioFileInfo{
|
||||
p: SlashSeparator,
|
||||
isDir: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
bucket, object := path2BucketObject(path)
|
||||
if bucket == "" {
|
||||
return nil, errors.New("bucket name cannot be empty")
|
||||
}
|
||||
|
||||
clnt, err := driver.getMinIOClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if object == "" {
|
||||
ok, err := clnt.BucketExists(context.Background(), bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return &minioFileInfo{
|
||||
p: pathClean(bucket),
|
||||
info: minio.ObjectInfo{Key: bucket},
|
||||
isDir: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
objInfo, err := clnt.StatObject(context.Background(), bucket, object, minio.StatObjectOptions{})
|
||||
if err != nil {
|
||||
if minio.ToErrorResponse(err).Code == "NoSuchKey" {
|
||||
// dummy return to satisfy LIST (stat -> list) behavior.
|
||||
return &minioFileInfo{
|
||||
p: pathClean(object),
|
||||
info: minio.ObjectInfo{Key: object},
|
||||
isDir: true,
|
||||
}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isDir := strings.HasSuffix(objInfo.Key, SlashSeparator)
|
||||
return &minioFileInfo{
|
||||
p: pathClean(object),
|
||||
info: objInfo,
|
||||
isDir: isDir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListDir implements ftpDriver
|
||||
func (driver *ftpDriver) ListDir(ctx *ftp.Context, path string, callback func(os.FileInfo) error) (err error) {
|
||||
stopFn := globalFtpMetrics.log(ctx, path)
|
||||
defer stopFn(err)
|
||||
|
||||
clnt, err := driver.getMinIOClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
bucket, prefix := path2BucketObject(path)
|
||||
if bucket == "" {
|
||||
buckets, err := clnt.ListBuckets(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, bucket := range buckets {
|
||||
info := minioFileInfo{
|
||||
p: pathClean(bucket.Name),
|
||||
info: minio.ObjectInfo{Key: retainSlash(bucket.Name), LastModified: bucket.CreationDate},
|
||||
isDir: true,
|
||||
}
|
||||
if err := callback(&info); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
prefix = retainSlash(prefix)
|
||||
|
||||
for object := range clnt.ListObjects(cctx, bucket, minio.ListObjectsOptions{
|
||||
Prefix: prefix,
|
||||
Recursive: false,
|
||||
}) {
|
||||
if object.Err != nil {
|
||||
return object.Err
|
||||
}
|
||||
|
||||
if object.Key == prefix {
|
||||
continue
|
||||
}
|
||||
|
||||
isDir := strings.HasSuffix(object.Key, SlashSeparator)
|
||||
info := minioFileInfo{
|
||||
p: pathClean(strings.TrimPrefix(object.Key, prefix)),
|
||||
info: object,
|
||||
isDir: isDir,
|
||||
}
|
||||
|
||||
if err := callback(&info); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *ftpDriver) CheckPasswd(c *ftp.Context, username, password string) (ok bool, err error) {
|
||||
stopFn := globalFtpMetrics.log(c, username)
|
||||
defer stopFn(err)
|
||||
|
||||
if globalIAMSys.LDAPConfig.Enabled() {
|
||||
ldapUserDN, groupDistNames, err := globalIAMSys.LDAPConfig.Bind(username, password)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ldapPolicies, _ := globalIAMSys.PolicyDBGet(ldapUserDN, false, groupDistNames...)
|
||||
if len(ldapPolicies) == 0 {
|
||||
// no policy associated reject it.
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
ui, ok := globalIAMSys.GetUser(context.Background(), username)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
return subtle.ConstantTimeCompare([]byte(ui.Credentials.SecretKey), []byte(password)) == 1, nil
|
||||
}
|
||||
|
||||
func (driver *ftpDriver) getMinIOClient(ctx *ftp.Context) (*minio.Client, error) {
|
||||
ui, ok := globalIAMSys.GetUser(context.Background(), ctx.Sess.LoginUser())
|
||||
if !ok && !globalIAMSys.LDAPConfig.Enabled() {
|
||||
return nil, errNoSuchUser
|
||||
}
|
||||
if !ok && globalIAMSys.LDAPConfig.Enabled() {
|
||||
targetUser, targetGroups, err := globalIAMSys.LDAPConfig.LookupUserDN(ctx.Sess.LoginUser())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ldapPolicies, _ := globalIAMSys.PolicyDBGet(targetUser, false, targetGroups...)
|
||||
if len(ldapPolicies) == 0 {
|
||||
return nil, errAuthentication
|
||||
}
|
||||
expiryDur, err := globalIAMSys.LDAPConfig.GetExpiryDuration("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
claims := make(map[string]interface{})
|
||||
claims[expClaim] = UTCNow().Add(expiryDur).Unix()
|
||||
claims[ldapUser] = targetUser
|
||||
claims[ldapUserN] = ctx.Sess.LoginUser()
|
||||
|
||||
cred, err := auth.GetNewCredentialsWithMetadata(claims, globalActiveCred.SecretKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set the parent of the temporary access key, this is useful
|
||||
// in obtaining service accounts by this cred.
|
||||
cred.ParentUser = targetUser
|
||||
|
||||
// Set this value to LDAP groups, LDAP user can be part
|
||||
// of large number of groups
|
||||
cred.Groups = targetGroups
|
||||
|
||||
// Set the newly generated credentials, policyName is empty on purpose
|
||||
// LDAP policies are applied automatically using their ldapUser, ldapGroups
|
||||
// mapping.
|
||||
updatedAt, err := globalIAMSys.SetTempUser(context.Background(), cred.AccessKey, cred, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Call hook for site replication.
|
||||
logger.LogIf(context.Background(), globalSiteReplicationSys.IAMChangeHook(context.Background(), madmin.SRIAMItem{
|
||||
Type: madmin.SRIAMItemSTSAcc,
|
||||
STSCredential: &madmin.SRSTSCredential{
|
||||
AccessKey: cred.AccessKey,
|
||||
SecretKey: cred.SecretKey,
|
||||
SessionToken: cred.SessionToken,
|
||||
ParentUser: cred.ParentUser,
|
||||
},
|
||||
UpdatedAt: updatedAt,
|
||||
}))
|
||||
|
||||
return minio.New(driver.endpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(cred.AccessKey, cred.SecretKey, cred.SessionToken),
|
||||
Secure: globalIsTLS,
|
||||
Transport: globalRemoteTargetTransport,
|
||||
})
|
||||
}
|
||||
|
||||
// ok == true - at this point
|
||||
|
||||
if ui.Credentials.IsTemp() {
|
||||
// Temporary credentials are not allowed.
|
||||
return nil, errAuthentication
|
||||
}
|
||||
|
||||
return minio.New(driver.endpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(ui.Credentials.AccessKey, ui.Credentials.SecretKey, ""),
|
||||
Secure: globalIsTLS,
|
||||
Transport: globalRemoteTargetTransport,
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteDir implements ftpDriver
|
||||
func (driver *ftpDriver) DeleteDir(ctx *ftp.Context, path string) (err error) {
|
||||
stopFn := globalFtpMetrics.log(ctx, path)
|
||||
defer stopFn(err)
|
||||
|
||||
bucket, prefix := path2BucketObject(path)
|
||||
if bucket == "" {
|
||||
return errors.New("deleting all buckets not allowed")
|
||||
}
|
||||
|
||||
clnt, err := driver.getMinIOClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
objectsCh := make(chan minio.ObjectInfo)
|
||||
|
||||
// Send object names that are needed to be removed to objectsCh
|
||||
go func() {
|
||||
defer close(objectsCh)
|
||||
opts := minio.ListObjectsOptions{Prefix: prefix, Recursive: true}
|
||||
for object := range clnt.ListObjects(cctx, bucket, opts) {
|
||||
if object.Err != nil {
|
||||
return
|
||||
}
|
||||
objectsCh <- object
|
||||
}
|
||||
}()
|
||||
|
||||
// Call RemoveObjects API
|
||||
for err := range clnt.RemoveObjects(context.Background(), bucket, objectsCh, minio.RemoveObjectsOptions{}) {
|
||||
if err.Err != nil {
|
||||
return err.Err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteFile implements ftpDriver
|
||||
func (driver *ftpDriver) DeleteFile(ctx *ftp.Context, path string) (err error) {
|
||||
stopFn := globalFtpMetrics.log(ctx, path)
|
||||
defer stopFn(err)
|
||||
|
||||
bucket, object := path2BucketObject(path)
|
||||
if bucket == "" {
|
||||
return errors.New("bucket name cannot be empty")
|
||||
}
|
||||
|
||||
clnt, err := driver.getMinIOClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return clnt.RemoveObject(context.Background(), bucket, object, minio.RemoveObjectOptions{})
|
||||
}
|
||||
|
||||
// Rename implements ftpDriver
|
||||
func (driver *ftpDriver) Rename(ctx *ftp.Context, fromPath string, toPath string) (err error) {
|
||||
stopFn := globalFtpMetrics.log(ctx, fromPath, toPath)
|
||||
defer stopFn(err)
|
||||
|
||||
return NotImplemented{}
|
||||
}
|
||||
|
||||
// MakeDir implements ftpDriver
|
||||
func (driver *ftpDriver) MakeDir(ctx *ftp.Context, path string) (err error) {
|
||||
stopFn := globalFtpMetrics.log(ctx, path)
|
||||
defer stopFn(err)
|
||||
|
||||
bucket, prefix := path2BucketObject(path)
|
||||
if bucket == "" {
|
||||
return errors.New("bucket name cannot be empty")
|
||||
}
|
||||
|
||||
clnt, err := driver.getMinIOClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dirPath := buildMinioDir(prefix)
|
||||
|
||||
_, err = clnt.PutObject(context.Background(), bucket, dirPath, bytes.NewReader([]byte("")), 0,
|
||||
// Always send Content-MD5 to succeed with bucket with
|
||||
// locking enabled. There is no performance hit since
|
||||
// this is always an empty object
|
||||
minio.PutObjectOptions{SendContentMd5: true},
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetFile implements ftpDriver
|
||||
func (driver *ftpDriver) GetFile(ctx *ftp.Context, path string, offset int64) (n int64, rc io.ReadCloser, err error) {
|
||||
stopFn := globalFtpMetrics.log(ctx, path)
|
||||
defer stopFn(err)
|
||||
|
||||
bucket, object := path2BucketObject(path)
|
||||
if bucket == "" {
|
||||
return 0, nil, errors.New("bucket name cannot be empty")
|
||||
}
|
||||
|
||||
clnt, err := driver.getMinIOClient(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
opts := minio.GetObjectOptions{}
|
||||
obj, err := clnt.GetObject(context.Background(), bucket, object, opts)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil && obj != nil {
|
||||
obj.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = obj.Seek(offset, io.SeekStart)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
info, err := obj.Stat()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
return info.Size - offset, obj, nil
|
||||
}
|
||||
|
||||
// PutFile implements ftpDriver
|
||||
func (driver *ftpDriver) PutFile(ctx *ftp.Context, path string, data io.Reader, offset int64) (n int64, err error) {
|
||||
stopFn := globalFtpMetrics.log(ctx, path)
|
||||
defer stopFn(err)
|
||||
|
||||
bucket, object := path2BucketObject(path)
|
||||
if bucket == "" {
|
||||
return 0, errors.New("bucket name cannot be empty")
|
||||
}
|
||||
|
||||
if offset != -1 {
|
||||
// FTP - APPEND not implemented
|
||||
return 0, NotImplemented{}
|
||||
}
|
||||
|
||||
clnt, err := driver.getMinIOClient(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
info, err := clnt.PutObject(context.Background(), bucket, object, data, -1, minio.PutObjectOptions{
|
||||
ContentType: "application/octet-stream",
|
||||
SendContentMd5: true,
|
||||
})
|
||||
return info.Size, err
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
// Copyright (c) 2015-2023 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/cli"
|
||||
"github.com/minio/minio/internal/ioutil"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/pkg/sftp"
|
||||
ftp "goftp.io/server/v2"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// minioLogger use an instance of this to log in a standard format
|
||||
type minioLogger struct{}
|
||||
|
||||
// Print implement Logger
|
||||
func (log *minioLogger) Print(sessionID string, message interface{}) {
|
||||
if serverDebugLog {
|
||||
logger.Info("%s %s", sessionID, message)
|
||||
}
|
||||
}
|
||||
|
||||
// Printf implement Logger
|
||||
func (log *minioLogger) Printf(sessionID string, format string, v ...interface{}) {
|
||||
if serverDebugLog {
|
||||
if sessionID != "" {
|
||||
logger.Info("%s %s", sessionID, fmt.Sprintf(format, v...))
|
||||
} else {
|
||||
logger.Info(format, v...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCommand impelment Logger
|
||||
func (log *minioLogger) PrintCommand(sessionID string, command string, params string) {
|
||||
if serverDebugLog {
|
||||
if command == "PASS" {
|
||||
logger.Info("%s > PASS ****", sessionID)
|
||||
} else {
|
||||
logger.Info("%s > %s %s", sessionID, command, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PrintResponse impelment Logger
|
||||
func (log *minioLogger) PrintResponse(sessionID string, code int, message string) {
|
||||
if serverDebugLog {
|
||||
logger.Info("%s < %d %s", sessionID, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func startSFTPServer(c *cli.Context) {
|
||||
args := c.StringSlice("sftp")
|
||||
|
||||
var (
|
||||
port int
|
||||
publicIP string
|
||||
sshPrivateKey string
|
||||
)
|
||||
|
||||
var err error
|
||||
for _, arg := range args {
|
||||
tokens := strings.SplitN(arg, "=", 2)
|
||||
if len(tokens) != 2 {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed to --sftp=%s", arg), "unable to start SFTP server")
|
||||
}
|
||||
switch tokens[0] {
|
||||
case "address":
|
||||
host, portStr, err := net.SplitHostPort(tokens[1])
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed to --sftp=%s (%v)", arg, err), "unable to start SFTP server")
|
||||
}
|
||||
port, err = strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed to --sftp=%s (%v)", arg, err), "unable to start SFTP server")
|
||||
}
|
||||
if port < 1 || port > 65535 {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed to --sftp=%s, (port number must be between 1 to 65535)", arg), "unable to start SFTP server")
|
||||
}
|
||||
publicIP = host
|
||||
case "ssh-private-key":
|
||||
sshPrivateKey = tokens[1]
|
||||
}
|
||||
}
|
||||
|
||||
if port == 0 {
|
||||
port = 8022 // Default SFTP port, since no port was given.
|
||||
}
|
||||
|
||||
if sshPrivateKey == "" {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed, private key file is mandatory for --sftp='ssh-private-key=path/to/id_ecdsa'"), "unable to start SFTP server")
|
||||
}
|
||||
|
||||
privateBytes, err := ioutil.ReadFile(sshPrivateKey)
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed, private key file is not accessible: %v", err), "unable to start SFTP server")
|
||||
}
|
||||
|
||||
private, err := ssh.ParsePrivateKey(privateBytes)
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed, private key file is not parseable: %v", err), "unable to start SFTP server")
|
||||
}
|
||||
|
||||
// An SSH server is represented by a ServerConfig, which holds
|
||||
// certificate details and handles authentication of ServerConns.
|
||||
config := &ssh.ServerConfig{
|
||||
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
|
||||
ui, ok := globalIAMSys.GetUser(context.Background(), c.User())
|
||||
if !ok {
|
||||
return nil, errNoSuchUser
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(ui.Credentials.SecretKey), pass) == 1 {
|
||||
return &ssh.Permissions{
|
||||
CriticalOptions: map[string]string{
|
||||
"accessKey": c.User(),
|
||||
},
|
||||
Extensions: make(map[string]string),
|
||||
}, nil
|
||||
}
|
||||
return nil, errAuthentication
|
||||
},
|
||||
}
|
||||
|
||||
config.AddHostKey(private)
|
||||
|
||||
// Once a ServerConfig has been configured, connections can be accepted.
|
||||
listener, err := net.Listen("tcp", net.JoinHostPort(publicIP, strconv.Itoa(port)))
|
||||
if err != nil {
|
||||
logger.Fatal(err, "unable to start listening on --sftp='port=%d'", port)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("MinIO SFTP Server listening on %s", net.JoinHostPort(publicIP, strconv.Itoa(port))))
|
||||
|
||||
for {
|
||||
nConn, err := listener.Accept()
|
||||
if err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Before use, a handshake must be performed on the incoming net.Conn.
|
||||
sconn, chans, reqs, err := ssh.NewServerConn(nConn, config)
|
||||
if err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
// The incoming Request channel must be serviced.
|
||||
go ssh.DiscardRequests(reqs)
|
||||
|
||||
// Service the incoming Channel channel.
|
||||
for newChannel := range chans {
|
||||
// Channels have a type, depending on the application level
|
||||
// protocol intended. In the case of an SFTP session, this is "subsystem"
|
||||
// with a payload string of "<length=4>sftp"
|
||||
if newChannel.ChannelType() != "session" {
|
||||
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
|
||||
continue
|
||||
}
|
||||
channel, requests, err := newChannel.Accept()
|
||||
if err != nil {
|
||||
logger.Fatal(err, "unable to accept the connection requests channel")
|
||||
}
|
||||
|
||||
// Sessions have out-of-band requests such as "shell",
|
||||
// "pty-req" and "env". Here we handle only the
|
||||
// "subsystem" request.
|
||||
go func(in <-chan *ssh.Request) {
|
||||
for req := range in {
|
||||
// We only reply to SSH packets that have `sftp` payload.
|
||||
req.Reply(req.Type == "subsystem" && string(req.Payload[4:]) == "sftp", nil)
|
||||
}
|
||||
}(requests)
|
||||
|
||||
server := sftp.NewRequestServer(channel, NewSFTPDriver(sconn.Permissions))
|
||||
if err := server.Serve(); err == io.EOF {
|
||||
server.Close()
|
||||
} else if err != nil {
|
||||
logger.Fatal(err, "unable to start SFTP server")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startFTPServer(c *cli.Context) {
|
||||
args := c.StringSlice("ftp")
|
||||
|
||||
var (
|
||||
port int
|
||||
publicIP string
|
||||
portRange string
|
||||
tlsPrivateKey string
|
||||
tlsPublicCert string
|
||||
)
|
||||
|
||||
var err error
|
||||
for _, arg := range args {
|
||||
tokens := strings.SplitN(arg, "=", 2)
|
||||
if len(tokens) != 2 {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed to --ftp=%s", arg), "unable to start FTP server")
|
||||
}
|
||||
switch tokens[0] {
|
||||
case "address":
|
||||
host, portStr, err := net.SplitHostPort(tokens[1])
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed to --ftp=%s (%v)", arg, err), "unable to start FTP server")
|
||||
}
|
||||
port, err = strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed to --ftp=%s (%v)", arg, err), "unable to start FTP server")
|
||||
}
|
||||
if port < 1 || port > 65535 {
|
||||
logger.Fatal(fmt.Errorf("invalid arguments passed to --ftp=%s, (port number must be between 1 to 65535)", arg), "unable to start FTP server")
|
||||
}
|
||||
publicIP = host
|
||||
case "passive-port-range":
|
||||
portRange = tokens[1]
|
||||
case "tls-private-key":
|
||||
tlsPrivateKey = tokens[1]
|
||||
case "tls-public-cert":
|
||||
tlsPublicCert = tokens[1]
|
||||
}
|
||||
}
|
||||
|
||||
// Verify if only partial inputs are given for FTP(secure)
|
||||
{
|
||||
if tlsPrivateKey == "" && tlsPublicCert != "" {
|
||||
logger.Fatal(fmt.Errorf("invalid TLS arguments provided missing private key --ftp=\"tls-private-key=path/to/private.key\""), "unable to start FTP server")
|
||||
}
|
||||
|
||||
if tlsPrivateKey != "" && tlsPublicCert == "" {
|
||||
logger.Fatal(fmt.Errorf("invalid TLS arguments provided missing public cert --ftp=\"tls-public-cert=path/to/public.crt\""), "unable to start FTP server")
|
||||
}
|
||||
if port == 0 {
|
||||
port = 8021 // Default FTP port, since no port was given.
|
||||
}
|
||||
}
|
||||
|
||||
// If no TLS certs were provided, server is running in TLS for S3 API
|
||||
// we automatically make FTP also run under TLS mode.
|
||||
if globalIsTLS && tlsPrivateKey == "" && tlsPublicCert == "" {
|
||||
tlsPrivateKey = getPrivateKeyFile()
|
||||
tlsPublicCert = getPublicCertFile()
|
||||
}
|
||||
|
||||
tls := tlsPrivateKey != "" && tlsPublicCert != ""
|
||||
|
||||
name := "MinIO FTP Server"
|
||||
if tls {
|
||||
name = "MinIO FTP(Secure) Server"
|
||||
}
|
||||
|
||||
ftpServer, err := ftp.NewServer(&ftp.Options{
|
||||
Name: name,
|
||||
WelcomeMessage: fmt.Sprintf("Welcome to MinIO FTP Server Version='%s' License='GNU AGPLv3'", Version),
|
||||
Driver: NewFTPDriver(),
|
||||
Port: port,
|
||||
Perm: ftp.NewSimplePerm("nobody", "nobody"),
|
||||
TLS: tls,
|
||||
KeyFile: tlsPrivateKey,
|
||||
CertFile: tlsPublicCert,
|
||||
ExplicitFTPS: tls,
|
||||
Logger: &minioLogger{},
|
||||
PassivePorts: portRange,
|
||||
PublicIP: publicIP,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal(err, "unable to initialize FTP server")
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("%s listening on %s", name, net.JoinHostPort(publicIP, strconv.Itoa(port))))
|
||||
|
||||
if err = ftpServer.ListenAndServe(); err != nil {
|
||||
logger.Fatal(err, "unable to start FTP server")
|
||||
}
|
||||
}
|
||||
+2
-58
@@ -36,7 +36,6 @@ import (
|
||||
"github.com/minio/minio/internal/config/dns"
|
||||
"github.com/minio/minio/internal/crypto"
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
"github.com/minio/minio/internal/http/stats"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/minio/internal/mcontext"
|
||||
)
|
||||
@@ -136,9 +135,8 @@ func setRequestLimitHandler(h http.Handler) http.Handler {
|
||||
|
||||
// Reserved bucket.
|
||||
const (
|
||||
minioReservedBucket = "minio"
|
||||
minioReservedBucketPath = SlashSeparator + minioReservedBucket
|
||||
minioReservedBucketPathWithSlash = SlashSeparator + minioReservedBucket + SlashSeparator
|
||||
minioReservedBucket = "minio"
|
||||
minioReservedBucketPath = SlashSeparator + minioReservedBucket
|
||||
|
||||
loginPathPrefix = SlashSeparator + "login"
|
||||
)
|
||||
@@ -280,60 +278,6 @@ func parseAmzDateHeader(req *http.Request) (time.Time, APIErrorCode) {
|
||||
return time.Time{}, ErrMissingDateHeader
|
||||
}
|
||||
|
||||
// splitStr splits a string into n parts, empty strings are added
|
||||
// if we are not able to reach n elements
|
||||
func splitStr(path, sep string, n int) []string {
|
||||
splits := strings.SplitN(path, sep, n)
|
||||
// Add empty strings if we found elements less than nr
|
||||
for i := n - len(splits); i > 0; i-- {
|
||||
splits = append(splits, "")
|
||||
}
|
||||
return splits
|
||||
}
|
||||
|
||||
func url2Bucket(p string) (bucket string) {
|
||||
tokens := splitStr(p, SlashSeparator, 3)
|
||||
return tokens[1]
|
||||
}
|
||||
|
||||
// setHttpStatsHandler sets a http Stats handler to gather HTTP statistics
|
||||
func setHTTPStatsHandler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Meters s3 connection stats.
|
||||
meteredRequest := &stats.IncomingTrafficMeter{ReadCloser: r.Body}
|
||||
meteredResponse := &stats.OutgoingTrafficMeter{ResponseWriter: w}
|
||||
|
||||
// Execute the request
|
||||
r.Body = meteredRequest
|
||||
h.ServeHTTP(meteredResponse, r)
|
||||
|
||||
if strings.HasPrefix(r.URL.Path, storageRESTPrefix) ||
|
||||
strings.HasPrefix(r.URL.Path, peerRESTPrefix) ||
|
||||
strings.HasPrefix(r.URL.Path, peerS3Prefix) ||
|
||||
strings.HasPrefix(r.URL.Path, lockRESTPrefix) {
|
||||
globalConnStats.incInputBytes(meteredRequest.BytesRead())
|
||||
globalConnStats.incOutputBytes(meteredResponse.BytesWritten())
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(r.URL.Path, minioReservedBucketPath) {
|
||||
globalConnStats.incAdminInputBytes(meteredRequest.BytesRead())
|
||||
globalConnStats.incAdminOutputBytes(meteredResponse.BytesWritten())
|
||||
return
|
||||
}
|
||||
|
||||
globalConnStats.incS3InputBytes(meteredRequest.BytesRead())
|
||||
globalConnStats.incS3OutputBytes(meteredResponse.BytesWritten())
|
||||
|
||||
if r.URL != nil {
|
||||
bucket := url2Bucket(r.URL.Path)
|
||||
if bucket != "" && bucket != minioReservedBucket {
|
||||
globalBucketConnStats.incS3InputBytes(bucket, meteredRequest.BytesRead())
|
||||
globalBucketConnStats.incS3OutputBytes(bucket, meteredResponse.BytesWritten())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Bad path components to be rejected by the path validity handler.
|
||||
const (
|
||||
dotdotComponent = ".."
|
||||
|
||||
+30
-28
@@ -81,12 +81,6 @@ func getLocalBackgroundHealStatus(ctx context.Context, o ObjectLayer) (madmin.Bg
|
||||
ScannedItemsCount: bgSeq.getScannedItemsCount(),
|
||||
}
|
||||
|
||||
if globalMRFState.initialized() {
|
||||
status.MRF = map[string]madmin.MRFStatus{
|
||||
globalLocalNodeName: globalMRFState.getCurrentMRFRoundInfo(),
|
||||
}
|
||||
}
|
||||
|
||||
healDisksMap := map[string]struct{}{}
|
||||
for _, ep := range getLocalDisksToHeal() {
|
||||
healDisksMap[ep.String()] = struct{}{}
|
||||
@@ -187,16 +181,16 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
}
|
||||
var forwardTo string
|
||||
// If we resume to the same bucket, forward to last known item.
|
||||
if tracker.Bucket != "" {
|
||||
if tracker.Bucket == bucket {
|
||||
forwardTo = tracker.Object
|
||||
if b := tracker.getBucket(); b != "" {
|
||||
if b == bucket {
|
||||
forwardTo = tracker.getObject()
|
||||
} else {
|
||||
// Reset to where last bucket ended if resuming.
|
||||
tracker.resume()
|
||||
}
|
||||
}
|
||||
tracker.Object = ""
|
||||
tracker.Bucket = bucket
|
||||
tracker.setObject("")
|
||||
tracker.setBucket(bucket)
|
||||
// Heal current bucket again in case if it is failed
|
||||
// in the being of erasure set healing
|
||||
if _, err := er.HealBucket(ctx, bucket, madmin.HealOpts{
|
||||
@@ -208,7 +202,7 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
|
||||
if serverDebugLog {
|
||||
console.Debugf(color.Green("healDrive:")+" healing bucket %s content on %s erasure set\n",
|
||||
bucket, humanize.Ordinal(tracker.SetIndex+1))
|
||||
bucket, humanize.Ordinal(er.setIndex+1))
|
||||
}
|
||||
|
||||
disks, _ := er.getOnlineDisksWithHealing()
|
||||
@@ -251,20 +245,14 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
go func() {
|
||||
for res := range results {
|
||||
if res.entryDone {
|
||||
tracker.Object = res.name
|
||||
if time.Since(tracker.LastUpdate) > time.Minute {
|
||||
tracker.setObject(res.name)
|
||||
if time.Since(tracker.getLastUpdate()) > time.Minute {
|
||||
logger.LogIf(ctx, tracker.update(ctx))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if res.success {
|
||||
tracker.ItemsHealed++
|
||||
tracker.BytesDone += res.bytes
|
||||
} else {
|
||||
tracker.ItemsFailed++
|
||||
tracker.BytesFailed += res.bytes
|
||||
}
|
||||
tracker.updateProgress(res.success, res.bytes)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -339,7 +327,9 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
// If not deleted, assume they failed.
|
||||
result = healEntryFailure(uint64(version.Size))
|
||||
if version.VersionID != "" {
|
||||
logger.LogIf(ctx, fmt.Errorf("unable to heal object %s/%s-v(%s): %w", bucket, version.Name, version.VersionID, err))
|
||||
if !isErrVersionNotFound(err) {
|
||||
logger.LogIf(ctx, fmt.Errorf("unable to heal object %s/%s-v(%s): %w", bucket, version.Name, version.VersionID, err))
|
||||
}
|
||||
} else {
|
||||
logger.LogIf(ctx, fmt.Errorf("unable to heal object %s/%s: %w", bucket, version.Name, err))
|
||||
}
|
||||
@@ -418,21 +408,32 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
logger.LogIf(ctx, tracker.update(ctx))
|
||||
}
|
||||
}
|
||||
tracker.Object = ""
|
||||
tracker.Bucket = ""
|
||||
|
||||
tracker.setObject("")
|
||||
tracker.setBucket("")
|
||||
|
||||
return retErr
|
||||
}
|
||||
|
||||
// healObject sends the given object/version to the background healing workers
|
||||
// and only returns when healing of the object is done.
|
||||
func healObject(bucket, object, versionID string, scan madmin.HealScanMode) {
|
||||
func healBucket(bucket string, scan madmin.HealScanMode) error {
|
||||
// Get background heal sequence to send elements to heal
|
||||
globalHealStateLK.Lock()
|
||||
bgSeq, ok := globalBackgroundHealState.getHealSequenceByToken(bgHealingUUID)
|
||||
globalHealStateLK.Unlock()
|
||||
if ok {
|
||||
bgSeq.queueHealTask(healSource{
|
||||
return bgSeq.queueHealTask(healSource{bucket: bucket}, madmin.HealItemBucket)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// healObject sends the given object/version to the background healing workers
|
||||
func healObject(bucket, object, versionID string, scan madmin.HealScanMode) error {
|
||||
// Get background heal sequence to send elements to heal
|
||||
globalHealStateLK.Lock()
|
||||
bgSeq, ok := globalBackgroundHealState.getHealSequenceByToken(bgHealingUUID)
|
||||
globalHealStateLK.Unlock()
|
||||
if ok {
|
||||
return bgSeq.queueHealTask(healSource{
|
||||
bucket: bucket,
|
||||
object: object,
|
||||
versionID: versionID,
|
||||
@@ -443,4 +444,5 @@ func healObject(bucket, object, versionID string, scan madmin.HealScanMode) {
|
||||
},
|
||||
}, madmin.HealItemObject)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/minio/minio/internal/config/api"
|
||||
xioutil "github.com/minio/minio/internal/ioutil"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/minio/internal/mcontext"
|
||||
)
|
||||
|
||||
type apiConfig struct {
|
||||
@@ -272,6 +273,10 @@ func maxClients(f http.HandlerFunc) http.HandlerFunc {
|
||||
|
||||
globalHTTPStats.addRequestsInQueue(1)
|
||||
|
||||
if tc, ok := r.Context().Value(mcontext.ContextTraceKey).(*mcontext.TraceCtxt); ok {
|
||||
tc.FuncName = "s3.MaxClients"
|
||||
}
|
||||
|
||||
deadlineTimer := time.NewTimer(deadline)
|
||||
defer deadlineTimer.Stop()
|
||||
|
||||
@@ -288,6 +293,10 @@ func maxClients(f http.HandlerFunc) http.HandlerFunc {
|
||||
globalHTTPStats.addRequestsInQueue(-1)
|
||||
return
|
||||
case <-r.Context().Done():
|
||||
// When the client disconnects before getting the S3 handler
|
||||
// status code response, set the status code to 499 so this request
|
||||
// will be properly audited and traced.
|
||||
w.WriteHeader(499)
|
||||
globalHTTPStats.addRequestsInQueue(-1)
|
||||
return
|
||||
}
|
||||
|
||||
+37
-2
@@ -34,6 +34,7 @@ import (
|
||||
"github.com/minio/minio/internal/handlers"
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/minio/internal/mcontext"
|
||||
xnet "github.com/minio/pkg/net"
|
||||
)
|
||||
|
||||
@@ -357,8 +358,42 @@ func collectAPIStats(api string, f http.HandlerFunc) http.HandlerFunc {
|
||||
|
||||
f.ServeHTTP(w, r)
|
||||
|
||||
if sw, ok := w.(*xhttp.ResponseRecorder); ok {
|
||||
globalHTTPStats.updateStats(api, r, sw)
|
||||
tc, ok := r.Context().Value(mcontext.ContextTraceKey).(*mcontext.TraceCtxt)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if tc != nil {
|
||||
if strings.HasPrefix(r.URL.Path, storageRESTPrefix) ||
|
||||
strings.HasPrefix(r.URL.Path, peerRESTPrefix) ||
|
||||
strings.HasPrefix(r.URL.Path, peerS3Prefix) ||
|
||||
strings.HasPrefix(r.URL.Path, lockRESTPrefix) {
|
||||
globalConnStats.incInputBytes(int64(tc.RequestRecorder.Size()))
|
||||
globalConnStats.incOutputBytes(int64(tc.ResponseRecorder.Size()))
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(r.URL.Path, minioReservedBucketPath) {
|
||||
globalConnStats.incAdminInputBytes(int64(tc.RequestRecorder.Size()))
|
||||
globalConnStats.incAdminOutputBytes(int64(tc.ResponseRecorder.Size()))
|
||||
return
|
||||
}
|
||||
|
||||
globalHTTPStats.updateStats(api, tc.ResponseRecorder)
|
||||
globalConnStats.incS3InputBytes(int64(tc.RequestRecorder.Size()))
|
||||
globalConnStats.incS3OutputBytes(int64(tc.ResponseRecorder.Size()))
|
||||
|
||||
resource, err := getResource(r.URL.Path, r.Host, globalDomainNames)
|
||||
if err != nil {
|
||||
logger.LogIf(r.Context(), fmt.Errorf("Unable to get the actual resource in the incoming request: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
bucket, _ := path2BucketObject(resource)
|
||||
if bucket != "" && bucket != minioReservedBucket {
|
||||
globalBucketConnStats.incS3InputBytes(bucket, int64(tc.RequestRecorder.Size()))
|
||||
globalBucketConnStats.incS3OutputBytes(bucket, int64(tc.ResponseRecorder.Size()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-7
@@ -19,7 +19,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -299,12 +298,7 @@ func (st *HTTPStats) toServerHTTPStats() ServerHTTPStats {
|
||||
}
|
||||
|
||||
// Update statistics from http request and response data
|
||||
func (st *HTTPStats) updateStats(api string, r *http.Request, w *xhttp.ResponseRecorder) {
|
||||
// Ignore non S3 requests
|
||||
if strings.HasSuffix(r.URL.Path, minioReservedBucketPathWithSlash) {
|
||||
return
|
||||
}
|
||||
|
||||
func (st *HTTPStats) updateStats(api string, w *xhttp.ResponseRecorder) {
|
||||
st.totalS3Requests.Inc(api)
|
||||
|
||||
// Increment the prometheus http request response histogram with appropriate label
|
||||
|
||||
+14
-21
@@ -71,40 +71,33 @@ func httpTracer(h http.Handler) http.Handler {
|
||||
// http stats requests and audit if enabled.
|
||||
respRecorder := xhttp.NewResponseRecorder(w)
|
||||
|
||||
if globalTrace.NumSubscribers(madmin.TraceS3|madmin.TraceInternal) == 0 {
|
||||
h.ServeHTTP(respRecorder, r)
|
||||
return
|
||||
}
|
||||
// Setup a http request body recorder
|
||||
reqRecorder := &xhttp.RequestRecorder{Reader: r.Body}
|
||||
r.Body = reqRecorder
|
||||
|
||||
// Create tracing data structure and associate it to the request context
|
||||
tc := mcontext.TraceCtxt{
|
||||
AmzReqID: r.Header.Get(xhttp.AmzRequestID),
|
||||
AmzReqID: r.Header.Get(xhttp.AmzRequestID),
|
||||
RequestRecorder: reqRecorder,
|
||||
ResponseRecorder: respRecorder,
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), mcontext.ContextTraceKey, &tc)
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
// Setup a http request body recorder
|
||||
reqRecorder := &xhttp.RequestRecorder{Reader: r.Body}
|
||||
|
||||
tc.RequestRecorder = reqRecorder
|
||||
tc.ResponseRecorder = respRecorder
|
||||
|
||||
// Execute call.
|
||||
r.Body = reqRecorder
|
||||
r = r.WithContext(context.WithValue(r.Context(), mcontext.ContextTraceKey, &tc))
|
||||
|
||||
reqStartTime := time.Now().UTC()
|
||||
h.ServeHTTP(respRecorder, r)
|
||||
reqEndTime := time.Now().UTC()
|
||||
|
||||
if globalTrace.NumSubscribers(madmin.TraceS3|madmin.TraceInternal) == 0 {
|
||||
// no subscribers nothing to trace.
|
||||
return
|
||||
}
|
||||
|
||||
tt := madmin.TraceInternal
|
||||
if strings.HasPrefix(tc.FuncName, "s3.") {
|
||||
tt = madmin.TraceS3
|
||||
}
|
||||
// No need to continue if no subscribers for actual type...
|
||||
if globalTrace.NumSubscribers(tt) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate input body size with headers
|
||||
reqHeaders := r.Header.Clone()
|
||||
reqHeaders.Set("Host", r.Host)
|
||||
@@ -113,7 +106,7 @@ func httpTracer(h http.Handler) http.Handler {
|
||||
} else {
|
||||
reqHeaders.Set("Transfer-Encoding", strings.Join(r.TransferEncoding, ","))
|
||||
}
|
||||
inputBytes := reqRecorder.BodySize()
|
||||
inputBytes := reqRecorder.Size()
|
||||
for k, v := range reqHeaders {
|
||||
inputBytes += len(k) + len(v)
|
||||
}
|
||||
|
||||
@@ -1451,6 +1451,11 @@ func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (u UserIdentit
|
||||
u, ok = sys.store.GetUser(accessKey)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
if accessKey == globalActiveCred.AccessKey {
|
||||
return newUserIdentity(globalActiveCred), true
|
||||
}
|
||||
}
|
||||
return u, ok && u.Credentials.IsValid()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ package cmd
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/internal/event"
|
||||
@@ -141,8 +142,30 @@ func (api objectAPIHandlers) ListenNotificationHandler(w http.ResponseWriter, r
|
||||
peer.Listen(listenCh, ctx.Done(), values)
|
||||
}
|
||||
|
||||
keepAliveTicker := time.NewTicker(500 * time.Millisecond)
|
||||
defer keepAliveTicker.Stop()
|
||||
var (
|
||||
emptyEventTicker <-chan time.Time
|
||||
keepAliveTicker <-chan time.Time
|
||||
)
|
||||
|
||||
if p := values.Get("ping"); p != "" {
|
||||
pingInterval, err := strconv.Atoi(p)
|
||||
if err != nil {
|
||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidQueryParams), r.URL)
|
||||
return
|
||||
}
|
||||
if pingInterval < 1 {
|
||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidQueryParams), r.URL)
|
||||
return
|
||||
}
|
||||
t := time.NewTicker(time.Duration(pingInterval) * time.Second)
|
||||
defer t.Stop()
|
||||
emptyEventTicker = t.C
|
||||
} else {
|
||||
// Deprecated Apr 2023
|
||||
t := time.NewTicker(500 * time.Millisecond)
|
||||
defer t.Stop()
|
||||
keepAliveTicker = t.C
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
for {
|
||||
@@ -155,7 +178,12 @@ func (api objectAPIHandlers) ListenNotificationHandler(w http.ResponseWriter, r
|
||||
// Flush if nothing is queued
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
case <-keepAliveTicker.C:
|
||||
case <-emptyEventTicker:
|
||||
if err := enc.Encode(struct{ Records []event.Event }{}); err != nil {
|
||||
return
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
case <-keepAliveTicker:
|
||||
if _, err := w.Write([]byte(" ")); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
+14
-9
@@ -18,6 +18,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -126,6 +127,7 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
|
||||
scanDir = func(current string) error {
|
||||
// Skip forward, if requested...
|
||||
var sb bytes.Buffer
|
||||
forward := ""
|
||||
if len(opts.ForwardTo) > 0 && strings.HasPrefix(opts.ForwardTo, current) {
|
||||
forward = strings.TrimPrefix(opts.ForwardTo, current)
|
||||
@@ -160,6 +162,9 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
return nil
|
||||
}
|
||||
dirObjects := make(map[string]struct{})
|
||||
|
||||
// Avoid a bunch of cleanup when joining.
|
||||
current = strings.Trim(current, SlashSeparator)
|
||||
for i, entry := range entries {
|
||||
if opts.Limit > 0 && objsReturned >= opts.Limit {
|
||||
return nil
|
||||
@@ -176,7 +181,7 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
entries[i] = ""
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(entry, slashSeparator) {
|
||||
if hasSuffixByte(entry, SlashSeparatorChar) {
|
||||
if strings.HasSuffix(entry, globalDirSuffixWithSlash) {
|
||||
// Add without extension so it is sorted correctly.
|
||||
entry = strings.TrimSuffix(entry, globalDirSuffixWithSlash) + slashSeparator
|
||||
@@ -198,7 +203,7 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
if HasSuffix(entry, xlStorageFormatFile) {
|
||||
var meta metaCacheEntry
|
||||
s.walkReadMu.Lock()
|
||||
meta.metadata, err = s.readMetadata(ctx, pathJoin(volumeDir, current, entry))
|
||||
meta.metadata, err = s.readMetadata(ctx, pathJoinBuf(&sb, volumeDir, current, entry))
|
||||
s.walkReadMu.Unlock()
|
||||
diskHealthCheckOK(ctx, err)
|
||||
if err != nil {
|
||||
@@ -212,7 +217,7 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
}
|
||||
meta.name = strings.TrimSuffix(entry, xlStorageFormatFile)
|
||||
meta.name = strings.TrimSuffix(meta.name, SlashSeparator)
|
||||
meta.name = pathJoin(current, meta.name)
|
||||
meta.name = pathJoinBuf(&sb, current, meta.name)
|
||||
meta.name = decodeDirObject(meta.name)
|
||||
|
||||
objReturned(meta.metadata)
|
||||
@@ -223,7 +228,7 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
if HasSuffix(entry, xlStorageFormatFileV1) {
|
||||
var meta metaCacheEntry
|
||||
s.walkReadMu.Lock()
|
||||
meta.metadata, err = xioutil.ReadFile(pathJoin(volumeDir, current, entry))
|
||||
meta.metadata, err = xioutil.ReadFile(pathJoinBuf(&sb, volumeDir, current, entry))
|
||||
s.walkReadMu.Unlock()
|
||||
diskHealthCheckOK(ctx, err)
|
||||
if err != nil {
|
||||
@@ -234,7 +239,7 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
}
|
||||
meta.name = strings.TrimSuffix(entry, xlStorageFormatFileV1)
|
||||
meta.name = strings.TrimSuffix(meta.name, SlashSeparator)
|
||||
meta.name = pathJoin(current, meta.name)
|
||||
meta.name = pathJoinBuf(&sb, current, meta.name)
|
||||
objReturned(meta.metadata)
|
||||
|
||||
out <- meta
|
||||
@@ -267,7 +272,7 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
if contextCanceled(ctx) {
|
||||
return ctx.Err()
|
||||
}
|
||||
meta := metaCacheEntry{name: pathJoin(current, entry)}
|
||||
meta := metaCacheEntry{name: pathJoinBuf(&sb, current, entry)}
|
||||
|
||||
// If directory entry on stack before this, pop it now.
|
||||
for len(dirStack) > 0 && dirStack[len(dirStack)-1] < meta.name {
|
||||
@@ -291,7 +296,7 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
}
|
||||
|
||||
s.walkReadMu.Lock()
|
||||
meta.metadata, err = s.readMetadata(ctx, pathJoin(volumeDir, meta.name, xlStorageFormatFile))
|
||||
meta.metadata, err = s.readMetadata(ctx, pathJoinBuf(&sb, volumeDir, meta.name, xlStorageFormatFile))
|
||||
s.walkReadMu.Unlock()
|
||||
diskHealthCheckOK(ctx, err)
|
||||
switch {
|
||||
@@ -304,7 +309,7 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
|
||||
out <- meta
|
||||
case osIsNotExist(err), isSysErrIsDir(err):
|
||||
meta.metadata, err = xioutil.ReadFile(pathJoin(volumeDir, meta.name, xlStorageFormatFileV1))
|
||||
meta.metadata, err = xioutil.ReadFile(pathJoinBuf(&sb, volumeDir, meta.name, xlStorageFormatFileV1))
|
||||
diskHealthCheckOK(ctx, err)
|
||||
if err == nil {
|
||||
// It was an object
|
||||
@@ -317,7 +322,7 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
// NOT an object, append to stack (with slash)
|
||||
// If dirObject, but no metadata (which is unexpected) we skip it.
|
||||
if !isDirObj {
|
||||
if !isDirEmpty(pathJoin(volumeDir, meta.name+slashSeparator)) {
|
||||
if !isDirEmpty(pathJoinBuf(&sb, volumeDir, meta.name)) {
|
||||
dirStack = append(dirStack, meta.name+slashSeparator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ const (
|
||||
waitingTotal MetricName = "waiting_total"
|
||||
incomingTotal MetricName = "incoming_total"
|
||||
objectTotal MetricName = "object_total"
|
||||
versionTotal MetricName = "version_total"
|
||||
offlineTotal MetricName = "offline_total"
|
||||
onlineTotal MetricName = "online_total"
|
||||
openTotal MetricName = "open_total"
|
||||
@@ -509,6 +510,16 @@ func getBucketUsageObjectsTotalMD() MetricDescription {
|
||||
}
|
||||
}
|
||||
|
||||
func getBucketUsageVersionsTotalMD() MetricDescription {
|
||||
return MetricDescription{
|
||||
Namespace: bucketMetricNamespace,
|
||||
Subsystem: usageSubsystem,
|
||||
Name: versionTotal,
|
||||
Help: "Total number of versions",
|
||||
Type: gaugeMetric,
|
||||
}
|
||||
}
|
||||
|
||||
func getBucketRepLatencyMD() MetricDescription {
|
||||
return MetricDescription{
|
||||
Namespace: bucketMetricNamespace,
|
||||
@@ -1995,6 +2006,12 @@ func getBucketUsageMetrics() *MetricsGroup {
|
||||
VariableLabels: map[string]string{"bucket": bucket},
|
||||
})
|
||||
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getBucketUsageVersionsTotalMD(),
|
||||
Value: float64(usage.VersionsCount),
|
||||
VariableLabels: map[string]string{"bucket": bucket},
|
||||
})
|
||||
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getBucketRepReceivedBytesMD(),
|
||||
Value: float64(stats.ReplicaSize),
|
||||
|
||||
+26
-157
@@ -20,16 +20,13 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
mrfInfoResetInterval = 10 * time.Second
|
||||
mrfOpsQueueSize = 10000
|
||||
mrfOpsQueueSize = 100000
|
||||
)
|
||||
|
||||
// partialOperation is a successful upload/delete of an object
|
||||
@@ -38,35 +35,17 @@ type partialOperation struct {
|
||||
bucket string
|
||||
object string
|
||||
versionID string
|
||||
size int64
|
||||
setIndex int
|
||||
poolIndex int
|
||||
}
|
||||
|
||||
type setInfo struct {
|
||||
index, pool int
|
||||
queued time.Time
|
||||
}
|
||||
|
||||
// mrfState sncapsulates all the information
|
||||
// related to the global background MRF.
|
||||
type mrfState struct {
|
||||
ready int32 // ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||
_ int32 // For 64 bits alignment
|
||||
|
||||
ctx context.Context
|
||||
objectAPI ObjectLayer
|
||||
|
||||
mu sync.Mutex
|
||||
opCh chan partialOperation
|
||||
pendingOps map[partialOperation]setInfo
|
||||
setReconnectEvent chan setInfo
|
||||
|
||||
itemsHealed uint64
|
||||
bytesHealed uint64
|
||||
pendingItems uint64
|
||||
pendingBytes uint64
|
||||
|
||||
triggeredAt time.Time
|
||||
mu sync.Mutex
|
||||
opCh chan partialOperation
|
||||
}
|
||||
|
||||
// Initialize healing MRF subsystem
|
||||
@@ -75,24 +54,15 @@ func (m *mrfState) init(ctx context.Context, objAPI ObjectLayer) {
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.ctx = ctx
|
||||
m.objectAPI = objAPI
|
||||
m.opCh = make(chan partialOperation, mrfOpsQueueSize)
|
||||
m.pendingOps = make(map[partialOperation]setInfo)
|
||||
m.setReconnectEvent = make(chan setInfo)
|
||||
m.objectAPI = objAPI
|
||||
|
||||
go globalMRFState.maintainMRFList()
|
||||
go globalMRFState.healRoutine()
|
||||
|
||||
atomic.StoreInt32(&m.ready, 1)
|
||||
}
|
||||
|
||||
func (m *mrfState) initialized() bool {
|
||||
return atomic.LoadInt32(&m.ready) != 0
|
||||
}
|
||||
|
||||
// Add a partial S3 operation (put/delete) when one or more disks are offline.
|
||||
func (m *mrfState) addPartialOp(op partialOperation) {
|
||||
if !m.initialized() {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -102,140 +72,39 @@ func (m *mrfState) addPartialOp(op partialOperation) {
|
||||
}
|
||||
}
|
||||
|
||||
// Receive the new set (disk) reconnection event
|
||||
func (m *mrfState) newSetReconnected(pool, set int) {
|
||||
if !m.initialized() {
|
||||
return
|
||||
}
|
||||
|
||||
idler := time.NewTimer(100 * time.Millisecond)
|
||||
defer idler.Stop()
|
||||
|
||||
select {
|
||||
case m.setReconnectEvent <- setInfo{index: set, pool: pool}:
|
||||
case <-idler.C:
|
||||
}
|
||||
}
|
||||
|
||||
// Get current MRF stats of the last MRF activity
|
||||
func (m *mrfState) getCurrentMRFRoundInfo() madmin.MRFStatus {
|
||||
m.mu.Lock()
|
||||
triggeredAt := m.triggeredAt
|
||||
itemsHealed := m.itemsHealed
|
||||
bytesHealed := m.bytesHealed
|
||||
pendingItems := m.pendingItems
|
||||
pendingBytes := m.pendingBytes
|
||||
m.mu.Unlock()
|
||||
|
||||
if pendingItems == 0 {
|
||||
return madmin.MRFStatus{}
|
||||
}
|
||||
|
||||
return madmin.MRFStatus{
|
||||
Started: triggeredAt,
|
||||
ItemsHealed: itemsHealed,
|
||||
BytesHealed: bytesHealed,
|
||||
TotalItems: itemsHealed + pendingItems,
|
||||
TotalBytes: bytesHealed + pendingBytes,
|
||||
}
|
||||
}
|
||||
|
||||
// maintainMRFList gathers the list of successful partial uploads
|
||||
// from all underlying er.sets and puts them in a global map which
|
||||
// should not have more than 10000 entries.
|
||||
func (m *mrfState) maintainMRFList() {
|
||||
for fOp := range m.opCh {
|
||||
m.mu.Lock()
|
||||
if len(m.pendingOps) > mrfOpsQueueSize {
|
||||
m.mu.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
m.pendingOps[fOp] = setInfo{index: fOp.setIndex, pool: fOp.poolIndex}
|
||||
m.pendingItems++
|
||||
if fOp.size > 0 {
|
||||
m.pendingBytes += uint64(fOp.size)
|
||||
}
|
||||
|
||||
m.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Reset current MRF stats
|
||||
func (m *mrfState) resetMRFInfoIfNoPendingOps() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.pendingItems > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
m.itemsHealed = 0
|
||||
m.bytesHealed = 0
|
||||
m.pendingItems = 0
|
||||
m.pendingBytes = 0
|
||||
m.triggeredAt = time.Time{}
|
||||
}
|
||||
var healSleeper = newDynamicSleeper(5, time.Second, false)
|
||||
|
||||
// healRoutine listens to new disks reconnection events and
|
||||
// issues healing requests for queued objects belonging to the
|
||||
// corresponding erasure set
|
||||
func (m *mrfState) healRoutine() {
|
||||
idler := time.NewTimer(mrfInfoResetInterval)
|
||||
defer idler.Stop()
|
||||
|
||||
mrfHealingOpts := madmin.HealOpts{
|
||||
ScanMode: madmin.HealNormalScan,
|
||||
Remove: healDeleteDangling,
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-m.ctx.Done():
|
||||
return
|
||||
case <-idler.C:
|
||||
m.resetMRFInfoIfNoPendingOps()
|
||||
idler.Reset(mrfInfoResetInterval)
|
||||
case setInfo := <-m.setReconnectEvent:
|
||||
// Get the list of objects related the er.set
|
||||
// to which the connected disk belongs.
|
||||
var mrfOperations []partialOperation
|
||||
m.mu.Lock()
|
||||
for k, v := range m.pendingOps {
|
||||
if v == setInfo {
|
||||
mrfOperations = append(mrfOperations, k)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
|
||||
if len(mrfOperations) == 0 {
|
||||
continue
|
||||
case u, ok := <-m.opCh:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
m.triggeredAt = time.Now().UTC()
|
||||
m.mu.Unlock()
|
||||
|
||||
// Heal objects
|
||||
for _, u := range mrfOperations {
|
||||
_, err := m.objectAPI.HealObject(m.ctx, u.bucket, u.object, u.versionID, mrfHealingOpts)
|
||||
m.mu.Lock()
|
||||
if err == nil {
|
||||
m.itemsHealed++
|
||||
m.bytesHealed += uint64(u.size)
|
||||
}
|
||||
m.pendingItems--
|
||||
m.pendingBytes -= uint64(u.size)
|
||||
delete(m.pendingOps, u)
|
||||
m.mu.Unlock()
|
||||
|
||||
if !isErrObjectNotFound(err) && !isErrVersionNotFound(err) {
|
||||
// Log healing error if any
|
||||
logger.LogIf(m.ctx, err)
|
||||
}
|
||||
now := time.Now()
|
||||
if now.Sub(u.queued) < time.Second {
|
||||
// let recently failed networks to reconnect
|
||||
// making MRF wait for 1s before retrying,
|
||||
// i.e 4 reconnect attempts.
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
waitForLowHTTPReq()
|
||||
// wait on timer per heal
|
||||
wait := healSleeper.Timer(context.Background())
|
||||
|
||||
if u.object == "" {
|
||||
healBucket(u.bucket, madmin.HealNormalScan)
|
||||
} else {
|
||||
healObject(u.bucket, u.object, u.versionID, madmin.HealNormalScan)
|
||||
}
|
||||
|
||||
wait()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,6 +193,9 @@ type ObjectInfo struct {
|
||||
// Checksums added on upload.
|
||||
// Encoded, maybe encrypted.
|
||||
Checksum []byte
|
||||
|
||||
// Inlined
|
||||
Inlined bool
|
||||
}
|
||||
|
||||
// ArchiveInfo returns any saved zip archive meta information.
|
||||
|
||||
@@ -674,6 +674,12 @@ func isErrBucketNotFound(err error) bool {
|
||||
return errors.As(err, &bkNotFound)
|
||||
}
|
||||
|
||||
// isErrReadQuorum check if the error type is InsufficentReadQuorum
|
||||
func isErrReadQuorum(err error) bool {
|
||||
var rquorum InsufficientReadQuorum
|
||||
return errors.As(err, &rquorum)
|
||||
}
|
||||
|
||||
// isErrObjectNotFound - Check if error type is ObjectNotFound.
|
||||
func isErrObjectNotFound(err error) bool {
|
||||
var objNotFound ObjectNotFound
|
||||
|
||||
@@ -179,15 +179,6 @@ func (o *ObjectOptions) PutReplicationState() (r ReplicationState) {
|
||||
return
|
||||
}
|
||||
|
||||
// LockType represents required locking for ObjectLayer operations
|
||||
type LockType int
|
||||
|
||||
const (
|
||||
noLock LockType = iota
|
||||
readLock
|
||||
writeLock
|
||||
)
|
||||
|
||||
// ObjectLayer implements primitives for object API layer.
|
||||
type ObjectLayer interface {
|
||||
// Locking operations on object.
|
||||
@@ -214,12 +205,12 @@ type ObjectLayer interface {
|
||||
// Object operations.
|
||||
|
||||
// GetObjectNInfo returns a GetObjectReader that satisfies the
|
||||
// ReadCloser interface. The Close method unlocks the object
|
||||
// after reading, so it must always be called after usage.
|
||||
// ReadCloser interface. The Close method runs any cleanup
|
||||
// functions, so it must always be called after reading till EOF
|
||||
//
|
||||
// IMPORTANTLY, when implementations return err != nil, this
|
||||
// function MUST NOT return a non-nil ReadCloser.
|
||||
GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (reader *GetObjectReader, err error)
|
||||
GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (reader *GetObjectReader, err error)
|
||||
GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error)
|
||||
PutObject(ctx context.Context, bucket, object string, data *PutObjReader, opts ObjectOptions) (objInfo ObjectInfo, err error)
|
||||
CopyObject(ctx context.Context, srcBucket, srcObject, destBucket, destObject string, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (objInfo ObjectInfo, err error)
|
||||
@@ -272,7 +263,7 @@ func GetObject(ctx context.Context, api ObjectLayer, bucket, object string, star
|
||||
}
|
||||
Range := &HTTPRangeSpec{Start: startOffset, End: startOffset + length}
|
||||
|
||||
reader, err := api.GetObjectNInfo(ctx, bucket, object, Range, header, readLock, opts)
|
||||
reader, err := api.GetObjectNInfo(ctx, bucket, object, Range, header, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -89,6 +89,9 @@ func getOpts(ctx context.Context, r *http.Request, bucket, object string) (Objec
|
||||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
if isMaxPartID(partNumber) {
|
||||
return opts, errInvalidMaxParts
|
||||
}
|
||||
if partNumber <= 0 {
|
||||
return opts, errInvalidArgument
|
||||
}
|
||||
|
||||
+93
-1
@@ -209,6 +209,9 @@ func checkObjectNameForLengthAndSlash(bucket, object string) error {
|
||||
// SlashSeparator - slash separator.
|
||||
const SlashSeparator = "/"
|
||||
|
||||
// SlashSeparatorChar - slash separator.
|
||||
const SlashSeparatorChar = '/'
|
||||
|
||||
// retainSlash - retains slash from a path.
|
||||
func retainSlash(s string) string {
|
||||
if s == "" {
|
||||
@@ -231,13 +234,102 @@ func pathsJoinPrefix(prefix string, elem ...string) (paths []string) {
|
||||
func pathJoin(elem ...string) string {
|
||||
trailingSlash := ""
|
||||
if len(elem) > 0 {
|
||||
if HasSuffix(elem[len(elem)-1], SlashSeparator) {
|
||||
if hasSuffixByte(elem[len(elem)-1], SlashSeparatorChar) {
|
||||
trailingSlash = SlashSeparator
|
||||
}
|
||||
}
|
||||
return path.Join(elem...) + trailingSlash
|
||||
}
|
||||
|
||||
// pathJoinBuf - like path.Join() but retains trailing SlashSeparator of the last element.
|
||||
// Provide a string builder to reduce allocation.
|
||||
func pathJoinBuf(dst *bytes.Buffer, elem ...string) string {
|
||||
trailingSlash := len(elem) > 0 && hasSuffixByte(elem[len(elem)-1], SlashSeparatorChar)
|
||||
dst.Reset()
|
||||
added := 0
|
||||
for _, e := range elem {
|
||||
if added > 0 || e != "" {
|
||||
if added > 0 {
|
||||
dst.WriteRune(SlashSeparatorChar)
|
||||
}
|
||||
dst.WriteString(e)
|
||||
added += len(e)
|
||||
}
|
||||
}
|
||||
|
||||
if pathNeedsClean(dst.Bytes()) {
|
||||
s := path.Clean(dst.String())
|
||||
if trailingSlash {
|
||||
return s + SlashSeparator
|
||||
}
|
||||
return s
|
||||
}
|
||||
if trailingSlash {
|
||||
dst.WriteRune(SlashSeparatorChar)
|
||||
}
|
||||
return dst.String()
|
||||
}
|
||||
|
||||
// hasSuffixByte returns true if the last byte of s is 'suffix'
|
||||
func hasSuffixByte(s string, suffix byte) bool {
|
||||
return len(s) > 0 && s[len(s)-1] == suffix
|
||||
}
|
||||
|
||||
// pathNeedsClean returns whether path.Clean may change the path.
|
||||
// Will detect all cases that will be cleaned,
|
||||
// but may produce false positives on non-trivial paths.
|
||||
func pathNeedsClean(path []byte) bool {
|
||||
if len(path) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
rooted := path[0] == '/'
|
||||
n := len(path)
|
||||
|
||||
r, w := 0, 0
|
||||
if rooted {
|
||||
r, w = 1, 1
|
||||
}
|
||||
|
||||
for r < n {
|
||||
switch {
|
||||
case path[r] > 127:
|
||||
// Non ascii.
|
||||
return true
|
||||
case path[r] == '/':
|
||||
// multiple / elements
|
||||
return true
|
||||
case path[r] == '.' && (r+1 == n || path[r+1] == '/'):
|
||||
// . element - assume it has to be cleaned.
|
||||
return true
|
||||
case path[r] == '.' && path[r+1] == '.' && (r+2 == n || path[r+2] == '/'):
|
||||
// .. element: remove to last / - assume it has to be cleaned.
|
||||
return true
|
||||
default:
|
||||
// real path element.
|
||||
// add slash if needed
|
||||
if rooted && w != 1 || !rooted && w != 0 {
|
||||
w++
|
||||
}
|
||||
// copy element
|
||||
for ; r < n && path[r] != '/'; r++ {
|
||||
w++
|
||||
}
|
||||
// allow one slash, not at end
|
||||
if r < n-1 && path[r] == '/' {
|
||||
r++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Turn empty string into "."
|
||||
if w == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// mustGetUUID - get a random UUID.
|
||||
func mustGetUUID() string {
|
||||
u, err := uuid.NewRandom()
|
||||
|
||||
@@ -740,3 +740,70 @@ func TestS2CompressReader(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_pathNeedsClean(t *testing.T) {
|
||||
type pathTest struct {
|
||||
path, result string
|
||||
}
|
||||
|
||||
cleantests := []pathTest{
|
||||
// Already clean
|
||||
{"", "."},
|
||||
{"abc", "abc"},
|
||||
{"abc/def", "abc/def"},
|
||||
{"a/b/c", "a/b/c"},
|
||||
{".", "."},
|
||||
{"..", ".."},
|
||||
{"../..", "../.."},
|
||||
{"../../abc", "../../abc"},
|
||||
{"/abc", "/abc"},
|
||||
{"/abc/def", "/abc/def"},
|
||||
{"/", "/"},
|
||||
|
||||
// Remove trailing slash
|
||||
{"abc/", "abc"},
|
||||
{"abc/def/", "abc/def"},
|
||||
{"a/b/c/", "a/b/c"},
|
||||
{"./", "."},
|
||||
{"../", ".."},
|
||||
{"../../", "../.."},
|
||||
{"/abc/", "/abc"},
|
||||
|
||||
// Remove doubled slash
|
||||
{"abc//def//ghi", "abc/def/ghi"},
|
||||
{"//abc", "/abc"},
|
||||
{"///abc", "/abc"},
|
||||
{"//abc//", "/abc"},
|
||||
{"abc//", "abc"},
|
||||
|
||||
// Remove . elements
|
||||
{"abc/./def", "abc/def"},
|
||||
{"/./abc/def", "/abc/def"},
|
||||
{"abc/.", "abc"},
|
||||
|
||||
// Remove .. elements
|
||||
{"abc/def/ghi/../jkl", "abc/def/jkl"},
|
||||
{"abc/def/../ghi/../jkl", "abc/jkl"},
|
||||
{"abc/def/..", "abc"},
|
||||
{"abc/def/../..", "."},
|
||||
{"/abc/def/../..", "/"},
|
||||
{"abc/def/../../..", ".."},
|
||||
{"/abc/def/../../..", "/"},
|
||||
{"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
|
||||
|
||||
// Combinations
|
||||
{"abc/./../def", "def"},
|
||||
{"abc//./../def", "def"},
|
||||
{"abc/../../././../def", "../../def"},
|
||||
}
|
||||
for _, test := range cleantests {
|
||||
want := test.path != test.result
|
||||
got := pathNeedsClean([]byte(test.path))
|
||||
if !got {
|
||||
t.Logf("no clean: %q", test.path)
|
||||
}
|
||||
if want && !got {
|
||||
t.Errorf("input: %q, want %v, got %v", test.path, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,15 +367,17 @@ func deleteObjectVersions(ctx context.Context, o ObjectLayer, bucket string, toD
|
||||
continue
|
||||
}
|
||||
dobj := deletedObjs[i]
|
||||
oi := ObjectInfo{
|
||||
Bucket: bucket,
|
||||
Name: dobj.ObjectName,
|
||||
VersionID: dobj.VersionID,
|
||||
}
|
||||
traceFn := globalLifecycleSys.trace(oi)
|
||||
// Send audit for the lifecycle delete operation
|
||||
auditLogLifecycle(
|
||||
ctx,
|
||||
ObjectInfo{
|
||||
Bucket: bucket,
|
||||
Name: dobj.ObjectName,
|
||||
VersionID: dobj.VersionID,
|
||||
},
|
||||
ILMExpiry)
|
||||
oi,
|
||||
ILMExpiry, traceFn)
|
||||
|
||||
sendEvent(eventArgs{
|
||||
EventName: event.ObjectRemovedDelete,
|
||||
|
||||
+21
-14
@@ -238,7 +238,8 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
|
||||
Start: offset,
|
||||
End: -1,
|
||||
}
|
||||
return getObjectNInfo(ctx, bucket, object, rs, r.Header, noLock, opts)
|
||||
opts.NoLock = true
|
||||
return getObjectNInfo(ctx, bucket, object, rs, r.Header, opts)
|
||||
},
|
||||
actualSize,
|
||||
)
|
||||
@@ -402,7 +403,7 @@ func (api objectAPIHandlers) getObjectHandler(ctx context.Context, objectAPI Obj
|
||||
}
|
||||
|
||||
var proxy proxyResult
|
||||
gr, err := getObjectNInfo(ctx, bucket, object, rs, r.Header, readLock, opts)
|
||||
gr, err := getObjectNInfo(ctx, bucket, object, rs, r.Header, opts)
|
||||
if err != nil {
|
||||
var (
|
||||
reader *GetObjectReader
|
||||
@@ -1098,19 +1099,12 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
return checkCopyObjectPreconditions(ctx, w, r, o)
|
||||
}
|
||||
getOpts.CheckPrecondFn = checkCopyPrecondFn
|
||||
|
||||
// FIXME: a possible race exists between a parallel
|
||||
// GetObject v/s CopyObject with metadata updates, ideally
|
||||
// we should be holding write lock here but it is not
|
||||
// possible due to other constraints such as knowing
|
||||
// the type of source content etc.
|
||||
lock := noLock
|
||||
if !cpSrcDstSame {
|
||||
lock = readLock
|
||||
if cpSrcDstSame {
|
||||
getOpts.NoLock = true
|
||||
}
|
||||
|
||||
var rs *HTTPRangeSpec
|
||||
gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, lock, getOpts)
|
||||
gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, getOpts)
|
||||
if err != nil {
|
||||
if isErrPreconditionFailed(err) {
|
||||
return
|
||||
@@ -2084,11 +2078,24 @@ func (api objectAPIHandlers) PutObjectExtractHandler(w http.ResponseWriter, r *h
|
||||
getObjectInfo = api.CacheAPI().GetObjectInfo
|
||||
}
|
||||
|
||||
// Extract request metadata
|
||||
metadata, err := extractMetadata(ctx, r)
|
||||
if err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
metadata[xhttp.AmzStorageClass] = sc
|
||||
|
||||
putObjectTar := func(reader io.Reader, info os.FileInfo, object string) error {
|
||||
size := info.Size()
|
||||
metadata := map[string]string{
|
||||
xhttp.AmzStorageClass: sc,
|
||||
|
||||
// Copy metadata and make space for a bit more
|
||||
metaCopy := make(map[string]string, len(metadata)+5)
|
||||
for k, v := range metadata {
|
||||
metaCopy[k] = v
|
||||
}
|
||||
// Shadow now...
|
||||
metadata := metaCopy
|
||||
|
||||
actualSize := size
|
||||
var idxCb func() []byte
|
||||
|
||||
@@ -1268,7 +1268,7 @@ func testAPIPutObjectStreamSigV4Handler(obj ObjectLayer, instanceType, bucketNam
|
||||
t.Fatalf("Test %d: %s: ContentEncoding is set to \"%s\" which is unexpected, expected \"%s\"", i+1, instanceType, objInfo.ContentEncoding, expectedContentEncoding)
|
||||
}
|
||||
buffer := new(bytes.Buffer)
|
||||
r, err := obj.GetObjectNInfo(context.Background(), testCase.bucketName, testCase.objectName, nil, nil, readLock, opts)
|
||||
r, err := obj.GetObjectNInfo(context.Background(), testCase.bucketName, testCase.objectName, nil, nil, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %d: %s: Failed to fetch the copied object: <ERROR> %s", i+1, instanceType, err)
|
||||
}
|
||||
@@ -1559,7 +1559,7 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a
|
||||
if testCase.expectedRespStatus == http.StatusOK {
|
||||
buffer := new(bytes.Buffer)
|
||||
// Fetch the object to check whether the content is same as the one uploaded via PutObject.
|
||||
gr, err := obj.GetObjectNInfo(context.Background(), testCase.bucketName, testCase.objectName, nil, nil, readLock, opts)
|
||||
gr, err := obj.GetObjectNInfo(context.Background(), testCase.bucketName, testCase.objectName, nil, nil, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %d: %s: Failed to fetch the copied object: <ERROR> %s", i, instanceType, err)
|
||||
}
|
||||
@@ -1608,7 +1608,7 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a
|
||||
if testCase.expectedRespStatus == http.StatusOK {
|
||||
buffer := new(bytes.Buffer)
|
||||
// Fetch the object to check whether the content is same as the one uploaded via PutObject.
|
||||
gr, err := obj.GetObjectNInfo(context.Background(), testCase.bucketName, testCase.objectName, nil, nil, readLock, opts)
|
||||
gr, err := obj.GetObjectNInfo(context.Background(), testCase.bucketName, testCase.objectName, nil, nil, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %d: %s: Failed to fetch the copied object: <ERROR> %s", i, instanceType, err)
|
||||
}
|
||||
@@ -1769,7 +1769,7 @@ func testAPICopyObjectPartHandlerSanity(obj ObjectLayer, instanceType, bucketNam
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
r, err := obj.GetObjectNInfo(context.Background(), bucketName, testObject, nil, nil, readLock, ObjectOptions{})
|
||||
r, err := obj.GetObjectNInfo(context.Background(), bucketName, testObject, nil, nil, ObjectOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Test: %s reading completed file failed: <ERROR> %v", instanceType, err)
|
||||
}
|
||||
@@ -2485,7 +2485,7 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
// Note that this goes directly to the file system,
|
||||
// so encryption/compression may interfere at some point.
|
||||
buffers[0].Reset()
|
||||
r, err := obj.GetObjectNInfo(context.Background(), testCase.bucketName, testCase.newObjectName, nil, nil, readLock, opts)
|
||||
r, err := obj.GetObjectNInfo(context.Background(), testCase.bucketName, testCase.newObjectName, nil, nil, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %d: %s reading completed file failed: <ERROR> %v", i, instanceType, err)
|
||||
}
|
||||
|
||||
@@ -46,8 +46,9 @@ func getLambdaEventData(bucket, object string, cred auth.Credentials, r *http.Re
|
||||
secure = globalMinioEndpointURL.Scheme == "https"
|
||||
}
|
||||
|
||||
duration := time.Since(cred.Expiration)
|
||||
if cred.Expiration.IsZero() {
|
||||
duration := time.Until(cred.Expiration)
|
||||
if cred.Expiration.IsZero() || duration > time.Hour {
|
||||
// Always limit to 1 hour.
|
||||
duration = time.Hour
|
||||
}
|
||||
|
||||
|
||||
@@ -353,7 +353,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
|
||||
return false
|
||||
}
|
||||
getOpts.CheckPrecondFn = checkCopyPartPrecondFn
|
||||
gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, readLock, getOpts)
|
||||
gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, getOpts)
|
||||
if err != nil {
|
||||
if isErrPreconditionFailed(err) {
|
||||
return
|
||||
|
||||
+28
-14
@@ -137,16 +137,7 @@ func (sys *S3PeerSys) ListBuckets(ctx context.Context, opts BucketOptions) (resu
|
||||
func (sys *S3PeerSys) GetBucketInfo(ctx context.Context, bucket string, opts BucketOptions) (binfo BucketInfo, err error) {
|
||||
g := errgroup.WithNErrs(len(sys.peerClients))
|
||||
|
||||
bucketInfos := make([]BucketInfo, len(sys.peerClients)+1)
|
||||
|
||||
bucketInfo, err := getBucketInfoLocal(ctx, bucket, opts)
|
||||
if err != nil {
|
||||
return BucketInfo{}, err
|
||||
}
|
||||
|
||||
errs := []error{nil}
|
||||
bucketInfos[0] = bucketInfo
|
||||
|
||||
bucketInfos := make([]BucketInfo, len(sys.peerClients))
|
||||
for idx, client := range sys.peerClients {
|
||||
idx := idx
|
||||
client := client
|
||||
@@ -158,19 +149,29 @@ func (sys *S3PeerSys) GetBucketInfo(ctx context.Context, bucket string, opts Buc
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bucketInfos[idx+1] = bucketInfo
|
||||
bucketInfos[idx] = bucketInfo
|
||||
return nil
|
||||
}, idx)
|
||||
}
|
||||
|
||||
errs = append(errs, g.Wait()...)
|
||||
errs := g.Wait()
|
||||
|
||||
bucketInfo, err := getBucketInfoLocal(ctx, bucket, opts)
|
||||
errs = append(errs, err)
|
||||
bucketInfos = append(bucketInfos, bucketInfo)
|
||||
|
||||
quorum := (len(sys.allPeerClients) / 2)
|
||||
if err = reduceReadQuorumErrs(ctx, errs, bucketOpIgnoredErrs, quorum); err != nil {
|
||||
return BucketInfo{}, toObjectErr(err, bucket)
|
||||
}
|
||||
|
||||
return bucketInfo, nil
|
||||
|
||||
for i, err := range errs {
|
||||
if err == nil {
|
||||
return bucketInfos[i], nil
|
||||
}
|
||||
}
|
||||
|
||||
return BucketInfo{}, toObjectErr(errVolumeNotFound, bucket)
|
||||
}
|
||||
|
||||
func (client *peerS3Client) ListBuckets(ctx context.Context, opts BucketOptions) ([]BucketInfo, error) {
|
||||
@@ -224,6 +225,19 @@ func (sys *S3PeerSys) MakeBucket(ctx context.Context, bucket string, opts MakeBu
|
||||
|
||||
quorum := (len(sys.allPeerClients) / 2) + 1
|
||||
err := reduceWriteQuorumErrs(ctx, errs, bucketOpIgnoredErrs, quorum)
|
||||
|
||||
// Perform MRF on missing buckets for temporary errors.
|
||||
for _, err := range errs {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if errors.Is(err, errPeerOffline) || errors.Is(err, errDiskNotFound) ||
|
||||
isNetworkError(err) {
|
||||
globalMRFState.addPartialOp(partialOperation{
|
||||
bucket: bucket,
|
||||
})
|
||||
}
|
||||
}
|
||||
return toObjectErr(err, bucket)
|
||||
}
|
||||
|
||||
|
||||
@@ -58,8 +58,6 @@ var globalHandlers = []mux.MiddlewareFunc{
|
||||
setCrossDomainPolicy,
|
||||
// Limits all body and header sizes to a maximum fixed limit
|
||||
setRequestLimitHandler,
|
||||
// Network statistics
|
||||
setHTTPStatsHandler,
|
||||
// Validate all the incoming requests.
|
||||
setRequestValidityHandler,
|
||||
// set x-amz-request-id header.
|
||||
|
||||
@@ -188,7 +188,7 @@ func (api objectAPIHandlers) getObjectInArchiveFileHandler(ctx context.Context,
|
||||
end = zipObjInfo.Size
|
||||
}
|
||||
rs := &HTTPRangeSpec{Start: file.Offset, End: end}
|
||||
gr, err := objectAPI.GetObjectNInfo(ctx, bucket, zipPath, rs, nil, readLock, opts)
|
||||
gr, err := objectAPI.GetObjectNInfo(ctx, bucket, zipPath, rs, nil, opts)
|
||||
if err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return
|
||||
@@ -331,7 +331,7 @@ func getFilesListFromZIPObject(ctx context.Context, objectAPI ObjectLayer, bucke
|
||||
var objSize int64
|
||||
for {
|
||||
rs := &HTTPRangeSpec{IsSuffixLength: true, Start: int64(-size)}
|
||||
gr, err := objectAPI.GetObjectNInfo(ctx, bucket, object, rs, nil, readLock, opts)
|
||||
gr, err := objectAPI.GetObjectNInfo(ctx, bucket, object, rs, nil, opts)
|
||||
if err != nil {
|
||||
return nil, ObjectInfo{}, err
|
||||
}
|
||||
|
||||
+26
-7
@@ -105,6 +105,14 @@ var ServerFlags = []cli.Flag{
|
||||
Value: 10 * time.Minute,
|
||||
EnvVar: "MINIO_CONN_WRITE_DEADLINE",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "ftp",
|
||||
Usage: "enable and configure an FTP(Secure) server",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "sftp",
|
||||
Usage: "enable and configure an SFTP server",
|
||||
},
|
||||
}
|
||||
|
||||
var gatewayCmd = cli.Command{
|
||||
@@ -145,22 +153,23 @@ FLAGS:
|
||||
{{range .VisibleFlags}}{{.}}
|
||||
{{end}}{{end}}
|
||||
EXAMPLES:
|
||||
1. Start minio server on "/home/shared" directory.
|
||||
1. Start MinIO server on "/home/shared" directory.
|
||||
{{.Prompt}} {{.HelpName}} /home/shared
|
||||
|
||||
2. Start single node server with 64 local drives "/mnt/data1" to "/mnt/data64".
|
||||
{{.Prompt}} {{.HelpName}} /mnt/data{1...64}
|
||||
|
||||
3. Start distributed minio server on an 32 node setup with 32 drives each, run following command on all the nodes
|
||||
{{.Prompt}} {{.EnvVarSetCommand}} MINIO_ROOT_USER{{.AssignmentOperator}}minio
|
||||
{{.Prompt}} {{.EnvVarSetCommand}} MINIO_ROOT_PASSWORD{{.AssignmentOperator}}miniostorage
|
||||
3. Start distributed MinIO server on an 32 node setup with 32 drives each, run following command on all the nodes
|
||||
{{.Prompt}} {{.HelpName}} http://node{1...32}.example.com/mnt/export{1...32}
|
||||
|
||||
4. Start distributed minio server in an expanded setup, run the following command on all the nodes
|
||||
{{.Prompt}} {{.EnvVarSetCommand}} MINIO_ROOT_USER{{.AssignmentOperator}}minio
|
||||
{{.Prompt}} {{.EnvVarSetCommand}} MINIO_ROOT_PASSWORD{{.AssignmentOperator}}miniostorage
|
||||
4. Start distributed MinIO server in an expanded setup, run the following command on all the nodes
|
||||
{{.Prompt}} {{.HelpName}} http://node{1...16}.example.com/mnt/export{1...32} \
|
||||
http://node{17...64}.example.com/mnt/export{1...64}
|
||||
|
||||
5. Start distributed MinIO server, with FTP and SFTP servers on all interfaces via port 8021, 8022 respectively
|
||||
{{.Prompt}} {{.HelpName}} http://node{1...4}.example.com/mnt/export{1...4} \
|
||||
--ftp="address=:8021" --ftp="passive-port-range=30000-40000" \
|
||||
--sftp="address=:8022" --sftp="ssh-private-key=${HOME}/.ssh/id_rsa"
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -667,6 +676,16 @@ func serverMain(ctx *cli.Context) {
|
||||
logger.FatalIf(newConsoleServerFn().Serve(), "Unable to initialize console server")
|
||||
}()
|
||||
}
|
||||
|
||||
// if we see FTP args, start FTP if possible
|
||||
if len(ctx.StringSlice("ftp")) > 0 {
|
||||
go startFTPServer(ctx)
|
||||
}
|
||||
|
||||
// If we see SFTP args, start SFTP if possible
|
||||
if len(ctx.StringSlice("sftp")) > 0 {
|
||||
go startSFTPServer(ctx)
|
||||
}
|
||||
}()
|
||||
|
||||
// Background all other operations such as initializing bucket metadata etc.
|
||||
|
||||
@@ -127,16 +127,14 @@ func printServerCommonMsg(apiEndpoints []string) {
|
||||
apiEndpointStr := strings.Join(apiEndpoints, " ")
|
||||
|
||||
// Colorize the message and print.
|
||||
logger.Info(color.Blue("API: ") + color.Bold(fmt.Sprintf("%s ", apiEndpointStr)))
|
||||
logger.Info(color.Blue("S3-API: ") + color.Bold(fmt.Sprintf("%s ", apiEndpointStr)))
|
||||
if color.IsTerminal() && (!globalCLIContext.Anonymous && !globalCLIContext.JSON) {
|
||||
logger.Info(color.Blue("RootUser: ") + color.Bold(fmt.Sprintf("%s ", cred.AccessKey)))
|
||||
logger.Info(color.Blue("RootPass: ") + color.Bold(fmt.Sprintf("%s ", cred.SecretKey)))
|
||||
logger.Info(color.Blue("RootPass: ") + color.Bold(fmt.Sprintf("%s \n", cred.SecretKey)))
|
||||
if region != "" {
|
||||
logger.Info(color.Blue("Region: ") + color.Bold(fmt.Sprintf(getFormatStr(len(region), 2), region)))
|
||||
}
|
||||
}
|
||||
printEventNotifiers()
|
||||
printLambdaTargets()
|
||||
|
||||
if globalBrowserEnabled {
|
||||
consoleEndpointStr := strings.Join(stripStandardPorts(getConsoleEndpoints(), globalMinioConsoleHost), " ")
|
||||
@@ -146,6 +144,9 @@ func printServerCommonMsg(apiEndpoints []string) {
|
||||
logger.Info(color.Blue("RootPass: ") + color.Bold(fmt.Sprintf("%s ", cred.SecretKey)))
|
||||
}
|
||||
}
|
||||
|
||||
printEventNotifiers()
|
||||
printLambdaTargets()
|
||||
}
|
||||
|
||||
// Prints startup message for Object API access, prints link to our SDK documentation.
|
||||
|
||||
@@ -0,0 +1,442 @@
|
||||
// Copyright (c) 2015-2023 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/pkg/sftp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type sftpDriver struct {
|
||||
permissions *ssh.Permissions
|
||||
endpoint string
|
||||
}
|
||||
|
||||
//msgp:ignore sftpMetrics
|
||||
type sftpMetrics struct{}
|
||||
|
||||
var globalSftpMetrics sftpMetrics
|
||||
|
||||
func sftpTrace(s *sftp.Request, startTime time.Time, source string, user string, err error) madmin.TraceInfo {
|
||||
var errStr string
|
||||
if err != nil {
|
||||
errStr = err.Error()
|
||||
}
|
||||
return madmin.TraceInfo{
|
||||
TraceType: madmin.TraceFTP,
|
||||
Time: startTime,
|
||||
NodeName: globalLocalNodeName,
|
||||
FuncName: fmt.Sprintf("sftp USER=%s COMMAND=%s PARAM=%s, Source=%s", user, s.Method, s.Filepath, source),
|
||||
Duration: time.Since(startTime),
|
||||
Path: s.Filepath,
|
||||
Error: errStr,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *sftpMetrics) log(s *sftp.Request, user string) func(err error) {
|
||||
startTime := time.Now()
|
||||
source := getSource(2)
|
||||
return func(err error) {
|
||||
globalTrace.Publish(sftpTrace(s, startTime, source, user, err))
|
||||
}
|
||||
}
|
||||
|
||||
// NewSFTPDriver initializes sftp.Handlers implementation of following interfaces
|
||||
//
|
||||
// - sftp.Fileread
|
||||
// - sftp.Filewrite
|
||||
// - sftp.Filelist
|
||||
// - sftp.Filecmd
|
||||
func NewSFTPDriver(perms *ssh.Permissions) sftp.Handlers {
|
||||
handler := &sftpDriver{endpoint: fmt.Sprintf("127.0.0.1:%s", globalMinioPort), permissions: perms}
|
||||
return sftp.Handlers{
|
||||
FileGet: handler,
|
||||
FilePut: handler,
|
||||
FileCmd: handler,
|
||||
FileList: handler,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *sftpDriver) getMinIOClient() (*minio.Client, error) {
|
||||
ui, ok := globalIAMSys.GetUser(context.Background(), f.AccessKey())
|
||||
if !ok && !globalIAMSys.LDAPConfig.Enabled() {
|
||||
return nil, errNoSuchUser
|
||||
}
|
||||
if !ok && globalIAMSys.LDAPConfig.Enabled() {
|
||||
targetUser, targetGroups, err := globalIAMSys.LDAPConfig.LookupUserDN(f.AccessKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ldapPolicies, _ := globalIAMSys.PolicyDBGet(targetUser, false, targetGroups...)
|
||||
if len(ldapPolicies) == 0 {
|
||||
return nil, errAuthentication
|
||||
}
|
||||
expiryDur, err := globalIAMSys.LDAPConfig.GetExpiryDuration("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
claims := make(map[string]interface{})
|
||||
claims[expClaim] = UTCNow().Add(expiryDur).Unix()
|
||||
claims[ldapUser] = targetUser
|
||||
claims[ldapUserN] = f.AccessKey()
|
||||
|
||||
cred, err := auth.GetNewCredentialsWithMetadata(claims, globalActiveCred.SecretKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set the parent of the temporary access key, this is useful
|
||||
// in obtaining service accounts by this cred.
|
||||
cred.ParentUser = targetUser
|
||||
|
||||
// Set this value to LDAP groups, LDAP user can be part
|
||||
// of large number of groups
|
||||
cred.Groups = targetGroups
|
||||
|
||||
// Set the newly generated credentials, policyName is empty on purpose
|
||||
// LDAP policies are applied automatically using their ldapUser, ldapGroups
|
||||
// mapping.
|
||||
updatedAt, err := globalIAMSys.SetTempUser(context.Background(), cred.AccessKey, cred, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Call hook for site replication.
|
||||
logger.LogIf(context.Background(), globalSiteReplicationSys.IAMChangeHook(context.Background(), madmin.SRIAMItem{
|
||||
Type: madmin.SRIAMItemSTSAcc,
|
||||
STSCredential: &madmin.SRSTSCredential{
|
||||
AccessKey: cred.AccessKey,
|
||||
SecretKey: cred.SecretKey,
|
||||
SessionToken: cred.SessionToken,
|
||||
ParentUser: cred.ParentUser,
|
||||
},
|
||||
UpdatedAt: updatedAt,
|
||||
}))
|
||||
|
||||
return minio.New(f.endpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(cred.AccessKey, cred.SecretKey, cred.SessionToken),
|
||||
Secure: globalIsTLS,
|
||||
Transport: globalRemoteTargetTransport,
|
||||
})
|
||||
}
|
||||
|
||||
// ok == true - at this point
|
||||
|
||||
if ui.Credentials.IsTemp() {
|
||||
// Temporary credentials are not allowed.
|
||||
return nil, errAuthentication
|
||||
}
|
||||
|
||||
return minio.New(f.endpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(ui.Credentials.AccessKey, ui.Credentials.SecretKey, ""),
|
||||
Secure: globalIsTLS,
|
||||
Transport: globalRemoteTargetTransport,
|
||||
})
|
||||
}
|
||||
|
||||
func (f *sftpDriver) AccessKey() string {
|
||||
return f.permissions.CriticalOptions["accessKey"]
|
||||
}
|
||||
|
||||
func (f *sftpDriver) Fileread(r *sftp.Request) (ra io.ReaderAt, err error) {
|
||||
stopFn := globalSftpMetrics.log(r, f.AccessKey())
|
||||
defer stopFn(err)
|
||||
|
||||
flags := r.Pflags()
|
||||
if !flags.Read {
|
||||
// sanity check
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
bucket, object := path2BucketObject(r.Filepath)
|
||||
if bucket == "" {
|
||||
return nil, errors.New("bucket name cannot be empty")
|
||||
}
|
||||
|
||||
clnt, err := f.getMinIOClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj, err := clnt.GetObject(context.Background(), bucket, object, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = obj.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
type writerAt struct {
|
||||
w *io.PipeWriter
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
func (w *writerAt) Close() error {
|
||||
err := w.w.Close()
|
||||
w.wg.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *writerAt) WriteAt(b []byte, offset int64) (n int, err error) {
|
||||
return w.w.Write(b)
|
||||
}
|
||||
|
||||
func (f *sftpDriver) Filewrite(r *sftp.Request) (w io.WriterAt, err error) {
|
||||
stopFn := globalSftpMetrics.log(r, f.AccessKey())
|
||||
defer stopFn(err)
|
||||
|
||||
flags := r.Pflags()
|
||||
if !flags.Write {
|
||||
// sanity check
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
bucket, object := path2BucketObject(r.Filepath)
|
||||
if bucket == "" {
|
||||
return nil, errors.New("bucket name cannot be empty")
|
||||
}
|
||||
|
||||
clnt, err := f.getMinIOClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
|
||||
wa := &writerAt{w: pw, wg: &sync.WaitGroup{}}
|
||||
wa.wg.Add(1)
|
||||
go func() {
|
||||
_, err := clnt.PutObject(r.Context(), bucket, object, pr, -1, minio.PutObjectOptions{SendContentMd5: true})
|
||||
pr.CloseWithError(err)
|
||||
wa.wg.Done()
|
||||
}()
|
||||
return wa, nil
|
||||
}
|
||||
|
||||
func (f *sftpDriver) Filecmd(r *sftp.Request) (err error) {
|
||||
stopFn := globalSftpMetrics.log(r, f.AccessKey())
|
||||
defer stopFn(err)
|
||||
|
||||
clnt, err := f.getMinIOClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case "Setstat", "Rename", "Link", "Symlink":
|
||||
return NotImplemented{}
|
||||
|
||||
case "Rmdir":
|
||||
bucket, prefix := path2BucketObject(r.Filepath)
|
||||
if bucket == "" {
|
||||
return errors.New("deleting all buckets not allowed")
|
||||
}
|
||||
|
||||
cctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
objectsCh := make(chan minio.ObjectInfo)
|
||||
|
||||
// Send object names that are needed to be removed to objectsCh
|
||||
go func() {
|
||||
defer close(objectsCh)
|
||||
opts := minio.ListObjectsOptions{Prefix: prefix, Recursive: true}
|
||||
for object := range clnt.ListObjects(cctx, bucket, opts) {
|
||||
if object.Err != nil {
|
||||
return
|
||||
}
|
||||
objectsCh <- object
|
||||
}
|
||||
}()
|
||||
|
||||
// Call RemoveObjects API
|
||||
for err := range clnt.RemoveObjects(context.Background(), bucket, objectsCh, minio.RemoveObjectsOptions{}) {
|
||||
if err.Err != nil {
|
||||
return err.Err
|
||||
}
|
||||
}
|
||||
|
||||
case "Remove":
|
||||
bucket, object := path2BucketObject(r.Filepath)
|
||||
if bucket == "" {
|
||||
return errors.New("bucket name cannot be empty")
|
||||
}
|
||||
|
||||
return clnt.RemoveObject(context.Background(), bucket, object, minio.RemoveObjectOptions{})
|
||||
|
||||
case "Mkdir":
|
||||
bucket, prefix := path2BucketObject(r.Filepath)
|
||||
if bucket == "" {
|
||||
return errors.New("bucket name cannot be empty")
|
||||
}
|
||||
|
||||
dirPath := buildMinioDir(prefix)
|
||||
|
||||
_, err = clnt.PutObject(context.Background(), bucket, dirPath, bytes.NewReader([]byte("")), 0,
|
||||
// Always send Content-MD5 to succeed with bucket with
|
||||
// locking enabled. There is no performance hit since
|
||||
// this is always an empty object
|
||||
minio.PutObjectOptions{SendContentMd5: true},
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return NotImplemented{}
|
||||
}
|
||||
|
||||
type listerAt []os.FileInfo
|
||||
|
||||
// Modeled after strings.Reader's ReadAt() implementation
|
||||
func (f listerAt) ListAt(ls []os.FileInfo, offset int64) (int, error) {
|
||||
var n int
|
||||
if offset >= int64(len(f)) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = copy(ls, f[offset:])
|
||||
if n < len(ls) {
|
||||
return n, io.EOF
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (f *sftpDriver) Filelist(r *sftp.Request) (la sftp.ListerAt, err error) {
|
||||
stopFn := globalSftpMetrics.log(r, f.AccessKey())
|
||||
defer stopFn(err)
|
||||
|
||||
clnt, err := f.getMinIOClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case "List":
|
||||
var files []os.FileInfo
|
||||
|
||||
bucket, prefix := path2BucketObject(r.Filepath)
|
||||
if bucket == "" {
|
||||
buckets, err := clnt.ListBuckets(r.Context())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, bucket := range buckets {
|
||||
files = append(files, &minioFileInfo{
|
||||
p: bucket.Name,
|
||||
info: minio.ObjectInfo{Key: bucket.Name, LastModified: bucket.CreationDate},
|
||||
isDir: true,
|
||||
})
|
||||
}
|
||||
|
||||
return listerAt(files), nil
|
||||
}
|
||||
|
||||
prefix = retainSlash(prefix)
|
||||
|
||||
for object := range clnt.ListObjects(r.Context(), bucket, minio.ListObjectsOptions{
|
||||
Prefix: prefix,
|
||||
Recursive: false,
|
||||
}) {
|
||||
if object.Err != nil {
|
||||
return nil, object.Err
|
||||
}
|
||||
|
||||
if object.Key == prefix {
|
||||
continue
|
||||
}
|
||||
|
||||
isDir := strings.HasSuffix(object.Key, SlashSeparator)
|
||||
files = append(files, &minioFileInfo{
|
||||
p: pathClean(strings.TrimPrefix(object.Key, prefix)),
|
||||
info: object,
|
||||
isDir: isDir,
|
||||
})
|
||||
}
|
||||
|
||||
return listerAt(files), nil
|
||||
|
||||
case "Stat":
|
||||
if r.Filepath == SlashSeparator {
|
||||
return listerAt{&minioFileInfo{
|
||||
p: r.Filepath,
|
||||
isDir: true,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
bucket, object := path2BucketObject(r.Filepath)
|
||||
if bucket == "" {
|
||||
return nil, errors.New("bucket name cannot be empty")
|
||||
}
|
||||
|
||||
if object == "" {
|
||||
ok, err := clnt.BucketExists(context.Background(), bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return listerAt{&minioFileInfo{
|
||||
p: pathClean(bucket),
|
||||
info: minio.ObjectInfo{Key: bucket},
|
||||
isDir: true,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
objInfo, err := clnt.StatObject(context.Background(), bucket, object, minio.StatObjectOptions{})
|
||||
if err != nil {
|
||||
if minio.ToErrorResponse(err).Code == "NoSuchKey" {
|
||||
// dummy return to satisfy LIST (stat -> list) behavior.
|
||||
return listerAt{&minioFileInfo{
|
||||
p: pathClean(object),
|
||||
info: minio.ObjectInfo{Key: object},
|
||||
isDir: true,
|
||||
}}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isDir := strings.HasSuffix(objInfo.Key, SlashSeparator)
|
||||
return listerAt{&minioFileInfo{
|
||||
p: pathClean(object),
|
||||
info: objInfo,
|
||||
isDir: isDir,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
return nil, NotImplemented{}
|
||||
}
|
||||
@@ -112,3 +112,6 @@ var errIAMNotInitialized = errors.New("IAM sub-system is being initialized, plea
|
||||
|
||||
// error returned when upload id not found
|
||||
var errUploadIDNotFound = errors.New("Specified Upload ID is not found")
|
||||
|
||||
// error returned when PartNumber is greater than the maximum allowed 10000 parts
|
||||
var errInvalidMaxParts = errors.New("Part number is greater than the maximum allowed 10000 parts")
|
||||
|
||||
@@ -1583,8 +1583,9 @@ func (x *xlMetaV2) AddVersion(fi FileInfo) error {
|
||||
if len(k) > len(ReservedMetadataPrefixLower) && strings.EqualFold(k[:len(ReservedMetadataPrefixLower)], ReservedMetadataPrefixLower) {
|
||||
// Skip tierFVID, tierFVMarker keys; it's used
|
||||
// only for creating free-version.
|
||||
// Skip xMinIOHealing, it's used only in RenameData
|
||||
switch k {
|
||||
case tierFVIDKey, tierFVMarkerKey:
|
||||
case tierFVIDKey, tierFVMarkerKey, xMinIOHealing:
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -1010,3 +1010,51 @@ func Test_mergeXLV2Versions2(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestXMinIOHealingSkip(t *testing.T) {
|
||||
xl := xlMetaV2{}
|
||||
failOnErr := func(err error) {
|
||||
t.Helper()
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed with %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
fi := FileInfo{
|
||||
Volume: "volume",
|
||||
Name: "object-name",
|
||||
VersionID: "756100c6-b393-4981-928a-d49bbc164741",
|
||||
IsLatest: true,
|
||||
Deleted: false,
|
||||
ModTime: time.Now(),
|
||||
Size: 1 << 10,
|
||||
Mode: 0,
|
||||
Erasure: ErasureInfo{
|
||||
Algorithm: ReedSolomon.String(),
|
||||
DataBlocks: 4,
|
||||
ParityBlocks: 2,
|
||||
BlockSize: 10000,
|
||||
Index: 1,
|
||||
Distribution: []int{1, 2, 3, 4, 5, 6, 7, 8},
|
||||
Checksums: []ChecksumInfo{{
|
||||
PartNumber: 1,
|
||||
Algorithm: HighwayHash256S,
|
||||
Hash: nil,
|
||||
}},
|
||||
},
|
||||
NumVersions: 1,
|
||||
}
|
||||
|
||||
fi.SetHealing()
|
||||
failOnErr(xl.AddVersion(fi))
|
||||
|
||||
var err error
|
||||
fi, err = xl.ToFileInfo(fi.Volume, fi.Name, fi.VersionID, false)
|
||||
if err != nil {
|
||||
t.Fatalf("xl.ToFileInfo failed with %v", err)
|
||||
}
|
||||
|
||||
if fi.Healing() {
|
||||
t.Fatal("Expected fi.Healing to be false")
|
||||
}
|
||||
}
|
||||
|
||||
+19
-32
@@ -365,10 +365,10 @@ func (s *xlStorage) Healing() *healingTracker {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var h healingTracker
|
||||
h := newHealingTracker()
|
||||
_, err = h.UnmarshalMsg(b)
|
||||
logger.LogIf(GlobalContext, err)
|
||||
return &h
|
||||
return h
|
||||
}
|
||||
|
||||
// checkODirectDiskSupport asks the disk to write some data
|
||||
@@ -435,11 +435,19 @@ func (s *xlStorage) readMetadata(ctx context.Context, itemPath string) ([]byte,
|
||||
func (s *xlStorage) NSScanner(ctx context.Context, cache dataUsageCache, updates chan<- dataUsageEntry, scanMode madmin.HealScanMode) (dataUsageCache, error) {
|
||||
atomic.AddInt32(&s.scanning, 1)
|
||||
defer atomic.AddInt32(&s.scanning, -1)
|
||||
var err error
|
||||
stopFn := globalScannerMetrics.log(scannerMetricScanBucketDrive, s.diskPath, cache.Info.Name)
|
||||
defer func() {
|
||||
res := make(map[string]string)
|
||||
if err != nil {
|
||||
res["err"] = err.Error()
|
||||
}
|
||||
stopFn(res)
|
||||
}()
|
||||
|
||||
// Updates must be closed before we return.
|
||||
defer close(updates)
|
||||
var lc *lifecycle.Lifecycle
|
||||
var err error
|
||||
|
||||
// Check if the current bucket has a configured lifecycle policy
|
||||
if globalLifecycleSys != nil {
|
||||
@@ -1671,35 +1679,10 @@ func (s *xlStorage) openFileDirect(path string, mode int) (f *os.File, err error
|
||||
}
|
||||
|
||||
func (s *xlStorage) openFileSync(filePath string, mode int) (f *os.File, err error) {
|
||||
// Create top level directories if they don't exist.
|
||||
// with mode 0777 mkdir honors system umask.
|
||||
if err = mkdirAll(pathutil.Dir(filePath), 0o777); err != nil {
|
||||
return nil, osErrToFileErr(err)
|
||||
}
|
||||
|
||||
w, err := OpenFile(filePath, mode|writeMode, 0o666)
|
||||
if err != nil {
|
||||
// File path cannot be verified since one of the parents is a file.
|
||||
switch {
|
||||
case isSysErrIsDir(err):
|
||||
return nil, errIsNotRegular
|
||||
case osIsPermission(err):
|
||||
return nil, errFileAccessDenied
|
||||
case isSysErrNotDir(err):
|
||||
return nil, errFileAccessDenied
|
||||
case isSysErrIO(err):
|
||||
return nil, errFaultyDisk
|
||||
case isSysErrTooManyFiles(err):
|
||||
return nil, errTooManyOpenFiles
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return w, nil
|
||||
return s.openFile(filePath, mode|writeMode)
|
||||
}
|
||||
|
||||
func (s *xlStorage) openFileNoSync(filePath string, mode int) (f *os.File, err error) {
|
||||
func (s *xlStorage) openFile(filePath string, mode int) (f *os.File, err error) {
|
||||
// Create top level directories if they don't exist.
|
||||
// with mode 0777 mkdir honors system umask.
|
||||
if err = mkdirAll(pathutil.Dir(filePath), 0o777); err != nil {
|
||||
@@ -1950,7 +1933,7 @@ func (s *xlStorage) writeAll(ctx context.Context, volume string, path string, b
|
||||
}
|
||||
w, err = s.openFileSync(filePath, flags)
|
||||
} else {
|
||||
w, err = s.openFileNoSync(filePath, flags)
|
||||
w, err = s.openFile(filePath, flags)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -2357,7 +2340,11 @@ func (s *xlStorage) RenameData(ctx context.Context, srcVolume, srcPath string, f
|
||||
// suspended or disabled on this bucket. RenameData will replace
|
||||
// the 'null' version. We add a free-version to track its tiered
|
||||
// content for asynchronous deletion.
|
||||
if fi.VersionID == "" && !fi.IsRestoreObjReq() {
|
||||
//
|
||||
// Note: RestoreObject and HealObject requests don't end up replacing the
|
||||
// null version and therefore don't require the free-version to track
|
||||
// anything
|
||||
if fi.VersionID == "" && !fi.IsRestoreObjReq() && !fi.Healing() {
|
||||
// Note: Restore object request reuses PutObject/Multipart
|
||||
// upload to copy back its data from the remote tier. This
|
||||
// doesn't replace the existing version, so we don't need to add
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
# MinIO FTP/SFTP Server
|
||||
|
||||
MinIO natively supports FTP/SFTP protocol, this allows any ftp/sftp client to upload and download files.
|
||||
|
||||
Currently supported `FTP/SFTP` operations are as follows:
|
||||
|
||||
| ftp-client commands | supported |
|
||||
|:-------------------:|:----------|
|
||||
| get | yes |
|
||||
| put | yes |
|
||||
| ls | yes |
|
||||
| mkdir | yes |
|
||||
| rmdir | yes |
|
||||
| delete | yes |
|
||||
| append | no |
|
||||
| rename | no |
|
||||
|
||||
MinIO supports following FTP/SFTP based protocols to access and manage data.
|
||||
|
||||
- Secure File Transfer Protocol (SFTP) – Defined by the Internet Engineering Task Force (IETF) as an
|
||||
extended version of SSH 2.0, allowing file transfer over SSH and for use with Transport Layer
|
||||
Security (TLS) and VPN applications.
|
||||
|
||||
- File Transfer Protocol over SSL/TLS (FTPS) – Encrypted FTP communication via TLS certificates.
|
||||
|
||||
- File Transfer Protocol (FTP) – Defined by RFC114 originally, and replaced by RFC765 and RFC959
|
||||
unencrypted FTP communication (Not-recommended)
|
||||
|
||||
## Scope
|
||||
|
||||
- All IAM Credentials are allowed access excluding rotating credentials, rotating credentials
|
||||
are not allowed to login via FTP/SFTP ports, you must use S3 API port for if you are using
|
||||
rotating credentials.
|
||||
|
||||
- Access to bucket(s) and object(s) are governed via IAM policies associated with the incoming
|
||||
login credentials.
|
||||
|
||||
- Allows authentication and access for all
|
||||
- Built-in IDP users and their respective service accounts
|
||||
- LDAP/AD users and their respective service accounts
|
||||
- OpenID/OIDC service accounts
|
||||
|
||||
- On versioned buckets, FTP/SFTP only operates on latest objects, if you need to retrieve
|
||||
an older version you must use an `S3 API client` such as [`mc`](https://github.com/minio/mc).
|
||||
|
||||
- All features currently used by your buckets will work as is without any changes
|
||||
- SSE (Server Side Encryption)
|
||||
- Replication (Server Side Replication)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- It is assumed you have users created and configured with relevant access policies, to start with
|
||||
use basic "readwrite" canned policy to test all the operations before you finalize on what level
|
||||
of restrictions are needed for a user.
|
||||
|
||||
- No "admin:*" operations are needed for FTP/SFTP access to the bucket(s) and object(s), so you may
|
||||
skip them for restrictions.
|
||||
|
||||
## Usage
|
||||
|
||||
Start MinIO in a distributed setup, with 'ftp/sftp' enabled.
|
||||
|
||||
```
|
||||
minio server http://server{1...4}/disk{1...4}
|
||||
--ftp="address=:8021" --ftp="passive-port-range=30000-40000" \
|
||||
--sftp="address=:8022" --sftp="ssh-private-key=/home/miniouser/.ssh/id_rsa"
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
Following example shows connecting via ftp client using `minioadmin` credentials, and list a bucket named `runner`:
|
||||
|
||||
```
|
||||
ftp localhost -P 8021
|
||||
Connected to localhost.
|
||||
220 Welcome to MinIO FTP Server
|
||||
Name (localhost:user): minioadmin
|
||||
331 User name ok, password required
|
||||
Password:
|
||||
230 Password ok, continue
|
||||
Remote system type is UNIX.
|
||||
Using binary mode to transfer files.
|
||||
ftp> ls runner/
|
||||
229 Entering Extended Passive Mode (|||39155|)
|
||||
150 Opening ASCII mode data connection for file list
|
||||
drwxrwxrwx 1 nobody nobody 0 Jan 1 00:00 chunkdocs/
|
||||
drwxrwxrwx 1 nobody nobody 0 Jan 1 00:00 testdir/
|
||||
...
|
||||
```
|
||||
|
||||
Following example shows how to list an object and download it locally via `ftp` client:
|
||||
|
||||
```
|
||||
ftp> ls runner/chunkdocs/metadata
|
||||
229 Entering Extended Passive Mode (|||44269|)
|
||||
150 Opening ASCII mode data connection for file list
|
||||
-rwxrwxrwx 1 nobody nobody 45 Apr 1 06:13 chunkdocs/metadata
|
||||
226 Closing data connection, sent 75 bytes
|
||||
ftp> get
|
||||
(remote-file) runner/chunkdocs/metadata
|
||||
(local-file) test
|
||||
local: test remote: runner/chunkdocs/metadata
|
||||
229 Entering Extended Passive Mode (|||37785|)
|
||||
150 Data transfer starting 45 bytes
|
||||
45 3.58 KiB/s
|
||||
226 Closing data connection, sent 45 bytes
|
||||
45 bytes received in 00:00 (3.55 KiB/s)
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
Following example shows connecting via sftp client using `minioadmin` credentials, and list a bucket named `runner`:
|
||||
|
||||
```
|
||||
sftp -P 8022 minioadmin@localhost
|
||||
minioadmin@localhost's password:
|
||||
Connected to localhost.
|
||||
sftp> ls runner/
|
||||
chunkdocs testdir
|
||||
```
|
||||
|
||||
Following example shows how to download an object locally via `sftp` client:
|
||||
|
||||
```
|
||||
sftp> get runner/chunkdocs/metadata metadata
|
||||
Fetching /runner/chunkdocs/metadata to metadata
|
||||
metadata 100% 226 16.6KB/s 00:00
|
||||
sftp>
|
||||
```
|
||||
|
||||
## Advanced options
|
||||
|
||||
### Change default FTP port
|
||||
|
||||
Default port '8021' can be changed via
|
||||
|
||||
```
|
||||
--ftp="address=:3021"
|
||||
```
|
||||
|
||||
### Change FTP passive port range
|
||||
|
||||
By default FTP requests OS to give a free port automatically, however you may want to restrict
|
||||
this to specific ports in certain restricted environments via
|
||||
|
||||
```
|
||||
--ftp="passive-port-range=30000-40000"
|
||||
```
|
||||
|
||||
### Change default SFTP port
|
||||
|
||||
Default port '8022' can be changed via
|
||||
|
||||
```
|
||||
--sftp="address=:3022"
|
||||
```
|
||||
|
||||
### TLS (FTP)
|
||||
|
||||
Unlike SFTP server, FTP server is insecure by default. To operate under TLS mode, you need to provide certificates via
|
||||
|
||||
```
|
||||
--ftp="tls-private-key=path/to/private.key" --ftp="tls-public-cert=path/to/public.crt"
|
||||
```
|
||||
|
||||
> NOTE: if MinIO distributed setup is already configured to run under TLS, FTP will automatically use the relevant
|
||||
> certs from the server certificate chain, this is mainly to add simplicity of setup. However if you wish to terminate
|
||||
> TLS certificates via a different domain for your FTP servers you may choose the above command line options.
|
||||
|
||||
@@ -2,7 +2,7 @@ version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:RELEASE.2023-03-24T21-41-23Z
|
||||
image: quay.io/minio/minio:RELEASE.2023-04-13T03-08-07Z
|
||||
command: server --console-address ":9001" http://minio{1...4}/data{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
|
||||
@@ -32,7 +32,7 @@ require (
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/inconshreveable/mousetrap v1.1.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/klauspost/compress v1.16.3
|
||||
github.com/klauspost/compress v1.16.4
|
||||
github.com/klauspost/cpuid/v2 v2.2.4
|
||||
github.com/klauspost/filepathx v1.1.1
|
||||
github.com/klauspost/pgzip v1.2.5
|
||||
@@ -40,15 +40,15 @@ require (
|
||||
github.com/klauspost/reedsolomon v1.11.7
|
||||
github.com/lib/pq v1.10.7
|
||||
github.com/lithammer/shortuuid/v4 v4.0.0
|
||||
github.com/miekg/dns v1.1.52
|
||||
github.com/miekg/dns v1.1.53
|
||||
github.com/minio/cli v1.24.2
|
||||
github.com/minio/console v0.26.3
|
||||
github.com/minio/csvparser v1.0.0
|
||||
github.com/minio/dperf v0.4.2
|
||||
github.com/minio/highwayhash v1.0.2
|
||||
github.com/minio/kes-go v0.1.0
|
||||
github.com/minio/madmin-go/v2 v2.0.18
|
||||
github.com/minio/minio-go/v7 v7.0.50
|
||||
github.com/minio/madmin-go/v2 v2.0.20
|
||||
github.com/minio/minio-go/v7 v7.0.52
|
||||
github.com/minio/mux v1.9.0
|
||||
github.com/minio/pkg v1.6.6-0.20230330040824-5db111e5f63c
|
||||
github.com/minio/selfupdate v0.6.0
|
||||
@@ -66,6 +66,7 @@ require (
|
||||
github.com/philhofer/fwd v1.1.2
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.10.1
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/prometheus/client_model v0.3.0
|
||||
github.com/prometheus/common v0.42.0
|
||||
@@ -74,7 +75,7 @@ require (
|
||||
github.com/rs/cors v1.8.3
|
||||
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417
|
||||
github.com/secure-io/sio-go v0.3.1
|
||||
github.com/shirou/gopsutil/v3 v3.23.2
|
||||
github.com/shirou/gopsutil/v3 v3.23.3
|
||||
github.com/tidwall/gjson v1.14.4
|
||||
github.com/tinylib/msgp v1.1.8
|
||||
github.com/valyala/bytebufferpool v1.0.0
|
||||
@@ -84,11 +85,12 @@ require (
|
||||
go.etcd.io/etcd/client/v3 v3.5.7
|
||||
go.uber.org/atomic v1.10.0
|
||||
go.uber.org/zap v1.24.0
|
||||
golang.org/x/crypto v0.7.0
|
||||
golang.org/x/oauth2 v0.6.0
|
||||
golang.org/x/sys v0.6.0
|
||||
goftp.io/server/v2 v2.0.0
|
||||
golang.org/x/crypto v0.8.0
|
||||
golang.org/x/oauth2 v0.7.0
|
||||
golang.org/x/sys v0.7.0
|
||||
golang.org/x/time v0.3.0
|
||||
google.golang.org/api v0.114.0
|
||||
google.golang.org/api v0.117.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
@@ -98,7 +100,7 @@ require (
|
||||
cloud.google.com/go v0.110.0 // indirect
|
||||
cloud.google.com/go/compute v1.19.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v0.13.0 // indirect
|
||||
cloud.google.com/go/iam v1.0.0 // indirect
|
||||
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/apache/thrift v0.18.1 // indirect
|
||||
@@ -138,7 +140,8 @@ require (
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect
|
||||
github.com/google/pprof v0.0.0-20230406165453-00490a63f317 // indirect
|
||||
github.com/google/s2a-go v0.1.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.8.0 // indirect
|
||||
@@ -156,6 +159,7 @@ require (
|
||||
github.com/jessevdk/go-flags v1.5.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/juju/ratelimit v1.0.2 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
@@ -173,7 +177,7 @@ require (
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/minio/colorjson v1.0.4 // indirect
|
||||
github.com/minio/filepath v1.0.0 // indirect
|
||||
github.com/minio/mc v0.0.0-20230323200304-81453d7c8fcc // indirect
|
||||
github.com/minio/mc v0.0.0-20230411170328-83336dfab325 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/websocket v1.6.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
@@ -198,10 +202,11 @@ require (
|
||||
github.com/pquerna/cachecontrol v0.1.0 // indirect
|
||||
github.com/prometheus/prom2json v1.3.2 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/rivo/tview v0.0.0-20230325211341-281d14d896d7 // indirect
|
||||
github.com/rivo/tview v0.0.0-20230406072732-e22ce9588bb4 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/rjeczalik/notify v0.9.3 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.5 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
@@ -211,18 +216,18 @@ require (
|
||||
github.com/xdg/stringprep v1.0.3 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect
|
||||
go.mongodb.org/mongo-driver v1.11.3 // indirect
|
||||
go.mongodb.org/mongo-driver v1.11.4 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/net v0.9.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/term v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
golang.org/x/term v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/tools v0.8.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230327215041-6ac7f18bb9d5 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/h2non/filetype.v1 v1.0.5 // indirect
|
||||
|
||||
@@ -112,8 +112,8 @@ cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J
|
||||
cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=
|
||||
cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=
|
||||
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
|
||||
cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
|
||||
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
|
||||
cloud.google.com/go/iam v1.0.0 h1:hlQJMovyJJwYjZcTohUH4o1L8Z8kYz+E+W/zktiLCBc=
|
||||
cloud.google.com/go/iam v1.0.0/go.mod h1:ikbQ4f1r91wTmBmmOtBCOtuEOei6taatNXytzB7Cxew=
|
||||
cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
|
||||
cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
|
||||
cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
|
||||
@@ -539,9 +539,11 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd h1:r8yyd+DJDmsUhGrRBxH5Pj7KeFK5l+Y3FsgT8keqKtk=
|
||||
github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
|
||||
github.com/google/pprof v0.0.0-20230406165453-00490a63f317 h1:hFhpt7CTmR3DX+b4R19ydQFtofxT0Sv3QsKNMVQYTMQ=
|
||||
github.com/google/pprof v0.0.0-20230406165453-00490a63f317/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.0 h1:3Qm0liEiCErViKERO2Su5wp+9PfMRiuS6XB5FvpKnYQ=
|
||||
github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -637,6 +639,7 @@ github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1R
|
||||
github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs=
|
||||
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
@@ -660,8 +663,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
|
||||
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU=
|
||||
github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
@@ -679,6 +682,7 @@ github.com/klauspost/reedsolomon v1.11.7/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@@ -755,8 +759,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c=
|
||||
github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
|
||||
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/minio/cli v1.24.2 h1:J+fCUh9mhPLjN3Lj/YhklXvxj8mnyE/D6FpFduXJ2jg=
|
||||
github.com/minio/cli v1.24.2/go.mod h1:bYxnK0uS629N3Bq+AOZZ+6lwF77Sodk4+UL9vNuXhOY=
|
||||
github.com/minio/colorjson v1.0.4 h1:sNJYTb2uNswdqmGARg9wrogCX8+GRZzEacYbJT86e00=
|
||||
@@ -774,15 +778,18 @@ github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLT
|
||||
github.com/minio/kes-go v0.1.0 h1:h201DyOYP5sTqajkxFGxmXz/kPbT8HQNX1uh3Yx2PFc=
|
||||
github.com/minio/kes-go v0.1.0/go.mod h1:VorHLaIYis9/MxAHAtXN4d8PUMNKhIxTIlvFt0hBOEo=
|
||||
github.com/minio/madmin-go v1.6.6/go.mod h1:ATvkBOLiP3av4D++2v1UEHC/QzsGtgXD5kYvvRYzdKs=
|
||||
github.com/minio/madmin-go/v2 v2.0.18 h1:5kME44CIL7RlNm99hg6EU0K5AiIX0RtVHFu0nMEWmNE=
|
||||
github.com/minio/madmin-go/v2 v2.0.18/go.mod h1:8bL1RMNkblIENFSgGYjeHrzUx9PxROb7OqfNuMU9ivE=
|
||||
github.com/minio/mc v0.0.0-20230323200304-81453d7c8fcc h1:hhSnDwYb8clwMC/BZkhpoqicaPAlSIBqieqRZwHHg9E=
|
||||
github.com/minio/mc v0.0.0-20230323200304-81453d7c8fcc/go.mod h1:JbACfcKKvBZF4Pvipki9rtNDXBPPBCQtkVcs6GwdniQ=
|
||||
github.com/minio/madmin-go/v2 v2.0.19 h1:XznxdMVCTyr0A88JrZFhdxWY8KLfJcrs0TTmFiE9cc8=
|
||||
github.com/minio/madmin-go/v2 v2.0.19/go.mod h1:8bL1RMNkblIENFSgGYjeHrzUx9PxROb7OqfNuMU9ivE=
|
||||
github.com/minio/madmin-go/v2 v2.0.20 h1:EO2IIQsnaVM3ki/ONcW0Ry4UpDn5aa+/S1kLkHuDBs8=
|
||||
github.com/minio/madmin-go/v2 v2.0.20/go.mod h1:8bL1RMNkblIENFSgGYjeHrzUx9PxROb7OqfNuMU9ivE=
|
||||
github.com/minio/mc v0.0.0-20230411170328-83336dfab325 h1:da5I7G0Va6UnqoxzPHus7zmqo2pEQnltI09/XKNvHP4=
|
||||
github.com/minio/mc v0.0.0-20230411170328-83336dfab325/go.mod h1:v9AeUV4eaMpKCuNz0tk/MSGxvNs3duYvWUIxzpacxtc=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v6 v6.0.46/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg=
|
||||
github.com/minio/minio-go/v7 v7.0.41/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw=
|
||||
github.com/minio/minio-go/v7 v7.0.50 h1:4IL4V8m/kI90ZL6GupCARZVrBv8/XrcKcJhaJ3iz68k=
|
||||
github.com/minio/minio-go/v7 v7.0.50/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9du7A7/Z3HU=
|
||||
github.com/minio/minio-go/v7 v7.0.52 h1:8XhG36F6oKQUDDSuz6dY3rioMzovKjW40W6ANuN0Dps=
|
||||
github.com/minio/minio-go/v7 v7.0.52/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9du7A7/Z3HU=
|
||||
github.com/minio/mux v1.9.0 h1:dWafQFyEfGhJvK6AwLOt83bIG5bxKxKJnKMCi0XAaoA=
|
||||
github.com/minio/mux v1.9.0/go.mod h1:1pAare17ZRL5GpmNL+9YmqHoWnLmMZF9C/ioUCfy0BQ=
|
||||
github.com/minio/pkg v1.5.4/go.mod h1:2MOaRFdmFKULD+uOLc3qHLGTQTuxCNPKNPfLBTxC8CA=
|
||||
@@ -790,6 +797,7 @@ github.com/minio/pkg v1.6.6-0.20230330040824-5db111e5f63c h1:Ukw0+d0T/+9lserJfod
|
||||
github.com/minio/pkg v1.6.6-0.20230330040824-5db111e5f63c/go.mod h1:0iX1IuJGSCnMvIvrEJauk1GgQSX9JdU6Kh0P3EQRGkI=
|
||||
github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU=
|
||||
github.com/minio/selfupdate v0.6.0/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM=
|
||||
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||
github.com/minio/simdjson-go v0.4.5 h1:r4IQwjRGmWCQ2VeMc7fGiilu1z5du0gJ/I/FsKwgo5A=
|
||||
@@ -891,6 +899,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||
github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
|
||||
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
@@ -940,8 +949,8 @@ github.com/rabbitmq/amqp091-go v1.8.0 h1:GBFy5PpLQ5jSVVSYv8ecHGqeX7UTLYR4ItQbDCs
|
||||
github.com/rabbitmq/amqp091-go v1.8.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rivo/tview v0.0.0-20230325211341-281d14d896d7 h1:8sjyhdBA9HmxHGUeAM/43uSHfyFhFodbxekw2mYhpo0=
|
||||
github.com/rivo/tview v0.0.0-20230325211341-281d14d896d7/go.mod h1:nVwGv4MP47T0jvlk7KuTTjjuSmrGO4JF0iaiNt4bufE=
|
||||
github.com/rivo/tview v0.0.0-20230406072732-e22ce9588bb4 h1:zX+lRcFRPX1jn8A11jxT0dEQhkmUM7pec+9NLK8MiTQ=
|
||||
github.com/rivo/tview v0.0.0-20230406072732-e22ce9588bb4/go.mod h1:nVwGv4MP47T0jvlk7KuTTjjuSmrGO4JF0iaiNt4bufE=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
@@ -972,8 +981,13 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
|
||||
github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc=
|
||||
github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs=
|
||||
github.com/shirou/gopsutil/v3 v3.22.9/go.mod h1:bBYl1kjgEJpWpxeHmLI+dVHWtyAwfcmSBLDsp2TNT8A=
|
||||
github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU=
|
||||
github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M=
|
||||
github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
|
||||
github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU=
|
||||
github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
||||
github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
|
||||
github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
||||
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
|
||||
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
@@ -987,6 +1001,7 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
@@ -1084,8 +1099,8 @@ go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/
|
||||
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
|
||||
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
|
||||
go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
|
||||
go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y=
|
||||
go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
|
||||
go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas=
|
||||
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@@ -1104,19 +1119,23 @@ go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
goftp.io/server/v2 v2.0.0 h1:FF8JKXXKDxAeO1uXEZz7G+IZwCDhl19dpVIlDtp3QAg=
|
||||
goftp.io/server/v2 v2.0.0/go.mod h1:7+H/EIq7tXdfo1Muu5p+l3oQ6rYkDZ8lY7IM5d5kVdQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -1136,8 +1155,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -1175,8 +1194,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1188,6 +1207,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -1220,6 +1240,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
@@ -1236,8 +1257,8 @@ golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1262,8 +1283,8 @@ golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7Lm
|
||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
|
||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
|
||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -1355,6 +1376,7 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1384,16 +1406,17 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1407,8 +1430,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1478,8 +1501,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
|
||||
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1537,8 +1560,8 @@ google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaE
|
||||
google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
|
||||
google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
|
||||
google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
|
||||
google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
|
||||
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
||||
google.golang.org/api v0.117.0 h1:JsXRperckxXjnPl42ku4+KQRhWFiW6XjcZlOEHoxG8M=
|
||||
google.golang.org/api v0.117.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -1646,8 +1669,8 @@ google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53B
|
||||
google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
|
||||
google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
|
||||
google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck=
|
||||
google.golang.org/genproto v0.0.0-20230327215041-6ac7f18bb9d5 h1:Kd6tRRHXw8z4TlPlWi+NaK10gsePL6GdZBQChptOLGA=
|
||||
google.golang.org/genproto v0.0.0-20230327215041-6ac7f18bb9d5/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -1714,6 +1737,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/h2non/filetype.v1 v1.0.5 h1:CC1jjJjoEhNVbMhXYalmGBhOBK2V70Q1N850wt/98/Y=
|
||||
gopkg.in/h2non/filetype.v1 v1.0.5/go.mod h1:M0yem4rwSX5lLVrkEuRRp2/NinFMD5vgJ4DlAhZcfNo=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
|
||||
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
apiVersion: v1
|
||||
description: Multi-Cloud Object Storage
|
||||
name: minio
|
||||
version: 5.0.7
|
||||
appVersion: RELEASE.2023-02-10T18-48-39Z
|
||||
version: 5.0.8
|
||||
appVersion: RELEASE.2023-04-13T03-08-07Z
|
||||
keywords:
|
||||
- minio
|
||||
- storage
|
||||
|
||||
@@ -73,7 +73,9 @@ createUser() {
|
||||
# set policy for user
|
||||
if [ ! -z $POLICY -a $POLICY != " " ] ; then
|
||||
echo "Adding policy '$POLICY' for '$USER'"
|
||||
set +e ; # policy already attach errors out, allow it.
|
||||
${MC} admin policy attach myminio $POLICY --user=$USER
|
||||
set -e
|
||||
else
|
||||
echo "User '$USER' has no policy attached."
|
||||
fi
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "minio.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
@@ -12,102 +12,102 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "minio.fullname" -}}
|
||||
{{- if .Values.fullnameOverride -}}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- if contains $name .Release.Name -}}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if .Values.fullnameOverride -}}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- if contains $name .Release.Name -}}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "minio.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the appropriate apiVersion for networkpolicy.
|
||||
*/}}
|
||||
{{- define "minio.networkPolicy.apiVersion" -}}
|
||||
{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.Version -}}
|
||||
{{- print "extensions/v1beta1" -}}
|
||||
{{- else if semverCompare ">=1.7-0, <1.16-0" .Capabilities.KubeVersion.Version -}}
|
||||
{{- print "networking.k8s.io/v1beta1" -}}
|
||||
{{- else if semverCompare "^1.16-0" .Capabilities.KubeVersion.Version -}}
|
||||
{{- print "networking.k8s.io/v1" -}}
|
||||
{{- end -}}
|
||||
{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.Version -}}
|
||||
{{- print "extensions/v1beta1" -}}
|
||||
{{- else if semverCompare ">=1.7-0, <1.16-0" .Capabilities.KubeVersion.Version -}}
|
||||
{{- print "networking.k8s.io/v1beta1" -}}
|
||||
{{- else if semverCompare "^1.16-0" .Capabilities.KubeVersion.Version -}}
|
||||
{{- print "networking.k8s.io/v1" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the appropriate apiVersion for deployment.
|
||||
*/}}
|
||||
{{- define "minio.deployment.apiVersion" -}}
|
||||
{{- if semverCompare "<1.9-0" .Capabilities.KubeVersion.Version -}}
|
||||
{{- print "apps/v1beta2" -}}
|
||||
{{- else -}}
|
||||
{{- print "apps/v1" -}}
|
||||
{{- end -}}
|
||||
{{- if semverCompare "<1.9-0" .Capabilities.KubeVersion.Version -}}
|
||||
{{- print "apps/v1beta2" -}}
|
||||
{{- else -}}
|
||||
{{- print "apps/v1" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the appropriate apiVersion for statefulset.
|
||||
*/}}
|
||||
{{- define "minio.statefulset.apiVersion" -}}
|
||||
{{- if semverCompare "<1.16-0" .Capabilities.KubeVersion.Version -}}
|
||||
{{- print "apps/v1beta2" -}}
|
||||
{{- else -}}
|
||||
{{- print "apps/v1" -}}
|
||||
{{- end -}}
|
||||
{{- if semverCompare "<1.16-0" .Capabilities.KubeVersion.Version -}}
|
||||
{{- print "apps/v1beta2" -}}
|
||||
{{- else -}}
|
||||
{{- print "apps/v1" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the appropriate apiVersion for ingress.
|
||||
*/}}
|
||||
{{- define "minio.ingress.apiVersion" -}}
|
||||
{{- if semverCompare "<1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
{{- print "extensions/v1beta1" -}}
|
||||
{{- else if semverCompare "<1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
{{- print "networking.k8s.io/v1beta1" -}}
|
||||
{{- else -}}
|
||||
{{- print "networking.k8s.io/v1" -}}
|
||||
{{- end -}}
|
||||
{{- if semverCompare "<1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
{{- print "extensions/v1beta1" -}}
|
||||
{{- else if semverCompare "<1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
{{- print "networking.k8s.io/v1beta1" -}}
|
||||
{{- else -}}
|
||||
{{- print "networking.k8s.io/v1" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the appropriate apiVersion for console ingress.
|
||||
*/}}
|
||||
{{- define "minio.consoleIngress.apiVersion" -}}
|
||||
{{- if semverCompare "<1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
{{- print "extensions/v1beta1" -}}
|
||||
{{- else if semverCompare "<1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
{{- print "networking.k8s.io/v1beta1" -}}
|
||||
{{- else -}}
|
||||
{{- print "networking.k8s.io/v1" -}}
|
||||
{{- end -}}
|
||||
{{- if semverCompare "<1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
{{- print "extensions/v1beta1" -}}
|
||||
{{- else if semverCompare "<1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
{{- print "networking.k8s.io/v1beta1" -}}
|
||||
{{- else -}}
|
||||
{{- print "networking.k8s.io/v1" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Determine secret name.
|
||||
*/}}
|
||||
{{- define "minio.secretName" -}}
|
||||
{{- if .Values.existingSecret -}}
|
||||
{{- .Values.existingSecret }}
|
||||
{{- else -}}
|
||||
{{- include "minio.fullname" . -}}
|
||||
{{- end -}}
|
||||
{{- if .Values.existingSecret -}}
|
||||
{{- .Values.existingSecret }}
|
||||
{{- else -}}
|
||||
{{- include "minio.fullname" . -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Determine name for scc role and rolebinding
|
||||
*/}}
|
||||
{{- define "minio.sccRoleName" -}}
|
||||
{{- printf "%s-%s" "scc" (include "minio.fullname" .) | trunc 63 | trimSuffix "-" -}}
|
||||
{{- printf "%s-%s" "scc" (include "minio.fullname" .) | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
@@ -195,9 +195,9 @@ otherwise it generates a random value.
|
||||
{{- $len := (default 16 .Length) | int -}}
|
||||
{{- $obj := (lookup "v1" "Secret" .Namespace .Name).data -}}
|
||||
{{- if $obj }}
|
||||
{{- index $obj .Key | b64dec -}}
|
||||
{{- index $obj .Key | b64dec -}}
|
||||
{{- else -}}
|
||||
{{- randAlphaNum $len -}}
|
||||
{{- randAlphaNum $len -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ template "minio.fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
@@ -10,24 +9,24 @@ metadata:
|
||||
heritage: {{ .Release.Service }}
|
||||
data:
|
||||
initialize: |-
|
||||
{{ include (print $.Template.BasePath "/_helper_create_bucket.txt") . | indent 4 }}
|
||||
{{- include (print $.Template.BasePath "/_helper_create_bucket.txt") . | nindent 4 }}
|
||||
add-user: |-
|
||||
{{ include (print $.Template.BasePath "/_helper_create_user.txt") . | indent 4 }}
|
||||
{{- include (print $.Template.BasePath "/_helper_create_user.txt") . | nindent 4 }}
|
||||
add-policy: |-
|
||||
{{ include (print $.Template.BasePath "/_helper_create_policy.txt") . | indent 4 }}
|
||||
{{- range $idx, $policy := .Values.policies }}
|
||||
{{- include (print $.Template.BasePath "/_helper_create_policy.txt") . | nindent 4 }}
|
||||
{{- range $idx, $policy := .Values.policies }}
|
||||
# Policy: {{ $policy.name }}
|
||||
policy_{{ $idx }}.json: |-
|
||||
{{ include (print $.Template.BasePath "/_helper_policy.tpl") . | indent 4 }}
|
||||
{{ end }}
|
||||
{{- range $idx, $svc := .Values.svcaccts }}
|
||||
{{- if $svc.policy }}
|
||||
{{- include (print $.Template.BasePath "/_helper_policy.tpl") . | nindent 4 }}
|
||||
{{ end }}
|
||||
{{- range $idx, $svc := .Values.svcaccts }}
|
||||
{{- if $svc.policy }}
|
||||
# SVC: {{ $svc.accessKey }}
|
||||
svc_policy_{{ $idx }}.json: |-
|
||||
{{ include (print $.Template.BasePath "/_helper_policy.tpl") .policy | indent 4 }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{- include (print $.Template.BasePath "/_helper_policy.tpl") .policy | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
add-svcacct: |-
|
||||
{{ include (print $.Template.BasePath "/_helper_create_svcacct.txt") . | indent 4 }}
|
||||
{{- include (print $.Template.BasePath "/_helper_create_svcacct.txt") . | nindent 4 }}
|
||||
custom-command: |-
|
||||
{{ include (print $.Template.BasePath "/_helper_custom_command.txt") . | indent 4 }}
|
||||
{{- include (print $.Template.BasePath "/_helper_custom_command.txt") . | nindent 4 }}
|
||||
|
||||
@@ -6,36 +6,33 @@ apiVersion: {{ template "minio.consoleIngress.apiVersion" . }}
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
{{- with .Values.consoleIngress.labels }}
|
||||
{{ toYaml . | indent 4 }}
|
||||
{{- end }}
|
||||
|
||||
{{- with .Values.consoleIngress.annotations }}
|
||||
annotations:
|
||||
{{ toYaml . | indent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.consoleIngress.labels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.consoleIngress.annotations }}
|
||||
annotations: {{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.consoleIngress.ingressClassName }}
|
||||
{{- if .Values.consoleIngress.ingressClassName }}
|
||||
ingressClassName: {{ .Values.consoleIngress.ingressClassName }}
|
||||
{{- end }}
|
||||
{{- if .Values.consoleIngress.tls }}
|
||||
{{- end }}
|
||||
{{- if .Values.consoleIngress.tls }}
|
||||
tls:
|
||||
{{- range .Values.consoleIngress.tls }}
|
||||
{{- range .Values.consoleIngress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.consoleIngress.hosts }}
|
||||
{{- range .Values.consoleIngress.hosts }}
|
||||
- http:
|
||||
paths:
|
||||
- path: {{ $ingressPath }}
|
||||
@@ -54,5 +51,5 @@ spec:
|
||||
{{- if . }}
|
||||
host: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,48 +1,43 @@
|
||||
{{ $scheme := "http" }}
|
||||
{{- if .Values.tls.enabled }}
|
||||
{{ $scheme = "https" }}
|
||||
{{ end }}
|
||||
{{ $scheme := .Values.tls.enabled | ternary "https" "http" }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "minio.fullname" . }}-console
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
{{- if .Values.consoleService.annotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.consoleService.annotations | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.consoleService.annotations }}
|
||||
annotations: {{- toYaml .Values.consoleService.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if (or (eq .Values.consoleService.type "ClusterIP" "") (empty .Values.consoleService.type)) }}
|
||||
{{- if (or (eq .Values.consoleService.type "ClusterIP" "") (empty .Values.consoleService.type)) }}
|
||||
type: ClusterIP
|
||||
{{- if not (empty .Values.consoleService.clusterIP) }}
|
||||
clusterIP: {{ .Values.consoleService.clusterIP }}
|
||||
{{end}}
|
||||
{{- else if eq .Values.consoleService.type "LoadBalancer" }}
|
||||
{{- end }}
|
||||
{{- else if eq .Values.consoleService.type "LoadBalancer" }}
|
||||
type: {{ .Values.consoleService.type }}
|
||||
loadBalancerIP: {{ default "" .Values.consoleService.loadBalancerIP }}
|
||||
{{- else }}
|
||||
{{- else }}
|
||||
type: {{ .Values.consoleService.type }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: {{ $scheme }}
|
||||
port: {{ .Values.consoleService.port }}
|
||||
protocol: TCP
|
||||
{{- if (and (eq .Values.consoleService.type "NodePort") ( .Values.consoleService.nodePort)) }}
|
||||
{{- if (and (eq .Values.consoleService.type "NodePort") ( .Values.consoleService.nodePort)) }}
|
||||
nodePort: {{ .Values.consoleService.nodePort }}
|
||||
{{- else }}
|
||||
{{- else }}
|
||||
targetPort: {{ .Values.minioConsolePort }}
|
||||
{{- end}}
|
||||
{{- if .Values.consoleService.externalIPs }}
|
||||
{{- end }}
|
||||
{{- if .Values.consoleService.externalIPs }}
|
||||
externalIPs:
|
||||
{{- range $i , $ip := .Values.consoleService.externalIPs }}
|
||||
- {{ $ip }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $i , $ip := .Values.consoleService.externalIPs }}
|
||||
- {{ $ip }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
selector:
|
||||
app: {{ template "minio.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
{{- if eq .Values.mode "standalone" }}
|
||||
{{ $scheme := "http" }}
|
||||
{{- if .Values.tls.enabled }}
|
||||
{{ $scheme = "https" }}
|
||||
{{ end }}
|
||||
{{ $scheme := .Values.tls.enabled | ternary "https" "http" }}
|
||||
{{ $bucketRoot := or ($.Values.bucketRoot) ($.Values.mountPath) }}
|
||||
apiVersion: {{ template "minio.deployment.apiVersion" . }}
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ template "minio.fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
{{- if .Values.additionalLabels }}
|
||||
{{ toYaml .Values.additionalLabels | trimSuffix "\n" | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.additionalAnnotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.additionalAnnotations | trimSuffix "\n" | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.additionalLabels }}
|
||||
{{- toYaml .Values.additionalLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.additionalAnnotations }}
|
||||
annotations: {{- toYaml .Values.additionalAnnotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
strategy:
|
||||
type: {{ .Values.DeploymentUpdate.type }}
|
||||
@@ -28,7 +23,7 @@ spec:
|
||||
rollingUpdate:
|
||||
maxSurge: {{ .Values.DeploymentUpdate.maxSurge }}
|
||||
maxUnavailable: {{ .Values.DeploymentUpdate.maxUnavailable }}
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
@@ -40,17 +35,17 @@ spec:
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
{{- if .Values.podLabels }}
|
||||
{{ toYaml .Values.podLabels | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.podLabels }}
|
||||
{{- toYaml .Values.podLabels | nindent 8 }}
|
||||
{{- end }}
|
||||
annotations:
|
||||
{{- if not .Values.ignoreChartChecksums }}
|
||||
{{- if not .Values.ignoreChartChecksums }}
|
||||
checksum/secrets: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- if .Values.podAnnotations }}
|
||||
{{ toYaml .Values.podAnnotations | trimSuffix "\n" | indent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.podAnnotations }}
|
||||
{{- toYaml .Values.podAnnotations | trimSuffix "\n" | indent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.priorityClassName }}
|
||||
priorityClassName: "{{ .Values.priorityClassName }}"
|
||||
@@ -58,7 +53,7 @@ spec:
|
||||
{{- if .Values.runtimeClassName }}
|
||||
runtimeClassName: "{{ .Values.runtimeClassName }}"
|
||||
{{- end }}
|
||||
{{- if and .Values.securityContext.enabled .Values.persistence.enabled }}
|
||||
{{- if and .Values.securityContext.enabled .Values.persistence.enabled }}
|
||||
securityContext:
|
||||
runAsUser: {{ .Values.securityContext.runAsUser }}
|
||||
runAsGroup: {{ .Values.securityContext.runAsGroup }}
|
||||
@@ -66,10 +61,10 @@ spec:
|
||||
{{- if and (ge .Capabilities.KubeVersion.Major "1") (ge .Capabilities.KubeVersion.Minor "20") }}
|
||||
fsGroupChangePolicy: {{ .Values.securityContext.fsGroupChangePolicy }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ if .Values.serviceAccount.create }}
|
||||
{{- end }}
|
||||
{{ if .Values.serviceAccount.create }}
|
||||
serviceAccountName: {{ .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
@@ -114,11 +109,11 @@ spec:
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: MINIO_CONFIG_ENV_FILE
|
||||
value: "/tmp/minio-config-env/config.env"
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
{{- if .Values.metrics.serviceMonitor.public }}
|
||||
- name: MINIO_PROMETHEUS_AUTH_TYPE
|
||||
value: "public"
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
{{- if .Values.oidc.enabled }}
|
||||
- name: MINIO_IDENTITY_OPENID_CONFIG_URL
|
||||
value: {{ .Values.oidc.configUrl }}
|
||||
@@ -136,7 +131,7 @@ spec:
|
||||
value: {{ .Values.oidc.redirectUri }}
|
||||
- name: MINIO_IDENTITY_OPENID_COMMENT
|
||||
value: {{ .Values.oidc.comment }}
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
{{- if .Values.etcd.endpoints }}
|
||||
- name: MINIO_ETCD_ENDPOINTS
|
||||
value: {{ join "," .Values.etcd.endpoints | quote }}
|
||||
@@ -160,37 +155,33 @@ spec:
|
||||
{{- range $key, $val := .Values.environment }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $val | quote }}
|
||||
{{- end}}
|
||||
resources:
|
||||
{{ toYaml .Values.resources | indent 12 }}
|
||||
{{- end }}
|
||||
resources: {{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- with .Values.extraContainers }}
|
||||
{{- if eq (typeOf .) "string" }}
|
||||
{{- tpl . $ | nindent 8 }}
|
||||
{{- else }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- if eq (typeOf .) "string" }}
|
||||
{{- tpl . $ | nindent 8 }}
|
||||
{{- else }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- include "minio.imagePullSecrets" . | indent 6 }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- include "minio.imagePullSecrets" . | indent 6 }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: export
|
||||
{{- if .Values.persistence.enabled }}
|
||||
{{- if .Values.persistence.enabled }}
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .Values.persistence.existingClaim | default (include "minio.fullname" .) }}
|
||||
{{- else }}
|
||||
{{- else }}
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: extra-secret
|
||||
secret:
|
||||
|
||||
@@ -6,53 +6,50 @@ apiVersion: {{ template "minio.ingress.apiVersion" . }}
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
{{- with .Values.ingress.labels }}
|
||||
{{ toYaml . | indent 4 }}
|
||||
{{- end }}
|
||||
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{ toYaml . | indent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ingress.labels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations: {{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.ingressClassName }}
|
||||
{{- if .Values.ingress.ingressClassName }}
|
||||
ingressClassName: {{ .Values.ingress.ingressClassName }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- http:
|
||||
paths:
|
||||
- path: {{ $ingressPath }}
|
||||
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
|
||||
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
number: {{ $servicePort }}
|
||||
{{- else }}
|
||||
{{- else }}
|
||||
backend:
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: {{ $servicePort }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if . }}
|
||||
host: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -3,7 +3,6 @@ kind: NetworkPolicy
|
||||
apiVersion: {{ template "minio.networkPolicy.apiVersion" . }}
|
||||
metadata:
|
||||
name: {{ template "minio.fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
|
||||
@@ -7,7 +7,6 @@ apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: minio
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
spec:
|
||||
|
||||
@@ -3,7 +3,6 @@ apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ template "minio.fullname" . }}-post-job
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}-post-job
|
||||
chart: {{ template "minio.chart" . }}
|
||||
@@ -25,23 +24,19 @@ spec:
|
||||
{{- toYaml .Values.podLabels | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.postJob.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml .Values.postJob.podAnnotations | nindent 8 }}
|
||||
annotations: {{- toYaml .Values.postJob.podAnnotations | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
{{- include "minio.imagePullSecrets" . | nindent 6 }}
|
||||
{{- include "minio.imagePullSecrets" . | indent 6 }}
|
||||
{{- if .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml .Values.postJob.nodeSelector | nindent 8 }}
|
||||
nodeSelector: {{- toYaml .Values.postJob.nodeSelector | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.postJob.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
affinity: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.postJob.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
tolerations: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.postJob.securityContext.enabled }}
|
||||
securityContext:
|
||||
@@ -82,7 +77,7 @@ spec:
|
||||
items:
|
||||
- key: {{ .Values.tls.publicCrt }}
|
||||
path: CAs/public.crt
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
containers:
|
||||
{{- if .Values.buckets }}
|
||||
- name: minio-make-bucket
|
||||
@@ -110,9 +105,8 @@ spec:
|
||||
{{- if .Values.tls.enabled }}
|
||||
- name: cert-secret-volume-mc
|
||||
mountPath: {{ .Values.configPathmc }}certs
|
||||
{{ end }}
|
||||
resources:
|
||||
{{- toYaml .Values.makeBucketJob.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
resources: {{- toYaml .Values.makeBucketJob.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.users }}
|
||||
- name: minio-make-user
|
||||
@@ -140,9 +134,8 @@ spec:
|
||||
{{- if .Values.tls.enabled }}
|
||||
- name: cert-secret-volume-mc
|
||||
mountPath: {{ .Values.configPathmc }}certs
|
||||
{{ end }}
|
||||
resources:
|
||||
{{- toYaml .Values.makeUserJob.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
resources: {{- toYaml .Values.makeUserJob.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.policies }}
|
||||
- name: minio-make-policy
|
||||
@@ -170,9 +163,8 @@ spec:
|
||||
{{- if .Values.tls.enabled }}
|
||||
- name: cert-secret-volume-mc
|
||||
mountPath: {{ .Values.configPathmc }}certs
|
||||
{{ end }}
|
||||
resources:
|
||||
{{- toYaml .Values.makePolicyJob.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
resources: {{- toYaml .Values.makePolicyJob.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.customCommands }}
|
||||
- name: minio-custom-command
|
||||
@@ -200,9 +192,8 @@ spec:
|
||||
{{- if .Values.tls.enabled }}
|
||||
- name: cert-secret-volume-mc
|
||||
mountPath: {{ .Values.configPathmc }}certs
|
||||
{{ end }}
|
||||
resources:
|
||||
{{- toYaml .Values.customCommandJob.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
resources: {{- toYaml .Values.customCommandJob.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.svcaccts }}
|
||||
- name: minio-make-svcacct
|
||||
@@ -230,8 +221,7 @@ spec:
|
||||
{{- if .Values.tls.enabled }}
|
||||
- name: cert-secret-volume-mc
|
||||
mountPath: {{ .Values.configPathmc }}certs
|
||||
{{ end }}
|
||||
resources:
|
||||
{{- toYaml .Values.makeServiceAccountJob.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
resources: {{- toYaml .Values.makeServiceAccountJob.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -4,32 +4,29 @@ apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ template "minio.fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
{{- if .Values.persistence.annotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.persistence.annotations | trimSuffix "\n" | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.persistence.annotations }}
|
||||
annotations: {{- toYaml .Values.persistence.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.persistence.accessMode | quote }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size | quote }}
|
||||
|
||||
{{- if .Values.persistence.storageClass }}
|
||||
{{- if (eq "-" .Values.persistence.storageClass) }}
|
||||
{{- if .Values.persistence.storageClass }}
|
||||
{{- if (eq "-" .Values.persistence.storageClass) }}
|
||||
storageClassName: ""
|
||||
{{- else }}
|
||||
{{- else }}
|
||||
storageClassName: "{{ .Values.persistence.storageClass }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.persistence.VolumeName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.persistence.VolumeName }}
|
||||
volumeName: "{{ .Values.persistence.VolumeName }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -3,7 +3,6 @@ apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ template "minio.secretName" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
|
||||
@@ -1,49 +1,44 @@
|
||||
{{ $scheme := "http" }}
|
||||
{{- if .Values.tls.enabled }}
|
||||
{{ $scheme = "https" }}
|
||||
{{ end }}
|
||||
{{ $scheme := .Values.tls.enabled | ternary "https" "http" }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "minio.fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
monitoring: "true"
|
||||
{{- if .Values.service.annotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.service.annotations | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.annotations }}
|
||||
annotations: {{- toYaml .Values.service.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if (or (eq .Values.service.type "ClusterIP" "") (empty .Values.service.type)) }}
|
||||
{{- if (or (eq .Values.service.type "ClusterIP" "") (empty .Values.service.type)) }}
|
||||
type: ClusterIP
|
||||
{{- if not (empty .Values.service.clusterIP) }}
|
||||
clusterIP: {{ .Values.service.clusterIP }}
|
||||
{{end}}
|
||||
{{- else if eq .Values.service.type "LoadBalancer" }}
|
||||
{{- end }}
|
||||
{{- else if eq .Values.service.type "LoadBalancer" }}
|
||||
type: {{ .Values.service.type }}
|
||||
loadBalancerIP: {{ default "" .Values.service.loadBalancerIP }}
|
||||
{{- else }}
|
||||
{{- else }}
|
||||
type: {{ .Values.service.type }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: {{ $scheme }}
|
||||
port: {{ .Values.service.port }}
|
||||
protocol: TCP
|
||||
{{- if (and (eq .Values.service.type "NodePort") ( .Values.service.nodePort)) }}
|
||||
{{- if (and (eq .Values.service.type "NodePort") ( .Values.service.nodePort)) }}
|
||||
nodePort: {{ .Values.service.nodePort }}
|
||||
{{- else }}
|
||||
{{- else }}
|
||||
targetPort: {{ .Values.minioAPIPort }}
|
||||
{{- end}}
|
||||
{{- if .Values.service.externalIPs }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.externalIPs }}
|
||||
externalIPs:
|
||||
{{- range $i , $ip := .Values.service.externalIPs }}
|
||||
- {{ $ip }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $i , $ip := .Values.service.externalIPs }}
|
||||
- {{ $ip }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
selector:
|
||||
app: {{ template "minio.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ .Values.serviceAccount.name | quote }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
{{- end -}}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
{{- if and .Values.metrics.serviceMonitor.enabled .Values.metrics.serviceMonitor.includeNode}}
|
||||
{{- if and .Values.metrics.serviceMonitor.enabled .Values.metrics.serviceMonitor.includeNode }}
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: {{ template "minio.fullname" . }}
|
||||
{{- if .Values.metrics.serviceMonitor.namespace }}
|
||||
namespace: {{ .Values.metrics.serviceMonitor.namespace }}
|
||||
{{ else }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
{{- end }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
@@ -14,12 +12,11 @@ metadata:
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
{{- if .Values.metrics.serviceMonitor.additionalLabels }}
|
||||
{{ toYaml .Values.metrics.serviceMonitor.additionalLabels | indent 4 }}
|
||||
{{- toYaml .Values.metrics.serviceMonitor.additionalLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.metrics.serviceMonitor.annotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.metrics.serviceMonitor.annotations | trimSuffix "\n" | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.metrics.serviceMonitor.annotations }}
|
||||
annotations: {{- toYaml .Values.metrics.serviceMonitor.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
endpoints:
|
||||
{{- if .Values.tls.enabled }}
|
||||
@@ -31,7 +28,7 @@ spec:
|
||||
name: {{ .Values.tls.certSecret }}
|
||||
key: {{ .Values.tls.publicCrt }}
|
||||
serverName: {{ template "minio.fullname" . }}
|
||||
{{ else }}
|
||||
{{- else }}
|
||||
- port: http
|
||||
scheme: http
|
||||
{{- end }}
|
||||
@@ -43,7 +40,7 @@ spec:
|
||||
scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }}
|
||||
{{- end }}
|
||||
{{- if .Values.metrics.serviceMonitor.relabelConfigs }}
|
||||
{{ toYaml .Values.metrics.serviceMonitor.relabelConfigs | indent 6 }}
|
||||
{{- toYaml .Values.metrics.serviceMonitor.relabelConfigs | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- if not .Values.metrics.serviceMonitor.public }}
|
||||
bearerTokenSecret:
|
||||
@@ -67,8 +64,6 @@ metadata:
|
||||
name: {{ template "minio.fullname" . }}-cluster
|
||||
{{- if .Values.metrics.serviceMonitor.namespace }}
|
||||
namespace: {{ .Values.metrics.serviceMonitor.namespace }}
|
||||
{{ else }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
{{- end }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
@@ -76,7 +71,7 @@ metadata:
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
{{- if .Values.metrics.serviceMonitor.additionalLabels }}
|
||||
{{ toYaml .Values.metrics.serviceMonitor.additionalLabels | indent 4 }}
|
||||
{{- toYaml .Values.metrics.serviceMonitor.additionalLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
jobName: {{ template "minio.fullname" . }}
|
||||
@@ -93,11 +88,11 @@ spec:
|
||||
path: /minio/v2/metrics/cluster
|
||||
{{- if .Values.tls.enabled }}
|
||||
scheme: https
|
||||
{{ else }}
|
||||
{{- else }}
|
||||
scheme: http
|
||||
{{- end }}
|
||||
{{- if .Values.metrics.serviceMonitor.relabelConfigsCluster }}
|
||||
{{ toYaml .Values.metrics.serviceMonitor.relabelConfigsCluster | indent 2 }}
|
||||
{{- toYaml .Values.metrics.serviceMonitor.relabelConfigsCluster | nindent 2 }}
|
||||
{{- end }}
|
||||
targets:
|
||||
staticConfig:
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
{{ $nodeCount := .Values.replicas | int }}
|
||||
{{ $replicas := mul $poolCount $nodeCount }}
|
||||
{{ $drivesPerNode := .Values.drivesPerNode | int }}
|
||||
{{ $scheme := "http" }}
|
||||
{{- if .Values.tls.enabled }}
|
||||
{{ $scheme = "https" }}
|
||||
{{ end }}
|
||||
{{ $scheme := .Values.tls.enabled | ternary "https" "http" }}
|
||||
{{ $mountPath := .Values.mountPath }}
|
||||
{{ $bucketRoot := or ($.Values.bucketRoot) ($.Values.mountPath) }}
|
||||
{{ $subPath := .Values.persistence.subPath }}
|
||||
@@ -18,12 +15,11 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "minio.fullname" . }}-svc
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
release: "{{ .Release.Name }}"
|
||||
heritage: "{{ .Release.Service }}"
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
spec:
|
||||
publishNotReadyAddresses: true
|
||||
clusterIP: None
|
||||
@@ -40,19 +36,17 @@ apiVersion: {{ template "minio.statefulset.apiVersion" . }}
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ template "minio.fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
chart: {{ template "minio.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
{{- if .Values.additionalLabels }}
|
||||
{{ toYaml .Values.additionalLabels | trimSuffix "\n" | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.additionalAnnotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.additionalAnnotations | trimSuffix "\n" | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.additionalLabels }}
|
||||
{{- toYaml .Values.additionalLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.additionalAnnotations }}
|
||||
annotations: {{- toYaml .Values.additionalAnnotations | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
updateStrategy:
|
||||
type: {{ .Values.StatefulSetUpdate.updateStrategy }}
|
||||
@@ -69,17 +63,17 @@ spec:
|
||||
labels:
|
||||
app: {{ template "minio.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
{{- if .Values.podLabels }}
|
||||
{{ toYaml .Values.podLabels | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.podLabels }}
|
||||
{{- toYaml .Values.podLabels | nindent 8 }}
|
||||
{{- end }}
|
||||
annotations:
|
||||
{{- if not .Values.ignoreChartChecksums }}
|
||||
{{- if not .Values.ignoreChartChecksums }}
|
||||
checksum/secrets: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- if .Values.podAnnotations }}
|
||||
{{ toYaml .Values.podAnnotations | trimSuffix "\n" | indent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.podAnnotations }}
|
||||
{{- toYaml .Values.podAnnotations | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.priorityClassName }}
|
||||
priorityClassName: "{{ .Values.priorityClassName }}"
|
||||
@@ -87,7 +81,7 @@ spec:
|
||||
{{- if .Values.runtimeClassName }}
|
||||
runtimeClassName: "{{ .Values.runtimeClassName }}"
|
||||
{{- end }}
|
||||
{{- if and .Values.securityContext.enabled .Values.persistence.enabled }}
|
||||
{{- if and .Values.securityContext.enabled .Values.persistence.enabled }}
|
||||
securityContext:
|
||||
runAsUser: {{ .Values.securityContext.runAsUser }}
|
||||
runAsGroup: {{ .Values.securityContext.runAsGroup }}
|
||||
@@ -95,18 +89,19 @@ spec:
|
||||
{{- if and (ge .Capabilities.KubeVersion.Major "1") (ge .Capabilities.KubeVersion.Minor "20") }}
|
||||
fsGroupChangePolicy: {{ .Values.securityContext.fsGroupChangePolicy }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ if .Values.serviceAccount.create }}
|
||||
{{- end }}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
serviceAccountName: {{ .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
|
||||
command: [ "/bin/sh",
|
||||
command: [
|
||||
"/bin/sh",
|
||||
"-ce",
|
||||
"/usr/bin/docker-entrypoint.sh minio server {{- range $i := until $poolCount }}{{ $factor := mul $i $nodeCount }}{{ $endIndex := add $factor $nodeCount }}{{ $beginIndex := mul $i $nodeCount }} {{ $scheme }}://{{ template `minio.fullname` $ }}-{{ `{` }}{{ $beginIndex }}...{{ sub $endIndex 1 }}{{ `}`}}.{{ template `minio.fullname` $ }}-svc.{{ $.Release.Namespace }}.svc.{{ $.Values.clusterDomain }}{{if (gt $drivesPerNode 1)}}{{ $bucketRoot }}-{{ `{` }}0...{{ sub $drivesPerNode 1 }}{{ `}` }}{{else}}{{ $bucketRoot }}{{end}}{{- end}} -S {{ .Values.certsPath }} --address :{{ .Values.minioAPIPort }} --console-address :{{ .Values.minioConsolePort }} {{- template `minio.extraArgs` . }}" ]
|
||||
"/usr/bin/docker-entrypoint.sh minio server {{- range $i := until $poolCount }}{{ $factor := mul $i $nodeCount }}{{ $endIndex := add $factor $nodeCount }}{{ $beginIndex := mul $i $nodeCount }} {{ $scheme }}://{{ template `minio.fullname` $ }}-{{ `{` }}{{ $beginIndex }}...{{ sub $endIndex 1 }}{{ `}`}}.{{ template `minio.fullname` $ }}-svc.{{ $.Release.Namespace }}.svc.{{ $.Values.clusterDomain }}{{if (gt $drivesPerNode 1)}}{{ $bucketRoot }}-{{ `{` }}0...{{ sub $drivesPerNode 1 }}{{ `}` }}{{ else }}{{ $bucketRoot }}{{end }}{{- end }} -S {{ .Values.certsPath }} --address :{{ .Values.minioAPIPort }} --console-address :{{ .Values.minioConsolePort }} {{- template `minio.extraArgs` . }}"
|
||||
]
|
||||
volumeMounts:
|
||||
{{- if $penabled }}
|
||||
{{- if (gt $drivesPerNode 1) }}
|
||||
@@ -152,11 +147,11 @@ spec:
|
||||
{{- if .Values.extraSecret }}
|
||||
- name: MINIO_CONFIG_ENV_FILE
|
||||
value: "/tmp/minio-config-env/config.env"
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
{{- if .Values.metrics.serviceMonitor.public }}
|
||||
- name: MINIO_PROMETHEUS_AUTH_TYPE
|
||||
value: "public"
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
{{- if .Values.oidc.enabled }}
|
||||
- name: MINIO_IDENTITY_OPENID_CONFIG_URL
|
||||
value: {{ .Values.oidc.configUrl }}
|
||||
@@ -174,39 +169,34 @@ spec:
|
||||
value: {{ .Values.oidc.redirectUri }}
|
||||
- name: MINIO_IDENTITY_OPENID_COMMENT
|
||||
value: {{ .Values.oidc.comment }}
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
{{- range $key, $val := .Values.environment }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $val | quote }}
|
||||
{{- end}}
|
||||
resources:
|
||||
{{ toYaml .Values.resources | indent 12 }}
|
||||
{{- end }}
|
||||
resources: {{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- with .Values.extraContainers }}
|
||||
{{- if eq (typeOf .) "string" }}
|
||||
{{- tpl . $ | nindent 8 }}
|
||||
{{- else }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- if eq (typeOf .) "string" }}
|
||||
{{- tpl . $ | nindent 8 }}
|
||||
{{- else }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- include "minio.imagePullSecrets" . | indent 6 }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if and (gt $replicas 1) (ge .Capabilities.KubeVersion.Major "1") (ge .Capabilities.KubeVersion.Minor "19") }}
|
||||
{{- with .Values.topologySpreadConstraints }}
|
||||
topologySpreadConstraints:
|
||||
{{ toYaml . | indent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- include "minio.imagePullSecrets" . | indent 6 }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if and (gt $replicas 1) (ge .Capabilities.KubeVersion.Major "1") (ge .Capabilities.KubeVersion.Minor "19") }}
|
||||
{{- with .Values.topologySpreadConstraints }}
|
||||
topologySpreadConstraints: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: minio-user
|
||||
secret:
|
||||
@@ -218,18 +208,17 @@ spec:
|
||||
{{- end }}
|
||||
{{- include "minio.tlsKeysVolume" . | indent 8 }}
|
||||
{{- if .Values.extraVolumes }}
|
||||
{{ toYaml .Values.extraVolumes | nindent 8 }}
|
||||
{{- toYaml .Values.extraVolumes | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.persistence.enabled }}
|
||||
{{- if .Values.persistence.enabled }}
|
||||
volumeClaimTemplates:
|
||||
{{- if gt $drivesPerNode 1 }}
|
||||
{{- if gt $drivesPerNode 1 }}
|
||||
{{- range $diskId := until $drivesPerNode}}
|
||||
- metadata:
|
||||
name: export-{{ $diskId }}
|
||||
{{- if $.Values.persistence.annotations }}
|
||||
annotations:
|
||||
{{ toYaml $.Values.persistence.annotations | trimSuffix "\n" | indent 10 }}
|
||||
{{- end }}
|
||||
{{- if $.Values.persistence.annotations }}
|
||||
annotations: {{- toYaml $.Values.persistence.annotations | nindent 10 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
accessModes: [ {{ $accessMode | quote }} ]
|
||||
{{- if $storageClass }}
|
||||
@@ -239,13 +228,12 @@ spec:
|
||||
requests:
|
||||
storage: {{ $psize }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- else }}
|
||||
- metadata:
|
||||
name: export
|
||||
{{- if $.Values.persistence.annotations }}
|
||||
annotations:
|
||||
{{ toYaml $.Values.persistence.annotations | trimSuffix "\n" | indent 10 }}
|
||||
{{- end }}
|
||||
{{- if $.Values.persistence.annotations }}
|
||||
annotations: {{- toYaml $.Values.persistence.annotations | nindent 10 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
accessModes: [ {{ $accessMode | quote }} ]
|
||||
{{- if $storageClass }}
|
||||
@@ -254,6 +242,6 @@ spec:
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ $psize }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -14,7 +14,7 @@ clusterDomain: cluster.local
|
||||
##
|
||||
image:
|
||||
repository: quay.io/minio/minio
|
||||
tag: RELEASE.2023-02-10T18-48-39Z
|
||||
tag: RELEASE.2023-04-13T03-08-07Z
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
imagePullSecrets: []
|
||||
@@ -25,7 +25,7 @@ imagePullSecrets: []
|
||||
##
|
||||
mcImage:
|
||||
repository: quay.io/minio/mc
|
||||
tag: RELEASE.2023-01-28T20-29-38Z
|
||||
tag: RELEASE.2023-04-12T02-21-51Z
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
## minio mode, i.e. standalone or distributed
|
||||
|
||||
+101
-79
@@ -1,9 +1,31 @@
|
||||
apiVersion: v1
|
||||
entries:
|
||||
minio:
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2023-04-13T03-08-07Z
|
||||
created: "2023-04-13T14:39:33.904722194-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 034d68f85799f6693836975797f85a91842cf2d003a6c4ff401bd4ea4c946af6
|
||||
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-5.0.8.tgz
|
||||
version: 5.0.8
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2023-02-10T18-48-39Z
|
||||
created: "2023-02-13T16:07:05.314663249+05:30"
|
||||
created: "2023-04-13T14:39:33.902972411-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 3f935a310e1b5b873052629b66005c160356ca7b2bd394cb07b34dbaf9905e3f
|
||||
home: https://min.io
|
||||
@@ -25,7 +47,7 @@ entries:
|
||||
version: 5.0.7
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2023-02-10T18-48-39Z
|
||||
created: "2023-02-13T16:07:05.310251414+05:30"
|
||||
created: "2023-04-13T14:39:33.899768149-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 82ef858ce483c2d736444792986cb36bd0fb4fc90a80b97fe30d7b2f2034d24a
|
||||
home: https://min.io
|
||||
@@ -47,7 +69,7 @@ entries:
|
||||
version: 5.0.6
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2023-01-31T02-24-19Z
|
||||
created: "2023-02-13T16:07:05.306878973+05:30"
|
||||
created: "2023-04-13T14:39:33.898010335-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: fefeea10e4e525e45f82fb80a03900d34605ec432dd92f56d94eaf4fb1b98c41
|
||||
home: https://min.io
|
||||
@@ -69,7 +91,7 @@ entries:
|
||||
version: 5.0.5
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-12-12T19-27-27Z
|
||||
created: "2023-02-13T16:07:05.303936931+05:30"
|
||||
created: "2023-04-13T14:39:33.896365088-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 6b305783c98b0b97ffab079ff4430094fd0ca6e98e82bb8153cb93033a1bf40f
|
||||
home: https://min.io
|
||||
@@ -91,7 +113,7 @@ entries:
|
||||
version: 5.0.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-12-12T19-27-27Z
|
||||
created: "2023-02-13T16:07:05.300970047+05:30"
|
||||
created: "2023-04-13T14:39:33.894702003-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: bac89157c53b324aece263c294aa49f5c9b64f426b4b06c9bca3d72e77e244f2
|
||||
home: https://min.io
|
||||
@@ -113,7 +135,7 @@ entries:
|
||||
version: 5.0.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-12-12T19-27-27Z
|
||||
created: "2023-02-13T16:07:05.297908435+05:30"
|
||||
created: "2023-04-13T14:39:33.89270488-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 935ce4f09366231b11d414d626f887fa6fa6024dd30a42e81e810ca1438d5904
|
||||
home: https://min.io
|
||||
@@ -135,7 +157,7 @@ entries:
|
||||
version: 5.0.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-11-11T03-44-20Z
|
||||
created: "2023-02-13T16:07:05.293712618+05:30"
|
||||
created: "2023-04-13T14:39:33.89095156-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 3e952c5d737980b8ccdfb819021eafb4b4e8da226f764a1dc3de1ba63ceb1ffa
|
||||
home: https://min.io
|
||||
@@ -157,7 +179,7 @@ entries:
|
||||
version: 5.0.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-10-24T18-35-07Z
|
||||
created: "2023-02-13T16:07:05.29069869+05:30"
|
||||
created: "2023-04-13T14:39:33.88770083-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 6215c800d84fd4c40e4fb4142645fc1c6a039c251776a3cc8c11a24b9e3b59c7
|
||||
home: https://min.io
|
||||
@@ -179,7 +201,7 @@ entries:
|
||||
version: 5.0.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-10-24T18-35-07Z
|
||||
created: "2023-02-13T16:07:05.287706406+05:30"
|
||||
created: "2023-04-13T14:39:33.885874401-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 2d3d884490ea1127742f938bc9382844bae713caae08b3308f766f3c9000659a
|
||||
home: https://min.io
|
||||
@@ -201,7 +223,7 @@ entries:
|
||||
version: 4.1.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-09-17T00-09-45Z
|
||||
created: "2023-02-13T16:07:05.260150201+05:30"
|
||||
created: "2023-04-13T14:39:33.870938962-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 6f16f2dbfed91ab81a7fae60b6ea32f554365bd27bf5fda55b64a0fa264f4252
|
||||
home: https://min.io
|
||||
@@ -223,7 +245,7 @@ entries:
|
||||
version: 4.0.15
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-09-01T23-53-36Z
|
||||
created: "2023-02-13T16:07:05.256767815+05:30"
|
||||
created: "2023-04-13T14:39:33.869292215-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 35d89d8f49d53ea929466fb88ee26123431326033f1387e6b2d536a629c0a398
|
||||
home: https://min.io
|
||||
@@ -245,7 +267,7 @@ entries:
|
||||
version: 4.0.14
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-08-22T23-53-06Z
|
||||
created: "2023-02-13T16:07:05.253636559+05:30"
|
||||
created: "2023-04-13T14:39:33.867593706-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 5b86937ca88d9f6046141fdc2b1cc54760435ed92d289cd0a115fa7148781d4e
|
||||
home: https://min.io
|
||||
@@ -267,7 +289,7 @@ entries:
|
||||
version: 4.0.13
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-08-13T21-54-44Z
|
||||
created: "2023-02-13T16:07:05.250600219+05:30"
|
||||
created: "2023-04-13T14:39:33.86487566-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 2d9c227c0f46ea8bdef4d760c212156fd4c6623ddc5406779c569fe925527787
|
||||
home: https://min.io
|
||||
@@ -289,7 +311,7 @@ entries:
|
||||
version: 4.0.12
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-08-05T23-27-09Z
|
||||
created: "2023-02-13T16:07:05.24764702+05:30"
|
||||
created: "2023-04-13T14:39:33.863292598-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 6caaffcb636e040cd7e8bc4883a1674a673757f4781c32d53b5ec0f41fea3944
|
||||
home: https://min.io
|
||||
@@ -311,7 +333,7 @@ entries:
|
||||
version: 4.0.11
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-08-02T23-59-16Z
|
||||
created: "2023-02-13T16:07:05.243432562+05:30"
|
||||
created: "2023-04-13T14:39:33.861701816-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 841d87788fb094d6a7d8a91e91821fe1e847bc952e054c781fc93742d112e18a
|
||||
home: https://min.io
|
||||
@@ -333,7 +355,7 @@ entries:
|
||||
version: 4.0.10
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-08-02T23-59-16Z
|
||||
created: "2023-02-13T16:07:05.284607994+05:30"
|
||||
created: "2023-04-13T14:39:33.884239166-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 6f1a78382df3215deac07495a5e7de7009a1153b4cf6cb565630652a69aec4cf
|
||||
home: https://min.io
|
||||
@@ -355,7 +377,7 @@ entries:
|
||||
version: 4.0.9
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-07-29T19-40-48Z
|
||||
created: "2023-02-13T16:07:05.281734578+05:30"
|
||||
created: "2023-04-13T14:39:33.882599235-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: d11db37963636922cb778b6bc0ad2ca4724cb391ea7b785995ada52467d7dd83
|
||||
home: https://min.io
|
||||
@@ -377,7 +399,7 @@ entries:
|
||||
version: 4.0.8
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-07-26T00-53-03Z
|
||||
created: "2023-02-13T16:07:05.278329233+05:30"
|
||||
created: "2023-04-13T14:39:33.881089691-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: ca775e08c84331bb5029d4d29867d30c16e2c62e897788eb432212a756e91e4e
|
||||
home: https://min.io
|
||||
@@ -399,7 +421,7 @@ entries:
|
||||
version: 4.0.7
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-05-08T23-50-31Z
|
||||
created: "2023-02-13T16:07:05.275016183+05:30"
|
||||
created: "2023-04-13T14:39:33.879531106-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 06542b8f3d149d5908b15de9a8d6f8cf304af0213830be56dc315785d14f9ccd
|
||||
home: https://min.io
|
||||
@@ -421,7 +443,7 @@ entries:
|
||||
version: 4.0.6
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-05-08T23-50-31Z
|
||||
created: "2023-02-13T16:07:05.272312786+05:30"
|
||||
created: "2023-04-13T14:39:33.877099765-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: dd2676362f067454a496cdd293609d0c904b08f521625af49f95402a024ba1f5
|
||||
home: https://min.io
|
||||
@@ -443,7 +465,7 @@ entries:
|
||||
version: 4.0.5
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-05-08T23-50-31Z
|
||||
created: "2023-02-13T16:07:05.269633639+05:30"
|
||||
created: "2023-04-13T14:39:33.875614067-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: bab9ef192d4eda4c572ad0ce0cf551736c847f582d1837d6833ee10543c23167
|
||||
home: https://min.io
|
||||
@@ -465,7 +487,7 @@ entries:
|
||||
version: 4.0.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-05-08T23-50-31Z
|
||||
created: "2023-02-13T16:07:05.266852082+05:30"
|
||||
created: "2023-04-13T14:39:33.874105602-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: c770bb9841c76576e4e8573f78b0ec33e0d729504c9667e67ad62d48df5ed64c
|
||||
home: https://min.io
|
||||
@@ -487,7 +509,7 @@ entries:
|
||||
version: 4.0.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-05-08T23-50-31Z
|
||||
created: "2023-02-13T16:07:05.264019141+05:30"
|
||||
created: "2023-04-13T14:39:33.872532023-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 95835f4199d963e2a23a2493610b348e6f2ff8b71c1a648c4a3b84af9b7a83eb
|
||||
home: https://min.io
|
||||
@@ -509,7 +531,7 @@ entries:
|
||||
version: 4.0.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-04-30T22-23-53Z
|
||||
created: "2023-02-13T16:07:05.24049945+05:30"
|
||||
created: "2023-04-13T14:39:33.860146689-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 55a088c403b056e1f055a97426aa11759c3d6cbad38face170fe6cbbec7d568f
|
||||
home: https://min.io
|
||||
@@ -531,7 +553,7 @@ entries:
|
||||
version: 4.0.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-04-26T01-20-24Z
|
||||
created: "2023-02-13T16:07:05.237726036+05:30"
|
||||
created: "2023-04-13T14:39:33.85863375-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: f541237e24336ec3f7f45ae0d523fef694e3a2f9ef648c5b11c15734db6ba2b2
|
||||
home: https://min.io
|
||||
@@ -553,7 +575,7 @@ entries:
|
||||
version: 4.0.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-04-16T04-26-02Z
|
||||
created: "2023-02-13T16:07:05.23502228+05:30"
|
||||
created: "2023-04-13T14:39:33.857146288-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: edc0c3dd6d5246a06b74ba16bb4aff80a6d7225dc9aecf064fd89a8af371b9c1
|
||||
home: https://min.io
|
||||
@@ -575,7 +597,7 @@ entries:
|
||||
version: 3.6.6
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-04-12T06-55-35Z
|
||||
created: "2023-02-13T16:07:05.232278897+05:30"
|
||||
created: "2023-04-13T14:39:33.855161049-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 211e89f6b9eb0b9a3583abaa127be60e1f9717a098e6b2858cb9dc1cc50c1650
|
||||
home: https://min.io
|
||||
@@ -597,7 +619,7 @@ entries:
|
||||
version: 3.6.5
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-04-09T15-09-52Z
|
||||
created: "2023-02-13T16:07:05.229480491+05:30"
|
||||
created: "2023-04-13T14:39:33.853131766-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 534a879d73b370a18b554b93d0930e1c115419619c4ce4ec7dbaae632acacf06
|
||||
home: https://min.io
|
||||
@@ -619,7 +641,7 @@ entries:
|
||||
version: 3.6.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-03-24T00-43-44Z
|
||||
created: "2023-02-13T16:07:05.225464477+05:30"
|
||||
created: "2023-04-13T14:39:33.851651471-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 99508b20eb0083a567dcccaf9a6c237e09575ed1d70cd2e8333f89c472d13d75
|
||||
home: https://min.io
|
||||
@@ -641,7 +663,7 @@ entries:
|
||||
version: 3.6.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-03-17T06-34-49Z
|
||||
created: "2023-02-13T16:07:05.223267703+05:30"
|
||||
created: "2023-04-13T14:39:33.850155733-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: b4cd25611ca322b1d23d23112fdfa6b068fd91eefe0b0663b88ff87ea4282495
|
||||
home: https://min.io
|
||||
@@ -663,7 +685,7 @@ entries:
|
||||
version: 3.6.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-03-14T18-25-24Z
|
||||
created: "2023-02-13T16:07:05.221366808+05:30"
|
||||
created: "2023-04-13T14:39:33.848658622-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: d75b88162bfe54740a233bcecf87328bba2ae23d170bec3a35c828bc6fdc224c
|
||||
home: https://min.io
|
||||
@@ -685,7 +707,7 @@ entries:
|
||||
version: 3.6.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-03-11T23-57-45Z
|
||||
created: "2023-02-13T16:07:05.219604685+05:30"
|
||||
created: "2023-04-13T14:39:33.8471424-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 22e53a1184a21a679bc7d8b94e955777f3506340fc29da5ab0cb6d729bdbde8d
|
||||
home: https://min.io
|
||||
@@ -707,7 +729,7 @@ entries:
|
||||
version: 3.6.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-03-03T21-21-16Z
|
||||
created: "2023-02-13T16:07:05.217806866+05:30"
|
||||
created: "2023-04-13T14:39:33.845572238-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 6fda968d3fdfd60470c0055a4e1a3bd8e5aee9ad0af5ba2fb7b7b926fdc9e4a0
|
||||
home: https://min.io
|
||||
@@ -729,7 +751,7 @@ entries:
|
||||
version: 3.5.9
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-26T02-54-46Z
|
||||
created: "2023-02-13T16:07:05.216065792+05:30"
|
||||
created: "2023-04-13T14:39:33.843283699-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 8e015369048a3a82bbd53ad36696786f18561c6b25d14eee9e2c93a7336cef46
|
||||
home: https://min.io
|
||||
@@ -751,7 +773,7 @@ entries:
|
||||
version: 3.5.8
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-18T01-50-10Z
|
||||
created: "2023-02-13T16:07:05.214380811+05:30"
|
||||
created: "2023-04-13T14:39:33.841793213-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: cb3543fe748e5f0d59b3ccf4ab9af8e10b731405ae445d1f5715e30013632373
|
||||
home: https://min.io
|
||||
@@ -773,7 +795,7 @@ entries:
|
||||
version: 3.5.7
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-18T01-50-10Z
|
||||
created: "2023-02-13T16:07:05.213073341+05:30"
|
||||
created: "2023-04-13T14:39:33.840320255-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: f2e359fa5eefffc59abb3d14a8fa94b11ddeaa99f6cd8dd5f40f4e04121000d6
|
||||
home: https://min.io
|
||||
@@ -795,7 +817,7 @@ entries:
|
||||
version: 3.5.6
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-16T00-35-27Z
|
||||
created: "2023-02-13T16:07:05.211775229+05:30"
|
||||
created: "2023-04-13T14:39:33.838848475-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 529d56cca9d83a3d0e5672e63b6e87b5bcbe10a6b45f7a55ba998cceb32f9c81
|
||||
home: https://min.io
|
||||
@@ -817,7 +839,7 @@ entries:
|
||||
version: 3.5.5
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-12T00-51-25Z
|
||||
created: "2023-02-13T16:07:05.210542911+05:30"
|
||||
created: "2023-04-13T14:39:33.837463455-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 3d530598f8ece67bec5b7f990d206584893987c713502f9228e4ee24b5535414
|
||||
home: https://min.io
|
||||
@@ -839,7 +861,7 @@ entries:
|
||||
version: 3.5.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-12T00-51-25Z
|
||||
created: "2023-02-13T16:07:05.209264623+05:30"
|
||||
created: "2023-04-13T14:39:33.836037131-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 53937031348b29615f07fc4869b2d668391d8ba9084630a497abd7a7dea9dfb0
|
||||
home: https://min.io
|
||||
@@ -861,7 +883,7 @@ entries:
|
||||
version: 3.5.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-07T08-17-33Z
|
||||
created: "2023-02-13T16:07:05.207795313+05:30"
|
||||
created: "2023-04-13T14:39:33.834131809-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 68d643414ff0d565716c5715034fcbf1af262e041915a5c02eb51ec1a65c1ea0
|
||||
home: https://min.io
|
||||
@@ -883,7 +905,7 @@ entries:
|
||||
version: 3.5.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-01T18-00-14Z
|
||||
created: "2023-02-13T16:07:05.206469654+05:30"
|
||||
created: "2023-04-13T14:39:33.832190048-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: a3e855ed0f31233b989fffd775a29d6fbfa0590089010ff16783fd7f142ef6e7
|
||||
home: https://min.io
|
||||
@@ -905,7 +927,7 @@ entries:
|
||||
version: 3.5.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-02-01T18-00-14Z
|
||||
created: "2023-02-13T16:07:05.205352117+05:30"
|
||||
created: "2023-04-13T14:39:33.829251194-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: b1b0ae3c54b4260a698753e11d7781bb8ddc67b7e3fbf0af82796e4cd4ef92a3
|
||||
home: https://min.io
|
||||
@@ -927,7 +949,7 @@ entries:
|
||||
version: 3.5.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-01-28T02-28-16Z
|
||||
created: "2023-02-13T16:07:05.204249186+05:30"
|
||||
created: "2023-04-13T14:39:33.827349955-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: fecf25d2d3fb208c6f894fed642a60780a570b7f6d0adddde846af7236dc80aa
|
||||
home: https://min.io
|
||||
@@ -949,7 +971,7 @@ entries:
|
||||
version: 3.4.8
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-01-25T19-56-04Z
|
||||
created: "2023-02-13T16:07:05.203126945+05:30"
|
||||
created: "2023-04-13T14:39:33.825179668-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: c78008caa5ce98f64c887630f59d0cbd481cb3f19a7d4e9d3e81bf4e1e45cadc
|
||||
home: https://min.io
|
||||
@@ -971,7 +993,7 @@ entries:
|
||||
version: 3.4.7
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-01-08T03-11-54Z
|
||||
created: "2023-02-13T16:07:05.202010303+05:30"
|
||||
created: "2023-04-13T14:39:33.823029641-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 8f2e2691bf897f74ff094dd370ec56ba9d417e5e8926710c14c2ba346330238d
|
||||
home: https://min.io
|
||||
@@ -993,7 +1015,7 @@ entries:
|
||||
version: 3.4.6
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2022-01-04T07-41-07Z
|
||||
created: "2023-02-13T16:07:05.200824734+05:30"
|
||||
created: "2023-04-13T14:39:33.821363456-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: bacd140f0016fab35f516bde787da6449b3a960c071fad9e4b6563118033ac84
|
||||
home: https://min.io
|
||||
@@ -1015,7 +1037,7 @@ entries:
|
||||
version: 3.4.5
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-29T06-49-06Z
|
||||
created: "2023-02-13T16:07:05.199028566+05:30"
|
||||
created: "2023-04-13T14:39:33.81968069-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 48a453ea5ffeef25933904caefd9470bfb26224dfc2d1096bd0031467ba53007
|
||||
home: https://min.io
|
||||
@@ -1037,7 +1059,7 @@ entries:
|
||||
version: 3.4.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-20T22-07-16Z
|
||||
created: "2023-02-13T16:07:05.197899552+05:30"
|
||||
created: "2023-04-13T14:39:33.817648739-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 47ef4a930713b98f9438ceca913c6e700f85bb25dba5624b056486254b5f0c60
|
||||
home: https://min.io
|
||||
@@ -1059,7 +1081,7 @@ entries:
|
||||
version: 3.4.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-20T22-07-16Z
|
||||
created: "2023-02-13T16:07:05.19670699+05:30"
|
||||
created: "2023-04-13T14:39:33.816178628-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: d6763f7e2ea66810bd55eb225579a9c3b968f9ae1256f45fd469362e55d846ff
|
||||
home: https://min.io
|
||||
@@ -1081,7 +1103,7 @@ entries:
|
||||
version: 3.4.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-10T23-03-39Z
|
||||
created: "2023-02-13T16:07:05.195579255+05:30"
|
||||
created: "2023-04-13T14:39:33.813912605-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 2fb822c87216ba3fc2ae51a54a0a3e239aa560d86542991504a841cc2a2b9a37
|
||||
home: https://min.io
|
||||
@@ -1103,7 +1125,7 @@ entries:
|
||||
version: 3.4.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-18T04-42-33Z
|
||||
created: "2023-02-13T16:07:05.19443234+05:30"
|
||||
created: "2023-04-13T14:39:33.812658948-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: fa8ba1aeb1a15316c6be8403416a5e6b5e6139b7166592087e7bddc9e6db5453
|
||||
home: https://min.io
|
||||
@@ -1125,7 +1147,7 @@ entries:
|
||||
version: 3.4.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-10T23-03-39Z
|
||||
created: "2023-02-13T16:07:05.193267056+05:30"
|
||||
created: "2023-04-13T14:39:33.811338087-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: b9b0af9ca50b8d00868e1f1b989dca275829d9110af6de91bb9b3a398341e894
|
||||
home: https://min.io
|
||||
@@ -1147,7 +1169,7 @@ entries:
|
||||
version: 3.3.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-10T23-03-39Z
|
||||
created: "2023-02-13T16:07:05.191695909+05:30"
|
||||
created: "2023-04-13T14:39:33.809986868-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: f8b22a5b8fe95a7ddf61b825e17d11c9345fb10e4c126b0d78381608aa300a08
|
||||
home: https://min.io
|
||||
@@ -1169,7 +1191,7 @@ entries:
|
||||
version: 3.3.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-12-10T23-03-39Z
|
||||
created: "2023-02-13T16:07:05.189974413+05:30"
|
||||
created: "2023-04-13T14:39:33.80878071-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: c48d474f269427abe5ab446f00687d0625b3d1adfc5c73bdb4b21ca9e42853fb
|
||||
home: https://min.io
|
||||
@@ -1191,7 +1213,7 @@ entries:
|
||||
version: 3.3.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-11-24T23-19-33Z
|
||||
created: "2023-02-13T16:07:05.188215082+05:30"
|
||||
created: "2023-04-13T14:39:33.807416726-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 7c3da39d9b0090cbf5efedf0cc163a1e2df05becc5152c3add8e837384690bc4
|
||||
home: https://min.io
|
||||
@@ -1213,7 +1235,7 @@ entries:
|
||||
version: 3.3.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-11-24T23-19-33Z
|
||||
created: "2023-02-13T16:07:05.186446002+05:30"
|
||||
created: "2023-04-13T14:39:33.806163505-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 50d6590b4cc779c40f81cc13b1586fbe508aa7f3230036c760bfc5f4154fbce4
|
||||
home: https://min.io
|
||||
@@ -1235,7 +1257,7 @@ entries:
|
||||
version: 3.3.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-10-13T00-23-17Z
|
||||
created: "2023-02-13T16:07:05.184663736+05:30"
|
||||
created: "2023-04-13T14:39:33.804903629-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 5b797b7208cd904c11a76cd72938c8652160cb5fcd7f09fa41e4e703e6d64054
|
||||
home: https://min.io
|
||||
@@ -1257,7 +1279,7 @@ entries:
|
||||
version: 3.2.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-10-10T16-53-30Z
|
||||
created: "2023-02-13T16:07:05.182907313+05:30"
|
||||
created: "2023-04-13T14:39:33.802626491-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: e084ac4bb095f071e59f8f08bd092e4ab2404c1ddadacfdce7dbe248f1bafff8
|
||||
home: https://min.io
|
||||
@@ -1279,7 +1301,7 @@ entries:
|
||||
version: 3.1.9
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-10-06T23-36-31Z
|
||||
created: "2023-02-13T16:07:05.181104969+05:30"
|
||||
created: "2023-04-13T14:39:33.801365971-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 2890430a8d9487d1fa5508c26776e4881d0086b2c052aa6bdc65c0e4423b9159
|
||||
home: https://min.io
|
||||
@@ -1301,7 +1323,7 @@ entries:
|
||||
version: 3.1.8
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-10-02T16-31-05Z
|
||||
created: "2023-02-13T16:07:05.179044431+05:30"
|
||||
created: "2023-04-13T14:39:33.800136574-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 01a92196af6c47e3a01e1c68d7cf693a8bc487cba810c2cecff155071e4d6a11
|
||||
home: https://min.io
|
||||
@@ -1323,7 +1345,7 @@ entries:
|
||||
version: 3.1.7
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-18T18-09-59Z
|
||||
created: "2023-02-13T16:07:05.177402147+05:30"
|
||||
created: "2023-04-13T14:39:33.798923934-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: e779d73f80b75f33b9c9d995ab10fa455c9c57ee575ebc54e06725a64cd04310
|
||||
home: https://min.io
|
||||
@@ -1345,7 +1367,7 @@ entries:
|
||||
version: 3.1.6
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-18T18-09-59Z
|
||||
created: "2023-02-13T16:07:05.176014131+05:30"
|
||||
created: "2023-04-13T14:39:33.797532313-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 19de4bbc8a400f0c2a94c5e85fc25c9bfc666e773fb3e368dd621d5a57dd1c2a
|
||||
home: https://min.io
|
||||
@@ -1367,7 +1389,7 @@ entries:
|
||||
version: 3.1.5
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-18T18-09-59Z
|
||||
created: "2023-02-13T16:07:05.174916165+05:30"
|
||||
created: "2023-04-13T14:39:33.796286874-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: f789d93a171296dd01af0105a5ce067c663597afbb2432faeda293b752b355c0
|
||||
home: https://min.io
|
||||
@@ -1389,7 +1411,7 @@ entries:
|
||||
version: 3.1.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-09T21-37-07Z
|
||||
created: "2023-02-13T16:07:05.173842859+05:30"
|
||||
created: "2023-04-13T14:39:33.794957696-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: e2eb34d31560b012ef6581f0ff6004ea4376c968cbe0daed2d8f3a614a892afb
|
||||
home: https://min.io
|
||||
@@ -1411,7 +1433,7 @@ entries:
|
||||
version: 3.1.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-09T21-37-07Z
|
||||
created: "2023-02-13T16:07:05.172742202+05:30"
|
||||
created: "2023-04-13T14:39:33.793014019-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 8d7e0cc46b3583abd71b97dc0c071f98321101f90eca17348f1e9e0831be64cd
|
||||
home: https://min.io
|
||||
@@ -1433,7 +1455,7 @@ entries:
|
||||
version: 3.1.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-09T21-37-07Z
|
||||
created: "2023-02-13T16:07:05.171626414+05:30"
|
||||
created: "2023-04-13T14:39:33.790939383-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 50dcbf366b1b21f4a6fc429d0b884c0c7ff481d0fb95c5e9b3ae157c348dd124
|
||||
home: https://min.io
|
||||
@@ -1455,7 +1477,7 @@ entries:
|
||||
version: 3.1.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-09T21-37-07Z
|
||||
created: "2023-02-13T16:07:05.170357547+05:30"
|
||||
created: "2023-04-13T14:39:33.788074463-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 6c01af55d2e2e5f716eabf6fef3a92a8464d0674529e9bacab292e5478a73b7a
|
||||
home: https://min.io
|
||||
@@ -1477,7 +1499,7 @@ entries:
|
||||
version: 3.1.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-09-03T03-56-13Z
|
||||
created: "2023-02-13T16:07:05.168351721+05:30"
|
||||
created: "2023-04-13T14:39:33.786791063-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 18e10be4d0458bc590ca9abf753227e0c70f60511495387b8d4fb15a4daf932e
|
||||
home: https://min.io
|
||||
@@ -1499,7 +1521,7 @@ entries:
|
||||
version: 3.0.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-31T05-46-54Z
|
||||
created: "2023-02-13T16:07:05.167096215+05:30"
|
||||
created: "2023-04-13T14:39:33.785648827-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: f5b6e7f6272a9e71aef3b75555f6f756a39eef65cb78873f26451dba79b19906
|
||||
home: https://min.io
|
||||
@@ -1521,7 +1543,7 @@ entries:
|
||||
version: 3.0.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-31T05-46-54Z
|
||||
created: "2023-02-13T16:07:05.165813385+05:30"
|
||||
created: "2023-04-13T14:39:33.78451068-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 6d2ee1336c412affaaf209fdb80215be2a6ebb23ab2443adbaffef9e7df13fab
|
||||
home: https://min.io
|
||||
@@ -1543,7 +1565,7 @@ entries:
|
||||
version: 3.0.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-31T05-46-54Z
|
||||
created: "2023-02-13T16:07:05.164494238+05:30"
|
||||
created: "2023-04-13T14:39:33.783363751-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 0a004aaf5bb61deed6a5c88256d1695ebe2f9ff1553874a93e4acfd75e8d339b
|
||||
home: https://min.io
|
||||
@@ -1563,7 +1585,7 @@ entries:
|
||||
version: 2.0.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-25T00-41-18Z
|
||||
created: "2023-02-13T16:07:05.163195346+05:30"
|
||||
created: "2023-04-13T14:39:33.782197288-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: fcd944e837ee481307de6aa3d387ea18c234f995a84c15abb211aab4a4054afc
|
||||
home: https://min.io
|
||||
@@ -1583,7 +1605,7 @@ entries:
|
||||
version: 2.0.0
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-25T00-41-18Z
|
||||
created: "2023-02-13T16:07:05.161797765+05:30"
|
||||
created: "2023-04-13T14:39:33.780959636-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 7b6c033d43a856479eb493ab8ca05b230f77c3e42e209e8f298fac6af1a9796f
|
||||
home: https://min.io
|
||||
@@ -1603,7 +1625,7 @@ entries:
|
||||
version: 1.0.5
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-25T00-41-18Z
|
||||
created: "2023-02-13T16:07:05.159565648+05:30"
|
||||
created: "2023-04-13T14:39:33.778433355-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: abd221245ace16c8e0c6c851cf262d1474a5219dcbf25c4b2e7b77142f9c59ed
|
||||
home: https://min.io
|
||||
@@ -1623,7 +1645,7 @@ entries:
|
||||
version: 1.0.4
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-20T18-32-01Z
|
||||
created: "2023-02-13T16:07:05.156671779+05:30"
|
||||
created: "2023-04-13T14:39:33.776484146-07:00"
|
||||
description: Multi-Cloud Object Storage
|
||||
digest: 922a333f5413d1042f7aa81929f43767f6ffca9b260c46713f04ce1dda86d57d
|
||||
home: https://min.io
|
||||
@@ -1643,7 +1665,7 @@ entries:
|
||||
version: 1.0.3
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-20T18-32-01Z
|
||||
created: "2023-02-13T16:07:05.155053059+05:30"
|
||||
created: "2023-04-13T14:39:33.774527099-07:00"
|
||||
description: High Performance, Kubernetes Native Object Storage
|
||||
digest: 10e22773506bbfb1c66442937956534cf4057b94f06a977db78b8cd223588388
|
||||
home: https://min.io
|
||||
@@ -1663,7 +1685,7 @@ entries:
|
||||
version: 1.0.2
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-20T18-32-01Z
|
||||
created: "2023-02-13T16:07:05.153511576+05:30"
|
||||
created: "2023-04-13T14:39:33.772578779-07:00"
|
||||
description: High Performance, Kubernetes Native Object Storage
|
||||
digest: ef86ab6df23d6942705da9ef70991b649638c51bc310587d37a425268ba4a06c
|
||||
home: https://min.io
|
||||
@@ -1683,7 +1705,7 @@ entries:
|
||||
version: 1.0.1
|
||||
- apiVersion: v1
|
||||
appVersion: RELEASE.2021-08-17T20-53-08Z
|
||||
created: "2023-02-13T16:07:05.151860845+05:30"
|
||||
created: "2023-04-13T14:39:33.770495939-07:00"
|
||||
description: High Performance, Kubernetes Native Object Storage
|
||||
digest: 1add7608692cbf39aaf9b1252530e566f7b2f306a14e390b0f49b97a20f2b188
|
||||
home: https://min.io
|
||||
@@ -1701,4 +1723,4 @@ entries:
|
||||
urls:
|
||||
- https://charts.min.io/helm-releases/minio-1.0.0.tgz
|
||||
version: 1.0.0
|
||||
generated: "2023-02-13T16:07:05.14973705+05:30"
|
||||
generated: "2023-04-13T14:39:33.768251143-07:00"
|
||||
|
||||
@@ -186,10 +186,14 @@ func (m *Monitor) DeleteBucket(bucket string) {
|
||||
// DeleteBucketThrottle deletes monitoring for a bucket's target
|
||||
func (m *Monitor) DeleteBucketThrottle(bucket, arn string) {
|
||||
m.tlock.Lock()
|
||||
delete(m.bucketThrottle, bucket)
|
||||
if _, ok := m.bucketThrottle[bucket]; ok {
|
||||
delete(m.bucketThrottle[bucket], arn)
|
||||
}
|
||||
m.tlock.Unlock()
|
||||
m.mlock.Lock()
|
||||
delete(m.activeBuckets, bucket)
|
||||
if _, ok := m.activeBuckets[bucket]; ok {
|
||||
delete(m.activeBuckets[bucket], arn)
|
||||
}
|
||||
m.mlock.Unlock()
|
||||
}
|
||||
|
||||
|
||||
@@ -602,6 +602,17 @@ var renamedSubsys = map[string]string{
|
||||
// Add future sub-system renames
|
||||
}
|
||||
|
||||
const ( // deprecated keys
|
||||
apiReplicationWorkers = "replication_workers"
|
||||
apiReplicationFailedWorkers = "replication_failed_workers"
|
||||
)
|
||||
|
||||
// map of subsystem to deleted keys
|
||||
var deletedSubSysKeys = map[string][]string{
|
||||
APISubSys: {apiReplicationWorkers, apiReplicationFailedWorkers},
|
||||
// Add future sub-system deleted keys
|
||||
}
|
||||
|
||||
// Merge - merges a new config with all the
|
||||
// missing values for default configs,
|
||||
// returns a config.
|
||||
@@ -632,9 +643,16 @@ func (c Config) Merge() Config {
|
||||
}
|
||||
subSys = rnSubSys
|
||||
}
|
||||
// Delete deprecated keys for subsystem if any
|
||||
if keys, ok := deletedSubSysKeys[subSys]; ok {
|
||||
for _, key := range keys {
|
||||
ckvs.Delete(key)
|
||||
}
|
||||
}
|
||||
cp[subSys][tgt] = ckvs
|
||||
}
|
||||
}
|
||||
|
||||
return cp
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
// Copyright (c) 2015-2023 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/internal/event"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
@@ -268,9 +269,9 @@ func (target *KafkaTarget) Close() error {
|
||||
|
||||
// Check if atleast one broker in cluster is active
|
||||
func (k KafkaArgs) pingBrokers() bool {
|
||||
d := net.Dialer{Timeout: 60 * time.Second}
|
||||
for _, broker := range k.Brokers {
|
||||
_, dErr := net.Dial("tcp", broker.String())
|
||||
if dErr == nil {
|
||||
if _, err := d.Dial("tcp", broker.String()); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ func newBufPool(sz int) httputil.BufferPool {
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
// ServeHTTP forwards HTTP traffic using the configured transport
|
||||
func (f *Forwarder) ServeHTTP(w http.ResponseWriter, inReq *http.Request) {
|
||||
outReq := new(http.Request)
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
"net/http"
|
||||
@@ -53,6 +54,8 @@ const (
|
||||
ChecksumCRC32C
|
||||
// ChecksumInvalid indicates an invalid checksum.
|
||||
ChecksumInvalid
|
||||
// ChecksumMultipart indicates the checksum is from a multipart upload.
|
||||
ChecksumMultipart
|
||||
|
||||
// ChecksumNone indicates no checksum.
|
||||
ChecksumNone ChecksumType = 0
|
||||
@@ -194,8 +197,17 @@ func ReadCheckSums(b []byte) map[string]string {
|
||||
if length == 0 || len(b) < length {
|
||||
break
|
||||
}
|
||||
res[typ.String()] = base64.StdEncoding.EncodeToString(b[:length])
|
||||
cs := base64.StdEncoding.EncodeToString(b[:length])
|
||||
b = b[length:]
|
||||
if typ.Is(ChecksumMultipart) {
|
||||
t, n := binary.Uvarint(b)
|
||||
if n < 0 {
|
||||
break
|
||||
}
|
||||
cs = fmt.Sprintf("%s-%d", cs, t)
|
||||
b = b[n:]
|
||||
}
|
||||
res[typ.String()] = cs
|
||||
}
|
||||
if len(res) == 0 {
|
||||
res = nil
|
||||
@@ -225,8 +237,9 @@ func NewChecksumString(alg, value string) *Checksum {
|
||||
}
|
||||
|
||||
// AppendTo will append the checksum to b.
|
||||
// 'parts' is used when checksum has ChecksumMultipart set.
|
||||
// ReadCheckSums reads the values back.
|
||||
func (c *Checksum) AppendTo(b []byte) []byte {
|
||||
func (c *Checksum) AppendTo(b []byte, parts int) []byte {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -238,6 +251,13 @@ func (c *Checksum) AppendTo(b []byte) []byte {
|
||||
}
|
||||
b = append(b, tmp[:n]...)
|
||||
b = append(b, crc...)
|
||||
if c.Type.Is(ChecksumMultipart) {
|
||||
if parts < 0 {
|
||||
parts = 0
|
||||
}
|
||||
n := binary.PutUvarint(tmp[:], uint64(parts))
|
||||
b = append(b, tmp[:n]...)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ func (r *RequestRecorder) Read(p []byte) (n int, err error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// BodySize returns the body size if the currently read object
|
||||
func (r *RequestRecorder) BodySize() int {
|
||||
// Size returns the body size if the currently read object
|
||||
func (r *RequestRecorder) Size() int {
|
||||
return r.bytesRead
|
||||
}
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package stats
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// IncomingTrafficMeter counts the incoming bytes from the underlying request.Body.
|
||||
type IncomingTrafficMeter struct {
|
||||
countBytes int64
|
||||
io.ReadCloser
|
||||
}
|
||||
|
||||
// Read calls the underlying Read and counts the transferred bytes.
|
||||
func (r *IncomingTrafficMeter) Read(p []byte) (n int, err error) {
|
||||
n, err = r.ReadCloser.Read(p)
|
||||
r.countBytes += int64(n)
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// BytesRead returns the number of transferred bytes
|
||||
func (r *IncomingTrafficMeter) BytesRead() int64 {
|
||||
return r.countBytes
|
||||
}
|
||||
|
||||
// OutgoingTrafficMeter counts the outgoing bytes through the responseWriter.
|
||||
type OutgoingTrafficMeter struct {
|
||||
countBytes int64
|
||||
// wrapper for underlying http.ResponseWriter.
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
// Write calls the underlying write and counts the output bytes
|
||||
func (w *OutgoingTrafficMeter) Write(p []byte) (n int, err error) {
|
||||
n, err = w.ResponseWriter.Write(p)
|
||||
w.countBytes += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Flush calls the underlying Flush.
|
||||
func (w *OutgoingTrafficMeter) Flush() {
|
||||
w.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
// BytesWritten returns the number of transferred bytes
|
||||
func (w *OutgoingTrafficMeter) BytesWritten() int64 {
|
||||
return w.countBytes
|
||||
}
|
||||
@@ -24,11 +24,10 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/internal/http/stats"
|
||||
internalAudit "github.com/minio/minio/internal/logger/message/audit"
|
||||
"github.com/minio/minio/internal/mcontext"
|
||||
"github.com/minio/pkg/logger/message/audit"
|
||||
|
||||
"github.com/klauspost/compress/gzhttp"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
)
|
||||
@@ -96,34 +95,13 @@ func AuditLog(ctx context.Context, w http.ResponseWriter, r *http.Request, reqCl
|
||||
headerBytes int64
|
||||
)
|
||||
|
||||
var st *xhttp.ResponseRecorder
|
||||
|
||||
for {
|
||||
switch v := w.(type) {
|
||||
case *gzhttp.GzipResponseWriter:
|
||||
// the writer may be obscured by gzip response writer
|
||||
w = v.ResponseWriter
|
||||
case *gzhttp.NoGzipResponseWriter:
|
||||
// the writer may be obscured by no-gzip response writer
|
||||
w = v.ResponseWriter
|
||||
case *stats.OutgoingTrafficMeter:
|
||||
// the writer may be obscured by http stats response writer
|
||||
w = v.ResponseWriter
|
||||
case *xhttp.ResponseRecorder:
|
||||
st = v
|
||||
goto exit
|
||||
default:
|
||||
goto exit
|
||||
}
|
||||
}
|
||||
exit:
|
||||
|
||||
if st != nil {
|
||||
statusCode = st.StatusCode
|
||||
timeToResponse = time.Now().UTC().Sub(st.StartTime)
|
||||
timeToFirstByte = st.TimeToFirstByte
|
||||
outputBytes = int64(st.Size())
|
||||
headerBytes = int64(st.HeaderSize())
|
||||
tc, ok := r.Context().Value(mcontext.ContextTraceKey).(*mcontext.TraceCtxt)
|
||||
if ok {
|
||||
statusCode = tc.ResponseRecorder.StatusCode
|
||||
outputBytes = int64(tc.ResponseRecorder.Size())
|
||||
headerBytes = int64(tc.ResponseRecorder.HeaderSize())
|
||||
timeToResponse = time.Now().UTC().Sub(tc.ResponseRecorder.StartTime)
|
||||
timeToFirstByte = tc.ResponseRecorder.TimeToFirstByte
|
||||
}
|
||||
|
||||
entry.AccessKey = reqInfo.Cred.AccessKey
|
||||
@@ -147,7 +125,7 @@ func AuditLog(ctx context.Context, w http.ResponseWriter, r *http.Request, reqCl
|
||||
entry.API.TimeToResponse = strconv.FormatInt(timeToResponse.Nanoseconds(), 10) + "ns"
|
||||
entry.API.TimeToResponseInNS = strconv.FormatInt(timeToResponse.Nanoseconds(), 10)
|
||||
entry.Tags = reqInfo.GetTagsMap()
|
||||
// ttfb will be recorded only for GET requests, Ignore such cases where ttfb will be empty.
|
||||
// ignore cases for ttfb when its zero.
|
||||
if timeToFirstByte != 0 {
|
||||
entry.API.TimeToFirstByte = strconv.FormatInt(timeToFirstByte.Nanoseconds(), 10) + "ns"
|
||||
entry.API.TimeToFirstByteInNS = strconv.FormatInt(timeToFirstByte.Nanoseconds(), 10)
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/minio/pkg/logger/message/audit"
|
||||
|
||||
@@ -148,18 +149,16 @@ type Config struct {
|
||||
}
|
||||
|
||||
// Check if atleast one broker in cluster is active
|
||||
func (k Config) pingBrokers() error {
|
||||
var err error
|
||||
func (k Config) pingBrokers() (err error) {
|
||||
d := net.Dialer{Timeout: 60 * time.Second}
|
||||
|
||||
for _, broker := range k.Brokers {
|
||||
_, err1 := net.Dial("tcp", broker.String())
|
||||
if err1 != nil {
|
||||
if err == nil {
|
||||
// Set first error
|
||||
err = err1
|
||||
}
|
||||
_, err = d.Dial("tcp", broker.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stats returns the target statistics.
|
||||
|
||||
Reference in New Issue
Block a user