mirror of
https://github.com/pgsty/minio.git
synced 2026-11-04 14:52:02 +03:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 504e52b45e | |||
| 38c0840834 | |||
| c65e67c357 | |||
| fb2360ff88 | |||
| 1a2de1bdde | |||
| 993b97f1db | |||
| 1c4d28d7af | |||
| af55f37b27 | |||
| 2d67c26794 | |||
| 006cacfefb | |||
| c28f09d4a7 | |||
| 73992d2b9f | |||
| a8f143298f | |||
| 2d44c161c7 | |||
| 9511056f44 | |||
| fb4ad000b6 | |||
| a8ff12bc72 | |||
| 1e1bd3afd9 | |||
| 7b239ae154 | |||
| 8a11282522 | |||
| 85c3db3a93 | |||
| 37383ecd09 | |||
| 6378ca10a4 | |||
| 9e81ccd2d9 |
+5
-9
@@ -1,10 +1,10 @@
|
||||
FROM golang:1.21-alpine as build
|
||||
FROM golang:1.22-alpine as build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV CGO_ENABLED 0
|
||||
ENV GOPATH=/go
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
@@ -54,12 +54,8 @@ ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MC_CONFIG_DIR=/tmp/.mc
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio /usr/bin/minio
|
||||
COPY --from=build /go/bin/minio.minisig /usr/bin/minio.minisig
|
||||
COPY --from=build /go/bin/minio.sha256sum /usr/bin/minio.sha256sum
|
||||
COPY --from=build /go/bin/mc /usr/bin/mc
|
||||
COPY --from=build /go/bin/mc.minisig /usr/bin/mc.minisig
|
||||
COPY --from=build /go/bin/mc.sha256sum /usr/bin/mc.sha256sum
|
||||
COPY --from=build /go/bin/minio* /usr/bin/
|
||||
COPY --from=build /go/bin/mc* /usr/bin/
|
||||
COPY --from=build /go/bin/cur* /usr/bin/
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
|
||||
+5
-9
@@ -1,10 +1,10 @@
|
||||
FROM golang:1.21-alpine as build
|
||||
FROM golang:1.22-alpine AS build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV CGO_ENABLED 0
|
||||
ENV GOPATH=/go
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
@@ -54,12 +54,8 @@ ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MC_CONFIG_DIR=/tmp/.mc
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio /usr/bin/minio
|
||||
COPY --from=build /go/bin/minio.minisig /usr/bin/minio.minisig
|
||||
COPY --from=build /go/bin/minio.sha256sum /usr/bin/minio.sha256sum
|
||||
COPY --from=build /go/bin/mc /usr/bin/mc
|
||||
COPY --from=build /go/bin/mc.minisig /usr/bin/mc.minisig
|
||||
COPY --from=build /go/bin/mc.sha256sum /usr/bin/mc.sha256sum
|
||||
COPY --from=build /go/bin/minio* /usr/bin/
|
||||
COPY --from=build /go/bin/mc* /usr/bin/
|
||||
COPY --from=build /go/bin/cur* /usr/bin/
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
FROM golang:1.21-alpine as build
|
||||
FROM golang:1.22-alpine AS build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV CGO_ENABLED 0
|
||||
ENV GOPATH=/go
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
@@ -52,12 +52,8 @@ ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MINIO_CONFIG_ENV_FILE=config.env
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio /usr/bin/minio
|
||||
COPY --from=build /go/bin/minio.minisig /usr/bin/minio.minisig
|
||||
COPY --from=build /go/bin/minio.sha256sum /usr/bin/minio.sha256sum
|
||||
COPY --from=build /go/bin/mc /usr/bin/mc
|
||||
COPY --from=build /go/bin/mc.minisig /usr/bin/mc.minisig
|
||||
COPY --from=build /go/bin/mc.sha256sum /usr/bin/mc.sha256sum
|
||||
COPY --from=build /go/bin/minio* /usr/bin/
|
||||
COPY --from=build /go/bin/mc* /usr/bin/
|
||||
COPY --from=build /go/bin/cur* /usr/bin/
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
FROM golang:1.21-alpine as build
|
||||
FROM golang:1.22-alpine AS build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RELEASE
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV CGO_ENABLED 0
|
||||
ENV GOPATH=/go
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
# Install curl and minisign
|
||||
RUN apk add -U --no-cache ca-certificates && \
|
||||
@@ -54,12 +54,8 @@ ENV MINIO_ACCESS_KEY_FILE=access_key \
|
||||
MC_CONFIG_DIR=/tmp/.mc
|
||||
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=build /go/bin/minio /usr/bin/minio
|
||||
COPY --from=build /go/bin/minio.minisig /usr/bin/minio.minisig
|
||||
COPY --from=build /go/bin/minio.sha256sum /usr/bin/minio.sha256sum
|
||||
COPY --from=build /go/bin/mc /usr/bin/mc
|
||||
COPY --from=build /go/bin/mc.minisig /usr/bin/mc.minisig
|
||||
COPY --from=build /go/bin/mc.sha256sum /usr/bin/mc.sha256sum
|
||||
COPY --from=build /go/bin/minio* /usr/bin/
|
||||
COPY --from=build /go/bin/mc* /usr/bin/
|
||||
COPY --from=build /go/bin/cur* /usr/bin/
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
|
||||
@@ -427,6 +427,21 @@ func (a adminAPIHandlers) ExportBucketMetadataHandler(w http.ResponseWriter, r *
|
||||
cfgPath := pathJoin(bi.Name, cfgFile)
|
||||
bucket := bi.Name
|
||||
switch cfgFile {
|
||||
case bucketPolicyConfig:
|
||||
config, _, err := globalBucketMetadataSys.GetBucketPolicy(bucket)
|
||||
if err != nil {
|
||||
if errors.Is(err, BucketPolicyNotFound{Bucket: bucket}) {
|
||||
continue
|
||||
}
|
||||
writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL)
|
||||
return
|
||||
}
|
||||
configData, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL)
|
||||
return
|
||||
}
|
||||
rawDataFn(bytes.NewReader(configData), cfgPath, len(configData))
|
||||
case bucketNotificationConfig:
|
||||
config, err := globalBucketMetadataSys.GetNotificationConfig(bucket)
|
||||
if err != nil {
|
||||
@@ -796,6 +811,7 @@ func (a adminAPIHandlers) ImportBucketMetadataHandler(w http.ResponseWriter, r *
|
||||
}
|
||||
|
||||
bucketMap[bucket].NotificationConfigXML = configData
|
||||
bucketMap[bucket].NotificationConfigUpdatedAt = updatedAt
|
||||
rpt.SetStatus(bucket, fileName, nil)
|
||||
case bucketPolicyConfig:
|
||||
// Error out if Content-Length is beyond allowed size.
|
||||
|
||||
@@ -72,10 +72,12 @@ type healingTracker struct {
|
||||
|
||||
// Numbers when current bucket started healing,
|
||||
// for resuming with correct numbers.
|
||||
ResumeItemsHealed uint64 `json:"-"`
|
||||
ResumeItemsFailed uint64 `json:"-"`
|
||||
ResumeBytesDone uint64 `json:"-"`
|
||||
ResumeBytesFailed uint64 `json:"-"`
|
||||
ResumeItemsHealed uint64 `json:"-"`
|
||||
ResumeItemsFailed uint64 `json:"-"`
|
||||
ResumeItemsSkipped uint64 `json:"-"`
|
||||
ResumeBytesDone uint64 `json:"-"`
|
||||
ResumeBytesFailed uint64 `json:"-"`
|
||||
ResumeBytesSkipped uint64 `json:"-"`
|
||||
|
||||
// Filled on startup/restarts.
|
||||
QueuedBuckets []string
|
||||
@@ -90,6 +92,9 @@ type healingTracker struct {
|
||||
BytesSkipped uint64
|
||||
|
||||
RetryAttempts uint64
|
||||
|
||||
Finished bool // finished healing, whether with errors or not
|
||||
|
||||
// Add future tracking capabilities
|
||||
// Be sure that they are included in toHealingDisk
|
||||
}
|
||||
@@ -143,6 +148,26 @@ func initHealingTracker(disk StorageAPI, healID string) *healingTracker {
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *healingTracker) resetHealing() {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
h.ItemsHealed = 0
|
||||
h.ItemsFailed = 0
|
||||
h.BytesDone = 0
|
||||
h.BytesFailed = 0
|
||||
h.ResumeItemsHealed = 0
|
||||
h.ResumeItemsFailed = 0
|
||||
h.ResumeBytesDone = 0
|
||||
h.ResumeBytesFailed = 0
|
||||
h.ItemsSkipped = 0
|
||||
h.BytesSkipped = 0
|
||||
|
||||
h.HealedBuckets = nil
|
||||
h.Object = ""
|
||||
h.Bucket = ""
|
||||
}
|
||||
|
||||
func (h *healingTracker) getLastUpdate() time.Time {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
@@ -262,8 +287,10 @@ func (h *healingTracker) resume() {
|
||||
|
||||
h.ItemsHealed = h.ResumeItemsHealed
|
||||
h.ItemsFailed = h.ResumeItemsFailed
|
||||
h.ItemsSkipped = h.ResumeItemsSkipped
|
||||
h.BytesDone = h.ResumeBytesDone
|
||||
h.BytesFailed = h.ResumeBytesFailed
|
||||
h.BytesSkipped = h.ResumeBytesSkipped
|
||||
}
|
||||
|
||||
// bucketDone should be called when a bucket is done healing.
|
||||
@@ -274,8 +301,10 @@ func (h *healingTracker) bucketDone(bucket string) {
|
||||
|
||||
h.ResumeItemsHealed = h.ItemsHealed
|
||||
h.ResumeItemsFailed = h.ItemsFailed
|
||||
h.ResumeItemsSkipped = h.ItemsSkipped
|
||||
h.ResumeBytesDone = h.BytesDone
|
||||
h.ResumeBytesFailed = h.BytesFailed
|
||||
h.ResumeBytesSkipped = h.BytesSkipped
|
||||
h.HealedBuckets = append(h.HealedBuckets, bucket)
|
||||
for i, b := range h.QueuedBuckets {
|
||||
if b == bucket {
|
||||
@@ -324,6 +353,7 @@ func (h *healingTracker) toHealingDisk() madmin.HealingDisk {
|
||||
PoolIndex: h.PoolIndex,
|
||||
SetIndex: h.SetIndex,
|
||||
DiskIndex: h.DiskIndex,
|
||||
Finished: h.Finished,
|
||||
Path: h.Path,
|
||||
Started: h.Started.UTC(),
|
||||
LastUpdate: h.LastUpdate.UTC(),
|
||||
@@ -339,6 +369,7 @@ func (h *healingTracker) toHealingDisk() madmin.HealingDisk {
|
||||
Object: h.Object,
|
||||
QueuedBuckets: h.QueuedBuckets,
|
||||
HealedBuckets: h.HealedBuckets,
|
||||
RetryAttempts: h.RetryAttempts,
|
||||
|
||||
ObjectsHealed: h.ItemsHealed, // Deprecated July 2021
|
||||
ObjectsFailed: h.ItemsFailed, // Deprecated July 2021
|
||||
@@ -353,8 +384,7 @@ func initAutoHeal(ctx context.Context, objAPI ObjectLayer) {
|
||||
}
|
||||
|
||||
initBackgroundHealing(ctx, objAPI) // start quick background healing
|
||||
|
||||
if env.Get("_MINIO_AUTO_DRIVE_HEALING", config.EnableOn) == config.EnableOn || env.Get("_MINIO_AUTO_DISK_HEALING", config.EnableOn) == config.EnableOn {
|
||||
if env.Get("_MINIO_AUTO_DRIVE_HEALING", config.EnableOn) == config.EnableOn {
|
||||
globalBackgroundHealState.pushHealLocalDisks(getLocalDisksToHeal()...)
|
||||
go monitorLocalDisksAndHeal(ctx, z)
|
||||
}
|
||||
@@ -373,7 +403,7 @@ func getLocalDisksToHeal() (disksToHeal Endpoints) {
|
||||
disksToHeal = append(disksToHeal, disk.Endpoint())
|
||||
continue
|
||||
}
|
||||
if disk.Healing() != nil {
|
||||
if h := disk.Healing(); h != nil && !h.Finished {
|
||||
disksToHeal = append(disksToHeal, disk.Endpoint())
|
||||
}
|
||||
}
|
||||
@@ -472,16 +502,19 @@ func healFreshDisk(ctx context.Context, z *erasureServerPools, endpoint Endpoint
|
||||
// if objects have failed healing, we attempt a retry to heal the drive upto 3 times before giving up.
|
||||
if tracker.ItemsFailed > 0 && tracker.RetryAttempts < 4 {
|
||||
tracker.RetryAttempts++
|
||||
bugLogIf(ctx, tracker.update(ctx))
|
||||
|
||||
healingLogEvent(ctx, "Healing of drive '%s' is incomplete, retrying %s time (healed: %d, skipped: %d, failed: %d).", disk,
|
||||
humanize.Ordinal(int(tracker.RetryAttempts)), tracker.ItemsHealed, tracker.ItemsSkipped, tracker.ItemsFailed)
|
||||
|
||||
tracker.resetHealing()
|
||||
bugLogIf(ctx, tracker.update(ctx))
|
||||
|
||||
return errRetryHealing
|
||||
}
|
||||
|
||||
if tracker.ItemsFailed > 0 {
|
||||
healingLogEvent(ctx, "Healing of drive '%s' is incomplete, retried %d times (healed: %d, skipped: %d, failed: %d).", disk,
|
||||
tracker.RetryAttempts-1, tracker.ItemsHealed, tracker.ItemsSkipped, tracker.ItemsFailed)
|
||||
tracker.RetryAttempts, tracker.ItemsHealed, tracker.ItemsSkipped, tracker.ItemsFailed)
|
||||
} else {
|
||||
if tracker.RetryAttempts > 0 {
|
||||
healingLogEvent(ctx, "Healing of drive '%s' is complete, retried %d times (healed: %d, skipped: %d).", disk,
|
||||
@@ -519,7 +552,8 @@ func healFreshDisk(ctx context.Context, z *erasureServerPools, endpoint Endpoint
|
||||
continue
|
||||
}
|
||||
if t.HealID == tracker.HealID {
|
||||
t.delete(ctx)
|
||||
t.Finished = true
|
||||
t.update(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +132,12 @@ func (z *healingTracker) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "ResumeItemsFailed")
|
||||
return
|
||||
}
|
||||
case "ResumeItemsSkipped":
|
||||
z.ResumeItemsSkipped, err = dc.ReadUint64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ResumeItemsSkipped")
|
||||
return
|
||||
}
|
||||
case "ResumeBytesDone":
|
||||
z.ResumeBytesDone, err = dc.ReadUint64()
|
||||
if err != nil {
|
||||
@@ -144,6 +150,12 @@ func (z *healingTracker) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "ResumeBytesFailed")
|
||||
return
|
||||
}
|
||||
case "ResumeBytesSkipped":
|
||||
z.ResumeBytesSkipped, err = dc.ReadUint64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ResumeBytesSkipped")
|
||||
return
|
||||
}
|
||||
case "QueuedBuckets":
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadArrayHeader()
|
||||
@@ -206,6 +218,12 @@ func (z *healingTracker) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "RetryAttempts")
|
||||
return
|
||||
}
|
||||
case "Finished":
|
||||
z.Finished, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Finished")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
@@ -219,9 +237,9 @@ func (z *healingTracker) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *healingTracker) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 26
|
||||
// map header, size 29
|
||||
// write "ID"
|
||||
err = en.Append(0xde, 0x0, 0x1a, 0xa2, 0x49, 0x44)
|
||||
err = en.Append(0xde, 0x0, 0x1d, 0xa2, 0x49, 0x44)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -400,6 +418,16 @@ func (z *healingTracker) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = msgp.WrapError(err, "ResumeItemsFailed")
|
||||
return
|
||||
}
|
||||
// write "ResumeItemsSkipped"
|
||||
err = en.Append(0xb2, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteUint64(z.ResumeItemsSkipped)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ResumeItemsSkipped")
|
||||
return
|
||||
}
|
||||
// write "ResumeBytesDone"
|
||||
err = en.Append(0xaf, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x44, 0x6f, 0x6e, 0x65)
|
||||
if err != nil {
|
||||
@@ -420,6 +448,16 @@ func (z *healingTracker) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = msgp.WrapError(err, "ResumeBytesFailed")
|
||||
return
|
||||
}
|
||||
// write "ResumeBytesSkipped"
|
||||
err = en.Append(0xb2, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteUint64(z.ResumeBytesSkipped)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ResumeBytesSkipped")
|
||||
return
|
||||
}
|
||||
// write "QueuedBuckets"
|
||||
err = en.Append(0xad, 0x51, 0x75, 0x65, 0x75, 0x65, 0x64, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73)
|
||||
if err != nil {
|
||||
@@ -494,15 +532,25 @@ func (z *healingTracker) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = msgp.WrapError(err, "RetryAttempts")
|
||||
return
|
||||
}
|
||||
// write "Finished"
|
||||
err = en.Append(0xa8, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.Finished)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Finished")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *healingTracker) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 26
|
||||
// map header, size 29
|
||||
// string "ID"
|
||||
o = append(o, 0xde, 0x0, 0x1a, 0xa2, 0x49, 0x44)
|
||||
o = append(o, 0xde, 0x0, 0x1d, 0xa2, 0x49, 0x44)
|
||||
o = msgp.AppendString(o, z.ID)
|
||||
// string "PoolIndex"
|
||||
o = append(o, 0xa9, 0x50, 0x6f, 0x6f, 0x6c, 0x49, 0x6e, 0x64, 0x65, 0x78)
|
||||
@@ -555,12 +603,18 @@ func (z *healingTracker) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
// string "ResumeItemsFailed"
|
||||
o = append(o, 0xb1, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64)
|
||||
o = msgp.AppendUint64(o, z.ResumeItemsFailed)
|
||||
// string "ResumeItemsSkipped"
|
||||
o = append(o, 0xb2, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64)
|
||||
o = msgp.AppendUint64(o, z.ResumeItemsSkipped)
|
||||
// string "ResumeBytesDone"
|
||||
o = append(o, 0xaf, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x44, 0x6f, 0x6e, 0x65)
|
||||
o = msgp.AppendUint64(o, z.ResumeBytesDone)
|
||||
// string "ResumeBytesFailed"
|
||||
o = append(o, 0xb1, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64)
|
||||
o = msgp.AppendUint64(o, z.ResumeBytesFailed)
|
||||
// string "ResumeBytesSkipped"
|
||||
o = append(o, 0xb2, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64)
|
||||
o = msgp.AppendUint64(o, z.ResumeBytesSkipped)
|
||||
// string "QueuedBuckets"
|
||||
o = append(o, 0xad, 0x51, 0x75, 0x65, 0x75, 0x65, 0x64, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73)
|
||||
o = msgp.AppendArrayHeader(o, uint32(len(z.QueuedBuckets)))
|
||||
@@ -585,6 +639,9 @@ func (z *healingTracker) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
// string "RetryAttempts"
|
||||
o = append(o, 0xad, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73)
|
||||
o = msgp.AppendUint64(o, z.RetryAttempts)
|
||||
// string "Finished"
|
||||
o = append(o, 0xa8, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64)
|
||||
o = msgp.AppendBool(o, z.Finished)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -714,6 +771,12 @@ func (z *healingTracker) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "ResumeItemsFailed")
|
||||
return
|
||||
}
|
||||
case "ResumeItemsSkipped":
|
||||
z.ResumeItemsSkipped, bts, err = msgp.ReadUint64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ResumeItemsSkipped")
|
||||
return
|
||||
}
|
||||
case "ResumeBytesDone":
|
||||
z.ResumeBytesDone, bts, err = msgp.ReadUint64Bytes(bts)
|
||||
if err != nil {
|
||||
@@ -726,6 +789,12 @@ func (z *healingTracker) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "ResumeBytesFailed")
|
||||
return
|
||||
}
|
||||
case "ResumeBytesSkipped":
|
||||
z.ResumeBytesSkipped, bts, err = msgp.ReadUint64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ResumeBytesSkipped")
|
||||
return
|
||||
}
|
||||
case "QueuedBuckets":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
@@ -788,6 +857,12 @@ func (z *healingTracker) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "RetryAttempts")
|
||||
return
|
||||
}
|
||||
case "Finished":
|
||||
z.Finished, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Finished")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
@@ -802,7 +877,7 @@ func (z *healingTracker) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *healingTracker) Msgsize() (s int) {
|
||||
s = 3 + 3 + msgp.StringPrefixSize + len(z.ID) + 10 + msgp.IntSize + 9 + msgp.IntSize + 10 + msgp.IntSize + 5 + msgp.StringPrefixSize + len(z.Path) + 9 + msgp.StringPrefixSize + len(z.Endpoint) + 8 + msgp.TimeSize + 11 + msgp.TimeSize + 18 + msgp.Uint64Size + 17 + msgp.Uint64Size + 12 + msgp.Uint64Size + 12 + msgp.Uint64Size + 10 + msgp.Uint64Size + 12 + msgp.Uint64Size + 7 + msgp.StringPrefixSize + len(z.Bucket) + 7 + msgp.StringPrefixSize + len(z.Object) + 18 + msgp.Uint64Size + 18 + msgp.Uint64Size + 16 + msgp.Uint64Size + 18 + msgp.Uint64Size + 14 + msgp.ArrayHeaderSize
|
||||
s = 3 + 3 + msgp.StringPrefixSize + len(z.ID) + 10 + msgp.IntSize + 9 + msgp.IntSize + 10 + msgp.IntSize + 5 + msgp.StringPrefixSize + len(z.Path) + 9 + msgp.StringPrefixSize + len(z.Endpoint) + 8 + msgp.TimeSize + 11 + msgp.TimeSize + 18 + msgp.Uint64Size + 17 + msgp.Uint64Size + 12 + msgp.Uint64Size + 12 + msgp.Uint64Size + 10 + msgp.Uint64Size + 12 + msgp.Uint64Size + 7 + msgp.StringPrefixSize + len(z.Bucket) + 7 + msgp.StringPrefixSize + len(z.Object) + 18 + msgp.Uint64Size + 18 + msgp.Uint64Size + 19 + msgp.Uint64Size + 16 + msgp.Uint64Size + 18 + msgp.Uint64Size + 19 + msgp.Uint64Size + 14 + msgp.ArrayHeaderSize
|
||||
for za0001 := range z.QueuedBuckets {
|
||||
s += msgp.StringPrefixSize + len(z.QueuedBuckets[za0001])
|
||||
}
|
||||
@@ -810,6 +885,6 @@ func (z *healingTracker) Msgsize() (s int) {
|
||||
for za0002 := range z.HealedBuckets {
|
||||
s += msgp.StringPrefixSize + len(z.HealedBuckets[za0002])
|
||||
}
|
||||
s += 7 + msgp.StringPrefixSize + len(z.HealID) + 13 + msgp.Uint64Size + 13 + msgp.Uint64Size + 14 + msgp.Uint64Size
|
||||
s += 7 + msgp.StringPrefixSize + len(z.HealID) + 13 + msgp.Uint64Size + 13 + msgp.Uint64Size + 14 + msgp.Uint64Size + 9 + msgp.BoolSize
|
||||
return
|
||||
}
|
||||
|
||||
@@ -141,13 +141,7 @@ func (b *streamingBitrotReader) Close() error {
|
||||
}
|
||||
if closer, ok := b.rc.(io.Closer); ok {
|
||||
// drain the body for connection reuse at network layer.
|
||||
xhttp.DrainBody(struct {
|
||||
io.Reader
|
||||
io.Closer
|
||||
}{
|
||||
Reader: b.rc,
|
||||
Closer: closeWrapper(func() error { return nil }),
|
||||
})
|
||||
xhttp.DrainBody(io.NopCloser(b.rc))
|
||||
return closer.Close()
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1003,7 +1003,7 @@ func ongoingRestoreObj() restoreObjStatus {
|
||||
}
|
||||
}
|
||||
|
||||
// completeRestoreObj constructs restoreObjStatus for a completed restore-object with given expiry.
|
||||
// completedRestoreObj constructs restoreObjStatus for a completed restore-object with given expiry.
|
||||
func completedRestoreObj(expiry time.Time) restoreObjStatus {
|
||||
return restoreObjStatus{
|
||||
ongoing: false,
|
||||
|
||||
+35
-17
@@ -124,6 +124,7 @@ func (sys *BucketMetadataSys) updateAndParse(ctx context.Context, bucket string,
|
||||
meta.PolicyConfigUpdatedAt = updatedAt
|
||||
case bucketNotificationConfig:
|
||||
meta.NotificationConfigXML = configData
|
||||
meta.NotificationConfigUpdatedAt = updatedAt
|
||||
case bucketLifecycleConfig:
|
||||
meta.LifecycleConfigXML = configData
|
||||
meta.LifecycleConfigUpdatedAt = updatedAt
|
||||
@@ -153,6 +154,8 @@ func (sys *BucketMetadataSys) updateAndParse(ctx context.Context, bucket string,
|
||||
if err != nil {
|
||||
return updatedAt, fmt.Errorf("Error encrypting bucket target metadata %w", err)
|
||||
}
|
||||
meta.BucketTargetsConfigUpdatedAt = updatedAt
|
||||
meta.BucketTargetsConfigMetaUpdatedAt = updatedAt
|
||||
default:
|
||||
return updatedAt, fmt.Errorf("Unknown bucket %s metadata update requested %s", bucket, configFile)
|
||||
}
|
||||
@@ -264,6 +267,21 @@ func (sys *BucketMetadataSys) GetVersioningConfig(bucket string) (*versioning.Ve
|
||||
return meta.versioningConfig, meta.VersioningConfigUpdatedAt, nil
|
||||
}
|
||||
|
||||
// GetBucketPolicy returns configured bucket policy
|
||||
func (sys *BucketMetadataSys) GetBucketPolicy(bucket string) (*policy.BucketPolicy, time.Time, error) {
|
||||
meta, _, err := sys.GetConfig(GlobalContext, bucket)
|
||||
if err != nil {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
return nil, time.Time{}, BucketPolicyNotFound{Bucket: bucket}
|
||||
}
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
if meta.policyConfig == nil {
|
||||
return nil, time.Time{}, BucketPolicyNotFound{Bucket: bucket}
|
||||
}
|
||||
return meta.policyConfig, meta.PolicyConfigUpdatedAt, nil
|
||||
}
|
||||
|
||||
// GetTaggingConfig returns configured tagging config
|
||||
// The returned object may not be modified.
|
||||
func (sys *BucketMetadataSys) GetTaggingConfig(bucket string) (*tags.Tags, time.Time, error) {
|
||||
@@ -489,7 +507,7 @@ func (sys *BucketMetadataSys) Init(ctx context.Context, buckets []string, objAPI
|
||||
}
|
||||
|
||||
// concurrently load bucket metadata to speed up loading bucket metadata.
|
||||
func (sys *BucketMetadataSys) concurrentLoad(ctx context.Context, buckets []string, failedBuckets map[string]struct{}) {
|
||||
func (sys *BucketMetadataSys) concurrentLoad(ctx context.Context, buckets []string) {
|
||||
g := errgroup.WithNErrs(len(buckets))
|
||||
bucketMetas := make([]BucketMetadata, len(buckets))
|
||||
for index := range buckets {
|
||||
@@ -530,10 +548,6 @@ func (sys *BucketMetadataSys) concurrentLoad(ctx context.Context, buckets []stri
|
||||
|
||||
for i, meta := range bucketMetas {
|
||||
if errs[i] != nil {
|
||||
if failedBuckets == nil {
|
||||
failedBuckets = make(map[string]struct{})
|
||||
}
|
||||
failedBuckets[buckets[i]] = struct{}{}
|
||||
continue
|
||||
}
|
||||
globalEventNotifier.set(buckets[i], meta) // set notification targets
|
||||
@@ -541,7 +555,7 @@ func (sys *BucketMetadataSys) concurrentLoad(ctx context.Context, buckets []stri
|
||||
}
|
||||
}
|
||||
|
||||
func (sys *BucketMetadataSys) refreshBucketsMetadataLoop(ctx context.Context, failedBuckets map[string]struct{}) {
|
||||
func (sys *BucketMetadataSys) refreshBucketsMetadataLoop(ctx context.Context) {
|
||||
const bucketMetadataRefresh = 15 * time.Minute
|
||||
|
||||
sleeper := newDynamicSleeper(2, 150*time.Millisecond, false)
|
||||
@@ -571,7 +585,10 @@ func (sys *BucketMetadataSys) refreshBucketsMetadataLoop(ctx context.Context, fa
|
||||
for i := range buckets {
|
||||
wait := sleeper.Timer(ctx)
|
||||
|
||||
meta, err := loadBucketMetadata(ctx, sys.objAPI, buckets[i].Name)
|
||||
bucket := buckets[i].Name
|
||||
updated := false
|
||||
|
||||
meta, err := loadBucketMetadata(ctx, sys.objAPI, bucket)
|
||||
if err != nil {
|
||||
internalLogIf(ctx, err, logger.WarningKind)
|
||||
wait() // wait to proceed to next entry.
|
||||
@@ -579,14 +596,16 @@ func (sys *BucketMetadataSys) refreshBucketsMetadataLoop(ctx context.Context, fa
|
||||
}
|
||||
|
||||
sys.Lock()
|
||||
sys.metadataMap[buckets[i].Name] = meta
|
||||
// Update if the bucket metadata in the memory is older than on-disk one
|
||||
if lu := sys.metadataMap[bucket].lastUpdate(); lu.Before(meta.lastUpdate()) {
|
||||
updated = true
|
||||
sys.metadataMap[bucket] = meta
|
||||
}
|
||||
sys.Unlock()
|
||||
|
||||
// Initialize the failed buckets
|
||||
if _, ok := failedBuckets[buckets[i].Name]; ok {
|
||||
globalEventNotifier.set(buckets[i].Name, meta)
|
||||
globalBucketTargetSys.set(buckets[i].Name, meta)
|
||||
delete(failedBuckets, buckets[i].Name)
|
||||
if updated {
|
||||
globalEventNotifier.set(bucket, meta)
|
||||
globalBucketTargetSys.set(bucket, meta)
|
||||
}
|
||||
|
||||
wait() // wait to proceed to next entry.
|
||||
@@ -607,13 +626,12 @@ func (sys *BucketMetadataSys) Initialized() bool {
|
||||
// Loads bucket metadata for all buckets into BucketMetadataSys.
|
||||
func (sys *BucketMetadataSys) init(ctx context.Context, buckets []string) {
|
||||
count := globalEndpoints.ESCount() * 10
|
||||
failedBuckets := make(map[string]struct{})
|
||||
for {
|
||||
if len(buckets) < count {
|
||||
sys.concurrentLoad(ctx, buckets, failedBuckets)
|
||||
sys.concurrentLoad(ctx, buckets)
|
||||
break
|
||||
}
|
||||
sys.concurrentLoad(ctx, buckets[:count], failedBuckets)
|
||||
sys.concurrentLoad(ctx, buckets[:count])
|
||||
buckets = buckets[count:]
|
||||
}
|
||||
|
||||
@@ -622,7 +640,7 @@ func (sys *BucketMetadataSys) init(ctx context.Context, buckets []string) {
|
||||
sys.Unlock()
|
||||
|
||||
if globalIsDistErasure {
|
||||
go sys.refreshBucketsMetadataLoop(ctx, failedBuckets)
|
||||
go sys.refreshBucketsMetadataLoop(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+65
-8
@@ -81,14 +81,19 @@ type BucketMetadata struct {
|
||||
ReplicationConfigXML []byte
|
||||
BucketTargetsConfigJSON []byte
|
||||
BucketTargetsConfigMetaJSON []byte
|
||||
PolicyConfigUpdatedAt time.Time
|
||||
ObjectLockConfigUpdatedAt time.Time
|
||||
EncryptionConfigUpdatedAt time.Time
|
||||
TaggingConfigUpdatedAt time.Time
|
||||
QuotaConfigUpdatedAt time.Time
|
||||
ReplicationConfigUpdatedAt time.Time
|
||||
VersioningConfigUpdatedAt time.Time
|
||||
LifecycleConfigUpdatedAt time.Time
|
||||
|
||||
PolicyConfigUpdatedAt time.Time
|
||||
ObjectLockConfigUpdatedAt time.Time
|
||||
EncryptionConfigUpdatedAt time.Time
|
||||
TaggingConfigUpdatedAt time.Time
|
||||
QuotaConfigUpdatedAt time.Time
|
||||
ReplicationConfigUpdatedAt time.Time
|
||||
VersioningConfigUpdatedAt time.Time
|
||||
LifecycleConfigUpdatedAt time.Time
|
||||
NotificationConfigUpdatedAt time.Time
|
||||
BucketTargetsConfigUpdatedAt time.Time
|
||||
BucketTargetsConfigMetaUpdatedAt time.Time
|
||||
// Add a new UpdatedAt field and update lastUpdate function
|
||||
|
||||
// Unexported fields. Must be updated atomically.
|
||||
policyConfig *policy.BucketPolicy
|
||||
@@ -120,6 +125,46 @@ func newBucketMetadata(name string) BucketMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
// Return the last update of this bucket metadata, which
|
||||
// means, the last update of any policy document.
|
||||
func (b BucketMetadata) lastUpdate() (t time.Time) {
|
||||
if b.PolicyConfigUpdatedAt.After(t) {
|
||||
t = b.PolicyConfigUpdatedAt
|
||||
}
|
||||
if b.ObjectLockConfigUpdatedAt.After(t) {
|
||||
t = b.ObjectLockConfigUpdatedAt
|
||||
}
|
||||
if b.EncryptionConfigUpdatedAt.After(t) {
|
||||
t = b.EncryptionConfigUpdatedAt
|
||||
}
|
||||
if b.TaggingConfigUpdatedAt.After(t) {
|
||||
t = b.TaggingConfigUpdatedAt
|
||||
}
|
||||
if b.QuotaConfigUpdatedAt.After(t) {
|
||||
t = b.QuotaConfigUpdatedAt
|
||||
}
|
||||
if b.ReplicationConfigUpdatedAt.After(t) {
|
||||
t = b.ReplicationConfigUpdatedAt
|
||||
}
|
||||
if b.VersioningConfigUpdatedAt.After(t) {
|
||||
t = b.VersioningConfigUpdatedAt
|
||||
}
|
||||
if b.LifecycleConfigUpdatedAt.After(t) {
|
||||
t = b.LifecycleConfigUpdatedAt
|
||||
}
|
||||
if b.NotificationConfigUpdatedAt.After(t) {
|
||||
t = b.NotificationConfigUpdatedAt
|
||||
}
|
||||
if b.BucketTargetsConfigUpdatedAt.After(t) {
|
||||
t = b.BucketTargetsConfigUpdatedAt
|
||||
}
|
||||
if b.BucketTargetsConfigMetaUpdatedAt.After(t) {
|
||||
t = b.BucketTargetsConfigMetaUpdatedAt
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Versioning returns true if versioning is enabled
|
||||
func (b BucketMetadata) Versioning() bool {
|
||||
return b.LockEnabled || (b.versioningConfig != nil && b.versioningConfig.Enabled()) || (b.objectLockConfig != nil && b.objectLockConfig.Enabled())
|
||||
@@ -440,6 +485,18 @@ func (b *BucketMetadata) defaultTimestamps() {
|
||||
if b.LifecycleConfigUpdatedAt.IsZero() {
|
||||
b.LifecycleConfigUpdatedAt = b.Created
|
||||
}
|
||||
|
||||
if b.NotificationConfigUpdatedAt.IsZero() {
|
||||
b.NotificationConfigUpdatedAt = b.Created
|
||||
}
|
||||
|
||||
if b.BucketTargetsConfigUpdatedAt.IsZero() {
|
||||
b.BucketTargetsConfigUpdatedAt = b.Created
|
||||
}
|
||||
|
||||
if b.BucketTargetsConfigMetaUpdatedAt.IsZero() {
|
||||
b.BucketTargetsConfigMetaUpdatedAt = b.Created
|
||||
}
|
||||
}
|
||||
|
||||
// Save config to supplied ObjectLayer api.
|
||||
|
||||
@@ -156,6 +156,24 @@ func (z *BucketMetadata) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "LifecycleConfigUpdatedAt")
|
||||
return
|
||||
}
|
||||
case "NotificationConfigUpdatedAt":
|
||||
z.NotificationConfigUpdatedAt, err = dc.ReadTime()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "NotificationConfigUpdatedAt")
|
||||
return
|
||||
}
|
||||
case "BucketTargetsConfigUpdatedAt":
|
||||
z.BucketTargetsConfigUpdatedAt, err = dc.ReadTime()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BucketTargetsConfigUpdatedAt")
|
||||
return
|
||||
}
|
||||
case "BucketTargetsConfigMetaUpdatedAt":
|
||||
z.BucketTargetsConfigMetaUpdatedAt, err = dc.ReadTime()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BucketTargetsConfigMetaUpdatedAt")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
@@ -169,9 +187,9 @@ func (z *BucketMetadata) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *BucketMetadata) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 22
|
||||
// map header, size 25
|
||||
// write "Name"
|
||||
err = en.Append(0xde, 0x0, 0x16, 0xa4, 0x4e, 0x61, 0x6d, 0x65)
|
||||
err = en.Append(0xde, 0x0, 0x19, 0xa4, 0x4e, 0x61, 0x6d, 0x65)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -390,15 +408,45 @@ func (z *BucketMetadata) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = msgp.WrapError(err, "LifecycleConfigUpdatedAt")
|
||||
return
|
||||
}
|
||||
// write "NotificationConfigUpdatedAt"
|
||||
err = en.Append(0xbb, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteTime(z.NotificationConfigUpdatedAt)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "NotificationConfigUpdatedAt")
|
||||
return
|
||||
}
|
||||
// write "BucketTargetsConfigUpdatedAt"
|
||||
err = en.Append(0xbc, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteTime(z.BucketTargetsConfigUpdatedAt)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BucketTargetsConfigUpdatedAt")
|
||||
return
|
||||
}
|
||||
// write "BucketTargetsConfigMetaUpdatedAt"
|
||||
err = en.Append(0xd9, 0x20, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x65, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteTime(z.BucketTargetsConfigMetaUpdatedAt)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BucketTargetsConfigMetaUpdatedAt")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *BucketMetadata) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 22
|
||||
// map header, size 25
|
||||
// string "Name"
|
||||
o = append(o, 0xde, 0x0, 0x16, 0xa4, 0x4e, 0x61, 0x6d, 0x65)
|
||||
o = append(o, 0xde, 0x0, 0x19, 0xa4, 0x4e, 0x61, 0x6d, 0x65)
|
||||
o = msgp.AppendString(o, z.Name)
|
||||
// string "Created"
|
||||
o = append(o, 0xa7, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64)
|
||||
@@ -463,6 +511,15 @@ func (z *BucketMetadata) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
// string "LifecycleConfigUpdatedAt"
|
||||
o = append(o, 0xb8, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74)
|
||||
o = msgp.AppendTime(o, z.LifecycleConfigUpdatedAt)
|
||||
// string "NotificationConfigUpdatedAt"
|
||||
o = append(o, 0xbb, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74)
|
||||
o = msgp.AppendTime(o, z.NotificationConfigUpdatedAt)
|
||||
// string "BucketTargetsConfigUpdatedAt"
|
||||
o = append(o, 0xbc, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74)
|
||||
o = msgp.AppendTime(o, z.BucketTargetsConfigUpdatedAt)
|
||||
// string "BucketTargetsConfigMetaUpdatedAt"
|
||||
o = append(o, 0xd9, 0x20, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x65, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74)
|
||||
o = msgp.AppendTime(o, z.BucketTargetsConfigMetaUpdatedAt)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -616,6 +673,24 @@ func (z *BucketMetadata) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "LifecycleConfigUpdatedAt")
|
||||
return
|
||||
}
|
||||
case "NotificationConfigUpdatedAt":
|
||||
z.NotificationConfigUpdatedAt, bts, err = msgp.ReadTimeBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "NotificationConfigUpdatedAt")
|
||||
return
|
||||
}
|
||||
case "BucketTargetsConfigUpdatedAt":
|
||||
z.BucketTargetsConfigUpdatedAt, bts, err = msgp.ReadTimeBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BucketTargetsConfigUpdatedAt")
|
||||
return
|
||||
}
|
||||
case "BucketTargetsConfigMetaUpdatedAt":
|
||||
z.BucketTargetsConfigMetaUpdatedAt, bts, err = msgp.ReadTimeBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BucketTargetsConfigMetaUpdatedAt")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
@@ -630,6 +705,6 @@ func (z *BucketMetadata) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *BucketMetadata) Msgsize() (s int) {
|
||||
s = 3 + 5 + msgp.StringPrefixSize + len(z.Name) + 8 + msgp.TimeSize + 12 + msgp.BoolSize + 17 + msgp.BytesPrefixSize + len(z.PolicyConfigJSON) + 22 + msgp.BytesPrefixSize + len(z.NotificationConfigXML) + 19 + msgp.BytesPrefixSize + len(z.LifecycleConfigXML) + 20 + msgp.BytesPrefixSize + len(z.ObjectLockConfigXML) + 20 + msgp.BytesPrefixSize + len(z.VersioningConfigXML) + 20 + msgp.BytesPrefixSize + len(z.EncryptionConfigXML) + 17 + msgp.BytesPrefixSize + len(z.TaggingConfigXML) + 16 + msgp.BytesPrefixSize + len(z.QuotaConfigJSON) + 21 + msgp.BytesPrefixSize + len(z.ReplicationConfigXML) + 24 + msgp.BytesPrefixSize + len(z.BucketTargetsConfigJSON) + 28 + msgp.BytesPrefixSize + len(z.BucketTargetsConfigMetaJSON) + 22 + msgp.TimeSize + 26 + msgp.TimeSize + 26 + msgp.TimeSize + 23 + msgp.TimeSize + 21 + msgp.TimeSize + 27 + msgp.TimeSize + 26 + msgp.TimeSize + 25 + msgp.TimeSize
|
||||
s = 3 + 5 + msgp.StringPrefixSize + len(z.Name) + 8 + msgp.TimeSize + 12 + msgp.BoolSize + 17 + msgp.BytesPrefixSize + len(z.PolicyConfigJSON) + 22 + msgp.BytesPrefixSize + len(z.NotificationConfigXML) + 19 + msgp.BytesPrefixSize + len(z.LifecycleConfigXML) + 20 + msgp.BytesPrefixSize + len(z.ObjectLockConfigXML) + 20 + msgp.BytesPrefixSize + len(z.VersioningConfigXML) + 20 + msgp.BytesPrefixSize + len(z.EncryptionConfigXML) + 17 + msgp.BytesPrefixSize + len(z.TaggingConfigXML) + 16 + msgp.BytesPrefixSize + len(z.QuotaConfigJSON) + 21 + msgp.BytesPrefixSize + len(z.ReplicationConfigXML) + 24 + msgp.BytesPrefixSize + len(z.BucketTargetsConfigJSON) + 28 + msgp.BytesPrefixSize + len(z.BucketTargetsConfigMetaJSON) + 22 + msgp.TimeSize + 26 + msgp.TimeSize + 26 + msgp.TimeSize + 23 + msgp.TimeSize + 21 + msgp.TimeSize + 27 + msgp.TimeSize + 26 + msgp.TimeSize + 25 + msgp.TimeSize + 28 + msgp.TimeSize + 29 + msgp.TimeSize + 34 + msgp.TimeSize
|
||||
return
|
||||
}
|
||||
|
||||
+35
-50
@@ -601,28 +601,44 @@ func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, seqNumber uint3
|
||||
partEncRelOffset := int64(seqNumber) * (SSEDAREPackageBlockSize + SSEDAREPackageMetaSize)
|
||||
|
||||
w := &DecryptBlocksReader{
|
||||
reader: inputReader,
|
||||
startSeqNum: seqNumber,
|
||||
partDecRelOffset: partDecRelOffset,
|
||||
partEncRelOffset: partEncRelOffset,
|
||||
parts: oi.Parts,
|
||||
partIndex: partStart,
|
||||
header: h,
|
||||
bucket: bucket,
|
||||
object: object,
|
||||
customerKeyHeader: h.Get(xhttp.AmzServerSideEncryptionCustomerKey),
|
||||
copySource: copySource,
|
||||
metadata: cloneMSS(oi.UserDefined),
|
||||
reader: inputReader,
|
||||
startSeqNum: seqNumber,
|
||||
partDecRelOffset: partDecRelOffset,
|
||||
partEncRelOffset: partEncRelOffset,
|
||||
parts: oi.Parts,
|
||||
partIndex: partStart,
|
||||
}
|
||||
|
||||
if w.copySource {
|
||||
w.customerKeyHeader = h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey)
|
||||
// In case of SSE-C, we have to decrypt the OEK using the client-provided key.
|
||||
// In case of a SSE-C server-side copy, the client might provide two keys,
|
||||
// one for the source and one for the target. This reader is the source.
|
||||
var ssecClientKey []byte
|
||||
if crypto.SSEC.IsEncrypted(oi.UserDefined) {
|
||||
if copySource && crypto.SSECopy.IsRequested(h) {
|
||||
key, err := crypto.SSECopy.ParseHTTP(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ssecClientKey = key[:]
|
||||
} else {
|
||||
key, err := crypto.SSEC.ParseHTTP(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ssecClientKey = key[:]
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt the OEK once and reuse it for all subsequent parts.
|
||||
objectEncryptionKey, err := decryptObjectMeta(ssecClientKey, bucket, object, oi.UserDefined)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w.objectEncryptionKey = objectEncryptionKey
|
||||
|
||||
if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
@@ -638,48 +654,17 @@ type DecryptBlocksReader struct {
|
||||
// Current part index
|
||||
partIndex int
|
||||
// Parts information
|
||||
parts []ObjectPartInfo
|
||||
header http.Header
|
||||
bucket, object string
|
||||
metadata map[string]string
|
||||
parts []ObjectPartInfo
|
||||
|
||||
objectEncryptionKey []byte
|
||||
partDecRelOffset, partEncRelOffset int64
|
||||
|
||||
copySource bool
|
||||
// Customer Key
|
||||
customerKeyHeader string
|
||||
}
|
||||
|
||||
func (d *DecryptBlocksReader) buildDecrypter(partID int) error {
|
||||
m := cloneMSS(d.metadata)
|
||||
// Initialize the first decrypter; new decrypters will be
|
||||
// initialized in Read() operation as needed.
|
||||
var key []byte
|
||||
var err error
|
||||
if d.copySource {
|
||||
if crypto.SSEC.IsEncrypted(d.metadata) {
|
||||
d.header.Set(xhttp.AmzServerSideEncryptionCopyCustomerKey, d.customerKeyHeader)
|
||||
key, err = ParseSSECopyCustomerRequest(d.header, d.metadata)
|
||||
}
|
||||
} else {
|
||||
if crypto.SSEC.IsEncrypted(d.metadata) {
|
||||
d.header.Set(xhttp.AmzServerSideEncryptionCustomerKey, d.customerKeyHeader)
|
||||
key, err = ParseSSECustomerHeader(d.header)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objectEncryptionKey, err := decryptObjectMeta(key, d.bucket, d.object, m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var partIDbin [4]byte
|
||||
binary.LittleEndian.PutUint32(partIDbin[:], uint32(partID)) // marshal part ID
|
||||
|
||||
mac := hmac.New(sha256.New, objectEncryptionKey) // derive part encryption key from part ID and object key
|
||||
mac := hmac.New(sha256.New, d.objectEncryptionKey) // derive part encryption key from part ID and object key
|
||||
mac.Write(partIDbin[:])
|
||||
partEncryptionKey := mac.Sum(nil)
|
||||
|
||||
@@ -1104,7 +1089,7 @@ func (o *ObjectInfo) metadataDecrypter(h http.Header) objectMetaDecryptFn {
|
||||
}
|
||||
}
|
||||
|
||||
// decryptChecksums will attempt to decode checksums and return it/them if set.
|
||||
// decryptPartsChecksums will attempt to decode checksums and return it/them if set.
|
||||
// if part > 0, and we have the checksum for the part that will be returned.
|
||||
func (o *ObjectInfo) decryptPartsChecksums(h http.Header) {
|
||||
data := o.Checksum
|
||||
|
||||
@@ -48,14 +48,14 @@ func newParallelReader(readers []io.ReaderAt, e Erasure, offset, totalLength int
|
||||
r2b[i] = i
|
||||
}
|
||||
bufs := make([][]byte, len(readers))
|
||||
// Fill buffers
|
||||
b := globalBytePoolCap.Load().Get()
|
||||
shardSize := int(e.ShardSize())
|
||||
if cap(b) < len(readers)*shardSize {
|
||||
// We should always have enough capacity, but older objects may be bigger.
|
||||
globalBytePoolCap.Load().Put(b)
|
||||
b = nil
|
||||
} else {
|
||||
var b []byte
|
||||
|
||||
// We should always have enough capacity, but older objects may be bigger
|
||||
// we do not need stashbuffer for them.
|
||||
if globalBytePoolCap.Load().WidthCap() >= len(readers)*shardSize {
|
||||
// Fill buffers
|
||||
b = globalBytePoolCap.Load().Get()
|
||||
// Seed the buffers.
|
||||
for i := range bufs {
|
||||
bufs[i] = b[i*shardSize : (i+1)*shardSize]
|
||||
|
||||
@@ -110,7 +110,7 @@ func (er erasureObjects) checkUploadIDExists(ctx context.Context, bucket, object
|
||||
return fi, partsMetadata, err
|
||||
}
|
||||
|
||||
// cleanMultipartPath removes all extraneous files and parts from the multipart folder, this is used per CompleteMultipart.
|
||||
// cleanupMultipartPath removes all extraneous files and parts from the multipart folder, this is used per CompleteMultipart.
|
||||
// do not use this function outside of completeMultipartUpload()
|
||||
func (er erasureObjects) cleanupMultipartPath(ctx context.Context, paths ...string) {
|
||||
storageDisks := er.getDisks()
|
||||
|
||||
@@ -1980,6 +1980,34 @@ func (z *erasureServerPools) DeleteBucket(ctx context.Context, bucket string, op
|
||||
defer lk.Unlock(lkctx)
|
||||
}
|
||||
|
||||
if !opts.Force {
|
||||
results := make(chan itemOrErr[ObjectInfo])
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Minute)
|
||||
defer cancel()
|
||||
err := z.Walk(ctx, bucket, "", results, WalkOptions{Limit: 1})
|
||||
if err != nil {
|
||||
s3LogIf(ctx, fmt.Errorf("unable to verify if the bucket %s is empty: %w", bucket, err))
|
||||
return toObjectErr(err, bucket)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case r, found := <-results:
|
||||
if found {
|
||||
if r.Err != nil {
|
||||
s3LogIf(ctx, fmt.Errorf("unable to verify if the bucket %s is empty: %w", bucket, r.Err))
|
||||
return toObjectErr(r.Err, bucket)
|
||||
}
|
||||
return toObjectErr(errVolumeNotEmpty, bucket)
|
||||
}
|
||||
}
|
||||
|
||||
// Always pass force to the lower level
|
||||
opts.Force = true
|
||||
}
|
||||
|
||||
err := z.s3Peer.DeleteBucket(ctx, bucket, opts)
|
||||
if err == nil || isErrBucketNotFound(err) {
|
||||
// If site replication is configured, hold on to deleted bucket state until sites sync
|
||||
@@ -2198,6 +2226,7 @@ func (z *erasureServerPools) Walk(ctx context.Context, bucket, prefix string, re
|
||||
filterPrefix: filterPrefix,
|
||||
recursive: true,
|
||||
forwardTo: opts.Marker,
|
||||
perDiskLimit: opts.Limit,
|
||||
minDisks: listingQuorum,
|
||||
reportNotFound: false,
|
||||
agreed: send,
|
||||
|
||||
+5
-2
@@ -230,8 +230,11 @@ func (s *erasureSets) connectDisks(log bool) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if disk.IsLocal() && disk.Healing() != nil {
|
||||
globalBackgroundHealState.pushHealLocalDisks(disk.Endpoint())
|
||||
if disk.IsLocal() {
|
||||
h := disk.Healing()
|
||||
if h != nil && !h.Finished {
|
||||
globalBackgroundHealState.pushHealLocalDisks(disk.Endpoint())
|
||||
}
|
||||
}
|
||||
s.erasureDisksMu.Lock()
|
||||
setIndex, diskIndex, err := findDiskIndex(s.format, format)
|
||||
|
||||
+3
-5
@@ -203,11 +203,9 @@ func getDisksInfo(disks []StorageAPI, endpoints []Endpoint, metrics bool) (disks
|
||||
di.State = diskErrToDriveState(err)
|
||||
di.FreeInodes = info.FreeInodes
|
||||
di.UsedInodes = info.UsedInodes
|
||||
if info.Healing {
|
||||
if hi := disks[index].Healing(); hi != nil {
|
||||
hd := hi.toHealingDisk()
|
||||
di.HealInfo = &hd
|
||||
}
|
||||
if hi := disks[index].Healing(); hi != nil {
|
||||
hd := hi.toHealingDisk()
|
||||
di.HealInfo = &hd
|
||||
}
|
||||
di.Metrics = &madmin.DiskMetrics{
|
||||
LastMinute: make(map[string]madmin.TimedAction, len(info.Metrics.LastMinute)),
|
||||
|
||||
+25
-12
@@ -167,6 +167,19 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
return errServerNotInitialized
|
||||
}
|
||||
|
||||
started := tracker.Started
|
||||
if started.IsZero() || started.Equal(timeSentinel) {
|
||||
healingLogIf(ctx, fmt.Errorf("unexpected tracker healing start time found: %v", started))
|
||||
started = time.Time{}
|
||||
}
|
||||
|
||||
// Final tracer update before quitting
|
||||
defer func() {
|
||||
tracker.setObject("")
|
||||
tracker.setBucket("")
|
||||
healingLogIf(ctx, tracker.update(ctx))
|
||||
}()
|
||||
|
||||
for _, bucket := range healBuckets {
|
||||
if err := bgSeq.healBucket(objAPI, bucket, true); err != nil {
|
||||
// Log bucket healing error if any, we shall retry again.
|
||||
@@ -227,7 +240,11 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
|
||||
// Collect updates to tracker from concurrent healEntry calls
|
||||
results := make(chan healEntryResult, 1000)
|
||||
defer close(results)
|
||||
quitting := make(chan struct{})
|
||||
defer func() {
|
||||
close(results)
|
||||
<-quitting
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for res := range results {
|
||||
@@ -241,6 +258,9 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
|
||||
tracker.updateProgress(res.success, res.skipped, res.bytes)
|
||||
}
|
||||
|
||||
healingLogIf(ctx, tracker.update(ctx))
|
||||
close(quitting)
|
||||
}()
|
||||
|
||||
var retErr error
|
||||
@@ -428,13 +448,10 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
|
||||
var versionNotFound int
|
||||
for _, version := range fivs.Versions {
|
||||
// Ignore a version with a modtime newer than healing start time.
|
||||
if version.ModTime.After(tracker.Started) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Apply lifecycle rules on the objects that are expired.
|
||||
if filterLifecycle(bucket, version.Name, version) {
|
||||
// Ignore healing a version if:
|
||||
// - It is uploaded after the drive healing is started
|
||||
// - An object that is already expired by ILM rule.
|
||||
if !started.IsZero() && version.ModTime.After(started) || filterLifecycle(bucket, version.Name, version) {
|
||||
versionNotFound++
|
||||
if !send(healEntrySkipped(uint64(version.Size))) {
|
||||
return
|
||||
@@ -549,10 +566,6 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
healingLogIf(ctx, tracker.update(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
tracker.setObject("")
|
||||
tracker.setBucket("")
|
||||
|
||||
if retErr != nil {
|
||||
return retErr
|
||||
}
|
||||
|
||||
+114
-104
@@ -23,6 +23,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -367,7 +368,7 @@ func (c *iamCache) removeGroupFromMembershipsMap(group string) {
|
||||
// information in IAM (i.e sys.iam*Map) - this info is stored only in the STS
|
||||
// generated credentials. Thus we skip looking up group memberships, user map,
|
||||
// and group map and check the appropriate policy maps directly.
|
||||
func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([]string, time.Time, error) {
|
||||
func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool, policyPresent bool) ([]string, time.Time, error) {
|
||||
if isGroup {
|
||||
if store.getUsersSysType() == MinIOUsersSysType {
|
||||
g, ok := c.iamGroupsMap[name]
|
||||
@@ -392,105 +393,107 @@ func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([
|
||||
if ok {
|
||||
return policy.toSlice(), policy.UpdatedAt, nil
|
||||
}
|
||||
if err := store.loadMappedPolicyWithRetry(context.TODO(), name, regUser, true, c.iamGroupPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
policy, _ = c.iamGroupPolicyMap.Load(name)
|
||||
return policy.toSlice(), policy.UpdatedAt, nil
|
||||
}
|
||||
|
||||
// When looking for a user's policies, we also check if the user
|
||||
// and the groups they are member of are enabled.
|
||||
u, ok := c.iamUsersMap[name]
|
||||
if ok {
|
||||
if !u.Credentials.IsValid() {
|
||||
return nil, time.Time{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// For internal IDP regular/service account user accounts, the policy
|
||||
// mapping is iamUserPolicyMap. For STS accounts, the parent user would be
|
||||
// passed here and we lookup the mapping in iamSTSPolicyMap.
|
||||
mp, ok := c.iamUserPolicyMap.Load(name)
|
||||
if !ok {
|
||||
if err := store.loadMappedPolicyWithRetry(context.TODO(), name, regUser, false, c.iamUserPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
|
||||
mp, ok = c.iamUserPolicyMap.Load(name)
|
||||
if !ok {
|
||||
// Since user "name" could be a parent user of an STS account, we look up
|
||||
// mappings for those too.
|
||||
mp, ok = c.iamSTSPolicyMap.Load(name)
|
||||
if !ok {
|
||||
// Attempt to load parent user mapping for STS accounts
|
||||
if err := store.loadMappedPolicyWithRetry(context.TODO(), name, stsUser, false, c.iamSTSPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
mp, _ = c.iamSTSPolicyMap.Load(name)
|
||||
if !policyPresent {
|
||||
if err := store.loadMappedPolicy(context.TODO(), name, regUser, true, c.iamGroupPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
policy, _ = c.iamGroupPolicyMap.Load(name)
|
||||
return policy.toSlice(), policy.UpdatedAt, nil
|
||||
}
|
||||
return nil, time.Time{}, nil
|
||||
}
|
||||
|
||||
// returned policy could be empty, we use set to de-duplicate.
|
||||
policies := set.CreateStringSet(mp.toSlice()...)
|
||||
var policies set.StringSet
|
||||
var updatedAt time.Time
|
||||
|
||||
for _, group := range u.Credentials.Groups {
|
||||
if store.getUsersSysType() == MinIOUsersSysType {
|
||||
g, ok := c.iamGroupsMap[group]
|
||||
if !ok {
|
||||
if err := store.loadGroup(context.Background(), group, c.iamGroupsMap); err != nil {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
g, ok = c.iamGroupsMap[group]
|
||||
if !ok {
|
||||
return nil, time.Time{}, errNoSuchGroup
|
||||
}
|
||||
if store.getUsersSysType() == LDAPUsersSysType {
|
||||
// For LDAP policy mapping is part of STS users, we only need to lookup
|
||||
// those mappings.
|
||||
mp, ok := c.iamSTSPolicyMap.Load(name)
|
||||
if !ok {
|
||||
// Attempt to load parent user mapping for STS accounts
|
||||
if err := store.loadMappedPolicy(context.TODO(), name, stsUser, false, c.iamSTSPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
|
||||
// Group is disabled, so we return no policy - this
|
||||
// ensures the request is denied.
|
||||
if g.Status == statusDisabled {
|
||||
mp, _ = c.iamSTSPolicyMap.Load(name)
|
||||
}
|
||||
policies = set.CreateStringSet(mp.toSlice()...)
|
||||
updatedAt = mp.UpdatedAt
|
||||
} else {
|
||||
// When looking for a user's policies, we also check if the user
|
||||
// and the groups they are member of are enabled.
|
||||
u, ok := c.iamUsersMap[name]
|
||||
if ok {
|
||||
if !u.Credentials.IsValid() {
|
||||
return nil, time.Time{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
policy, ok := c.iamGroupPolicyMap.Load(group)
|
||||
// For internal IDP regular/service account user accounts, the policy
|
||||
// mapping is iamUserPolicyMap. For STS accounts, the parent user would be
|
||||
// passed here and we lookup the mapping in iamSTSPolicyMap.
|
||||
mp, ok := c.iamUserPolicyMap.Load(name)
|
||||
if !ok {
|
||||
if err := store.loadMappedPolicyWithRetry(context.TODO(), group, regUser, true, c.iamGroupPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
if err := store.loadMappedPolicy(context.TODO(), name, regUser, false, c.iamUserPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
policy, _ = c.iamGroupPolicyMap.Load(group)
|
||||
mp, ok = c.iamUserPolicyMap.Load(name)
|
||||
if !ok {
|
||||
// Since user "name" could be a parent user of an STS account, we look up
|
||||
// mappings for those too.
|
||||
mp, ok = c.iamSTSPolicyMap.Load(name)
|
||||
if !ok {
|
||||
// Attempt to load parent user mapping for STS accounts
|
||||
if err := store.loadMappedPolicy(context.TODO(), name, stsUser, false, c.iamSTSPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
mp, _ = c.iamSTSPolicyMap.Load(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
policies = set.CreateStringSet(mp.toSlice()...)
|
||||
|
||||
for _, p := range policy.toSlice() {
|
||||
policies.Add(p)
|
||||
for _, group := range u.Credentials.Groups {
|
||||
g, ok := c.iamGroupsMap[group]
|
||||
if ok {
|
||||
// Group is disabled, so we return no policy - this
|
||||
// ensures the request is denied.
|
||||
if g.Status == statusDisabled {
|
||||
return nil, time.Time{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
policy, ok := c.iamGroupPolicyMap.Load(group)
|
||||
if !ok {
|
||||
if err := store.loadMappedPolicy(context.TODO(), group, regUser, true, c.iamGroupPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
policy, _ = c.iamGroupPolicyMap.Load(group)
|
||||
}
|
||||
|
||||
for _, p := range policy.toSlice() {
|
||||
policies.Add(p)
|
||||
}
|
||||
}
|
||||
updatedAt = mp.UpdatedAt
|
||||
}
|
||||
|
||||
for _, group := range c.iamUserGroupMemberships[name].ToSlice() {
|
||||
if store.getUsersSysType() == MinIOUsersSysType {
|
||||
g, ok := c.iamGroupsMap[group]
|
||||
if !ok {
|
||||
if err := store.loadGroup(context.Background(), group, c.iamGroupsMap); err != nil {
|
||||
return nil, time.Time{}, err
|
||||
if ok {
|
||||
// Group is disabled, so we return no policy - this
|
||||
// ensures the request is denied.
|
||||
if g.Status == statusDisabled {
|
||||
return nil, time.Time{}, nil
|
||||
}
|
||||
g, ok = c.iamGroupsMap[group]
|
||||
if !ok {
|
||||
return nil, time.Time{}, errNoSuchGroup
|
||||
}
|
||||
}
|
||||
|
||||
// Group is disabled, so we return no policy - this
|
||||
// ensures the request is denied.
|
||||
if g.Status == statusDisabled {
|
||||
return nil, time.Time{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
policy, ok := c.iamGroupPolicyMap.Load(group)
|
||||
if !ok {
|
||||
if err := store.loadMappedPolicyWithRetry(context.TODO(), group, regUser, true, c.iamGroupPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
if err := store.loadMappedPolicy(context.TODO(), group, regUser, true, c.iamGroupPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
policy, _ = c.iamGroupPolicyMap.Load(group)
|
||||
@@ -501,7 +504,7 @@ func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([
|
||||
}
|
||||
}
|
||||
|
||||
return policies.ToSlice(), mp.UpdatedAt, nil
|
||||
return policies.ToSlice(), updatedAt, nil
|
||||
}
|
||||
|
||||
func (c *iamCache) updateUserWithClaims(key string, u UserIdentity) error {
|
||||
@@ -754,13 +757,14 @@ func (store *IAMStoreSys) PolicyDBGet(name string, groups ...string) ([]string,
|
||||
cache := store.rlock()
|
||||
defer store.runlock()
|
||||
|
||||
policies, _, err := cache.policyDBGet(store, name, false)
|
||||
policies, _, err := cache.policyDBGet(store, name, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userPolicyPresent := len(policies) > 0
|
||||
for _, group := range groups {
|
||||
ps, _, err := cache.policyDBGet(store, group, true)
|
||||
ps, _, err := cache.policyDBGet(store, group, true, userPolicyPresent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -945,7 +949,7 @@ func (store *IAMStoreSys) GetGroupDescription(group string) (gd madmin.GroupDesc
|
||||
cache := store.rlock()
|
||||
defer store.runlock()
|
||||
|
||||
ps, updatedAt, err := cache.policyDBGet(store, group, true)
|
||||
ps, updatedAt, err := cache.policyDBGet(store, group, true, false)
|
||||
if err != nil {
|
||||
return gd, err
|
||||
}
|
||||
@@ -974,34 +978,46 @@ func (store *IAMStoreSys) GetGroupDescription(group string) (gd madmin.GroupDesc
|
||||
}, nil
|
||||
}
|
||||
|
||||
// updateGroups updates the group from the persistent store, and also related policy mapping if any.
|
||||
func (store *IAMStoreSys) updateGroups(ctx context.Context, cache *iamCache) (res []string, err error) {
|
||||
if store.getUsersSysType() == MinIOUsersSysType {
|
||||
m := map[string]GroupInfo{}
|
||||
err = store.loadGroups(ctx, m)
|
||||
if iamOS, ok := store.IAMStorageAPI.(*IAMObjectStore); ok {
|
||||
listedConfigItems, err := iamOS.listAllIAMConfigItems(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
cache.iamGroupsMap = m
|
||||
cache.updatedAt = time.Now()
|
||||
for k := range cache.iamGroupsMap {
|
||||
res = append(res, k)
|
||||
if store.getUsersSysType() == MinIOUsersSysType {
|
||||
groupsList := listedConfigItems[groupsListKey]
|
||||
for _, item := range groupsList {
|
||||
group := path.Dir(item)
|
||||
if err = iamOS.loadGroup(ctx, group, cache.iamGroupsMap); err != nil && !errors.Is(err, errNoSuchGroup) {
|
||||
return nil, fmt.Errorf("unable to load the group: %w", err)
|
||||
}
|
||||
res = append(res, group)
|
||||
}
|
||||
}
|
||||
|
||||
groupPolicyMappingsList := listedConfigItems[policyDBGroupsListKey]
|
||||
for _, item := range groupPolicyMappingsList {
|
||||
group := strings.TrimSuffix(item, ".json")
|
||||
if err = iamOS.loadMappedPolicy(ctx, group, regUser, true, cache.iamGroupPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
|
||||
return nil, fmt.Errorf("unable to load the policy mapping for the group: %w", err)
|
||||
}
|
||||
res = append(res, group)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
if store.getUsersSysType() == LDAPUsersSysType {
|
||||
m := xsync.NewMapOf[string, MappedPolicy]()
|
||||
err = store.loadMappedPolicies(ctx, stsUser, true, m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cache.iamGroupPolicyMap = m
|
||||
cache.updatedAt = time.Now()
|
||||
cache.iamGroupPolicyMap.Range(func(k string, v MappedPolicy) bool {
|
||||
res = append(res, k)
|
||||
return true
|
||||
})
|
||||
// For etcd just return from cache.
|
||||
for k := range cache.iamGroupsMap {
|
||||
res = append(res, k)
|
||||
}
|
||||
|
||||
cache.iamGroupPolicyMap.Range(func(k string, v MappedPolicy) bool {
|
||||
res = append(res, k)
|
||||
return true
|
||||
})
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -2800,14 +2816,8 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) error
|
||||
}
|
||||
|
||||
if groupLoad {
|
||||
load := len(newCache.iamGroupsMap) == 0
|
||||
if store.getUsersSysType() == LDAPUsersSysType && newCache.iamGroupPolicyMap.Size() == 0 {
|
||||
load = true
|
||||
}
|
||||
if load {
|
||||
// NOTE: we are not worried about loading errors from groups.
|
||||
store.updateGroups(ctx, newCache)
|
||||
}
|
||||
// NOTE: we are not worried about loading errors from groups.
|
||||
store.updateGroups(ctx, newCache)
|
||||
newCache.buildUserGroupMemberships()
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -417,7 +417,7 @@ func (sys *IAMSys) periodicRoutines(ctx context.Context, baseInterval time.Durat
|
||||
took := time.Since(refreshStart).Seconds()
|
||||
if took > maxDurationSecondsForLog {
|
||||
// Log if we took a lot of time to load.
|
||||
logger.Info("IAM refresh took (duration: %s)", took)
|
||||
logger.Info("IAM refresh took (duration: %.2fs)", took)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-14
@@ -22,7 +22,6 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/minio/internal/kms"
|
||||
@@ -197,7 +196,7 @@ func (a kmsAPIHandlers) KMSListKeysHandler(w http.ResponseWriter, r *http.Reques
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
|
||||
return
|
||||
}
|
||||
allKeyNames, _, err := GlobalKMS.ListKeyNames(ctx, &kms.ListRequest{
|
||||
allKeys, _, err := GlobalKMS.ListKeys(ctx, &kms.ListRequest{
|
||||
Prefix: r.Form.Get("pattern"),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -213,21 +212,17 @@ func (a kmsAPIHandlers) KMSListKeysHandler(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
// Now we have all the key names, for each of them, check whether the policy grants permission for
|
||||
// the user to list it.
|
||||
keyNames := []string{}
|
||||
for _, name := range allKeyNames {
|
||||
if checkKMSActionAllowed(r, owner, cred, policy.KMSListKeysAction, name) {
|
||||
keyNames = append(keyNames, name)
|
||||
// the user to list it. Filter in place to leave only allowed keys.
|
||||
n := 0
|
||||
for _, k := range allKeys {
|
||||
if checkKMSActionAllowed(r, owner, cred, policy.KMSListKeysAction, k.Name) {
|
||||
allKeys[n] = k
|
||||
n++
|
||||
}
|
||||
}
|
||||
allKeys = allKeys[:n]
|
||||
|
||||
values := make([]kes.KeyInfo, 0, len(keyNames))
|
||||
for _, name := range keyNames {
|
||||
values = append(values, kes.KeyInfo{
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
if res, err := json.Marshal(values); err != nil {
|
||||
if res, err := json.Marshal(allKeys); err != nil {
|
||||
writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL)
|
||||
} else {
|
||||
writeSuccessResponseJSON(w, res)
|
||||
|
||||
+17
-20
@@ -658,13 +658,24 @@ func execKMSTest(t *testing.T, test kmsTestCase, adminTestBed *adminErasureTestB
|
||||
|
||||
// Check returned key list is correct
|
||||
if test.wantKeyNames != nil {
|
||||
gotKeyNames := keyNamesFromListKeysResp(t, rec.Body.Bytes())
|
||||
if len(test.wantKeyNames) != len(gotKeyNames) {
|
||||
t.Fatalf("want keys len: %d, got len: %d", len(test.wantKeyNames), len(gotKeyNames))
|
||||
keys := []madmin.KMSKeyInfo{}
|
||||
err := json.Unmarshal(rec.Body.Bytes(), &keys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i, wantKeyName := range test.wantKeyNames {
|
||||
if gotKeyNames[i] != wantKeyName {
|
||||
t.Fatalf("want key name %s, in position %d, got %s", wantKeyName, i, gotKeyNames[i])
|
||||
if len(keys) != len(test.wantKeyNames) {
|
||||
t.Fatalf("want %d keys, got %d", len(test.wantKeyNames), len(keys))
|
||||
}
|
||||
|
||||
for i, want := range keys {
|
||||
if want.CreatedBy != kms.StubCreatedBy {
|
||||
t.Fatalf("want key created by %s, got %s", kms.StubCreatedBy, want.CreatedBy)
|
||||
}
|
||||
if want.CreatedAt != kms.StubCreatedAt {
|
||||
t.Fatalf("want key created at %s, got %s", kms.StubCreatedAt, want.CreatedAt)
|
||||
}
|
||||
if test.wantKeyNames[i] != want.Name {
|
||||
t.Fatalf("want key name %s, got %s", test.wantKeyNames[i], want.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -835,17 +846,3 @@ func setupKMSUser(t *testing.T, accessKey, secretKey, p string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func keyNamesFromListKeysResp(t *testing.T, b []byte) []string {
|
||||
var keyInfos []madmin.KMSKeyInfo
|
||||
err := json.Unmarshal(b, &keyInfos)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot unmarshal '%s', err: %v", b, err)
|
||||
}
|
||||
|
||||
var gotKeyNames []string
|
||||
for _, keyInfo := range keyInfos {
|
||||
gotKeyNames = append(gotKeyNames, keyInfo.Name)
|
||||
}
|
||||
return gotKeyNames
|
||||
}
|
||||
|
||||
@@ -93,14 +93,18 @@ func testDeleteObject(obj ObjectLayer, instanceType string, t TestErrHandler) {
|
||||
md5Bytes := md5.Sum([]byte(object.content))
|
||||
oi, err := obj.PutObject(context.Background(), testCase.bucketName, object.name, mustGetPutObjReader(t, strings.NewReader(object.content),
|
||||
int64(len(object.content)), hex.EncodeToString(md5Bytes[:]), ""), ObjectOptions{})
|
||||
t.Log(oi)
|
||||
if err != nil {
|
||||
t.Log(oi)
|
||||
t.Fatalf("%s : %s", instanceType, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
oi, err := obj.DeleteObject(context.Background(), testCase.bucketName, testCase.pathToDelete, ObjectOptions{})
|
||||
t.Log(oi, err)
|
||||
if err != nil && !isErrObjectNotFound(err) {
|
||||
t.Log(oi)
|
||||
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s", i+1, instanceType, err)
|
||||
continue
|
||||
}
|
||||
|
||||
result, err := obj.ListObjects(context.Background(), testCase.bucketName, "", "", "", 1000)
|
||||
if err != nil {
|
||||
|
||||
@@ -145,6 +145,7 @@ type WalkOptions struct {
|
||||
LatestOnly bool // returns only latest versions for all matching objects
|
||||
AskDisks string // dictates how many disks are being listed
|
||||
VersionsSort WalkVersionsSortOrder // sort order for versions of the same object; default: Ascending order in ModTime
|
||||
Limit int // maximum number of items, 0 means no limit
|
||||
}
|
||||
|
||||
// ExpirationOptions represents object options for object expiration at objectLayer.
|
||||
|
||||
@@ -219,9 +219,9 @@ func (z *MakeBucketOptions) Msgsize() (s int) {
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *WalkOptions) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 4
|
||||
// map header, size 5
|
||||
// string "Marker"
|
||||
o = append(o, 0x84, 0xa6, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x72)
|
||||
o = append(o, 0x85, 0xa6, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x72)
|
||||
o = msgp.AppendString(o, z.Marker)
|
||||
// string "LatestOnly"
|
||||
o = append(o, 0xaa, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x6e, 0x6c, 0x79)
|
||||
@@ -232,6 +232,9 @@ func (z *WalkOptions) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
// string "VersionsSort"
|
||||
o = append(o, 0xac, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x6f, 0x72, 0x74)
|
||||
o = msgp.AppendUint8(o, uint8(z.VersionsSort))
|
||||
// string "Limit"
|
||||
o = append(o, 0xa5, 0x4c, 0x69, 0x6d, 0x69, 0x74)
|
||||
o = msgp.AppendInt(o, z.Limit)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -281,6 +284,12 @@ func (z *WalkOptions) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
}
|
||||
z.VersionsSort = WalkVersionsSortOrder(zb0002)
|
||||
}
|
||||
case "Limit":
|
||||
z.Limit, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Limit")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
@@ -295,7 +304,7 @@ func (z *WalkOptions) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *WalkOptions) Msgsize() (s int) {
|
||||
s = 1 + 7 + msgp.StringPrefixSize + len(z.Marker) + 11 + msgp.BoolSize + 9 + msgp.StringPrefixSize + len(z.AskDisks) + 13 + msgp.Uint8Size
|
||||
s = 1 + 7 + msgp.StringPrefixSize + len(z.Marker) + 11 + msgp.BoolSize + 9 + msgp.StringPrefixSize + len(z.AskDisks) + 13 + msgp.Uint8Size + 6 + msgp.IntSize
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -225,9 +225,11 @@ func getAndValidateAttributesOpts(ctx context.Context, w http.ResponseWriter, r
|
||||
|
||||
func parseObjectAttributes(h http.Header) (attributes map[string]struct{}) {
|
||||
attributes = make(map[string]struct{})
|
||||
for _, v := range strings.Split(strings.TrimSpace(h.Get(xhttp.AmzObjectAttributes)), ",") {
|
||||
if v != "" {
|
||||
attributes[v] = struct{}{}
|
||||
for _, headerVal := range h.Values(xhttp.AmzObjectAttributes) {
|
||||
for _, v := range strings.Split(strings.TrimSpace(headerVal), ",") {
|
||||
if v != "" {
|
||||
attributes[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2015-2024 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"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
)
|
||||
|
||||
// TestGetAndValidateAttributesOpts is currently minimal and covers a subset of getAndValidateAttributesOpts(),
|
||||
// it is intended to be expanded when the function is worked on in the future.
|
||||
func TestGetAndValidateAttributesOpts(t *testing.T) {
|
||||
globalBucketVersioningSys = &BucketVersioningSys{}
|
||||
bucket := minioMetaBucket
|
||||
ctx := context.Background()
|
||||
testCases := []struct {
|
||||
name string
|
||||
headers http.Header
|
||||
wantObjectAttrs map[string]struct{}
|
||||
}{
|
||||
{
|
||||
name: "empty header",
|
||||
headers: http.Header{},
|
||||
wantObjectAttrs: map[string]struct{}{},
|
||||
},
|
||||
{
|
||||
name: "single header line",
|
||||
headers: http.Header{
|
||||
xhttp.AmzObjectAttributes: []string{"test1,test2"},
|
||||
},
|
||||
wantObjectAttrs: map[string]struct{}{
|
||||
"test1": {}, "test2": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple header lines with some duplicates",
|
||||
headers: http.Header{
|
||||
xhttp.AmzObjectAttributes: []string{"test1,test2", "test3,test4", "test4,test3"},
|
||||
},
|
||||
wantObjectAttrs: map[string]struct{}{
|
||||
"test1": {}, "test2": {}, "test3": {}, "test4": {},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/test", nil)
|
||||
req.Header = testCase.headers
|
||||
|
||||
opts, _ := getAndValidateAttributesOpts(ctx, rec, req, bucket, "testobject")
|
||||
|
||||
if !reflect.DeepEqual(opts.ObjectAttributes, testCase.wantObjectAttrs) {
|
||||
t.Errorf("want opts %v, got %v", testCase.wantObjectAttrs, opts.ObjectAttributes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+1
-18
@@ -279,24 +279,7 @@ func deleteBucketLocal(ctx context.Context, bucket string, opts DeleteBucketOpti
|
||||
}, index)
|
||||
}
|
||||
|
||||
var recreate bool
|
||||
errs := g.Wait()
|
||||
for index, err := range errs {
|
||||
if errors.Is(err, errVolumeNotEmpty) {
|
||||
recreate = true
|
||||
}
|
||||
if err == nil && recreate {
|
||||
// ignore any errors
|
||||
localDrives[index].MakeVol(ctx, bucket)
|
||||
}
|
||||
}
|
||||
|
||||
// Since we recreated buckets and error was `not-empty`, return not-empty.
|
||||
if recreate {
|
||||
return errVolumeNotEmpty
|
||||
} // for all other errors reduce by write quorum.
|
||||
|
||||
return reduceWriteQuorumErrs(ctx, errs, bucketOpIgnoredErrs, (len(localDrives)/2)+1)
|
||||
return reduceWriteQuorumErrs(ctx, g.Wait(), bucketOpIgnoredErrs, (len(localDrives)/2)+1)
|
||||
}
|
||||
|
||||
func makeBucketLocal(ctx context.Context, bucket string, opts MakeBucketOptions) error {
|
||||
|
||||
@@ -23,9 +23,10 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
xnet "github.com/minio/pkg/v3/net"
|
||||
|
||||
"github.com/minio/minio/internal/color"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
xnet "github.com/minio/pkg/v3/net"
|
||||
)
|
||||
|
||||
// generates format string depending on the string length and padding.
|
||||
@@ -143,7 +144,7 @@ func printServerCommonMsg(apiEndpoints []string) {
|
||||
|
||||
// Prints startup message for Object API access, prints link to our SDK documentation.
|
||||
func printObjectAPIMsg() {
|
||||
logger.Startup(color.Blue("\nDocs: ") + "https://min.io/docs/minio/linux/index.html")
|
||||
logger.Startup(color.Blue("\nDocs: ") + "https://docs.min.io")
|
||||
}
|
||||
|
||||
func printLambdaTargets() {
|
||||
|
||||
+13
-13
@@ -162,31 +162,32 @@ internalAuth:
|
||||
}
|
||||
|
||||
if caPublicKey != nil && pass == nil {
|
||||
|
||||
err := validateKey(c, key)
|
||||
if err != nil {
|
||||
return nil, errAuthentication
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Temporary credentials are not allowed.
|
||||
if ui.Credentials.IsTemp() {
|
||||
return nil, errAuthentication
|
||||
}
|
||||
|
||||
if subtle.ConstantTimeCompare([]byte(ui.Credentials.SecretKey), pass) != 1 {
|
||||
return nil, errAuthentication
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
copts := map[string]string{
|
||||
"AccessKey": ui.Credentials.AccessKey,
|
||||
"SecretKey": ui.Credentials.SecretKey,
|
||||
}
|
||||
if ui.Credentials.IsTemp() {
|
||||
copts["SessionToken"] = ui.Credentials.SessionToken
|
||||
}
|
||||
|
||||
return &ssh.Permissions{
|
||||
CriticalOptions: map[string]string{
|
||||
"AccessKey": ui.Credentials.AccessKey,
|
||||
"SecretKey": ui.Credentials.SecretKey,
|
||||
"SessionToken": ui.Credentials.SessionToken,
|
||||
},
|
||||
Extensions: make(map[string]string),
|
||||
CriticalOptions: copts,
|
||||
Extensions: make(map[string]string),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -207,9 +208,8 @@ func processLDAPAuthentication(key ssh.PublicKey, pass []byte, user string) (per
|
||||
|
||||
return &ssh.Permissions{
|
||||
CriticalOptions: map[string]string{
|
||||
"AccessKey": sa.Credentials.AccessKey,
|
||||
"SecretKey": sa.Credentials.SecretKey,
|
||||
"SessionToken": sa.Credentials.SessionToken,
|
||||
"AccessKey": sa.Credentials.AccessKey,
|
||||
"SecretKey": sa.Credentials.SecretKey,
|
||||
},
|
||||
Extensions: make(map[string]string),
|
||||
}, nil
|
||||
|
||||
+28
-19
@@ -118,7 +118,8 @@ type xlStorage struct {
|
||||
major, minor uint32
|
||||
fsType string
|
||||
|
||||
immediatePurge chan string
|
||||
immediatePurge chan string
|
||||
immediatePurgeCancel context.CancelFunc
|
||||
|
||||
// mutex to prevent concurrent read operations overloading walks.
|
||||
rotational bool
|
||||
@@ -216,17 +217,21 @@ func newXLStorage(ep Endpoint, cleanUp bool) (s *xlStorage, err error) {
|
||||
if globalIsTesting || globalIsCICD {
|
||||
immediatePurgeQueue = 1
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(GlobalContext)
|
||||
|
||||
s = &xlStorage{
|
||||
drivePath: ep.Path,
|
||||
endpoint: ep,
|
||||
globalSync: globalFSOSync,
|
||||
diskInfoCache: cachevalue.New[DiskInfo](),
|
||||
immediatePurge: make(chan string, immediatePurgeQueue),
|
||||
drivePath: ep.Path,
|
||||
endpoint: ep,
|
||||
globalSync: globalFSOSync,
|
||||
diskInfoCache: cachevalue.New[DiskInfo](),
|
||||
immediatePurge: make(chan string, immediatePurgeQueue),
|
||||
immediatePurgeCancel: cancel,
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err == nil {
|
||||
go s.cleanupTrashImmediateCallers(GlobalContext)
|
||||
if cleanUp && err == nil {
|
||||
go s.cleanupTrashImmediateCallers(ctx)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -341,7 +346,13 @@ func newXLStorage(ep Endpoint, cleanUp bool) (s *xlStorage, err error) {
|
||||
// Healing is 'true' when
|
||||
// - if we found an unformatted disk (no 'format.json')
|
||||
// - if we found healing tracker 'healing.bin'
|
||||
dcinfo.Healing = errors.Is(err, errUnformattedDisk) || (s.Healing() != nil)
|
||||
dcinfo.Healing = errors.Is(err, errUnformattedDisk)
|
||||
if !dcinfo.Healing {
|
||||
if hi := s.Healing(); hi != nil && !hi.Finished {
|
||||
dcinfo.Healing = true
|
||||
}
|
||||
}
|
||||
|
||||
dcinfo.ID = diskID
|
||||
return dcinfo, err
|
||||
},
|
||||
@@ -393,7 +404,8 @@ func (s *xlStorage) Endpoint() Endpoint {
|
||||
return s.endpoint
|
||||
}
|
||||
|
||||
func (*xlStorage) Close() error {
|
||||
func (s *xlStorage) Close() error {
|
||||
s.immediatePurgeCancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1196,7 +1208,12 @@ func (s *xlStorage) cleanupTrashImmediateCallers(ctx context.Context) {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case entry := <-s.immediatePurge:
|
||||
removeAll(entry)
|
||||
// Add deadlines such that immediate purge is not
|
||||
// perpetually hung here.
|
||||
w := xioutil.NewDeadlineWorker(globalDriveConfig.GetMaxTimeout())
|
||||
w.Run(func() error {
|
||||
return removeAll(entry)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2073,14 +2090,6 @@ func (s *xlStorage) ReadFileStream(ctx context.Context, volume, path string, off
|
||||
return &sendFileReader{Reader: io.LimitReader(file, length), Closer: file}, nil
|
||||
}
|
||||
|
||||
// closeWrapper converts a function to an io.Closer
|
||||
type closeWrapper func() error
|
||||
|
||||
// Close calls the wrapped function.
|
||||
func (c closeWrapper) Close() error {
|
||||
return c()
|
||||
}
|
||||
|
||||
// CreateFile - creates the file.
|
||||
func (s *xlStorage) CreateFile(ctx context.Context, origvolume, volume, path string, fileSize int64, r io.Reader) (err error) {
|
||||
if origvolume != "" {
|
||||
|
||||
@@ -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.2024-08-03T04-33-23Z
|
||||
image: quay.io/minio/minio:RELEASE.2024-08-17T01-24-54Z
|
||||
command: server --console-address ":9001" http://minio{1...4}/data{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
|
||||
@@ -4,10 +4,10 @@ go 1.22
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.43.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.0
|
||||
github.com/IBM/sarama v1.43.2
|
||||
github.com/IBM/sarama v1.43.3
|
||||
github.com/alecthomas/participle v0.7.1
|
||||
github.com/bcicen/jstream v1.0.1
|
||||
github.com/beevik/ntp v1.4.3
|
||||
@@ -42,19 +42,19 @@ require (
|
||||
github.com/klauspost/reedsolomon v1.12.3
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/lithammer/shortuuid/v4 v4.0.0
|
||||
github.com/miekg/dns v1.1.61
|
||||
github.com/miekg/dns v1.1.62
|
||||
github.com/minio/cli v1.24.2
|
||||
github.com/minio/console v1.7.0
|
||||
github.com/minio/console v1.7.1-0.20240817215248-0b07cb38850f
|
||||
github.com/minio/csvparser v1.0.0
|
||||
github.com/minio/dnscache v0.1.1
|
||||
github.com/minio/dperf v0.5.3
|
||||
github.com/minio/highwayhash v1.0.3
|
||||
github.com/minio/kms-go/kes v0.3.0
|
||||
github.com/minio/kms-go/kms v0.4.0
|
||||
github.com/minio/madmin-go/v3 v3.0.61
|
||||
github.com/minio/minio-go/v7 v7.0.75
|
||||
github.com/minio/madmin-go/v3 v3.0.64
|
||||
github.com/minio/minio-go/v7 v7.0.76
|
||||
github.com/minio/mux v1.9.0
|
||||
github.com/minio/pkg/v3 v3.0.11
|
||||
github.com/minio/pkg/v3 v3.0.13
|
||||
github.com/minio/selfupdate v0.6.0
|
||||
github.com/minio/simdjson-go v0.4.5
|
||||
github.com/minio/sio v0.4.0
|
||||
@@ -62,7 +62,7 @@ require (
|
||||
github.com/minio/zipindex v0.3.1
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/nats-io/nats-server/v2 v2.9.23
|
||||
github.com/nats-io/nats.go v1.36.0
|
||||
github.com/nats-io/nats.go v1.37.0
|
||||
github.com/nats-io/stan.go v0.10.4
|
||||
github.com/ncw/directio v1.0.5
|
||||
github.com/nsqio/go-nsq v1.1.0
|
||||
@@ -71,7 +71,7 @@ require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/pkg/xattr v0.4.10
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/prometheus/client_golang v1.20.2
|
||||
github.com/prometheus/client_model v0.6.1
|
||||
github.com/prometheus/common v0.55.0
|
||||
github.com/prometheus/procfs v0.15.1
|
||||
@@ -91,13 +91,13 @@ require (
|
||||
go.uber.org/zap v1.27.0
|
||||
goftp.io/server/v2 v2.0.1
|
||||
golang.org/x/crypto v0.26.0
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948
|
||||
golang.org/x/oauth2 v0.22.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sys v0.24.0
|
||||
golang.org/x/term v0.23.0
|
||||
golang.org/x/time v0.6.0
|
||||
google.golang.org/api v0.191.0
|
||||
google.golang.org/api v0.194.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -105,11 +105,11 @@ require (
|
||||
require (
|
||||
aead.dev/mem v0.2.0 // indirect
|
||||
aead.dev/minisign v0.3.0 // indirect
|
||||
cloud.google.com/go v0.115.0 // indirect
|
||||
cloud.google.com/go/auth v0.8.0 // indirect
|
||||
cloud.google.com/go v0.115.1 // indirect
|
||||
cloud.google.com/go/auth v0.9.1 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.13 // indirect
|
||||
cloud.google.com/go/iam v1.2.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
@@ -120,13 +120,11 @@ require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/charmbracelet/bubbles v0.18.0 // indirect
|
||||
github.com/charmbracelet/bubbletea v0.26.6 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.12.1 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.1.4 // indirect
|
||||
github.com/charmbracelet/x/input v0.1.3 // indirect
|
||||
github.com/charmbracelet/x/term v0.1.1 // indirect
|
||||
github.com/charmbracelet/x/windows v0.1.2 // indirect
|
||||
github.com/charmbracelet/bubbles v0.19.0 // indirect
|
||||
github.com/charmbracelet/bubbletea v0.27.1 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.13.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.2.3 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
@@ -189,7 +187,7 @@ require (
|
||||
github.com/lestrrat-go/jwx v1.2.30 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240819163618-b1d8f4d146e7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-ieproxy v0.0.12 // indirect
|
||||
@@ -199,7 +197,7 @@ require (
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/minio/colorjson v1.0.8 // indirect
|
||||
github.com/minio/filepath v1.0.0 // indirect
|
||||
github.com/minio/mc v0.0.0-20240812085227-32b7b7219356 // indirect
|
||||
github.com/minio/mc v0.0.0-20240826104958-a55d9a8d17da // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/pkg/v2 v2.0.19 // indirect
|
||||
github.com/minio/websocket v1.6.0 // indirect
|
||||
@@ -226,7 +224,7 @@ require (
|
||||
github.com/prometheus/prometheus v0.54.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rjeczalik/notify v0.9.3 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/safchain/ethtool v0.4.1 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/tidwall/gjson v1.17.3 // indirect
|
||||
@@ -235,26 +233,25 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||
github.com/unrolled/secure v1.15.0 // indirect
|
||||
github.com/vbauerster/mpb/v8 v8.7.5 // indirect
|
||||
github.com/vbauerster/mpb/v8 v8.8.2 // indirect
|
||||
github.com/xdg/stringprep v1.0.3 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
|
||||
go.mongodb.org/mongo-driver v1.16.1 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240808171019-573a1156607a // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240808171019-573a1156607a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240808171019-573a1156607a // indirect
|
||||
google.golang.org/genproto v0.0.0-20240823204242-4ba0660f739c // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240823204242-4ba0660f739c // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240823204242-4ba0660f739c // indirect
|
||||
google.golang.org/grpc v1.65.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
)
|
||||
|
||||
@@ -4,25 +4,25 @@ aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
|
||||
aead.dev/minisign v0.3.0 h1:8Xafzy5PEVZqYDNP60yJHARlW1eOQtsKNp/Ph2c0vRA=
|
||||
aead.dev/minisign v0.3.0/go.mod h1:NLvG3Uoq3skkRMDuc3YHpWUTMTrSExqm+Ij73W13F6Y=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
||||
cloud.google.com/go/auth v0.8.0 h1:y8jUJLl/Fg+qNBWxP/Hox2ezJvjkrPb952PC1p0G6A4=
|
||||
cloud.google.com/go/auth v0.8.0/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc=
|
||||
cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ=
|
||||
cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc=
|
||||
cloud.google.com/go/auth v0.9.1 h1:+pMtLEV2k0AXKvs/tGZojuj6QaioxfUjOpMsG5Gtx+w=
|
||||
cloud.google.com/go/auth v0.9.1/go.mod h1:Sw8ocT5mhhXxFklyhT12Eiy0ed6tTrPMCJjSI8KhYLk=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
|
||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||
cloud.google.com/go/iam v1.1.13 h1:7zWBXG9ERbMLrzQBRhFliAV+kjcRToDTgQT3CTwYyv4=
|
||||
cloud.google.com/go/iam v1.1.13/go.mod h1:K8mY0uSXwEXS30KrnVb+j54LB/ntfZu1dr+4zFMNbus=
|
||||
cloud.google.com/go/longrunning v0.5.11 h1:Havn1kGjz3whCfoD8dxMLP73Ph5w+ODyZB9RUsDxtGk=
|
||||
cloud.google.com/go/longrunning v0.5.11/go.mod h1:rDn7//lmlfWV1Dx6IB4RatCPenTwwmqXuiP0/RgoEO4=
|
||||
cloud.google.com/go/iam v1.2.0 h1:kZKMKVNk/IsSSc/udOb83K0hL/Yh/Gcqpz+oAkoIFN8=
|
||||
cloud.google.com/go/iam v1.2.0/go.mod h1:zITGuWgsLZxd8OwAlX+eMFgZDXzBm7icj1PVTYG766Q=
|
||||
cloud.google.com/go/longrunning v0.5.12 h1:5LqSIdERr71CqfUsFlJdBpOkBH8FBCFD7P1nTWy3TYE=
|
||||
cloud.google.com/go/longrunning v0.5.12/go.mod h1:S5hMV8CDJ6r50t2ubVJSKQVv5u0rmik5//KgLO3k4lU=
|
||||
cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs=
|
||||
cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 h1:GJHeeA2N7xrG3q30L2UXDyuWRzDM900/65j70wcM4Ww=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
|
||||
@@ -37,8 +37,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mx
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/IBM/sarama v1.43.2 h1:HABeEqRUh32z8yzY2hGB/j8mHSzC/HA9zlEjqFNCzSw=
|
||||
github.com/IBM/sarama v1.43.2/go.mod h1:Kyo4WkF24Z+1nz7xeVUFWIuKVV8RS3wM8mkvPKMdXFQ=
|
||||
github.com/IBM/sarama v1.43.3 h1:Yj6L2IaNvb2mRBop39N7mmJAHBVY3dTPncr3qGVkxPA=
|
||||
github.com/IBM/sarama v1.43.3/go.mod h1:FVIRaLrhK3Cla/9FfRF5X9Zua2KpS3SYIXxhac1H+FQ=
|
||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
@@ -60,6 +60,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
|
||||
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
|
||||
github.com/bcicen/jstream v1.0.1 h1:BXY7Cu4rdmc0rhyTVyT3UkxAiX3bnLpKLas9btbH5ck=
|
||||
github.com/bcicen/jstream v1.0.1/go.mod h1:9ielPxqFry7Y4Tg3j4BfjPocfJ3TbsRtXOAYXYmRuAQ=
|
||||
github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho=
|
||||
@@ -72,20 +74,18 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
||||
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
|
||||
github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s=
|
||||
github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk=
|
||||
github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs=
|
||||
github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8=
|
||||
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
|
||||
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||
github.com/charmbracelet/x/input v0.1.3 h1:oy4TMhyGQsYs/WWJwu1ELUMFnjiUAXwtDf048fHbCkg=
|
||||
github.com/charmbracelet/x/input v0.1.3/go.mod h1:1gaCOyw1KI9e2j00j/BBZ4ErzRZqa05w0Ghn83yIhKU=
|
||||
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
|
||||
github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
|
||||
github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg=
|
||||
github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ=
|
||||
github.com/charmbracelet/bubbles v0.19.0 h1:gKZkKXPP6GlDk6EcfujDK19PCQqRjaJZQ7QRERx1UF0=
|
||||
github.com/charmbracelet/bubbles v0.19.0/go.mod h1:WILteEqZ+krG5c3ntGEMeG99nCupcuIk7V0/zOP0tOA=
|
||||
github.com/charmbracelet/bubbletea v0.27.1 h1:/yhaJKX52pxG4jZVKCNWj/oq0QouPdXycriDRA6m6r8=
|
||||
github.com/charmbracelet/bubbletea v0.27.1/go.mod h1:xc4gm5yv+7tbniEvQ0naiG9P3fzYhk16cTgDZQQW6YE=
|
||||
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
|
||||
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
|
||||
github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY=
|
||||
github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
|
||||
github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0=
|
||||
github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo=
|
||||
github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
@@ -376,8 +376,8 @@ github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw
|
||||
github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
|
||||
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/lufia/plan9stats v0.0.0-20240819163618-b1d8f4d146e7 h1:5RK988zAqB3/AN3opGfRpoQgAVqr6/A5+qRTi67VUZY=
|
||||
github.com/lufia/plan9stats v0.0.0-20240819163618-b1d8f4d146e7/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
@@ -403,14 +403,14 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
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.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
|
||||
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
|
||||
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||
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.8 h1:AS6gEQ1dTRYHmC4xuoodPDRILHP/9Wz5wYUGDQfPLpg=
|
||||
github.com/minio/colorjson v1.0.8/go.mod h1:wrs39G/4kqNlGjwqHvPlAnXuc2tlPszo6JKdSBCLN8w=
|
||||
github.com/minio/console v1.7.0 h1:0G1Z3To1dvnV6KxP/IgZ4h8GNzEQ3FU5M3FHBKjB9hE=
|
||||
github.com/minio/console v1.7.0/go.mod h1:yFhhM3Y3uT4N1WtphcYr3QAd7WYLU8CEuTcIiDpksWs=
|
||||
github.com/minio/console v1.7.1-0.20240817215248-0b07cb38850f h1:+nfGhuHtHRwQgHK/C2t44knNMyJ3upwouwIR+hUz7Ts=
|
||||
github.com/minio/console v1.7.1-0.20240817215248-0b07cb38850f/go.mod h1:XOmAKeIFM3RbWMc6n7neBUzXboF8Xd6HkZw4FH2QrWo=
|
||||
github.com/minio/csvparser v1.0.0 h1:xJEHcYK8ZAjeW4hNV9Zu30u+/2o4UyPnYgyjWp8b7ZU=
|
||||
github.com/minio/csvparser v1.0.0/go.mod h1:lKXskSLzPgC5WQyzP7maKH7Sl1cqvANXo9YCto8zbtM=
|
||||
github.com/minio/dnscache v0.1.1 h1:AMYLqomzskpORiUA1ciN9k7bZT1oB3YZN4cEIi88W5o=
|
||||
@@ -426,21 +426,21 @@ github.com/minio/kms-go/kes v0.3.0 h1:SU8VGVM/Hk9w1OiSby3OatkcojooUqIdDHl6dtM6Nk
|
||||
github.com/minio/kms-go/kes v0.3.0/go.mod h1:w6DeVT878qEOU3nUrYVy1WOT5H1Ig9hbDIh698NYJKY=
|
||||
github.com/minio/kms-go/kms v0.4.0 h1:cLPZceEp+05xHotVBaeFJrgL7JcXM4lBy6PU0idkE7I=
|
||||
github.com/minio/kms-go/kms v0.4.0/go.mod h1:q12CehiIy2qgBnDKq6Q7wmPi2PHSyRVug5DKp0HAVeE=
|
||||
github.com/minio/madmin-go/v3 v3.0.61 h1:JB85GXfvc7DuOES6tpZXNX//Rv6DLlWXv9hDpFpN8sw=
|
||||
github.com/minio/madmin-go/v3 v3.0.61/go.mod h1:IFAwr0XMrdsLovxAdCcuq/eoL4nRuMVQQv0iubJANQw=
|
||||
github.com/minio/mc v0.0.0-20240812085227-32b7b7219356 h1:qz2SKs+PIWRg/qEecVn0mr7JQfw/f/hXGynQ++tGK6A=
|
||||
github.com/minio/mc v0.0.0-20240812085227-32b7b7219356/go.mod h1:7aetD0/ltOoc1SHcPJ7lZepzs9eEyHRa9s/NjhH+Do4=
|
||||
github.com/minio/madmin-go/v3 v3.0.64 h1:Btwgs3CrgSciVaCWv/3clOxuDdUzylo/oTQp0M8GkwE=
|
||||
github.com/minio/madmin-go/v3 v3.0.64/go.mod h1:IFAwr0XMrdsLovxAdCcuq/eoL4nRuMVQQv0iubJANQw=
|
||||
github.com/minio/mc v0.0.0-20240826104958-a55d9a8d17da h1:/JNoAwFPhCG0hUkc9k/wUlzMUO7tA9WLIoWgqQjYph4=
|
||||
github.com/minio/mc v0.0.0-20240826104958-a55d9a8d17da/go.mod h1:g/LyYpzVUVY+ZxfIDtzigg+L3NjAn88EBsLAkFvo+M0=
|
||||
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.75 h1:0uLrB6u6teY2Jt+cJUVi9cTvDRuBKWSRzSAcznRkwlE=
|
||||
github.com/minio/minio-go/v7 v7.0.75/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8=
|
||||
github.com/minio/minio-go/v7 v7.0.76 h1:9nxHH2XDai61cT/EFhyIw/wW4vJfpPNvl7lSFpRt+Ng=
|
||||
github.com/minio/minio-go/v7 v7.0.76/go.mod h1:AVM3IUN6WwKzmwBxVdjzhH8xq+f57JSbbvzqvUzR6eg=
|
||||
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/v2 v2.0.19 h1:r187/k/oVH9H0DDwvLY5WipkJaZ4CLd4KI3KgIUExR0=
|
||||
github.com/minio/pkg/v2 v2.0.19/go.mod h1:luK9LAhQlAPzSuF6F326XSCKjMc1G3Tbh+a9JYwqh8M=
|
||||
github.com/minio/pkg/v3 v3.0.11 h1:+r61IPJRKE34lps1LkMGuksTtcxApq9s52Du+3b//wo=
|
||||
github.com/minio/pkg/v3 v3.0.11/go.mod h1:QfWcz9jh3Qu0Xg1mVBKhBzIKj7hKB7vz61/9pR4ZZ9Q=
|
||||
github.com/minio/pkg/v3 v3.0.13 h1:T/GnTZn1VY2Endi1sLGHDCZ0NQgTRvZfUmx9abPHKsw=
|
||||
github.com/minio/pkg/v3 v3.0.13/go.mod h1:nEXe/Hy7eUW+uKoOfZtQSb8/wfh3ddH0GnO+SoFoSvs=
|
||||
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=
|
||||
@@ -488,8 +488,8 @@ github.com/nats-io/nats.go v1.13.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/
|
||||
github.com/nats-io/nats.go v1.14.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
|
||||
github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
|
||||
github.com/nats-io/nats.go v1.22.1/go.mod h1:tLqubohF7t4z3du1QDPYJIQQyhb4wl6DhjxEajSI7UA=
|
||||
github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU=
|
||||
github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
|
||||
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
@@ -533,8 +533,8 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
|
||||
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
@@ -567,8 +567,8 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
|
||||
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/safchain/ethtool v0.4.1 h1:S6mEleTADqgynileXoiapt/nKnatyR6bmIHoF+h2ADo=
|
||||
github.com/safchain/ethtool v0.4.1/go.mod h1:XLLnZmy4OCRTkksP/UiMjij96YmIsBfmBQcs7H6tA48=
|
||||
@@ -624,14 +624,12 @@ github.com/unrolled/secure v1.15.0 h1:q7x+pdp8jAHnbzxu6UheP8fRlG/rwYTb8TPuQ3rn9O
|
||||
github.com/unrolled/secure v1.15.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/vbauerster/mpb/v8 v8.7.5 h1:hUF3zaNsuaBBwzEFoCvfuX3cpesQXZC0Phm/JcHZQ+c=
|
||||
github.com/vbauerster/mpb/v8 v8.7.5/go.mod h1:bRCnR7K+mj5WXKsy0NWB6Or+wctYGvVwKn6huwvxKa0=
|
||||
github.com/vbauerster/mpb/v8 v8.8.2 h1:j9D/WmvKZw0BK1etRkw8lxVMKs4KO3TgdXsQWyEyPuc=
|
||||
github.com/vbauerster/mpb/v8 v8.8.2/go.mod h1:JfCCrtcMsJwP6ZwMn9e5LMnNyp3TVNpUWWkN+nd4EWk=
|
||||
github.com/xdg/scram v1.0.5 h1:TuS0RFmt5Is5qm9Tm2SoD89OPqe4IRiFtyFY4iwWXsw=
|
||||
github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||
github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4=
|
||||
github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -654,18 +652,18 @@ go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4
|
||||
go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -696,8 +694,8 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
@@ -829,19 +827,19 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.191.0 h1:cJcF09Z+4HAB2t5qTQM1ZtfL/PemsLFkcFG67qq2afk=
|
||||
google.golang.org/api v0.191.0/go.mod h1:tD5dsFGxFza0hnQveGfVk9QQYKcfp+VzgRqyXFxE0+E=
|
||||
google.golang.org/api v0.194.0 h1:dztZKG9HgtIpbI35FhfuSNR/zmaMVdxNlntHj1sIS4s=
|
||||
google.golang.org/api v0.194.0/go.mod h1:AgvUFdojGANh3vI+P7EVnxj3AISHllxGCJSFmggmnd0=
|
||||
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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20240808171019-573a1156607a h1:3JVv3Ujh+kGiajpSqHWnbWPuu0nQqMZ3hASNDDF9974=
|
||||
google.golang.org/genproto v0.0.0-20240808171019-573a1156607a/go.mod h1:7uvplUBj4RjHAxIZ//98LzOvrQ04JBkaixRmCMI29hc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240808171019-573a1156607a h1:KyUe15n7B1YCu+kMmPtlXxgkLQbp+Dw0tCRZf9Sd+CE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240808171019-573a1156607a/go.mod h1:4+X6GvPs+25wZKbQq9qyAXrwIRExv7w0Ea6MgZLZiDM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240808171019-573a1156607a h1:EKiZZXueP9/T68B8Nl0GAx9cjbQnCId0yP3qPMgaaHs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240808171019-573a1156607a/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/genproto v0.0.0-20240823204242-4ba0660f739c h1:TYOEhrQMrNDTAd2rX9m+WgGr8Ku6YNuj1D7OX6rWSok=
|
||||
google.golang.org/genproto v0.0.0-20240823204242-4ba0660f739c/go.mod h1:2rC5OendXvZ8wGEo/cSLheztrZDZaSoHanUcd1xtZnw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240823204242-4ba0660f739c h1:e0zB268kOca6FbuJkYUGxfwG4DKFZG/8DLyv9Zv66cE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240823204242-4ba0660f739c/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240823204242-4ba0660f739c h1:Kqjm4WpoWvwhMPcrAczoTyMySQmYa9Wy2iL6Con4zn8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240823204242-4ba0660f739c/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
|
||||
+29
-11
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015-2023 MinIO, Inc.
|
||||
// Copyright (c) 2015-2024 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
@@ -17,7 +17,9 @@
|
||||
|
||||
package bpool
|
||||
|
||||
import "github.com/klauspost/reedsolomon"
|
||||
import (
|
||||
"github.com/klauspost/reedsolomon"
|
||||
)
|
||||
|
||||
// BytePoolCap implements a leaky pool of []byte in the form of a bounded channel.
|
||||
type BytePoolCap struct {
|
||||
@@ -29,11 +31,14 @@ type BytePoolCap struct {
|
||||
// NewBytePoolCap creates a new BytePool bounded to the given maxSize, with new
|
||||
// byte arrays sized based on width.
|
||||
func NewBytePoolCap(maxSize uint64, width int, capwidth int) (bp *BytePoolCap) {
|
||||
if capwidth > 0 && capwidth < 64 {
|
||||
if capwidth <= 0 {
|
||||
panic("total buffer capacity must be provided")
|
||||
}
|
||||
if capwidth < 64 {
|
||||
panic("buffer capped with smaller than 64 bytes is not supported")
|
||||
}
|
||||
if capwidth > 0 && width > capwidth {
|
||||
panic("buffer length cannot be > capacity of the buffer")
|
||||
if width > capwidth {
|
||||
panic("minimum buffer length cannot be > capacity of the buffer")
|
||||
}
|
||||
return &BytePoolCap{
|
||||
c: make(chan []byte, maxSize),
|
||||
@@ -60,11 +65,7 @@ func (bp *BytePoolCap) Get() (b []byte) {
|
||||
// reuse existing buffer
|
||||
default:
|
||||
// create new aligned buffer
|
||||
if bp.wcap > 0 {
|
||||
b = reedsolomon.AllocAligned(1, bp.wcap)[0][:bp.w]
|
||||
} else {
|
||||
b = reedsolomon.AllocAligned(1, bp.w)[0]
|
||||
}
|
||||
b = reedsolomon.AllocAligned(1, bp.wcap)[0][:bp.w]
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -74,8 +75,17 @@ func (bp *BytePoolCap) Put(b []byte) {
|
||||
if bp == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cap(b) != bp.wcap {
|
||||
// someone tried to put back buffer which is not part of this buffer pool
|
||||
// we simply don't put this back into pool, a modified buffer provided
|
||||
// by this package is no more usable, callers make sure to not modify
|
||||
// the capacity of the buffer.
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case bp.c <- b:
|
||||
case bp.c <- b[:bp.w]:
|
||||
// buffer went back into pool
|
||||
default:
|
||||
// buffer didn't go back into pool, just discard
|
||||
@@ -97,3 +107,11 @@ func (bp *BytePoolCap) WidthCap() (n int) {
|
||||
}
|
||||
return bp.wcap
|
||||
}
|
||||
|
||||
// CurrentSize returns current size of buffer pool
|
||||
func (bp *BytePoolCap) CurrentSize() int {
|
||||
if bp == nil {
|
||||
return 0
|
||||
}
|
||||
return len(bp.c) * bp.w
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
|
||||
package bpool
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests - bytePool functionality.
|
||||
func TestBytePool(t *testing.T) {
|
||||
@@ -25,20 +27,20 @@ func TestBytePool(t *testing.T) {
|
||||
width := 1024
|
||||
capWidth := 2048
|
||||
|
||||
bufPool := NewBytePoolCap(size, width, capWidth)
|
||||
bp := NewBytePoolCap(size, width, capWidth)
|
||||
|
||||
// Check the width
|
||||
if bufPool.Width() != width {
|
||||
t.Fatalf("bytepool width invalid: got %v want %v", bufPool.Width(), width)
|
||||
if bp.Width() != width {
|
||||
t.Fatalf("bytepool width invalid: got %v want %v", bp.Width(), width)
|
||||
}
|
||||
|
||||
// Check with width cap
|
||||
if bufPool.WidthCap() != capWidth {
|
||||
t.Fatalf("bytepool capWidth invalid: got %v want %v", bufPool.WidthCap(), capWidth)
|
||||
if bp.WidthCap() != capWidth {
|
||||
t.Fatalf("bytepool capWidth invalid: got %v want %v", bp.WidthCap(), capWidth)
|
||||
}
|
||||
|
||||
// Check that retrieved buffer are of the expected width
|
||||
b := bufPool.Get()
|
||||
b := bp.Get()
|
||||
if len(b) != width {
|
||||
t.Fatalf("bytepool length invalid: got %v want %v", len(b), width)
|
||||
}
|
||||
@@ -46,14 +48,14 @@ func TestBytePool(t *testing.T) {
|
||||
t.Fatalf("bytepool cap invalid: got %v want %v", cap(b), capWidth)
|
||||
}
|
||||
|
||||
bufPool.Put(b)
|
||||
bp.Put(b)
|
||||
|
||||
// Fill the pool beyond the capped pool size.
|
||||
for i := uint64(0); i < size*2; i++ {
|
||||
bufPool.Put(make([]byte, bufPool.w))
|
||||
bp.Put(make([]byte, bp.w, bp.wcap))
|
||||
}
|
||||
|
||||
b = bufPool.Get()
|
||||
b = bp.Get()
|
||||
if len(b) != width {
|
||||
t.Fatalf("bytepool length invalid: got %v want %v", len(b), width)
|
||||
}
|
||||
@@ -61,31 +63,37 @@ func TestBytePool(t *testing.T) {
|
||||
t.Fatalf("bytepool length invalid: got %v want %v", cap(b), capWidth)
|
||||
}
|
||||
|
||||
bufPool.Put(b)
|
||||
|
||||
// Close the channel so we can iterate over it.
|
||||
close(bufPool.c)
|
||||
bp.Put(b)
|
||||
|
||||
// Check the size of the pool.
|
||||
if uint64(len(bufPool.c)) != size {
|
||||
t.Fatalf("bytepool size invalid: got %v want %v", len(bufPool.c), size)
|
||||
if uint64(len(bp.c)) != size {
|
||||
t.Fatalf("bytepool size invalid: got %v want %v", len(bp.c), size)
|
||||
}
|
||||
|
||||
bufPoolNoCap := NewBytePoolCap(size, width, 0)
|
||||
// Check the width
|
||||
if bufPoolNoCap.Width() != width {
|
||||
t.Fatalf("bytepool width invalid: got %v want %v", bufPool.Width(), width)
|
||||
// lets drain the buf channel first before we validate invalid buffers.
|
||||
for i := uint64(0); i < size; i++ {
|
||||
bp.Get() // discard
|
||||
}
|
||||
|
||||
// Check with width cap
|
||||
if bufPoolNoCap.WidthCap() != 0 {
|
||||
t.Fatalf("bytepool capWidth invalid: got %v want %v", bufPool.WidthCap(), 0)
|
||||
// Try putting some invalid buffers into pool
|
||||
bp.Put(make([]byte, bp.w, bp.wcap-1)) // wrong capacity is rejected (less)
|
||||
bp.Put(make([]byte, bp.w, bp.wcap+1)) // wrong capacity is rejected (more)
|
||||
bp.Put(make([]byte, width)) // wrong capacity is rejected (very less)
|
||||
if len(bp.c) > 0 {
|
||||
t.Fatal("bytepool should have rejected invalid packets")
|
||||
}
|
||||
b = bufPoolNoCap.Get()
|
||||
|
||||
// Try putting a short slice into pool
|
||||
bp.Put(make([]byte, bp.w, bp.wcap)[:2])
|
||||
if len(bp.c) != 1 {
|
||||
t.Fatal("bytepool should have accepted short slice with sufficient capacity")
|
||||
}
|
||||
|
||||
b = bp.Get()
|
||||
if len(b) != width {
|
||||
t.Fatalf("bytepool length invalid: got %v want %v", len(b), width)
|
||||
}
|
||||
if cap(b) != width {
|
||||
t.Fatalf("bytepool length invalid: got %v want %v", cap(b), width)
|
||||
}
|
||||
|
||||
// Close the channel.
|
||||
close(bp.c)
|
||||
}
|
||||
|
||||
@@ -259,5 +259,6 @@ const (
|
||||
// http headers sent to webhook targets
|
||||
const (
|
||||
// Reports the version of MinIO server
|
||||
MinIOVersion = "x-minio-version"
|
||||
MinIOVersion = "x-minio-version"
|
||||
WebhookEventPayloadCount = "x-minio-webhook-payload-count"
|
||||
)
|
||||
|
||||
@@ -48,7 +48,7 @@ type conn interface {
|
||||
// CreateKey creates a new key at the KMS with the given key ID.
|
||||
CreateKey(context.Context, *CreateKeyRequest) error
|
||||
|
||||
ListKeyNames(context.Context, *ListRequest) ([]string, string, error)
|
||||
ListKeys(context.Context, *ListRequest) ([]madmin.KMSKeyInfo, string, error)
|
||||
|
||||
// GenerateKey generates a new data encryption key using the
|
||||
// key referenced by the key ID.
|
||||
|
||||
+10
-2
@@ -126,8 +126,16 @@ func (c *kesConn) Status(ctx context.Context) (map[string]madmin.ItemState, erro
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (c *kesConn) ListKeyNames(ctx context.Context, req *ListRequest) ([]string, string, error) {
|
||||
return c.client.ListKeys(ctx, req.Prefix, req.Limit)
|
||||
func (c *kesConn) ListKeys(ctx context.Context, req *ListRequest) ([]madmin.KMSKeyInfo, string, error) {
|
||||
names, continueAt, err := c.client.ListKeys(ctx, req.Prefix, req.Limit)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
keyInfos := make([]madmin.KMSKeyInfo, len(names))
|
||||
for i := range names {
|
||||
keyInfos[i].Name = names[i]
|
||||
}
|
||||
return keyInfos, continueAt, nil
|
||||
}
|
||||
|
||||
// CreateKey tries to create a new key at the KMS with the
|
||||
|
||||
+10
-8
@@ -213,13 +213,13 @@ func (k *KMS) CreateKey(ctx context.Context, req *CreateKeyRequest) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ListKeyNames returns a list of key names and a potential
|
||||
// ListKeys returns a list of keys with metadata and a potential
|
||||
// next name from where to continue a subsequent listing.
|
||||
func (k *KMS) ListKeyNames(ctx context.Context, req *ListRequest) ([]string, string, error) {
|
||||
func (k *KMS) ListKeys(ctx context.Context, req *ListRequest) ([]madmin.KMSKeyInfo, string, error) {
|
||||
if req.Prefix == "*" {
|
||||
req.Prefix = ""
|
||||
}
|
||||
return k.conn.ListKeyNames(ctx, req)
|
||||
return k.conn.ListKeys(ctx, req)
|
||||
}
|
||||
|
||||
// GenerateKey generates a new data key using the master key req.Name.
|
||||
@@ -320,7 +320,7 @@ func (c *kmsConn) Status(ctx context.Context) (map[string]madmin.ItemState, erro
|
||||
return stat, nil
|
||||
}
|
||||
|
||||
func (c *kmsConn) ListKeyNames(ctx context.Context, req *ListRequest) ([]string, string, error) {
|
||||
func (c *kmsConn) ListKeys(ctx context.Context, req *ListRequest) ([]madmin.KMSKeyInfo, string, error) {
|
||||
resp, err := c.client.ListKeys(ctx, &kms.ListRequest{
|
||||
Enclave: c.enclave,
|
||||
Prefix: req.Prefix,
|
||||
@@ -331,11 +331,13 @@ func (c *kmsConn) ListKeyNames(ctx context.Context, req *ListRequest) ([]string,
|
||||
return nil, "", errListingKeysFailed(err)
|
||||
}
|
||||
|
||||
names := make([]string, 0, len(resp.Items))
|
||||
for _, item := range resp.Items {
|
||||
names = append(names, item.Name)
|
||||
keyInfos := make([]madmin.KMSKeyInfo, len(resp.Items))
|
||||
for i, v := range resp.Items {
|
||||
keyInfos[i].Name = v.Name
|
||||
keyInfos[i].CreatedAt = v.CreatedAt
|
||||
keyInfos[i].CreatedBy = string(v.CreatedBy)
|
||||
}
|
||||
return names, resp.ContinueAt, nil
|
||||
return keyInfos, resp.ContinueAt, nil
|
||||
}
|
||||
|
||||
func (c *kmsConn) CreateKey(ctx context.Context, req *CreateKeyRequest) error {
|
||||
|
||||
@@ -95,12 +95,12 @@ func (secretKey) Status(context.Context) (map[string]madmin.ItemState, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListKeyNames returns a list of key names. The builtin KMS consists of just a single key.
|
||||
func (s secretKey) ListKeyNames(ctx context.Context, req *ListRequest) ([]string, string, error) {
|
||||
// ListKeys returns a list of keys with metadata. The builtin KMS consists of just a single key.
|
||||
func (s secretKey) ListKeys(ctx context.Context, req *ListRequest) ([]madmin.KMSKeyInfo, string, error) {
|
||||
if strings.HasPrefix(s.keyID, req.Prefix) && strings.HasPrefix(s.keyID, req.ContinueAt) {
|
||||
return []string{s.keyID}, "", nil
|
||||
return []madmin.KMSKeyInfo{{Name: s.keyID}}, "", nil
|
||||
}
|
||||
return []string{}, "", nil
|
||||
return []madmin.KMSKeyInfo{}, "", nil
|
||||
}
|
||||
|
||||
// CreateKey returns ErrKeyExists unless req.Name is equal to the secretKey name.
|
||||
|
||||
+12
-4
@@ -22,11 +22,19 @@ import (
|
||||
"net/http"
|
||||
"slices"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/pkg/v3/wildcard"
|
||||
)
|
||||
|
||||
var (
|
||||
// StubCreatedAt is a constant timestamp for testing
|
||||
StubCreatedAt = time.Date(2024, time.January, 1, 15, 0, 0, 0, time.UTC)
|
||||
// StubCreatedBy is a constant created identity for testing
|
||||
StubCreatedBy = "MinIO"
|
||||
)
|
||||
|
||||
// NewStub returns a stub of KMS for testing
|
||||
func NewStub(defaultKeyName string) *KMS {
|
||||
return &KMS{
|
||||
@@ -64,15 +72,15 @@ func (s StubKMS) Status(context.Context) (map[string]madmin.ItemState, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListKeyNames returns a list of key names.
|
||||
func (s StubKMS) ListKeyNames(ctx context.Context, req *ListRequest) ([]string, string, error) {
|
||||
matches := []string{}
|
||||
// ListKeys returns a list of keys with metadata.
|
||||
func (s StubKMS) ListKeys(ctx context.Context, req *ListRequest) ([]madmin.KMSKeyInfo, string, error) {
|
||||
matches := []madmin.KMSKeyInfo{}
|
||||
if req.Prefix == "" {
|
||||
req.Prefix = "*"
|
||||
}
|
||||
for _, keyName := range s.KeyNames {
|
||||
if wildcard.MatchAsPatternPrefix(req.Prefix, keyName) {
|
||||
matches = append(matches, keyName)
|
||||
matches = append(matches, madmin.KMSKeyInfo{Name: keyName, CreatedAt: StubCreatedAt, CreatedBy: StubCreatedBy})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ package logger
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
@@ -422,9 +421,7 @@ func sendLog(ctx context.Context, entry log.Entry) {
|
||||
for _, t := range systemTgts {
|
||||
if err := t.Send(ctx, entry); err != nil {
|
||||
if consoleTgt != nil { // Sending to the console never fails
|
||||
b, _ := json.Marshal(entry)
|
||||
entry.Trace.Message = fmt.Sprintf("sendLog: event %s was not sent to Logger target (%v): %v", string(b), t.String(), err.Error())
|
||||
consoleTgt.Send(ctx, entry)
|
||||
consoleTgt.Send(ctx, errToEntry(ctx, "logging", fmt.Errorf("unable to send log event to Logger target (%s): %v", t.String(), err), entry.Level))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -42,7 +44,7 @@ import (
|
||||
|
||||
const (
|
||||
// Timeout for the webhook http call
|
||||
webhookCallTimeout = 3 * time.Second
|
||||
webhookCallTimeout = 5 * time.Second
|
||||
|
||||
// maxWorkers is the maximum number of concurrent http loggers
|
||||
maxWorkers = 16
|
||||
@@ -219,17 +221,23 @@ func (h *Target) initMemoryStore(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Target) send(ctx context.Context, payload []byte, payloadType string, timeout time.Duration) (err error) {
|
||||
func (h *Target) send(ctx context.Context, payload []byte, payloadCount int, payloadType string, timeout time.Duration) (err error) {
|
||||
h.failedMessages.Add(int64(payloadCount))
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
h.status.Store(statusOffline)
|
||||
if xnet.IsNetworkOrHostDown(err, false) {
|
||||
h.status.Store(statusOffline)
|
||||
}
|
||||
} else {
|
||||
h.failedMessages.Add(-int64(payloadCount))
|
||||
h.status.Store(statusOnline)
|
||||
}
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
|
||||
h.Endpoint(), bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
@@ -238,6 +246,7 @@ func (h *Target) send(ctx context.Context, payload []byte, payloadType string, t
|
||||
if payloadType != "" {
|
||||
req.Header.Set(xhttp.ContentType, payloadType)
|
||||
}
|
||||
req.Header.Set(xhttp.WebhookEventPayloadCount, strconv.Itoa(payloadCount))
|
||||
req.Header.Set(xhttp.MinIOVersion, xhttp.GlobalMinIOVersion)
|
||||
req.Header.Set(xhttp.MinioDeploymentID, xhttp.GlobalDeploymentID)
|
||||
|
||||
@@ -353,6 +362,7 @@ func (h *Target) startQueueProcessor(ctx context.Context, mainWorker bool) {
|
||||
return
|
||||
}
|
||||
|
||||
var count int
|
||||
if !isTick {
|
||||
h.totalMessages.Add(1)
|
||||
|
||||
@@ -366,13 +376,15 @@ func (h *Target) startQueueProcessor(ctx context.Context, mainWorker bool) {
|
||||
h.failedMessages.Add(1)
|
||||
continue
|
||||
}
|
||||
count++
|
||||
} else {
|
||||
entries = append(entries, entry)
|
||||
count++
|
||||
}
|
||||
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
if len(entries) != h.batchSize {
|
||||
if len(h.logCh) > 0 || len(globalBuffer) > 0 || len(entries) == 0 {
|
||||
if count != h.batchSize {
|
||||
if len(h.logCh) > 0 || len(globalBuffer) > 0 || count == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -406,16 +418,15 @@ func (h *Target) startQueueProcessor(ctx context.Context, mainWorker bool) {
|
||||
}
|
||||
|
||||
if !isDirQueue {
|
||||
err = h.send(ctx, buf.Bytes(), h.payloadType, webhookCallTimeout)
|
||||
err = h.send(ctx, buf.Bytes(), count, h.payloadType, webhookCallTimeout)
|
||||
} else {
|
||||
err = h.store.PutMultiple(entries)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
h.config.LogOnceIf(
|
||||
context.Background(),
|
||||
fmt.Errorf("unable to send webhook log entry to '%s' err '%w'", name, err),
|
||||
fmt.Errorf("unable to send webhook log entry(s) to '%s' err '%w': %d", name, err, count),
|
||||
name,
|
||||
)
|
||||
|
||||
@@ -428,7 +439,7 @@ func (h *Target) startQueueProcessor(ctx context.Context, mainWorker bool) {
|
||||
}
|
||||
|
||||
entries = make([]interface{}, 0)
|
||||
|
||||
count = 0
|
||||
if !isDirQueue {
|
||||
buf.Reset()
|
||||
}
|
||||
@@ -529,14 +540,16 @@ func (h *Target) SendFromStore(key store.Key) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
h.failedMessages.Add(1)
|
||||
defer func() {
|
||||
if err == nil {
|
||||
h.failedMessages.Add(-1)
|
||||
count := 1
|
||||
v := strings.Split(key.Name, ":")
|
||||
if len(v) == 2 {
|
||||
count, err = strconv.Atoi(v[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if err := h.send(context.Background(), eventData, h.payloadType, webhookCallTimeout); err != nil {
|
||||
if err := h.send(context.Background(), eventData, count, h.payloadType, webhookCallTimeout); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
none = "none"
|
||||
use = "use"
|
||||
none = "none"
|
||||
use = "use"
|
||||
ignore = "ignore"
|
||||
|
||||
defaultRecordDelimiter = "\n"
|
||||
defaultFieldDelimiter = ","
|
||||
@@ -92,11 +93,22 @@ func (args *ReaderArgs) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (er
|
||||
}
|
||||
switch tagName {
|
||||
case "FileHeaderInfo":
|
||||
args.FileHeaderInfo = strings.ToLower(s)
|
||||
s = strings.ToLower(s)
|
||||
if len(s) != 0 {
|
||||
if s != none && s != use && s != ignore {
|
||||
return errors.New("unsupported FileHeaderInfo")
|
||||
}
|
||||
args.FileHeaderInfo = s
|
||||
}
|
||||
|
||||
case "RecordDelimiter":
|
||||
args.RecordDelimiter = s
|
||||
if len(s) != 0 {
|
||||
args.RecordDelimiter = s
|
||||
}
|
||||
case "FieldDelimiter":
|
||||
args.FieldDelimiter = s
|
||||
if len(s) != 0 {
|
||||
args.FieldDelimiter = s
|
||||
}
|
||||
case "QuoteCharacter":
|
||||
if utf8.RuneCountInString(s) > 1 {
|
||||
return fmt.Errorf("unsupported QuoteCharacter '%v'", s)
|
||||
@@ -112,7 +124,9 @@ func (args *ReaderArgs) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (er
|
||||
return fmt.Errorf("unsupported QuoteEscapeCharacter '%v'", s)
|
||||
}
|
||||
case "Comments":
|
||||
args.CommentCharacter = s
|
||||
if len(s) != 0 {
|
||||
args.CommentCharacter = s
|
||||
}
|
||||
default:
|
||||
return errors.New("unrecognized option")
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package store
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -107,17 +108,18 @@ func (store *QueueStore[_]) Delete() error {
|
||||
|
||||
// PutMultiple - puts an item to the store.
|
||||
func (store *QueueStore[I]) PutMultiple(item []I) error {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
if uint64(len(store.entries)) >= store.entryLimit {
|
||||
return errLimitExceeded
|
||||
}
|
||||
// Generate a new UUID for the key.
|
||||
key, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return store.multiWrite(key.String(), item)
|
||||
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
if uint64(len(store.entries)) >= store.entryLimit {
|
||||
return errLimitExceeded
|
||||
}
|
||||
return store.multiWrite(fmt.Sprintf("%d:%s", len(item), key.String()), item)
|
||||
}
|
||||
|
||||
// multiWrite - writes an item to the directory.
|
||||
|
||||
Reference in New Issue
Block a user