mirror of
https://github.com/pgsty/minio.git
synced 2026-08-10 16:23:28 +03:00
Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b5f28e99b | |||
| b69bcdcdc4 | |||
| e385f54185 | |||
| 9a4d003ac7 | |||
| d5656eeb65 | |||
| 8edc67b0a9 | |||
| 18b0b7299a | |||
| 09b0e7133d | |||
| 6d08af61a0 | |||
| a7577da768 | |||
| 325fd80687 | |||
| 09626d78ff | |||
| 8f03c6e0db | |||
| 2c2f5d871c | |||
| c599c11e70 | |||
| ef06644799 | |||
| 6769d4dd54 | |||
| f3e7c42425 | |||
| f46bee242c | |||
| d7520f0ae6 | |||
| 44b70eb646 | |||
| 828d4df6f0 | |||
| 467714f33b | |||
| f8696cc8f6 | |||
| 9a7c7ab2d0 | |||
| 40fb3371fa | |||
| 51874a5776 | |||
| 62ce52c8fd | |||
| 2bdb9511bd | |||
| 9a012a53ef | |||
| 0aae0180fb | |||
| 1dd8ef09a6 | |||
| 95032e4710 | |||
| b1351e2dee | |||
| 30c2596512 | |||
| 2b5e4b853c | |||
| 85bcb5874a | |||
| 92788e4cf4 | |||
| 8a698fef71 | |||
| b49ce1713f | |||
| c2b54d92f6 | |||
| f965434022 | |||
| a3ac62596c | |||
| 2faba02d6b | |||
| ee158e1610 | |||
| fa68efb1e7 | |||
| 8c53a4405a | |||
| c32f699105 | |||
| 53aa8f5650 | |||
| 56887f3208 | |||
| 92180bc793 | |||
| 22aa16ab12 | |||
| 526b829a09 | |||
| c44f311c4f | |||
| 9ea5d08ecd | |||
| 35deb1a8e2 | |||
| c7f7c47388 | |||
| cb7dab17cb | |||
| cd419a35fe | |||
| e06168596f | |||
| 4c8197a119 | |||
| 23c10350f3 | |||
| b6e98aed01 | |||
| 00dcba9ddd | |||
| 607cafadbc | |||
| 68dde2359f | |||
| f9dbf41e27 | |||
| 7405760f44 | |||
| 7e4a6b4bcd | |||
| b5791e6f28 | |||
| 00cb58eaf3 | |||
| f961ec4aaf | |||
| 134db72bb7 | |||
| 6fd0b434e2 | |||
| effe21f3eb | |||
| 1118b285d3 | |||
| 912a0031b7 | |||
| a14e192376 | |||
| f8e15e7d09 | |||
| 7b9f9e0628 | |||
| ac8e9ce04f | |||
| cfd8645843 | |||
| 0c068b15c7 | |||
| 30a466aa71 | |||
| 4d94609c44 | |||
| 6b63123ca9 |
@@ -1,8 +0,0 @@
|
||||
|
||||
[codespell]
|
||||
# certs_test.go - has lots of ceritificates.
|
||||
skip = go.mod,go.sum,*.txt,LICENSE,*.zip,.git,*.pdf,*.svg,.codespellrc,CREDITS,certs_test.go
|
||||
check-hidden = true
|
||||
ignore-regex = \b(newfolder/afile|filterIn|HelpES)\b
|
||||
ignore-words-list = inout,bui,to,bu,te,ot,toi,ist,parms,flate
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Codespell
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
codespell:
|
||||
name: Check for spelling errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Codespell
|
||||
uses: codespell-project/actions-codespell@v2
|
||||
@@ -25,8 +25,8 @@ if [ ! -f ./mc ]; then
|
||||
fi
|
||||
|
||||
(
|
||||
cd /tmp
|
||||
go install github.com/minio/minio/docs/debugging/s3-check-md5@master
|
||||
cd ./docs/debugging/s3-check-md5
|
||||
go install -v
|
||||
)
|
||||
|
||||
export RELEASE=RELEASE.2023-08-29T23-07-35Z
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: Test GitHub Action
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check spelling of repo
|
||||
uses: crate-ci/typos@master
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
[files]
|
||||
extend-exclude = [
|
||||
".git/",
|
||||
"docs/",
|
||||
]
|
||||
ignore-hidden = false
|
||||
|
||||
[default]
|
||||
extend-ignore-re = [
|
||||
"Patrick Collison",
|
||||
"Copyright 2014 Unknwon",
|
||||
"[0-9A-Za-z/+=]{64}",
|
||||
"ZXJuZXQxDjAMBgNVBA-some-junk-Q4wDAYDVQQLEwVNaW5pbzEOMAwGA1UEAxMF",
|
||||
"eyJmb28iOiJiYXIifQ",
|
||||
'http\.Header\{"X-Amz-Server-Side-Encryptio":',
|
||||
'sessionToken',
|
||||
]
|
||||
|
||||
[default.extend-words]
|
||||
"encrypter" = "encrypter"
|
||||
"requestor" = "requestor"
|
||||
|
||||
[default.extend-identifiers]
|
||||
"bui" = "bui"
|
||||
"toi" = "toi"
|
||||
"ot" = "ot"
|
||||
"dm2nd" = "dm2nd"
|
||||
"HashiCorp" = "HashiCorp"
|
||||
"ParseND" = "ParseND"
|
||||
"ParseNDStream" = "ParseNDStream"
|
||||
"TestGetPartialObjectMisAligned" = "TestGetPartialObjectMisAligned"
|
||||
@@ -16432,213 +16432,6 @@ https://github.com/matttproud/golang_protobuf_extensions
|
||||
|
||||
================================================================
|
||||
|
||||
github.com/matttproud/golang_protobuf_extensions/v2
|
||||
https://github.com/matttproud/golang_protobuf_extensions/v2
|
||||
----------------------------------------------------------------
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
================================================================
|
||||
|
||||
github.com/miekg/dns
|
||||
https://github.com/miekg/dns
|
||||
----------------------------------------------------------------
|
||||
@@ -18271,8 +18064,8 @@ https://github.com/minio/highwayhash
|
||||
|
||||
================================================================
|
||||
|
||||
github.com/minio/kes-go
|
||||
https://github.com/minio/kes-go
|
||||
github.com/minio/kms-go
|
||||
https://github.com/minio/kms-go
|
||||
----------------------------------------------------------------
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
@@ -6,7 +6,8 @@ GOARCH := $(shell go env GOARCH)
|
||||
GOOS := $(shell go env GOOS)
|
||||
|
||||
VERSION ?= $(shell git describe --tags)
|
||||
TAG ?= "quay.io/minio/minio:$(VERSION)"
|
||||
REPO ?= quay.io/minio
|
||||
TAG ?= $(REPO)/minio:$(VERSION)
|
||||
|
||||
GOLANGCI_DIR = .bin/golangci/$(GOLANGCI_VERSION)
|
||||
GOLANGCI = $(GOLANGCI_DIR)/golangci-lint
|
||||
@@ -144,12 +145,11 @@ hotfix-vars:
|
||||
$(eval LDFLAGS := $(shell MINIO_RELEASE="RELEASE" MINIO_HOTFIX="hotfix.$(shell git rev-parse --short HEAD)" go run buildscripts/gen-ldflags.go $(shell git describe --tags --abbrev=0 | \
|
||||
sed 's#RELEASE\.\([0-9]\+\)-\([0-9]\+\)-\([0-9]\+\)T\([0-9]\+\)-\([0-9]\+\)-\([0-9]\+\)Z#\1-\2-\3T\4:\5:\6Z#')))
|
||||
$(eval VERSION := $(shell git describe --tags --abbrev=0).hotfix.$(shell git rev-parse --short HEAD))
|
||||
$(eval TAG := "minio/minio:$(VERSION)")
|
||||
|
||||
hotfix: hotfix-vars clean install ## builds minio binary with hotfix tags
|
||||
@wget -q -c https://github.com/minio/pkger/releases/download/v2.2.0/pkger_2.2.0_linux_amd64.deb
|
||||
@wget -q -c https://github.com/minio/pkger/releases/download/v2.2.1/pkger_2.2.1_linux_amd64.deb
|
||||
@wget -q -c https://raw.githubusercontent.com/minio/minio-service/v1.0.1/linux-systemd/distributed/minio.service
|
||||
@sudo apt install ./pkger_2.2.0_linux_amd64.deb --yes
|
||||
@sudo apt install ./pkger_2.2.1_linux_amd64.deb --yes
|
||||
@mkdir -p minio-release/$(GOOS)-$(GOARCH)/archive
|
||||
@cp -af ./minio minio-release/$(GOOS)-$(GOARCH)/minio
|
||||
@cp -af ./minio minio-release/$(GOOS)-$(GOARCH)/minio.$(VERSION)
|
||||
@@ -177,9 +177,9 @@ docker: build ## builds minio docker container
|
||||
@docker build -q --no-cache -t $(TAG) . -f Dockerfile
|
||||
|
||||
install-race: checks ## builds minio to $(PWD)
|
||||
@echo "Building minio binary to './minio'"
|
||||
@echo "Building minio binary with -race to './minio'"
|
||||
@GORACE=history_size=7 CGO_ENABLED=1 go build -tags kqueue -race -trimpath --ldflags "$(LDFLAGS)" -o $(PWD)/minio 1>/dev/null
|
||||
@echo "Installing minio binary to '$(GOPATH)/bin/minio'"
|
||||
@echo "Installing minio binary with -race to '$(GOPATH)/bin/minio'"
|
||||
@mkdir -p $(GOPATH)/bin && cp -f $(PWD)/minio $(GOPATH)/bin/minio
|
||||
|
||||
install: build ## builds minio and installs it to $GOPATH/bin.
|
||||
|
||||
@@ -123,7 +123,7 @@ You can also connect using any S3-compatible tool, such as the MinIO Client `mc`
|
||||
|
||||
## Install from Source
|
||||
|
||||
Use the following commands to compile and run a standalone MinIO server from source. Source installation is only intended for developers and advanced users. If you do not have a working Golang environment, please follow [How to install Golang](https://golang.org/doc/install). Minimum version required is [go1.19](https://golang.org/dl/#stable)
|
||||
Use the following commands to compile and run a standalone MinIO server from source. Source installation is only intended for developers and advanced users. If you do not have a working Golang environment, please follow [How to install Golang](https://golang.org/doc/install). Minimum version required is [go1.21](https://golang.org/dl/#stable)
|
||||
|
||||
```sh
|
||||
go install github.com/minio/minio@latest
|
||||
|
||||
@@ -44,7 +44,6 @@ func main() {
|
||||
opts := madmin.HealOpts{
|
||||
Recursive: true, // recursively heal all objects at 'prefix'
|
||||
Remove: true, // remove content that has lost quorum and not recoverable
|
||||
Recreate: true, // rewrite all old non-inlined xl.meta to new xl.meta
|
||||
ScanMode: madmin.HealNormalScan, // by default do not do 'deep' scanning
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,11 @@ function verify_rewrite() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
go install github.com/minio/minio/docs/debugging/s3-check-md5@master
|
||||
(
|
||||
cd ./docs/debugging/s3-check-md5
|
||||
go install -v
|
||||
)
|
||||
|
||||
if ! s3-check-md5 \
|
||||
-debug \
|
||||
-versions \
|
||||
|
||||
@@ -83,7 +83,7 @@ function start_minio_3_node() {
|
||||
}
|
||||
|
||||
function check_online() {
|
||||
if grep -q 'Unable to initialize sub-systems' ${WORK_DIR}/dist-minio-*.log; then
|
||||
if ! grep -q 'Status:' ${WORK_DIR}/dist-minio-*.log; then
|
||||
echo "1"
|
||||
fi
|
||||
}
|
||||
@@ -109,6 +109,7 @@ function perform_test() {
|
||||
|
||||
rm -rf ${WORK_DIR}/${1}/*/
|
||||
|
||||
set -x
|
||||
start_minio_3_node 120 $2
|
||||
|
||||
rv=$(check_online)
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/klauspost/compress/zip"
|
||||
"github.com/minio/kes-go"
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio-go/v7/pkg/tags"
|
||||
"github.com/minio/minio/internal/bucket/lifecycle"
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/kes-go"
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/minio/internal/config"
|
||||
|
||||
+16
-15
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/klauspost/compress/zip"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/minio/internal/cachevalue"
|
||||
"github.com/minio/minio/internal/config/dns"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/mux"
|
||||
@@ -621,6 +622,11 @@ func (a adminAPIHandlers) AddServiceAccount(w http.ResponseWriter, r *http.Reque
|
||||
return
|
||||
}
|
||||
|
||||
if createReq.AccessKey == globalActiveCred.AccessKey {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAddUserInvalidArgument), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
targetGroups []string
|
||||
err error
|
||||
@@ -1067,6 +1073,10 @@ func (a adminAPIHandlers) DeleteServiceAccount(w http.ResponseWriter, r *http.Re
|
||||
return
|
||||
}
|
||||
|
||||
if serviceAccount == siteReplicatorSvcAcc && globalSiteReplicationSys.isEnabled() {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidArgument), r.URL)
|
||||
return
|
||||
}
|
||||
// We do not care if service account is readable or not at this point,
|
||||
// since this is a delete call we shall allow it to be deleted if possible.
|
||||
svcAccount, _, err := globalIAMSys.GetServiceAccount(ctx, serviceAccount)
|
||||
@@ -1188,26 +1198,17 @@ func (a adminAPIHandlers) AccountInfoHandler(w http.ResponseWriter, r *http.Requ
|
||||
return rd, wr
|
||||
}
|
||||
|
||||
bucketStorageCache.Once.Do(func() {
|
||||
// Set this to 10 secs since its enough, as scanner
|
||||
// does not update the bucket usage values frequently.
|
||||
bucketStorageCache.TTL = 10 * time.Second
|
||||
|
||||
// Rely on older value if usage loading fails from disk.
|
||||
bucketStorageCache.Relax = true
|
||||
bucketStorageCache.Update = func() (interface{}, error) {
|
||||
bucketStorageCache.InitOnce(10*time.Second,
|
||||
cachevalue.Opts{ReturnLastGood: true, NoWait: true},
|
||||
func() (DataUsageInfo, error) {
|
||||
ctx, done := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer done()
|
||||
|
||||
return loadDataUsageFromBackend(ctx, objectAPI)
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
var dataUsageInfo DataUsageInfo
|
||||
v, _ := bucketStorageCache.Get()
|
||||
if v != nil {
|
||||
dataUsageInfo, _ = v.(DataUsageInfo)
|
||||
}
|
||||
dataUsageInfo, _ := bucketStorageCache.Get()
|
||||
|
||||
// If etcd, dns federation configured list buckets from etcd.
|
||||
var err error
|
||||
|
||||
+42
-22
@@ -1192,7 +1192,7 @@ type healInitParams struct {
|
||||
}
|
||||
|
||||
// extractHealInitParams - Validates params for heal init API.
|
||||
func extractHealInitParams(vars map[string]string, qParms url.Values, r io.Reader) (hip healInitParams, err APIErrorCode) {
|
||||
func extractHealInitParams(vars map[string]string, qParams url.Values, r io.Reader) (hip healInitParams, err APIErrorCode) {
|
||||
hip.bucket = vars[mgmtBucket]
|
||||
hip.objPrefix = vars[mgmtPrefix]
|
||||
|
||||
@@ -1213,13 +1213,13 @@ func extractHealInitParams(vars map[string]string, qParms url.Values, r io.Reade
|
||||
return
|
||||
}
|
||||
|
||||
if len(qParms[mgmtClientToken]) > 0 {
|
||||
hip.clientToken = qParms[mgmtClientToken][0]
|
||||
if len(qParams[mgmtClientToken]) > 0 {
|
||||
hip.clientToken = qParams[mgmtClientToken][0]
|
||||
}
|
||||
if _, ok := qParms[mgmtForceStart]; ok {
|
||||
if _, ok := qParams[mgmtForceStart]; ok {
|
||||
hip.forceStart = true
|
||||
}
|
||||
if _, ok := qParms[mgmtForceStop]; ok {
|
||||
if _, ok := qParams[mgmtForceStop]; ok {
|
||||
hip.forceStop = true
|
||||
}
|
||||
|
||||
@@ -1984,7 +1984,6 @@ func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// The ConsoleLogHandler handler sends console logs to the connected HTTP client.
|
||||
func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
objectAPI, _ := validateAdminReq(ctx, w, r, policy.ConsoleLogAdminAction)
|
||||
if objectAPI == nil {
|
||||
return
|
||||
@@ -2009,44 +2008,65 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque
|
||||
|
||||
setEventStreamHeaders(w)
|
||||
|
||||
logCh := make(chan log.Info, 4000)
|
||||
|
||||
logCh := make(chan log.Info, 1000)
|
||||
peers, _ := newPeerRestClients(globalEndpoints)
|
||||
|
||||
encodedCh := make(chan []byte, 1000+len(peers)*1000)
|
||||
err = globalConsoleSys.Subscribe(logCh, ctx.Done(), node, limitLines, logKind, nil)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
// Convert local entries to JSON
|
||||
go func() {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case li := <-logCh:
|
||||
if !li.SendLog(node, logKind) {
|
||||
continue
|
||||
}
|
||||
buf.Reset()
|
||||
if err := enc.Encode(li); err != nil {
|
||||
continue
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case encodedCh <- append(grid.GetByteBuffer()[:0], buf.Bytes()...):
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Collect from matching peers
|
||||
for _, peer := range peers {
|
||||
if peer == nil {
|
||||
continue
|
||||
}
|
||||
if node == "" || strings.EqualFold(peer.host.Name, node) {
|
||||
peer.ConsoleLog(logCh, ctx.Done())
|
||||
peer.ConsoleLog(ctx, logKind, encodedCh)
|
||||
}
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
|
||||
keepAliveTicker := time.NewTicker(500 * time.Millisecond)
|
||||
defer keepAliveTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case log, ok := <-logCh:
|
||||
case log, ok := <-encodedCh:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if log.SendLog(node, logKind) {
|
||||
if err := enc.Encode(log); err != nil {
|
||||
return
|
||||
}
|
||||
if len(logCh) == 0 {
|
||||
// Flush if nothing is queued
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
_, err = w.Write(log)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
grid.PutByteBuffer(log)
|
||||
if len(logCh) == 0 {
|
||||
// Flush if nothing is queued
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
case <-keepAliveTicker.C:
|
||||
if len(logCh) > 0 {
|
||||
|
||||
@@ -348,7 +348,7 @@ func TestExtractHealInitParams(t *testing.T) {
|
||||
}
|
||||
return v
|
||||
}
|
||||
qParmsArr := []url.Values{
|
||||
qParamsArr := []url.Values{
|
||||
// Invalid cases
|
||||
mkParams("", true, true),
|
||||
mkParams("111", true, true),
|
||||
@@ -373,9 +373,9 @@ func TestExtractHealInitParams(t *testing.T) {
|
||||
body := `{"recursive": false, "dryRun": true, "remove": false, "scanMode": 0}`
|
||||
|
||||
// Test all combinations!
|
||||
for pIdx, parms := range qParmsArr {
|
||||
for pIdx, params := range qParamsArr {
|
||||
for vIdx, vars := range varsArr {
|
||||
_, err := extractHealInitParams(vars, parms, bytes.NewReader([]byte(body)))
|
||||
_, err := extractHealInitParams(vars, params, bytes.NewReader([]byte(body)))
|
||||
isErrCase := false
|
||||
if pIdx < 4 || vIdx < 1 {
|
||||
isErrCase = true
|
||||
|
||||
@@ -713,7 +713,7 @@ func (h *healSequence) queueHealTask(source healSource, healType madmin.HealItem
|
||||
select {
|
||||
case globalBackgroundHealRoutine.tasks <- task:
|
||||
if serverDebugLog {
|
||||
logger.Info("Task in the queue: %#v", task)
|
||||
fmt.Printf("Task in the queue: %#v\n", task)
|
||||
}
|
||||
default:
|
||||
// task queue is full, no more workers, we shall move on and heal later.
|
||||
@@ -730,7 +730,7 @@ func (h *healSequence) queueHealTask(source healSource, healType madmin.HealItem
|
||||
select {
|
||||
case globalBackgroundHealRoutine.tasks <- task:
|
||||
if serverDebugLog {
|
||||
logger.Info("Task in the queue: %#v", task)
|
||||
fmt.Printf("Task in the queue: %#v\n", task)
|
||||
}
|
||||
case <-h.ctx.Done():
|
||||
return nil
|
||||
|
||||
+9
-18
@@ -19,9 +19,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/klauspost/compress/gzhttp"
|
||||
"github.com/klauspost/compress/gzip"
|
||||
@@ -69,14 +66,6 @@ func (h hFlag) Has(flag hFlag) bool {
|
||||
return h&flag != 0
|
||||
}
|
||||
|
||||
func getHandlerName(f http.HandlerFunc) string {
|
||||
name := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
|
||||
name = strings.TrimPrefix(name, "github.com/minio/minio/cmd.adminAPIHandlers.")
|
||||
name = strings.TrimSuffix(name, "Handler-fm")
|
||||
name = strings.TrimSuffix(name, "-fm")
|
||||
return name
|
||||
}
|
||||
|
||||
// adminMiddleware performs some common admin handler functionality for all
|
||||
// handlers:
|
||||
//
|
||||
@@ -86,9 +75,13 @@ func getHandlerName(f http.HandlerFunc) string {
|
||||
//
|
||||
// - sets up call to send AuditLog
|
||||
//
|
||||
// Note that, while this is a middleware function (i.e. it takes a handler
|
||||
// function and returns one), due to flags being passed based on required
|
||||
// conditions, it is done per-"handler function registration" in the router.
|
||||
// While this is a middleware function (i.e. it takes a handler function and
|
||||
// returns one), due to flags being passed based on required conditions, it is
|
||||
// done per-"handler function registration" in the router.
|
||||
//
|
||||
// The passed in handler function must be a method of `adminAPIHandlers` for the
|
||||
// name displayed in logs and trace to be accurate. The name is extracted via
|
||||
// reflection.
|
||||
//
|
||||
// When no flags are passed, gzip compression, http tracing of headers and
|
||||
// checking of object layer availability are all enabled. Use flags to modify
|
||||
@@ -100,10 +93,8 @@ func adminMiddleware(f http.HandlerFunc, flags ...hFlag) http.HandlerFunc {
|
||||
handlerFlags |= flag
|
||||
}
|
||||
|
||||
// Get name of the handler using reflection. NOTE: The passed in handler
|
||||
// function must be a method of `adminAPIHandlers` for this extraction to
|
||||
// work as expected.
|
||||
handlerName := getHandlerName(f)
|
||||
// Get name of the handler using reflection.
|
||||
handlerName := getHandlerName(f, "adminAPIHandlers")
|
||||
|
||||
var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
|
||||
// Update request context with `logger.ReqInfo`.
|
||||
|
||||
+21
-4
@@ -390,7 +390,7 @@ const (
|
||||
ErrParseExpectedIdentForGroupName
|
||||
ErrParseExpectedIdentForAlias
|
||||
ErrParseUnsupportedCallWithStar
|
||||
ErrParseNonUnaryAgregateFunctionCall
|
||||
ErrParseNonUnaryAggregateFunctionCall
|
||||
ErrParseMalformedJoin
|
||||
ErrParseExpectedIdentForAt
|
||||
ErrParseAsteriskIsNotAloneInSelectList
|
||||
@@ -433,6 +433,9 @@ const (
|
||||
// New Codes for GetObjectAttributes and GetObjectVersionAttributes
|
||||
ErrInvalidAttributeName
|
||||
|
||||
ErrAdminNoAccessKey
|
||||
ErrAdminNoSecretKey
|
||||
|
||||
apiErrCodeEnd // This is used only for the testing code
|
||||
)
|
||||
|
||||
@@ -1359,6 +1362,16 @@ var errorCodes = errorCodeMap{
|
||||
Description: "The secret key is invalid.",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrAdminNoAccessKey: {
|
||||
Code: "XMinioAdminNoAccessKey",
|
||||
Description: "No access key was provided.",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrAdminNoSecretKey: {
|
||||
Code: "XMinioAdminNoSecretKey",
|
||||
Description: "No secret key was provided.",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrAdminConfigNoQuorum: {
|
||||
Code: "XMinioAdminConfigNoQuorum",
|
||||
Description: "Configuration update failed because server quorum was not met",
|
||||
@@ -1886,8 +1899,8 @@ var errorCodes = errorCodeMap{
|
||||
Description: "Only COUNT with (*) as a parameter is supported in the SQL expression.",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrParseNonUnaryAgregateFunctionCall: {
|
||||
Code: "ParseNonUnaryAgregateFunctionCall",
|
||||
ErrParseNonUnaryAggregateFunctionCall: {
|
||||
Code: "ParseNonUnaryAggregateFunctionCall",
|
||||
Description: "Only one argument is supported for aggregate functions in the SQL expression.",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
@@ -2008,7 +2021,7 @@ var errorCodes = errorCodeMap{
|
||||
},
|
||||
ErrAddUserInvalidArgument: {
|
||||
Code: "XMinioInvalidIAMCredentials",
|
||||
Description: "User is not allowed to be same as admin access key",
|
||||
Description: "Credential is not allowed to be same as admin access key",
|
||||
HTTPStatusCode: http.StatusForbidden,
|
||||
},
|
||||
ErrAdminResourceInvalidArgument: {
|
||||
@@ -2124,6 +2137,10 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) {
|
||||
apiErr = ErrAdminInvalidAccessKey
|
||||
case auth.ErrInvalidSecretKeyLength:
|
||||
apiErr = ErrAdminInvalidSecretKey
|
||||
case auth.ErrNoAccessKeyWithSecretKey:
|
||||
apiErr = ErrAdminNoAccessKey
|
||||
case auth.ErrNoSecretKeyWithAccessKey:
|
||||
apiErr = ErrAdminNoSecretKey
|
||||
case errInvalidStorageClass:
|
||||
apiErr = ErrInvalidStorageClass
|
||||
case errErasureReadQuorum:
|
||||
|
||||
+3
-3
@@ -891,7 +891,7 @@ func writeResponse(w http.ResponseWriter, statusCode int, response []byte, mType
|
||||
}
|
||||
// Similar check to http.checkWriteHeaderCode
|
||||
if statusCode < 100 || statusCode > 999 {
|
||||
logger.Error(fmt.Sprintf("invalid WriteHeader code %v", statusCode))
|
||||
logger.LogIf(context.Background(), fmt.Errorf("invalid WriteHeader code %v", statusCode))
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
setCommonHeaders(w)
|
||||
@@ -944,7 +944,7 @@ func writeSuccessResponseHeadersOnly(w http.ResponseWriter) {
|
||||
writeResponse(w, http.StatusOK, nil, mimeNone)
|
||||
}
|
||||
|
||||
// writeErrorRespone writes error headers
|
||||
// writeErrorResponse writes error headers
|
||||
func writeErrorResponse(ctx context.Context, w http.ResponseWriter, err APIError, reqURL *url.URL) {
|
||||
if err.HTTPStatusCode == http.StatusServiceUnavailable {
|
||||
// Set retry-after header to indicate user-agents to retry request after 120secs.
|
||||
@@ -961,7 +961,7 @@ func writeErrorResponse(ctx context.Context, w http.ResponseWriter, err APIError
|
||||
|
||||
// Similar check to http.checkWriteHeaderCode
|
||||
if err.HTTPStatusCode < 100 || err.HTTPStatusCode > 999 {
|
||||
logger.Error(fmt.Sprintf("invalid WriteHeader code %v from %v", err.HTTPStatusCode, err.Code))
|
||||
logger.LogIf(ctx, fmt.Errorf("invalid WriteHeader code %v from %v", err.HTTPStatusCode, err.Code))
|
||||
err.HTTPStatusCode = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
|
||||
+301
-161
@@ -18,14 +18,11 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/klauspost/compress/gzhttp"
|
||||
consoleapi "github.com/minio/console/api"
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/mux"
|
||||
"github.com/minio/pkg/v2/wildcard"
|
||||
"github.com/rs/cors"
|
||||
@@ -171,6 +168,87 @@ var rejectedBucketAPIs = []rejectedAPI{
|
||||
},
|
||||
}
|
||||
|
||||
// Set of s3 handler options as bit flags.
|
||||
type s3HFlag uint8
|
||||
|
||||
const (
|
||||
// when provided, disables Gzip compression.
|
||||
noGZS3HFlag = 1 << iota
|
||||
|
||||
// when provided, enables only tracing of headers. Otherwise, both headers
|
||||
// and body are traced.
|
||||
traceHdrsS3HFlag
|
||||
|
||||
// when provided, disables throttling via the `maxClients` middleware.
|
||||
noThrottleS3HFlag
|
||||
)
|
||||
|
||||
func (h s3HFlag) has(flag s3HFlag) bool {
|
||||
// Use bitwise-AND and check if the result is non-zero.
|
||||
return h&flag != 0
|
||||
}
|
||||
|
||||
// s3APIMiddleware - performs some common handler functionality for S3 API
|
||||
// handlers.
|
||||
//
|
||||
// It is set per-"handler function registration" in the router to allow for
|
||||
// behavior modification via flags.
|
||||
//
|
||||
// This middleware always calls `collectAPIStats` to collect API stats.
|
||||
//
|
||||
// The passed in handler function must be a method of `objectAPIHandlers` for
|
||||
// the name displayed in logs and trace to be accurate. The name is extracted
|
||||
// via reflection.
|
||||
//
|
||||
// When **no** flags are passed, the behavior is to trace both headers and body,
|
||||
// gzip the response and throttle the handler via `maxClients`. Each of these
|
||||
// can be disabled via the corresponding `s3HFlag`.
|
||||
//
|
||||
// CAUTION: for requests involving large req/resp bodies ensure to pass the
|
||||
// `traceHdrsS3HFlag`, otherwise both headers and body will be traced, causing
|
||||
// high memory usage!
|
||||
func s3APIMiddleware(f http.HandlerFunc, flags ...s3HFlag) http.HandlerFunc {
|
||||
// Collect all flags with bitwise-OR and assign operator
|
||||
var handlerFlags s3HFlag
|
||||
for _, flag := range flags {
|
||||
handlerFlags |= flag
|
||||
}
|
||||
|
||||
// Get name of the handler using reflection.
|
||||
handlerName := getHandlerName(f, "objectAPIHandlers")
|
||||
|
||||
var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
|
||||
// Wrap the actual handler with the appropriate tracing middleware.
|
||||
var tracedHandler http.HandlerFunc
|
||||
if handlerFlags.has(traceHdrsS3HFlag) {
|
||||
tracedHandler = httpTraceHdrs(f)
|
||||
} else {
|
||||
tracedHandler = httpTraceAll(f)
|
||||
}
|
||||
|
||||
// Skip wrapping with the gzip middleware if specified.
|
||||
var gzippedHandler http.HandlerFunc = tracedHandler
|
||||
if !handlerFlags.has(noGZS3HFlag) {
|
||||
gzippedHandler = gzipHandler(gzippedHandler)
|
||||
}
|
||||
|
||||
// Skip wrapping with throttling middleware if specified.
|
||||
var throttledHandler http.HandlerFunc = gzippedHandler
|
||||
if !handlerFlags.has(noThrottleS3HFlag) {
|
||||
throttledHandler = maxClients(throttledHandler)
|
||||
}
|
||||
|
||||
// Collect API stats using the API name got from reflection in
|
||||
// `getHandlerName`.
|
||||
statsCollectedHandler := collectAPIStats(handlerName, throttledHandler)
|
||||
|
||||
// Call the final handler.
|
||||
statsCollectedHandler(w, r)
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
// registerAPIRouter - registers S3 compatible APIs.
|
||||
func registerAPIRouter(router *mux.Router) {
|
||||
// Initialize API.
|
||||
@@ -208,12 +286,6 @@ func registerAPIRouter(router *mux.Router) {
|
||||
}
|
||||
routers = append(routers, apiRouter.PathPrefix("/{bucket}").Subrouter())
|
||||
|
||||
gz, err := gzhttp.NewWrapper(gzhttp.MinSize(1000), gzhttp.CompressionLevel(gzip.BestSpeed))
|
||||
if err != nil {
|
||||
// Static params, so this is very unlikely.
|
||||
logger.Fatal(err, "Unable to initialize server")
|
||||
}
|
||||
|
||||
for _, router := range routers {
|
||||
// Register all rejected object APIs
|
||||
for _, r := range rejectedObjAPIs {
|
||||
@@ -225,240 +297,307 @@ func registerAPIRouter(router *mux.Router) {
|
||||
|
||||
// Object operations
|
||||
// HeadObject
|
||||
router.Methods(http.MethodHead).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("headobject", maxClients(gz(httpTraceAll(api.HeadObjectHandler)))))
|
||||
router.Methods(http.MethodHead).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.HeadObjectHandler))
|
||||
|
||||
// GetObjectAttributes
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("getobjectattributes", maxClients(gz(httpTraceHdrs(api.GetObjectAttributesHandler))))).Queries("attributes", "")
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.GetObjectAttributesHandler, traceHdrsS3HFlag)).
|
||||
Queries("attributes", "")
|
||||
|
||||
// CopyObjectPart
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").
|
||||
HeadersRegexp(xhttp.AmzCopySource, ".*?(\\/|%2F).*?").
|
||||
HandlerFunc(collectAPIStats("copyobjectpart", maxClients(gz(httpTraceAll(api.CopyObjectPartHandler))))).
|
||||
HandlerFunc(s3APIMiddleware(api.CopyObjectPartHandler)).
|
||||
Queries("partNumber", "{partNumber:.*}", "uploadId", "{uploadId:.*}")
|
||||
// PutObjectPart
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("putobjectpart", maxClients(gz(httpTraceHdrs(api.PutObjectPartHandler))))).Queries("partNumber", "{partNumber:.*}", "uploadId", "{uploadId:.*}")
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.PutObjectPartHandler, traceHdrsS3HFlag)).
|
||||
Queries("partNumber", "{partNumber:.*}", "uploadId", "{uploadId:.*}")
|
||||
// ListObjectParts
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("listobjectparts", maxClients(gz(httpTraceAll(api.ListObjectPartsHandler))))).Queries("uploadId", "{uploadId:.*}")
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.ListObjectPartsHandler)).
|
||||
Queries("uploadId", "{uploadId:.*}")
|
||||
// CompleteMultipartUpload
|
||||
router.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("completemultipartupload", maxClients(gz(httpTraceAll(api.CompleteMultipartUploadHandler))))).Queries("uploadId", "{uploadId:.*}")
|
||||
router.Methods(http.MethodPost).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.CompleteMultipartUploadHandler)).
|
||||
Queries("uploadId", "{uploadId:.*}")
|
||||
// NewMultipartUpload
|
||||
router.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("newmultipartupload", maxClients(gz(httpTraceAll(api.NewMultipartUploadHandler))))).Queries("uploads", "")
|
||||
router.Methods(http.MethodPost).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.NewMultipartUploadHandler)).
|
||||
Queries("uploads", "")
|
||||
// AbortMultipartUpload
|
||||
router.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("abortmultipartupload", maxClients(gz(httpTraceAll(api.AbortMultipartUploadHandler))))).Queries("uploadId", "{uploadId:.*}")
|
||||
router.Methods(http.MethodDelete).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.AbortMultipartUploadHandler)).
|
||||
Queries("uploadId", "{uploadId:.*}")
|
||||
// GetObjectACL - this is a dummy call.
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("getobjectacl", maxClients(gz(httpTraceHdrs(api.GetObjectACLHandler))))).Queries("acl", "")
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.GetObjectACLHandler, traceHdrsS3HFlag)).
|
||||
Queries("acl", "")
|
||||
// PutObjectACL - this is a dummy call.
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("putobjectacl", maxClients(gz(httpTraceHdrs(api.PutObjectACLHandler))))).Queries("acl", "")
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.PutObjectACLHandler, traceHdrsS3HFlag)).
|
||||
Queries("acl", "")
|
||||
// GetObjectTagging
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("getobjecttagging", maxClients(gz(httpTraceHdrs(api.GetObjectTaggingHandler))))).Queries("tagging", "")
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.GetObjectTaggingHandler, traceHdrsS3HFlag)).
|
||||
Queries("tagging", "")
|
||||
// PutObjectTagging
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("putobjecttagging", maxClients(gz(httpTraceHdrs(api.PutObjectTaggingHandler))))).Queries("tagging", "")
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.PutObjectTaggingHandler, traceHdrsS3HFlag)).
|
||||
Queries("tagging", "")
|
||||
// DeleteObjectTagging
|
||||
router.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("deleteobjecttagging", maxClients(gz(httpTraceHdrs(api.DeleteObjectTaggingHandler))))).Queries("tagging", "")
|
||||
router.Methods(http.MethodDelete).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.DeleteObjectTaggingHandler, traceHdrsS3HFlag)).
|
||||
Queries("tagging", "")
|
||||
// SelectObjectContent
|
||||
router.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("selectobjectcontent", maxClients(gz(httpTraceHdrs(api.SelectObjectContentHandler))))).Queries("select", "").Queries("select-type", "2")
|
||||
router.Methods(http.MethodPost).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.SelectObjectContentHandler, traceHdrsS3HFlag)).
|
||||
Queries("select", "").Queries("select-type", "2")
|
||||
// GetObjectRetention
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("getobjectretention", maxClients(gz(httpTraceAll(api.GetObjectRetentionHandler))))).Queries("retention", "")
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.GetObjectRetentionHandler)).
|
||||
Queries("retention", "")
|
||||
// GetObjectLegalHold
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("getobjectlegalhold", maxClients(gz(httpTraceAll(api.GetObjectLegalHoldHandler))))).Queries("legal-hold", "")
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.GetObjectLegalHoldHandler)).
|
||||
Queries("legal-hold", "")
|
||||
// GetObject with lambda ARNs
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("getobject", maxClients(gz(httpTraceHdrs(api.GetObjectLambdaHandler))))).Queries("lambdaArn", "{lambdaArn:.*}")
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.GetObjectLambdaHandler, traceHdrsS3HFlag)).
|
||||
Queries("lambdaArn", "{lambdaArn:.*}")
|
||||
// GetObject
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("getobject", maxClients(gz(httpTraceHdrs(api.GetObjectHandler)))))
|
||||
router.Methods(http.MethodGet).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.GetObjectHandler, traceHdrsS3HFlag))
|
||||
// CopyObject
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").HeadersRegexp(xhttp.AmzCopySource, ".*?(\\/|%2F).*?").HandlerFunc(
|
||||
collectAPIStats("copyobject", maxClients(gz(httpTraceAll(api.CopyObjectHandler)))))
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").
|
||||
HeadersRegexp(xhttp.AmzCopySource, ".*?(\\/|%2F).*?").
|
||||
HandlerFunc(s3APIMiddleware(api.CopyObjectHandler))
|
||||
// PutObjectRetention
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("putobjectretention", maxClients(gz(httpTraceAll(api.PutObjectRetentionHandler))))).Queries("retention", "")
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.PutObjectRetentionHandler)).
|
||||
Queries("retention", "")
|
||||
// PutObjectLegalHold
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("putobjectlegalhold", maxClients(gz(httpTraceAll(api.PutObjectLegalHoldHandler))))).Queries("legal-hold", "")
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.PutObjectLegalHoldHandler)).
|
||||
Queries("legal-hold", "")
|
||||
|
||||
// PutObject with auto-extract support for zip
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").HeadersRegexp(xhttp.AmzSnowballExtract, "true").HandlerFunc(
|
||||
collectAPIStats("putobject", maxClients(gz(httpTraceHdrs(api.PutObjectExtractHandler)))))
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").
|
||||
HeadersRegexp(xhttp.AmzSnowballExtract, "true").
|
||||
HandlerFunc(s3APIMiddleware(api.PutObjectExtractHandler, traceHdrsS3HFlag))
|
||||
|
||||
// PutObject
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("putobject", maxClients(gz(httpTraceHdrs(api.PutObjectHandler)))))
|
||||
router.Methods(http.MethodPut).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.PutObjectHandler, traceHdrsS3HFlag))
|
||||
|
||||
// DeleteObject
|
||||
router.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("deleteobject", maxClients(gz(httpTraceAll(api.DeleteObjectHandler)))))
|
||||
router.Methods(http.MethodDelete).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.DeleteObjectHandler))
|
||||
|
||||
// PostRestoreObject
|
||||
router.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(
|
||||
collectAPIStats("restoreobject", maxClients(gz(httpTraceAll(api.PostRestoreObjectHandler))))).Queries("restore", "")
|
||||
router.Methods(http.MethodPost).Path("/{object:.+}").
|
||||
HandlerFunc(s3APIMiddleware(api.PostRestoreObjectHandler)).
|
||||
Queries("restore", "")
|
||||
|
||||
// Bucket operations
|
||||
|
||||
// GetBucketLocation
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketlocation", maxClients(gz(httpTraceAll(api.GetBucketLocationHandler))))).Queries("location", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketLocationHandler)).
|
||||
Queries("location", "")
|
||||
// GetBucketPolicy
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketpolicy", maxClients(gz(httpTraceAll(api.GetBucketPolicyHandler))))).Queries("policy", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketPolicyHandler)).
|
||||
Queries("policy", "")
|
||||
// GetBucketLifecycle
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketlifecycle", maxClients(gz(httpTraceAll(api.GetBucketLifecycleHandler))))).Queries("lifecycle", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketLifecycleHandler)).
|
||||
Queries("lifecycle", "")
|
||||
// GetBucketEncryption
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketencryption", maxClients(gz(httpTraceAll(api.GetBucketEncryptionHandler))))).Queries("encryption", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketEncryptionHandler)).
|
||||
Queries("encryption", "")
|
||||
// GetBucketObjectLockConfig
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketobjectlockconfiguration", maxClients(gz(httpTraceAll(api.GetBucketObjectLockConfigHandler))))).Queries("object-lock", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketObjectLockConfigHandler)).
|
||||
Queries("object-lock", "")
|
||||
// GetBucketReplicationConfig
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketreplicationconfiguration", maxClients(gz(httpTraceAll(api.GetBucketReplicationConfigHandler))))).Queries("replication", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketReplicationConfigHandler)).
|
||||
Queries("replication", "")
|
||||
// GetBucketVersioning
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketversioning", maxClients(gz(httpTraceAll(api.GetBucketVersioningHandler))))).Queries("versioning", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketVersioningHandler)).
|
||||
Queries("versioning", "")
|
||||
// GetBucketNotification
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketnotification", maxClients(gz(httpTraceAll(api.GetBucketNotificationHandler))))).Queries("notification", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketNotificationHandler)).
|
||||
Queries("notification", "")
|
||||
// ListenNotification
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("listennotification", gz(httpTraceAll(api.ListenNotificationHandler)))).Queries("events", "{events:.*}")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.ListenNotificationHandler, noThrottleS3HFlag)).
|
||||
Queries("events", "{events:.*}")
|
||||
// ResetBucketReplicationStatus - MinIO extension API
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("resetbucketreplicationstatus", maxClients(gz(httpTraceAll(api.ResetBucketReplicationStatusHandler))))).Queries("replication-reset-status", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.ResetBucketReplicationStatusHandler)).
|
||||
Queries("replication-reset-status", "")
|
||||
|
||||
// Dummy Bucket Calls
|
||||
// GetBucketACL -- this is a dummy call.
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketacl", maxClients(gz(httpTraceAll(api.GetBucketACLHandler))))).Queries("acl", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketACLHandler)).
|
||||
Queries("acl", "")
|
||||
// PutBucketACL -- this is a dummy call.
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("putbucketacl", maxClients(gz(httpTraceAll(api.PutBucketACLHandler))))).Queries("acl", "")
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.PutBucketACLHandler)).
|
||||
Queries("acl", "")
|
||||
// GetBucketCors - this is a dummy call.
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketcors", maxClients(gz(httpTraceAll(api.GetBucketCorsHandler))))).Queries("cors", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketCorsHandler)).
|
||||
Queries("cors", "")
|
||||
// GetBucketWebsiteHandler - this is a dummy call.
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketwebsite", maxClients(gz(httpTraceAll(api.GetBucketWebsiteHandler))))).Queries("website", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketWebsiteHandler)).
|
||||
Queries("website", "")
|
||||
// GetBucketAccelerateHandler - this is a dummy call.
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketaccelerate", maxClients(gz(httpTraceAll(api.GetBucketAccelerateHandler))))).Queries("accelerate", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketAccelerateHandler)).
|
||||
Queries("accelerate", "")
|
||||
// GetBucketRequestPaymentHandler - this is a dummy call.
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketrequestpayment", maxClients(gz(httpTraceAll(api.GetBucketRequestPaymentHandler))))).Queries("requestPayment", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketRequestPaymentHandler)).
|
||||
Queries("requestPayment", "")
|
||||
// GetBucketLoggingHandler - this is a dummy call.
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketlogging", maxClients(gz(httpTraceAll(api.GetBucketLoggingHandler))))).Queries("logging", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketLoggingHandler)).
|
||||
Queries("logging", "")
|
||||
// GetBucketTaggingHandler
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbuckettagging", maxClients(gz(httpTraceAll(api.GetBucketTaggingHandler))))).Queries("tagging", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketTaggingHandler)).
|
||||
Queries("tagging", "")
|
||||
// DeleteBucketWebsiteHandler
|
||||
router.Methods(http.MethodDelete).HandlerFunc(
|
||||
collectAPIStats("deletebucketwebsite", maxClients(gz(httpTraceAll(api.DeleteBucketWebsiteHandler))))).Queries("website", "")
|
||||
router.Methods(http.MethodDelete).
|
||||
HandlerFunc(s3APIMiddleware(api.DeleteBucketWebsiteHandler)).
|
||||
Queries("website", "")
|
||||
// DeleteBucketTaggingHandler
|
||||
router.Methods(http.MethodDelete).HandlerFunc(
|
||||
collectAPIStats("deletebuckettagging", maxClients(gz(httpTraceAll(api.DeleteBucketTaggingHandler))))).Queries("tagging", "")
|
||||
router.Methods(http.MethodDelete).
|
||||
HandlerFunc(s3APIMiddleware(api.DeleteBucketTaggingHandler)).
|
||||
Queries("tagging", "")
|
||||
|
||||
// ListMultipartUploads
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("listmultipartuploads", maxClients(gz(httpTraceAll(api.ListMultipartUploadsHandler))))).Queries("uploads", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.ListMultipartUploadsHandler)).
|
||||
Queries("uploads", "")
|
||||
// ListObjectsV2M
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("listobjectsv2M", maxClients(gz(httpTraceAll(api.ListObjectsV2MHandler))))).Queries("list-type", "2", "metadata", "true")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.ListObjectsV2MHandler)).
|
||||
Queries("list-type", "2", "metadata", "true")
|
||||
// ListObjectsV2
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("listobjectsv2", maxClients(gz(httpTraceAll(api.ListObjectsV2Handler))))).Queries("list-type", "2")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.ListObjectsV2Handler)).
|
||||
Queries("list-type", "2")
|
||||
// ListObjectVersions
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("listobjectversions", maxClients(gz(httpTraceAll(api.ListObjectVersionsMHandler))))).Queries("versions", "", "metadata", "true")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.ListObjectVersionsMHandler)).
|
||||
Queries("versions", "", "metadata", "true")
|
||||
// ListObjectVersions
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("listobjectversions", maxClients(gz(httpTraceAll(api.ListObjectVersionsHandler))))).Queries("versions", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.ListObjectVersionsHandler)).
|
||||
Queries("versions", "")
|
||||
// GetBucketPolicyStatus
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getpolicystatus", maxClients(gz(httpTraceAll(api.GetBucketPolicyStatusHandler))))).Queries("policyStatus", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketPolicyStatusHandler)).
|
||||
Queries("policyStatus", "")
|
||||
// PutBucketLifecycle
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("putbucketlifecycle", maxClients(gz(httpTraceAll(api.PutBucketLifecycleHandler))))).Queries("lifecycle", "")
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.PutBucketLifecycleHandler)).
|
||||
Queries("lifecycle", "")
|
||||
// PutBucketReplicationConfig
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("putbucketreplicationconfiguration", maxClients(gz(httpTraceAll(api.PutBucketReplicationConfigHandler))))).Queries("replication", "")
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.PutBucketReplicationConfigHandler)).
|
||||
Queries("replication", "")
|
||||
// PutBucketEncryption
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("putbucketencryption", maxClients(gz(httpTraceAll(api.PutBucketEncryptionHandler))))).Queries("encryption", "")
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.PutBucketEncryptionHandler)).
|
||||
Queries("encryption", "")
|
||||
|
||||
// PutBucketPolicy
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("putbucketpolicy", maxClients(gz(httpTraceAll(api.PutBucketPolicyHandler))))).Queries("policy", "")
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.PutBucketPolicyHandler)).
|
||||
Queries("policy", "")
|
||||
|
||||
// PutBucketObjectLockConfig
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("putbucketobjectlockconfig", maxClients(gz(httpTraceAll(api.PutBucketObjectLockConfigHandler))))).Queries("object-lock", "")
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.PutBucketObjectLockConfigHandler)).
|
||||
Queries("object-lock", "")
|
||||
// PutBucketTaggingHandler
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("putbuckettagging", maxClients(gz(httpTraceAll(api.PutBucketTaggingHandler))))).Queries("tagging", "")
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.PutBucketTaggingHandler)).
|
||||
Queries("tagging", "")
|
||||
// PutBucketVersioning
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("putbucketversioning", maxClients(gz(httpTraceAll(api.PutBucketVersioningHandler))))).Queries("versioning", "")
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.PutBucketVersioningHandler)).
|
||||
Queries("versioning", "")
|
||||
// PutBucketNotification
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("putbucketnotification", maxClients(gz(httpTraceAll(api.PutBucketNotificationHandler))))).Queries("notification", "")
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.PutBucketNotificationHandler)).
|
||||
Queries("notification", "")
|
||||
// ResetBucketReplicationStart - MinIO extension API
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("resetbucketreplicationstart", maxClients(gz(httpTraceAll(api.ResetBucketReplicationStartHandler))))).Queries("replication-reset", "")
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.ResetBucketReplicationStartHandler)).
|
||||
Queries("replication-reset", "")
|
||||
|
||||
// PutBucket
|
||||
router.Methods(http.MethodPut).HandlerFunc(
|
||||
collectAPIStats("putbucket", maxClients(gz(httpTraceAll(api.PutBucketHandler)))))
|
||||
router.Methods(http.MethodPut).
|
||||
HandlerFunc(s3APIMiddleware(api.PutBucketHandler))
|
||||
// HeadBucket
|
||||
router.Methods(http.MethodHead).HandlerFunc(
|
||||
collectAPIStats("headbucket", maxClients(gz(httpTraceAll(api.HeadBucketHandler)))))
|
||||
router.Methods(http.MethodHead).
|
||||
HandlerFunc(s3APIMiddleware(api.HeadBucketHandler))
|
||||
// PostPolicy
|
||||
router.Methods(http.MethodPost).MatcherFunc(func(r *http.Request, _ *mux.RouteMatch) bool {
|
||||
return isRequestPostPolicySignatureV4(r)
|
||||
}).HandlerFunc(collectAPIStats("postpolicybucket", maxClients(gz(httpTraceHdrs(api.PostPolicyBucketHandler)))))
|
||||
router.Methods(http.MethodPost).
|
||||
MatcherFunc(func(r *http.Request, _ *mux.RouteMatch) bool {
|
||||
return isRequestPostPolicySignatureV4(r)
|
||||
}).
|
||||
HandlerFunc(s3APIMiddleware(api.PostPolicyBucketHandler, traceHdrsS3HFlag))
|
||||
// DeleteMultipleObjects
|
||||
router.Methods(http.MethodPost).HandlerFunc(
|
||||
collectAPIStats("deletemultipleobjects", maxClients(gz(httpTraceAll(api.DeleteMultipleObjectsHandler))))).Queries("delete", "")
|
||||
router.Methods(http.MethodPost).
|
||||
HandlerFunc(s3APIMiddleware(api.DeleteMultipleObjectsHandler)).
|
||||
Queries("delete", "")
|
||||
// DeleteBucketPolicy
|
||||
router.Methods(http.MethodDelete).HandlerFunc(
|
||||
collectAPIStats("deletebucketpolicy", maxClients(gz(httpTraceAll(api.DeleteBucketPolicyHandler))))).Queries("policy", "")
|
||||
router.Methods(http.MethodDelete).
|
||||
HandlerFunc(s3APIMiddleware(api.DeleteBucketPolicyHandler)).
|
||||
Queries("policy", "")
|
||||
// DeleteBucketReplication
|
||||
router.Methods(http.MethodDelete).HandlerFunc(
|
||||
collectAPIStats("deletebucketreplicationconfiguration", maxClients(gz(httpTraceAll(api.DeleteBucketReplicationConfigHandler))))).Queries("replication", "")
|
||||
router.Methods(http.MethodDelete).
|
||||
HandlerFunc(s3APIMiddleware(api.DeleteBucketReplicationConfigHandler)).
|
||||
Queries("replication", "")
|
||||
// DeleteBucketLifecycle
|
||||
router.Methods(http.MethodDelete).HandlerFunc(
|
||||
collectAPIStats("deletebucketlifecycle", maxClients(gz(httpTraceAll(api.DeleteBucketLifecycleHandler))))).Queries("lifecycle", "")
|
||||
router.Methods(http.MethodDelete).
|
||||
HandlerFunc(s3APIMiddleware(api.DeleteBucketLifecycleHandler)).
|
||||
Queries("lifecycle", "")
|
||||
// DeleteBucketEncryption
|
||||
router.Methods(http.MethodDelete).HandlerFunc(
|
||||
collectAPIStats("deletebucketencryption", maxClients(gz(httpTraceAll(api.DeleteBucketEncryptionHandler))))).Queries("encryption", "")
|
||||
router.Methods(http.MethodDelete).
|
||||
HandlerFunc(s3APIMiddleware(api.DeleteBucketEncryptionHandler)).
|
||||
Queries("encryption", "")
|
||||
// DeleteBucket
|
||||
router.Methods(http.MethodDelete).HandlerFunc(
|
||||
collectAPIStats("deletebucket", maxClients(gz(httpTraceAll(api.DeleteBucketHandler)))))
|
||||
router.Methods(http.MethodDelete).
|
||||
HandlerFunc(s3APIMiddleware(api.DeleteBucketHandler))
|
||||
|
||||
// MinIO extension API for replication.
|
||||
//
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketreplicationmetrics", maxClients(gz(httpTraceAll(api.GetBucketReplicationMetricsV2Handler))))).Queries("replication-metrics", "2")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketReplicationMetricsV2Handler)).
|
||||
Queries("replication-metrics", "2")
|
||||
// deprecated handler
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("getbucketreplicationmetrics", maxClients(gz(httpTraceAll(api.GetBucketReplicationMetricsHandler))))).Queries("replication-metrics", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.GetBucketReplicationMetricsHandler)).
|
||||
Queries("replication-metrics", "")
|
||||
|
||||
// ValidateBucketReplicationCreds
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("checkbucketreplicationconfiguration", maxClients(gz(httpTraceAll(api.ValidateBucketReplicationCredsHandler))))).Queries("replication-check", "")
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.ValidateBucketReplicationCredsHandler)).
|
||||
Queries("replication-check", "")
|
||||
|
||||
// Register rejected bucket APIs
|
||||
for _, r := range rejectedBucketAPIs {
|
||||
@@ -468,24 +607,25 @@ func registerAPIRouter(router *mux.Router) {
|
||||
}
|
||||
|
||||
// S3 ListObjectsV1 (Legacy)
|
||||
router.Methods(http.MethodGet).HandlerFunc(
|
||||
collectAPIStats("listobjectsv1", maxClients(gz(httpTraceAll(api.ListObjectsV1Handler)))))
|
||||
router.Methods(http.MethodGet).
|
||||
HandlerFunc(s3APIMiddleware(api.ListObjectsV1Handler))
|
||||
}
|
||||
|
||||
// Root operation
|
||||
|
||||
// ListenNotification
|
||||
apiRouter.Methods(http.MethodGet).Path(SlashSeparator).HandlerFunc(
|
||||
collectAPIStats("listennotification", gz(httpTraceAll(api.ListenNotificationHandler)))).Queries("events", "{events:.*}")
|
||||
apiRouter.Methods(http.MethodGet).Path(SlashSeparator).
|
||||
HandlerFunc(s3APIMiddleware(api.ListenNotificationHandler, noThrottleS3HFlag)).
|
||||
Queries("events", "{events:.*}")
|
||||
|
||||
// ListBuckets
|
||||
apiRouter.Methods(http.MethodGet).Path(SlashSeparator).HandlerFunc(
|
||||
collectAPIStats("listbuckets", maxClients(gz(httpTraceAll(api.ListBucketsHandler)))))
|
||||
apiRouter.Methods(http.MethodGet).Path(SlashSeparator).
|
||||
HandlerFunc(s3APIMiddleware(api.ListBucketsHandler))
|
||||
|
||||
// S3 browser with signature v4 adds '//' for ListBuckets request, so rather
|
||||
// than failing with UnknownAPIRequest we simply handle it for now.
|
||||
apiRouter.Methods(http.MethodGet).Path(SlashSeparator + SlashSeparator).HandlerFunc(
|
||||
collectAPIStats("listbuckets", maxClients(gz(httpTraceAll(api.ListBucketsHandler)))))
|
||||
apiRouter.Methods(http.MethodGet).Path(SlashSeparator + SlashSeparator).
|
||||
HandlerFunc(s3APIMiddleware(api.ListBucketsHandler))
|
||||
|
||||
// If none of the routes match add default error handler routes
|
||||
apiRouter.NotFoundHandler = collectAPIStats("notfound", httpTraceAll(errorResponseHandler))
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -100,3 +104,16 @@ func s3EncodeName(name, encodingType string) string {
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// getHandlerName returns the name of the handler function. It takes the type
|
||||
// name as a string to clean up the name retrieved via reflection. This function
|
||||
// only works correctly when the type is present in the cmd package.
|
||||
func getHandlerName(f http.HandlerFunc, cmdType string) string {
|
||||
name := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
|
||||
|
||||
packageName := fmt.Sprintf("github.com/minio/minio/cmd.%s.", cmdType)
|
||||
name = strings.TrimPrefix(name, packageName)
|
||||
name = strings.TrimSuffix(name, "Handler-fm")
|
||||
name = strings.TrimSuffix(name, "-fm")
|
||||
return name
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -294,6 +294,11 @@ func checkClaimsFromToken(r *http.Request, cred auth.Credentials) (map[string]in
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
// Expired credentials must return error right away.
|
||||
if cred.IsTemp() && cred.IsExpired() {
|
||||
return nil, toAPIErrorCode(r.Context(), errInvalidAccessKeyID)
|
||||
}
|
||||
|
||||
secret := globalActiveCred.SecretKey
|
||||
if cred.IsServiceAccount() {
|
||||
token = cred.SessionToken
|
||||
@@ -301,6 +306,13 @@ func checkClaimsFromToken(r *http.Request, cred auth.Credentials) (map[string]in
|
||||
}
|
||||
|
||||
if token != "" {
|
||||
var err error
|
||||
if globalSiteReplicationSys.isEnabled() && cred.AccessKey != siteReplicatorSvcAcc {
|
||||
secret, err = getTokenSigningKey()
|
||||
if err != nil {
|
||||
return nil, toAPIErrorCode(r.Context(), err)
|
||||
}
|
||||
}
|
||||
claims, err := getClaimsFromTokenWithSecret(token, secret)
|
||||
if err != nil {
|
||||
return nil, toAPIErrorCode(r.Context(), err)
|
||||
|
||||
@@ -237,7 +237,7 @@ func TestIsRequestPresignedSignatureV2(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsRequestPresignedSignatureV4 - Test validates the logic for presign signature verision v4 detection.
|
||||
// TestIsRequestPresignedSignatureV4 - Test validates the logic for presign signature version v4 detection.
|
||||
func TestIsRequestPresignedSignatureV4(t *testing.T) {
|
||||
testCases := []struct {
|
||||
inputQueryKey string
|
||||
|
||||
@@ -102,7 +102,6 @@ func waitForLowHTTPReq() {
|
||||
|
||||
func initBackgroundHealing(ctx context.Context, objAPI ObjectLayer) {
|
||||
// Run the background healer
|
||||
globalBackgroundHealRoutine = newHealRoutine()
|
||||
for i := 0; i < globalBackgroundHealRoutine.workers; i++ {
|
||||
go globalBackgroundHealRoutine.AddWorker(ctx, objAPI)
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ type healingTracker struct {
|
||||
// ID of the current healing operation
|
||||
HealID string
|
||||
|
||||
ItemsSkipped uint64
|
||||
BytesSkipped uint64
|
||||
// Add future tracking capabilities
|
||||
// Be sure that they are included in toHealingDisk
|
||||
}
|
||||
@@ -175,14 +177,18 @@ func (h *healingTracker) setObject(object string) {
|
||||
h.Object = object
|
||||
}
|
||||
|
||||
func (h *healingTracker) updateProgress(success bool, bytes uint64) {
|
||||
func (h *healingTracker) updateProgress(success, skipped bool, bytes uint64) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
if success {
|
||||
switch {
|
||||
case success:
|
||||
h.ItemsHealed++
|
||||
h.BytesDone += bytes
|
||||
} else {
|
||||
case skipped:
|
||||
h.ItemsSkipped++
|
||||
h.BytesSkipped += bytes
|
||||
default:
|
||||
h.ItemsFailed++
|
||||
h.BytesFailed += bytes
|
||||
}
|
||||
@@ -323,8 +329,10 @@ func (h *healingTracker) toHealingDisk() madmin.HealingDisk {
|
||||
ObjectsTotalCount: h.ObjectsTotalCount,
|
||||
ObjectsTotalSize: h.ObjectsTotalSize,
|
||||
ItemsHealed: h.ItemsHealed,
|
||||
ItemsSkipped: h.ItemsSkipped,
|
||||
ItemsFailed: h.ItemsFailed,
|
||||
BytesDone: h.BytesDone,
|
||||
BytesSkipped: h.BytesSkipped,
|
||||
BytesFailed: h.BytesFailed,
|
||||
Bucket: h.Bucket,
|
||||
Object: h.Object,
|
||||
@@ -353,7 +361,7 @@ func initAutoHeal(ctx context.Context, objAPI ObjectLayer) {
|
||||
|
||||
func getLocalDisksToHeal() (disksToHeal Endpoints) {
|
||||
globalLocalDrivesMu.RLock()
|
||||
localDrives := globalLocalDrives
|
||||
localDrives := cloneDrives(globalLocalDrives)
|
||||
globalLocalDrivesMu.RUnlock()
|
||||
for _, disk := range localDrives {
|
||||
_, err := disk.GetDiskID()
|
||||
@@ -376,25 +384,10 @@ func getLocalDisksToHeal() (disksToHeal Endpoints) {
|
||||
var newDiskHealingTimeout = newDynamicTimeout(30*time.Second, 10*time.Second)
|
||||
|
||||
func healFreshDisk(ctx context.Context, z *erasureServerPools, endpoint Endpoint) error {
|
||||
disk, format, _, err := connectEndpoint(endpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: %w, %s", err, endpoint)
|
||||
}
|
||||
defer disk.Close()
|
||||
poolIdx := globalEndpoints.GetLocalPoolIdx(disk.Endpoint())
|
||||
if poolIdx < 0 {
|
||||
return fmt.Errorf("unexpected pool index (%d) found for %s", poolIdx, disk.Endpoint())
|
||||
}
|
||||
|
||||
// Calculate the set index where the current endpoint belongs
|
||||
z.serverPools[poolIdx].erasureDisksMu.RLock()
|
||||
setIdx, _, err := findDiskIndex(z.serverPools[poolIdx].format, format)
|
||||
z.serverPools[poolIdx].erasureDisksMu.RUnlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if setIdx < 0 {
|
||||
return fmt.Errorf("unexpected set index (%d) found for %s", setIdx, disk.Endpoint())
|
||||
poolIdx, setIdx := endpoint.PoolIdx, endpoint.SetIdx
|
||||
disk := getStorageViaEndpoint(endpoint)
|
||||
if disk == nil {
|
||||
return fmt.Errorf("Unexpected error disk must be initialized by now after formatting: %s", endpoint)
|
||||
}
|
||||
|
||||
// Prevent parallel erasure set healing
|
||||
@@ -420,7 +413,7 @@ func healFreshDisk(ctx context.Context, z *erasureServerPools, endpoint Endpoint
|
||||
tracker = initHealingTracker(disk, mustGetUUID())
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Healing drive '%s' - 'mc admin heal alias/ --verbose' to check the current status.", endpoint))
|
||||
logger.Event(ctx, "Healing drive '%s' - 'mc admin heal alias/ --verbose' to check the current status.", endpoint)
|
||||
|
||||
buckets, _ := z.ListBuckets(ctx, BucketOptions{})
|
||||
// Buckets data are dispersed in multiple pools/sets, make
|
||||
@@ -440,10 +433,6 @@ func healFreshDisk(ctx context.Context, z *erasureServerPools, endpoint Endpoint
|
||||
return buckets[i].Created.After(buckets[j].Created)
|
||||
})
|
||||
|
||||
if serverDebugLog {
|
||||
logger.Info("Healing drive '%v' on %s pool, belonging to %s erasure set", disk, humanize.Ordinal(poolIdx+1), humanize.Ordinal(setIdx+1))
|
||||
}
|
||||
|
||||
// Load bucket totals
|
||||
cache := dataUsageCache{}
|
||||
if err := cache.load(ctx, z.serverPools[poolIdx].sets[setIdx], dataUsageCacheName); err == nil {
|
||||
@@ -463,11 +452,7 @@ func healFreshDisk(ctx context.Context, z *erasureServerPools, endpoint Endpoint
|
||||
return err
|
||||
}
|
||||
|
||||
if tracker.ItemsFailed > 0 {
|
||||
logger.Info("Healing of drive '%s' failed (healed: %d, failed: %d).", disk, tracker.ItemsHealed, tracker.ItemsFailed)
|
||||
} else {
|
||||
logger.Info("Healing of drive '%s' complete (healed: %d, failed: %d).", disk, tracker.ItemsHealed, tracker.ItemsFailed)
|
||||
}
|
||||
logger.Event(ctx, "Healing of drive '%s' is finished (healed: %d, skipped: %d, failed: %d).", disk, tracker.ItemsHealed, tracker.ItemsSkipped, tracker.ItemsFailed)
|
||||
|
||||
if len(tracker.QueuedBuckets) > 0 {
|
||||
return fmt.Errorf("not all buckets were healed: %v", tracker.QueuedBuckets)
|
||||
@@ -475,7 +460,7 @@ func healFreshDisk(ctx context.Context, z *erasureServerPools, endpoint Endpoint
|
||||
|
||||
if serverDebugLog {
|
||||
tracker.printTo(os.Stdout)
|
||||
logger.Info("\n")
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
if tracker.HealID == "" { // HealID was empty only before Feb 2023
|
||||
|
||||
@@ -188,6 +188,18 @@ func (z *healingTracker) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "HealID")
|
||||
return
|
||||
}
|
||||
case "ItemsSkipped":
|
||||
z.ItemsSkipped, err = dc.ReadUint64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ItemsSkipped")
|
||||
return
|
||||
}
|
||||
case "BytesSkipped":
|
||||
z.BytesSkipped, err = dc.ReadUint64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BytesSkipped")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
@@ -201,9 +213,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 23
|
||||
// map header, size 25
|
||||
// write "ID"
|
||||
err = en.Append(0xde, 0x0, 0x17, 0xa2, 0x49, 0x44)
|
||||
err = en.Append(0xde, 0x0, 0x19, 0xa2, 0x49, 0x44)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -446,15 +458,35 @@ func (z *healingTracker) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = msgp.WrapError(err, "HealID")
|
||||
return
|
||||
}
|
||||
// write "ItemsSkipped"
|
||||
err = en.Append(0xac, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteUint64(z.ItemsSkipped)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ItemsSkipped")
|
||||
return
|
||||
}
|
||||
// write "BytesSkipped"
|
||||
err = en.Append(0xac, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteUint64(z.BytesSkipped)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BytesSkipped")
|
||||
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 23
|
||||
// map header, size 25
|
||||
// string "ID"
|
||||
o = append(o, 0xde, 0x0, 0x17, 0xa2, 0x49, 0x44)
|
||||
o = append(o, 0xde, 0x0, 0x19, 0xa2, 0x49, 0x44)
|
||||
o = msgp.AppendString(o, z.ID)
|
||||
// string "PoolIndex"
|
||||
o = append(o, 0xa9, 0x50, 0x6f, 0x6f, 0x6c, 0x49, 0x6e, 0x64, 0x65, 0x78)
|
||||
@@ -528,6 +560,12 @@ func (z *healingTracker) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
// string "HealID"
|
||||
o = append(o, 0xa6, 0x48, 0x65, 0x61, 0x6c, 0x49, 0x44)
|
||||
o = msgp.AppendString(o, z.HealID)
|
||||
// string "ItemsSkipped"
|
||||
o = append(o, 0xac, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64)
|
||||
o = msgp.AppendUint64(o, z.ItemsSkipped)
|
||||
// string "BytesSkipped"
|
||||
o = append(o, 0xac, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64)
|
||||
o = msgp.AppendUint64(o, z.BytesSkipped)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -713,6 +751,18 @@ func (z *healingTracker) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "HealID")
|
||||
return
|
||||
}
|
||||
case "ItemsSkipped":
|
||||
z.ItemsSkipped, bts, err = msgp.ReadUint64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ItemsSkipped")
|
||||
return
|
||||
}
|
||||
case "BytesSkipped":
|
||||
z.BytesSkipped, bts, err = msgp.ReadUint64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BytesSkipped")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
@@ -735,6 +785,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)
|
||||
s += 7 + msgp.StringPrefixSize + len(z.HealID) + 13 + msgp.Uint64Size + 13 + msgp.Uint64Size
|
||||
return
|
||||
}
|
||||
|
||||
+7
-2
@@ -572,7 +572,11 @@ func (r *BatchJobExpire) Start(ctx context.Context, api ObjectLayer, job BatchJo
|
||||
}()
|
||||
|
||||
expireCh := make(chan []expireObjInfo, workerSize)
|
||||
go batchObjsForDelete(ctx, r, ri, job, api, wk, expireCh)
|
||||
expireDoneCh := make(chan struct{})
|
||||
go func() {
|
||||
defer close(expireDoneCh)
|
||||
batchObjsForDelete(ctx, r, ri, job, api, wk, expireCh)
|
||||
}()
|
||||
|
||||
var (
|
||||
prevObj ObjectInfo
|
||||
@@ -651,7 +655,8 @@ func (r *BatchJobExpire) Start(ctx context.Context, api ObjectLayer, job BatchJo
|
||||
}
|
||||
xioutil.SafeClose(expireCh)
|
||||
|
||||
wk.Wait() // waits for all expire goroutines to complete
|
||||
<-expireDoneCh // waits for the expire goroutine to complete
|
||||
wk.Wait() // waits for all expire workers to retire
|
||||
|
||||
ri.Complete = ri.ObjectsFailed == 0
|
||||
ri.Failed = ri.ObjectsFailed > 0
|
||||
|
||||
@@ -925,6 +925,21 @@ func (ri *batchJobInfo) trackCurrentBucketObject(bucket string, info ObjectInfo,
|
||||
ri.countItem(info.Size, info.DeleteMarker, success)
|
||||
}
|
||||
|
||||
func (ri *batchJobInfo) trackCurrentBucketBatch(bucket string, batch []ObjectInfo) {
|
||||
if ri == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ri.mu.Lock()
|
||||
defer ri.mu.Unlock()
|
||||
|
||||
ri.Bucket = bucket
|
||||
for i := range batch {
|
||||
ri.Object = batch[i].Name
|
||||
ri.countItem(batch[i].Size, batch[i].DeleteMarker, true)
|
||||
}
|
||||
}
|
||||
|
||||
// Start start the batch replication job, resumes if there was a pending job via "job.ID"
|
||||
func (r *BatchJobReplicateV1) Start(ctx context.Context, api ObjectLayer, job BatchJobRequest) error {
|
||||
ri := &batchJobInfo{
|
||||
@@ -1087,6 +1102,11 @@ func (r *BatchJobReplicateV1) Start(ctx context.Context, api ObjectLayer, job Ba
|
||||
for _, b := range batch {
|
||||
slowCh <- b
|
||||
}
|
||||
} else {
|
||||
ri.trackCurrentBucketBatch(r.Source.Bucket, batch)
|
||||
globalBatchJobsMetrics.save(job.ID, ri)
|
||||
// persist in-memory state to disk after every 10secs.
|
||||
logger.LogIf(ctx, ri.updateAfter(ctx, api, 10*time.Second, job))
|
||||
}
|
||||
batch = batch[:0]
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/kes-go"
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/kms"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
|
||||
@@ -580,7 +580,7 @@ func testListBucketsHandler(obj ObjectLayer, instanceType, bucketName string, ap
|
||||
expectedRespStatus: http.StatusOK,
|
||||
},
|
||||
// Test case - 2.
|
||||
// Test case with invalid accessKey to produce and validate Signature MisMatch error.
|
||||
// Test case with invalid accessKey to produce and validate Signature Mismatch error.
|
||||
{
|
||||
bucketName: bucketName,
|
||||
accessKey: "abcd",
|
||||
|
||||
+277
-83
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
// Copyright (c) 2015-2024 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -39,12 +38,9 @@ import (
|
||||
"github.com/minio/minio/internal/bucket/lifecycle"
|
||||
"github.com/minio/minio/internal/event"
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
xioutil "github.com/minio/minio/internal/ioutil"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/minio/internal/s3select"
|
||||
"github.com/minio/pkg/v2/env"
|
||||
xnet "github.com/minio/pkg/v2/net"
|
||||
"github.com/minio/pkg/v2/workers"
|
||||
"github.com/zeebo/xxh3"
|
||||
)
|
||||
|
||||
@@ -105,96 +101,280 @@ type expiryTask struct {
|
||||
src lcEventSrc
|
||||
}
|
||||
|
||||
// expiryStats records metrics related to ILM expiry activities
|
||||
type expiryStats struct {
|
||||
missedExpiryTasks atomic.Int64
|
||||
missedFreeVersTasks atomic.Int64
|
||||
missedTierJournalTasks atomic.Int64
|
||||
workers atomic.Int32
|
||||
}
|
||||
|
||||
// MissedTasks returns the number of ILM expiry tasks that were missed since
|
||||
// there were no available workers.
|
||||
func (e *expiryStats) MissedTasks() int64 {
|
||||
return e.missedExpiryTasks.Load()
|
||||
}
|
||||
|
||||
// MissedFreeVersTasks returns the number of free version collection tasks that
|
||||
// were missed since there were no available workers.
|
||||
func (e *expiryStats) MissedFreeVersTasks() int64 {
|
||||
return e.missedFreeVersTasks.Load()
|
||||
}
|
||||
|
||||
// MissedTierJournalTasks returns the number of tasks to remove tiered objects
|
||||
// that were missed since there were no available workers.
|
||||
func (e *expiryStats) MissedTierJournalTasks() int64 {
|
||||
return e.missedTierJournalTasks.Load()
|
||||
}
|
||||
|
||||
// NumWorkers returns the number of active workers executing one of ILM expiry
|
||||
// tasks or free version collection tasks.
|
||||
func (e *expiryStats) NumWorkers() int32 {
|
||||
return e.workers.Load()
|
||||
}
|
||||
|
||||
type expiryOp interface {
|
||||
OpHash() uint64
|
||||
}
|
||||
|
||||
type freeVersionTask struct {
|
||||
ObjectInfo
|
||||
}
|
||||
|
||||
func (f freeVersionTask) OpHash() uint64 {
|
||||
return xxh3.HashString(f.TransitionedObject.Tier + f.TransitionedObject.Name)
|
||||
}
|
||||
|
||||
func (n newerNoncurrentTask) OpHash() uint64 {
|
||||
return xxh3.HashString(n.bucket + n.versions[0].ObjectV.ObjectName)
|
||||
}
|
||||
|
||||
func (j jentry) OpHash() uint64 {
|
||||
return xxh3.HashString(j.TierName + j.ObjName)
|
||||
}
|
||||
|
||||
func (e expiryTask) OpHash() uint64 {
|
||||
return xxh3.HashString(e.objInfo.Bucket + e.objInfo.Name)
|
||||
}
|
||||
|
||||
// expiryState manages all ILM related expiration activities.
|
||||
type expiryState struct {
|
||||
once sync.Once
|
||||
byDaysCh chan expiryTask
|
||||
byNewerNoncurrentCh chan newerNoncurrentTask
|
||||
mu sync.RWMutex
|
||||
workers atomic.Pointer[[]chan expiryOp]
|
||||
|
||||
ctx context.Context
|
||||
objAPI ObjectLayer
|
||||
|
||||
stats expiryStats
|
||||
}
|
||||
|
||||
// PendingTasks returns the number of pending ILM expiry tasks.
|
||||
func (es *expiryState) PendingTasks() int {
|
||||
return len(es.byDaysCh) + len(es.byNewerNoncurrentCh)
|
||||
w := es.workers.Load()
|
||||
if w == nil || len(*w) == 0 {
|
||||
return 0
|
||||
}
|
||||
var tasks int
|
||||
for _, wrkr := range *w {
|
||||
tasks += len(wrkr)
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
// close closes work channels exactly once.
|
||||
func (es *expiryState) close() {
|
||||
es.once.Do(func() {
|
||||
xioutil.SafeClose(es.byDaysCh)
|
||||
xioutil.SafeClose(es.byNewerNoncurrentCh)
|
||||
})
|
||||
// enqueueTierJournalEntry enqueues a tier journal entry referring to a remote
|
||||
// object corresponding to a 'replaced' object versions. This applies only to
|
||||
// non-versioned or version suspended buckets.
|
||||
func (es *expiryState) enqueueTierJournalEntry(je jentry) {
|
||||
wrkr := es.getWorkerCh(je.OpHash())
|
||||
if wrkr == nil {
|
||||
es.stats.missedTierJournalTasks.Add(1)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-GlobalContext.Done():
|
||||
case wrkr <- je:
|
||||
default:
|
||||
es.stats.missedTierJournalTasks.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
// enqueueFreeVersion enqueues a free version to be deleted
|
||||
func (es *expiryState) enqueueFreeVersion(oi ObjectInfo) {
|
||||
task := freeVersionTask{ObjectInfo: oi}
|
||||
wrkr := es.getWorkerCh(task.OpHash())
|
||||
if wrkr == nil {
|
||||
es.stats.missedFreeVersTasks.Add(1)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-GlobalContext.Done():
|
||||
case wrkr <- task:
|
||||
default:
|
||||
es.stats.missedFreeVersTasks.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
// enqueueByDays enqueues object versions expired by days for expiry.
|
||||
func (es *expiryState) enqueueByDays(oi ObjectInfo, event lifecycle.Event, src lcEventSrc) {
|
||||
task := expiryTask{objInfo: oi, event: event, src: src}
|
||||
wrkr := es.getWorkerCh(task.OpHash())
|
||||
if wrkr == nil {
|
||||
es.stats.missedExpiryTasks.Add(1)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-GlobalContext.Done():
|
||||
es.close()
|
||||
case es.byDaysCh <- expiryTask{objInfo: oi, event: event, src: src}:
|
||||
case wrkr <- task:
|
||||
default:
|
||||
es.stats.missedExpiryTasks.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
// enqueueByNewerNoncurrent enqueues object versions expired by
|
||||
// NewerNoncurrentVersions limit for expiry.
|
||||
func (es *expiryState) enqueueByNewerNoncurrent(bucket string, versions []ObjectToDelete, lcEvent lifecycle.Event) {
|
||||
task := newerNoncurrentTask{bucket: bucket, versions: versions, event: lcEvent}
|
||||
wrkr := es.getWorkerCh(task.OpHash())
|
||||
if wrkr == nil {
|
||||
es.stats.missedExpiryTasks.Add(1)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-GlobalContext.Done():
|
||||
es.close()
|
||||
case es.byNewerNoncurrentCh <- newerNoncurrentTask{bucket: bucket, versions: versions, event: lcEvent}:
|
||||
case wrkr <- task:
|
||||
default:
|
||||
es.stats.missedExpiryTasks.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
// globalExpiryState is the per-node instance which manages all ILM expiry tasks.
|
||||
var globalExpiryState *expiryState
|
||||
|
||||
func newExpiryState() *expiryState {
|
||||
return &expiryState{
|
||||
byDaysCh: make(chan expiryTask, 100000),
|
||||
byNewerNoncurrentCh: make(chan newerNoncurrentTask, 100000),
|
||||
// newExpiryState creates an expiryState with buffered channels allocated for
|
||||
// each ILM expiry task type.
|
||||
func newExpiryState(ctx context.Context, objAPI ObjectLayer, n int) *expiryState {
|
||||
es := &expiryState{
|
||||
ctx: ctx,
|
||||
objAPI: objAPI,
|
||||
}
|
||||
workers := make([]chan expiryOp, 0, n)
|
||||
es.workers.Store(&workers)
|
||||
es.ResizeWorkers(n)
|
||||
return es
|
||||
}
|
||||
|
||||
func (es *expiryState) getWorkerCh(h uint64) chan<- expiryOp {
|
||||
w := es.workers.Load()
|
||||
if w == nil || len(*w) == 0 {
|
||||
return nil
|
||||
}
|
||||
workers := *w
|
||||
return workers[h%uint64(len(workers))]
|
||||
}
|
||||
|
||||
func (es *expiryState) ResizeWorkers(n int) {
|
||||
// Lock to avoid multiple resizes to happen at the same time.
|
||||
es.mu.Lock()
|
||||
defer es.mu.Unlock()
|
||||
var workers []chan expiryOp
|
||||
if v := es.workers.Load(); v != nil {
|
||||
// Copy to new array.
|
||||
workers = append(workers, *v...)
|
||||
}
|
||||
|
||||
if n == len(workers) || n < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
for len(workers) < n {
|
||||
input := make(chan expiryOp, 10000)
|
||||
workers = append(workers, input)
|
||||
go es.Worker(input)
|
||||
es.stats.workers.Add(1)
|
||||
}
|
||||
|
||||
for len(workers) > n {
|
||||
worker := workers[len(workers)-1]
|
||||
workers = workers[:len(workers)-1]
|
||||
worker <- expiryOp(nil)
|
||||
es.stats.workers.Add(-1)
|
||||
}
|
||||
// Atomically replace workers.
|
||||
es.workers.Store(&workers)
|
||||
}
|
||||
|
||||
// Worker handles 4 types of expiration tasks.
|
||||
// 1. Expiry of objects, includes regular and transitioned objects
|
||||
// 2. Expiry of noncurrent versions due to NewerNoncurrentVersions
|
||||
// 3. Expiry of free-versions, for remote objects of transitioned object which have been expired since.
|
||||
// 4. Expiry of remote objects corresponding to objects in a
|
||||
// non-versioned/version suspended buckets
|
||||
func (es *expiryState) Worker(input <-chan expiryOp) {
|
||||
for {
|
||||
select {
|
||||
case <-es.ctx.Done():
|
||||
return
|
||||
case v, ok := <-input:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if v == nil {
|
||||
// ResizeWorkers signaling worker to quit
|
||||
return
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case expiryTask:
|
||||
if v.objInfo.TransitionedObject.Status != "" {
|
||||
applyExpiryOnTransitionedObject(es.ctx, es.objAPI, v.objInfo, v.event, v.src)
|
||||
} else {
|
||||
applyExpiryOnNonTransitionedObjects(es.ctx, es.objAPI, v.objInfo, v.event, v.src)
|
||||
}
|
||||
case newerNoncurrentTask:
|
||||
deleteObjectVersions(es.ctx, es.objAPI, v.bucket, v.versions, v.event)
|
||||
case jentry:
|
||||
logger.LogIf(es.ctx, deleteObjectFromRemoteTier(es.ctx, v.ObjName, v.VersionID, v.TierName))
|
||||
case freeVersionTask:
|
||||
oi := v.ObjectInfo
|
||||
traceFn := globalLifecycleSys.trace(oi)
|
||||
if !oi.TransitionedObject.FreeVersion {
|
||||
// nothing to be done
|
||||
return
|
||||
}
|
||||
|
||||
ignoreNotFoundErr := func(err error) error {
|
||||
switch {
|
||||
case isErrVersionNotFound(err), isErrObjectNotFound(err):
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// Remove the remote object
|
||||
err := deleteObjectFromRemoteTier(es.ctx, oi.TransitionedObject.Name, oi.TransitionedObject.VersionID, oi.TransitionedObject.Tier)
|
||||
if ignoreNotFoundErr(err) != nil {
|
||||
logger.LogIf(es.ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove this free version
|
||||
_, err = es.objAPI.DeleteObject(es.ctx, oi.Bucket, oi.Name, ObjectOptions{
|
||||
VersionID: oi.VersionID,
|
||||
InclFreeVersions: true,
|
||||
})
|
||||
if err == nil {
|
||||
auditLogLifecycle(es.ctx, oi, ILMFreeVersionDelete, nil, traceFn)
|
||||
}
|
||||
if ignoreNotFoundErr(err) != nil {
|
||||
logger.LogIf(es.ctx, err)
|
||||
}
|
||||
default:
|
||||
logger.LogIf(es.ctx, fmt.Errorf("Invalid work type - %v", v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initBackgroundExpiry(ctx context.Context, objectAPI ObjectLayer) {
|
||||
globalExpiryState = newExpiryState()
|
||||
|
||||
workerSize, _ := strconv.Atoi(env.Get("_MINIO_ILM_EXPIRY_WORKERS", strconv.Itoa((runtime.GOMAXPROCS(0)+1)/2)))
|
||||
if workerSize == 0 {
|
||||
workerSize = 4
|
||||
}
|
||||
ewk, err := workers.New(workerSize)
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
}
|
||||
|
||||
nwk, err := workers.New(workerSize)
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for t := range globalExpiryState.byDaysCh {
|
||||
ewk.Take()
|
||||
go func(t expiryTask) {
|
||||
defer ewk.Give()
|
||||
if t.objInfo.TransitionedObject.Status != "" {
|
||||
applyExpiryOnTransitionedObject(ctx, objectAPI, t.objInfo, t.event, t.src)
|
||||
} else {
|
||||
applyExpiryOnNonTransitionedObjects(ctx, objectAPI, t.objInfo, t.event, t.src)
|
||||
}
|
||||
}(t)
|
||||
}
|
||||
ewk.Wait()
|
||||
}()
|
||||
go func() {
|
||||
for t := range globalExpiryState.byNewerNoncurrentCh {
|
||||
nwk.Take()
|
||||
go func(t newerNoncurrentTask) {
|
||||
defer nwk.Give()
|
||||
deleteObjectVersions(ctx, objectAPI, t.bucket, t.versions, t.event)
|
||||
}(t)
|
||||
}
|
||||
nwk.Wait()
|
||||
}()
|
||||
globalExpiryState = newExpiryState(ctx, objectAPI, globalILMConfig.getExpirationWorkers())
|
||||
}
|
||||
|
||||
// newerNoncurrentTask encapsulates arguments required by worker to expire objects
|
||||
@@ -258,6 +438,10 @@ func newTransitionState(ctx context.Context) *transitionState {
|
||||
// of transition workers.
|
||||
func (t *transitionState) Init(objAPI ObjectLayer) {
|
||||
n := globalAPIConfig.getTransitionWorkers()
|
||||
// Prefer ilm.transition_workers over now deprecated api.transition_workers
|
||||
if tw := globalILMConfig.getTransitionWorkers(); tw > 0 {
|
||||
n = tw
|
||||
}
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
@@ -395,12 +579,15 @@ func enqueueTransitionImmediate(obj ObjectInfo, src lcEventSrc) {
|
||||
//
|
||||
// 1. when a restored (via PostRestoreObject API) object expires.
|
||||
// 2. when a transitioned object expires (based on an ILM rule).
|
||||
func expireTransitionedObject(ctx context.Context, objectAPI ObjectLayer, oi *ObjectInfo, lcOpts lifecycle.ObjectOpts, lcEvent lifecycle.Event, src lcEventSrc) error {
|
||||
func expireTransitionedObject(ctx context.Context, objectAPI ObjectLayer, oi *ObjectInfo, lcEvent lifecycle.Event, src lcEventSrc) error {
|
||||
traceFn := globalLifecycleSys.trace(*oi)
|
||||
var opts ObjectOptions
|
||||
opts.Versioned = globalBucketVersioningSys.PrefixEnabled(oi.Bucket, oi.Name)
|
||||
opts.VersionID = lcOpts.VersionID
|
||||
opts.Expiration = ExpirationOptions{Expire: true}
|
||||
opts := ObjectOptions{
|
||||
Versioned: globalBucketVersioningSys.PrefixEnabled(oi.Bucket, oi.Name),
|
||||
Expiration: ExpirationOptions{Expire: true},
|
||||
}
|
||||
if lcEvent.Action.DeleteVersioned() {
|
||||
opts.VersionID = oi.VersionID
|
||||
}
|
||||
tags := newLifecycleAuditEvent(src, lcEvent).Tags()
|
||||
if lcEvent.Action.DeleteRestored() {
|
||||
// delete locally restored copy of object or object version
|
||||
@@ -415,18 +602,18 @@ func expireTransitionedObject(ctx context.Context, objectAPI ObjectLayer, oi *Ob
|
||||
}
|
||||
return err
|
||||
}
|
||||
// When an object is past expiry or when a transitioned object is being
|
||||
// deleted, 'mark' the data in the remote tier for delete.
|
||||
entry := jentry{
|
||||
ObjName: oi.TransitionedObject.Name,
|
||||
VersionID: oi.TransitionedObject.VersionID,
|
||||
TierName: oi.TransitionedObject.Tier,
|
||||
|
||||
// Delete remote object from warm-tier
|
||||
err := deleteObjectFromRemoteTier(ctx, oi.TransitionedObject.Name, oi.TransitionedObject.VersionID, oi.TransitionedObject.Tier)
|
||||
if err == nil {
|
||||
// Skip adding free version since we successfully deleted the
|
||||
// remote object
|
||||
opts.SkipFreeVersion = true
|
||||
} else {
|
||||
logger.LogIf(ctx, err)
|
||||
}
|
||||
if err := globalTierJournal.AddEntry(entry); err != nil {
|
||||
return err
|
||||
}
|
||||
// Delete metadata on source, now that data in remote tier has been
|
||||
// marked for deletion.
|
||||
|
||||
// Now, delete object from hot-tier namespace
|
||||
if _, err := objectAPI.DeleteObject(ctx, oi.Bucket, oi.Name, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -435,13 +622,13 @@ func expireTransitionedObject(ctx context.Context, objectAPI ObjectLayer, oi *Ob
|
||||
defer auditLogLifecycle(ctx, *oi, ILMExpiry, tags, traceFn)
|
||||
|
||||
eventName := event.ObjectRemovedDelete
|
||||
if lcOpts.DeleteMarker {
|
||||
if oi.DeleteMarker {
|
||||
eventName = event.ObjectRemovedDeleteMarkerCreated
|
||||
}
|
||||
objInfo := ObjectInfo{
|
||||
Name: oi.Name,
|
||||
VersionID: lcOpts.VersionID,
|
||||
DeleteMarker: lcOpts.DeleteMarker,
|
||||
VersionID: oi.VersionID,
|
||||
DeleteMarker: oi.DeleteMarker,
|
||||
}
|
||||
// Notify object deleted event.
|
||||
sendEvent(eventArgs{
|
||||
@@ -471,7 +658,14 @@ func genTransitionObjName(bucket string) (string, error) {
|
||||
// storage specified by the transition ARN, the metadata is left behind on source cluster and original content
|
||||
// is moved to the transition tier. Note that in the case of encrypted objects, entire encrypted stream is moved
|
||||
// to the transition tier without decrypting or re-encrypting.
|
||||
func transitionObject(ctx context.Context, objectAPI ObjectLayer, oi ObjectInfo, lae lcAuditEvent) error {
|
||||
func transitionObject(ctx context.Context, objectAPI ObjectLayer, oi ObjectInfo, lae lcAuditEvent) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
globalScannerMetrics.timeILM(lae.Action)(1)
|
||||
}()
|
||||
|
||||
opts := ObjectOptions{
|
||||
Transition: TransitionOptions{
|
||||
Status: lifecycle.TransitionPending,
|
||||
|
||||
@@ -289,7 +289,7 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
|
||||
return
|
||||
}
|
||||
|
||||
// Extract all the litsObjectsV1 query params to their native values.
|
||||
// Extract all the listObjectsV1 query params to their native values.
|
||||
prefix, marker, delimiter, maxKeys, encodingType, s3Error := getListObjectsV1Args(r.Form)
|
||||
if s3Error != ErrNone {
|
||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL)
|
||||
|
||||
+15
-18
@@ -25,6 +25,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/cachevalue"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
)
|
||||
|
||||
@@ -42,44 +43,40 @@ func NewBucketQuotaSys() *BucketQuotaSys {
|
||||
return &BucketQuotaSys{}
|
||||
}
|
||||
|
||||
var bucketStorageCache timedValue
|
||||
var bucketStorageCache = cachevalue.New[DataUsageInfo]()
|
||||
|
||||
// Init initialize bucket quota.
|
||||
func (sys *BucketQuotaSys) Init(objAPI ObjectLayer) {
|
||||
bucketStorageCache.Once.Do(func() {
|
||||
// Set this to 10 secs since its enough, as scanner
|
||||
// does not update the bucket usage values frequently.
|
||||
bucketStorageCache.TTL = 10 * time.Second
|
||||
// Rely on older value if usage loading fails from disk.
|
||||
bucketStorageCache.Relax = true
|
||||
bucketStorageCache.Update = func() (interface{}, error) {
|
||||
bucketStorageCache.InitOnce(10*time.Second,
|
||||
cachevalue.Opts{ReturnLastGood: true, NoWait: true},
|
||||
func() (DataUsageInfo, error) {
|
||||
ctx, done := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer done()
|
||||
|
||||
return loadDataUsageFromBackend(ctx, objAPI)
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// GetBucketUsageInfo return bucket usage info for a given bucket
|
||||
func (sys *BucketQuotaSys) GetBucketUsageInfo(bucket string) (BucketUsageInfo, error) {
|
||||
v, err := bucketStorageCache.Get()
|
||||
dui, err := bucketStorageCache.Get()
|
||||
timedout := OperationTimedOut{}
|
||||
if err != nil && !errors.Is(err, context.DeadlineExceeded) && !errors.As(err, &timedout) {
|
||||
if v != nil {
|
||||
if len(dui.BucketsUsage) > 0 {
|
||||
logger.LogOnceIf(GlobalContext, fmt.Errorf("unable to retrieve usage information for bucket: %s, relying on older value cached in-memory: err(%v)", bucket, err), "bucket-usage-cache-"+bucket)
|
||||
} else {
|
||||
logger.LogOnceIf(GlobalContext, errors.New("unable to retrieve usage information for bucket: %s, no reliable usage value available - quota will not be enforced"), "bucket-usage-empty-"+bucket)
|
||||
}
|
||||
}
|
||||
|
||||
var bui BucketUsageInfo
|
||||
dui, ok := v.(DataUsageInfo)
|
||||
if ok {
|
||||
bui = dui.BucketsUsage[bucket]
|
||||
if len(dui.BucketsUsage) > 0 {
|
||||
bui, ok := dui.BucketsUsage[bucket]
|
||||
if ok {
|
||||
return bui, nil
|
||||
}
|
||||
}
|
||||
|
||||
return bui, nil
|
||||
return BucketUsageInfo{}, nil
|
||||
}
|
||||
|
||||
// parseBucketQuota parses BucketQuota from json
|
||||
|
||||
@@ -464,7 +464,7 @@ func (r *ReplicationStats) getLatestReplicationStats(bucket string) (s BucketSta
|
||||
return r.calculateBucketReplicationStats(bucket, bucketStats)
|
||||
}
|
||||
|
||||
func (r *ReplicationStats) incQ(bucket string, sz int64, isDeleleRepl bool, opType replication.Type) {
|
||||
func (r *ReplicationStats) incQ(bucket string, sz int64, isDeleteRepl bool, opType replication.Type) {
|
||||
r.qCache.Lock()
|
||||
defer r.qCache.Unlock()
|
||||
v, ok := r.qCache.bucketStats[bucket]
|
||||
|
||||
@@ -3393,7 +3393,7 @@ func (p *ReplicationPool) persistToDrive(ctx context.Context, v MRFReplicateEntr
|
||||
}
|
||||
|
||||
globalLocalDrivesMu.RLock()
|
||||
localDrives := globalLocalDrives
|
||||
localDrives := cloneDrives(globalLocalDrives)
|
||||
globalLocalDrivesMu.RUnlock()
|
||||
|
||||
for _, localDrive := range localDrives {
|
||||
@@ -3460,7 +3460,7 @@ func (p *ReplicationPool) loadMRF() (mrfRec MRFReplicateEntries, err error) {
|
||||
}
|
||||
|
||||
globalLocalDrivesMu.RLock()
|
||||
localDrives := globalLocalDrives
|
||||
localDrives := cloneDrives(globalLocalDrives)
|
||||
globalLocalDrivesMu.RUnlock()
|
||||
|
||||
for _, localDrive := range localDrives {
|
||||
|
||||
+8
-9
@@ -27,7 +27,6 @@ import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -50,7 +49,7 @@ import (
|
||||
"github.com/minio/console/api/operations"
|
||||
consoleoauth2 "github.com/minio/console/pkg/auth/idp/oauth2"
|
||||
consoleCerts "github.com/minio/console/pkg/certs"
|
||||
"github.com/minio/kes-go"
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
@@ -83,13 +82,9 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
logger.Init(GOPATH, GOROOT)
|
||||
logger.RegisterError(config.FmtError)
|
||||
|
||||
initGlobalContext()
|
||||
|
||||
globalBatchJobsMetrics = batchJobMetrics{metrics: make(map[string]*batchJobInfo)}
|
||||
go globalBatchJobsMetrics.purgeJobMetrics()
|
||||
|
||||
@@ -395,6 +390,7 @@ func buildServerCtxt(ctx *cli.Context, ctxt *serverCtxt) (err error) {
|
||||
ctxt.ConnReadDeadline = ctx.Duration("conn-read-deadline")
|
||||
ctxt.ConnWriteDeadline = ctx.Duration("conn-write-deadline")
|
||||
ctxt.ConnClientReadDeadline = ctx.Duration("conn-client-read-deadline")
|
||||
ctxt.ConnClientWriteDeadline = ctx.Duration("conn-client-write-deadline")
|
||||
|
||||
ctxt.ShutdownTimeout = ctx.Duration("shutdown-timeout")
|
||||
ctxt.IdleTimeout = ctx.Duration("idle-timeout")
|
||||
@@ -753,7 +749,12 @@ func serverHandleEnvVars() {
|
||||
for _, endpoint := range minioEndpoints {
|
||||
if net.ParseIP(endpoint) == nil {
|
||||
// Checking if the IP is a DNS entry.
|
||||
addrs, err := globalDNSCache.LookupHost(GlobalContext, endpoint)
|
||||
lookupHost := globalDNSCache.LookupHost
|
||||
if IsKubernetes() || IsDocker() {
|
||||
lookupHost = net.DefaultResolver.LookupHost
|
||||
}
|
||||
|
||||
addrs, err := lookupHost(GlobalContext, endpoint)
|
||||
if err != nil {
|
||||
logger.FatalIf(err, "Unable to initialize MinIO server with [%s] invalid entry found in MINIO_PUBLIC_IPS", endpoint)
|
||||
}
|
||||
@@ -896,7 +897,6 @@ func handleKMSConfig() {
|
||||
}
|
||||
kmsConf = kms.Config{
|
||||
Endpoints: endpoints,
|
||||
Enclave: env.Get(kms.EnvKESEnclave, ""),
|
||||
DefaultKeyID: env.Get(kms.EnvKESKeyName, ""),
|
||||
APIKey: key,
|
||||
RootCAs: rootCAs,
|
||||
@@ -942,7 +942,6 @@ func handleKMSConfig() {
|
||||
|
||||
kmsConf = kms.Config{
|
||||
Endpoints: endpoints,
|
||||
Enclave: env.Get(kms.EnvKESEnclave, ""),
|
||||
DefaultKeyID: env.Get(kms.EnvKESKeyName, ""),
|
||||
Certificate: certificate,
|
||||
ReloadCertEvents: reloadCertEvents,
|
||||
|
||||
@@ -18,13 +18,17 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/minio/internal/auth"
|
||||
"github.com/minio/minio/internal/config/browser"
|
||||
"github.com/minio/minio/internal/kms"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/config"
|
||||
@@ -41,6 +45,7 @@ import (
|
||||
"github.com/minio/minio/internal/config/identity/openid"
|
||||
idplugin "github.com/minio/minio/internal/config/identity/plugin"
|
||||
xtls "github.com/minio/minio/internal/config/identity/tls"
|
||||
"github.com/minio/minio/internal/config/ilm"
|
||||
"github.com/minio/minio/internal/config/lambda"
|
||||
"github.com/minio/minio/internal/config/notify"
|
||||
"github.com/minio/minio/internal/config/policy/opa"
|
||||
@@ -74,6 +79,7 @@ func initHelp() {
|
||||
config.SubnetSubSys: subnet.DefaultKVS,
|
||||
config.CallhomeSubSys: callhome.DefaultKVS,
|
||||
config.DriveSubSys: drive.DefaultKVS,
|
||||
config.ILMSubSys: ilm.DefaultKVS,
|
||||
config.CacheSubSys: cache.DefaultKVS,
|
||||
config.BatchSubSys: batch.DefaultKVS,
|
||||
config.BrowserSubSys: browser.DefaultKVS,
|
||||
@@ -569,6 +575,7 @@ func applyDynamicConfigForSubSys(ctx context.Context, objAPI ObjectLayer, s conf
|
||||
}
|
||||
|
||||
globalAPIConfig.init(apiConfig, setDriveCounts)
|
||||
autoGenerateRootCredentials() // Generate the KMS root credentials here since we don't know whether API root access is disabled until now.
|
||||
|
||||
// Initialize remote instance transport once.
|
||||
getRemoteInstanceTransportOnce.Do(func() {
|
||||
@@ -711,6 +718,18 @@ func applyDynamicConfigForSubSys(ctx context.Context, objAPI ObjectLayer, s conf
|
||||
return fmt.Errorf("Unable to apply browser config: %w", err)
|
||||
}
|
||||
globalBrowserConfig.Update(browserCfg)
|
||||
case config.ILMSubSys:
|
||||
ilmCfg, err := ilm.LookupConfig(s[config.ILMSubSys][config.Default])
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to apply ilm config: %w", err)
|
||||
}
|
||||
if globalTransitionState != nil {
|
||||
globalTransitionState.UpdateWorkers(ilmCfg.TransitionWorkers)
|
||||
}
|
||||
if globalExpiryState != nil {
|
||||
globalExpiryState.ResizeWorkers(ilmCfg.ExpirationWorkers)
|
||||
}
|
||||
globalILMConfig.update(ilmCfg)
|
||||
}
|
||||
globalServerConfigMu.Lock()
|
||||
defer globalServerConfigMu.Unlock()
|
||||
@@ -720,6 +739,55 @@ func applyDynamicConfigForSubSys(ctx context.Context, objAPI ObjectLayer, s conf
|
||||
return nil
|
||||
}
|
||||
|
||||
// autoGenerateRootCredentials generates root credentials deterministically if
|
||||
// a KMS is configured, no manual credentials have been specified and if root
|
||||
// access is disabled.
|
||||
func autoGenerateRootCredentials() {
|
||||
if GlobalKMS == nil {
|
||||
return
|
||||
}
|
||||
if globalAPIConfig.permitRootAccess() || !globalActiveCred.Equal(auth.DefaultCredentials) {
|
||||
return
|
||||
}
|
||||
|
||||
if manager, ok := GlobalKMS.(kms.KeyManager); ok {
|
||||
stat, err := GlobalKMS.Stat(GlobalContext)
|
||||
if err != nil {
|
||||
logger.LogIf(GlobalContext, err, "Unable to generate root credentials using KMS")
|
||||
return
|
||||
}
|
||||
|
||||
aKey, err := manager.HMAC(GlobalContext, stat.DefaultKey, []byte("root access key"))
|
||||
if errors.Is(err, kes.ErrNotAllowed) {
|
||||
return // If we don't have permission to compute the HMAC, don't change the cred.
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal(err, "Unable to generate root access key using KMS")
|
||||
}
|
||||
|
||||
sKey, err := manager.HMAC(GlobalContext, stat.DefaultKey, []byte("root secret key"))
|
||||
if err != nil {
|
||||
// Here, we must have permission. Otherwise, we would have failed earlier.
|
||||
logger.Fatal(err, "Unable to generate root secret key using KMS")
|
||||
}
|
||||
|
||||
accessKey, err := auth.GenerateAccessKey(20, bytes.NewReader(aKey))
|
||||
if err != nil {
|
||||
logger.Fatal(err, "Unable to generate root access key")
|
||||
}
|
||||
secretKey, err := auth.GenerateSecretKey(32, bytes.NewReader(sKey))
|
||||
if err != nil {
|
||||
logger.Fatal(err, "Unable to generate root secret key")
|
||||
}
|
||||
|
||||
logger.Info("Automatically generated root access key and secret key with the KMS")
|
||||
globalActiveCred = auth.Credentials{
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// applyDynamicConfig will apply dynamic config values.
|
||||
// Dynamic systems should be in config.SubSystemsDynamic as well.
|
||||
func applyDynamicConfig(ctx context.Context, objAPI ObjectLayer, s config.Config) error {
|
||||
|
||||
+57
-62
@@ -529,17 +529,27 @@ func (f *folderScanner) scanFolder(ctx context.Context, folder cachedFolder, int
|
||||
len(existingFolders)+len(newFolders) >= dataScannerCompactAtFolders ||
|
||||
len(existingFolders)+len(newFolders) >= dataScannerForceCompactAtFolders
|
||||
|
||||
if len(existingFolders)+len(newFolders) > int(scannerExcessFolders.Load()) {
|
||||
if totalFolders := len(existingFolders) + len(newFolders); totalFolders > int(scannerExcessFolders.Load()) {
|
||||
prefixName := strings.TrimSuffix(folder.name, "/") + "/"
|
||||
sendEvent(eventArgs{
|
||||
EventName: event.PrefixManyFolders,
|
||||
BucketName: f.root,
|
||||
Object: ObjectInfo{
|
||||
Name: strings.TrimSuffix(folder.name, "/") + "/",
|
||||
Size: int64(len(existingFolders) + len(newFolders)),
|
||||
Name: prefixName,
|
||||
Size: int64(totalFolders),
|
||||
},
|
||||
UserAgent: "scanner",
|
||||
UserAgent: "Scanner",
|
||||
Host: globalMinioHost,
|
||||
})
|
||||
auditLogInternal(context.Background(), AuditLogOptions{
|
||||
Event: "scanner:manyprefixes",
|
||||
APIName: "Scanner",
|
||||
Bucket: f.root,
|
||||
Object: prefixName,
|
||||
Tags: map[string]interface{}{
|
||||
"x-minio-prefixes-total": strconv.Itoa(totalFolders),
|
||||
},
|
||||
})
|
||||
}
|
||||
if !into.Compacted && shouldCompact {
|
||||
into.Compacted = true
|
||||
@@ -953,15 +963,6 @@ func (i *scannerItem) applyLifecycle(ctx context.Context, o ObjectLayer, oi Obje
|
||||
console.Debugf(applyActionsLogPrefix+" lifecycle: %q Initial scan: %v\n", i.objectPath(), lcEvt.Action)
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
if lcEvt.Action != lifecycle.NoneAction {
|
||||
numVersions := uint64(1)
|
||||
if lcEvt.Action == lifecycle.DeleteAllVersionsAction {
|
||||
numVersions = uint64(oi.NumVersions)
|
||||
}
|
||||
globalScannerMetrics.timeILM(lcEvt.Action)(numVersions)
|
||||
}
|
||||
}()
|
||||
|
||||
switch lcEvt.Action {
|
||||
// This version doesn't contribute towards sizeS only when it is permanently deleted.
|
||||
@@ -981,45 +982,9 @@ func (i *scannerItem) applyLifecycle(ctx context.Context, o ObjectLayer, oi Obje
|
||||
return lcEvt.Action, size
|
||||
}
|
||||
|
||||
// applyTierObjSweep removes remote object pending deletion and the free-version
|
||||
// tracking this information.
|
||||
func (i *scannerItem) applyTierObjSweep(ctx context.Context, o ObjectLayer, oi ObjectInfo) {
|
||||
traceFn := globalLifecycleSys.trace(oi)
|
||||
if !oi.TransitionedObject.FreeVersion {
|
||||
// nothing to be done
|
||||
return
|
||||
}
|
||||
|
||||
ignoreNotFoundErr := func(err error) error {
|
||||
switch {
|
||||
case isErrVersionNotFound(err), isErrObjectNotFound(err):
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// Remove the remote object
|
||||
err := deleteObjectFromRemoteTier(ctx, oi.TransitionedObject.Name, oi.TransitionedObject.VersionID, oi.TransitionedObject.Tier)
|
||||
if ignoreNotFoundErr(err) != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove this free version
|
||||
_, err = o.DeleteObject(ctx, oi.Bucket, oi.Name, ObjectOptions{
|
||||
VersionID: oi.VersionID,
|
||||
InclFreeVersions: true,
|
||||
})
|
||||
if err == nil {
|
||||
auditLogLifecycle(ctx, oi, ILMFreeVersionDelete, nil, traceFn)
|
||||
}
|
||||
if ignoreNotFoundErr(err) != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// applyNewerNoncurrentVersionLimit removes noncurrent versions older than the most recent NewerNoncurrentVersions configured.
|
||||
// Note: This function doesn't update sizeSummary since it always removes versions that it doesn't return.
|
||||
func (i *scannerItem) applyNewerNoncurrentVersionLimit(ctx context.Context, _ ObjectLayer, fivs []FileInfo) ([]ObjectInfo, error) {
|
||||
func (i *scannerItem) applyNewerNoncurrentVersionLimit(ctx context.Context, _ ObjectLayer, fivs []FileInfo, expState *expiryState) ([]ObjectInfo, error) {
|
||||
done := globalScannerMetrics.time(scannerMetricApplyNonCurrent)
|
||||
defer done()
|
||||
|
||||
@@ -1086,14 +1051,14 @@ func (i *scannerItem) applyNewerNoncurrentVersionLimit(ctx context.Context, _ Ob
|
||||
})
|
||||
}
|
||||
|
||||
globalExpiryState.enqueueByNewerNoncurrent(i.bucket, toDel, event)
|
||||
expState.enqueueByNewerNoncurrent(i.bucket, toDel, event)
|
||||
return objectInfos, nil
|
||||
}
|
||||
|
||||
// applyVersionActions will apply lifecycle checks on all versions of a scanned item. Returns versions that remain
|
||||
// after applying lifecycle checks configured.
|
||||
func (i *scannerItem) applyVersionActions(ctx context.Context, o ObjectLayer, fivs []FileInfo) ([]ObjectInfo, error) {
|
||||
objInfos, err := i.applyNewerNoncurrentVersionLimit(ctx, o, fivs)
|
||||
func (i *scannerItem) applyVersionActions(ctx context.Context, o ObjectLayer, fivs []FileInfo, expState *expiryState) ([]ObjectInfo, error) {
|
||||
objInfos, err := i.applyNewerNoncurrentVersionLimit(ctx, o, fivs, expState)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1107,18 +1072,18 @@ func (i *scannerItem) applyVersionActions(ctx context.Context, o ObjectLayer, fi
|
||||
Object: ObjectInfo{
|
||||
Name: i.objectPath(),
|
||||
},
|
||||
UserAgent: "Internal: [Scanner]",
|
||||
UserAgent: "Scanner",
|
||||
Host: globalLocalNodeName,
|
||||
RespElements: map[string]string{"x-minio-versions": strconv.Itoa(len(fivs))},
|
||||
RespElements: map[string]string{"x-minio-versions": strconv.Itoa(len(objInfos))},
|
||||
})
|
||||
|
||||
auditLogInternal(context.Background(), AuditLogOptions{
|
||||
Event: "scanner:manyversions",
|
||||
APIName: ILMExpiry,
|
||||
APIName: "Scanner",
|
||||
Bucket: i.bucket,
|
||||
Object: i.objectPath(),
|
||||
Tags: map[string]interface{}{
|
||||
"x-minio-versions": strconv.Itoa(len(fivs)),
|
||||
"x-minio-versions": strconv.Itoa(len(objInfos)),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1218,7 +1183,17 @@ func applyTransitionRule(event lifecycle.Event, src lcEventSrc, obj ObjectInfo)
|
||||
}
|
||||
|
||||
func applyExpiryOnTransitionedObject(ctx context.Context, objLayer ObjectLayer, obj ObjectInfo, lcEvent lifecycle.Event, src lcEventSrc) bool {
|
||||
if err := expireTransitionedObject(ctx, objLayer, &obj, obj.ToLifecycleOpts(), lcEvent, src); err != nil {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Note: DeleteAllVersions action is not supported for
|
||||
// transitioned objects
|
||||
globalScannerMetrics.timeILM(lcEvent.Action)(1)
|
||||
}()
|
||||
|
||||
if err = expireTransitionedObject(ctx, objLayer, &obj, lcEvent, src); err != nil {
|
||||
if isErrObjectNotFound(err) || isErrVersionNotFound(err) {
|
||||
return false
|
||||
}
|
||||
@@ -1245,8 +1220,25 @@ func applyExpiryOnNonTransitionedObjects(ctx context.Context, objLayer ObjectLay
|
||||
if lcEvent.Action.DeleteAll() {
|
||||
opts.DeletePrefix = true
|
||||
}
|
||||
var (
|
||||
dobj ObjectInfo
|
||||
err error
|
||||
)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
obj, err := objLayer.DeleteObject(ctx, obj.Bucket, obj.Name, opts)
|
||||
if lcEvent.Action != lifecycle.NoneAction {
|
||||
numVersions := uint64(1)
|
||||
if lcEvent.Action == lifecycle.DeleteAllVersionsAction {
|
||||
numVersions = uint64(obj.NumVersions)
|
||||
}
|
||||
globalScannerMetrics.timeILM(lcEvent.Action)(numVersions)
|
||||
}
|
||||
}()
|
||||
|
||||
dobj, err = objLayer.DeleteObject(ctx, obj.Bucket, obj.Name, opts)
|
||||
if err != nil {
|
||||
if isErrObjectNotFound(err) || isErrVersionNotFound(err) {
|
||||
return false
|
||||
@@ -1255,10 +1247,13 @@ func applyExpiryOnNonTransitionedObjects(ctx context.Context, objLayer ObjectLay
|
||||
logger.LogOnceIf(ctx, err, "non-transition-expiry")
|
||||
return false
|
||||
}
|
||||
if dobj.Name == "" {
|
||||
dobj = obj
|
||||
}
|
||||
|
||||
tags := newLifecycleAuditEvent(src, lcEvent).Tags()
|
||||
// Send audit for the lifecycle delete operation
|
||||
auditLogLifecycle(ctx, obj, ILMExpiry, tags, traceFn)
|
||||
auditLogLifecycle(ctx, dobj, ILMExpiry, tags, traceFn)
|
||||
|
||||
eventName := event.ObjectRemovedDelete
|
||||
if obj.DeleteMarker {
|
||||
@@ -1268,8 +1263,8 @@ func applyExpiryOnNonTransitionedObjects(ctx context.Context, objLayer ObjectLay
|
||||
// Notify object deleted event.
|
||||
sendEvent(eventArgs{
|
||||
EventName: eventName,
|
||||
BucketName: obj.Bucket,
|
||||
Object: obj,
|
||||
BucketName: dobj.Bucket,
|
||||
Object: dobj,
|
||||
UserAgent: "Internal: [ILM-Expiry]",
|
||||
Host: globalLocalNodeName,
|
||||
})
|
||||
|
||||
@@ -39,14 +39,20 @@ func TestApplyNewerNoncurrentVersionsLimit(t *testing.T) {
|
||||
globalBucketMetadataSys = NewBucketMetadataSys()
|
||||
globalBucketObjectLockSys = &BucketObjectLockSys{}
|
||||
globalBucketVersioningSys = &BucketVersioningSys{}
|
||||
globalExpiryState = newExpiryState()
|
||||
es := newExpiryState(context.Background(), objAPI, 0)
|
||||
workers := []chan expiryOp{make(chan expiryOp)}
|
||||
es.workers.Store(&workers)
|
||||
globalExpiryState = es
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
expired := make([]ObjectToDelete, 0, 5)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for t := range globalExpiryState.byNewerNoncurrentCh {
|
||||
expired = append(expired, t.versions...)
|
||||
workers := globalExpiryState.workers.Load()
|
||||
for t := range (*workers)[0] {
|
||||
if t, ok := t.(newerNoncurrentTask); ok {
|
||||
expired = append(expired, t.versions...)
|
||||
}
|
||||
}
|
||||
}()
|
||||
lc := lifecycle.Lifecycle{
|
||||
@@ -116,7 +122,7 @@ func TestApplyNewerNoncurrentVersionsLimit(t *testing.T) {
|
||||
for i, fi := range fivs[:2] {
|
||||
wants[i] = fi.ToObjectInfo(bucket, obj, versioned)
|
||||
}
|
||||
gots, err := item.applyNewerNoncurrentVersionLimit(context.TODO(), objAPI, fivs)
|
||||
gots, err := item.applyNewerNoncurrentVersionLimit(context.TODO(), objAPI, fivs, es)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed with err: %v", err)
|
||||
}
|
||||
@@ -125,7 +131,7 @@ func TestApplyNewerNoncurrentVersionsLimit(t *testing.T) {
|
||||
}
|
||||
|
||||
// Close expiry state's channel to inspect object versions enqueued for expiration
|
||||
close(globalExpiryState.byNewerNoncurrentCh)
|
||||
close(workers[0])
|
||||
wg.Wait()
|
||||
for _, obj := range expired {
|
||||
switch obj.ObjectV.VersionID {
|
||||
|
||||
+8
-14
@@ -24,6 +24,7 @@ import (
|
||||
"time"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/minio/minio/internal/cachevalue"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
)
|
||||
|
||||
@@ -62,7 +63,7 @@ func storeDataUsageInBackend(ctx context.Context, objAPI ObjectLayer, dui <-chan
|
||||
}
|
||||
}
|
||||
|
||||
var prefixUsageCache timedValue
|
||||
var prefixUsageCache = cachevalue.New[map[string]uint64]()
|
||||
|
||||
// loadPrefixUsageFromBackend returns prefix usages found in passed buckets
|
||||
//
|
||||
@@ -76,12 +77,10 @@ func loadPrefixUsageFromBackend(ctx context.Context, objAPI ObjectLayer, bucket
|
||||
|
||||
cache := dataUsageCache{}
|
||||
|
||||
prefixUsageCache.Once.Do(func() {
|
||||
prefixUsageCache.TTL = 30 * time.Second
|
||||
|
||||
prefixUsageCache.InitOnce(30*time.Second,
|
||||
// No need to fail upon Update() error, fallback to old value.
|
||||
prefixUsageCache.Relax = true
|
||||
prefixUsageCache.Update = func() (interface{}, error) {
|
||||
cachevalue.Opts{ReturnLastGood: true, NoWait: true},
|
||||
func() (map[string]uint64, error) {
|
||||
m := make(map[string]uint64)
|
||||
for _, pool := range z.serverPools {
|
||||
for _, er := range pool.sets {
|
||||
@@ -106,15 +105,10 @@ func loadPrefixUsageFromBackend(ctx context.Context, objAPI ObjectLayer, bucket
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
v, _ := prefixUsageCache.Get()
|
||||
if v != nil {
|
||||
return v.(map[string]uint64), nil
|
||||
}
|
||||
|
||||
return map[string]uint64{}, nil
|
||||
return prefixUsageCache.Get()
|
||||
}
|
||||
|
||||
func loadDataUsageFromBackend(ctx context.Context, objAPI ObjectLayer) (DataUsageInfo, error) {
|
||||
|
||||
@@ -34,7 +34,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/kes-go"
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/minio/internal/crypto"
|
||||
"github.com/minio/minio/internal/etag"
|
||||
"github.com/minio/minio/internal/fips"
|
||||
|
||||
+11
-8
@@ -75,6 +75,16 @@ type Endpoint struct {
|
||||
PoolIdx, SetIdx, DiskIdx int
|
||||
}
|
||||
|
||||
// Equal returns true if endpoint == ep
|
||||
func (endpoint Endpoint) Equal(ep Endpoint) bool {
|
||||
if endpoint.IsLocal == ep.IsLocal && endpoint.PoolIdx == ep.PoolIdx && endpoint.SetIdx == ep.SetIdx && endpoint.DiskIdx == ep.DiskIdx {
|
||||
if endpoint.Path == ep.Path && endpoint.Host == ep.Host {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (endpoint Endpoint) String() string {
|
||||
if endpoint.Host == "" {
|
||||
return endpoint.Path
|
||||
@@ -567,13 +577,6 @@ func (endpoints Endpoints) GetAllStrings() (all []string) {
|
||||
func hostResolveToLocalhost(endpoint Endpoint) bool {
|
||||
hostIPs, err := getHostIP(endpoint.Hostname())
|
||||
if err != nil {
|
||||
// Log the message to console about the host resolving
|
||||
reqInfo := (&logger.ReqInfo{}).AppendTags(
|
||||
"host",
|
||||
endpoint.Hostname(),
|
||||
)
|
||||
ctx := logger.SetReqInfo(GlobalContext, reqInfo)
|
||||
logger.LogOnceIf(ctx, err, endpoint.Hostname(), logger.ErrorKind)
|
||||
return false
|
||||
}
|
||||
var loopback int
|
||||
@@ -867,7 +870,7 @@ func (p PoolEndpointList) UpdateIsLocal() error {
|
||||
))
|
||||
ctx := logger.SetReqInfo(GlobalContext,
|
||||
reqInfo)
|
||||
logger.LogOnceIf(ctx, err, endpoint.Hostname(), logger.ErrorKind)
|
||||
logger.LogOnceIf(ctx, fmt.Errorf("Unable to resolve DNS for %s: %w", endpoint, err), endpoint.Hostname(), logger.ErrorKind)
|
||||
}
|
||||
} else {
|
||||
resolvedList[endpoint] = true
|
||||
|
||||
@@ -58,8 +58,8 @@ func commonETags(etags []string) (etag string, maxima int) {
|
||||
}
|
||||
|
||||
// commonTime returns a maximally occurring time from a list of time.
|
||||
func commonTimeAndOccurence(times []time.Time, group time.Duration) (maxTime time.Time, maxima int) {
|
||||
timeOccurenceMap := make(map[int64]int, len(times))
|
||||
func commonTimeAndOccurrence(times []time.Time, group time.Duration) (maxTime time.Time, maxima int) {
|
||||
timeOccurrenceMap := make(map[int64]int, len(times))
|
||||
groupNano := group.Nanoseconds()
|
||||
// Ignore the uuid sentinel and count the rest.
|
||||
for _, t := range times {
|
||||
@@ -68,7 +68,7 @@ func commonTimeAndOccurence(times []time.Time, group time.Duration) (maxTime tim
|
||||
}
|
||||
nano := t.UnixNano()
|
||||
if group > 0 {
|
||||
for k := range timeOccurenceMap {
|
||||
for k := range timeOccurrenceMap {
|
||||
if k == nano {
|
||||
// We add to ourself later
|
||||
continue
|
||||
@@ -79,12 +79,12 @@ func commonTimeAndOccurence(times []time.Time, group time.Duration) (maxTime tim
|
||||
}
|
||||
// We are within the limit
|
||||
if diff < groupNano {
|
||||
timeOccurenceMap[k]++
|
||||
timeOccurrenceMap[k]++
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add ourself...
|
||||
timeOccurenceMap[nano]++
|
||||
timeOccurrenceMap[nano]++
|
||||
}
|
||||
|
||||
maxima = 0 // Counter for remembering max occurrence of elements.
|
||||
@@ -92,7 +92,7 @@ func commonTimeAndOccurence(times []time.Time, group time.Duration) (maxTime tim
|
||||
|
||||
// Find the common cardinality from previously collected
|
||||
// occurrences of elements.
|
||||
for nano, count := range timeOccurenceMap {
|
||||
for nano, count := range timeOccurrenceMap {
|
||||
if count < maxima {
|
||||
continue
|
||||
}
|
||||
@@ -111,7 +111,7 @@ func commonTimeAndOccurence(times []time.Time, group time.Duration) (maxTime tim
|
||||
// commonTime returns a maximally occurring time from a list of time if it
|
||||
// occurs >= quorum, else return timeSentinel
|
||||
func commonTime(modTimes []time.Time, quorum int) time.Time {
|
||||
if modTime, count := commonTimeAndOccurence(modTimes, 0); count >= quorum {
|
||||
if modTime, count := commonTimeAndOccurrence(modTimes, 0); count >= quorum {
|
||||
return modTime
|
||||
}
|
||||
|
||||
|
||||
+8
-24
@@ -144,7 +144,7 @@ func listAllBuckets(ctx context.Context, storageDisks []StorageAPI, healBuckets
|
||||
|
||||
// Only heal on disks where we are sure that healing is needed. We can expand
|
||||
// this list as and when we figure out more errors can be added to this list safely.
|
||||
func shouldHealObjectOnDisk(erErr, dataErr error, meta FileInfo, latestMeta FileInfo, doinline bool) bool {
|
||||
func shouldHealObjectOnDisk(erErr, dataErr error, meta FileInfo, latestMeta FileInfo) bool {
|
||||
switch {
|
||||
case errors.Is(erErr, errFileNotFound) || errors.Is(erErr, errFileVersionNotFound):
|
||||
return true
|
||||
@@ -157,10 +157,6 @@ func shouldHealObjectOnDisk(erErr, dataErr error, meta FileInfo, latestMeta File
|
||||
// always check first.
|
||||
return true
|
||||
}
|
||||
if doinline {
|
||||
// convert small files to 'inline'
|
||||
return true
|
||||
}
|
||||
if !meta.Deleted && !meta.IsRemote() {
|
||||
// If xl.meta was read fine but there may be problem with the part.N files.
|
||||
if IsErr(dataErr, []error{
|
||||
@@ -308,7 +304,6 @@ func (er *erasureObjects) healObject(ctx context.Context, bucket string, object
|
||||
errs, latestMeta, bucket, object, scanMode)
|
||||
|
||||
var erasure Erasure
|
||||
var recreate bool
|
||||
if !latestMeta.Deleted && !latestMeta.IsRemote() {
|
||||
// Initialize erasure coding
|
||||
erasure, err = NewErasure(ctx, latestMeta.Erasure.DataBlocks,
|
||||
@@ -316,15 +311,6 @@ func (er *erasureObjects) healObject(ctx context.Context, bucket string, object
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Is only 'true' if the opts.Recreate is true and
|
||||
// the object shardSize < smallFileThreshold do not
|
||||
// set this to 'true' arbitrarily and must be only
|
||||
// 'true' with caller ask.
|
||||
recreate = (opts.Recreate &&
|
||||
!latestMeta.InlineData() &&
|
||||
len(latestMeta.Parts) == 1 &&
|
||||
erasure.ShardFileSize(latestMeta.Parts[0].ActualSize) < smallFileThreshold)
|
||||
}
|
||||
|
||||
result.ObjectSize, err = latestMeta.ToObjectInfo(bucket, object, true).GetActualSize()
|
||||
@@ -353,7 +339,7 @@ func (er *erasureObjects) healObject(ctx context.Context, bucket string, object
|
||||
driveState = madmin.DriveStateCorrupt
|
||||
}
|
||||
|
||||
if shouldHealObjectOnDisk(errs[i], dataErrs[i], partsMetadata[i], latestMeta, recreate) {
|
||||
if shouldHealObjectOnDisk(errs[i], dataErrs[i], partsMetadata[i], latestMeta) {
|
||||
outDatedDisks[i] = storageDisks[i]
|
||||
disksToHealCount++
|
||||
result.Before.Drives = append(result.Before.Drives, madmin.HealDriveInfo{
|
||||
@@ -401,7 +387,7 @@ func (er *erasureObjects) healObject(ctx context.Context, bucket string, object
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if !latestMeta.XLV1 && !latestMeta.Deleted && !recreate && disksToHealCount > latestMeta.Erasure.ParityBlocks {
|
||||
if !latestMeta.XLV1 && !latestMeta.Deleted && disksToHealCount > latestMeta.Erasure.ParityBlocks {
|
||||
// Allow for dangling deletes, on versions that have DataDir missing etc.
|
||||
// this would end up restoring the correct readable versions.
|
||||
m, err := er.deleteIfDangling(ctx, bucket, object, partsMetadata, errs, dataErrs, ObjectOptions{
|
||||
@@ -499,7 +485,7 @@ func (er *erasureObjects) healObject(ctx context.Context, bucket string, object
|
||||
|
||||
var inlineBuffers []*bytes.Buffer
|
||||
if !latestMeta.Deleted && !latestMeta.IsRemote() {
|
||||
if latestMeta.InlineData() || recreate {
|
||||
if latestMeta.InlineData() {
|
||||
inlineBuffers = make([]*bytes.Buffer, len(outDatedDisks))
|
||||
}
|
||||
|
||||
@@ -603,9 +589,8 @@ func (er *erasureObjects) healObject(ctx context.Context, bucket string, object
|
||||
return result, err
|
||||
}
|
||||
|
||||
// - Remove any parts from healed disks after its been inlined.
|
||||
// - Remove any remaining parts from outdated disks from before transition.
|
||||
if recreate || partsMetadata[i].IsRemote() {
|
||||
if partsMetadata[i].IsRemote() {
|
||||
rmDataDir := partsMetadata[i].DataDir
|
||||
disk.Delete(ctx, bucket, pathJoin(encodeDirObject(object), rmDataDir), DeleteOptions{
|
||||
Immediate: true,
|
||||
@@ -1047,10 +1032,9 @@ func healTrace(funcName healingMetric, startTime time.Time, bucket, object strin
|
||||
}
|
||||
if opts != nil {
|
||||
tr.Custom = map[string]string{
|
||||
"dry": fmt.Sprint(opts.DryRun),
|
||||
"remove": fmt.Sprint(opts.Remove),
|
||||
"recreate": fmt.Sprint(opts.Recreate),
|
||||
"mode": fmt.Sprint(opts.ScanMode),
|
||||
"dry": fmt.Sprint(opts.DryRun),
|
||||
"remove": fmt.Sprint(opts.Remove),
|
||||
"mode": fmt.Sprint(opts.ScanMode),
|
||||
}
|
||||
if result != nil {
|
||||
tr.Custom["version-id"] = result.VersionID
|
||||
|
||||
@@ -614,7 +614,7 @@ func TestHealingDanglingObject(t *testing.T) {
|
||||
resetGlobalHealState()
|
||||
defer resetGlobalHealState()
|
||||
|
||||
// Set globalStoragClass.STANDARD to EC:4 for this test
|
||||
// Set globalStorageClass.STANDARD to EC:4 for this test
|
||||
saveSC := globalStorageClass
|
||||
defer func() {
|
||||
globalStorageClass.Update(saveSC)
|
||||
|
||||
+35
-10
@@ -27,6 +27,7 @@ import (
|
||||
|
||||
"github.com/minio/minio/internal/amztime"
|
||||
"github.com/minio/minio/internal/bucket/replication"
|
||||
"github.com/minio/minio/internal/crypto"
|
||||
"github.com/minio/minio/internal/hash/sha256"
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
@@ -300,17 +301,23 @@ func findFileInfoInQuorum(ctx context.Context, metaArr []FileInfo, modTime time.
|
||||
fmt.Fprintf(h, "%v", meta.Erasure.Distribution)
|
||||
}
|
||||
|
||||
// ILM transition fields
|
||||
fmt.Fprint(h, meta.TransitionStatus)
|
||||
fmt.Fprint(h, meta.TransitionTier)
|
||||
fmt.Fprint(h, meta.TransitionedObjName)
|
||||
fmt.Fprint(h, meta.TransitionVersionID)
|
||||
if meta.IsRemote() {
|
||||
// ILM transition fields
|
||||
fmt.Fprint(h, meta.TransitionStatus)
|
||||
fmt.Fprint(h, meta.TransitionTier)
|
||||
fmt.Fprint(h, meta.TransitionedObjName)
|
||||
fmt.Fprint(h, meta.TransitionVersionID)
|
||||
}
|
||||
|
||||
// Server-side replication fields
|
||||
fmt.Fprintf(h, "%v", meta.MarkDeleted)
|
||||
fmt.Fprint(h, meta.Metadata[string(meta.ReplicationState.ReplicaStatus)])
|
||||
fmt.Fprint(h, meta.Metadata[meta.ReplicationState.ReplicationStatusInternal])
|
||||
fmt.Fprint(h, meta.Metadata[meta.ReplicationState.VersionPurgeStatusInternal])
|
||||
// If metadata says encrypted, ask for it in quorum.
|
||||
if etyp, ok := crypto.IsEncrypted(meta.Metadata); ok {
|
||||
fmt.Fprint(h, etyp)
|
||||
}
|
||||
|
||||
// If compressed, look for compressed FileInfo only
|
||||
if meta.IsCompressed() {
|
||||
fmt.Fprint(h, meta.Metadata[ReservedMetadataPrefix+"compression"])
|
||||
}
|
||||
|
||||
metaHashes[i] = hex.EncodeToString(h.Sum(nil))
|
||||
h.Reset()
|
||||
@@ -518,6 +525,7 @@ func objectQuorumFromMeta(ctx context.Context, partsMetaData []FileInfo, errs []
|
||||
const (
|
||||
tierFVID = "tier-free-versionID"
|
||||
tierFVMarker = "tier-free-marker"
|
||||
tierSkipFVID = "tier-skip-fvid"
|
||||
)
|
||||
|
||||
// SetTierFreeVersionID sets free-version's versionID. This method is used by
|
||||
@@ -544,6 +552,23 @@ func (fi *FileInfo) SetTierFreeVersion() {
|
||||
fi.Metadata[ReservedMetadataPrefixLower+tierFVMarker] = ""
|
||||
}
|
||||
|
||||
// SetSkipTierFreeVersion indicates to skip adding a tier free version id.
|
||||
// Note: Used only when expiring tiered objects and the remote content has
|
||||
// already been scheduled for deletion
|
||||
func (fi *FileInfo) SetSkipTierFreeVersion() {
|
||||
if fi.Metadata == nil {
|
||||
fi.Metadata = make(map[string]string)
|
||||
}
|
||||
fi.Metadata[ReservedMetadataPrefixLower+tierSkipFVID] = ""
|
||||
}
|
||||
|
||||
// SkipTierFreeVersion returns true if set, false otherwise.
|
||||
// See SetSkipTierVersion for its purpose.
|
||||
func (fi *FileInfo) SkipTierFreeVersion() bool {
|
||||
_, ok := fi.Metadata[ReservedMetadataPrefixLower+tierSkipFVID]
|
||||
return ok
|
||||
}
|
||||
|
||||
// TierFreeVersion returns true if version is a free-version.
|
||||
func (fi *FileInfo) TierFreeVersion() bool {
|
||||
_, ok := fi.Metadata[ReservedMetadataPrefixLower+tierFVMarker]
|
||||
|
||||
@@ -314,3 +314,11 @@ func TestTransitionInfoEquals(t *testing.T) {
|
||||
t.Fatalf("Expected to be inequal: fi %v ofi %v", fi, ofi)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkipTierFreeVersion(t *testing.T) {
|
||||
fi := newFileInfo("object", 8, 8)
|
||||
fi.SetSkipTierFreeVersion()
|
||||
if ok := fi.SkipTierFreeVersion(); !ok {
|
||||
t.Fatal("Expected SkipTierFreeVersion to be set on FileInfo but wasn't")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
|
||||
"github.com/klauspost/readahead"
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
"github.com/minio/minio/internal/config/storageclass"
|
||||
"github.com/minio/minio/internal/crypto"
|
||||
"github.com/minio/minio/internal/hash"
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
@@ -461,6 +462,11 @@ func (er erasureObjects) newMultipartUpload(ctx context.Context, bucket string,
|
||||
userDefined["content-type"] = mimedb.TypeByExtension(path.Ext(object))
|
||||
}
|
||||
|
||||
// if storageClass is standard no need to save it as part of metadata.
|
||||
if userDefined[xhttp.AmzStorageClass] == storageclass.STANDARD {
|
||||
delete(userDefined, xhttp.AmzStorageClass)
|
||||
}
|
||||
|
||||
if opts.WantChecksum != nil && opts.WantChecksum.Type.IsSet() {
|
||||
userDefined[hash.MinIOMultipartChecksum] = opts.WantChecksum.Type.String()
|
||||
}
|
||||
|
||||
+18
-2
@@ -37,6 +37,7 @@ import (
|
||||
"github.com/minio/minio/internal/bucket/lifecycle"
|
||||
"github.com/minio/minio/internal/bucket/object/lock"
|
||||
"github.com/minio/minio/internal/bucket/replication"
|
||||
"github.com/minio/minio/internal/config/storageclass"
|
||||
"github.com/minio/minio/internal/crypto"
|
||||
"github.com/minio/minio/internal/event"
|
||||
"github.com/minio/minio/internal/hash"
|
||||
@@ -937,7 +938,11 @@ func (er erasureObjects) getObjectFileInfo(ctx context.Context, bucket, object s
|
||||
onlineDisks[i] = nil
|
||||
}
|
||||
|
||||
mrfCheck <- fi.ShallowCopy()
|
||||
select {
|
||||
case mrfCheck <- fi.ShallowCopy():
|
||||
case <-ctx.Done():
|
||||
return fi, onlineMeta, onlineDisks, toObjectErr(ctx.Err(), bucket, object)
|
||||
}
|
||||
|
||||
return fi, onlineMeta, onlineDisks, nil
|
||||
}
|
||||
@@ -960,7 +965,7 @@ func (er erasureObjects) getObjectInfo(ctx context.Context, bucket, object strin
|
||||
return objInfo, nil
|
||||
}
|
||||
|
||||
// getObjectInfoAndQuroum - wrapper for reading object metadata and constructs ObjectInfo, additionally returns write quorum for the object.
|
||||
// getObjectInfoAndQuorum - wrapper for reading object metadata and constructs ObjectInfo, additionally returns write quorum for the object.
|
||||
func (er erasureObjects) getObjectInfoAndQuorum(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, wquorum int, err error) {
|
||||
fi, _, _, err := er.getObjectFileInfo(ctx, bucket, object, opts, false)
|
||||
if err != nil {
|
||||
@@ -1505,6 +1510,11 @@ func (er erasureObjects) putObject(ctx context.Context, bucket string, object st
|
||||
userDefined["content-type"] = mimedb.TypeByExtension(path.Ext(object))
|
||||
}
|
||||
|
||||
// if storageClass is standard no need to save it as part of metadata.
|
||||
if userDefined[xhttp.AmzStorageClass] == storageclass.STANDARD {
|
||||
delete(userDefined, xhttp.AmzStorageClass)
|
||||
}
|
||||
|
||||
// Fill all the necessary metadata.
|
||||
// Update `xl.meta` content on each disks.
|
||||
for index := range partsMetadata {
|
||||
@@ -1965,6 +1975,9 @@ func (er erasureObjects) DeleteObject(ctx context.Context, bucket, object string
|
||||
ExpireRestored: opts.Transition.ExpireRestored,
|
||||
}
|
||||
fi.SetTierFreeVersionID(fvID)
|
||||
if opts.SkipFreeVersion {
|
||||
fi.SetSkipTierFreeVersion()
|
||||
}
|
||||
if opts.VersionID != "" {
|
||||
fi.VersionID = opts.VersionID
|
||||
} else if opts.Versioned {
|
||||
@@ -1994,6 +2007,9 @@ func (er erasureObjects) DeleteObject(ctx context.Context, bucket, object string
|
||||
ExpireRestored: opts.Transition.ExpireRestored,
|
||||
}
|
||||
dfi.SetTierFreeVersionID(fvID)
|
||||
if opts.SkipFreeVersion {
|
||||
dfi.SetSkipTierFreeVersion()
|
||||
}
|
||||
if err = er.deleteObjectVersion(ctx, bucket, object, dfi, opts.DeleteMarker); err != nil {
|
||||
return objInfo, toObjectErr(err, bucket, object)
|
||||
}
|
||||
|
||||
@@ -1167,7 +1167,7 @@ func (z *erasureServerPools) doDecommissionInRoutine(ctx context.Context, idx in
|
||||
z.poolMetaMutex.Unlock()
|
||||
|
||||
if !failed {
|
||||
logger.Info("Decommissioning complete for pool '%s', verifying for any pending objects", poolCmdLine)
|
||||
logger.Event(dctx, "Decommissioning complete for pool '%s', verifying for any pending objects", poolCmdLine)
|
||||
err := z.checkAfterDecom(dctx, idx)
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
|
||||
@@ -432,6 +432,8 @@ func (z *erasureServerPools) rebalanceBuckets(ctx context.Context, poolIdx int)
|
||||
}
|
||||
}()
|
||||
|
||||
logger.Event(ctx, "Pool %d rebalancing is started", poolIdx+1)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -456,6 +458,8 @@ func (z *erasureServerPools) rebalanceBuckets(ctx context.Context, poolIdx int)
|
||||
z.bucketRebalanceDone(bucket, poolIdx)
|
||||
}
|
||||
|
||||
logger.Event(ctx, "Pool %d rebalancing is done", poolIdx+1)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+86
-81
@@ -39,6 +39,7 @@ import (
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
"github.com/minio/minio-go/v7/pkg/tags"
|
||||
"github.com/minio/minio/internal/bpool"
|
||||
"github.com/minio/minio/internal/cachevalue"
|
||||
"github.com/minio/minio/internal/config/storageclass"
|
||||
xioutil "github.com/minio/minio/internal/ioutil"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
@@ -174,8 +175,25 @@ func newErasureServerPools(ctx context.Context, endpointServerPools EndpointServ
|
||||
z.poolMeta = newPoolMeta(z, poolMeta{})
|
||||
z.poolMeta.dontSave = true
|
||||
|
||||
bootstrapTrace("newSharedLock", func() {
|
||||
globalLeaderLock = newSharedLock(GlobalContext, z, "leader.lock")
|
||||
})
|
||||
|
||||
// Enable background operations on
|
||||
//
|
||||
// - Disk auto healing
|
||||
// - MRF (most recently failed) healing
|
||||
// - Background expiration routine for lifecycle policies
|
||||
bootstrapTrace("initAutoHeal", func() {
|
||||
initAutoHeal(GlobalContext, z)
|
||||
})
|
||||
|
||||
bootstrapTrace("initHealMRF", func() {
|
||||
go globalMRFState.healRoutine(z)
|
||||
})
|
||||
|
||||
// initialize the object layer.
|
||||
setObjectLayer(z)
|
||||
defer setObjectLayer(z)
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
attempt := 1
|
||||
@@ -1001,20 +1019,10 @@ func (z *erasureServerPools) PutObject(ctx context.Context, bucket string, objec
|
||||
}
|
||||
|
||||
object = encodeDirObject(object)
|
||||
|
||||
if z.SinglePool() {
|
||||
if !isMinioMetaBucketName(bucket) {
|
||||
avail, err := hasSpaceFor(getDiskInfos(ctx, z.serverPools[0].getHashedSet(object).getDisks()...), data.Size())
|
||||
if err != nil {
|
||||
logger.LogOnceIf(ctx, err, "erasure-write-quorum")
|
||||
return ObjectInfo{}, toObjectErr(errErasureWriteQuorum)
|
||||
}
|
||||
if !avail {
|
||||
return ObjectInfo{}, toObjectErr(errDiskFull)
|
||||
}
|
||||
}
|
||||
return z.serverPools[0].PutObject(ctx, bucket, object, data, opts)
|
||||
}
|
||||
|
||||
if !opts.NoLock {
|
||||
ns := z.NewNSLock(bucket, object)
|
||||
lkctx, err := ns.GetLock(ctx, globalOperationTimeout)
|
||||
@@ -1196,6 +1204,13 @@ func (z *erasureServerPools) DeleteObjects(ctx context.Context, bucket string, o
|
||||
}
|
||||
|
||||
func (z *erasureServerPools) CopyObject(ctx context.Context, srcBucket, srcObject, dstBucket, dstObject string, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (objInfo ObjectInfo, err error) {
|
||||
if err := checkCopyObjArgs(ctx, srcBucket, srcObject); err != nil {
|
||||
return ObjectInfo{}, err
|
||||
}
|
||||
if err := checkCopyObjArgs(ctx, dstBucket, dstObject); err != nil {
|
||||
return ObjectInfo{}, err
|
||||
}
|
||||
|
||||
srcObject = encodeDirObject(srcObject)
|
||||
dstObject = encodeDirObject(dstObject)
|
||||
|
||||
@@ -1574,21 +1589,11 @@ func (z *erasureServerPools) ListMultipartUploads(ctx context.Context, bucket, p
|
||||
|
||||
// Initiate a new multipart upload on a hashedSet based on object name.
|
||||
func (z *erasureServerPools) NewMultipartUpload(ctx context.Context, bucket, object string, opts ObjectOptions) (*NewMultipartUploadResult, error) {
|
||||
if err := checkNewMultipartArgs(ctx, bucket, object, z); err != nil {
|
||||
if err := checkNewMultipartArgs(ctx, bucket, object); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if z.SinglePool() {
|
||||
if !isMinioMetaBucketName(bucket) {
|
||||
avail, err := hasSpaceFor(getDiskInfos(ctx, z.serverPools[0].getHashedSet(object).getDisks()...), -1)
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
return nil, toObjectErr(errErasureWriteQuorum)
|
||||
}
|
||||
if !avail {
|
||||
return nil, toObjectErr(errDiskFull)
|
||||
}
|
||||
}
|
||||
return z.serverPools[0].NewMultipartUpload(ctx, bucket, object, opts)
|
||||
}
|
||||
|
||||
@@ -1621,7 +1626,7 @@ func (z *erasureServerPools) NewMultipartUpload(ctx context.Context, bucket, obj
|
||||
|
||||
// Copies a part of an object from source hashedSet to destination hashedSet.
|
||||
func (z *erasureServerPools) CopyObjectPart(ctx context.Context, srcBucket, srcObject, destBucket, destObject string, uploadID string, partID int, startOffset int64, length int64, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (PartInfo, error) {
|
||||
if err := checkNewMultipartArgs(ctx, srcBucket, srcObject, z); err != nil {
|
||||
if err := checkNewMultipartArgs(ctx, srcBucket, srcObject); err != nil {
|
||||
return PartInfo{}, err
|
||||
}
|
||||
|
||||
@@ -1631,7 +1636,7 @@ func (z *erasureServerPools) CopyObjectPart(ctx context.Context, srcBucket, srcO
|
||||
|
||||
// PutObjectPart - writes part of an object to hashedSet based on the object name.
|
||||
func (z *erasureServerPools) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *PutObjReader, opts ObjectOptions) (PartInfo, error) {
|
||||
if err := checkPutObjectPartArgs(ctx, bucket, object, uploadID, z); err != nil {
|
||||
if err := checkPutObjectPartArgs(ctx, bucket, object, uploadID); err != nil {
|
||||
return PartInfo{}, err
|
||||
}
|
||||
|
||||
@@ -1663,7 +1668,7 @@ func (z *erasureServerPools) PutObjectPart(ctx context.Context, bucket, object,
|
||||
}
|
||||
|
||||
func (z *erasureServerPools) GetMultipartInfo(ctx context.Context, bucket, object, uploadID string, opts ObjectOptions) (MultipartInfo, error) {
|
||||
if err := checkListPartsArgs(ctx, bucket, object, uploadID, z); err != nil {
|
||||
if err := checkListPartsArgs(ctx, bucket, object, uploadID); err != nil {
|
||||
return MultipartInfo{}, err
|
||||
}
|
||||
|
||||
@@ -1694,7 +1699,7 @@ func (z *erasureServerPools) GetMultipartInfo(ctx context.Context, bucket, objec
|
||||
|
||||
// ListObjectParts - lists all uploaded parts to an object in hashedSet.
|
||||
func (z *erasureServerPools) ListObjectParts(ctx context.Context, bucket, object, uploadID string, partNumberMarker int, maxParts int, opts ObjectOptions) (ListPartsInfo, error) {
|
||||
if err := checkListPartsArgs(ctx, bucket, object, uploadID, z); err != nil {
|
||||
if err := checkListPartsArgs(ctx, bucket, object, uploadID); err != nil {
|
||||
return ListPartsInfo{}, err
|
||||
}
|
||||
|
||||
@@ -1723,7 +1728,7 @@ func (z *erasureServerPools) ListObjectParts(ctx context.Context, bucket, object
|
||||
|
||||
// Aborts an in-progress multipart operation on hashedSet based on the object name.
|
||||
func (z *erasureServerPools) AbortMultipartUpload(ctx context.Context, bucket, object, uploadID string, opts ObjectOptions) error {
|
||||
if err := checkAbortMultipartArgs(ctx, bucket, object, uploadID, z); err != nil {
|
||||
if err := checkAbortMultipartArgs(ctx, bucket, object, uploadID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1754,7 +1759,7 @@ func (z *erasureServerPools) AbortMultipartUpload(ctx context.Context, bucket, o
|
||||
|
||||
// CompleteMultipartUpload - completes a pending multipart transaction, on hashedSet based on object name.
|
||||
func (z *erasureServerPools) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart, opts ObjectOptions) (objInfo ObjectInfo, err error) {
|
||||
if err = checkCompleteMultipartArgs(ctx, bucket, object, uploadID, z); err != nil {
|
||||
if err = checkCompleteMultipartArgs(ctx, bucket, object, uploadID); err != nil {
|
||||
return objInfo, err
|
||||
}
|
||||
|
||||
@@ -1853,21 +1858,20 @@ func (z *erasureServerPools) deleteAll(ctx context.Context, bucket, prefix strin
|
||||
}
|
||||
}
|
||||
|
||||
var listBucketsCache timedValue
|
||||
var listBucketsCache = cachevalue.New[[]BucketInfo]()
|
||||
|
||||
// List all buckets from one of the serverPools, we are not doing merge
|
||||
// sort here just for simplification. As per design it is assumed
|
||||
// that all buckets are present on all serverPools.
|
||||
func (z *erasureServerPools) ListBuckets(ctx context.Context, opts BucketOptions) (buckets []BucketInfo, err error) {
|
||||
if opts.Cached {
|
||||
listBucketsCache.Once.Do(func() {
|
||||
listBucketsCache.TTL = time.Second
|
||||
|
||||
listBucketsCache.Relax = true
|
||||
listBucketsCache.Update = func() (interface{}, error) {
|
||||
listBucketsCache.InitOnce(time.Second,
|
||||
cachevalue.Opts{ReturnLastGood: true, NoWait: true},
|
||||
func() ([]BucketInfo, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
buckets, err = z.s3Peer.ListBuckets(ctx, opts)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1878,15 +1882,10 @@ func (z *erasureServerPools) ListBuckets(ctx context.Context, opts BucketOptions
|
||||
}
|
||||
}
|
||||
return buckets, nil
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
v, _ := listBucketsCache.Get()
|
||||
if v != nil {
|
||||
return v.([]BucketInfo), nil
|
||||
}
|
||||
|
||||
return buckets, nil
|
||||
return listBucketsCache.Get()
|
||||
}
|
||||
|
||||
buckets, err = z.s3Peer.ListBuckets(ctx, opts)
|
||||
@@ -2280,55 +2279,23 @@ type HealthOptions struct {
|
||||
// was queried
|
||||
type HealthResult struct {
|
||||
Healthy bool
|
||||
HealthyRead bool
|
||||
HealingDrives int
|
||||
ESHealth []struct {
|
||||
Maintenance bool
|
||||
PoolID, SetID int
|
||||
Healthy bool
|
||||
HealthyRead bool
|
||||
HealthyDrives int
|
||||
HealingDrives int
|
||||
ReadQuorum int
|
||||
WriteQuorum int
|
||||
}
|
||||
WriteQuorum int
|
||||
ReadQuorum int
|
||||
UsingDefaults bool
|
||||
}
|
||||
|
||||
// ReadHealth returns if the cluster can serve read requests
|
||||
func (z *erasureServerPools) ReadHealth(ctx context.Context) bool {
|
||||
erasureSetUpCount := make([][]int, len(z.serverPools))
|
||||
for i := range z.serverPools {
|
||||
erasureSetUpCount[i] = make([]int, len(z.serverPools[i].sets))
|
||||
}
|
||||
|
||||
diskIDs := globalNotificationSys.GetLocalDiskIDs(ctx)
|
||||
diskIDs = append(diskIDs, getLocalDiskIDs(z))
|
||||
|
||||
for _, localDiskIDs := range diskIDs {
|
||||
for _, id := range localDiskIDs {
|
||||
poolIdx, setIdx, _, err := z.getPoolAndSet(id)
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
continue
|
||||
}
|
||||
erasureSetUpCount[poolIdx][setIdx]++
|
||||
}
|
||||
}
|
||||
|
||||
b := z.BackendInfo()
|
||||
poolReadQuorums := make([]int, len(b.StandardSCData))
|
||||
copy(poolReadQuorums, b.StandardSCData)
|
||||
|
||||
for poolIdx := range erasureSetUpCount {
|
||||
for setIdx := range erasureSetUpCount[poolIdx] {
|
||||
if erasureSetUpCount[poolIdx][setIdx] < poolReadQuorums[poolIdx] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Health - returns current status of the object layer health,
|
||||
// provides if write access exists across sets, additionally
|
||||
// can be used to query scenarios if health may be lost
|
||||
@@ -2351,6 +2318,21 @@ func (z *erasureServerPools) Health(ctx context.Context, opts HealthOptions) Hea
|
||||
storageInfo := z.StorageInfo(ctx, false)
|
||||
|
||||
for _, disk := range storageInfo.Disks {
|
||||
if opts.Maintenance {
|
||||
var skip bool
|
||||
globalLocalDrivesMu.RLock()
|
||||
for _, drive := range globalLocalDrives {
|
||||
if drive != nil && drive.Endpoint().String() == disk.Endpoint {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
globalLocalDrivesMu.RUnlock()
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if disk.PoolIndex > -1 && disk.SetIndex > -1 {
|
||||
if disk.State == madmin.DriveStateOk {
|
||||
si := erasureSetUpCount[disk.PoolIndex][disk.SetIndex]
|
||||
@@ -2397,9 +2379,20 @@ func (z *erasureServerPools) Health(ctx context.Context, opts HealthOptions) Hea
|
||||
}
|
||||
}
|
||||
|
||||
var maximumReadQuorum int
|
||||
for _, readQuorum := range poolReadQuorums {
|
||||
if maximumReadQuorum == 0 {
|
||||
maximumReadQuorum = readQuorum
|
||||
}
|
||||
if readQuorum > maximumReadQuorum {
|
||||
maximumReadQuorum = readQuorum
|
||||
}
|
||||
}
|
||||
|
||||
result := HealthResult{
|
||||
Healthy: true,
|
||||
WriteQuorum: maximumWriteQuorum,
|
||||
ReadQuorum: maximumReadQuorum,
|
||||
UsingDefaults: usingDefaults, // indicates if config was not initialized and we are using defaults on this node.
|
||||
}
|
||||
|
||||
@@ -2409,6 +2402,7 @@ func (z *erasureServerPools) Health(ctx context.Context, opts HealthOptions) Hea
|
||||
Maintenance bool
|
||||
PoolID, SetID int
|
||||
Healthy bool
|
||||
HealthyRead bool
|
||||
HealthyDrives, HealingDrives int
|
||||
ReadQuorum, WriteQuorum int
|
||||
}{
|
||||
@@ -2416,23 +2410,34 @@ func (z *erasureServerPools) Health(ctx context.Context, opts HealthOptions) Hea
|
||||
SetID: setIdx,
|
||||
PoolID: poolIdx,
|
||||
Healthy: erasureSetUpCount[poolIdx][setIdx].online >= poolWriteQuorums[poolIdx],
|
||||
HealthyRead: erasureSetUpCount[poolIdx][setIdx].online >= poolReadQuorums[poolIdx],
|
||||
HealthyDrives: erasureSetUpCount[poolIdx][setIdx].online,
|
||||
HealingDrives: erasureSetUpCount[poolIdx][setIdx].healing,
|
||||
ReadQuorum: poolReadQuorums[poolIdx],
|
||||
WriteQuorum: poolWriteQuorums[poolIdx],
|
||||
})
|
||||
|
||||
result.Healthy = erasureSetUpCount[poolIdx][setIdx].online >= poolWriteQuorums[poolIdx]
|
||||
if !result.Healthy {
|
||||
healthy := erasureSetUpCount[poolIdx][setIdx].online >= poolWriteQuorums[poolIdx]
|
||||
if !healthy {
|
||||
logger.LogIf(logger.SetReqInfo(ctx, reqInfo),
|
||||
fmt.Errorf("Write quorum may be lost on pool: %d, set: %d, expected write quorum: %d",
|
||||
poolIdx, setIdx, poolWriteQuorums[poolIdx]))
|
||||
}
|
||||
result.Healthy = result.Healthy && healthy
|
||||
|
||||
healthyRead := erasureSetUpCount[poolIdx][setIdx].online >= poolReadQuorums[poolIdx]
|
||||
if !healthyRead {
|
||||
logger.LogIf(logger.SetReqInfo(ctx, reqInfo),
|
||||
fmt.Errorf("Read quorum may be lost on pool: %d, set: %d, expected read quorum: %d",
|
||||
poolIdx, setIdx, poolReadQuorums[poolIdx]))
|
||||
}
|
||||
result.HealthyRead = result.HealthyRead && healthyRead
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Maintenance {
|
||||
result.Healthy = result.Healthy && drivesHealing == 0
|
||||
result.HealthyRead = result.HealthyRead && drivesHealing == 0
|
||||
result.HealingDrives = drivesHealing
|
||||
}
|
||||
|
||||
|
||||
+24
-19
@@ -264,13 +264,22 @@ func (s *erasureSets) connectDisks() {
|
||||
disk.SetDiskLoc(s.poolIndex, setIndex, diskIndex)
|
||||
disk.SetFormatData(formatData)
|
||||
s.erasureDisks[setIndex][diskIndex] = disk
|
||||
s.erasureDisksMu.Unlock()
|
||||
|
||||
if disk.IsLocal() && globalIsDistErasure {
|
||||
if disk.IsLocal() {
|
||||
globalLocalDrivesMu.Lock()
|
||||
globalLocalSetDrives[s.poolIndex][setIndex][diskIndex] = disk
|
||||
if globalIsDistErasure {
|
||||
globalLocalSetDrives[s.poolIndex][setIndex][diskIndex] = disk
|
||||
}
|
||||
for i, ldisk := range globalLocalDrives {
|
||||
_, k, l := ldisk.GetDiskLoc()
|
||||
if k == setIndex && l == diskIndex {
|
||||
globalLocalDrives[i] = disk
|
||||
break
|
||||
}
|
||||
}
|
||||
globalLocalDrivesMu.Unlock()
|
||||
}
|
||||
s.erasureDisksMu.Unlock()
|
||||
}(endpoint)
|
||||
}
|
||||
|
||||
@@ -316,15 +325,18 @@ func (s *erasureSets) GetLockers(setIndex int) func() ([]dsync.NetLocker, string
|
||||
}
|
||||
}
|
||||
|
||||
func (s *erasureSets) GetEndpointStrings(setIndex int) func() []string {
|
||||
return func() []string {
|
||||
eps := make([]string, s.setDriveCount)
|
||||
copy(eps, s.endpointStrings[setIndex*s.setDriveCount:setIndex*s.setDriveCount+s.setDriveCount])
|
||||
return eps
|
||||
}
|
||||
}
|
||||
|
||||
func (s *erasureSets) GetEndpoints(setIndex int) func() []Endpoint {
|
||||
return func() []Endpoint {
|
||||
s.erasureDisksMu.RLock()
|
||||
defer s.erasureDisksMu.RUnlock()
|
||||
|
||||
eps := make([]Endpoint, s.setDriveCount)
|
||||
for i := 0; i < s.setDriveCount; i++ {
|
||||
eps[i] = s.endpoints.Endpoints[setIndex*s.setDriveCount+i]
|
||||
}
|
||||
copy(eps, s.endpoints.Endpoints[setIndex*s.setDriveCount:setIndex*s.setDriveCount+s.setDriveCount])
|
||||
return eps
|
||||
}
|
||||
}
|
||||
@@ -454,7 +466,6 @@ func newErasureSets(ctx context.Context, endpoints PoolEndpoints, storageDisks [
|
||||
return
|
||||
}
|
||||
disk.SetDiskLoc(s.poolIndex, m, n)
|
||||
s.endpointStrings[m*setDriveCount+n] = disk.String()
|
||||
s.erasureDisks[m][n] = disk
|
||||
}(disk, i, j)
|
||||
}
|
||||
@@ -469,6 +480,7 @@ func newErasureSets(ctx context.Context, endpoints PoolEndpoints, storageDisks [
|
||||
getDisks: s.GetDisks(i),
|
||||
getLockers: s.GetLockers(i),
|
||||
getEndpoints: s.GetEndpoints(i),
|
||||
getEndpointStrings: s.GetEndpointStrings(i),
|
||||
nsMutex: mutex,
|
||||
}
|
||||
}(i)
|
||||
@@ -562,18 +574,11 @@ func auditObjectErasureSet(ctx context.Context, object string, set *erasureObjec
|
||||
return
|
||||
}
|
||||
|
||||
object = decodeDirObject(object)
|
||||
endpoints := set.getEndpoints()
|
||||
disksEndpoints := make([]string, 0, len(endpoints))
|
||||
for _, endpoint := range endpoints {
|
||||
disksEndpoints = append(disksEndpoints, endpoint.String())
|
||||
}
|
||||
|
||||
op := auditObjectOp{
|
||||
Name: object,
|
||||
Name: decodeDirObject(object),
|
||||
Pool: set.poolIndex + 1,
|
||||
Set: set.setIndex + 1,
|
||||
Disks: disksEndpoints,
|
||||
Disks: set.getEndpointStrings(),
|
||||
}
|
||||
|
||||
logger.GetReqInfo(ctx).AppendTags("objectLocation", op)
|
||||
|
||||
+6
-1
@@ -58,10 +58,14 @@ type erasureObjects struct {
|
||||
// getLockers returns list of remote and local lockers.
|
||||
getLockers func() ([]dsync.NetLocker, string)
|
||||
|
||||
// getEndpoints returns list of endpoint strings belonging this set.
|
||||
// getEndpoints returns list of endpoint belonging this set.
|
||||
// some may be local and some remote.
|
||||
getEndpoints func() []Endpoint
|
||||
|
||||
// getEndpoints returns list of endpoint strings belonging this set.
|
||||
// some may be local and some remote.
|
||||
getEndpointStrings func() []string
|
||||
|
||||
// Locker mutex map.
|
||||
nsMutex *nsLockMap
|
||||
}
|
||||
@@ -172,6 +176,7 @@ func getDisksInfo(disks []StorageAPI, endpoints []Endpoint, metrics bool) (disks
|
||||
PoolIndex: endpoints[index].PoolIdx,
|
||||
SetIndex: endpoints[index].SetIdx,
|
||||
DiskIndex: endpoints[index].DiskIdx,
|
||||
Local: endpoints[index].IsLocal,
|
||||
}
|
||||
if disks[index] == OfflineDisk {
|
||||
di.State = diskErrToDriveState(errDiskNotFound)
|
||||
|
||||
@@ -225,7 +225,11 @@ func (args eventArgs) ToEvent(escape bool) event.Event {
|
||||
},
|
||||
}
|
||||
|
||||
if args.EventName != event.ObjectRemovedDelete && args.EventName != event.ObjectRemovedDeleteMarkerCreated {
|
||||
isRemovedEvent := args.EventName == event.ObjectRemovedDelete ||
|
||||
args.EventName == event.ObjectRemovedDeleteMarkerCreated ||
|
||||
args.EventName == event.ObjectRemovedNoOP
|
||||
|
||||
if !isRemovedEvent {
|
||||
newEvent.S3.Object.ETag = args.Object.ETag
|
||||
newEvent.S3.Object.Size = args.Object.Size
|
||||
newEvent.S3.Object.ContentType = args.Object.ContentType
|
||||
|
||||
+8
-12
@@ -306,7 +306,12 @@ func countErrs(errs []error, err error) int {
|
||||
return i
|
||||
}
|
||||
|
||||
// Check if unformatted disks are equal to write quorum.
|
||||
// Does all errors indicate we need to initialize all disks?.
|
||||
func shouldInitErasureDisks(errs []error) bool {
|
||||
return countErrs(errs, errUnformattedDisk) == len(errs)
|
||||
}
|
||||
|
||||
// Check if unformatted disks are equal to 50%+1 of all the drives.
|
||||
func quorumUnformattedDisks(errs []error) bool {
|
||||
return countErrs(errs, errUnformattedDisk) >= (len(errs)/2)+1
|
||||
}
|
||||
@@ -773,9 +778,6 @@ func initFormatErasure(ctx context.Context, storageDisks []StorageAPI, setCount,
|
||||
hostCount := make(map[string]int, setDriveCount)
|
||||
for j := 0; j < setDriveCount; j++ {
|
||||
disk := storageDisks[i*setDriveCount+j]
|
||||
if disk == nil {
|
||||
continue
|
||||
}
|
||||
newFormat := format.Clone()
|
||||
newFormat.Erasure.This = format.Erasure.Sets[i][j]
|
||||
if deploymentID != "" {
|
||||
@@ -800,14 +802,8 @@ func initFormatErasure(ctx context.Context, storageDisks []StorageAPI, setCount,
|
||||
logger.Info(" - Drive: %s", disk.String())
|
||||
}
|
||||
})
|
||||
var warning string
|
||||
if wantAtMost == 0 {
|
||||
warning = fmt.Sprintf("Host %v has all drives of set. ", host)
|
||||
} else {
|
||||
warning = fmt.Sprintf("Host %v has more than %v drives of set. ", host, wantAtMost)
|
||||
}
|
||||
logger.Info(color.Yellow("WARNING: ") + warning +
|
||||
"A host failure will result in data becoming unavailable.")
|
||||
logger.Info(color.Yellow("WARNING:")+" Host %v has more than %v drives of set. "+
|
||||
"A host failure will result in data becoming unavailable.", host, wantAtMost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -35,7 +35,7 @@ type minioLogger struct{}
|
||||
// Print implement Logger
|
||||
func (log *minioLogger) Print(sessionID string, message interface{}) {
|
||||
if serverDebugLog {
|
||||
logger.Info("%s %s", sessionID, message)
|
||||
fmt.Printf("%s %s\n", sessionID, message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ func (log *minioLogger) Print(sessionID string, message interface{}) {
|
||||
func (log *minioLogger) Printf(sessionID string, format string, v ...interface{}) {
|
||||
if serverDebugLog {
|
||||
if sessionID != "" {
|
||||
logger.Info("%s %s", sessionID, fmt.Sprintf(format, v...))
|
||||
fmt.Printf("%s %s\n", sessionID, fmt.Sprintf(format, v...))
|
||||
} else {
|
||||
logger.Info(format, v...)
|
||||
fmt.Printf(format+"\n", v...)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,9 +54,9 @@ func (log *minioLogger) Printf(sessionID string, format string, v ...interface{}
|
||||
func (log *minioLogger) PrintCommand(sessionID string, command string, params string) {
|
||||
if serverDebugLog {
|
||||
if command == "PASS" {
|
||||
logger.Info("%s > PASS ****", sessionID)
|
||||
fmt.Printf("%s > PASS ****\n", sessionID)
|
||||
} else {
|
||||
logger.Info("%s > %s %s", sessionID, command, params)
|
||||
fmt.Printf("%s > %s %s\n", sessionID, command, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func (log *minioLogger) PrintCommand(sessionID string, command string, params st
|
||||
// PrintResponse implement Logger
|
||||
func (log *minioLogger) PrintResponse(sessionID string, code int, message string) {
|
||||
if serverDebugLog {
|
||||
logger.Info("%s < %d %s", sessionID, code, message)
|
||||
fmt.Printf("%s < %d %s\n", sessionID, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-7
@@ -177,7 +177,7 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
numHealers = uint64(v)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Healing drive '%s' - use %d parallel workers.", tracker.disk.String(), numHealers))
|
||||
logger.Event(ctx, fmt.Sprintf("Healing drive '%s' - use %d parallel workers.", tracker.disk.String(), numHealers))
|
||||
|
||||
jt, _ := workers.New(int(numHealers))
|
||||
|
||||
@@ -224,7 +224,9 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
|
||||
disks, _ := er.getOnlineDisksWithHealing(false)
|
||||
if len(disks) == 0 {
|
||||
logger.LogIf(ctx, fmt.Errorf("no online disks found to heal the bucket `%s`", bucket))
|
||||
// No object healing necessary
|
||||
tracker.bucketDone(bucket)
|
||||
logger.LogIf(ctx, tracker.update(ctx))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -236,6 +238,7 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
type healEntryResult struct {
|
||||
bytes uint64
|
||||
success bool
|
||||
skipped bool
|
||||
entryDone bool
|
||||
name string
|
||||
}
|
||||
@@ -256,6 +259,12 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
bytes: sz,
|
||||
}
|
||||
}
|
||||
healEntrySkipped := func(sz uint64) healEntryResult {
|
||||
return healEntryResult{
|
||||
bytes: sz,
|
||||
skipped: true,
|
||||
}
|
||||
}
|
||||
|
||||
filterLifecycle := func(bucket, object string, fi FileInfo) bool {
|
||||
if lc == nil {
|
||||
@@ -289,13 +298,16 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
continue
|
||||
}
|
||||
|
||||
tracker.updateProgress(res.success, res.bytes)
|
||||
tracker.updateProgress(res.success, res.skipped, res.bytes)
|
||||
}
|
||||
}()
|
||||
|
||||
send := func(result healEntryResult) bool {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if !contextCanceled(ctx) {
|
||||
logger.LogIf(ctx, ctx.Err())
|
||||
}
|
||||
return false
|
||||
case results <- result:
|
||||
return true
|
||||
@@ -367,6 +379,9 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
// Apply lifecycle rules on the objects that are expired.
|
||||
if filterLifecycle(bucket, version.Name, version) {
|
||||
versionNotFound++
|
||||
if !send(healEntrySkipped(uint64(version.Size))) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -472,9 +487,7 @@ func (er *erasureObjects) healErasureSet(ctx context.Context, buckets []string,
|
||||
|
||||
func healBucket(bucket string, scan madmin.HealScanMode) error {
|
||||
// Get background heal sequence to send elements to heal
|
||||
globalHealStateLK.Lock()
|
||||
bgSeq, ok := globalBackgroundHealState.getHealSequenceByToken(bgHealingUUID)
|
||||
globalHealStateLK.Unlock()
|
||||
if ok {
|
||||
return bgSeq.queueHealTask(healSource{bucket: bucket}, madmin.HealItemBucket)
|
||||
}
|
||||
@@ -484,9 +497,7 @@ func healBucket(bucket string, scan madmin.HealScanMode) error {
|
||||
// healObject sends the given object/version to the background healing workers
|
||||
func healObject(bucket, object, versionID string, scan madmin.HealScanMode) error {
|
||||
// Get background heal sequence to send elements to heal
|
||||
globalHealStateLK.Lock()
|
||||
bgSeq, ok := globalBackgroundHealState.getHealSequenceByToken(bgHealingUUID)
|
||||
globalHealStateLK.Unlock()
|
||||
if ok {
|
||||
return bgSeq.queueHealTask(healSource{
|
||||
bucket: bucket,
|
||||
|
||||
+13
-11
@@ -160,10 +160,11 @@ type serverCtxt struct {
|
||||
FTP []string
|
||||
SFTP []string
|
||||
|
||||
UserTimeout time.Duration
|
||||
ConnReadDeadline time.Duration
|
||||
ConnWriteDeadline time.Duration
|
||||
ConnClientReadDeadline time.Duration
|
||||
UserTimeout time.Duration
|
||||
ConnReadDeadline time.Duration
|
||||
ConnWriteDeadline time.Duration
|
||||
ConnClientReadDeadline time.Duration
|
||||
ConnClientWriteDeadline time.Duration
|
||||
|
||||
ShutdownTimeout time.Duration
|
||||
IdleTimeout time.Duration
|
||||
@@ -311,7 +312,8 @@ var (
|
||||
// Time when the server is started
|
||||
globalBootTime = UTCNow()
|
||||
|
||||
globalActiveCred auth.Credentials
|
||||
globalActiveCred auth.Credentials
|
||||
globalSiteReplicatorCred siteReplicatorCred
|
||||
|
||||
// Captures if root credentials are set via ENV.
|
||||
globalCredViaEnv bool
|
||||
@@ -378,13 +380,15 @@ var (
|
||||
return *ptr
|
||||
}
|
||||
|
||||
globalAllHealState *allHealState
|
||||
globalAllHealState = newHealState(GlobalContext, true)
|
||||
|
||||
// The always present healing routine ready to heal objects
|
||||
globalBackgroundHealRoutine *healRoutine
|
||||
globalBackgroundHealState *allHealState
|
||||
globalBackgroundHealRoutine = newHealRoutine()
|
||||
globalBackgroundHealState = newHealState(GlobalContext, false)
|
||||
|
||||
globalMRFState mrfState
|
||||
globalMRFState = mrfState{
|
||||
opCh: make(chan partialOperation, mrfOpsQueueSize),
|
||||
}
|
||||
|
||||
// If writes to FS backend should be O_SYNC.
|
||||
globalFSOSync bool
|
||||
@@ -407,8 +411,6 @@ var (
|
||||
|
||||
globalTierConfigMgr *TierConfigMgr
|
||||
|
||||
globalTierJournal *TierJournal
|
||||
|
||||
globalConsoleSrv *consoleapi.Server
|
||||
|
||||
// handles service freeze or un-freeze S3 API calls.
|
||||
|
||||
+5
-2
@@ -22,6 +22,7 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -70,7 +71,7 @@ func cgroupMemLimit() (limit uint64) {
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
limit, err = strconv.ParseUint(string(buf), 10, 64)
|
||||
limit, err = strconv.ParseUint(strings.TrimSpace(string(buf)), 10, 64)
|
||||
if err != nil {
|
||||
// The kernel can return valid but non integer values
|
||||
// but still, no need to interpret more
|
||||
@@ -169,7 +170,9 @@ func (t *apiConfig) init(cfg api.Config, setDriveCounts []int) {
|
||||
}
|
||||
t.replicationPriority = cfg.ReplicationPriority
|
||||
t.replicationMaxWorkers = cfg.ReplicationMaxWorkers
|
||||
if globalTransitionState != nil && cfg.TransitionWorkers != t.transitionWorkers {
|
||||
|
||||
// N B api.transition_workers will be deprecated
|
||||
if globalTransitionState != nil {
|
||||
globalTransitionState.UpdateWorkers(cfg.TransitionWorkers)
|
||||
}
|
||||
t.transitionWorkers = cfg.TransitionWorkers
|
||||
|
||||
+6
-10
@@ -296,27 +296,23 @@ func collectAPIStats(api string, f http.HandlerFunc) http.HandlerFunc {
|
||||
|
||||
bucket, _ := path2BucketObject(resource)
|
||||
|
||||
globalHTTPStats.currentS3Requests.Inc(api)
|
||||
defer globalHTTPStats.currentS3Requests.Dec(api)
|
||||
|
||||
_, err = globalBucketMetadataSys.Get(bucket) // check if this bucket exists.
|
||||
if bucket != "" && bucket != minioReservedBucket && err == nil {
|
||||
countBktStat := bucket != "" && bucket != minioReservedBucket && err == nil
|
||||
if countBktStat {
|
||||
globalBucketHTTPStats.updateHTTPStats(bucket, api, nil)
|
||||
}
|
||||
|
||||
globalHTTPStats.currentS3Requests.Inc(api)
|
||||
f.ServeHTTP(w, r)
|
||||
globalHTTPStats.currentS3Requests.Dec(api)
|
||||
|
||||
tc, ok := r.Context().Value(mcontext.ContextTraceKey).(*mcontext.TraceCtxt)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
tc, _ := r.Context().Value(mcontext.ContextTraceKey).(*mcontext.TraceCtxt)
|
||||
if tc != nil {
|
||||
globalHTTPStats.updateStats(api, tc.ResponseRecorder)
|
||||
globalConnStats.incS3InputBytes(int64(tc.RequestRecorder.Size()))
|
||||
globalConnStats.incS3OutputBytes(int64(tc.ResponseRecorder.Size()))
|
||||
|
||||
if bucket != "" && bucket != minioReservedBucket && err == nil {
|
||||
if countBktStat {
|
||||
globalBucketConnStats.incS3InputBytes(bucket, int64(tc.RequestRecorder.Size()))
|
||||
globalBucketConnStats.incS3OutputBytes(bucket, int64(tc.ResponseRecorder.Size()))
|
||||
globalBucketHTTPStats.updateHTTPStats(bucket, api, tc.ResponseRecorder)
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
)
|
||||
|
||||
// Tests validate bucket LocationConstraint.
|
||||
func TestIsValidLocationContraint(t *testing.T) {
|
||||
func TestIsValidLocationConstraint(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
|
||||
@@ -81,12 +81,28 @@ func ClusterReadCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(ctx, globalAPIConfig.getClusterDeadline())
|
||||
defer cancel()
|
||||
|
||||
result := objLayer.ReadHealth(ctx)
|
||||
if !result {
|
||||
writeResponse(w, http.StatusServiceUnavailable, nil, mimeNone)
|
||||
opts := HealthOptions{
|
||||
Maintenance: r.Form.Get("maintenance") == "true",
|
||||
DeploymentType: r.Form.Get("deployment-type"),
|
||||
}
|
||||
result := objLayer.Health(ctx, opts)
|
||||
w.Header().Set(xhttp.MinIOReadQuorum, strconv.Itoa(result.ReadQuorum))
|
||||
w.Header().Set(xhttp.MinIOStorageClassDefaults, strconv.FormatBool(result.UsingDefaults))
|
||||
// return how many drives are being healed if any
|
||||
if result.HealingDrives > 0 {
|
||||
w.Header().Set(xhttp.MinIOHealingDrives, strconv.Itoa(result.HealingDrives))
|
||||
}
|
||||
if !result.HealthyRead {
|
||||
// As a maintenance call we are purposefully asked to be taken
|
||||
// down, this is for orchestrators to know if we can safely
|
||||
// take this server down, return appropriate error.
|
||||
if opts.Maintenance {
|
||||
writeResponse(w, http.StatusPreconditionFailed, nil, mimeNone)
|
||||
} else {
|
||||
writeResponse(w, http.StatusServiceUnavailable, nil, mimeNone)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
writeResponse(w, http.StatusOK, nil, mimeNone)
|
||||
}
|
||||
|
||||
|
||||
+12
-8
@@ -19,6 +19,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -326,7 +327,7 @@ func (stats *HTTPAPIStats) Get(api string) int {
|
||||
}
|
||||
|
||||
// Load returns the recorded stats.
|
||||
func (stats *HTTPAPIStats) Load() map[string]int {
|
||||
func (stats *HTTPAPIStats) Load(toLower bool) map[string]int {
|
||||
if stats == nil {
|
||||
return map[string]int{}
|
||||
}
|
||||
@@ -336,6 +337,9 @@ func (stats *HTTPAPIStats) Load() map[string]int {
|
||||
|
||||
apiStats := make(map[string]int, len(stats.apiStats))
|
||||
for k, v := range stats.apiStats {
|
||||
if toLower {
|
||||
k = strings.ToLower(k)
|
||||
}
|
||||
apiStats[k] = v
|
||||
}
|
||||
return apiStats
|
||||
@@ -373,7 +377,7 @@ func (st *HTTPStats) incS3RequestsIncoming() {
|
||||
}
|
||||
|
||||
// Converts http stats into struct to be sent back to the client.
|
||||
func (st *HTTPStats) toServerHTTPStats() ServerHTTPStats {
|
||||
func (st *HTTPStats) toServerHTTPStats(toLowerKeys bool) ServerHTTPStats {
|
||||
serverStats := ServerHTTPStats{}
|
||||
serverStats.S3RequestsIncoming = atomic.SwapUint64(&st.s3RequestsIncoming, 0)
|
||||
serverStats.S3RequestsInQueue = atomic.LoadInt32(&st.s3RequestsInQueue)
|
||||
@@ -382,22 +386,22 @@ func (st *HTTPStats) toServerHTTPStats() ServerHTTPStats {
|
||||
serverStats.TotalS3RejectedHeader = atomic.LoadUint64(&st.rejectedRequestsHeader)
|
||||
serverStats.TotalS3RejectedInvalid = atomic.LoadUint64(&st.rejectedRequestsInvalid)
|
||||
serverStats.CurrentS3Requests = ServerHTTPAPIStats{
|
||||
APIStats: st.currentS3Requests.Load(),
|
||||
APIStats: st.currentS3Requests.Load(toLowerKeys),
|
||||
}
|
||||
serverStats.TotalS3Requests = ServerHTTPAPIStats{
|
||||
APIStats: st.totalS3Requests.Load(),
|
||||
APIStats: st.totalS3Requests.Load(toLowerKeys),
|
||||
}
|
||||
serverStats.TotalS3Errors = ServerHTTPAPIStats{
|
||||
APIStats: st.totalS3Errors.Load(),
|
||||
APIStats: st.totalS3Errors.Load(toLowerKeys),
|
||||
}
|
||||
serverStats.TotalS34xxErrors = ServerHTTPAPIStats{
|
||||
APIStats: st.totalS34xxErrors.Load(),
|
||||
APIStats: st.totalS34xxErrors.Load(toLowerKeys),
|
||||
}
|
||||
serverStats.TotalS35xxErrors = ServerHTTPAPIStats{
|
||||
APIStats: st.totalS35xxErrors.Load(),
|
||||
APIStats: st.totalS35xxErrors.Load(toLowerKeys),
|
||||
}
|
||||
serverStats.TotalS3Canceled = ServerHTTPAPIStats{
|
||||
APIStats: st.totalS3Canceled.Load(),
|
||||
APIStats: st.totalS3Canceled.Load(toLowerKeys),
|
||||
}
|
||||
return serverStats
|
||||
}
|
||||
|
||||
@@ -143,6 +143,10 @@ func (ies *IAMEtcdStore) deleteIAMConfig(ctx context.Context, path string) error
|
||||
return deleteKeyEtcd(ctx, ies.client, path)
|
||||
}
|
||||
|
||||
func (ies *IAMEtcdStore) loadPolicyDocWithRetry(ctx context.Context, policy string, m map[string]PolicyDoc, _ int) error {
|
||||
return ies.loadPolicyDoc(ctx, policy, m)
|
||||
}
|
||||
|
||||
func (ies *IAMEtcdStore) loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error {
|
||||
data, err := ies.loadIAMConfigBytes(ctx, getPolicyDocPath(policy))
|
||||
if err != nil {
|
||||
@@ -321,6 +325,10 @@ func (ies *IAMEtcdStore) loadGroups(ctx context.Context, m map[string]GroupInfo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ies *IAMEtcdStore) loadMappedPolicyWithRetry(ctx context.Context, name string, userType IAMUserType, isGroup bool, m map[string]MappedPolicy, _ int) error {
|
||||
return ies.loadMappedPolicy(ctx, name, userType, isGroup, m)
|
||||
}
|
||||
|
||||
func (ies *IAMEtcdStore) loadMappedPolicy(ctx context.Context, name string, userType IAMUserType, isGroup bool, m map[string]MappedPolicy) error {
|
||||
var p MappedPolicy
|
||||
err := ies.loadIAMConfig(ctx, &p, getMappedPolicyPath(name, userType, isGroup))
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
@@ -146,6 +147,41 @@ func (iamOS *IAMObjectStore) deleteIAMConfig(ctx context.Context, path string) e
|
||||
return deleteConfig(ctx, iamOS.objAPI, path)
|
||||
}
|
||||
|
||||
func (iamOS *IAMObjectStore) loadPolicyDocWithRetry(ctx context.Context, policy string, m map[string]PolicyDoc, retries int) error {
|
||||
for {
|
||||
retry:
|
||||
data, objInfo, err := iamOS.loadIAMConfigBytesWithMetadata(ctx, getPolicyDocPath(policy))
|
||||
if err != nil {
|
||||
if err == errConfigNotFound {
|
||||
return errNoSuchPolicy
|
||||
}
|
||||
retries--
|
||||
if retries <= 0 {
|
||||
return err
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
goto retry
|
||||
}
|
||||
|
||||
var p PolicyDoc
|
||||
err = p.parseJSON(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Version == 0 {
|
||||
// This means that policy was in the old version (without any
|
||||
// timestamp info). We fetch the mod time of the file and save
|
||||
// that as create and update date.
|
||||
p.CreateDate = objInfo.ModTime
|
||||
p.UpdateDate = objInfo.ModTime
|
||||
}
|
||||
|
||||
m[policy] = p
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (iamOS *IAMObjectStore) loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error {
|
||||
data, objInfo, err := iamOS.loadIAMConfigBytesWithMetadata(ctx, getPolicyDocPath(policy))
|
||||
if err != nil {
|
||||
@@ -289,6 +325,30 @@ func (iamOS *IAMObjectStore) loadGroups(ctx context.Context, m map[string]GroupI
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iamOS *IAMObjectStore) loadMappedPolicyWithRetry(ctx context.Context, name string, userType IAMUserType, isGroup bool,
|
||||
m map[string]MappedPolicy, retries int,
|
||||
) error {
|
||||
for {
|
||||
retry:
|
||||
var p MappedPolicy
|
||||
err := iamOS.loadIAMConfig(ctx, &p, getMappedPolicyPath(name, userType, isGroup))
|
||||
if err != nil {
|
||||
if err == errConfigNotFound {
|
||||
return errNoSuchPolicy
|
||||
}
|
||||
retries--
|
||||
if retries <= 0 {
|
||||
return err
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
goto retry
|
||||
}
|
||||
|
||||
m[name] = p
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (iamOS *IAMObjectStore) loadMappedPolicy(ctx context.Context, name string, userType IAMUserType, isGroup bool,
|
||||
m map[string]MappedPolicy,
|
||||
) error {
|
||||
@@ -300,6 +360,7 @@ func (iamOS *IAMObjectStore) loadMappedPolicy(ctx context.Context, name string,
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
m[name] = p
|
||||
return nil
|
||||
}
|
||||
|
||||
+23
-16
@@ -441,12 +441,14 @@ type IAMStorageAPI interface {
|
||||
runlock()
|
||||
getUsersSysType() UsersSysType
|
||||
loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error
|
||||
loadPolicyDocWithRetry(ctx context.Context, policy string, m map[string]PolicyDoc, retries int) error
|
||||
loadPolicyDocs(ctx context.Context, m map[string]PolicyDoc) error
|
||||
loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error
|
||||
loadUsers(ctx context.Context, userType IAMUserType, m map[string]UserIdentity) error
|
||||
loadGroup(ctx context.Context, group string, m map[string]GroupInfo) error
|
||||
loadGroups(ctx context.Context, m map[string]GroupInfo) error
|
||||
loadMappedPolicy(ctx context.Context, name string, userType IAMUserType, isGroup bool, m map[string]MappedPolicy) error
|
||||
loadMappedPolicyWithRetry(ctx context.Context, name string, userType IAMUserType, isGroup bool, m map[string]MappedPolicy, retries int) error
|
||||
loadMappedPolicies(ctx context.Context, userType IAMUserType, isGroup bool, m map[string]MappedPolicy) error
|
||||
saveIAMConfig(ctx context.Context, item interface{}, path string, opts ...options) error
|
||||
loadIAMConfig(ctx context.Context, item interface{}, path string) error
|
||||
@@ -1863,7 +1865,12 @@ func (store *IAMStoreSys) GetAllParentUsers() map[string]ParentUserInfo {
|
||||
if cred.IsServiceAccount() {
|
||||
claims, err = getClaimsFromTokenWithSecret(cred.SessionToken, cred.SecretKey)
|
||||
} else if cred.IsTemp() {
|
||||
claims, err = getClaimsFromTokenWithSecret(cred.SessionToken, globalActiveCred.SecretKey)
|
||||
var secretKey string
|
||||
secretKey, err = getTokenSigningKey()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
claims, err = getClaimsFromTokenWithSecret(cred.SessionToken, secretKey)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -2429,17 +2436,11 @@ func (store *IAMStoreSys) GetSTSAndServiceAccounts() []auth.Credentials {
|
||||
var res []auth.Credentials
|
||||
for _, u := range cache.iamUsersMap {
|
||||
cred := u.Credentials
|
||||
if cred.IsTemp() {
|
||||
panic("unexpected STS credential found in iamUsersMap")
|
||||
}
|
||||
if cred.IsServiceAccount() {
|
||||
res = append(res, cred)
|
||||
}
|
||||
}
|
||||
for _, u := range cache.iamSTSAccountsMap {
|
||||
if !u.Credentials.IsTemp() {
|
||||
panic("unexpected non STS credential found in iamSTSAccountsMap")
|
||||
}
|
||||
res = append(res, u.Credentials)
|
||||
}
|
||||
|
||||
@@ -2483,22 +2484,24 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
|
||||
store.loadUser(ctx, accessKey, regUser, cache.iamUsersMap)
|
||||
if _, found = cache.iamUsersMap[accessKey]; found {
|
||||
// load mapped policies
|
||||
store.loadMappedPolicy(ctx, accessKey, regUser, false, cache.iamUserPolicyMap)
|
||||
store.loadMappedPolicyWithRetry(ctx, accessKey, regUser, false, cache.iamUserPolicyMap, 3)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for service account
|
||||
if !found {
|
||||
store.loadUser(ctx, accessKey, svcUser, cache.iamUsersMap)
|
||||
if svc, found := cache.iamUsersMap[accessKey]; found {
|
||||
var svc UserIdentity
|
||||
svc, found = cache.iamUsersMap[accessKey]
|
||||
if found {
|
||||
// Load parent user and mapped policies.
|
||||
if store.getUsersSysType() == MinIOUsersSysType {
|
||||
store.loadUser(ctx, svc.Credentials.ParentUser, regUser, cache.iamUsersMap)
|
||||
store.loadMappedPolicy(ctx, svc.Credentials.ParentUser, regUser, false, cache.iamUserPolicyMap)
|
||||
store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, regUser, false, cache.iamUserPolicyMap, 3)
|
||||
} else {
|
||||
// In case of LDAP the parent user's policy mapping needs to be
|
||||
// loaded into sts map
|
||||
store.loadMappedPolicy(ctx, svc.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap)
|
||||
store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap, 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2510,7 +2513,7 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
|
||||
store.loadUser(ctx, accessKey, stsUser, cache.iamSTSAccountsMap)
|
||||
if stsUserCred, found = cache.iamSTSAccountsMap[accessKey]; found {
|
||||
// Load mapped policy
|
||||
store.loadMappedPolicy(ctx, stsUserCred.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap)
|
||||
store.loadMappedPolicyWithRetry(ctx, stsUserCred.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap, 3)
|
||||
stsAccountFound = true
|
||||
}
|
||||
}
|
||||
@@ -2519,13 +2522,13 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
|
||||
if !stsAccountFound {
|
||||
for _, policy := range cache.iamUserPolicyMap[accessKey].toSlice() {
|
||||
if _, found = cache.iamPolicyDocsMap[policy]; !found {
|
||||
store.loadPolicyDoc(ctx, policy, cache.iamPolicyDocsMap)
|
||||
store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, policy := range cache.iamSTSPolicyMap[stsUserCred.Credentials.AccessKey].toSlice() {
|
||||
if _, found = cache.iamPolicyDocsMap[policy]; !found {
|
||||
store.loadPolicyDoc(ctx, policy, cache.iamPolicyDocsMap)
|
||||
store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2534,8 +2537,12 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
|
||||
func extractJWTClaims(u UserIdentity) (*jwt.MapClaims, error) {
|
||||
jwtClaims, err := auth.ExtractClaims(u.Credentials.SessionToken, u.Credentials.SecretKey)
|
||||
if err != nil {
|
||||
// Session tokens for STS creds will be generated with root secret
|
||||
jwtClaims, err = auth.ExtractClaims(u.Credentials.SessionToken, globalActiveCred.SecretKey)
|
||||
secretKey, err := getTokenSigningKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Session tokens for STS creds will be generated with root secret or site-replicator-0 secret
|
||||
jwtClaims, err = auth.ExtractClaims(u.Credentials.SessionToken, secretKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+21
-2
@@ -201,6 +201,13 @@ func (sys *IAMSys) Load(ctx context.Context, firstTime bool) error {
|
||||
atomic.StoreUint64(&sys.LastRefreshTimeUnixNano, uint64(loadStartTime.Add(loadDuration).UnixNano()))
|
||||
atomic.AddUint64(&sys.TotalRefreshSuccesses, 1)
|
||||
|
||||
if !globalSiteReplicatorCred.IsValid() {
|
||||
sa, _, err := sys.getServiceAccount(ctx, siteReplicatorSvcAcc)
|
||||
if err == nil {
|
||||
globalSiteReplicatorCred.Set(sa.Credentials)
|
||||
}
|
||||
}
|
||||
|
||||
if firstTime {
|
||||
bootstrapTraceMsg(fmt.Sprintf("globalIAMSys.Load(): (duration: %s)", loadDuration))
|
||||
}
|
||||
@@ -940,6 +947,13 @@ func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser string, gro
|
||||
return auth.Credentials{}, time.Time{}, errInvalidArgument
|
||||
}
|
||||
|
||||
if len(opts.accessKey) > 0 && len(opts.secretKey) == 0 {
|
||||
return auth.Credentials{}, time.Time{}, auth.ErrNoSecretKeyWithAccessKey
|
||||
}
|
||||
if len(opts.secretKey) > 0 && len(opts.accessKey) == 0 {
|
||||
return auth.Credentials{}, time.Time{}, auth.ErrNoAccessKeyWithSecretKey
|
||||
}
|
||||
|
||||
var policyBuf []byte
|
||||
if opts.sessionPolicy != nil {
|
||||
err := opts.sessionPolicy.Validate()
|
||||
@@ -983,7 +997,7 @@ func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser string, gro
|
||||
|
||||
var accessKey, secretKey string
|
||||
var err error
|
||||
if len(opts.accessKey) > 0 {
|
||||
if len(opts.accessKey) > 0 || len(opts.secretKey) > 0 {
|
||||
accessKey, secretKey = opts.accessKey, opts.secretKey
|
||||
} else {
|
||||
accessKey, secretKey, err = auth.GenerateCredentials()
|
||||
@@ -1387,7 +1401,12 @@ func (sys *IAMSys) updateGroupMembershipsForLDAP(ctx context.Context) {
|
||||
jwtClaims, err = auth.ExtractClaims(cred.SessionToken, globalActiveCred.SecretKey)
|
||||
}
|
||||
} else {
|
||||
jwtClaims, err = auth.ExtractClaims(cred.SessionToken, globalActiveCred.SecretKey)
|
||||
var secretKey string
|
||||
secretKey, err = getTokenSigningKey()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
jwtClaims, err = auth.ExtractClaims(cred.SessionToken, secretKey)
|
||||
}
|
||||
if err != nil {
|
||||
// skip this cred - session token seems invalid
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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 (
|
||||
"sync"
|
||||
|
||||
"github.com/minio/minio/internal/config/ilm"
|
||||
)
|
||||
|
||||
var globalILMConfig = ilmConfig{
|
||||
cfg: ilm.Config{
|
||||
ExpirationWorkers: 100,
|
||||
TransitionWorkers: 100,
|
||||
},
|
||||
}
|
||||
|
||||
type ilmConfig struct {
|
||||
mu sync.RWMutex
|
||||
cfg ilm.Config
|
||||
}
|
||||
|
||||
func (c *ilmConfig) getExpirationWorkers() int {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return c.cfg.ExpirationWorkers
|
||||
}
|
||||
|
||||
func (c *ilmConfig) getTransitionWorkers() int {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return c.cfg.TransitionWorkers
|
||||
}
|
||||
|
||||
func (c *ilmConfig) update(cfg ilm.Config) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.cfg = cfg
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//go:build linux && !appengine
|
||||
// +build linux,!appengine
|
||||
|
||||
// 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 (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Returns true if no error and there is no object or prefix inside this directory
|
||||
func isDirEmpty(dirname string) bool {
|
||||
var stat syscall.Stat_t
|
||||
if err := syscall.Stat(dirname, &stat); err != nil {
|
||||
return false
|
||||
}
|
||||
if stat.Mode&syscall.S_IFMT == syscall.S_IFDIR && stat.Nlink == 2 {
|
||||
return true
|
||||
}
|
||||
// On filesystems such as btrfs, nfs this is not true, so fallback
|
||||
// to performing readdir() instead.
|
||||
if stat.Mode&syscall.S_IFMT == syscall.S_IFDIR && stat.Nlink < 2 {
|
||||
entries, err := readDirN(dirname, 1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(entries) == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
@@ -17,40 +20,11 @@
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/minio/minio/internal/logger"
|
||||
)
|
||||
|
||||
type tierMemJournal struct {
|
||||
entries chan jentry
|
||||
}
|
||||
|
||||
func newTierMemJournal(nevents int) *tierMemJournal {
|
||||
return &tierMemJournal{
|
||||
entries: make(chan jentry, nevents),
|
||||
// isDirEmpty - returns true if there is no error and no object and prefix inside this directory
|
||||
func isDirEmpty(dirname string) bool {
|
||||
entries, err := readDirN(dirname, 1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (j *tierMemJournal) processEntries(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case entry := <-j.entries:
|
||||
logger.LogIf(ctx, deleteObjectFromRemoteTier(ctx, entry.ObjName, entry.VersionID, entry.TierName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (j *tierMemJournal) AddEntry(je jentry) error {
|
||||
select {
|
||||
case j.entries <- je:
|
||||
default:
|
||||
return fmt.Errorf("failed to remove tiered content at %s with version %s from tier %s, will be retried later.",
|
||||
je.ObjName, je.VersionID, je.TierName)
|
||||
}
|
||||
return nil
|
||||
return len(entries) == 0
|
||||
}
|
||||
+5
-1
@@ -46,7 +46,7 @@ var (
|
||||
errAccessKeyDisabled = errors.New("The access key you provided is disabled")
|
||||
errAuthentication = errors.New("Authentication failed, check your access credentials")
|
||||
errNoAuthToken = errors.New("JWT token missing")
|
||||
errSkewedAuthTime = errors.New("Skewed authenticationdate/time")
|
||||
errSkewedAuthTime = errors.New("Skewed authentication date/time")
|
||||
errMalformedAuth = errors.New("Malformed authentication input")
|
||||
)
|
||||
|
||||
@@ -113,6 +113,10 @@ func metricsRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, []string, b
|
||||
return nil, errInvalidAccessKeyID
|
||||
}
|
||||
cred := u.Credentials
|
||||
// Expired credentials return error.
|
||||
if cred.IsTemp() && cred.IsExpired() {
|
||||
return nil, errInvalidAccessKeyID
|
||||
}
|
||||
return []byte(cred.SecretKey), nil
|
||||
} // this means claims.AccessKey == rootAccessKey
|
||||
if !globalAPIConfig.permitRootAccess() {
|
||||
|
||||
+7
-43
@@ -25,7 +25,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/kes-go"
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/kms"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
@@ -431,10 +431,6 @@ func (a kmsAPIHandlers) KMSDescribePolicyHandler(w http.ResponseWriter, r *http.
|
||||
writeSuccessResponseJSON(w, p)
|
||||
}
|
||||
|
||||
type assignPolicyRequest struct {
|
||||
Identity string
|
||||
}
|
||||
|
||||
// KMSAssignPolicyHandler - POST /minio/kms/v1/policy/assign?policy=<policy>
|
||||
func (a kmsAPIHandlers) KMSAssignPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := newContext(r, w, "KMSAssignPolicy")
|
||||
@@ -449,22 +445,8 @@ func (a kmsAPIHandlers) KMSAssignPolicyHandler(w http.ResponseWriter, r *http.Re
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
|
||||
return
|
||||
}
|
||||
manager, ok := GlobalKMS.(kms.PolicyManager)
|
||||
if !ok {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
var request assignPolicyRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
err := manager.AssignPolicy(ctx, r.Form.Get("policy"), request.Identity)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
writeSuccessResponseHeadersOnly(w)
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// KMSDeletePolicyHandler - DELETE /minio/kms/v1/policy/delete?policy=<policy>
|
||||
@@ -481,17 +463,8 @@ func (a kmsAPIHandlers) KMSDeletePolicyHandler(w http.ResponseWriter, r *http.Re
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
manager, ok := GlobalKMS.(kms.PolicyManager)
|
||||
if !ok {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
if err := manager.DeletePolicy(ctx, r.Form.Get("policy")); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
writeSuccessResponseHeadersOnly(w)
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// KMSListPoliciesHandler - GET /minio/kms/v1/policy/list?pattern=<pattern>
|
||||
@@ -668,17 +641,8 @@ func (a kmsAPIHandlers) KMSDeleteIdentityHandler(w http.ResponseWriter, r *http.
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL)
|
||||
return
|
||||
}
|
||||
manager, ok := GlobalKMS.(kms.IdentityManager)
|
||||
if !ok {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if err := manager.DeleteIdentity(ctx, r.Form.Get("policy")); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
writeSuccessResponseHeadersOnly(w)
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// KMSListIdentitiesHandler - GET /minio/kms/v1/identity/list?pattern=<pattern>
|
||||
|
||||
+10
-7
@@ -17,6 +17,8 @@
|
||||
|
||||
package cmd
|
||||
|
||||
//go:generate msgp -file=$GOFILE -unexported
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
@@ -36,12 +38,9 @@ type lockRequesterInfo struct {
|
||||
TimeLastRefresh time.Time // Timestamp for last lock refresh.
|
||||
Source string // Contains line, function and filename requesting the lock.
|
||||
Group bool // indicates if it was a group lock.
|
||||
// Owner represents the UUID of the owner who originally requested the lock
|
||||
// useful in expiry.
|
||||
Owner string
|
||||
// Quorum represents the quorum required for this lock to be active.
|
||||
Quorum int
|
||||
idx int
|
||||
Owner string // Owner represents the UUID of the owner who originally requested the lock.
|
||||
Quorum int // Quorum represents the quorum required for this lock to be active.
|
||||
idx int `msg:"-"` // index of the lock in the lockMap.
|
||||
}
|
||||
|
||||
// isWriteLock returns whether the lock is a write or read lock.
|
||||
@@ -50,6 +49,8 @@ func isWriteLock(lri []lockRequesterInfo) bool {
|
||||
}
|
||||
|
||||
// localLocker implements Dsync.NetLocker
|
||||
//
|
||||
//msgp:ignore localLocker
|
||||
type localLocker struct {
|
||||
mutex sync.Mutex
|
||||
lockMap map[string][]lockRequesterInfo
|
||||
@@ -238,7 +239,9 @@ func (l *localLocker) stats() lockStats {
|
||||
return st
|
||||
}
|
||||
|
||||
func (l *localLocker) DupLockMap() map[string][]lockRequesterInfo {
|
||||
type localLockMap map[string][]lockRequesterInfo
|
||||
|
||||
func (l *localLocker) DupLockMap() localLockMap {
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
|
||||
@@ -0,0 +1,620 @@
|
||||
package cmd
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *localLockMap) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var zb0004 uint32
|
||||
zb0004, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if (*z) == nil {
|
||||
(*z) = make(localLockMap, zb0004)
|
||||
} else if len((*z)) > 0 {
|
||||
for key := range *z {
|
||||
delete((*z), key)
|
||||
}
|
||||
}
|
||||
for zb0004 > 0 {
|
||||
zb0004--
|
||||
var zb0001 string
|
||||
var zb0002 []lockRequesterInfo
|
||||
zb0001, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
var zb0005 uint32
|
||||
zb0005, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0001)
|
||||
return
|
||||
}
|
||||
if cap(zb0002) >= int(zb0005) {
|
||||
zb0002 = (zb0002)[:zb0005]
|
||||
} else {
|
||||
zb0002 = make([]lockRequesterInfo, zb0005)
|
||||
}
|
||||
for zb0003 := range zb0002 {
|
||||
err = zb0002[zb0003].DecodeMsg(dc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0001, zb0003)
|
||||
return
|
||||
}
|
||||
}
|
||||
(*z)[zb0001] = zb0002
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z localLockMap) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = en.WriteMapHeader(uint32(len(z)))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0006, zb0007 := range z {
|
||||
err = en.WriteString(zb0006)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
err = en.WriteArrayHeader(uint32(len(zb0007)))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0006)
|
||||
return
|
||||
}
|
||||
for zb0008 := range zb0007 {
|
||||
err = zb0007[zb0008].EncodeMsg(en)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0006, zb0008)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z localLockMap) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
o = msgp.AppendMapHeader(o, uint32(len(z)))
|
||||
for zb0006, zb0007 := range z {
|
||||
o = msgp.AppendString(o, zb0006)
|
||||
o = msgp.AppendArrayHeader(o, uint32(len(zb0007)))
|
||||
for zb0008 := range zb0007 {
|
||||
o, err = zb0007[zb0008].MarshalMsg(o)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0006, zb0008)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *localLockMap) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var zb0004 uint32
|
||||
zb0004, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if (*z) == nil {
|
||||
(*z) = make(localLockMap, zb0004)
|
||||
} else if len((*z)) > 0 {
|
||||
for key := range *z {
|
||||
delete((*z), key)
|
||||
}
|
||||
}
|
||||
for zb0004 > 0 {
|
||||
var zb0001 string
|
||||
var zb0002 []lockRequesterInfo
|
||||
zb0004--
|
||||
zb0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
var zb0005 uint32
|
||||
zb0005, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0001)
|
||||
return
|
||||
}
|
||||
if cap(zb0002) >= int(zb0005) {
|
||||
zb0002 = (zb0002)[:zb0005]
|
||||
} else {
|
||||
zb0002 = make([]lockRequesterInfo, zb0005)
|
||||
}
|
||||
for zb0003 := range zb0002 {
|
||||
bts, err = zb0002[zb0003].UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, zb0001, zb0003)
|
||||
return
|
||||
}
|
||||
}
|
||||
(*z)[zb0001] = zb0002
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z localLockMap) Msgsize() (s int) {
|
||||
s = msgp.MapHeaderSize
|
||||
if z != nil {
|
||||
for zb0006, zb0007 := range z {
|
||||
_ = zb0007
|
||||
s += msgp.StringPrefixSize + len(zb0006) + msgp.ArrayHeaderSize
|
||||
for zb0008 := range zb0007 {
|
||||
s += zb0007[zb0008].Msgsize()
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *lockRequesterInfo) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Name":
|
||||
z.Name, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Name")
|
||||
return
|
||||
}
|
||||
case "Writer":
|
||||
z.Writer, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Writer")
|
||||
return
|
||||
}
|
||||
case "UID":
|
||||
z.UID, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "UID")
|
||||
return
|
||||
}
|
||||
case "Timestamp":
|
||||
z.Timestamp, err = dc.ReadTime()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Timestamp")
|
||||
return
|
||||
}
|
||||
case "TimeLastRefresh":
|
||||
z.TimeLastRefresh, err = dc.ReadTime()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "TimeLastRefresh")
|
||||
return
|
||||
}
|
||||
case "Source":
|
||||
z.Source, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Source")
|
||||
return
|
||||
}
|
||||
case "Group":
|
||||
z.Group, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Group")
|
||||
return
|
||||
}
|
||||
case "Owner":
|
||||
z.Owner, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Owner")
|
||||
return
|
||||
}
|
||||
case "Quorum":
|
||||
z.Quorum, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Quorum")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *lockRequesterInfo) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 9
|
||||
// write "Name"
|
||||
err = en.Append(0x89, 0xa4, 0x4e, 0x61, 0x6d, 0x65)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Name)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Name")
|
||||
return
|
||||
}
|
||||
// write "Writer"
|
||||
err = en.Append(0xa6, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.Writer)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Writer")
|
||||
return
|
||||
}
|
||||
// write "UID"
|
||||
err = en.Append(0xa3, 0x55, 0x49, 0x44)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.UID)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "UID")
|
||||
return
|
||||
}
|
||||
// write "Timestamp"
|
||||
err = en.Append(0xa9, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteTime(z.Timestamp)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Timestamp")
|
||||
return
|
||||
}
|
||||
// write "TimeLastRefresh"
|
||||
err = en.Append(0xaf, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteTime(z.TimeLastRefresh)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "TimeLastRefresh")
|
||||
return
|
||||
}
|
||||
// write "Source"
|
||||
err = en.Append(0xa6, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Source)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Source")
|
||||
return
|
||||
}
|
||||
// write "Group"
|
||||
err = en.Append(0xa5, 0x47, 0x72, 0x6f, 0x75, 0x70)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.Group)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Group")
|
||||
return
|
||||
}
|
||||
// write "Owner"
|
||||
err = en.Append(0xa5, 0x4f, 0x77, 0x6e, 0x65, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Owner)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Owner")
|
||||
return
|
||||
}
|
||||
// write "Quorum"
|
||||
err = en.Append(0xa6, 0x51, 0x75, 0x6f, 0x72, 0x75, 0x6d)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.Quorum)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Quorum")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *lockRequesterInfo) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 9
|
||||
// string "Name"
|
||||
o = append(o, 0x89, 0xa4, 0x4e, 0x61, 0x6d, 0x65)
|
||||
o = msgp.AppendString(o, z.Name)
|
||||
// string "Writer"
|
||||
o = append(o, 0xa6, 0x57, 0x72, 0x69, 0x74, 0x65, 0x72)
|
||||
o = msgp.AppendBool(o, z.Writer)
|
||||
// string "UID"
|
||||
o = append(o, 0xa3, 0x55, 0x49, 0x44)
|
||||
o = msgp.AppendString(o, z.UID)
|
||||
// string "Timestamp"
|
||||
o = append(o, 0xa9, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70)
|
||||
o = msgp.AppendTime(o, z.Timestamp)
|
||||
// string "TimeLastRefresh"
|
||||
o = append(o, 0xaf, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68)
|
||||
o = msgp.AppendTime(o, z.TimeLastRefresh)
|
||||
// string "Source"
|
||||
o = append(o, 0xa6, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65)
|
||||
o = msgp.AppendString(o, z.Source)
|
||||
// string "Group"
|
||||
o = append(o, 0xa5, 0x47, 0x72, 0x6f, 0x75, 0x70)
|
||||
o = msgp.AppendBool(o, z.Group)
|
||||
// string "Owner"
|
||||
o = append(o, 0xa5, 0x4f, 0x77, 0x6e, 0x65, 0x72)
|
||||
o = msgp.AppendString(o, z.Owner)
|
||||
// string "Quorum"
|
||||
o = append(o, 0xa6, 0x51, 0x75, 0x6f, 0x72, 0x75, 0x6d)
|
||||
o = msgp.AppendInt(o, z.Quorum)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *lockRequesterInfo) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Name":
|
||||
z.Name, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Name")
|
||||
return
|
||||
}
|
||||
case "Writer":
|
||||
z.Writer, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Writer")
|
||||
return
|
||||
}
|
||||
case "UID":
|
||||
z.UID, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "UID")
|
||||
return
|
||||
}
|
||||
case "Timestamp":
|
||||
z.Timestamp, bts, err = msgp.ReadTimeBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Timestamp")
|
||||
return
|
||||
}
|
||||
case "TimeLastRefresh":
|
||||
z.TimeLastRefresh, bts, err = msgp.ReadTimeBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "TimeLastRefresh")
|
||||
return
|
||||
}
|
||||
case "Source":
|
||||
z.Source, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Source")
|
||||
return
|
||||
}
|
||||
case "Group":
|
||||
z.Group, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Group")
|
||||
return
|
||||
}
|
||||
case "Owner":
|
||||
z.Owner, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Owner")
|
||||
return
|
||||
}
|
||||
case "Quorum":
|
||||
z.Quorum, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Quorum")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *lockRequesterInfo) Msgsize() (s int) {
|
||||
s = 1 + 5 + msgp.StringPrefixSize + len(z.Name) + 7 + msgp.BoolSize + 4 + msgp.StringPrefixSize + len(z.UID) + 10 + msgp.TimeSize + 16 + msgp.TimeSize + 7 + msgp.StringPrefixSize + len(z.Source) + 6 + msgp.BoolSize + 6 + msgp.StringPrefixSize + len(z.Owner) + 7 + msgp.IntSize
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *lockStats) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Total":
|
||||
z.Total, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Total")
|
||||
return
|
||||
}
|
||||
case "Writes":
|
||||
z.Writes, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Writes")
|
||||
return
|
||||
}
|
||||
case "Reads":
|
||||
z.Reads, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Reads")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z lockStats) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 3
|
||||
// write "Total"
|
||||
err = en.Append(0x83, 0xa5, 0x54, 0x6f, 0x74, 0x61, 0x6c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.Total)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Total")
|
||||
return
|
||||
}
|
||||
// write "Writes"
|
||||
err = en.Append(0xa6, 0x57, 0x72, 0x69, 0x74, 0x65, 0x73)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.Writes)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Writes")
|
||||
return
|
||||
}
|
||||
// write "Reads"
|
||||
err = en.Append(0xa5, 0x52, 0x65, 0x61, 0x64, 0x73)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.Reads)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Reads")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z lockStats) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 3
|
||||
// string "Total"
|
||||
o = append(o, 0x83, 0xa5, 0x54, 0x6f, 0x74, 0x61, 0x6c)
|
||||
o = msgp.AppendInt(o, z.Total)
|
||||
// string "Writes"
|
||||
o = append(o, 0xa6, 0x57, 0x72, 0x69, 0x74, 0x65, 0x73)
|
||||
o = msgp.AppendInt(o, z.Writes)
|
||||
// string "Reads"
|
||||
o = append(o, 0xa5, 0x52, 0x65, 0x61, 0x64, 0x73)
|
||||
o = msgp.AppendInt(o, z.Reads)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *lockStats) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Total":
|
||||
z.Total, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Total")
|
||||
return
|
||||
}
|
||||
case "Writes":
|
||||
z.Writes, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Writes")
|
||||
return
|
||||
}
|
||||
case "Reads":
|
||||
z.Reads, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Reads")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z lockStats) Msgsize() (s int) {
|
||||
s = 1 + 6 + msgp.IntSize + 7 + msgp.IntSize + 6 + msgp.IntSize
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
package cmd
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
func TestMarshalUnmarshallocalLockMap(t *testing.T) {
|
||||
v := localLockMap{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsglocalLockMap(b *testing.B) {
|
||||
v := localLockMap{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsglocalLockMap(b *testing.B) {
|
||||
v := localLockMap{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshallocalLockMap(b *testing.B) {
|
||||
v := localLockMap{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodelocalLockMap(t *testing.T) {
|
||||
v := localLockMap{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodelocalLockMap Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := localLockMap{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodelocalLockMap(b *testing.B) {
|
||||
v := localLockMap{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecodelocalLockMap(b *testing.B) {
|
||||
v := localLockMap{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshallockRequesterInfo(t *testing.T) {
|
||||
v := lockRequesterInfo{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsglockRequesterInfo(b *testing.B) {
|
||||
v := lockRequesterInfo{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsglockRequesterInfo(b *testing.B) {
|
||||
v := lockRequesterInfo{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshallockRequesterInfo(b *testing.B) {
|
||||
v := lockRequesterInfo{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodelockRequesterInfo(t *testing.T) {
|
||||
v := lockRequesterInfo{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodelockRequesterInfo Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := lockRequesterInfo{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodelockRequesterInfo(b *testing.B) {
|
||||
v := lockRequesterInfo{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecodelockRequesterInfo(b *testing.B) {
|
||||
v := lockRequesterInfo{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshallockStats(t *testing.T) {
|
||||
v := lockStats{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsglockStats(b *testing.B) {
|
||||
v := lockStats{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsglockStats(b *testing.B) {
|
||||
v := lockStats{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshallockStats(b *testing.B) {
|
||||
v := lockStats{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodelockStats(t *testing.T) {
|
||||
v := lockStats{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodelockStats Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := lockStats{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodelockStats(b *testing.B) {
|
||||
v := lockStats{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecodelockStats(b *testing.B) {
|
||||
v := lockStats{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-5
@@ -42,6 +42,8 @@ import (
|
||||
"github.com/minio/pkg/v2/console"
|
||||
)
|
||||
|
||||
//go:generate msgp -file $GOFILE -unexported
|
||||
|
||||
type listPathOptions struct {
|
||||
// ID of the listing.
|
||||
// This will be used to persist the list.
|
||||
@@ -99,18 +101,18 @@ type listPathOptions struct {
|
||||
|
||||
// Versioning config is used for if the path
|
||||
// has versioning enabled.
|
||||
Versioning *versioning.Versioning
|
||||
Versioning *versioning.Versioning `msg:"-"`
|
||||
|
||||
// Lifecycle performs filtering based on lifecycle.
|
||||
// This will filter out objects if the most recent version should be deleted by lifecycle.
|
||||
// Is not transferred across request calls.
|
||||
Lifecycle *lifecycle.Lifecycle
|
||||
Lifecycle *lifecycle.Lifecycle `msg:"-"`
|
||||
|
||||
// Retention configuration, needed to be passed along with lifecycle if set.
|
||||
Retention lock.Retention
|
||||
Retention lock.Retention `msg:"-"`
|
||||
|
||||
// Replication configuration
|
||||
Replication replicationConfig
|
||||
Replication replicationConfig `msg:"-"`
|
||||
|
||||
// StopDiskAtLimit will stop listing on each disk when limit number off objects has been returned.
|
||||
StopDiskAtLimit bool
|
||||
@@ -217,7 +219,10 @@ func (o *listPathOptions) gatherResults(ctx context.Context, in <-chan metaCache
|
||||
// Do not return io.EOF
|
||||
if resCh != nil {
|
||||
resErr = nil
|
||||
resCh <- results
|
||||
select {
|
||||
case resCh <- results:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
resCh = nil
|
||||
returned = true
|
||||
}
|
||||
@@ -767,6 +772,7 @@ func (er *erasureObjects) listPath(ctx context.Context, o listPathOptions, resul
|
||||
})
|
||||
}
|
||||
|
||||
//msgp:ignore metaCacheRPC
|
||||
type metaCacheRPC struct {
|
||||
o listPathOptions
|
||||
mu sync.Mutex
|
||||
@@ -917,6 +923,7 @@ func (er *erasureObjects) saveMetaCacheStream(ctx context.Context, mc *metaCache
|
||||
return nil
|
||||
}
|
||||
|
||||
//msgp:ignore listPathRawOptions
|
||||
type listPathRawOptions struct {
|
||||
disks []StorageAPI
|
||||
fallbackDisks []StorageAPI
|
||||
|
||||
@@ -0,0 +1,535 @@
|
||||
package cmd
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *listPathOptions) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "ID":
|
||||
z.ID, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ID")
|
||||
return
|
||||
}
|
||||
case "Bucket":
|
||||
z.Bucket, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Bucket")
|
||||
return
|
||||
}
|
||||
case "BaseDir":
|
||||
z.BaseDir, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BaseDir")
|
||||
return
|
||||
}
|
||||
case "Prefix":
|
||||
z.Prefix, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Prefix")
|
||||
return
|
||||
}
|
||||
case "FilterPrefix":
|
||||
z.FilterPrefix, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "FilterPrefix")
|
||||
return
|
||||
}
|
||||
case "Marker":
|
||||
z.Marker, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Marker")
|
||||
return
|
||||
}
|
||||
case "Limit":
|
||||
z.Limit, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Limit")
|
||||
return
|
||||
}
|
||||
case "AskDisks":
|
||||
z.AskDisks, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "AskDisks")
|
||||
return
|
||||
}
|
||||
case "InclDeleted":
|
||||
z.InclDeleted, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "InclDeleted")
|
||||
return
|
||||
}
|
||||
case "Recursive":
|
||||
z.Recursive, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Recursive")
|
||||
return
|
||||
}
|
||||
case "Separator":
|
||||
z.Separator, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Separator")
|
||||
return
|
||||
}
|
||||
case "Create":
|
||||
z.Create, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Create")
|
||||
return
|
||||
}
|
||||
case "IncludeDirectories":
|
||||
z.IncludeDirectories, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "IncludeDirectories")
|
||||
return
|
||||
}
|
||||
case "Transient":
|
||||
z.Transient, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Transient")
|
||||
return
|
||||
}
|
||||
case "Versioned":
|
||||
z.Versioned, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Versioned")
|
||||
return
|
||||
}
|
||||
case "StopDiskAtLimit":
|
||||
z.StopDiskAtLimit, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "StopDiskAtLimit")
|
||||
return
|
||||
}
|
||||
case "pool":
|
||||
z.pool, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "pool")
|
||||
return
|
||||
}
|
||||
case "set":
|
||||
z.set, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "set")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *listPathOptions) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 18
|
||||
// write "ID"
|
||||
err = en.Append(0xde, 0x0, 0x12, 0xa2, 0x49, 0x44)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.ID)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ID")
|
||||
return
|
||||
}
|
||||
// write "Bucket"
|
||||
err = en.Append(0xa6, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Bucket)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Bucket")
|
||||
return
|
||||
}
|
||||
// write "BaseDir"
|
||||
err = en.Append(0xa7, 0x42, 0x61, 0x73, 0x65, 0x44, 0x69, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.BaseDir)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BaseDir")
|
||||
return
|
||||
}
|
||||
// write "Prefix"
|
||||
err = en.Append(0xa6, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Prefix)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Prefix")
|
||||
return
|
||||
}
|
||||
// write "FilterPrefix"
|
||||
err = en.Append(0xac, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.FilterPrefix)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "FilterPrefix")
|
||||
return
|
||||
}
|
||||
// write "Marker"
|
||||
err = en.Append(0xa6, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Marker)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Marker")
|
||||
return
|
||||
}
|
||||
// write "Limit"
|
||||
err = en.Append(0xa5, 0x4c, 0x69, 0x6d, 0x69, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.Limit)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Limit")
|
||||
return
|
||||
}
|
||||
// write "AskDisks"
|
||||
err = en.Append(0xa8, 0x41, 0x73, 0x6b, 0x44, 0x69, 0x73, 0x6b, 0x73)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.AskDisks)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "AskDisks")
|
||||
return
|
||||
}
|
||||
// write "InclDeleted"
|
||||
err = en.Append(0xab, 0x49, 0x6e, 0x63, 0x6c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.InclDeleted)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "InclDeleted")
|
||||
return
|
||||
}
|
||||
// write "Recursive"
|
||||
err = en.Append(0xa9, 0x52, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.Recursive)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Recursive")
|
||||
return
|
||||
}
|
||||
// write "Separator"
|
||||
err = en.Append(0xa9, 0x53, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Separator)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Separator")
|
||||
return
|
||||
}
|
||||
// write "Create"
|
||||
err = en.Append(0xa6, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.Create)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Create")
|
||||
return
|
||||
}
|
||||
// write "IncludeDirectories"
|
||||
err = en.Append(0xb2, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.IncludeDirectories)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "IncludeDirectories")
|
||||
return
|
||||
}
|
||||
// write "Transient"
|
||||
err = en.Append(0xa9, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x65, 0x6e, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.Transient)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Transient")
|
||||
return
|
||||
}
|
||||
// write "Versioned"
|
||||
err = en.Append(0xa9, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.Versioned)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Versioned")
|
||||
return
|
||||
}
|
||||
// write "StopDiskAtLimit"
|
||||
err = en.Append(0xaf, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x69, 0x73, 0x6b, 0x41, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.StopDiskAtLimit)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "StopDiskAtLimit")
|
||||
return
|
||||
}
|
||||
// write "pool"
|
||||
err = en.Append(0xa4, 0x70, 0x6f, 0x6f, 0x6c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.pool)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "pool")
|
||||
return
|
||||
}
|
||||
// write "set"
|
||||
err = en.Append(0xa3, 0x73, 0x65, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.set)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "set")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *listPathOptions) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 18
|
||||
// string "ID"
|
||||
o = append(o, 0xde, 0x0, 0x12, 0xa2, 0x49, 0x44)
|
||||
o = msgp.AppendString(o, z.ID)
|
||||
// string "Bucket"
|
||||
o = append(o, 0xa6, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74)
|
||||
o = msgp.AppendString(o, z.Bucket)
|
||||
// string "BaseDir"
|
||||
o = append(o, 0xa7, 0x42, 0x61, 0x73, 0x65, 0x44, 0x69, 0x72)
|
||||
o = msgp.AppendString(o, z.BaseDir)
|
||||
// string "Prefix"
|
||||
o = append(o, 0xa6, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78)
|
||||
o = msgp.AppendString(o, z.Prefix)
|
||||
// string "FilterPrefix"
|
||||
o = append(o, 0xac, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78)
|
||||
o = msgp.AppendString(o, z.FilterPrefix)
|
||||
// string "Marker"
|
||||
o = append(o, 0xa6, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x72)
|
||||
o = msgp.AppendString(o, z.Marker)
|
||||
// string "Limit"
|
||||
o = append(o, 0xa5, 0x4c, 0x69, 0x6d, 0x69, 0x74)
|
||||
o = msgp.AppendInt(o, z.Limit)
|
||||
// string "AskDisks"
|
||||
o = append(o, 0xa8, 0x41, 0x73, 0x6b, 0x44, 0x69, 0x73, 0x6b, 0x73)
|
||||
o = msgp.AppendString(o, z.AskDisks)
|
||||
// string "InclDeleted"
|
||||
o = append(o, 0xab, 0x49, 0x6e, 0x63, 0x6c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64)
|
||||
o = msgp.AppendBool(o, z.InclDeleted)
|
||||
// string "Recursive"
|
||||
o = append(o, 0xa9, 0x52, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65)
|
||||
o = msgp.AppendBool(o, z.Recursive)
|
||||
// string "Separator"
|
||||
o = append(o, 0xa9, 0x53, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72)
|
||||
o = msgp.AppendString(o, z.Separator)
|
||||
// string "Create"
|
||||
o = append(o, 0xa6, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65)
|
||||
o = msgp.AppendBool(o, z.Create)
|
||||
// string "IncludeDirectories"
|
||||
o = append(o, 0xb2, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73)
|
||||
o = msgp.AppendBool(o, z.IncludeDirectories)
|
||||
// string "Transient"
|
||||
o = append(o, 0xa9, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x65, 0x6e, 0x74)
|
||||
o = msgp.AppendBool(o, z.Transient)
|
||||
// string "Versioned"
|
||||
o = append(o, 0xa9, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64)
|
||||
o = msgp.AppendBool(o, z.Versioned)
|
||||
// string "StopDiskAtLimit"
|
||||
o = append(o, 0xaf, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x69, 0x73, 0x6b, 0x41, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74)
|
||||
o = msgp.AppendBool(o, z.StopDiskAtLimit)
|
||||
// string "pool"
|
||||
o = append(o, 0xa4, 0x70, 0x6f, 0x6f, 0x6c)
|
||||
o = msgp.AppendInt(o, z.pool)
|
||||
// string "set"
|
||||
o = append(o, 0xa3, 0x73, 0x65, 0x74)
|
||||
o = msgp.AppendInt(o, z.set)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *listPathOptions) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "ID":
|
||||
z.ID, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ID")
|
||||
return
|
||||
}
|
||||
case "Bucket":
|
||||
z.Bucket, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Bucket")
|
||||
return
|
||||
}
|
||||
case "BaseDir":
|
||||
z.BaseDir, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BaseDir")
|
||||
return
|
||||
}
|
||||
case "Prefix":
|
||||
z.Prefix, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Prefix")
|
||||
return
|
||||
}
|
||||
case "FilterPrefix":
|
||||
z.FilterPrefix, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "FilterPrefix")
|
||||
return
|
||||
}
|
||||
case "Marker":
|
||||
z.Marker, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Marker")
|
||||
return
|
||||
}
|
||||
case "Limit":
|
||||
z.Limit, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Limit")
|
||||
return
|
||||
}
|
||||
case "AskDisks":
|
||||
z.AskDisks, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "AskDisks")
|
||||
return
|
||||
}
|
||||
case "InclDeleted":
|
||||
z.InclDeleted, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "InclDeleted")
|
||||
return
|
||||
}
|
||||
case "Recursive":
|
||||
z.Recursive, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Recursive")
|
||||
return
|
||||
}
|
||||
case "Separator":
|
||||
z.Separator, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Separator")
|
||||
return
|
||||
}
|
||||
case "Create":
|
||||
z.Create, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Create")
|
||||
return
|
||||
}
|
||||
case "IncludeDirectories":
|
||||
z.IncludeDirectories, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "IncludeDirectories")
|
||||
return
|
||||
}
|
||||
case "Transient":
|
||||
z.Transient, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Transient")
|
||||
return
|
||||
}
|
||||
case "Versioned":
|
||||
z.Versioned, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Versioned")
|
||||
return
|
||||
}
|
||||
case "StopDiskAtLimit":
|
||||
z.StopDiskAtLimit, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "StopDiskAtLimit")
|
||||
return
|
||||
}
|
||||
case "pool":
|
||||
z.pool, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "pool")
|
||||
return
|
||||
}
|
||||
case "set":
|
||||
z.set, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "set")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *listPathOptions) Msgsize() (s int) {
|
||||
s = 3 + 3 + msgp.StringPrefixSize + len(z.ID) + 7 + msgp.StringPrefixSize + len(z.Bucket) + 8 + msgp.StringPrefixSize + len(z.BaseDir) + 7 + msgp.StringPrefixSize + len(z.Prefix) + 13 + msgp.StringPrefixSize + len(z.FilterPrefix) + 7 + msgp.StringPrefixSize + len(z.Marker) + 6 + msgp.IntSize + 9 + msgp.StringPrefixSize + len(z.AskDisks) + 12 + msgp.BoolSize + 10 + msgp.BoolSize + 10 + msgp.StringPrefixSize + len(z.Separator) + 7 + msgp.BoolSize + 19 + msgp.BoolSize + 10 + msgp.BoolSize + 10 + msgp.BoolSize + 16 + msgp.BoolSize + 5 + msgp.IntSize + 4 + msgp.IntSize
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package cmd
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
func TestMarshalUnmarshallistPathOptions(t *testing.T) {
|
||||
v := listPathOptions{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsglistPathOptions(b *testing.B) {
|
||||
v := listPathOptions{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsglistPathOptions(b *testing.B) {
|
||||
v := listPathOptions{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshallistPathOptions(b *testing.B) {
|
||||
v := listPathOptions{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodelistPathOptions(t *testing.T) {
|
||||
v := listPathOptions{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodelistPathOptions Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := listPathOptions{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodelistPathOptions(b *testing.B) {
|
||||
v := listPathOptions{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecodelistPathOptions(b *testing.B) {
|
||||
v := listPathOptions{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -299,7 +299,11 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
// If directory entry on stack before this, pop it now.
|
||||
for len(dirStack) > 0 && dirStack[len(dirStack)-1] < meta.name {
|
||||
pop := dirStack[len(dirStack)-1]
|
||||
out <- metaCacheEntry{name: pop}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case out <- metaCacheEntry{name: pop}:
|
||||
}
|
||||
if opts.Recursive {
|
||||
// Scan folder we found. Should be in correct sort order where we are.
|
||||
err := scanDir(pop)
|
||||
@@ -368,7 +372,11 @@ func (s *xlStorage) WalkDir(ctx context.Context, opts WalkDirOptions, wr io.Writ
|
||||
return ctx.Err()
|
||||
}
|
||||
pop := dirStack[len(dirStack)-1]
|
||||
out <- metaCacheEntry{name: pop}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case out <- metaCacheEntry{name: pop}:
|
||||
}
|
||||
if opts.Recursive {
|
||||
// Scan folder we found. Should be in correct sort order where we are.
|
||||
logger.LogIf(ctx, scanDir(pop))
|
||||
|
||||
@@ -130,12 +130,6 @@ func collectLocalDisksMetrics(disks map[string]struct{}) map[string]madmin.DiskM
|
||||
}
|
||||
|
||||
metrics := make(map[string]madmin.DiskMetric)
|
||||
|
||||
procStats, procErr := disk.GetAllDrivesIOStats()
|
||||
if procErr != nil {
|
||||
return metrics
|
||||
}
|
||||
|
||||
storageInfo := objLayer.LocalStorageInfo(GlobalContext, true)
|
||||
for _, d := range storageInfo.Disks {
|
||||
if len(disks) != 0 {
|
||||
@@ -170,9 +164,8 @@ func collectLocalDisksMetrics(disks map[string]struct{}) map[string]madmin.DiskM
|
||||
}
|
||||
}
|
||||
|
||||
// get disk
|
||||
if procErr == nil {
|
||||
st := procStats[disk.DevID{Major: d.Major, Minor: d.Minor}]
|
||||
st, err := disk.GetDriveStats(d.Major, d.Minor)
|
||||
if err == nil {
|
||||
dm.IOStats = madmin.DiskIOStats{
|
||||
ReadIOs: st.ReadIOs,
|
||||
ReadMerges: st.ReadMerges,
|
||||
|
||||
+79
-38
@@ -27,7 +27,6 @@ import (
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -83,6 +82,11 @@ var (
|
||||
// resourceMetricsHelpMap maps metric name to its help string
|
||||
resourceMetricsHelpMap map[MetricName]string
|
||||
resourceMetricsGroups []*MetricsGroup
|
||||
// initial values for drives (at the time of server startup)
|
||||
// used for calculating avg values for drive metrics
|
||||
latestDriveStats map[string]madmin.DiskIOStats
|
||||
latestDriveStatsMu sync.RWMutex
|
||||
lastDriveStatsRefresh time.Time
|
||||
)
|
||||
|
||||
// PeerResourceMetrics represents the resource metrics
|
||||
@@ -142,7 +146,7 @@ func init() {
|
||||
writesKBPerSec: "Kilobytes written per second on a drive",
|
||||
readsAwait: "Average time for read requests to be served on a drive",
|
||||
writesAwait: "Average time for write requests to be served on a drive",
|
||||
percUtil: "Percentage of time the disk was busy since uptime",
|
||||
percUtil: "Percentage of time the disk was busy",
|
||||
usedBytes: "Used bytes on a drive",
|
||||
totalBytes: "Total bytes on a drive",
|
||||
usedInodes: "Total inodes used on a drive",
|
||||
@@ -214,50 +218,69 @@ func updateResourceMetrics(subSys MetricSubsystem, name MetricName, val float64,
|
||||
resourceMetricsMap[subSys] = subsysMetrics
|
||||
}
|
||||
|
||||
func collectDriveMetrics(m madmin.RealtimeMetrics) {
|
||||
upt, _ := host.Uptime()
|
||||
kib := 1 << 10
|
||||
// updateDriveIOStats - Updates the drive IO stats by calculating the difference between the current and latest updated values.
|
||||
func updateDriveIOStats(currentStats madmin.DiskIOStats, latestStats madmin.DiskIOStats, labels map[string]string) {
|
||||
sectorSize := uint64(512)
|
||||
|
||||
for d, dm := range m.ByDisk {
|
||||
stats := dm.IOStats
|
||||
labels := map[string]string{"drive": d}
|
||||
updateResourceMetrics(driveSubsystem, readsPerSec, float64(stats.ReadIOs)/float64(upt), labels, false)
|
||||
|
||||
readBytes := stats.ReadSectors * sectorSize
|
||||
readKib := float64(readBytes) / float64(kib)
|
||||
readKibPerSec := readKib / float64(upt)
|
||||
updateResourceMetrics(driveSubsystem, readsKBPerSec, readKibPerSec, labels, false)
|
||||
|
||||
updateResourceMetrics(driveSubsystem, writesPerSec, float64(stats.WriteIOs)/float64(upt), labels, false)
|
||||
|
||||
writeBytes := stats.WriteSectors * sectorSize
|
||||
writeKib := float64(writeBytes) / float64(kib)
|
||||
writeKibPerSec := writeKib / float64(upt)
|
||||
updateResourceMetrics(driveSubsystem, writesKBPerSec, writeKibPerSec, labels, false)
|
||||
|
||||
rdAwait := 0.0
|
||||
if stats.ReadIOs > 0 {
|
||||
rdAwait = float64(stats.ReadTicks) / float64(stats.ReadIOs)
|
||||
}
|
||||
updateResourceMetrics(driveSubsystem, readsAwait, rdAwait, labels, false)
|
||||
|
||||
wrAwait := 0.0
|
||||
if stats.WriteIOs > 0 {
|
||||
wrAwait = float64(stats.WriteTicks) / float64(stats.WriteIOs)
|
||||
}
|
||||
updateResourceMetrics(driveSubsystem, writesAwait, wrAwait, labels, false)
|
||||
|
||||
updateResourceMetrics(driveSubsystem, percUtil, float64(stats.TotalTicks)/float64(upt*10), labels, false)
|
||||
kib := float64(1 << 10)
|
||||
diffInSeconds := time.Now().UTC().Sub(lastDriveStatsRefresh).Seconds()
|
||||
if diffInSeconds == 0 {
|
||||
// too soon to update the stats
|
||||
return
|
||||
}
|
||||
diffStats := madmin.DiskIOStats{
|
||||
ReadIOs: currentStats.ReadIOs - latestStats.ReadIOs,
|
||||
WriteIOs: currentStats.WriteIOs - latestStats.WriteIOs,
|
||||
ReadTicks: currentStats.ReadTicks - latestStats.ReadTicks,
|
||||
WriteTicks: currentStats.WriteTicks - latestStats.WriteTicks,
|
||||
TotalTicks: currentStats.TotalTicks - latestStats.TotalTicks,
|
||||
ReadSectors: currentStats.ReadSectors - latestStats.ReadSectors,
|
||||
WriteSectors: currentStats.WriteSectors - latestStats.WriteSectors,
|
||||
}
|
||||
|
||||
updateResourceMetrics(driveSubsystem, readsPerSec, float64(diffStats.ReadIOs)/diffInSeconds, labels, false)
|
||||
readKib := float64(diffStats.ReadSectors*sectorSize) / kib
|
||||
updateResourceMetrics(driveSubsystem, readsKBPerSec, readKib/diffInSeconds, labels, false)
|
||||
|
||||
updateResourceMetrics(driveSubsystem, writesPerSec, float64(diffStats.WriteIOs)/diffInSeconds, labels, false)
|
||||
writeKib := float64(diffStats.WriteSectors*sectorSize) / kib
|
||||
updateResourceMetrics(driveSubsystem, writesKBPerSec, writeKib/diffInSeconds, labels, false)
|
||||
|
||||
rdAwait := 0.0
|
||||
if diffStats.ReadIOs > 0 {
|
||||
rdAwait = float64(diffStats.ReadTicks) / float64(diffStats.ReadIOs)
|
||||
}
|
||||
updateResourceMetrics(driveSubsystem, readsAwait, rdAwait, labels, false)
|
||||
|
||||
wrAwait := 0.0
|
||||
if diffStats.WriteIOs > 0 {
|
||||
wrAwait = float64(diffStats.WriteTicks) / float64(diffStats.WriteIOs)
|
||||
}
|
||||
updateResourceMetrics(driveSubsystem, writesAwait, wrAwait, labels, false)
|
||||
updateResourceMetrics(driveSubsystem, percUtil, float64(diffStats.TotalTicks)/(diffInSeconds*10), labels, false)
|
||||
}
|
||||
|
||||
func collectDriveMetrics(m madmin.RealtimeMetrics) {
|
||||
latestDriveStatsMu.Lock()
|
||||
for d, dm := range m.ByDisk {
|
||||
labels := map[string]string{"drive": d}
|
||||
latestStats, ok := latestDriveStats[d]
|
||||
if !ok {
|
||||
latestDriveStats[d] = dm.IOStats
|
||||
continue
|
||||
}
|
||||
updateDriveIOStats(dm.IOStats, latestStats, labels)
|
||||
latestDriveStats[d] = dm.IOStats
|
||||
}
|
||||
lastDriveStatsRefresh = time.Now().UTC()
|
||||
latestDriveStatsMu.Unlock()
|
||||
|
||||
globalLocalDrivesMu.RLock()
|
||||
localDrives := globalLocalDrives
|
||||
localDrives := cloneDrives(globalLocalDrives)
|
||||
globalLocalDrivesMu.RUnlock()
|
||||
|
||||
for _, d := range localDrives {
|
||||
labels := map[string]string{"drive": d.Endpoint().RawPath}
|
||||
di, err := d.DiskInfo(GlobalContext, DiskInfoOptions{})
|
||||
labels := map[string]string{"drive": di.Endpoint}
|
||||
if err == nil {
|
||||
updateResourceMetrics(driveSubsystem, usedBytes, float64(di.Used), labels, false)
|
||||
updateResourceMetrics(driveSubsystem, totalBytes, float64(di.Total), labels, false)
|
||||
@@ -339,8 +362,26 @@ func collectLocalResourceMetrics() {
|
||||
collectDriveMetrics(m)
|
||||
}
|
||||
|
||||
func initLatestValues() {
|
||||
m := collectLocalMetrics(madmin.MetricsDisk, collectMetricsOpts{
|
||||
hosts: map[string]struct{}{
|
||||
globalLocalNodeName: {},
|
||||
},
|
||||
})
|
||||
|
||||
latestDriveStatsMu.Lock()
|
||||
latestDriveStats = map[string]madmin.DiskIOStats{}
|
||||
for d, dm := range m.ByDisk {
|
||||
latestDriveStats[d] = dm.IOStats
|
||||
}
|
||||
lastDriveStatsRefresh = time.Now().UTC()
|
||||
latestDriveStatsMu.Unlock()
|
||||
}
|
||||
|
||||
// startResourceMetricsCollection - starts the job for collecting resource metrics
|
||||
func startResourceMetricsCollection() {
|
||||
initLatestValues()
|
||||
|
||||
resourceMetricsMapMu.Lock()
|
||||
resourceMetricsMap = map[MetricSubsystem]ResourceMetrics{}
|
||||
resourceMetricsMapMu.Unlock()
|
||||
|
||||
+95
-33
@@ -29,9 +29,10 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/minio/kes-go"
|
||||
"github.com/minio/kms-go/kes"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio/internal/bucket/lifecycle"
|
||||
"github.com/minio/minio/internal/cachevalue"
|
||||
xioutil "github.com/minio/minio/internal/ioutil"
|
||||
"github.com/minio/minio/internal/logger"
|
||||
"github.com/minio/minio/internal/mcontext"
|
||||
@@ -42,6 +43,8 @@ import (
|
||||
"github.com/prometheus/procfs"
|
||||
)
|
||||
|
||||
//go:generate msgp -file=$GOFILE -unexported -io=false
|
||||
|
||||
var (
|
||||
nodeCollector *minioNodeCollector
|
||||
clusterCollector *minioClusterCollector
|
||||
@@ -270,10 +273,14 @@ const (
|
||||
vmemory = "virtual_memory_bytes"
|
||||
cpu = "cpu_total_seconds"
|
||||
|
||||
expiryPendingTasks MetricName = "expiry_pending_tasks"
|
||||
transitionPendingTasks MetricName = "transition_pending_tasks"
|
||||
transitionActiveTasks MetricName = "transition_active_tasks"
|
||||
transitionMissedTasks MetricName = "transition_missed_immediate_tasks"
|
||||
expiryPendingTasks MetricName = "expiry_pending_tasks"
|
||||
expiryMissedTasks MetricName = "expiry_missed_tasks"
|
||||
expiryMissedFreeVersions MetricName = "expiry_missed_freeversions"
|
||||
expiryMissedTierJournalTasks MetricName = "expiry_missed_tierjournal_tasks"
|
||||
expiryNumWorkers MetricName = "expiry_num_workers"
|
||||
transitionPendingTasks MetricName = "transition_pending_tasks"
|
||||
transitionActiveTasks MetricName = "transition_active_tasks"
|
||||
transitionMissedTasks MetricName = "transition_missed_immediate_tasks"
|
||||
|
||||
transitionedBytes MetricName = "transitioned_bytes"
|
||||
transitionedObjects MetricName = "transitioned_objects"
|
||||
@@ -328,7 +335,7 @@ type Metric struct {
|
||||
|
||||
// MetricsGroup are a group of metrics that are initialized together.
|
||||
type MetricsGroup struct {
|
||||
metricsCache timedValue
|
||||
metricsCache *cachevalue.Cache[[]Metric] `msg:"-"`
|
||||
cacheInterval time.Duration
|
||||
metricsGroupOpts MetricsGroupOpts
|
||||
}
|
||||
@@ -351,11 +358,10 @@ type MetricsGroupOpts struct {
|
||||
|
||||
// RegisterRead register the metrics populator function to be used
|
||||
// to populate new values upon cache invalidation.
|
||||
func (g *MetricsGroup) RegisterRead(read func(ctx context.Context) []Metric) {
|
||||
g.metricsCache.Once.Do(func() {
|
||||
g.metricsCache.Relax = true
|
||||
g.metricsCache.TTL = g.cacheInterval
|
||||
g.metricsCache.Update = func() (interface{}, error) {
|
||||
func (g *MetricsGroup) RegisterRead(read func(context.Context) []Metric) {
|
||||
g.metricsCache = cachevalue.NewFromFunc(g.cacheInterval,
|
||||
cachevalue.Opts{ReturnLastGood: true},
|
||||
func() ([]Metric, error) {
|
||||
if g.metricsGroupOpts.dependGlobalObjectAPI {
|
||||
objLayer := newObjectLayerFn()
|
||||
// Service not initialized yet
|
||||
@@ -414,18 +420,18 @@ func (g *MetricsGroup) RegisterRead(read func(ctx context.Context) []Metric) {
|
||||
}
|
||||
}
|
||||
return read(GlobalContext), nil
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (m *Metric) copyMetric() Metric {
|
||||
func (m *Metric) clone() Metric {
|
||||
metric := Metric{
|
||||
Description: m.Description,
|
||||
Value: m.Value,
|
||||
HistogramBucketLabel: m.HistogramBucketLabel,
|
||||
StaticLabels: make(map[string]string),
|
||||
VariableLabels: make(map[string]string),
|
||||
Histogram: make(map[string]uint64),
|
||||
StaticLabels: make(map[string]string, len(m.StaticLabels)),
|
||||
VariableLabels: make(map[string]string, len(m.VariableLabels)),
|
||||
Histogram: make(map[string]uint64, len(m.Histogram)),
|
||||
}
|
||||
for k, v := range m.StaticLabels {
|
||||
metric.StaticLabels[k] = v
|
||||
@@ -443,15 +449,14 @@ func (m *Metric) copyMetric() Metric {
|
||||
// once the TTL expires "read()" registered function is called
|
||||
// to return the new values and updated.
|
||||
func (g *MetricsGroup) Get() (metrics []Metric) {
|
||||
c, _ := g.metricsCache.Get()
|
||||
m, ok := c.([]Metric)
|
||||
if !ok {
|
||||
m, _ := g.metricsCache.Get()
|
||||
if len(m) == 0 {
|
||||
return []Metric{}
|
||||
}
|
||||
|
||||
metrics = make([]Metric, 0, len(m))
|
||||
for i := range m {
|
||||
metrics = append(metrics, m[i].copyMetric())
|
||||
metrics = append(metrics, m[i].clone())
|
||||
}
|
||||
return metrics
|
||||
}
|
||||
@@ -536,7 +541,7 @@ func getNodeDriveTimeoutErrorsMD() MetricDescription {
|
||||
}
|
||||
}
|
||||
|
||||
func getNodeDriveAvailablityErrorsMD() MetricDescription {
|
||||
func getNodeDriveAvailabilityErrorsMD() MetricDescription {
|
||||
return MetricDescription{
|
||||
Namespace: nodeMetricNamespace,
|
||||
Subsystem: driveSubsystem,
|
||||
@@ -1822,7 +1827,10 @@ func getGoMetrics() *MetricsGroup {
|
||||
|
||||
// getHistogramMetrics fetches histogram metrics and returns it in a []Metric
|
||||
// Note: Typically used in MetricGroup.RegisterRead
|
||||
func getHistogramMetrics(hist *prometheus.HistogramVec, desc MetricDescription) []Metric {
|
||||
//
|
||||
// The last parameter is added for compatibility - if true it lowercases the
|
||||
// `api` label values.
|
||||
func getHistogramMetrics(hist *prometheus.HistogramVec, desc MetricDescription, toLowerAPILabels bool) []Metric {
|
||||
ch := make(chan prometheus.Metric)
|
||||
go func() {
|
||||
defer xioutil.SafeClose(ch)
|
||||
@@ -1846,7 +1854,11 @@ func getHistogramMetrics(hist *prometheus.HistogramVec, desc MetricDescription)
|
||||
for _, b := range h.Bucket {
|
||||
labels := make(map[string]string)
|
||||
for _, lp := range dtoMetric.GetLabel() {
|
||||
labels[*lp.Name] = *lp.Value
|
||||
if *lp.Name == "api" && toLowerAPILabels {
|
||||
labels[*lp.Name] = strings.ToLower(*lp.Value)
|
||||
} else {
|
||||
labels[*lp.Name] = *lp.Value
|
||||
}
|
||||
}
|
||||
labels["le"] = fmt.Sprintf("%.3f", *b.UpperBound)
|
||||
metric := Metric{
|
||||
@@ -1876,7 +1888,8 @@ func getBucketTTFBMetric() *MetricsGroup {
|
||||
cacheInterval: 10 * time.Second,
|
||||
}
|
||||
mg.RegisterRead(func(ctx context.Context) []Metric {
|
||||
return getHistogramMetrics(bucketHTTPRequestsDuration, getBucketTTFBDistributionMD())
|
||||
return getHistogramMetrics(bucketHTTPRequestsDuration,
|
||||
getBucketTTFBDistributionMD(), true)
|
||||
})
|
||||
return mg
|
||||
}
|
||||
@@ -1886,7 +1899,8 @@ func getS3TTFBMetric() *MetricsGroup {
|
||||
cacheInterval: 10 * time.Second,
|
||||
}
|
||||
mg.RegisterRead(func(ctx context.Context) []Metric {
|
||||
return getHistogramMetrics(httpRequestsDuration, getS3TTFBDistributionMD())
|
||||
return getHistogramMetrics(httpRequestsDuration,
|
||||
getS3TTFBDistributionMD(), true)
|
||||
})
|
||||
return mg
|
||||
}
|
||||
@@ -1999,6 +2013,42 @@ func getILMNodeMetrics() *MetricsGroup {
|
||||
expPendingTasks := Metric{
|
||||
Description: getExpiryPendingTasksMD(),
|
||||
}
|
||||
expMissedTasks := Metric{
|
||||
Description: MetricDescription{
|
||||
Namespace: nodeMetricNamespace,
|
||||
Subsystem: ilmSubsystem,
|
||||
Name: expiryMissedTasks,
|
||||
Help: "Number of object version expiry missed due to busy system",
|
||||
Type: counterMetric,
|
||||
},
|
||||
}
|
||||
expMissedFreeVersions := Metric{
|
||||
Description: MetricDescription{
|
||||
Namespace: nodeMetricNamespace,
|
||||
Subsystem: ilmSubsystem,
|
||||
Name: expiryMissedFreeVersions,
|
||||
Help: "Number of free versions expiry missed due to busy system",
|
||||
Type: counterMetric,
|
||||
},
|
||||
}
|
||||
expMissedTierJournalTasks := Metric{
|
||||
Description: MetricDescription{
|
||||
Namespace: nodeMetricNamespace,
|
||||
Subsystem: ilmSubsystem,
|
||||
Name: expiryMissedTierJournalTasks,
|
||||
Help: "Number of tier journal entries cleanup missed due to busy system",
|
||||
Type: counterMetric,
|
||||
},
|
||||
}
|
||||
expNumWorkers := Metric{
|
||||
Description: MetricDescription{
|
||||
Namespace: nodeMetricNamespace,
|
||||
Subsystem: ilmSubsystem,
|
||||
Name: expiryNumWorkers,
|
||||
Help: "Number of workers expiring object versions currently",
|
||||
Type: gaugeMetric,
|
||||
},
|
||||
}
|
||||
trPendingTasks := Metric{
|
||||
Description: getTransitionPendingTasksMD(),
|
||||
}
|
||||
@@ -2010,6 +2060,10 @@ func getILMNodeMetrics() *MetricsGroup {
|
||||
}
|
||||
if globalExpiryState != nil {
|
||||
expPendingTasks.Value = float64(globalExpiryState.PendingTasks())
|
||||
expMissedTasks.Value = float64(globalExpiryState.stats.MissedTasks())
|
||||
expMissedFreeVersions.Value = float64(globalExpiryState.stats.MissedFreeVersTasks())
|
||||
expMissedTierJournalTasks.Value = float64(globalExpiryState.stats.MissedTierJournalTasks())
|
||||
expNumWorkers.Value = float64(globalExpiryState.stats.NumWorkers())
|
||||
}
|
||||
if globalTransitionState != nil {
|
||||
trPendingTasks.Value = float64(globalTransitionState.PendingTasks())
|
||||
@@ -2018,6 +2072,10 @@ func getILMNodeMetrics() *MetricsGroup {
|
||||
}
|
||||
return []Metric{
|
||||
expPendingTasks,
|
||||
expMissedTasks,
|
||||
expMissedFreeVersions,
|
||||
expMissedTierJournalTasks,
|
||||
expNumWorkers,
|
||||
trPendingTasks,
|
||||
trActiveTasks,
|
||||
trMissedTasks,
|
||||
@@ -2869,7 +2927,7 @@ func getHTTPMetrics(opts MetricsGroupOpts) *MetricsGroup {
|
||||
}
|
||||
mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
|
||||
if !mg.metricsGroupOpts.bucketOnly {
|
||||
httpStats := globalHTTPStats.toServerHTTPStats()
|
||||
httpStats := globalHTTPStats.toServerHTTPStats(true)
|
||||
metrics = make([]Metric, 0, 3+
|
||||
len(httpStats.CurrentS3Requests.APIStats)+
|
||||
len(httpStats.TotalS3Requests.APIStats)+
|
||||
@@ -2965,7 +3023,7 @@ func getHTTPMetrics(opts MetricsGroupOpts) *MetricsGroup {
|
||||
}
|
||||
|
||||
httpStats := globalBucketHTTPStats.load(bucket)
|
||||
for k, v := range httpStats.currentS3Requests.Load() {
|
||||
for k, v := range httpStats.currentS3Requests.Load(true) {
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getBucketS3RequestsInFlightMD(),
|
||||
Value: float64(v),
|
||||
@@ -2973,7 +3031,7 @@ func getHTTPMetrics(opts MetricsGroupOpts) *MetricsGroup {
|
||||
})
|
||||
}
|
||||
|
||||
for k, v := range httpStats.totalS3Requests.Load() {
|
||||
for k, v := range httpStats.totalS3Requests.Load(true) {
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getBucketS3RequestsTotalMD(),
|
||||
Value: float64(v),
|
||||
@@ -2981,7 +3039,7 @@ func getHTTPMetrics(opts MetricsGroupOpts) *MetricsGroup {
|
||||
})
|
||||
}
|
||||
|
||||
for k, v := range httpStats.totalS3Canceled.Load() {
|
||||
for k, v := range httpStats.totalS3Canceled.Load(true) {
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getBucketS3RequestsCanceledMD(),
|
||||
Value: float64(v),
|
||||
@@ -2989,7 +3047,7 @@ func getHTTPMetrics(opts MetricsGroupOpts) *MetricsGroup {
|
||||
})
|
||||
}
|
||||
|
||||
for k, v := range httpStats.totalS34xxErrors.Load() {
|
||||
for k, v := range httpStats.totalS34xxErrors.Load(true) {
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getBucketS3Requests4xxErrorsMD(),
|
||||
Value: float64(v),
|
||||
@@ -2997,7 +3055,7 @@ func getHTTPMetrics(opts MetricsGroupOpts) *MetricsGroup {
|
||||
})
|
||||
}
|
||||
|
||||
for k, v := range httpStats.totalS35xxErrors.Load() {
|
||||
for k, v := range httpStats.totalS35xxErrors.Load(true) {
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getBucketS3Requests5xxErrorsMD(),
|
||||
Value: float64(v),
|
||||
@@ -3464,7 +3522,7 @@ func getLocalStorageMetrics(opts MetricsGroupOpts) *MetricsGroup {
|
||||
})
|
||||
|
||||
metrics = append(metrics, Metric{
|
||||
Description: getNodeDriveAvailablityErrorsMD(),
|
||||
Description: getNodeDriveAvailabilityErrorsMD(),
|
||||
Value: float64(disk.Metrics.TotalErrorsAvailability),
|
||||
VariableLabels: map[string]string{"drive": disk.DrivePath},
|
||||
})
|
||||
@@ -3989,6 +4047,7 @@ func collectMetric(metric Metric, labels []string, values []string, metricName s
|
||||
}
|
||||
}
|
||||
|
||||
//msgp:ignore minioBucketCollector
|
||||
type minioBucketCollector struct {
|
||||
metricsGroups []*MetricsGroup
|
||||
desc *prometheus.Desc
|
||||
@@ -4024,6 +4083,7 @@ func (c *minioBucketCollector) Collect(out chan<- prometheus.Metric) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
//msgp:ignore minioClusterCollector
|
||||
type minioClusterCollector struct {
|
||||
metricsGroups []*MetricsGroup
|
||||
desc *prometheus.Desc
|
||||
@@ -4083,6 +4143,8 @@ func ReportMetrics(ctx context.Context, metricsGroups []*MetricsGroup) <-chan Me
|
||||
}
|
||||
|
||||
// minioNodeCollector is the Custom Collector
|
||||
//
|
||||
//msgp:ignore minioNodeCollector
|
||||
type minioNodeCollector struct {
|
||||
metricsGroups []*MetricsGroup
|
||||
desc *prometheus.Desc
|
||||
|
||||
@@ -0,0 +1,644 @@
|
||||
package cmd
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *Metric) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 6
|
||||
// string "Description"
|
||||
o = append(o, 0x86, 0xab, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e)
|
||||
o, err = z.Description.MarshalMsg(o)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Description")
|
||||
return
|
||||
}
|
||||
// string "StaticLabels"
|
||||
o = append(o, 0xac, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73)
|
||||
o = msgp.AppendMapHeader(o, uint32(len(z.StaticLabels)))
|
||||
for za0001, za0002 := range z.StaticLabels {
|
||||
o = msgp.AppendString(o, za0001)
|
||||
o = msgp.AppendString(o, za0002)
|
||||
}
|
||||
// string "Value"
|
||||
o = append(o, 0xa5, 0x56, 0x61, 0x6c, 0x75, 0x65)
|
||||
o = msgp.AppendFloat64(o, z.Value)
|
||||
// string "VariableLabels"
|
||||
o = append(o, 0xae, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73)
|
||||
o = msgp.AppendMapHeader(o, uint32(len(z.VariableLabels)))
|
||||
for za0003, za0004 := range z.VariableLabels {
|
||||
o = msgp.AppendString(o, za0003)
|
||||
o = msgp.AppendString(o, za0004)
|
||||
}
|
||||
// string "HistogramBucketLabel"
|
||||
o = append(o, 0xb4, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c)
|
||||
o = msgp.AppendString(o, z.HistogramBucketLabel)
|
||||
// string "Histogram"
|
||||
o = append(o, 0xa9, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d)
|
||||
o = msgp.AppendMapHeader(o, uint32(len(z.Histogram)))
|
||||
for za0005, za0006 := range z.Histogram {
|
||||
o = msgp.AppendString(o, za0005)
|
||||
o = msgp.AppendUint64(o, za0006)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *Metric) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Description":
|
||||
bts, err = z.Description.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Description")
|
||||
return
|
||||
}
|
||||
case "StaticLabels":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "StaticLabels")
|
||||
return
|
||||
}
|
||||
if z.StaticLabels == nil {
|
||||
z.StaticLabels = make(map[string]string, zb0002)
|
||||
} else if len(z.StaticLabels) > 0 {
|
||||
for key := range z.StaticLabels {
|
||||
delete(z.StaticLabels, key)
|
||||
}
|
||||
}
|
||||
for zb0002 > 0 {
|
||||
var za0001 string
|
||||
var za0002 string
|
||||
zb0002--
|
||||
za0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "StaticLabels")
|
||||
return
|
||||
}
|
||||
za0002, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "StaticLabels", za0001)
|
||||
return
|
||||
}
|
||||
z.StaticLabels[za0001] = za0002
|
||||
}
|
||||
case "Value":
|
||||
z.Value, bts, err = msgp.ReadFloat64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Value")
|
||||
return
|
||||
}
|
||||
case "VariableLabels":
|
||||
var zb0003 uint32
|
||||
zb0003, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "VariableLabels")
|
||||
return
|
||||
}
|
||||
if z.VariableLabels == nil {
|
||||
z.VariableLabels = make(map[string]string, zb0003)
|
||||
} else if len(z.VariableLabels) > 0 {
|
||||
for key := range z.VariableLabels {
|
||||
delete(z.VariableLabels, key)
|
||||
}
|
||||
}
|
||||
for zb0003 > 0 {
|
||||
var za0003 string
|
||||
var za0004 string
|
||||
zb0003--
|
||||
za0003, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "VariableLabels")
|
||||
return
|
||||
}
|
||||
za0004, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "VariableLabels", za0003)
|
||||
return
|
||||
}
|
||||
z.VariableLabels[za0003] = za0004
|
||||
}
|
||||
case "HistogramBucketLabel":
|
||||
z.HistogramBucketLabel, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "HistogramBucketLabel")
|
||||
return
|
||||
}
|
||||
case "Histogram":
|
||||
var zb0004 uint32
|
||||
zb0004, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Histogram")
|
||||
return
|
||||
}
|
||||
if z.Histogram == nil {
|
||||
z.Histogram = make(map[string]uint64, zb0004)
|
||||
} else if len(z.Histogram) > 0 {
|
||||
for key := range z.Histogram {
|
||||
delete(z.Histogram, key)
|
||||
}
|
||||
}
|
||||
for zb0004 > 0 {
|
||||
var za0005 string
|
||||
var za0006 uint64
|
||||
zb0004--
|
||||
za0005, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Histogram")
|
||||
return
|
||||
}
|
||||
za0006, bts, err = msgp.ReadUint64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Histogram", za0005)
|
||||
return
|
||||
}
|
||||
z.Histogram[za0005] = za0006
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *Metric) Msgsize() (s int) {
|
||||
s = 1 + 12 + z.Description.Msgsize() + 13 + msgp.MapHeaderSize
|
||||
if z.StaticLabels != nil {
|
||||
for za0001, za0002 := range z.StaticLabels {
|
||||
_ = za0002
|
||||
s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002)
|
||||
}
|
||||
}
|
||||
s += 6 + msgp.Float64Size + 15 + msgp.MapHeaderSize
|
||||
if z.VariableLabels != nil {
|
||||
for za0003, za0004 := range z.VariableLabels {
|
||||
_ = za0004
|
||||
s += msgp.StringPrefixSize + len(za0003) + msgp.StringPrefixSize + len(za0004)
|
||||
}
|
||||
}
|
||||
s += 21 + msgp.StringPrefixSize + len(z.HistogramBucketLabel) + 10 + msgp.MapHeaderSize
|
||||
if z.Histogram != nil {
|
||||
for za0005, za0006 := range z.Histogram {
|
||||
_ = za0006
|
||||
s += msgp.StringPrefixSize + len(za0005) + msgp.Uint64Size
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *MetricDescription) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 5
|
||||
// string "Namespace"
|
||||
o = append(o, 0x85, 0xa9, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65)
|
||||
o = msgp.AppendString(o, string(z.Namespace))
|
||||
// string "Subsystem"
|
||||
o = append(o, 0xa9, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d)
|
||||
o = msgp.AppendString(o, string(z.Subsystem))
|
||||
// string "Name"
|
||||
o = append(o, 0xa4, 0x4e, 0x61, 0x6d, 0x65)
|
||||
o = msgp.AppendString(o, string(z.Name))
|
||||
// string "Help"
|
||||
o = append(o, 0xa4, 0x48, 0x65, 0x6c, 0x70)
|
||||
o = msgp.AppendString(o, z.Help)
|
||||
// string "Type"
|
||||
o = append(o, 0xa4, 0x54, 0x79, 0x70, 0x65)
|
||||
o = msgp.AppendString(o, string(z.Type))
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *MetricDescription) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "Namespace":
|
||||
{
|
||||
var zb0002 string
|
||||
zb0002, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Namespace")
|
||||
return
|
||||
}
|
||||
z.Namespace = MetricNamespace(zb0002)
|
||||
}
|
||||
case "Subsystem":
|
||||
{
|
||||
var zb0003 string
|
||||
zb0003, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Subsystem")
|
||||
return
|
||||
}
|
||||
z.Subsystem = MetricSubsystem(zb0003)
|
||||
}
|
||||
case "Name":
|
||||
{
|
||||
var zb0004 string
|
||||
zb0004, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Name")
|
||||
return
|
||||
}
|
||||
z.Name = MetricName(zb0004)
|
||||
}
|
||||
case "Help":
|
||||
z.Help, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Help")
|
||||
return
|
||||
}
|
||||
case "Type":
|
||||
{
|
||||
var zb0005 string
|
||||
zb0005, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Type")
|
||||
return
|
||||
}
|
||||
z.Type = MetricType(zb0005)
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *MetricDescription) Msgsize() (s int) {
|
||||
s = 1 + 10 + msgp.StringPrefixSize + len(string(z.Namespace)) + 10 + msgp.StringPrefixSize + len(string(z.Subsystem)) + 5 + msgp.StringPrefixSize + len(string(z.Name)) + 5 + msgp.StringPrefixSize + len(z.Help) + 5 + msgp.StringPrefixSize + len(string(z.Type))
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z MetricName) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
o = msgp.AppendString(o, string(z))
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *MetricName) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
{
|
||||
var zb0001 string
|
||||
zb0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
(*z) = MetricName(zb0001)
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z MetricName) Msgsize() (s int) {
|
||||
s = msgp.StringPrefixSize + len(string(z))
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z MetricNamespace) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
o = msgp.AppendString(o, string(z))
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *MetricNamespace) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
{
|
||||
var zb0001 string
|
||||
zb0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
(*z) = MetricNamespace(zb0001)
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z MetricNamespace) Msgsize() (s int) {
|
||||
s = msgp.StringPrefixSize + len(string(z))
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z MetricSubsystem) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
o = msgp.AppendString(o, string(z))
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *MetricSubsystem) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
{
|
||||
var zb0001 string
|
||||
zb0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
(*z) = MetricSubsystem(zb0001)
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z MetricSubsystem) Msgsize() (s int) {
|
||||
s = msgp.StringPrefixSize + len(string(z))
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z MetricType) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
o = msgp.AppendString(o, string(z))
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *MetricType) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
{
|
||||
var zb0001 string
|
||||
zb0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
(*z) = MetricType(zb0001)
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z MetricType) Msgsize() (s int) {
|
||||
s = msgp.StringPrefixSize + len(string(z))
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *MetricsGroup) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 2
|
||||
// string "cacheInterval"
|
||||
o = append(o, 0x82, 0xad, 0x63, 0x61, 0x63, 0x68, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c)
|
||||
o = msgp.AppendDuration(o, z.cacheInterval)
|
||||
// string "metricsGroupOpts"
|
||||
o = append(o, 0xb0, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4f, 0x70, 0x74, 0x73)
|
||||
o, err = z.metricsGroupOpts.MarshalMsg(o)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "metricsGroupOpts")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *MetricsGroup) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "cacheInterval":
|
||||
z.cacheInterval, bts, err = msgp.ReadDurationBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "cacheInterval")
|
||||
return
|
||||
}
|
||||
case "metricsGroupOpts":
|
||||
bts, err = z.metricsGroupOpts.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "metricsGroupOpts")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *MetricsGroup) Msgsize() (s int) {
|
||||
s = 1 + 14 + msgp.DurationSize + 17 + z.metricsGroupOpts.Msgsize()
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *MetricsGroupOpts) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 12
|
||||
// string "dependGlobalObjectAPI"
|
||||
o = append(o, 0x8c, 0xb5, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x50, 0x49)
|
||||
o = msgp.AppendBool(o, z.dependGlobalObjectAPI)
|
||||
// string "dependGlobalAuthNPlugin"
|
||||
o = append(o, 0xb7, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x4e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e)
|
||||
o = msgp.AppendBool(o, z.dependGlobalAuthNPlugin)
|
||||
// string "dependGlobalSiteReplicationSys"
|
||||
o = append(o, 0xbe, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x79, 0x73)
|
||||
o = msgp.AppendBool(o, z.dependGlobalSiteReplicationSys)
|
||||
// string "dependGlobalNotificationSys"
|
||||
o = append(o, 0xbb, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x79, 0x73)
|
||||
o = msgp.AppendBool(o, z.dependGlobalNotificationSys)
|
||||
// string "dependGlobalKMS"
|
||||
o = append(o, 0xaf, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4b, 0x4d, 0x53)
|
||||
o = msgp.AppendBool(o, z.dependGlobalKMS)
|
||||
// string "bucketOnly"
|
||||
o = append(o, 0xaa, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x79)
|
||||
o = msgp.AppendBool(o, z.bucketOnly)
|
||||
// string "dependGlobalLambdaTargetList"
|
||||
o = append(o, 0xbc, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74)
|
||||
o = msgp.AppendBool(o, z.dependGlobalLambdaTargetList)
|
||||
// string "dependGlobalIAMSys"
|
||||
o = append(o, 0xb2, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x41, 0x4d, 0x53, 0x79, 0x73)
|
||||
o = msgp.AppendBool(o, z.dependGlobalIAMSys)
|
||||
// string "dependGlobalLockServer"
|
||||
o = append(o, 0xb6, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x4c, 0x6f, 0x63, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72)
|
||||
o = msgp.AppendBool(o, z.dependGlobalLockServer)
|
||||
// string "dependGlobalIsDistErasure"
|
||||
o = append(o, 0xb9, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x73, 0x44, 0x69, 0x73, 0x74, 0x45, 0x72, 0x61, 0x73, 0x75, 0x72, 0x65)
|
||||
o = msgp.AppendBool(o, z.dependGlobalIsDistErasure)
|
||||
// string "dependGlobalBackgroundHealState"
|
||||
o = append(o, 0xbf, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x65, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65)
|
||||
o = msgp.AppendBool(o, z.dependGlobalBackgroundHealState)
|
||||
// string "dependBucketTargetSys"
|
||||
o = append(o, 0xb5, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x79, 0x73)
|
||||
o = msgp.AppendBool(o, z.dependBucketTargetSys)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *MetricsGroupOpts) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "dependGlobalObjectAPI":
|
||||
z.dependGlobalObjectAPI, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependGlobalObjectAPI")
|
||||
return
|
||||
}
|
||||
case "dependGlobalAuthNPlugin":
|
||||
z.dependGlobalAuthNPlugin, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependGlobalAuthNPlugin")
|
||||
return
|
||||
}
|
||||
case "dependGlobalSiteReplicationSys":
|
||||
z.dependGlobalSiteReplicationSys, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependGlobalSiteReplicationSys")
|
||||
return
|
||||
}
|
||||
case "dependGlobalNotificationSys":
|
||||
z.dependGlobalNotificationSys, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependGlobalNotificationSys")
|
||||
return
|
||||
}
|
||||
case "dependGlobalKMS":
|
||||
z.dependGlobalKMS, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependGlobalKMS")
|
||||
return
|
||||
}
|
||||
case "bucketOnly":
|
||||
z.bucketOnly, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "bucketOnly")
|
||||
return
|
||||
}
|
||||
case "dependGlobalLambdaTargetList":
|
||||
z.dependGlobalLambdaTargetList, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependGlobalLambdaTargetList")
|
||||
return
|
||||
}
|
||||
case "dependGlobalIAMSys":
|
||||
z.dependGlobalIAMSys, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependGlobalIAMSys")
|
||||
return
|
||||
}
|
||||
case "dependGlobalLockServer":
|
||||
z.dependGlobalLockServer, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependGlobalLockServer")
|
||||
return
|
||||
}
|
||||
case "dependGlobalIsDistErasure":
|
||||
z.dependGlobalIsDistErasure, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependGlobalIsDistErasure")
|
||||
return
|
||||
}
|
||||
case "dependGlobalBackgroundHealState":
|
||||
z.dependGlobalBackgroundHealState, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependGlobalBackgroundHealState")
|
||||
return
|
||||
}
|
||||
case "dependBucketTargetSys":
|
||||
z.dependBucketTargetSys, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "dependBucketTargetSys")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *MetricsGroupOpts) Msgsize() (s int) {
|
||||
s = 1 + 22 + msgp.BoolSize + 24 + msgp.BoolSize + 31 + msgp.BoolSize + 28 + msgp.BoolSize + 16 + msgp.BoolSize + 11 + msgp.BoolSize + 29 + msgp.BoolSize + 19 + msgp.BoolSize + 23 + msgp.BoolSize + 26 + msgp.BoolSize + 32 + msgp.BoolSize + 22 + msgp.BoolSize
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package cmd
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
func TestMarshalUnmarshalMetric(t *testing.T) {
|
||||
v := Metric{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsgMetric(b *testing.B) {
|
||||
v := Metric{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgMetric(b *testing.B) {
|
||||
v := Metric{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalMetric(b *testing.B) {
|
||||
v := Metric{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalMetricDescription(t *testing.T) {
|
||||
v := MetricDescription{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsgMetricDescription(b *testing.B) {
|
||||
v := MetricDescription{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgMetricDescription(b *testing.B) {
|
||||
v := MetricDescription{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalMetricDescription(b *testing.B) {
|
||||
v := MetricDescription{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalMetricsGroup(t *testing.T) {
|
||||
v := MetricsGroup{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsgMetricsGroup(b *testing.B) {
|
||||
v := MetricsGroup{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgMetricsGroup(b *testing.B) {
|
||||
v := MetricsGroup{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalMetricsGroup(b *testing.B) {
|
||||
v := MetricsGroup{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalMetricsGroupOpts(t *testing.T) {
|
||||
v := MetricsGroupOpts{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsgMetricsGroupOpts(b *testing.B) {
|
||||
v := MetricsGroupOpts{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgMetricsGroupOpts(b *testing.B) {
|
||||
v := MetricsGroupOpts{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalMetricsGroupOpts(b *testing.B) {
|
||||
v := MetricsGroupOpts{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ func TestGetHistogramMetrics(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
metrics := getHistogramMetrics(ttfbHist, getBucketTTFBDistributionMD())
|
||||
metrics := getHistogramMetrics(ttfbHist, getBucketTTFBDistributionMD(), false)
|
||||
// additional labels for +Inf for all histogram metrics
|
||||
if expPoints := len(labels) * (len(histBuckets) + 1); expPoints != len(metrics) {
|
||||
t.Fatalf("Expected %v data points but got %v", expPoints, len(metrics))
|
||||
|
||||
+1
-1
@@ -190,7 +190,7 @@ func healingMetricsPrometheus(ch chan<- prometheus.Metric) {
|
||||
// collects http metrics for MinIO server in Prometheus specific format
|
||||
// and sends to given channel
|
||||
func httpMetricsPrometheus(ch chan<- prometheus.Metric) {
|
||||
httpStats := globalHTTPStats.toServerHTTPStats()
|
||||
httpStats := globalHTTPStats.toServerHTTPStats(true)
|
||||
|
||||
for api, value := range httpStats.CurrentS3Requests.APIStats {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
|
||||
+3
-31
@@ -19,7 +19,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
@@ -44,37 +43,15 @@ type partialOperation struct {
|
||||
// mrfState sncapsulates all the information
|
||||
// related to the global background MRF.
|
||||
type mrfState struct {
|
||||
ctx context.Context
|
||||
pools *erasureServerPools
|
||||
|
||||
mu sync.RWMutex
|
||||
opCh chan partialOperation
|
||||
}
|
||||
|
||||
// Initialize healing MRF subsystem
|
||||
func (m *mrfState) init(ctx context.Context, objAPI ObjectLayer) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.ctx = ctx
|
||||
m.opCh = make(chan partialOperation, mrfOpsQueueSize)
|
||||
|
||||
var ok bool
|
||||
m.pools, ok = objAPI.(*erasureServerPools)
|
||||
if ok {
|
||||
go m.healRoutine()
|
||||
}
|
||||
}
|
||||
|
||||
// Add a partial S3 operation (put/delete) when one or more disks are offline.
|
||||
func (m *mrfState) addPartialOp(op partialOperation) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
select {
|
||||
case m.opCh <- op:
|
||||
default:
|
||||
@@ -86,10 +63,10 @@ var healSleeper = newDynamicSleeper(5, time.Second, false)
|
||||
// healRoutine listens to new disks reconnection events and
|
||||
// issues healing requests for queued objects belonging to the
|
||||
// corresponding erasure set
|
||||
func (m *mrfState) healRoutine() {
|
||||
func (m *mrfState) healRoutine(z *erasureServerPools) {
|
||||
for {
|
||||
select {
|
||||
case <-m.ctx.Done():
|
||||
case <-GlobalContext.Done():
|
||||
return
|
||||
case u, ok := <-m.opCh:
|
||||
if !ok {
|
||||
@@ -115,7 +92,7 @@ func (m *mrfState) healRoutine() {
|
||||
healBucket(u.bucket, scan)
|
||||
} else {
|
||||
if u.allVersions {
|
||||
m.pools.serverPools[u.poolIndex].sets[u.setIndex].listAndHeal(u.bucket, u.object, u.scanMode, healObjectVersionsDisparity)
|
||||
z.serverPools[u.poolIndex].sets[u.setIndex].listAndHeal(u.bucket, u.object, u.scanMode, healObjectVersionsDisparity)
|
||||
} else {
|
||||
healObject(u.bucket, u.object, u.versionID, scan)
|
||||
}
|
||||
@@ -125,8 +102,3 @@ func (m *mrfState) healRoutine() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize healing MRF
|
||||
func initHealMRF(ctx context.Context, obj ObjectLayer) {
|
||||
globalMRFState.init(ctx, obj)
|
||||
}
|
||||
|
||||
+6
-1
@@ -98,7 +98,12 @@ func mustGetLocalIP6() (ipList set.StringSet) {
|
||||
|
||||
// getHostIP returns IP address of given host.
|
||||
func getHostIP(host string) (ipList set.StringSet, err error) {
|
||||
addrs, err := globalDNSCache.LookupHost(GlobalContext, host)
|
||||
lookupHost := globalDNSCache.LookupHost
|
||||
if IsKubernetes() || IsDocker() {
|
||||
lookupHost = net.DefaultResolver.LookupHost
|
||||
}
|
||||
|
||||
addrs, err := lookupHost(GlobalContext, host)
|
||||
if err != nil {
|
||||
return ipList, err
|
||||
}
|
||||
|
||||
@@ -1104,24 +1104,6 @@ func (sys *NotificationSys) ServerInfo(metrics bool) []madmin.ServerProperties {
|
||||
return reply
|
||||
}
|
||||
|
||||
// GetLocalDiskIDs - return disk ids of the local disks of the peers.
|
||||
func (sys *NotificationSys) GetLocalDiskIDs(ctx context.Context) (localDiskIDs [][]string) {
|
||||
localDiskIDs = make([][]string, len(sys.peerClients))
|
||||
var wg sync.WaitGroup
|
||||
for idx, client := range sys.peerClients {
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(idx int, client *peerRESTClient) {
|
||||
defer wg.Done()
|
||||
localDiskIDs[idx] = client.GetLocalDiskIDs(ctx)
|
||||
}(idx, client)
|
||||
}
|
||||
wg.Wait()
|
||||
return localDiskIDs
|
||||
}
|
||||
|
||||
// returns all the peers that are currently online.
|
||||
func (sys *NotificationSys) getOnlinePeers() []*peerRESTClient {
|
||||
var peerClients []*peerRESTClient
|
||||
|
||||
@@ -680,13 +680,13 @@ func isErrBucketNotFound(err error) bool {
|
||||
return errors.As(err, &bkNotFound)
|
||||
}
|
||||
|
||||
// isErrReadQuorum check if the error type is InsufficentReadQuorum
|
||||
// isErrReadQuorum check if the error type is InsufficientReadQuorum
|
||||
func isErrReadQuorum(err error) bool {
|
||||
var rquorum InsufficientReadQuorum
|
||||
return errors.As(err, &rquorum)
|
||||
}
|
||||
|
||||
// isErrWriteQuorum check if the error type is InsufficentWriteQuorum
|
||||
// isErrWriteQuorum check if the error type is InsufficientWriteQuorum
|
||||
func isErrWriteQuorum(err error) bool {
|
||||
var rquorum InsufficientWriteQuorum
|
||||
return errors.As(err, &rquorum)
|
||||
|
||||
@@ -27,6 +27,11 @@ import (
|
||||
"github.com/minio/minio/internal/logger"
|
||||
)
|
||||
|
||||
// Checks on CopyObject arguments, bucket and object.
|
||||
func checkCopyObjArgs(ctx context.Context, bucket, object string) error {
|
||||
return checkBucketAndObjectNames(ctx, bucket, object)
|
||||
}
|
||||
|
||||
// Checks on GetObject arguments, bucket and object.
|
||||
func checkGetObjArgs(ctx context.Context, bucket, object string) error {
|
||||
return checkBucketAndObjectNames(ctx, bucket, object)
|
||||
@@ -106,42 +111,42 @@ func checkListMultipartArgs(ctx context.Context, bucket, prefix, keyMarker, uplo
|
||||
}
|
||||
|
||||
// Checks for NewMultipartUpload arguments validity, also validates if bucket exists.
|
||||
func checkNewMultipartArgs(ctx context.Context, bucket, object string, obj ObjectLayer) error {
|
||||
return checkObjectArgs(ctx, bucket, object, obj)
|
||||
func checkNewMultipartArgs(ctx context.Context, bucket, object string) error {
|
||||
return checkObjectArgs(ctx, bucket, object)
|
||||
}
|
||||
|
||||
func checkMultipartObjectArgs(ctx context.Context, bucket, object, uploadID string, obj ObjectLayer) error {
|
||||
func checkMultipartObjectArgs(ctx context.Context, bucket, object, uploadID string) error {
|
||||
_, err := base64.RawURLEncoding.DecodeString(uploadID)
|
||||
if err != nil {
|
||||
return MalformedUploadID{
|
||||
UploadID: uploadID,
|
||||
}
|
||||
}
|
||||
return checkObjectArgs(ctx, bucket, object, obj)
|
||||
return checkObjectArgs(ctx, bucket, object)
|
||||
}
|
||||
|
||||
// Checks for PutObjectPart arguments validity, also validates if bucket exists.
|
||||
func checkPutObjectPartArgs(ctx context.Context, bucket, object, uploadID string, obj ObjectLayer) error {
|
||||
return checkMultipartObjectArgs(ctx, bucket, object, uploadID, obj)
|
||||
func checkPutObjectPartArgs(ctx context.Context, bucket, object, uploadID string) error {
|
||||
return checkMultipartObjectArgs(ctx, bucket, object, uploadID)
|
||||
}
|
||||
|
||||
// Checks for ListParts arguments validity, also validates if bucket exists.
|
||||
func checkListPartsArgs(ctx context.Context, bucket, object, uploadID string, obj ObjectLayer) error {
|
||||
return checkMultipartObjectArgs(ctx, bucket, object, uploadID, obj)
|
||||
func checkListPartsArgs(ctx context.Context, bucket, object, uploadID string) error {
|
||||
return checkMultipartObjectArgs(ctx, bucket, object, uploadID)
|
||||
}
|
||||
|
||||
// Checks for CompleteMultipartUpload arguments validity, also validates if bucket exists.
|
||||
func checkCompleteMultipartArgs(ctx context.Context, bucket, object, uploadID string, obj ObjectLayer) error {
|
||||
return checkMultipartObjectArgs(ctx, bucket, object, uploadID, obj)
|
||||
func checkCompleteMultipartArgs(ctx context.Context, bucket, object, uploadID string) error {
|
||||
return checkMultipartObjectArgs(ctx, bucket, object, uploadID)
|
||||
}
|
||||
|
||||
// Checks for AbortMultipartUpload arguments validity, also validates if bucket exists.
|
||||
func checkAbortMultipartArgs(ctx context.Context, bucket, object, uploadID string, obj ObjectLayer) error {
|
||||
return checkMultipartObjectArgs(ctx, bucket, object, uploadID, obj)
|
||||
func checkAbortMultipartArgs(ctx context.Context, bucket, object, uploadID string) error {
|
||||
return checkMultipartObjectArgs(ctx, bucket, object, uploadID)
|
||||
}
|
||||
|
||||
// Checks Object arguments validity.
|
||||
func checkObjectArgs(ctx context.Context, bucket, object string, obj ObjectLayer) error {
|
||||
func checkObjectArgs(ctx context.Context, bucket, object string) error {
|
||||
// Verify if bucket is valid.
|
||||
if !isMinioMetaBucketName(bucket) && s3utils.CheckValidBucketNameStrict(bucket) != nil {
|
||||
return BucketNameInvalid{Bucket: bucket}
|
||||
|
||||
@@ -117,7 +117,13 @@ type ObjectOptions struct {
|
||||
// Object must have been read at this point.
|
||||
IndexCB func() []byte
|
||||
|
||||
// InclFreeVersions indicates that free versions need to be included
|
||||
// when looking up a version by fi.VersionID
|
||||
InclFreeVersions bool
|
||||
// SkipFreeVersion skips adding a free version when a tiered version is
|
||||
// being 'replaced'
|
||||
// Note: Used only when a tiered object is being expired.
|
||||
SkipFreeVersion bool
|
||||
|
||||
MetadataChg bool // is true if it is a metadata update operation.
|
||||
EvalRetentionBypassFn EvalRetentionBypassFn // only set for enforcing retention bypass on DeleteObject.
|
||||
@@ -287,7 +293,6 @@ type ObjectLayer interface {
|
||||
|
||||
// Returns health of the backend
|
||||
Health(ctx context.Context, opts HealthOptions) HealthResult
|
||||
ReadHealth(ctx context.Context) bool
|
||||
|
||||
// Metadata operations
|
||||
PutObjectMetadata(context.Context, string, string, ObjectOptions) (ObjectInfo, error)
|
||||
|
||||
@@ -227,7 +227,7 @@ func testObjectAPIPutObjectPart(obj ObjectLayer, instanceType string, t TestErrH
|
||||
inputReaderData string
|
||||
inputMd5 string
|
||||
inputSHA256 string
|
||||
intputDataSize int64
|
||||
inputDataSize int64
|
||||
// flag indicating whether the test should pass.
|
||||
shouldPass bool
|
||||
// expected error output.
|
||||
@@ -287,27 +287,27 @@ func testObjectAPIPutObjectPart(obj ObjectLayer, instanceType string, t TestErrH
|
||||
// Test case - 14.
|
||||
// Input with size more than the size of actual data inside the reader.
|
||||
{
|
||||
bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputReaderData: "abcd", inputMd5: "e2fc714c4727ee9395f324cd2e7f3335", intputDataSize: int64(len("abcd") + 1),
|
||||
bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputReaderData: "abcd", inputMd5: "e2fc714c4727ee9395f324cd2e7f3335", inputDataSize: int64(len("abcd") + 1),
|
||||
expectedError: hash.BadDigest{ExpectedMD5: "e2fc714c4727ee9395f324cd2e7f3335", CalculatedMD5: "e2fc714c4727ee9395f324cd2e7f331f"},
|
||||
},
|
||||
// Test case - 15.
|
||||
// Input with size less than the size of actual data inside the reader.
|
||||
{
|
||||
bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputReaderData: "abcd", inputMd5: "900150983cd24fb0d6963f7d28e17f73", intputDataSize: int64(len("abcd") - 1),
|
||||
bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputReaderData: "abcd", inputMd5: "900150983cd24fb0d6963f7d28e17f73", inputDataSize: int64(len("abcd") - 1),
|
||||
expectedError: ioutil.ErrOverread,
|
||||
},
|
||||
|
||||
// Test case - 16-19.
|
||||
// Validating for success cases.
|
||||
{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputReaderData: "abcd", inputMd5: "e2fc714c4727ee9395f324cd2e7f331f", inputSHA256: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", intputDataSize: int64(len("abcd")), shouldPass: true},
|
||||
{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 2, inputReaderData: "efgh", inputMd5: "1f7690ebdd9b4caf8fab49ca1757bf27", inputSHA256: "e5e088a0b66163a0a26a5e053d2a4496dc16ab6e0e3dd1adf2d16aa84a078c9d", intputDataSize: int64(len("efgh")), shouldPass: true},
|
||||
{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 3, inputReaderData: "ijkl", inputMd5: "09a0877d04abf8759f99adec02baf579", inputSHA256: "005c19658919186b85618c5870463eec8d9b8c1a9d00208a5352891ba5bbe086", intputDataSize: int64(len("abcd")), shouldPass: true},
|
||||
{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 4, inputReaderData: "mnop", inputMd5: "e132e96a5ddad6da8b07bba6f6131fef", inputSHA256: "f1afc31479522d6cff1ed068f93998f05a8cd3b22f5c37d7f307084f62d1d270", intputDataSize: int64(len("abcd")), shouldPass: true},
|
||||
{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 1, inputReaderData: "abcd", inputMd5: "e2fc714c4727ee9395f324cd2e7f331f", inputSHA256: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", inputDataSize: int64(len("abcd")), shouldPass: true},
|
||||
{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 2, inputReaderData: "efgh", inputMd5: "1f7690ebdd9b4caf8fab49ca1757bf27", inputSHA256: "e5e088a0b66163a0a26a5e053d2a4496dc16ab6e0e3dd1adf2d16aa84a078c9d", inputDataSize: int64(len("efgh")), shouldPass: true},
|
||||
{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 3, inputReaderData: "ijkl", inputMd5: "09a0877d04abf8759f99adec02baf579", inputSHA256: "005c19658919186b85618c5870463eec8d9b8c1a9d00208a5352891ba5bbe086", inputDataSize: int64(len("abcd")), shouldPass: true},
|
||||
{bucketName: bucket, objName: object, uploadID: uploadID, PartID: 4, inputReaderData: "mnop", inputMd5: "e132e96a5ddad6da8b07bba6f6131fef", inputSHA256: "f1afc31479522d6cff1ed068f93998f05a8cd3b22f5c37d7f307084f62d1d270", inputDataSize: int64(len("abcd")), shouldPass: true},
|
||||
}
|
||||
|
||||
// Validate all the test cases.
|
||||
for i, testCase := range testCases {
|
||||
actualInfo, actualErr := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, testCase.inputSHA256), opts)
|
||||
actualInfo, actualErr := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.inputDataSize, testCase.inputMd5, testCase.inputSHA256), opts)
|
||||
// All are test cases above are expected to fail.
|
||||
if actualErr != nil && testCase.shouldPass {
|
||||
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s.", i+1, instanceType, actualErr.Error())
|
||||
@@ -410,7 +410,7 @@ func testListMultipartUploads(obj ObjectLayer, instanceType string, t TestErrHan
|
||||
PartID int
|
||||
inputReaderData string
|
||||
inputMd5 string
|
||||
intputDataSize int64
|
||||
inputDataSize int64
|
||||
expectedMd5 string
|
||||
}{
|
||||
// Case 1-4.
|
||||
@@ -439,7 +439,7 @@ func testListMultipartUploads(obj ObjectLayer, instanceType string, t TestErrHan
|
||||
sha256sum := ""
|
||||
// Iterating over creatPartCases to generate multipart chunks.
|
||||
for _, testCase := range createPartCases {
|
||||
_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), opts)
|
||||
_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.inputDataSize, testCase.inputMd5, sha256sum), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%s : %s", instanceType, err.Error())
|
||||
}
|
||||
@@ -1263,7 +1263,7 @@ func testListObjectPartsDiskNotFound(obj ObjectLayer, instanceType string, disks
|
||||
PartID int
|
||||
inputReaderData string
|
||||
inputMd5 string
|
||||
intputDataSize int64
|
||||
inputDataSize int64
|
||||
expectedMd5 string
|
||||
}{
|
||||
// Case 1-4.
|
||||
@@ -1277,7 +1277,7 @@ func testListObjectPartsDiskNotFound(obj ObjectLayer, instanceType string, disks
|
||||
sha256sum := ""
|
||||
// Iterating over creatPartCases to generate multipart chunks.
|
||||
for _, testCase := range createPartCases {
|
||||
_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), opts)
|
||||
_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.inputDataSize, testCase.inputMd5, sha256sum), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%s : %s", instanceType, err.Error())
|
||||
}
|
||||
@@ -1500,7 +1500,7 @@ func testListObjectParts(obj ObjectLayer, instanceType string, t TestErrHandler)
|
||||
PartID int
|
||||
inputReaderData string
|
||||
inputMd5 string
|
||||
intputDataSize int64
|
||||
inputDataSize int64
|
||||
expectedMd5 string
|
||||
}{
|
||||
// Case 1-4.
|
||||
@@ -1514,7 +1514,7 @@ func testListObjectParts(obj ObjectLayer, instanceType string, t TestErrHandler)
|
||||
sha256sum := ""
|
||||
// Iterating over creatPartCases to generate multipart chunks.
|
||||
for _, testCase := range createPartCases {
|
||||
_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), opts)
|
||||
_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.inputDataSize, testCase.inputMd5, sha256sum), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%s : %s", instanceType, err.Error())
|
||||
}
|
||||
@@ -1744,7 +1744,7 @@ func testObjectCompleteMultipartUpload(obj ObjectLayer, instanceType string, t T
|
||||
PartID int
|
||||
inputReaderData string
|
||||
inputMd5 string
|
||||
intputDataSize int64
|
||||
inputDataSize int64
|
||||
}{
|
||||
// Case 1-4.
|
||||
// Creating sequence of parts for same uploadID.
|
||||
@@ -1761,7 +1761,7 @@ func testObjectCompleteMultipartUpload(obj ObjectLayer, instanceType string, t T
|
||||
var opts ObjectOptions
|
||||
// Iterating over creatPartCases to generate multipart chunks.
|
||||
for _, part := range parts {
|
||||
_, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID, mustGetPutObjReader(t, bytes.NewBufferString(part.inputReaderData), part.intputDataSize, part.inputMd5, sha256sum), opts)
|
||||
_, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID, mustGetPutObjReader(t, bytes.NewBufferString(part.inputReaderData), part.inputDataSize, part.inputMd5, sha256sum), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%s : %s", instanceType, err)
|
||||
}
|
||||
|
||||
@@ -70,12 +70,12 @@ func testObjectAPIPutObject(obj ObjectLayer, instanceType string, t TestErrHandl
|
||||
invalidMD5Header := md5Header([]byte("meh"))
|
||||
|
||||
testCases := []struct {
|
||||
bucketName string
|
||||
objName string
|
||||
inputData []byte
|
||||
inputMeta map[string]string
|
||||
inputSHA256 string
|
||||
intputDataSize int64
|
||||
bucketName string
|
||||
objName string
|
||||
inputData []byte
|
||||
inputMeta map[string]string
|
||||
inputSHA256 string
|
||||
inputDataSize int64
|
||||
// expected error output.
|
||||
expectedMd5 string
|
||||
expectedError error
|
||||
@@ -106,7 +106,7 @@ func testObjectAPIPutObject(obj ObjectLayer, instanceType string, t TestErrHandl
|
||||
7: {
|
||||
bucketName: bucket, objName: object, inputData: []byte("abcd"),
|
||||
inputMeta: map[string]string{"etag": "e2fc714c4727ee9395f324cd2e7f331f"},
|
||||
inputSHA256: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031580", intputDataSize: int64(len("abcd")),
|
||||
inputSHA256: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031580", inputDataSize: int64(len("abcd")),
|
||||
expectedError: hash.SHA256Mismatch{
|
||||
ExpectedSHA256: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031580",
|
||||
CalculatedSHA256: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589",
|
||||
@@ -116,76 +116,76 @@ func testObjectAPIPutObject(obj ObjectLayer, instanceType string, t TestErrHandl
|
||||
// Input with size more than the size of actual data inside the reader.
|
||||
8: {
|
||||
bucketName: bucket, objName: object, inputData: []byte("abcd"),
|
||||
inputMeta: map[string]string{"etag": "e2fc714c4727ee9395f324cd2e7f331e"}, intputDataSize: int64(len("abcd") + 1),
|
||||
inputMeta: map[string]string{"etag": "e2fc714c4727ee9395f324cd2e7f331e"}, inputDataSize: int64(len("abcd") + 1),
|
||||
expectedError: hash.BadDigest{ExpectedMD5: "e2fc714c4727ee9395f324cd2e7f331e", CalculatedMD5: "e2fc714c4727ee9395f324cd2e7f331f"},
|
||||
},
|
||||
|
||||
// Input with size less than the size of actual data inside the reader.
|
||||
9: {
|
||||
bucketName: bucket, objName: object, inputData: []byte("abcd"),
|
||||
inputMeta: map[string]string{"etag": "900150983cd24fb0d6963f7d28e17f73"}, intputDataSize: int64(len("abcd") - 1),
|
||||
inputMeta: map[string]string{"etag": "900150983cd24fb0d6963f7d28e17f73"}, inputDataSize: int64(len("abcd") - 1),
|
||||
expectedError: ioutil.ErrOverread,
|
||||
},
|
||||
|
||||
// Validating for success cases.
|
||||
10: {bucketName: bucket, objName: object, inputData: []byte("abcd"), inputMeta: map[string]string{"etag": "e2fc714c4727ee9395f324cd2e7f331f"}, intputDataSize: int64(len("abcd"))},
|
||||
11: {bucketName: bucket, objName: object, inputData: []byte("efgh"), inputMeta: map[string]string{"etag": "1f7690ebdd9b4caf8fab49ca1757bf27"}, intputDataSize: int64(len("efgh"))},
|
||||
12: {bucketName: bucket, objName: object, inputData: []byte("ijkl"), inputMeta: map[string]string{"etag": "09a0877d04abf8759f99adec02baf579"}, intputDataSize: int64(len("ijkl"))},
|
||||
13: {bucketName: bucket, objName: object, inputData: []byte("mnop"), inputMeta: map[string]string{"etag": "e132e96a5ddad6da8b07bba6f6131fef"}, intputDataSize: int64(len("mnop"))},
|
||||
10: {bucketName: bucket, objName: object, inputData: []byte("abcd"), inputMeta: map[string]string{"etag": "e2fc714c4727ee9395f324cd2e7f331f"}, inputDataSize: int64(len("abcd"))},
|
||||
11: {bucketName: bucket, objName: object, inputData: []byte("efgh"), inputMeta: map[string]string{"etag": "1f7690ebdd9b4caf8fab49ca1757bf27"}, inputDataSize: int64(len("efgh"))},
|
||||
12: {bucketName: bucket, objName: object, inputData: []byte("ijkl"), inputMeta: map[string]string{"etag": "09a0877d04abf8759f99adec02baf579"}, inputDataSize: int64(len("ijkl"))},
|
||||
13: {bucketName: bucket, objName: object, inputData: []byte("mnop"), inputMeta: map[string]string{"etag": "e132e96a5ddad6da8b07bba6f6131fef"}, inputDataSize: int64(len("mnop"))},
|
||||
|
||||
// With no metadata
|
||||
14: {bucketName: bucket, objName: object, inputData: data, intputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data)},
|
||||
15: {bucketName: bucket, objName: object, inputData: nilBytes, intputDataSize: int64(len(nilBytes)), expectedMd5: getMD5Hash(nilBytes)},
|
||||
16: {bucketName: bucket, objName: object, inputData: fiveMBBytes, intputDataSize: int64(len(fiveMBBytes)), expectedMd5: getMD5Hash(fiveMBBytes)},
|
||||
14: {bucketName: bucket, objName: object, inputData: data, inputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data)},
|
||||
15: {bucketName: bucket, objName: object, inputData: nilBytes, inputDataSize: int64(len(nilBytes)), expectedMd5: getMD5Hash(nilBytes)},
|
||||
16: {bucketName: bucket, objName: object, inputData: fiveMBBytes, inputDataSize: int64(len(fiveMBBytes)), expectedMd5: getMD5Hash(fiveMBBytes)},
|
||||
|
||||
// With arbitrary metadata
|
||||
17: {bucketName: bucket, objName: object, inputData: data, inputMeta: map[string]string{"answer": "42"}, intputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data)},
|
||||
18: {bucketName: bucket, objName: object, inputData: nilBytes, inputMeta: map[string]string{"answer": "42"}, intputDataSize: int64(len(nilBytes)), expectedMd5: getMD5Hash(nilBytes)},
|
||||
19: {bucketName: bucket, objName: object, inputData: fiveMBBytes, inputMeta: map[string]string{"answer": "42"}, intputDataSize: int64(len(fiveMBBytes)), expectedMd5: getMD5Hash(fiveMBBytes)},
|
||||
17: {bucketName: bucket, objName: object, inputData: data, inputMeta: map[string]string{"answer": "42"}, inputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data)},
|
||||
18: {bucketName: bucket, objName: object, inputData: nilBytes, inputMeta: map[string]string{"answer": "42"}, inputDataSize: int64(len(nilBytes)), expectedMd5: getMD5Hash(nilBytes)},
|
||||
19: {bucketName: bucket, objName: object, inputData: fiveMBBytes, inputMeta: map[string]string{"answer": "42"}, inputDataSize: int64(len(fiveMBBytes)), expectedMd5: getMD5Hash(fiveMBBytes)},
|
||||
|
||||
// With valid md5sum and sha256.
|
||||
20: {bucketName: bucket, objName: object, inputData: data, inputMeta: md5Header(data), inputSHA256: getSHA256Hash(data), intputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data)},
|
||||
21: {bucketName: bucket, objName: object, inputData: nilBytes, inputMeta: md5Header(nilBytes), inputSHA256: getSHA256Hash(nilBytes), intputDataSize: int64(len(nilBytes)), expectedMd5: getMD5Hash(nilBytes)},
|
||||
22: {bucketName: bucket, objName: object, inputData: fiveMBBytes, inputMeta: md5Header(fiveMBBytes), inputSHA256: getSHA256Hash(fiveMBBytes), intputDataSize: int64(len(fiveMBBytes)), expectedMd5: getMD5Hash(fiveMBBytes)},
|
||||
20: {bucketName: bucket, objName: object, inputData: data, inputMeta: md5Header(data), inputSHA256: getSHA256Hash(data), inputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data)},
|
||||
21: {bucketName: bucket, objName: object, inputData: nilBytes, inputMeta: md5Header(nilBytes), inputSHA256: getSHA256Hash(nilBytes), inputDataSize: int64(len(nilBytes)), expectedMd5: getMD5Hash(nilBytes)},
|
||||
22: {bucketName: bucket, objName: object, inputData: fiveMBBytes, inputMeta: md5Header(fiveMBBytes), inputSHA256: getSHA256Hash(fiveMBBytes), inputDataSize: int64(len(fiveMBBytes)), expectedMd5: getMD5Hash(fiveMBBytes)},
|
||||
|
||||
// data with invalid md5sum in header
|
||||
23: {
|
||||
bucketName: bucket, objName: object, inputData: data, inputMeta: invalidMD5Header, intputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data),
|
||||
bucketName: bucket, objName: object, inputData: data, inputMeta: invalidMD5Header, inputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data),
|
||||
expectedError: hash.BadDigest{ExpectedMD5: invalidMD5, CalculatedMD5: getMD5Hash(data)},
|
||||
},
|
||||
24: {
|
||||
bucketName: bucket, objName: object, inputData: nilBytes, inputMeta: invalidMD5Header, intputDataSize: int64(len(nilBytes)), expectedMd5: getMD5Hash(nilBytes),
|
||||
bucketName: bucket, objName: object, inputData: nilBytes, inputMeta: invalidMD5Header, inputDataSize: int64(len(nilBytes)), expectedMd5: getMD5Hash(nilBytes),
|
||||
expectedError: hash.BadDigest{ExpectedMD5: invalidMD5, CalculatedMD5: getMD5Hash(nilBytes)},
|
||||
},
|
||||
25: {
|
||||
bucketName: bucket, objName: object, inputData: fiveMBBytes, inputMeta: invalidMD5Header, intputDataSize: int64(len(fiveMBBytes)), expectedMd5: getMD5Hash(fiveMBBytes),
|
||||
bucketName: bucket, objName: object, inputData: fiveMBBytes, inputMeta: invalidMD5Header, inputDataSize: int64(len(fiveMBBytes)), expectedMd5: getMD5Hash(fiveMBBytes),
|
||||
expectedError: hash.BadDigest{ExpectedMD5: invalidMD5, CalculatedMD5: getMD5Hash(fiveMBBytes)},
|
||||
},
|
||||
|
||||
// data with size different from the actual number of bytes available in the reader
|
||||
26: {bucketName: bucket, objName: object, inputData: data, intputDataSize: int64(len(data) - 1), expectedMd5: getMD5Hash(data[:len(data)-1]), expectedError: ioutil.ErrOverread},
|
||||
27: {bucketName: bucket, objName: object, inputData: nilBytes, intputDataSize: int64(len(nilBytes) + 1), expectedMd5: getMD5Hash(nilBytes), expectedError: IncompleteBody{Bucket: bucket, Object: object}},
|
||||
26: {bucketName: bucket, objName: object, inputData: data, inputDataSize: int64(len(data) - 1), expectedMd5: getMD5Hash(data[:len(data)-1]), expectedError: ioutil.ErrOverread},
|
||||
27: {bucketName: bucket, objName: object, inputData: nilBytes, inputDataSize: int64(len(nilBytes) + 1), expectedMd5: getMD5Hash(nilBytes), expectedError: IncompleteBody{Bucket: bucket, Object: object}},
|
||||
28: {bucketName: bucket, objName: object, inputData: fiveMBBytes, expectedMd5: getMD5Hash(fiveMBBytes), expectedError: ioutil.ErrOverread},
|
||||
|
||||
// valid data with X-Amz-Meta- meta
|
||||
29: {bucketName: bucket, objName: object, inputData: data, inputMeta: map[string]string{"X-Amz-Meta-AppID": "a42"}, intputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data)},
|
||||
29: {bucketName: bucket, objName: object, inputData: data, inputMeta: map[string]string{"X-Amz-Meta-AppID": "a42"}, inputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data)},
|
||||
|
||||
// Put an empty object with a trailing slash
|
||||
30: {bucketName: bucket, objName: "emptydir/", inputData: []byte{}, expectedMd5: getMD5Hash([]byte{})},
|
||||
// Put an object inside the empty directory
|
||||
31: {bucketName: bucket, objName: "emptydir/" + object, inputData: data, intputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data)},
|
||||
31: {bucketName: bucket, objName: "emptydir/" + object, inputData: data, inputDataSize: int64(len(data)), expectedMd5: getMD5Hash(data)},
|
||||
// Put the empty object with a trailing slash again (refer to Test case 30), this needs to succeed
|
||||
32: {bucketName: bucket, objName: "emptydir/", inputData: []byte{}, expectedMd5: getMD5Hash([]byte{})},
|
||||
|
||||
// With invalid crc32.
|
||||
33: {
|
||||
bucketName: bucket, objName: object, inputData: []byte("abcd"),
|
||||
inputMeta: map[string]string{"etag": "e2fc714c4727ee9395f324cd2e7f331f", "x-amz-checksum-crc32": "abcd"},
|
||||
intputDataSize: int64(len("abcd")),
|
||||
inputMeta: map[string]string{"etag": "e2fc714c4727ee9395f324cd2e7f331f", "x-amz-checksum-crc32": "abcd"},
|
||||
inputDataSize: int64(len("abcd")),
|
||||
},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
in := mustGetPutObjReader(t, bytes.NewReader(testCase.inputData), testCase.intputDataSize, testCase.inputMeta["etag"], testCase.inputSHA256)
|
||||
in := mustGetPutObjReader(t, bytes.NewReader(testCase.inputData), testCase.inputDataSize, testCase.inputMeta["etag"], testCase.inputSHA256)
|
||||
objInfo, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, in, ObjectOptions{UserDefined: testCase.inputMeta})
|
||||
if actualErr != nil && testCase.expectedError == nil {
|
||||
t.Errorf("Test %d: %s: Expected to pass, but failed with: error %s.", i, instanceType, actualErr.Error())
|
||||
@@ -243,11 +243,11 @@ func testObjectAPIPutObjectDiskNotFound(obj ObjectLayer, instanceType string, di
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
bucketName string
|
||||
objName string
|
||||
inputData []byte
|
||||
inputMeta map[string]string
|
||||
intputDataSize int64
|
||||
bucketName string
|
||||
objName string
|
||||
inputData []byte
|
||||
inputMeta map[string]string
|
||||
inputDataSize int64
|
||||
// flag indicating whether the test should pass.
|
||||
shouldPass bool
|
||||
// expected error output.
|
||||
@@ -263,7 +263,7 @@ func testObjectAPIPutObjectDiskNotFound(obj ObjectLayer, instanceType string, di
|
||||
|
||||
sha256sum := ""
|
||||
for i, testCase := range testCases {
|
||||
objInfo, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, mustGetPutObjReader(t, bytes.NewReader(testCase.inputData), testCase.intputDataSize, testCase.inputMeta["etag"], sha256sum), ObjectOptions{UserDefined: testCase.inputMeta})
|
||||
objInfo, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, mustGetPutObjReader(t, bytes.NewReader(testCase.inputData), testCase.inputDataSize, testCase.inputMeta["etag"], sha256sum), ObjectOptions{UserDefined: testCase.inputMeta})
|
||||
if actualErr != nil && testCase.shouldPass {
|
||||
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s.", i+1, instanceType, actualErr.Error())
|
||||
}
|
||||
@@ -291,11 +291,11 @@ func testObjectAPIPutObjectDiskNotFound(obj ObjectLayer, instanceType string, di
|
||||
|
||||
// Validate the last test.
|
||||
testCase := struct {
|
||||
bucketName string
|
||||
objName string
|
||||
inputData []byte
|
||||
inputMeta map[string]string
|
||||
intputDataSize int64
|
||||
bucketName string
|
||||
objName string
|
||||
inputData []byte
|
||||
inputMeta map[string]string
|
||||
inputDataSize int64
|
||||
// flag indicating whether the test should pass.
|
||||
shouldPass bool
|
||||
// expected error output.
|
||||
@@ -312,7 +312,7 @@ func testObjectAPIPutObjectDiskNotFound(obj ObjectLayer, instanceType string, di
|
||||
errErasureWriteQuorum,
|
||||
}
|
||||
|
||||
_, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, mustGetPutObjReader(t, bytes.NewReader(testCase.inputData), testCase.intputDataSize, testCase.inputMeta["etag"], sha256sum), ObjectOptions{UserDefined: testCase.inputMeta})
|
||||
_, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, mustGetPutObjReader(t, bytes.NewReader(testCase.inputData), testCase.inputDataSize, testCase.inputMeta["etag"], sha256sum), ObjectOptions{UserDefined: testCase.inputMeta})
|
||||
if actualErr != nil && testCase.shouldPass {
|
||||
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s.", len(testCases)+1, instanceType, actualErr.Error())
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ func TestIsValidBucketName(t *testing.T) {
|
||||
{"192.168.1.1", false},
|
||||
{"$this-is-not-valid-too", false},
|
||||
{"contains-$-dollar", false},
|
||||
{"contains-^-carret", false},
|
||||
{"contains-^-caret", false},
|
||||
{"contains-$-dollar", false},
|
||||
{"contains-$-dollar", false},
|
||||
{"......", false},
|
||||
@@ -195,7 +195,7 @@ func TestIsValidObjectName(t *testing.T) {
|
||||
{"117Gn8rfHL2ACARPAhaFd0AGzic9pUbIA/5OCn5A", true},
|
||||
{"SHØRT", true},
|
||||
{"f*le", true},
|
||||
{"contains-^-carret", true},
|
||||
{"contains-^-caret", true},
|
||||
{"contains-|-pipe", true},
|
||||
{"contains-`-tick", true},
|
||||
{"..test", true},
|
||||
|
||||
@@ -2891,6 +2891,18 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
|
||||
return
|
||||
}
|
||||
if isErrObjectNotFound(err) || isErrVersionNotFound(err) {
|
||||
// Send an event when the object is not found
|
||||
objInfo.Name = object
|
||||
objInfo.VersionID = opts.VersionID
|
||||
sendEvent(eventArgs{
|
||||
EventName: event.ObjectRemovedNoOP,
|
||||
BucketName: bucket,
|
||||
Object: objInfo,
|
||||
ReqParams: extractReqParams(r),
|
||||
RespElements: extractRespElements(w),
|
||||
UserAgent: r.UserAgent(),
|
||||
Host: handlers.GetSourceIP(r),
|
||||
})
|
||||
writeSuccessNoContent(w)
|
||||
return
|
||||
}
|
||||
|
||||
+52
-43
@@ -1302,7 +1302,7 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a
|
||||
|
||||
copySourceHeader := map[string]string{"X-Amz-Copy-Source": "somewhere"}
|
||||
invalidMD5Header := map[string]string{"Content-Md5": "42"}
|
||||
inalidStorageClassHeader := map[string]string{xhttp.AmzStorageClass: "INVALID"}
|
||||
invalidStorageClassHeader := map[string]string{xhttp.AmzStorageClass: "INVALID"}
|
||||
|
||||
addCustomHeaders := func(req *http.Request, customHeaders map[string]string) {
|
||||
for k, value := range customHeaders {
|
||||
@@ -1350,7 +1350,7 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a
|
||||
objectName: objectName,
|
||||
data: bytesData,
|
||||
dataLen: len(bytesData),
|
||||
accessKey: "Wrong-AcessID",
|
||||
accessKey: "Wrong-AccessID",
|
||||
secretKey: credentials.SecretKey,
|
||||
|
||||
expectedRespStatus: http.StatusForbidden,
|
||||
@@ -1408,7 +1408,7 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a
|
||||
6: {
|
||||
bucketName: bucketName,
|
||||
objectName: objectName,
|
||||
headers: inalidStorageClassHeader,
|
||||
headers: invalidStorageClassHeader,
|
||||
data: bytesData,
|
||||
dataLen: len(bytesData),
|
||||
accessKey: credentials.AccessKey,
|
||||
@@ -2225,10 +2225,19 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
|
||||
expectedRespStatus: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// Test case - 3.
|
||||
// Test case with new object name is same as object to be copied.
|
||||
// Test case with invalid source object.
|
||||
3: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: "dir//newObject1",
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
accessKey: credentials.AccessKey,
|
||||
secretKey: credentials.SecretKey,
|
||||
expectedRespStatus: http.StatusBadRequest,
|
||||
},
|
||||
// Test case - 4.
|
||||
// Test case with new object name is same as object to be copied.
|
||||
4: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: objectName,
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2239,10 +2248,10 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
expectedRespStatus: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// Test case - 4.
|
||||
// Test case - 5.
|
||||
// Test case with new object name is same as object to be copied.
|
||||
// But source copy is without leading slash
|
||||
4: {
|
||||
5: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: objectName,
|
||||
copySourceSame: true,
|
||||
@@ -2253,10 +2262,10 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
expectedRespStatus: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// Test case - 5.
|
||||
// Test case - 6.
|
||||
// Test case with new object name is same as object to be copied
|
||||
// but metadata is updated.
|
||||
5: {
|
||||
6: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: objectName,
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2270,9 +2279,9 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
expectedRespStatus: http.StatusOK,
|
||||
},
|
||||
|
||||
// Test case - 6.
|
||||
// Test case - 7.
|
||||
// Test case with invalid metadata-directive.
|
||||
6: {
|
||||
7: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: "newObject1",
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2286,10 +2295,10 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
expectedRespStatus: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// Test case - 7.
|
||||
// Test case - 8.
|
||||
// Test case with new object name is same as object to be copied
|
||||
// fail with BadRequest.
|
||||
7: {
|
||||
8: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: objectName,
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2304,11 +2313,11 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
expectedRespStatus: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
// Test case - 8.
|
||||
// Test case - 9.
|
||||
// Test case with non-existent source file.
|
||||
// Case for the purpose of failing `api.ObjectAPI.GetObjectInfo`.
|
||||
// Expecting the response status code to http.StatusNotFound (404).
|
||||
8: {
|
||||
9: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: objectName,
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + "non-existent-object"),
|
||||
@@ -2318,11 +2327,11 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
expectedRespStatus: http.StatusNotFound,
|
||||
},
|
||||
|
||||
// Test case - 9.
|
||||
// Test case - 10.
|
||||
// Test case with non-existent source file.
|
||||
// Case for the purpose of failing `api.ObjectAPI.PutObject`.
|
||||
// Expecting the response status code to http.StatusNotFound (404).
|
||||
9: {
|
||||
10: {
|
||||
bucketName: "non-existent-destination-bucket",
|
||||
newObjectName: objectName,
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2332,9 +2341,9 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
expectedRespStatus: http.StatusNotFound,
|
||||
},
|
||||
|
||||
// Test case - 10.
|
||||
// Test case - 11.
|
||||
// Case with invalid AccessKey.
|
||||
10: {
|
||||
11: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: objectName,
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2343,8 +2352,8 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
|
||||
expectedRespStatus: http.StatusForbidden,
|
||||
},
|
||||
// Test case - 11, copy metadata from newObject1 with satisfying modified header.
|
||||
11: {
|
||||
// Test case - 12, copy metadata from newObject1 with satisfying modified header.
|
||||
12: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: "newObject1",
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2353,8 +2362,8 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
secretKey: credentials.SecretKey,
|
||||
expectedRespStatus: http.StatusOK,
|
||||
},
|
||||
// Test case - 12, copy metadata from newObject1 with unsatisfying modified header.
|
||||
12: {
|
||||
// Test case - 13, copy metadata from newObject1 with unsatisfying modified header.
|
||||
13: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: "newObject1",
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2363,8 +2372,8 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
secretKey: credentials.SecretKey,
|
||||
expectedRespStatus: http.StatusPreconditionFailed,
|
||||
},
|
||||
// Test case - 13, copy metadata from newObject1 with wrong modified header format
|
||||
13: {
|
||||
// Test case - 14, copy metadata from newObject1 with wrong modified header format
|
||||
14: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: "newObject1",
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2373,8 +2382,8 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
secretKey: credentials.SecretKey,
|
||||
expectedRespStatus: http.StatusOK,
|
||||
},
|
||||
// Test case - 14, copy metadata from newObject1 with satisfying unmodified header.
|
||||
14: {
|
||||
// Test case - 15, copy metadata from newObject1 with satisfying unmodified header.
|
||||
15: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: "newObject1",
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2383,8 +2392,8 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
secretKey: credentials.SecretKey,
|
||||
expectedRespStatus: http.StatusOK,
|
||||
},
|
||||
// Test case - 15, copy metadata from newObject1 with unsatisfying unmodified header.
|
||||
15: {
|
||||
// Test case - 16, copy metadata from newObject1 with unsatisfying unmodified header.
|
||||
16: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: "newObject1",
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2393,8 +2402,8 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
secretKey: credentials.SecretKey,
|
||||
expectedRespStatus: http.StatusPreconditionFailed,
|
||||
},
|
||||
// Test case - 16, copy metadata from newObject1 with incorrect unmodified header format.
|
||||
16: {
|
||||
// Test case - 17, copy metadata from newObject1 with incorrect unmodified header format.
|
||||
17: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: "newObject1",
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator + bucketName + SlashSeparator + objectName),
|
||||
@@ -2403,8 +2412,8 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
secretKey: credentials.SecretKey,
|
||||
expectedRespStatus: http.StatusOK,
|
||||
},
|
||||
// Test case - 17, copy metadata from newObject1 with null versionId
|
||||
17: {
|
||||
// Test case - 18, copy metadata from newObject1 with null versionId
|
||||
18: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: "newObject1",
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator+bucketName+SlashSeparator+objectName) + "?versionId=null",
|
||||
@@ -2412,8 +2421,8 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
secretKey: credentials.SecretKey,
|
||||
expectedRespStatus: http.StatusOK,
|
||||
},
|
||||
// Test case - 18, copy metadata from newObject1 with non null versionId
|
||||
18: {
|
||||
// Test case - 19, copy metadata from newObject1 with non null versionId
|
||||
19: {
|
||||
bucketName: bucketName,
|
||||
newObjectName: "newObject1",
|
||||
copySourceHeader: url.QueryEscape(SlashSeparator+bucketName+SlashSeparator+objectName) + "?versionId=17",
|
||||
@@ -2574,7 +2583,7 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
||||
}
|
||||
|
||||
// Wrapper for calling NewMultipartUpload tests for both Erasure multiple disks and single node setup.
|
||||
// First register the HTTP handler for NewMutlipartUpload, then a HTTP request for NewMultipart upload is made.
|
||||
// First register the HTTP handler for NewMultipartUpload, then a HTTP request for NewMultipart upload is made.
|
||||
// The UploadID from the response body is parsed and its existence is asserted with an attempt to ListParts using it.
|
||||
func TestAPINewMultipartHandler(t *testing.T) {
|
||||
defer DetectTestLeak(t)()
|
||||
@@ -2614,7 +2623,7 @@ func testAPINewMultipartHandler(obj ObjectLayer, instanceType, bucketName string
|
||||
t.Fatalf("Invalid UploadID: <ERROR> %s", err)
|
||||
}
|
||||
|
||||
// Testing the response for Invalid AcccessID.
|
||||
// Testing the response for Invalid AccessID.
|
||||
// Forcing the signature check to fail.
|
||||
rec = httptest.NewRecorder()
|
||||
// construct HTTP request for NewMultipart upload.
|
||||
@@ -2664,7 +2673,7 @@ func testAPINewMultipartHandler(obj ObjectLayer, instanceType, bucketName string
|
||||
t.Fatalf("Invalid UploadID: <ERROR> %s", err)
|
||||
}
|
||||
|
||||
// Testing the response for invalid AcccessID.
|
||||
// Testing the response for invalid AccessID.
|
||||
// Forcing the V2 signature check to fail.
|
||||
recV2 = httptest.NewRecorder()
|
||||
// construct HTTP request for NewMultipartUpload endpoint.
|
||||
@@ -2819,7 +2828,7 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s
|
||||
PartID int
|
||||
inputReaderData string
|
||||
inputMd5 string
|
||||
intputDataSize int64
|
||||
inputDataSize int64
|
||||
}{
|
||||
// Case 1-4.
|
||||
// Creating sequence of parts for same uploadID.
|
||||
@@ -2839,7 +2848,7 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s
|
||||
// Iterating over creatPartCases to generate multipart chunks.
|
||||
for _, part := range parts {
|
||||
_, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID,
|
||||
mustGetPutObjReader(t, strings.NewReader(part.inputReaderData), part.intputDataSize, part.inputMd5, ""), opts)
|
||||
mustGetPutObjReader(t, strings.NewReader(part.inputReaderData), part.inputDataSize, part.inputMd5, ""), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%s : %s", instanceType, err)
|
||||
}
|
||||
@@ -3187,7 +3196,7 @@ func testAPIAbortMultipartHandler(obj ObjectLayer, instanceType, bucketName stri
|
||||
PartID int
|
||||
inputReaderData string
|
||||
inputMd5 string
|
||||
intputDataSize int64
|
||||
inputDataSize int64
|
||||
}{
|
||||
// Case 1-4.
|
||||
// Creating sequence of parts for same uploadID.
|
||||
@@ -3207,7 +3216,7 @@ func testAPIAbortMultipartHandler(obj ObjectLayer, instanceType, bucketName stri
|
||||
// Iterating over createPartCases to generate multipart chunks.
|
||||
for _, part := range parts {
|
||||
_, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID,
|
||||
mustGetPutObjReader(t, strings.NewReader(part.inputReaderData), part.intputDataSize, part.inputMd5, ""), opts)
|
||||
mustGetPutObjReader(t, strings.NewReader(part.inputReaderData), part.inputDataSize, part.inputMd5, ""), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%s : %s", instanceType, err)
|
||||
}
|
||||
@@ -3233,7 +3242,7 @@ func testAPIAbortMultipartHandler(obj ObjectLayer, instanceType, bucketName stri
|
||||
expectedRespStatus: http.StatusNoContent,
|
||||
},
|
||||
// Test case - 2.
|
||||
// Abort non-existng upload ID.
|
||||
// Abort non-existing upload ID.
|
||||
{
|
||||
bucket: bucketName,
|
||||
object: objectName,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user