diff options
| author | 2025-11-05 21:58:38 +0100 | |
|---|---|---|
| committer | 2025-11-17 14:12:41 +0100 | |
| commit | d0c551acb5ab79efafd325f5b19c76a3de835356 (patch) | |
| tree | ba9462cdc5081015fbd6bf5d98d9f39334d13207 /vendor/github.com/minio | |
| parent | [performance] when transforming media, perform read operations of large files... (diff) | |
| download | gotosocial-d0c551acb5ab79efafd325f5b19c76a3de835356.tar.xz | |
[chore] update dependencies (#4542)
- github.com/minio/minio-go/v7: v7.0.95 -> v7.0.97
- github.com/ncruces/go-sqlite3: v0.29.1 -> v0.30.0
- github.com/tdewolff/minify/v2: v2.24.5 -> v2.24.6
- codeberg.org/gruf/go-mmap: fixes build for BSD platforms
Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4542
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
Diffstat (limited to 'vendor/github.com/minio')
51 files changed, 1929 insertions, 622 deletions
diff --git a/vendor/github.com/minio/crc64nvme/crc64.go b/vendor/github.com/minio/crc64nvme/crc64.go index 40ac28c76..ca34a48e0 100644 --- a/vendor/github.com/minio/crc64nvme/crc64.go +++ b/vendor/github.com/minio/crc64nvme/crc64.go @@ -125,14 +125,19 @@ func update(crc uint64, p []byte) uint64 { p = p[align:] } runs := len(p) / 128 - crc = updateAsm(crc, p[:128*runs]) + if hasAsm512 && runs >= 8 { + // Use 512-bit wide instructions for >= 1KB. + crc = updateAsm512(crc, p[:128*runs]) + } else { + crc = updateAsm(crc, p[:128*runs]) + } return update(crc, p[128*runs:]) } buildSlicing8TablesOnce() crc = ^crc // table comparison is somewhat expensive, so avoid it for small sizes - for len(p) >= 64 { + if len(p) >= 64 { var helperTable = slicing8TableNVME // Update using slicing-by-8 for len(p) > 8 { diff --git a/vendor/github.com/minio/crc64nvme/crc64_amd64.go b/vendor/github.com/minio/crc64nvme/crc64_amd64.go index fc8538bc3..c741591a6 100644 --- a/vendor/github.com/minio/crc64nvme/crc64_amd64.go +++ b/vendor/github.com/minio/crc64nvme/crc64_amd64.go @@ -11,5 +11,7 @@ import ( ) var hasAsm = cpuid.CPU.Supports(cpuid.SSE2, cpuid.CLMUL, cpuid.SSE4) +var hasAsm512 = cpuid.CPU.Supports(cpuid.AVX512F, cpuid.VPCLMULQDQ, cpuid.AVX512VL, cpuid.CLMUL) func updateAsm(crc uint64, p []byte) (checksum uint64) +func updateAsm512(crc uint64, p []byte) (checksum uint64) diff --git a/vendor/github.com/minio/crc64nvme/crc64_amd64.s b/vendor/github.com/minio/crc64nvme/crc64_amd64.s index 9782321fd..acfea6a15 100644 --- a/vendor/github.com/minio/crc64nvme/crc64_amd64.s +++ b/vendor/github.com/minio/crc64nvme/crc64_amd64.s @@ -155,3 +155,153 @@ skip128: NOTQ AX MOVQ AX, checksum+32(FP) RET + +// Constants, pre-splatted. +DATA ·asmConstantsPoly<>+0x00(SB)/8, $0xa1ca681e733f9c40 +DATA ·asmConstantsPoly<>+0x08(SB)/8, $0 +DATA ·asmConstantsPoly<>+0x10(SB)/8, $0xa1ca681e733f9c40 +DATA ·asmConstantsPoly<>+0x18(SB)/8, $0 +DATA ·asmConstantsPoly<>+0x20(SB)/8, $0xa1ca681e733f9c40 +DATA ·asmConstantsPoly<>+0x28(SB)/8, $0 +DATA ·asmConstantsPoly<>+0x30(SB)/8, $0xa1ca681e733f9c40 +DATA ·asmConstantsPoly<>+0x38(SB)/8, $0 +// Upper +DATA ·asmConstantsPoly<>+0x40(SB)/8, $0 +DATA ·asmConstantsPoly<>+0x48(SB)/8, $0x5f852fb61e8d92dc +DATA ·asmConstantsPoly<>+0x50(SB)/8, $0 +DATA ·asmConstantsPoly<>+0x58(SB)/8, $0x5f852fb61e8d92dc +DATA ·asmConstantsPoly<>+0x60(SB)/8, $0 +DATA ·asmConstantsPoly<>+0x68(SB)/8, $0x5f852fb61e8d92dc +DATA ·asmConstantsPoly<>+0x70(SB)/8, $0 +DATA ·asmConstantsPoly<>+0x78(SB)/8, $0x5f852fb61e8d92dc +GLOBL ·asmConstantsPoly<>(SB), (NOPTR+RODATA), $128 + +TEXT ·updateAsm512(SB), $0-40 + MOVQ crc+0(FP), AX // checksum + MOVQ p_base+8(FP), SI // start pointer + MOVQ p_len+16(FP), CX // length of buffer + NOTQ AX + SHRQ $7, CX + CMPQ CX, $1 + VPXORQ Z8, Z8, Z8 // Initialize ZMM8 to zero + JLT skip128 + + VMOVDQU64 0x00(SI), Z0 + VMOVDQU64 0x40(SI), Z4 + MOVQ $·asmConstantsPoly<>(SB), BX + VMOVQ AX, X8 + + // XOR initialization value into lower 64 bits of ZMM0 + VPXORQ Z8, Z0, Z0 + CMPQ CX, $1 + JE tail128 + + VMOVDQU64 0(BX), Z8 + VMOVDQU64 64(BX), Z9 + + PCALIGN $16 + +loop128: + VMOVDQU64 0x80(SI), Z1 + VMOVDQU64 0xc0(SI), Z5 + ADDQ $128, SI + + SUBQ $1, CX + VPCLMULQDQ $0x00, Z8, Z0, Z10 + VPCLMULQDQ $0x11, Z9, Z0, Z0 + VPTERNLOGD $0x96, Z1, Z10, Z0 // Combine results with xor into Z0 + + VPCLMULQDQ $0x00, Z8, Z4, Z10 + VPCLMULQDQ $0x11, Z9, Z4, Z4 + VPTERNLOGD $0x96, Z5, Z10, Z4 // Combine results with xor into Z4 + + CMPQ CX, $1 + JGT loop128 + +tail128: + // Extract X0 to X3 from ZMM0 + VEXTRACTF32X4 $1, Z0, X1 // X1: Second 128-bit lane + VEXTRACTF32X4 $2, Z0, X2 // X2: Third 128-bit lane + VEXTRACTF32X4 $3, Z0, X3 // X3: Fourth 128-bit lane + + // Extract X4 to X7 from ZMM4 + VEXTRACTF32X4 $1, Z4, X5 // X5: Second 128-bit lane + VEXTRACTF32X4 $2, Z4, X6 // X6: Third 128-bit lane + VEXTRACTF32X4 $3, Z4, X7 // X7: Fourth 128-bit lane + + MOVQ $0xd083dd594d96319d, AX + MOVQ AX, X11 + PCLMULQDQ $0x00, X0, X11 + MOVQ $0x946588403d4adcbc, AX + PINSRQ $0x1, AX, X12 + PCLMULQDQ $0x11, X12, X0 + PXOR X11, X7 + PXOR X0, X7 + MOVQ $0x3c255f5ebc414423, AX + MOVQ AX, X11 + PCLMULQDQ $0x00, X1, X11 + MOVQ $0x34f5a24e22d66e90, AX + PINSRQ $0x1, AX, X12 + PCLMULQDQ $0x11, X12, X1 + PXOR X11, X1 + PXOR X7, X1 + MOVQ $0x7b0ab10dd0f809fe, AX + MOVQ AX, X11 + PCLMULQDQ $0x00, X2, X11 + MOVQ $0x03363823e6e791e5, AX + PINSRQ $0x1, AX, X12 + PCLMULQDQ $0x11, X12, X2 + PXOR X11, X2 + PXOR X1, X2 + MOVQ $0x0c32cdb31e18a84a, AX + MOVQ AX, X11 + PCLMULQDQ $0x00, X3, X11 + MOVQ $0x62242240ace5045a, AX + PINSRQ $0x1, AX, X12 + PCLMULQDQ $0x11, X12, X3 + PXOR X11, X3 + PXOR X2, X3 + MOVQ $0xbdd7ac0ee1a4a0f0, AX + MOVQ AX, X11 + PCLMULQDQ $0x00, X4, X11 + MOVQ $0xa3ffdc1fe8e82a8b, AX + PINSRQ $0x1, AX, X12 + PCLMULQDQ $0x11, X12, X4 + PXOR X11, X4 + PXOR X3, X4 + MOVQ $0xb0bc2e589204f500, AX + MOVQ AX, X11 + PCLMULQDQ $0x00, X5, X11 + MOVQ $0xe1e0bb9d45d7a44c, AX + PINSRQ $0x1, AX, X12 + PCLMULQDQ $0x11, X12, X5 + PXOR X11, X5 + PXOR X4, X5 + MOVQ $0xeadc41fd2ba3d420, AX + MOVQ AX, X11 + PCLMULQDQ $0x00, X6, X11 + MOVQ $0x21e9761e252621ac, AX + PINSRQ $0x1, AX, X12 + PCLMULQDQ $0x11, X12, X6 + PXOR X11, X6 + PXOR X5, X6 + MOVQ AX, X5 + PCLMULQDQ $0x00, X6, X5 + PSHUFD $0xee, X6, X6 + PXOR X5, X6 + MOVQ $0x27ecfa329aef9f77, AX + MOVQ AX, X4 + PCLMULQDQ $0x00, X4, X6 + PEXTRQ $0, X6, BX + MOVQ $0x34d926535897936b, AX + MOVQ AX, X4 + PCLMULQDQ $0x00, X4, X6 + PXOR X5, X6 + PEXTRQ $1, X6, AX + XORQ BX, AX + +skip128: + NOTQ AX + MOVQ AX, checksum+32(FP) + VZEROUPPER + RET diff --git a/vendor/github.com/minio/crc64nvme/crc64_arm64.go b/vendor/github.com/minio/crc64nvme/crc64_arm64.go index 141ecf54d..7e3ea9134 100644 --- a/vendor/github.com/minio/crc64nvme/crc64_arm64.go +++ b/vendor/github.com/minio/crc64nvme/crc64_arm64.go @@ -11,5 +11,7 @@ import ( ) var hasAsm = cpuid.CPU.Supports(cpuid.ASIMD, cpuid.PMULL, cpuid.SHA3) +var hasAsm512 = false func updateAsm(crc uint64, p []byte) (checksum uint64) +func updateAsm512(crc uint64, p []byte) (checksum uint64) { panic("should not be reached") } diff --git a/vendor/github.com/minio/crc64nvme/crc64_other.go b/vendor/github.com/minio/crc64nvme/crc64_other.go index 467958c69..ae260f7fb 100644 --- a/vendor/github.com/minio/crc64nvme/crc64_other.go +++ b/vendor/github.com/minio/crc64nvme/crc64_other.go @@ -7,5 +7,7 @@ package crc64nvme var hasAsm = false +var hasAsm512 = false -func updateAsm(crc uint64, p []byte) (checksum uint64) { panic("should not be reached") } +func updateAsm(crc uint64, p []byte) (checksum uint64) { panic("should not be reached") } +func updateAsm512(crc uint64, p []byte) (checksum uint64) { panic("should not be reached") } diff --git a/vendor/github.com/minio/minio-go/v7/CLAUDE.md b/vendor/github.com/minio/minio-go/v7/CLAUDE.md new file mode 100644 index 000000000..26ff95323 --- /dev/null +++ b/vendor/github.com/minio/minio-go/v7/CLAUDE.md @@ -0,0 +1,125 @@ +CLAUDE.md +========= + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +Commands +-------- + +### Testing + +```bash +# Run all tests with race detection (requires MinIO server at localhost:9000) +SERVER_ENDPOINT=localhost:9000 ACCESS_KEY=minioadmin SECRET_KEY=minioadmin ENABLE_HTTPS=1 MINT_MODE=full go test -race -v ./... + +# Run tests without race detection +go test ./... + +# Run short tests only (no functional tests) +go test -short -race ./... + +# Run functional tests +go build -race functional_tests.go +SERVER_ENDPOINT=localhost:9000 ACCESS_KEY=minioadmin SECRET_KEY=minioadmin ENABLE_HTTPS=1 MINT_MODE=full ./functional_tests + +# Run functional tests without TLS +SERVER_ENDPOINT=localhost:9000 ACCESS_KEY=minioadmin SECRET_KEY=minioadmin ENABLE_HTTPS=0 MINT_MODE=full ./functional_tests +``` + +### Linting and Code Quality + +```bash +# Run all checks (lint, vet, test, examples, functional tests) +make checks + +# Run linter only +make lint + +# Run vet and staticcheck +make vet + +# Alternative: run golangci-lint directly +golangci-lint run --timeout=5m --config ./.golangci.yml +``` + +### Building Examples + +```bash +# Build all examples +make examples + +# Build a specific example +cd examples/s3 && go build -mod=mod putobject.go +``` + +Architecture +------------ + +### Core Client Structure + +The MinIO Go SDK is organized around a central `Client` struct (api.go:52) that implements Amazon S3 compatible methods. Key architectural patterns: + +1. **Modular API Organization**: API methods are split into logical files: + + - `api-bucket-*.go`: Bucket operations (lifecycle, encryption, versioning, etc.) + - `api-object-*.go`: Object operations (legal hold, retention, tagging, etc.) + - `api-get-*.go`, `api-put-*.go`: GET and PUT operations + - `api-list.go`: Listing operations + - `api-stat.go`: Status/info operations + +2. **Credential Management**: The `pkg/credentials/` package provides various credential providers: + + - Static credentials + - Environment variables (AWS/MinIO) + - IAM roles + - STS (Security Token Service) variants + - File-based credentials + - Chain provider for fallback mechanisms + +3. **Request Signing**: The `pkg/signer/` package handles AWS signature versions: + + - V2 signatures (legacy) + - V4 signatures (standard) + - Streaming signatures for large uploads + +4. **Transport Layer**: Custom HTTP transport with: + + - Retry logic with configurable max retries + - Health status monitoring + - Tracing support via httptrace + - Bucket location caching (`bucketLocCache`\) + - Session caching for credentials + +5. **Helper Packages**: + + - `pkg/encrypt/`: Server-side encryption utilities + - `pkg/notification/`: Event notification handling + - `pkg/policy/`: Bucket policy management + - `pkg/lifecycle/`: Object lifecycle rules + - `pkg/tags/`: Object and bucket tagging + - `pkg/s3utils/`: S3 utility functions + - `pkg/kvcache/`: Key-value caching + - `pkg/singleflight/`: Deduplication of concurrent requests + +### Testing Strategy + +- Unit tests alongside implementation files (`*_test.go`\) +- Comprehensive functional tests in `functional_tests.go` requiring a live MinIO server +- Example programs in `examples/` directory demonstrating API usage +- Build tag `//go:build mint` for integration tests + +### Error Handling + +- Custom error types in `api-error-response.go` +- HTTP status code mapping +- Retry logic for transient failures +- Detailed error context preservation + +Important Patterns +------------------ + +1. **Context Usage**: All API methods accept `context.Context` for cancellation and timeout control +2. **Options Pattern**: Methods use Options structs for optional parameters (e.g., `PutObjectOptions`, `GetObjectOptions`\) +3. **Streaming Support**: Large file operations use io.Reader/Writer interfaces for memory efficiency +4. **Bucket Lookup Types**: Supports both path-style and virtual-host-style S3 URLs +5. **MD5/SHA256 Hashing**: Configurable hash functions for integrity checks via `md5Hasher` and `sha256Hasher` diff --git a/vendor/github.com/minio/minio-go/v7/CONTRIBUTING.md b/vendor/github.com/minio/minio-go/v7/CONTRIBUTING.md index 24522ef75..e976dd6be 100644 --- a/vendor/github.com/minio/minio-go/v7/CONTRIBUTING.md +++ b/vendor/github.com/minio/minio-go/v7/CONTRIBUTING.md @@ -1,22 +1,23 @@ -### Developer Guidelines +### Developer Guidelines -``minio-go`` welcomes your contribution. To make the process as seamless as possible, we ask for the following: +`minio-go` welcomes your contribution. To make the process as seamless as possible, we ask for the following: -* Go ahead and fork the project and make your changes. We encourage pull requests to discuss code changes. - - Fork it - - Create your feature branch (git checkout -b my-new-feature) - - Commit your changes (git commit -am 'Add some feature') - - Push to the branch (git push origin my-new-feature) - - Create new Pull Request +- Go ahead and fork the project and make your changes. We encourage pull requests to discuss code changes. -* When you're ready to create a pull request, be sure to: - - Have test cases for the new code. If you have questions about how to do it, please ask in your pull request. - - Run `go fmt` - - Squash your commits into a single commit. `git rebase -i`. It's okay to force update your pull request. - - Make sure `go test -race ./...` and `go build` completes. - NOTE: go test runs functional tests and requires you to have a AWS S3 account. Set them as environment variables - ``ACCESS_KEY`` and ``SECRET_KEY``. To run shorter version of the tests please use ``go test -short -race ./...`` + - Fork it + - Create your feature branch (git checkout -b my-new-feature) + - Commit your changes (git commit -am 'Add some feature') + - Push to the branch (git push origin my-new-feature) + - Create new Pull Request -* Read [Effective Go](https://github.com/golang/go/wiki/CodeReviewComments) article from Golang project - - `minio-go` project is strictly conformant with Golang style - - if you happen to observe offending code, please feel free to send a pull request +- When you're ready to create a pull request, be sure to: + + - Have test cases for the new code. If you have questions about how to do it, please ask in your pull request. + - Run `go fmt` + - Squash your commits into a single commit. `git rebase -i`. It's okay to force update your pull request. + - Make sure `go test -race ./...` and `go build` completes. NOTE: go test runs functional tests and requires you to have a AWS S3 account. Set them as environment variables`ACCESS_KEY` and `SECRET_KEY`. To run shorter version of the tests please use `go test -short -race ./...` + +- Read [Effective Go](https://github.com/golang/go/wiki/CodeReviewComments) article from Golang project + + - `minio-go` project is strictly conformant with Golang style + - if you happen to observe offending code, please feel free to send a pull request diff --git a/vendor/github.com/minio/minio-go/v7/MAINTAINERS.md b/vendor/github.com/minio/minio-go/v7/MAINTAINERS.md index f640dfb9f..9b189373f 100644 --- a/vendor/github.com/minio/minio-go/v7/MAINTAINERS.md +++ b/vendor/github.com/minio/minio-go/v7/MAINTAINERS.md @@ -1,11 +1,15 @@ -# For maintainers only +For maintainers only +==================== -## Responsibilities +Responsibilities +---------------- Please go through this link [Maintainer Responsibility](https://gist.github.com/abperiasamy/f4d9b31d3186bbd26522) ### Making new releases + Tag and sign your release commit, additionally this step requires you to have access to MinIO's trusted private key. + ```sh $ export GNUPGHOME=/media/${USER}/minio/trusted $ git tag -s 4.0.0 @@ -14,6 +18,7 @@ $ git push --tags ``` ### Update version + Once release has been made update `libraryVersion` constant in `api.go` to next to be released version. ```sh @@ -22,14 +27,17 @@ $ grep libraryVersion api.go ``` Commit your changes + ``` $ git commit -a -m "Update version for next release" --author "MinIO Trusted <trusted@min.io>" ``` ### Announce + Announce new release by adding release notes at https://github.com/minio/minio-go/releases from `trusted@min.io` account. Release notes requires two sections `highlights` and `changelog`. Highlights is a bulleted list of salient features in this release and Changelog contains list of all commits since the last release. To generate `changelog` + ```sh $ git log --no-color --pretty=format:'-%d %s (%cr) <%an>' <last_release_tag>..<latest_release_tag> ``` diff --git a/vendor/github.com/minio/minio-go/v7/README.md b/vendor/github.com/minio/minio-go/v7/README.md index be7963c52..36c1004c9 100644 --- a/vendor/github.com/minio/minio-go/v7/README.md +++ b/vendor/github.com/minio/minio-go/v7/README.md @@ -1,13 +1,14 @@ -# MinIO Go Client SDK for Amazon S3 Compatible Cloud Storage [](https://slack.min.io) [](https://sourcegraph.com/github.com/minio/minio-go?badge) [](https://github.com/minio/minio-go/blob/master/LICENSE) +MinIO Go Client SDK for Amazon S3 Compatible Cloud Storage [](https://slack.min.io) [](https://sourcegraph.com/github.com/minio/minio-go?badge) [](https://github.com/minio/minio-go/blob/master/LICENSE) +================================================================================================================================================================================================================================================================================================================================================================================================================== The MinIO Go Client SDK provides straightforward APIs to access any Amazon S3 compatible object storage. -This Quickstart Guide covers how to install the MinIO client SDK, connect to MinIO, and create a sample file uploader. -For a complete list of APIs and examples, see the [godoc documentation](https://pkg.go.dev/github.com/minio/minio-go/v7) or [Go Client API Reference](https://min.io/docs/minio/linux/developers/go/API.html). +This Quickstart Guide covers how to install the MinIO client SDK, connect to MinIO, and create a sample file uploader. For a complete list of APIs and examples, see the [godoc documentation](https://pkg.go.dev/github.com/minio/minio-go/v7) or [Go Client API Reference](https://min.io/docs/minio/linux/developers/go/API.html). These examples presume a working [Go development environment](https://golang.org/doc/install) and the [MinIO `mc` command line tool](https://min.io/docs/minio/linux/reference/minio-mc.html). -## Download from Github +Download from Github +-------------------- From your project directory: @@ -15,12 +16,13 @@ From your project directory: go get github.com/minio/minio-go/v7 ``` -## Initialize a MinIO Client Object +Initialize a MinIO Client Object +-------------------------------- The MinIO client requires the following parameters to connect to an Amazon S3 compatible object storage: | Parameter | Description | -| ----------------- | ---------------------------------------------------------- | +|-------------------|------------------------------------------------------------| | `endpoint` | URL to object storage service. | | `_minio.Options_` | All the options such as credentials, custom transport etc. | @@ -53,83 +55,82 @@ func main() { } ``` -## Example - File Uploader +Example - File Uploader +----------------------- -This sample code connects to an object storage server, creates a bucket, and uploads a file to the bucket. -It uses the MinIO `play` server, a public MinIO cluster located at [https://play.min.io](https://play.min.io). +This sample code connects to an object storage server, creates a bucket, and uploads a file to the bucket. It uses the MinIO `play` server, a public MinIO cluster located at [https://play.min.io](https://play.min.io). -The `play` server runs the latest stable version of MinIO and may be used for testing and development. -The access credentials shown in this example are open to the public and all data uploaded to `play` should be considered public and non-protected. +The `play` server runs the latest stable version of MinIO and may be used for testing and development. The access credentials shown in this example are open to the public and all data uploaded to `play` should be considered public and non-protected. ### FileUploader.go This example does the following: -- Connects to the MinIO `play` server using the provided credentials. -- Creates a bucket named `testbucket`. -- Uploads a file named `testdata` from `/tmp`. -- Verifies the file was created using `mc ls`. - -```go -// FileUploader.go MinIO example -package main - -import ( - "context" - "log" - - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" -) - -func main() { - ctx := context.Background() - endpoint := "play.min.io" - accessKeyID := "Q3AM3UQ867SPQQA43P2F" - secretAccessKey := "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG" - useSSL := true +- Connects to the MinIO `play` server using the provided credentials. +- Creates a bucket named `testbucket`. +- Uploads a file named `testdata` from `/tmp`. +- Verifies the file was created using `mc ls`. + + ```go + // FileUploader.go MinIO example + package main + + import ( + "context" + "log" + + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + ) + + func main() { + ctx := context.Background() + endpoint := "play.min.io" + accessKeyID := "Q3AM3UQ867SPQQA43P2F" + secretAccessKey := "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG" + useSSL := true + + // Initialize minio client object. + minioClient, err := minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), + Secure: useSSL, + }) + if err != nil { + log.Fatalln(err) + } - // Initialize minio client object. - minioClient, err := minio.New(endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), - Secure: useSSL, - }) - if err != nil { - log.Fatalln(err) - } + // Make a new bucket called testbucket. + bucketName := "testbucket" + location := "us-east-1" + + err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: location}) + if err != nil { + // Check to see if we already own this bucket (which happens if you run this twice) + exists, errBucketExists := minioClient.BucketExists(ctx, bucketName) + if errBucketExists == nil && exists { + log.Printf("We already own %s\n", bucketName) + } else { + log.Fatalln(err) + } + } else { + log.Printf("Successfully created %s\n", bucketName) + } - // Make a new bucket called testbucket. - bucketName := "testbucket" - location := "us-east-1" + // Upload the test file + // Change the value of filePath if the file is in another location + objectName := "testdata" + filePath := "/tmp/testdata" + contentType := "application/octet-stream" - err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: location}) - if err != nil { - // Check to see if we already own this bucket (which happens if you run this twice) - exists, errBucketExists := minioClient.BucketExists(ctx, bucketName) - if errBucketExists == nil && exists { - log.Printf("We already own %s\n", bucketName) - } else { + // Upload the test file with FPutObject + info, err := minioClient.FPutObject(ctx, bucketName, objectName, filePath, minio.PutObjectOptions{ContentType: contentType}) + if err != nil { log.Fatalln(err) } - } else { - log.Printf("Successfully created %s\n", bucketName) - } - // Upload the test file - // Change the value of filePath if the file is in another location - objectName := "testdata" - filePath := "/tmp/testdata" - contentType := "application/octet-stream" - - // Upload the test file with FPutObject - info, err := minioClient.FPutObject(ctx, bucketName, objectName, filePath, minio.PutObjectOptions{ContentType: contentType}) - if err != nil { - log.Fatalln(err) + log.Printf("Successfully uploaded %s of size %d\n", objectName, info.Size) } - - log.Printf("Successfully uploaded %s of size %d\n", objectName, info.Size) -} -``` + ``` **1. Create a test file containing data:** @@ -168,145 +169,150 @@ mc ls play/testbucket [2023-11-01 14:27:55 UTC] 20KiB STANDARD TestDataFile ``` -## API Reference +API Reference +------------- The full API Reference is available here. -* [Complete API Reference](https://min.io/docs/minio/linux/developers/go/API.html) +- [Complete API Reference](https://min.io/docs/minio/linux/developers/go/API.html) ### API Reference : Bucket Operations -* [`MakeBucket`](https://min.io/docs/minio/linux/developers/go/API.html#MakeBucket) -* [`ListBuckets`](https://min.io/docs/minio/linux/developers/go/API.html#ListBuckets) -* [`BucketExists`](https://min.io/docs/minio/linux/developers/go/API.html#BucketExists) -* [`RemoveBucket`](https://min.io/docs/minio/linux/developers/go/API.html#RemoveBucket) -* [`ListObjects`](https://min.io/docs/minio/linux/developers/go/API.html#ListObjects) -* [`ListIncompleteUploads`](https://min.io/docs/minio/linux/developers/go/API.html#ListIncompleteUploads) +- [`MakeBucket`](https://min.io/docs/minio/linux/developers/go/API.html#MakeBucket) +- [`ListBuckets`](https://min.io/docs/minio/linux/developers/go/API.html#ListBuckets) +- [`BucketExists`](https://min.io/docs/minio/linux/developers/go/API.html#BucketExists) +- [`RemoveBucket`](https://min.io/docs/minio/linux/developers/go/API.html#RemoveBucket) +- [`ListObjects`](https://min.io/docs/minio/linux/developers/go/API.html#ListObjects) +- [`ListIncompleteUploads`](https://min.io/docs/minio/linux/developers/go/API.html#ListIncompleteUploads) ### API Reference : Bucket policy Operations -* [`SetBucketPolicy`](https://min.io/docs/minio/linux/developers/go/API.html#SetBucketPolicy) -* [`GetBucketPolicy`](https://min.io/docs/minio/linux/developers/go/API.html#GetBucketPolicy) +- [`SetBucketPolicy`](https://min.io/docs/minio/linux/developers/go/API.html#SetBucketPolicy) +- [`GetBucketPolicy`](https://min.io/docs/minio/linux/developers/go/API.html#GetBucketPolicy) ### API Reference : Bucket notification Operations -* [`SetBucketNotification`](https://min.io/docs/minio/linux/developers/go/API.html#SetBucketNotification) -* [`GetBucketNotification`](https://min.io/docs/minio/linux/developers/go/API.html#GetBucketNotification) -* [`RemoveAllBucketNotification`](https://min.io/docs/minio/linux/developers/go/API.html#RemoveAllBucketNotification) -* [`ListenBucketNotification`](https://min.io/docs/minio/linux/developers/go/API.html#ListenBucketNotification) (MinIO Extension) -* [`ListenNotification`](https://min.io/docs/minio/linux/developers/go/API.html#ListenNotification) (MinIO Extension) +- [`SetBucketNotification`](https://min.io/docs/minio/linux/developers/go/API.html#SetBucketNotification) +- [`GetBucketNotification`](https://min.io/docs/minio/linux/developers/go/API.html#GetBucketNotification) +- [`RemoveAllBucketNotification`](https://min.io/docs/minio/linux/developers/go/API.html#RemoveAllBucketNotification) +- [`ListenBucketNotification`](https://min.io/docs/minio/linux/developers/go/API.html#ListenBucketNotification) (MinIO Extension) +- [`ListenNotification`](https://min.io/docs/minio/linux/developers/go/API.html#ListenNotification) (MinIO Extension) ### API Reference : File Object Operations -* [`FPutObject`](https://min.io/docs/minio/linux/developers/go/API.html#FPutObject) -* [`FGetObject`](https://min.io/docs/minio/linux/developers/go/API.html#FGetObject) +- [`FPutObject`](https://min.io/docs/minio/linux/developers/go/API.html#FPutObject) +- [`FGetObject`](https://min.io/docs/minio/linux/developers/go/API.html#FGetObject) ### API Reference : Object Operations -* [`GetObject`](https://min.io/docs/minio/linux/developers/go/API.html#GetObject) -* [`PutObject`](https://min.io/docs/minio/linux/developers/go/API.html#PutObject) -* [`PutObjectStreaming`](https://min.io/docs/minio/linux/developers/go/API.html#PutObjectStreaming) -* [`StatObject`](https://min.io/docs/minio/linux/developers/go/API.html#StatObject) -* [`CopyObject`](https://min.io/docs/minio/linux/developers/go/API.html#CopyObject) -* [`RemoveObject`](https://min.io/docs/minio/linux/developers/go/API.html#RemoveObject) -* [`RemoveObjects`](https://min.io/docs/minio/linux/developers/go/API.html#RemoveObjects) -* [`RemoveIncompleteUpload`](https://min.io/docs/minio/linux/developers/go/API.html#RemoveIncompleteUpload) -* [`SelectObjectContent`](https://min.io/docs/minio/linux/developers/go/API.html#SelectObjectContent) +- [`GetObject`](https://min.io/docs/minio/linux/developers/go/API.html#GetObject) +- [`PutObject`](https://min.io/docs/minio/linux/developers/go/API.html#PutObject) +- [`PutObjectStreaming`](https://min.io/docs/minio/linux/developers/go/API.html#PutObjectStreaming) +- [`StatObject`](https://min.io/docs/minio/linux/developers/go/API.html#StatObject) +- [`CopyObject`](https://min.io/docs/minio/linux/developers/go/API.html#CopyObject) +- [`RemoveObject`](https://min.io/docs/minio/linux/developers/go/API.html#RemoveObject) +- [`RemoveObjects`](https://min.io/docs/minio/linux/developers/go/API.html#RemoveObjects) +- [`RemoveIncompleteUpload`](https://min.io/docs/minio/linux/developers/go/API.html#RemoveIncompleteUpload) +- [`SelectObjectContent`](https://min.io/docs/minio/linux/developers/go/API.html#SelectObjectContent) ### API Reference : Presigned Operations -* [`PresignedGetObject`](https://min.io/docs/minio/linux/developers/go/API.html#PresignedGetObject) -* [`PresignedPutObject`](https://min.io/docs/minio/linux/developers/go/API.html#PresignedPutObject) -* [`PresignedHeadObject`](https://min.io/docs/minio/linux/developers/go/API.html#PresignedHeadObject) -* [`PresignedPostPolicy`](https://min.io/docs/minio/linux/developers/go/API.html#PresignedPostPolicy) +- [`PresignedGetObject`](https://min.io/docs/minio/linux/developers/go/API.html#PresignedGetObject) +- [`PresignedPutObject`](https://min.io/docs/minio/linux/developers/go/API.html#PresignedPutObject) +- [`PresignedHeadObject`](https://min.io/docs/minio/linux/developers/go/API.html#PresignedHeadObject) +- [`PresignedPostPolicy`](https://min.io/docs/minio/linux/developers/go/API.html#PresignedPostPolicy) ### API Reference : Client custom settings -* [`SetAppInfo`](https://min.io/docs/minio/linux/developers/go/API.html#SetAppInfo) -* [`TraceOn`](https://min.io/docs/minio/linux/developers/go/API.html#TraceOn) -* [`TraceOff`](https://min.io/docs/minio/linux/developers/go/API.html#TraceOff) +- [`SetAppInfo`](https://min.io/docs/minio/linux/developers/go/API.html#SetAppInfo) +- [`TraceOn`](https://min.io/docs/minio/linux/developers/go/API.html#TraceOn) +- [`TraceOff`](https://min.io/docs/minio/linux/developers/go/API.html#TraceOff) -## Full Examples +Full Examples +------------- ### Full Examples : Bucket Operations -* [makebucket.go](https://github.com/minio/minio-go/blob/master/examples/s3/makebucket.go) -* [listbuckets.go](https://github.com/minio/minio-go/blob/master/examples/s3/listbuckets.go) -* [bucketexists.go](https://github.com/minio/minio-go/blob/master/examples/s3/bucketexists.go) -* [removebucket.go](https://github.com/minio/minio-go/blob/master/examples/s3/removebucket.go) -* [listobjects.go](https://github.com/minio/minio-go/blob/master/examples/s3/listobjects.go) -* [listobjectsV2.go](https://github.com/minio/minio-go/blob/master/examples/s3/listobjectsV2.go) -* [listincompleteuploads.go](https://github.com/minio/minio-go/blob/master/examples/s3/listincompleteuploads.go) +- [makebucket.go](https://github.com/minio/minio-go/blob/master/examples/s3/makebucket.go) +- [listbuckets.go](https://github.com/minio/minio-go/blob/master/examples/s3/listbuckets.go) +- [bucketexists.go](https://github.com/minio/minio-go/blob/master/examples/s3/bucketexists.go) +- [removebucket.go](https://github.com/minio/minio-go/blob/master/examples/s3/removebucket.go) +- [listobjects.go](https://github.com/minio/minio-go/blob/master/examples/s3/listobjects.go) +- [listobjectsV2.go](https://github.com/minio/minio-go/blob/master/examples/s3/listobjectsV2.go) +- [listincompleteuploads.go](https://github.com/minio/minio-go/blob/master/examples/s3/listincompleteuploads.go) ### Full Examples : Bucket policy Operations -* [setbucketpolicy.go](https://github.com/minio/minio-go/blob/master/examples/s3/setbucketpolicy.go) -* [getbucketpolicy.go](https://github.com/minio/minio-go/blob/master/examples/s3/getbucketpolicy.go) -* [listbucketpolicies.go](https://github.com/minio/minio-go/blob/master/examples/s3/listbucketpolicies.go) +- [setbucketpolicy.go](https://github.com/minio/minio-go/blob/master/examples/s3/setbucketpolicy.go) +- [getbucketpolicy.go](https://github.com/minio/minio-go/blob/master/examples/s3/getbucketpolicy.go) +- [listbucketpolicies.go](https://github.com/minio/minio-go/blob/master/examples/s3/listbucketpolicies.go) ### Full Examples : Bucket lifecycle Operations -* [setbucketlifecycle.go](https://github.com/minio/minio-go/blob/master/examples/s3/setbucketlifecycle.go) -* [getbucketlifecycle.go](https://github.com/minio/minio-go/blob/master/examples/s3/getbucketlifecycle.go) +- [setbucketlifecycle.go](https://github.com/minio/minio-go/blob/master/examples/s3/setbucketlifecycle.go) +- [getbucketlifecycle.go](https://github.com/minio/minio-go/blob/master/examples/s3/getbucketlifecycle.go) ### Full Examples : Bucket encryption Operations -* [setbucketencryption.go](https://github.com/minio/minio-go/blob/master/examples/s3/setbucketencryption.go) -* [getbucketencryption.go](https://github.com/minio/minio-go/blob/master/examples/s3/getbucketencryption.go) -* [removebucketencryption.go](https://github.com/minio/minio-go/blob/master/examples/s3/removebucketencryption.go) +- [setbucketencryption.go](https://github.com/minio/minio-go/blob/master/examples/s3/setbucketencryption.go) +- [getbucketencryption.go](https://github.com/minio/minio-go/blob/master/examples/s3/getbucketencryption.go) +- [removebucketencryption.go](https://github.com/minio/minio-go/blob/master/examples/s3/removebucketencryption.go) ### Full Examples : Bucket replication Operations -* [setbucketreplication.go](https://github.com/minio/minio-go/blob/master/examples/s3/setbucketreplication.go) -* [getbucketreplication.go](https://github.com/minio/minio-go/blob/master/examples/s3/getbucketreplication.go) -* [removebucketreplication.go](https://github.com/minio/minio-go/blob/master/examples/s3/removebucketreplication.go) +- [setbucketreplication.go](https://github.com/minio/minio-go/blob/master/examples/s3/setbucketreplication.go) +- [getbucketreplication.go](https://github.com/minio/minio-go/blob/master/examples/s3/getbucketreplication.go) +- [removebucketreplication.go](https://github.com/minio/minio-go/blob/master/examples/s3/removebucketreplication.go) ### Full Examples : Bucket notification Operations -* [setbucketnotification.go](https://github.com/minio/minio-go/blob/master/examples/s3/setbucketnotification.go) -* [getbucketnotification.go](https://github.com/minio/minio-go/blob/master/examples/s3/getbucketnotification.go) -* [removeallbucketnotification.go](https://github.com/minio/minio-go/blob/master/examples/s3/removeallbucketnotification.go) -* [listenbucketnotification.go](https://github.com/minio/minio-go/blob/master/examples/minio/listenbucketnotification.go) (MinIO Extension) -* [listennotification.go](https://github.com/minio/minio-go/blob/master/examples/minio/listen-notification.go) (MinIO Extension) +- [setbucketnotification.go](https://github.com/minio/minio-go/blob/master/examples/s3/setbucketnotification.go) +- [getbucketnotification.go](https://github.com/minio/minio-go/blob/master/examples/s3/getbucketnotification.go) +- [removeallbucketnotification.go](https://github.com/minio/minio-go/blob/master/examples/s3/removeallbucketnotification.go) +- [listenbucketnotification.go](https://github.com/minio/minio-go/blob/master/examples/minio/listenbucketnotification.go) (MinIO Extension) +- [listennotification.go](https://github.com/minio/minio-go/blob/master/examples/minio/listen-notification.go) (MinIO Extension) ### Full Examples : File Object Operations -* [fputobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/fputobject.go) -* [fgetobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/fgetobject.go) +- [fputobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/fputobject.go) +- [fgetobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/fgetobject.go) ### Full Examples : Object Operations -* [putobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/putobject.go) -* [getobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/getobject.go) -* [statobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/statobject.go) -* [copyobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/copyobject.go) -* [removeobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/removeobject.go) -* [removeincompleteupload.go](https://github.com/minio/minio-go/blob/master/examples/s3/removeincompleteupload.go) -* [removeobjects.go](https://github.com/minio/minio-go/blob/master/examples/s3/removeobjects.go) +- [putobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/putobject.go) +- [getobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/getobject.go) +- [statobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/statobject.go) +- [copyobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/copyobject.go) +- [removeobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/removeobject.go) +- [removeincompleteupload.go](https://github.com/minio/minio-go/blob/master/examples/s3/removeincompleteupload.go) +- [removeobjects.go](https://github.com/minio/minio-go/blob/master/examples/s3/removeobjects.go) ### Full Examples : Encrypted Object Operations -* [put-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/put-encrypted-object.go) -* [get-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/get-encrypted-object.go) -* [fput-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/fputencrypted-object.go) +- [put-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/put-encrypted-object.go) +- [get-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/get-encrypted-object.go) +- [fput-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/fputencrypted-object.go) ### Full Examples : Presigned Operations -* [presignedgetobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/presignedgetobject.go) -* [presignedputobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/presignedputobject.go) -* [presignedheadobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/presignedheadobject.go) -* [presignedpostpolicy.go](https://github.com/minio/minio-go/blob/master/examples/s3/presignedpostpolicy.go) +- [presignedgetobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/presignedgetobject.go) +- [presignedputobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/presignedputobject.go) +- [presignedheadobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/presignedheadobject.go) +- [presignedpostpolicy.go](https://github.com/minio/minio-go/blob/master/examples/s3/presignedpostpolicy.go) -## Explore Further +Explore Further +--------------- -* [Godoc Documentation](https://pkg.go.dev/github.com/minio/minio-go/v7) -* [Complete Documentation](https://min.io/docs/minio/kubernetes/upstream/index.html) -* [MinIO Go Client SDK API Reference](https://min.io/docs/minio/linux/developers/go/API.html) +- [Godoc Documentation](https://pkg.go.dev/github.com/minio/minio-go/v7) +- [Complete Documentation](https://min.io/docs/minio/kubernetes/upstream/index.html) +- [MinIO Go Client SDK API Reference](https://min.io/docs/minio/linux/developers/go/API.html) -## Contribute +Contribute +---------- [Contributors Guide](https://github.com/minio/minio-go/blob/master/CONTRIBUTING.md) -## License +License +------- This SDK is distributed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0), see [LICENSE](https://github.com/minio/minio-go/blob/master/LICENSE) and [NOTICE](https://github.com/minio/minio-go/blob/master/NOTICE) for more information. diff --git a/vendor/github.com/minio/minio-go/v7/api-bucket-cors.go b/vendor/github.com/minio/minio-go/v7/api-bucket-cors.go index 9d514947d..9967fe39e 100644 --- a/vendor/github.com/minio/minio-go/v7/api-bucket-cors.go +++ b/vendor/github.com/minio/minio-go/v7/api-bucket-cors.go @@ -26,7 +26,15 @@ import ( "github.com/minio/minio-go/v7/pkg/s3utils" ) -// SetBucketCors sets the cors configuration for the bucket +// SetBucketCors sets the Cross-Origin Resource Sharing (CORS) configuration for the bucket. +// If corsConfig is nil, the existing CORS configuration will be removed. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - corsConfig: CORS configuration to apply (nil to remove existing configuration) +// +// Returns an error if the operation fails. func (c *Client) SetBucketCors(ctx context.Context, bucketName string, corsConfig *cors.Config) error { if err := s3utils.CheckValidBucketName(bucketName); err != nil { return err @@ -90,7 +98,14 @@ func (c *Client) removeBucketCors(ctx context.Context, bucketName string) error return nil } -// GetBucketCors returns the current cors +// GetBucketCors retrieves the Cross-Origin Resource Sharing (CORS) configuration from the bucket. +// If no CORS configuration exists, returns nil with no error. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns the CORS configuration or an error if the operation fails. func (c *Client) GetBucketCors(ctx context.Context, bucketName string) (*cors.Config, error) { if err := s3utils.CheckValidBucketName(bucketName); err != nil { return nil, err diff --git a/vendor/github.com/minio/minio-go/v7/api-bucket-encryption.go b/vendor/github.com/minio/minio-go/v7/api-bucket-encryption.go index 24f94e034..3ae9fe279 100644 --- a/vendor/github.com/minio/minio-go/v7/api-bucket-encryption.go +++ b/vendor/github.com/minio/minio-go/v7/api-bucket-encryption.go @@ -28,6 +28,14 @@ import ( ) // SetBucketEncryption sets the default encryption configuration on an existing bucket. +// The encryption configuration specifies the default encryption behavior for objects uploaded to the bucket. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - config: Server-side encryption configuration to apply +// +// Returns an error if the operation fails or if config is nil. func (c *Client) SetBucketEncryption(ctx context.Context, bucketName string, config *sse.Configuration) error { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -69,7 +77,15 @@ func (c *Client) SetBucketEncryption(ctx context.Context, bucketName string, con return nil } -// RemoveBucketEncryption removes the default encryption configuration on a bucket with a context to control cancellations and timeouts. +// RemoveBucketEncryption removes the default encryption configuration from a bucket. +// After removal, the bucket will no longer apply default encryption to new objects. +// It uses the provided context to control cancellations and timeouts. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns an error if the operation fails. func (c *Client) RemoveBucketEncryption(ctx context.Context, bucketName string) error { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -97,8 +113,14 @@ func (c *Client) RemoveBucketEncryption(ctx context.Context, bucketName string) return nil } -// GetBucketEncryption gets the default encryption configuration -// on an existing bucket with a context to control cancellations and timeouts. +// GetBucketEncryption retrieves the default encryption configuration from a bucket. +// It uses the provided context to control cancellations and timeouts. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns the bucket's encryption configuration or an error if the operation fails. func (c *Client) GetBucketEncryption(ctx context.Context, bucketName string) (*sse.Configuration, error) { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { diff --git a/vendor/github.com/minio/minio-go/v7/api-bucket-notification.go b/vendor/github.com/minio/minio-go/v7/api-bucket-notification.go index 0d6011042..9e2a67769 100644 --- a/vendor/github.com/minio/minio-go/v7/api-bucket-notification.go +++ b/vendor/github.com/minio/minio-go/v7/api-bucket-notification.go @@ -21,12 +21,12 @@ import ( "bufio" "bytes" "context" + "encoding/json" "encoding/xml" "net/http" "net/url" "time" - "github.com/minio/minio-go/v7/internal/json" "github.com/minio/minio-go/v7/pkg/notification" "github.com/minio/minio-go/v7/pkg/s3utils" ) diff --git a/vendor/github.com/minio/minio-go/v7/api-bucket-policy.go b/vendor/github.com/minio/minio-go/v7/api-bucket-policy.go index 3a168c13e..0e561bdfa 100644 --- a/vendor/github.com/minio/minio-go/v7/api-bucket-policy.go +++ b/vendor/github.com/minio/minio-go/v7/api-bucket-policy.go @@ -26,7 +26,16 @@ import ( "github.com/minio/minio-go/v7/pkg/s3utils" ) -// SetBucketPolicy sets the access permissions on an existing bucket. +// SetBucketPolicy sets the access permissions policy on an existing bucket. +// The policy should be a valid JSON string that conforms to the IAM policy format. +// If policy is an empty string, the existing bucket policy will be removed. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - policy: JSON policy string (empty string to remove existing policy) +// +// Returns an error if the operation fails. func (c *Client) SetBucketPolicy(ctx context.Context, bucketName, policy string) error { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -95,7 +104,14 @@ func (c *Client) removeBucketPolicy(ctx context.Context, bucketName string) erro return nil } -// GetBucketPolicy returns the current policy +// GetBucketPolicy retrieves the access permissions policy for the bucket. +// If no bucket policy exists, returns an empty string with no error. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns the policy as a JSON string or an error if the operation fails. func (c *Client) GetBucketPolicy(ctx context.Context, bucketName string) (string, error) { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { diff --git a/vendor/github.com/minio/minio-go/v7/api-bucket-qos.go b/vendor/github.com/minio/minio-go/v7/api-bucket-qos.go new file mode 100644 index 000000000..d1493a5b9 --- /dev/null +++ b/vendor/github.com/minio/minio-go/v7/api-bucket-qos.go @@ -0,0 +1,212 @@ +/* + * MinIO Go Library for Amazon S3 Compatible Cloud Storage + * Copyright 2025 MinIO, Inc. + * 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. + */ + +package minio + +import ( + "context" + "encoding/json" + "io" + "net/http" + "net/url" + "strings" + + "github.com/minio/minio-go/v7/pkg/s3utils" + "gopkg.in/yaml.v3" +) + +// QOSConfigVersionCurrent is the current version of the QoS configuration. +const QOSConfigVersionCurrent = "v1" + +// QOSConfig represents the QoS configuration for a bucket. +type QOSConfig struct { + Version string `yaml:"version"` + Rules []QOSRule `yaml:"rules"` +} + +// QOSRule represents a single QoS rule. +type QOSRule struct { + ID string `yaml:"id"` + Label string `yaml:"label,omitempty"` + Priority int `yaml:"priority"` + ObjectPrefix string `yaml:"objectPrefix"` + API string `yaml:"api"` + Rate int64 `yaml:"rate"` + Burst int64 `yaml:"burst"` // not required for concurrency limit + Limit string `yaml:"limit"` // "concurrency" or "rps" +} + +// NewQOSConfig creates a new empty QoS configuration. +func NewQOSConfig() *QOSConfig { + return &QOSConfig{ + Version: "v1", + Rules: []QOSRule{}, + } +} + +// GetBucketQOS retrieves the Quality of Service (QoS) configuration for the bucket. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucket: Name of the bucket +// +// Returns the QoS configuration or an error if the operation fails. +func (c *Client) GetBucketQOS(ctx context.Context, bucket string) (*QOSConfig, error) { + var qosCfg QOSConfig + // Input validation. + if err := s3utils.CheckValidBucketName(bucket); err != nil { + return nil, err + } + urlValues := make(url.Values) + urlValues.Set("qos", "") + // Execute GET on bucket to fetch qos. + resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{ + bucketName: bucket, + queryValues: urlValues, + contentSHA256Hex: emptySHA256Hex, + }) + defer closeResponse(resp) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return nil, httpRespToErrorResponse(resp, bucket, "") + } + b, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if err = yaml.Unmarshal(b, &qosCfg); err != nil { + return nil, err + } + + return &qosCfg, nil +} + +// SetBucketQOS sets the Quality of Service (QoS) configuration for a bucket. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucket: Name of the bucket +// - qosCfg: QoS configuration to apply +// +// Returns an error if the operation fails. +func (c *Client) SetBucketQOS(ctx context.Context, bucket string, qosCfg *QOSConfig) error { + // Input validation. + if err := s3utils.CheckValidBucketName(bucket); err != nil { + return err + } + + data, err := yaml.Marshal(qosCfg) + if err != nil { + return err + } + // Get resources properly escaped and lined up before + // using them in http request. + urlValues := make(url.Values) + urlValues.Set("qos", "") + + reqMetadata := requestMetadata{ + bucketName: bucket, + queryValues: urlValues, + contentBody: strings.NewReader(string(data)), + contentLength: int64(len(data)), + } + + // Execute PUT to upload a new bucket QoS configuration. + resp, err := c.executeMethod(ctx, http.MethodPut, reqMetadata) + defer closeResponse(resp) + if err != nil { + return err + } + if resp != nil { + if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK { + return httpRespToErrorResponse(resp, bucket, "") + } + } + return nil +} + +// CounterMetric returns stats for a counter +type CounterMetric struct { + Last1m uint64 `json:"last1m"` + Last1hr uint64 `json:"last1hr"` + Total uint64 `json:"total"` +} + +// QOSMetric - metric for a qos rule per bucket +type QOSMetric struct { + APIName string `json:"apiName"` + Rule QOSRule `json:"rule"` + Totals CounterMetric `json:"totals"` + Throttled CounterMetric `json:"throttleCount"` + ExceededRateLimit CounterMetric `json:"exceededRateLimitCount"` + ClientDisconnCount CounterMetric `json:"clientDisconnectCount"` + ReqTimeoutCount CounterMetric `json:"reqTimeoutCount"` +} + +// QOSNodeStats represents stats for a bucket on a single node +type QOSNodeStats struct { + Stats []QOSMetric `json:"stats"` + NodeName string `json:"node"` +} + +// GetBucketQOSMetrics retrieves Quality of Service (QoS) metrics for a bucket. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - nodeName: Name of the node (empty string for all nodes) +// +// Returns QoS metrics per node or an error if the operation fails. +func (c *Client) GetBucketQOSMetrics(ctx context.Context, bucketName, nodeName string) (qs []QOSNodeStats, err error) { + // Input validation. + if err := s3utils.CheckValidBucketName(bucketName); err != nil { + return qs, err + } + // Get resources properly escaped and lined up before + // using them in http request. + urlValues := make(url.Values) + urlValues.Set("qos-metrics", "") + if nodeName != "" { + urlValues.Set("node", nodeName) + } + // Execute GET on bucket to get qos metrics. + resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{ + bucketName: bucketName, + queryValues: urlValues, + }) + + defer closeResponse(resp) + if err != nil { + return qs, err + } + + if resp.StatusCode != http.StatusOK { + return qs, httpRespToErrorResponse(resp, bucketName, "") + } + respBytes, err := io.ReadAll(resp.Body) + if err != nil { + return qs, err + } + + if err := json.Unmarshal(respBytes, &qs); err != nil { + return qs, err + } + return qs, nil +} diff --git a/vendor/github.com/minio/minio-go/v7/api-bucket-replication.go b/vendor/github.com/minio/minio-go/v7/api-bucket-replication.go index 8632bb85d..6dd7ae893 100644 --- a/vendor/github.com/minio/minio-go/v7/api-bucket-replication.go +++ b/vendor/github.com/minio/minio-go/v7/api-bucket-replication.go @@ -20,6 +20,7 @@ package minio import ( "bytes" "context" + "encoding/json" "encoding/xml" "io" "net/http" @@ -27,17 +28,30 @@ import ( "time" "github.com/google/uuid" - "github.com/minio/minio-go/v7/internal/json" "github.com/minio/minio-go/v7/pkg/replication" "github.com/minio/minio-go/v7/pkg/s3utils" ) -// RemoveBucketReplication removes a replication config on an existing bucket. +// RemoveBucketReplication removes the replication configuration from an existing bucket. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns an error if the operation fails. func (c *Client) RemoveBucketReplication(ctx context.Context, bucketName string) error { return c.removeBucketReplication(ctx, bucketName) } -// SetBucketReplication sets a replication config on an existing bucket. +// SetBucketReplication sets the replication configuration on an existing bucket. +// If the provided configuration is empty, this method removes the existing replication configuration. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - cfg: Replication configuration to apply +// +// Returns an error if the operation fails. func (c *Client) SetBucketReplication(ctx context.Context, bucketName string, cfg replication.Config) error { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -108,8 +122,14 @@ func (c *Client) removeBucketReplication(ctx context.Context, bucketName string) return nil } -// GetBucketReplication fetches bucket replication configuration.If config is not -// found, returns empty config with nil error. +// GetBucketReplication retrieves the bucket replication configuration. +// If no replication configuration is found, returns an empty config with nil error. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns the replication configuration or an error if the operation fails. func (c *Client) GetBucketReplication(ctx context.Context, bucketName string) (cfg replication.Config, err error) { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -155,7 +175,13 @@ func (c *Client) getBucketReplication(ctx context.Context, bucketName string) (c return cfg, nil } -// GetBucketReplicationMetrics fetches bucket replication status metrics +// GetBucketReplicationMetrics retrieves bucket replication status metrics. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns the replication metrics or an error if the operation fails. func (c *Client) GetBucketReplicationMetrics(ctx context.Context, bucketName string) (s replication.Metrics, err error) { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -200,8 +226,15 @@ func mustGetUUID() string { return u.String() } -// ResetBucketReplication kicks off replication of previously replicated objects if ExistingObjectReplication -// is enabled in the replication config +// ResetBucketReplication initiates replication of previously replicated objects. +// This requires ExistingObjectReplication to be enabled in the replication configuration. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - olderThan: Only replicate objects older than this duration (0 for all objects) +// +// Returns a reset ID that can be used to track the operation, or an error if the operation fails. func (c *Client) ResetBucketReplication(ctx context.Context, bucketName string, olderThan time.Duration) (rID string, err error) { rID = mustGetUUID() _, err = c.resetBucketReplicationOnTarget(ctx, bucketName, olderThan, "", rID) @@ -211,8 +244,16 @@ func (c *Client) ResetBucketReplication(ctx context.Context, bucketName string, return rID, nil } -// ResetBucketReplicationOnTarget kicks off replication of previously replicated objects if -// ExistingObjectReplication is enabled in the replication config +// ResetBucketReplicationOnTarget initiates replication of previously replicated objects to a specific target. +// This requires ExistingObjectReplication to be enabled in the replication configuration. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - olderThan: Only replicate objects older than this duration (0 for all objects) +// - tgtArn: ARN of the target to reset replication for +// +// Returns resync target information or an error if the operation fails. func (c *Client) ResetBucketReplicationOnTarget(ctx context.Context, bucketName string, olderThan time.Duration, tgtArn string) (replication.ResyncTargetsInfo, error) { return c.resetBucketReplicationOnTarget(ctx, bucketName, olderThan, tgtArn, mustGetUUID()) } @@ -222,7 +263,7 @@ func (c *Client) ResetBucketReplicationOnTarget(ctx context.Context, bucketName func (c *Client) resetBucketReplicationOnTarget(ctx context.Context, bucketName string, olderThan time.Duration, tgtArn, resetID string) (rinfo replication.ResyncTargetsInfo, err error) { // Input validation. if err = s3utils.CheckValidBucketName(bucketName); err != nil { - return + return rinfo, err } // Get resources properly escaped and lined up before // using them in http request. @@ -256,7 +297,14 @@ func (c *Client) resetBucketReplicationOnTarget(ctx context.Context, bucketName return rinfo, nil } -// GetBucketReplicationResyncStatus gets the status of replication resync +// GetBucketReplicationResyncStatus retrieves the status of a replication resync operation. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - arn: ARN of the replication target (empty string for all targets) +// +// Returns resync status information or an error if the operation fails. func (c *Client) GetBucketReplicationResyncStatus(ctx context.Context, bucketName, arn string) (rinfo replication.ResyncTargetsInfo, err error) { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -290,11 +338,18 @@ func (c *Client) GetBucketReplicationResyncStatus(ctx context.Context, bucketNam return rinfo, nil } -// CancelBucketReplicationResync cancels in progress replication resync +// CancelBucketReplicationResync cancels an in-progress replication resync operation. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - tgtArn: ARN of the replication target (empty string for all targets) +// +// Returns the ID of the canceled resync operation or an error if the operation fails. func (c *Client) CancelBucketReplicationResync(ctx context.Context, bucketName string, tgtArn string) (id string, err error) { // Input validation. if err = s3utils.CheckValidBucketName(bucketName); err != nil { - return + return id, err } // Get resources properly escaped and lined up before // using them in http request. @@ -326,7 +381,13 @@ func (c *Client) CancelBucketReplicationResync(ctx context.Context, bucketName s return id, nil } -// GetBucketReplicationMetricsV2 fetches bucket replication status metrics +// GetBucketReplicationMetricsV2 retrieves bucket replication status metrics using the V2 API. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns the V2 replication metrics or an error if the operation fails. func (c *Client) GetBucketReplicationMetricsV2(ctx context.Context, bucketName string) (s replication.MetricsV2, err error) { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -362,7 +423,13 @@ func (c *Client) GetBucketReplicationMetricsV2(ctx context.Context, bucketName s return s, nil } -// CheckBucketReplication validates if replication is set up properly for a bucket +// CheckBucketReplication validates whether replication is properly configured for a bucket. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns nil if replication is valid, or an error describing the validation failure. func (c *Client) CheckBucketReplication(ctx context.Context, bucketName string) (err error) { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { diff --git a/vendor/github.com/minio/minio-go/v7/api-bucket-tagging.go b/vendor/github.com/minio/minio-go/v7/api-bucket-tagging.go index 86d74298a..921f90f99 100644 --- a/vendor/github.com/minio/minio-go/v7/api-bucket-tagging.go +++ b/vendor/github.com/minio/minio-go/v7/api-bucket-tagging.go @@ -29,8 +29,14 @@ import ( "github.com/minio/minio-go/v7/pkg/tags" ) -// GetBucketTagging fetch tagging configuration for a bucket with a -// context to control cancellations and timeouts. +// GetBucketTagging fetches the tagging configuration for a bucket. +// It uses the provided context to control cancellations and timeouts. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns the bucket's tags or an error if the operation fails. func (c *Client) GetBucketTagging(ctx context.Context, bucketName string) (*tags.Tags, error) { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -61,8 +67,15 @@ func (c *Client) GetBucketTagging(ctx context.Context, bucketName string) (*tags return tags.ParseBucketXML(resp.Body) } -// SetBucketTagging sets tagging configuration for a bucket -// with a context to control cancellations and timeouts. +// SetBucketTagging sets the tagging configuration for a bucket. +// It uses the provided context to control cancellations and timeouts. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - tags: Tag set to apply to the bucket +// +// Returns an error if the operation fails or if tags is nil. func (c *Client) SetBucketTagging(ctx context.Context, bucketName string, tags *tags.Tags) error { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -104,8 +117,14 @@ func (c *Client) SetBucketTagging(ctx context.Context, bucketName string, tags * return nil } -// RemoveBucketTagging removes tagging configuration for a -// bucket with a context to control cancellations and timeouts. +// RemoveBucketTagging removes the tagging configuration from a bucket. +// It uses the provided context to control cancellations and timeouts. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// +// Returns an error if the operation fails. func (c *Client) RemoveBucketTagging(ctx context.Context, bucketName string) error { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { diff --git a/vendor/github.com/minio/minio-go/v7/api-compose-object.go b/vendor/github.com/minio/minio-go/v7/api-compose-object.go index 154af7121..232bd2c01 100644 --- a/vendor/github.com/minio/minio-go/v7/api-compose-object.go +++ b/vendor/github.com/minio/minio-go/v7/api-compose-object.go @@ -42,13 +42,15 @@ type CopyDestOptions struct { // provided key. If it is nil, no encryption is performed. Encryption encrypt.ServerSide + ChecksumType ChecksumType + // `userMeta` is the user-metadata key-value pairs to be set on the // destination. The keys are automatically prefixed with `x-amz-meta-` // if needed. If nil is passed, and if only a single source (of any // size) is provided in the ComposeObject call, then metadata from the // source is copied to the destination. // if no user-metadata is provided, it is copied from source - // (when there is only once source object in the compose + // (when there is only one source object in the compose // request) UserMetadata map[string]string // UserMetadata is only set to destination if ReplaceMetadata is true @@ -140,6 +142,9 @@ func (opts CopyDestOptions) Marshal(header http.Header) { if !opts.Expires.IsZero() { header.Set("Expires", opts.Expires.UTC().Format(http.TimeFormat)) } + if opts.ChecksumType.IsSet() { + header.Set(amzChecksumAlgo, opts.ChecksumType.String()) + } if opts.ReplaceMetadata { header.Set("x-amz-metadata-directive", replaceDirective) @@ -345,7 +350,7 @@ func (c *Client) copyObjectPartDo(ctx context.Context, srcBucket, srcObject, des }) defer closeResponse(resp) if err != nil { - return + return p, err } // Check if we got an error response. @@ -580,7 +585,7 @@ func partsRequired(size int64) int64 { // it is not the last part. func calculateEvenSplits(size int64, src CopySrcOptions) (startIndex, endIndex []int64) { if size == 0 { - return + return startIndex, endIndex } reqParts := partsRequired(size) @@ -617,5 +622,5 @@ func calculateEvenSplits(size int64, src CopySrcOptions) (startIndex, endIndex [ startIndex[j], endIndex[j] = cStart, cEnd } - return + return startIndex, endIndex } diff --git a/vendor/github.com/minio/minio-go/v7/api-error-response.go b/vendor/github.com/minio/minio-go/v7/api-error-response.go index e85aa322c..e5f88d98e 100644 --- a/vendor/github.com/minio/minio-go/v7/api-error-response.go +++ b/vendor/github.com/minio/minio-go/v7/api-error-response.go @@ -20,6 +20,7 @@ package minio import ( "bytes" "encoding/xml" + "errors" "fmt" "io" "net/http" @@ -128,9 +129,18 @@ func httpRespToErrorResponse(resp *http.Response, bucketName, objectName string) Server: resp.Header.Get("Server"), } + _, success := successStatus[resp.StatusCode] + errBody, err := xmlDecodeAndBody(resp.Body, &errResp) // Xml decoding failed with no body, fall back to HTTP headers. if err != nil { + var unmarshalErr xml.UnmarshalError + if success && errors.As(err, &unmarshalErr) { + // This is a successful message so not an error response + // return nil, + return nil + } + switch resp.StatusCode { case http.StatusNotFound: if objectName == "" { diff --git a/vendor/github.com/minio/minio-go/v7/api-get-object-attributes.go b/vendor/github.com/minio/minio-go/v7/api-get-object-attributes.go index e1155c372..d2e8cabde 100644 --- a/vendor/github.com/minio/minio-go/v7/api-get-object-attributes.go +++ b/vendor/github.com/minio/minio-go/v7/api-get-object-attributes.go @@ -127,7 +127,7 @@ func (o *ObjectAttributes) parseResponse(resp *http.Response) (err error) { } o.ObjectAttributesResponse = *response - return + return err } // GetObjectAttributes API combines HeadObject and ListParts. diff --git a/vendor/github.com/minio/minio-go/v7/api-get-object-file.go b/vendor/github.com/minio/minio-go/v7/api-get-object-file.go index 567a42e45..6ef9c9330 100644 --- a/vendor/github.com/minio/minio-go/v7/api-get-object-file.go +++ b/vendor/github.com/minio/minio-go/v7/api-get-object-file.go @@ -69,7 +69,7 @@ func (c *Client) FGetObject(ctx context.Context, bucketName, objectName, filePat } // Write to a temporary file "fileName.part.minio" before saving. - filePartPath := filePath + sum256Hex([]byte(objectStat.ETag)) + ".part.minio" + filePartPath := filepath.Join(filepath.Dir(filePath), sum256Hex([]byte(filepath.Base(filePath)+objectStat.ETag))+".part.minio") // If exists, open in append mode. If not create it as a part file. filePart, err := os.OpenFile(filePartPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600) diff --git a/vendor/github.com/minio/minio-go/v7/api-inventory-ext.go b/vendor/github.com/minio/minio-go/v7/api-inventory-ext.go new file mode 100644 index 000000000..498300785 --- /dev/null +++ b/vendor/github.com/minio/minio-go/v7/api-inventory-ext.go @@ -0,0 +1,332 @@ +/* + * MinIO Go Library for Amazon S3 Compatible Cloud Storage + * Copyright 2015-2025 MinIO, Inc. + * + * 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. + */ + +package minio + +import ( + "context" + "encoding/json" + "io" + "iter" + "net/http" + "net/url" + "strings" + "time" + + "github.com/minio/minio-go/v7/pkg/s3utils" +) + +// This file contains the inventory API extension for MinIO server. It is not +// compatible with AWS S3. + +func makeInventoryReqMetadata(bucket string, urlParams ...string) requestMetadata { + urlValues := make(url.Values) + urlValues.Set("minio-inventory", "") + + // If an odd number of parameters is given, we skip the last pair to avoid + // an out of bounds access. + for i := 0; i+1 < len(urlParams); i += 2 { + urlValues.Set(urlParams[i], urlParams[i+1]) + } + + return requestMetadata{ + bucketName: bucket, + queryValues: urlValues, + } +} + +// GenerateInventoryConfigYAML generates a YAML template for an inventory configuration. +// This is a MinIO-specific API and is not compatible with AWS S3. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucket: Name of the bucket +// - id: Unique identifier for the inventory configuration +// +// Returns a YAML template string that can be customized and used with PutBucketInventoryConfiguration. +func (c *Client) GenerateInventoryConfigYAML(ctx context.Context, bucket, id string) (string, error) { + if err := s3utils.CheckValidBucketName(bucket); err != nil { + return "", err + } + if id == "" { + return "", errInvalidArgument("inventory ID cannot be empty") + } + reqMeta := makeInventoryReqMetadata(bucket, "generate", "", "id", id) + resp, err := c.executeMethod(ctx, http.MethodGet, reqMeta) + defer closeResponse(resp) + if err != nil { + return "", err + } + if resp.StatusCode != http.StatusOK { + return "", httpRespToErrorResponse(resp, bucket, "") + } + buf := new(strings.Builder) + _, err = io.Copy(buf, resp.Body) + return buf.String(), err +} + +// inventoryPutConfigOpts is a placeholder for future options that may be added. +type inventoryPutConfigOpts struct{} + +// InventoryPutConfigOption is to allow for functional options for +// PutBucketInventoryConfiguration. It may be used in the future to customize +// the PutBucketInventoryConfiguration request, but currently does not do +// anything. +type InventoryPutConfigOption func(*inventoryPutConfigOpts) + +// PutBucketInventoryConfiguration creates or updates an inventory configuration for a bucket. +// This is a MinIO-specific API and is not compatible with AWS S3. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucket: Name of the bucket +// - id: Unique identifier for the inventory configuration +// - yamlDef: YAML definition of the inventory configuration +// +// Returns an error if the operation fails, or if bucket name, id, or yamlDef is empty. +func (c *Client) PutBucketInventoryConfiguration(ctx context.Context, bucket string, id string, yamlDef string, _ ...InventoryPutConfigOption) error { + if err := s3utils.CheckValidBucketName(bucket); err != nil { + return err + } + if id == "" { + return errInvalidArgument("inventory ID cannot be empty") + } + if yamlDef == "" { + return errInvalidArgument("YAML definition cannot be empty") + } + reqMeta := makeInventoryReqMetadata(bucket, "id", id) + reqMeta.contentBody = strings.NewReader(yamlDef) + reqMeta.contentLength = int64(len(yamlDef)) + reqMeta.contentMD5Base64 = sumMD5Base64([]byte(yamlDef)) + + resp, err := c.executeMethod(ctx, http.MethodPut, reqMeta) + defer closeResponse(resp) + if err != nil { + return err + } + if resp.StatusCode != http.StatusOK { + return httpRespToErrorResponse(resp, bucket, "") + } + return nil +} + +// GetBucketInventoryConfiguration retrieves the inventory configuration for a bucket. +// This is a MinIO-specific API and is not compatible with AWS S3. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucket: Name of the bucket +// - id: Unique identifier for the inventory configuration +// +// Returns the inventory configuration or an error if the operation fails or if the configuration doesn't exist. +func (c *Client) GetBucketInventoryConfiguration(ctx context.Context, bucket, id string) (*InventoryConfiguration, error) { + if err := s3utils.CheckValidBucketName(bucket); err != nil { + return nil, err + } + if id == "" { + return nil, errInvalidArgument("inventory ID cannot be empty") + } + reqMeta := makeInventoryReqMetadata(bucket, "id", id) + resp, err := c.executeMethod(ctx, http.MethodGet, reqMeta) + defer closeResponse(resp) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, httpRespToErrorResponse(resp, bucket, "") + } + decoder := json.NewDecoder(resp.Body) + var ic InventoryConfiguration + err = decoder.Decode(&ic) + if err != nil { + return nil, err + } + return &ic, nil +} + +// DeleteBucketInventoryConfiguration deletes an inventory configuration from a bucket. +// This is a MinIO-specific API and is not compatible with AWS S3. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucket: Name of the bucket +// - id: Unique identifier for the inventory configuration to delete +// +// Returns an error if the operation fails or if the configuration doesn't exist. +func (c *Client) DeleteBucketInventoryConfiguration(ctx context.Context, bucket, id string) error { + if err := s3utils.CheckValidBucketName(bucket); err != nil { + return err + } + if id == "" { + return errInvalidArgument("inventory ID cannot be empty") + } + reqMeta := makeInventoryReqMetadata(bucket, "id", id) + resp, err := c.executeMethod(ctx, http.MethodDelete, reqMeta) + defer closeResponse(resp) + if err != nil { + return err + } + if resp.StatusCode != http.StatusOK { + return httpRespToErrorResponse(resp, bucket, "") + } + return nil +} + +// InventoryConfiguration represents the inventory configuration +type InventoryConfiguration struct { + Bucket string `json:"bucket"` + ID string `json:"id"` + User string `json:"user"` + YamlDef string `json:"yamlDef,omitempty"` +} + +// InventoryListResult represents the result of listing inventory +// configurations. +type InventoryListResult struct { + Items []InventoryConfiguration `json:"items"` + NextContinuationToken string `json:"nextContinuationToken,omitempty"` +} + +// ListBucketInventoryConfigurations lists up to 100 inventory configurations for a bucket. +// This is a MinIO-specific API and is not compatible with AWS S3. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucket: Name of the bucket +// - continuationToken: Token for pagination (empty string for first request) +// +// Returns a list result with configurations and a continuation token for the next page, or an error. +func (c *Client) ListBucketInventoryConfigurations(ctx context.Context, bucket, continuationToken string) (lr *InventoryListResult, err error) { + if err := s3utils.CheckValidBucketName(bucket); err != nil { + return nil, err + } + reqMeta := makeInventoryReqMetadata(bucket, "continuation-token", continuationToken) + resp, err := c.executeMethod(ctx, http.MethodGet, reqMeta) + defer closeResponse(resp) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, httpRespToErrorResponse(resp, bucket, "") + } + + decoder := json.NewDecoder(resp.Body) + err = decoder.Decode(&lr) + if err != nil { + return nil, err + } + return lr, nil +} + +// ListBucketInventoryConfigurationsIterator returns an iterator that lists all inventory configurations +// for a bucket. This is a MinIO-specific API and is not compatible with AWS S3. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucket: Name of the bucket +// +// Returns an iterator that yields InventoryConfiguration values and errors. The iterator automatically +// handles pagination and fetches all configurations. +func (c *Client) ListBucketInventoryConfigurationsIterator(ctx context.Context, bucket string) iter.Seq2[InventoryConfiguration, error] { + return func(yield func(InventoryConfiguration, error) bool) { + if err := s3utils.CheckValidBucketName(bucket); err != nil { + yield(InventoryConfiguration{}, err) + return + } + var continuationToken string + for { + listResult, err := c.ListBucketInventoryConfigurations(ctx, bucket, continuationToken) + if err != nil { + yield(InventoryConfiguration{}, err) + return + } + + for _, item := range listResult.Items { + if !yield(item, nil) { + return + } + } + + if listResult.NextContinuationToken == "" { + return + } + continuationToken = listResult.NextContinuationToken + } + } +} + +// InventoryJobStatus represents the status of an inventory job. +type InventoryJobStatus struct { + Bucket string `json:"bucket"` + ID string `json:"id"` + User string `json:"user"` + AccessKey string `json:"accessKey"` + Schedule string `json:"schedule"` + State string `json:"state"` + NextScheduledTime time.Time `json:"nextScheduledTime,omitempty"` + StartTime time.Time `json:"startTime,omitempty"` + EndTime time.Time `json:"endTime,omitempty"` + LastUpdate time.Time `json:"lastUpdate,omitempty"` + Scanned string `json:"scanned,omitempty"` + Matched string `json:"matched,omitempty"` + ScannedCount uint64 `json:"scannedCount,omitempty"` + MatchedCount uint64 `json:"matchedCount,omitempty"` + RecordsWritten uint64 `json:"recordsWritten,omitempty"` + OutputFilesCount uint64 `json:"outputFilesCount,omitempty"` + ExecutionTime string `json:"executionTime,omitempty"` + NumStarts uint64 `json:"numStarts,omitempty"` + NumErrors uint64 `json:"numErrors,omitempty"` + NumLockLosses uint64 `json:"numLockLosses,omitempty"` + ManifestPath string `json:"manifestPath,omitempty"` + RetryAttempts uint64 `json:"retryAttempts,omitempty"` + LastFailTime time.Time `json:"lastFailTime,omitempty"` + LastFailErrors []string `json:"lastFailErrors,omitempty"` +} + +// GetBucketInventoryJobStatus retrieves the status of an inventory job for a bucket. +// This is a MinIO-specific API and is not compatible with AWS S3. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucket: Name of the bucket +// - id: Unique identifier for the inventory job +// +// Returns the inventory job status including execution state, progress, and error information, or an error if the operation fails. +func (c *Client) GetBucketInventoryJobStatus(ctx context.Context, bucket, id string) (*InventoryJobStatus, error) { + if err := s3utils.CheckValidBucketName(bucket); err != nil { + return nil, err + } + if id == "" { + return nil, errInvalidArgument("inventory ID cannot be empty") + } + reqMeta := makeInventoryReqMetadata(bucket, "id", id, "status", "") + resp, err := c.executeMethod(ctx, http.MethodGet, reqMeta) + defer closeResponse(resp) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, httpRespToErrorResponse(resp, bucket, "") + } + decoder := json.NewDecoder(resp.Body) + var jStatus InventoryJobStatus + err = decoder.Decode(&jStatus) + if err != nil { + return nil, err + } + return &jStatus, nil +} diff --git a/vendor/github.com/minio/minio-go/v7/api-object-legal-hold.go b/vendor/github.com/minio/minio-go/v7/api-object-legal-hold.go index 0c027d550..5ad9a4943 100644 --- a/vendor/github.com/minio/minio-go/v7/api-object-legal-hold.go +++ b/vendor/github.com/minio/minio-go/v7/api-object-legal-hold.go @@ -80,7 +80,16 @@ func newObjectLegalHold(status *LegalHoldStatus) (*objectLegalHold, error) { return legalHold, nil } -// PutObjectLegalHold : sets object legal hold for a given object and versionID. +// PutObjectLegalHold sets the legal hold status for an object and specific version. +// Legal hold prevents an object version from being overwritten or deleted, regardless of retention settings. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - objectName: Name of the object +// - opts: Options including Status (LegalHoldEnabled or LegalHoldDisabled) and optional VersionID +// +// Returns an error if the operation fails or if the status is invalid. func (c *Client) PutObjectLegalHold(ctx context.Context, bucketName, objectName string, opts PutObjectLegalHoldOptions) error { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -134,7 +143,15 @@ func (c *Client) PutObjectLegalHold(ctx context.Context, bucketName, objectName return nil } -// GetObjectLegalHold gets legal-hold status of given object. +// GetObjectLegalHold retrieves the legal hold status for an object and specific version. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - objectName: Name of the object +// - opts: Options including optional VersionID to target a specific version +// +// Returns the legal hold status (LegalHoldEnabled or LegalHoldDisabled) or an error if the operation fails. func (c *Client) GetObjectLegalHold(ctx context.Context, bucketName, objectName string, opts GetObjectLegalHoldOptions) (status *LegalHoldStatus, err error) { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { diff --git a/vendor/github.com/minio/minio-go/v7/api-object-retention.go b/vendor/github.com/minio/minio-go/v7/api-object-retention.go index b29cb1f8d..2efb5d89a 100644 --- a/vendor/github.com/minio/minio-go/v7/api-object-retention.go +++ b/vendor/github.com/minio/minio-go/v7/api-object-retention.go @@ -62,7 +62,16 @@ type PutObjectRetentionOptions struct { VersionID string } -// PutObjectRetention sets object retention for a given object and versionID. +// PutObjectRetention sets the retention configuration for an object and specific version. +// Object retention prevents an object version from being deleted or overwritten for a specified period. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - objectName: Name of the object +// - opts: Options including Mode (GOVERNANCE or COMPLIANCE), RetainUntilDate, optional VersionID, and GovernanceBypass +// +// Returns an error if the operation fails or if the retention settings are invalid. func (c *Client) PutObjectRetention(ctx context.Context, bucketName, objectName string, opts PutObjectRetentionOptions) error { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -125,7 +134,15 @@ func (c *Client) PutObjectRetention(ctx context.Context, bucketName, objectName return nil } -// GetObjectRetention gets retention of given object. +// GetObjectRetention retrieves the retention configuration for an object and specific version. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - objectName: Name of the object +// - versionID: Optional version ID to target a specific version (empty string for current version) +// +// Returns the retention mode (GOVERNANCE or COMPLIANCE), retain-until date, and any error. func (c *Client) GetObjectRetention(ctx context.Context, bucketName, objectName, versionID string) (mode *RetentionMode, retainUntilDate *time.Time, err error) { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { diff --git a/vendor/github.com/minio/minio-go/v7/api-object-tagging.go b/vendor/github.com/minio/minio-go/v7/api-object-tagging.go index 6623e262a..66d131106 100644 --- a/vendor/github.com/minio/minio-go/v7/api-object-tagging.go +++ b/vendor/github.com/minio/minio-go/v7/api-object-tagging.go @@ -40,8 +40,17 @@ type AdvancedObjectTaggingOptions struct { ReplicationProxyRequest string } -// PutObjectTagging replaces or creates object tag(s) and can target -// a specific object version in a versioned bucket. +// PutObjectTagging replaces or creates object tag(s) and can target a specific object version +// in a versioned bucket. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - objectName: Name of the object +// - otags: Tags to apply to the object +// - opts: Options including VersionID to target a specific version +// +// Returns an error if the operation fails. func (c *Client) PutObjectTagging(ctx context.Context, bucketName, objectName string, otags *tags.Tags, opts PutObjectTaggingOptions) error { // Input validation. if err := s3utils.CheckValidBucketName(bucketName); err != nil { @@ -96,8 +105,16 @@ type GetObjectTaggingOptions struct { Internal AdvancedObjectTaggingOptions } -// GetObjectTagging fetches object tag(s) with options to target -// a specific object version in a versioned bucket. +// GetObjectTagging retrieves object tag(s) with options to target a specific object version +// in a versioned bucket. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - objectName: Name of the object +// - opts: Options including VersionID to target a specific version +// +// Returns the object's tags or an error if the operation fails. func (c *Client) GetObjectTagging(ctx context.Context, bucketName, objectName string, opts GetObjectTaggingOptions) (*tags.Tags, error) { // Get resources properly escaped and lined up before // using them in http request. @@ -139,8 +156,16 @@ type RemoveObjectTaggingOptions struct { Internal AdvancedObjectTaggingOptions } -// RemoveObjectTagging removes object tag(s) with options to control a specific object -// version in a versioned bucket +// RemoveObjectTagging removes object tag(s) with options to target a specific object version +// in a versioned bucket. +// +// Parameters: +// - ctx: Context for request cancellation and timeout +// - bucketName: Name of the bucket +// - objectName: Name of the object +// - opts: Options including VersionID to target a specific version +// +// Returns an error if the operation fails. func (c *Client) RemoveObjectTagging(ctx context.Context, bucketName, objectName string, opts RemoveObjectTaggingOptions) error { // Get resources properly escaped and lined up before // using them in http request. diff --git a/vendor/github.com/minio/minio-go/v7/api-prompt-object.go b/vendor/github.com/minio/minio-go/v7/api-prompt-object.go index 26c41d34a..55c038ae0 100644 --- a/vendor/github.com/minio/minio-go/v7/api-prompt-object.go +++ b/vendor/github.com/minio/minio-go/v7/api-prompt-object.go @@ -20,10 +20,10 @@ package minio import ( "bytes" "context" + "encoding/json" "io" "net/http" - "github.com/minio/minio-go/v7/internal/json" "github.com/minio/minio-go/v7/pkg/s3utils" ) diff --git a/vendor/github.com/minio/minio-go/v7/api-put-object-common.go b/vendor/github.com/minio/minio-go/v7/api-put-object-common.go index 9ccb97cbb..52f69563c 100644 --- a/vendor/github.com/minio/minio-go/v7/api-put-object-common.go +++ b/vendor/github.com/minio/minio-go/v7/api-put-object-common.go @@ -31,7 +31,7 @@ const nullVersionID = "null" // Verify if reader is *minio.Object func isObject(reader io.Reader) (ok bool) { _, ok = reader.(*Object) - return + return ok } // Verify if reader is a generic ReaderAt @@ -56,7 +56,7 @@ func isReadAt(reader io.Reader) (ok bool) { } else { _, ok = reader.(io.ReaderAt) } - return + return ok } // OptimalPartInfo - calculate the optimal part info for a given @@ -79,31 +79,31 @@ func OptimalPartInfo(objectSize int64, configuredPartSize uint64) (totalPartsCou // object size is larger than supported maximum. if objectSize > maxMultipartPutObjectSize { err = errEntityTooLarge(objectSize, maxMultipartPutObjectSize, "", "") - return + return totalPartsCount, partSize, lastPartSize, err } var partSizeFlt float64 if configuredPartSize > 0 { if int64(configuredPartSize) > objectSize { err = errEntityTooLarge(int64(configuredPartSize), objectSize, "", "") - return + return totalPartsCount, partSize, lastPartSize, err } if !unknownSize { if objectSize > (int64(configuredPartSize) * maxPartsCount) { err = errInvalidArgument("Part size * max_parts(10000) is lesser than input objectSize.") - return + return totalPartsCount, partSize, lastPartSize, err } } if configuredPartSize < absMinPartSize { err = errInvalidArgument("Input part size is smaller than allowed minimum of 5MiB.") - return + return totalPartsCount, partSize, lastPartSize, err } if configuredPartSize > maxPartSize { err = errInvalidArgument("Input part size is bigger than allowed maximum of 5GiB.") - return + return totalPartsCount, partSize, lastPartSize, err } partSizeFlt = float64(configuredPartSize) diff --git a/vendor/github.com/minio/minio-go/v7/api-put-object-fan-out.go b/vendor/github.com/minio/minio-go/v7/api-put-object-fan-out.go index a6b5149f0..3023b949c 100644 --- a/vendor/github.com/minio/minio-go/v7/api-put-object-fan-out.go +++ b/vendor/github.com/minio/minio-go/v7/api-put-object-fan-out.go @@ -19,6 +19,7 @@ package minio import ( "context" + "encoding/json" "errors" "io" "mime/multipart" @@ -27,7 +28,6 @@ import ( "strings" "time" - "github.com/minio/minio-go/v7/internal/json" "github.com/minio/minio-go/v7/pkg/encrypt" ) diff --git a/vendor/github.com/minio/minio-go/v7/api-put-object-streaming.go b/vendor/github.com/minio/minio-go/v7/api-put-object-streaming.go index db5314d5f..79d0c1dc1 100644 --- a/vendor/github.com/minio/minio-go/v7/api-put-object-streaming.go +++ b/vendor/github.com/minio/minio-go/v7/api-put-object-streaming.go @@ -210,9 +210,13 @@ func (c *Client) putObjectMultipartStreamFromReadAt(ctx context.Context, bucketN } objPart, err := c.uploadPart(ctx, p) if err != nil { - uploadedPartsCh <- uploadedPartRes{ + select { + case <-ctx.Done(): + case uploadedPartsCh <- uploadedPartRes{ Error: err, + }: } + // Exit the goroutine. return } @@ -221,10 +225,13 @@ func (c *Client) putObjectMultipartStreamFromReadAt(ctx context.Context, bucketN uploadReq.Part = objPart // Send successful part info through the channel. - uploadedPartsCh <- uploadedPartRes{ + select { + case <-ctx.Done(): + case uploadedPartsCh <- uploadedPartRes{ Size: objPart.Size, PartNum: uploadReq.PartNum, Part: uploadReq.Part, + }: } } }(partSize) diff --git a/vendor/github.com/minio/minio-go/v7/api-put-object.go b/vendor/github.com/minio/minio-go/v7/api-put-object.go index 877cecb84..80f3d61f3 100644 --- a/vendor/github.com/minio/minio-go/v7/api-put-object.go +++ b/vendor/github.com/minio/minio-go/v7/api-put-object.go @@ -150,7 +150,7 @@ func (opts PutObjectOptions) getNumThreads() (numThreads int) { } else { numThreads = totalWorkers } - return + return numThreads } // Header - constructs the headers from metadata entered by user in @@ -249,7 +249,7 @@ func (opts PutObjectOptions) Header() (header http.Header) { header[k] = v } - return + return header } // validate() checks if the UserMetadata map has standard headers or and raises an error if so. diff --git a/vendor/github.com/minio/minio-go/v7/api-remove.go b/vendor/github.com/minio/minio-go/v7/api-remove.go index 2a38e014a..9794ffb2b 100644 --- a/vendor/github.com/minio/minio-go/v7/api-remove.go +++ b/vendor/github.com/minio/minio-go/v7/api-remove.go @@ -617,12 +617,11 @@ func (c *Client) removeObjects(ctx context.Context, bucketName string, objectsCh customHeader: headers, expect200OKWithError: true, }) - if resp != nil { - if resp.StatusCode != http.StatusOK { - e := httpRespToErrorResponse(resp, bucketName, "") - resultCh <- RemoveObjectResult{ObjectName: "", Err: e} - } + + if resp != nil && resp.StatusCode != http.StatusOK { + err = httpRespToErrorResponse(resp, bucketName, "") } + if err != nil { for _, b := range batch { resultCh <- RemoveObjectResult{ diff --git a/vendor/github.com/minio/minio-go/v7/api.go b/vendor/github.com/minio/minio-go/v7/api.go index 53ef6b85a..5352d793b 100644 --- a/vendor/github.com/minio/minio-go/v7/api.go +++ b/vendor/github.com/minio/minio-go/v7/api.go @@ -21,7 +21,6 @@ import ( "bytes" "context" "encoding/base64" - "encoding/xml" "errors" "fmt" "io" @@ -43,7 +42,6 @@ import ( md5simd "github.com/minio/md5-simd" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/kvcache" - "github.com/minio/minio-go/v7/pkg/peeker" "github.com/minio/minio-go/v7/pkg/s3utils" "github.com/minio/minio-go/v7/pkg/signer" "github.com/minio/minio-go/v7/pkg/singleflight" @@ -162,7 +160,7 @@ type Options struct { // Global constants. const ( libraryName = "minio-go" - libraryVersion = "v7.0.95" + libraryVersion = "v7.0.96" ) // User Agent should always following the below style. @@ -204,7 +202,9 @@ func New(endpoint string, opts *Options) (*Client, error) { return clnt, nil } -// EndpointURL returns the URL of the S3 endpoint. +// EndpointURL returns the URL of the S3-compatible endpoint that this client connects to. +// +// Returns a copy of the endpoint URL to prevent modification of internal state. func (c *Client) EndpointURL() *url.URL { endpoint := *c.endpointURL // copy to prevent callers from modifying internal state return &endpoint @@ -221,7 +221,7 @@ func (r *lockedRandSource) Int63() (n int64) { r.lk.Lock() n = r.src.Int63() r.lk.Unlock() - return + return n } // Seed uses the provided seed value to initialize the generator to a @@ -325,7 +325,14 @@ func privateNew(endpoint string, opts *Options) (*Client, error) { return clnt, nil } -// SetAppInfo - add application details to user agent. +// SetAppInfo adds custom application name and version to the User-Agent header for all requests. +// This helps identify your application in server logs and metrics. +// +// Parameters: +// - appName: Name of the application +// - appVersion: Version of the application +// +// Both parameters must be non-empty for the custom User-Agent to be set. func (c *Client) SetAppInfo(appName, appVersion string) { // if app name and version not set, we do not set a new user agent. if appName != "" && appVersion != "" { @@ -334,7 +341,11 @@ func (c *Client) SetAppInfo(appName, appVersion string) { } } -// TraceOn - enable HTTP tracing. +// TraceOn enables HTTP request and response tracing for debugging purposes. +// All HTTP traffic will be written to the provided output stream. +// +// Parameters: +// - outputStream: Writer where trace output will be written (defaults to os.Stdout if nil) func (c *Client) TraceOn(outputStream io.Writer) { // if outputStream is nil then default to os.Stdout. if outputStream == nil { @@ -347,19 +358,23 @@ func (c *Client) TraceOn(outputStream io.Writer) { c.isTraceEnabled = true } -// TraceErrorsOnlyOn - same as TraceOn, but only errors will be traced. +// TraceErrorsOnlyOn enables HTTP tracing but only for requests that result in errors. +// This is useful for debugging without the overhead of tracing all requests. +// +// Parameters: +// - outputStream: Writer where trace output will be written (defaults to os.Stdout if nil) func (c *Client) TraceErrorsOnlyOn(outputStream io.Writer) { c.TraceOn(outputStream) c.traceErrorsOnly = true } -// TraceErrorsOnlyOff - Turns off the errors only tracing and everything will be traced after this call. -// If all tracing needs to be turned off, call TraceOff(). +// TraceErrorsOnlyOff disables errors-only mode and traces all requests. +// To disable all tracing, call TraceOff() instead. func (c *Client) TraceErrorsOnlyOff() { c.traceErrorsOnly = false } -// TraceOff - disable HTTP tracing. +// TraceOff disables all HTTP tracing (both normal and errors-only modes). func (c *Client) TraceOff() { // Disable tracing. c.isTraceEnabled = false @@ -620,33 +635,11 @@ func (c *Client) do(req *http.Request) (resp *http.Response, err error) { return resp, nil } -// Peek resp.Body looking for S3 XMl error response: -// - Return the error XML bytes if an error is found -// - Make sure to always restablish the whole http response stream before returning -func tryParseErrRespFromBody(resp *http.Response) ([]byte, error) { - peeker := peeker.NewPeekReadCloser(resp.Body, 5*humanize.MiByte) - defer func() { - peeker.ReplayFromStart() - resp.Body = peeker - }() - - errResp := ErrorResponse{} - errBytes, err := xmlDecodeAndBody(peeker, &errResp) - if err != nil { - var unmarshalErr xml.UnmarshalError - if errors.As(err, &unmarshalErr) { - return nil, nil - } - return nil, err - } - return errBytes, nil -} - // List of success status. -var successStatus = []int{ - http.StatusOK, - http.StatusNoContent, - http.StatusPartialContent, +var successStatus = map[int]struct{}{ + http.StatusOK: {}, + http.StatusNoContent: {}, + http.StatusPartialContent: {}, } // executeMethod - instantiates a given method, and retries the @@ -729,29 +722,15 @@ func (c *Client) executeMethod(ctx context.Context, method string, metadata requ return nil, err } - var success bool - var errBodyBytes []byte - - for _, httpStatus := range successStatus { - if httpStatus == res.StatusCode { - success = true - break - } - } - - if success { - if !metadata.expect200OKWithError { - return res, nil - } - errBodyBytes, err = tryParseErrRespFromBody(res) - if err == nil && len(errBodyBytes) == 0 { - // No S3 XML error is found - return res, nil - } - } else { - errBodyBytes, err = io.ReadAll(res.Body) - } + _, success := successStatus[res.StatusCode] + if success && !metadata.expect200OKWithError { + // We do not expect 2xx to return an error return. + return res, nil + } // in all other situations we must first parse the body as ErrorResponse + // 5MiB is sufficiently large enough to hold any error or regular XML response. + var bodyBytes []byte + bodyBytes, err = io.ReadAll(io.LimitReader(res.Body, 5*humanize.MiByte)) // By now, res.Body should be closed closeResponse(res) if err != nil { @@ -759,16 +738,22 @@ func (c *Client) executeMethod(ctx context.Context, method string, metadata requ } // Save the body. - errBodySeeker := bytes.NewReader(errBodyBytes) - res.Body = io.NopCloser(errBodySeeker) + bodySeeker := bytes.NewReader(bodyBytes) + res.Body = io.NopCloser(bodySeeker) - // For errors verify if its retryable otherwise fail quickly. - errResponse := ToErrorResponse(httpRespToErrorResponse(res, metadata.bucketName, metadata.objectName)) - err = errResponse + apiErr := httpRespToErrorResponse(res, metadata.bucketName, metadata.objectName) // Save the body back again. - errBodySeeker.Seek(0, 0) // Seek back to starting point. - res.Body = io.NopCloser(errBodySeeker) + bodySeeker.Seek(0, 0) // Seek back to starting point. + res.Body = io.NopCloser(bodySeeker) + + if apiErr == nil { + return res, nil + } + + // For errors verify if its retryable otherwise fail quickly. + errResponse := ToErrorResponse(apiErr) + err = errResponse // Bucket region if set in error response and the error // code dictates invalid region, we can retry the request diff --git a/vendor/github.com/minio/minio-go/v7/bucket-cache.go b/vendor/github.com/minio/minio-go/v7/bucket-cache.go index b41902f65..a37a72ae8 100644 --- a/vendor/github.com/minio/minio-go/v7/bucket-cache.go +++ b/vendor/github.com/minio/minio-go/v7/bucket-cache.go @@ -142,7 +142,7 @@ func (c *Client) getBucketLocationRequest(ctx context.Context, bucketName string if h, p, err := net.SplitHostPort(targetURL.Host); err == nil { if targetURL.Scheme == "http" && p == "80" || targetURL.Scheme == "https" && p == "443" { targetURL.Host = h - if ip := net.ParseIP(h); ip != nil && ip.To16() != nil { + if ip := net.ParseIP(h); ip != nil && ip.To4() == nil { targetURL.Host = "[" + h + "]" } } diff --git a/vendor/github.com/minio/minio-go/v7/checksum.go b/vendor/github.com/minio/minio-go/v7/checksum.go index 0691c1fbb..0feb89bf8 100644 --- a/vendor/github.com/minio/minio-go/v7/checksum.go +++ b/vendor/github.com/minio/minio-go/v7/checksum.go @@ -24,13 +24,13 @@ import ( "encoding/binary" "errors" "hash" - "hash/crc32" "io" "math/bits" "net/http" "sort" "strings" + "github.com/klauspost/crc32" "github.com/minio/crc64nvme" ) diff --git a/vendor/github.com/minio/minio-go/v7/code_of_conduct.md b/vendor/github.com/minio/minio-go/v7/code_of_conduct.md index cb232c3c6..7dcdbfc3e 100644 --- a/vendor/github.com/minio/minio-go/v7/code_of_conduct.md +++ b/vendor/github.com/minio/minio-go/v7/code_of_conduct.md @@ -1,80 +1,52 @@ -# Contributor Covenant Code of Conduct +Contributor Covenant Code of Conduct +==================================== -## Our Pledge +Our Pledge +---------- -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. -## Our Standards +Our Standards +------------- -Examples of behavior that contributes to creating a positive environment -include: +Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior, in compliance with the -licensing terms applying to the Project developments. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. However, these actions shall respect the -licensing terms of the Project Developments that will always supersede such -Code of Conduct. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at dev@min.io. The project team -will review and investigate all complaints, and will respond in a way that it deems -appropriate to the circumstances. The project team is obligated to maintain -confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -This version includes a clarification to ensure that the code of conduct is in -compliance with the free software licensing terms of the project. - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +Our Responsibilities +-------------------- + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior, in compliance with the licensing terms applying to the Project developments. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. However, these actions shall respect the licensing terms of the Project Developments that will always supersede such Code of Conduct. + +Scope +----- + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +Enforcement +----------- + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dev@min.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +Attribution +----------- + +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.4, available at [http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4/) + +This version includes a clarification to ensure that the code of conduct is in compliance with the free software licensing terms of the project. diff --git a/vendor/github.com/minio/minio-go/v7/create-session.go b/vendor/github.com/minio/minio-go/v7/create-session.go index 676ad21d1..47c286564 100644 --- a/vendor/github.com/minio/minio-go/v7/create-session.go +++ b/vendor/github.com/minio/minio-go/v7/create-session.go @@ -114,7 +114,7 @@ func (c *Client) createSessionRequest(ctx context.Context, bucketName string, se if h, p, err := net.SplitHostPort(host); err == nil { if targetURL.Scheme == "http" && p == "80" || targetURL.Scheme == "https" && p == "443" { host = h - if ip := net.ParseIP(h); ip != nil && ip.To16() != nil { + if ip := net.ParseIP(h); ip != nil && ip.To4() == nil { host = "[" + h + "]" } } diff --git a/vendor/github.com/minio/minio-go/v7/endpoints.go b/vendor/github.com/minio/minio-go/v7/endpoints.go index 00f95d1b5..34b340b39 100644 --- a/vendor/github.com/minio/minio-go/v7/endpoints.go +++ b/vendor/github.com/minio/minio-go/v7/endpoints.go @@ -240,6 +240,10 @@ var awsS3EndpointMap = map[string]awsS3Endpoint{ "s3.mx-central-1.amazonaws.com", "s3.dualstack.mx-central-1.amazonaws.com", }, + "ap-east-2": { + "s3.ap-east-2.amazonaws.com", + "s3.dualstack.ap-east-2.amazonaws.com", + }, } // getS3ExpressEndpoint get Amazon S3 Express endpoing based on the region diff --git a/vendor/github.com/minio/minio-go/v7/functional_tests.go b/vendor/github.com/minio/minio-go/v7/functional_tests.go index 3ade9a6af..4f8f9dd8c 100644 --- a/vendor/github.com/minio/minio-go/v7/functional_tests.go +++ b/vendor/github.com/minio/minio-go/v7/functional_tests.go @@ -88,7 +88,7 @@ func createHTTPTransport() (transport *http.Transport) { transport.TLSClientConfig.InsecureSkipVerify = true } - return + return transport } var readFull = func(r io.Reader, buf []byte) (n int, err error) { @@ -123,7 +123,7 @@ var readFull = func(r io.Reader, buf []byte) (n int, err error) { } else if n > 0 && err == io.EOF { err = io.ErrUnexpectedEOF } - return + return n, err } func baseLogger(testName, function string, args map[string]interface{}, startTime time.Time) *slog.Logger { @@ -282,7 +282,7 @@ var mintDataDir = os.Getenv("MINT_DATA_DIR") func getMintDataDirFilePath(filename string) (fp string) { if mintDataDir == "" { - return + return fp } return filepath.Join(mintDataDir, filename) } @@ -2075,6 +2075,9 @@ func testPutObjectWithChecksums() { cmpChecksum(resp.ChecksumCRC32, meta["x-amz-checksum-crc32"]) cmpChecksum(resp.ChecksumCRC32C, meta["x-amz-checksum-crc32c"]) cmpChecksum(resp.ChecksumCRC64NVME, meta["x-amz-checksum-crc64nvme"]) + if resp.ChecksumMode != minio.ChecksumFullObjectMode.String() { + logError(testName, function, args, startTime, "", "Checksum mode is not full object", fmt.Errorf("got %s, want %s", resp.ChecksumMode, minio.ChecksumFullObjectMode.String())) + } // Read the data back gopts := minio.GetObjectOptions{Checksum: true} @@ -2095,6 +2098,9 @@ func testPutObjectWithChecksums() { cmpChecksum(st.ChecksumCRC32, meta["x-amz-checksum-crc32"]) cmpChecksum(st.ChecksumCRC32C, meta["x-amz-checksum-crc32c"]) cmpChecksum(st.ChecksumCRC64NVME, meta["x-amz-checksum-crc64nvme"]) + if st.ChecksumMode != minio.ChecksumFullObjectMode.String() { + logError(testName, function, args, startTime, "", "Checksum mode is not full object", fmt.Errorf("got %s, want %s", st.ChecksumMode, minio.ChecksumFullObjectMode.String())) + } if st.Size != int64(bufSize) { logError(testName, function, args, startTime, "", "Number of bytes returned by PutObject does not match GetObject, expected "+string(bufSize)+" got "+string(st.Size), err) @@ -2435,7 +2441,10 @@ func testPutMultipartObjectWithChecksums() { reader.Close() h := test.cs.Hasher() h.Reset() - want := hashMultiPart(b, partSize, test.cs) + // wantChksm might be the full object checksum or the multipart checksum, depending on the test.cs type. + wantChksm := hashMultiPart(b, partSize, test.cs) + // wantFullObjectChksm is always the full object checksum that is returned after CopyObject. + wantFullObjectChksm := hashMultiPart(b, len(b), test.cs) rd := bytes.NewReader(b) cs := test.cs @@ -2456,15 +2465,15 @@ func testPutMultipartObjectWithChecksums() { switch test.cs.Base() { case minio.ChecksumCRC32C: - cmpChecksum(resp.ChecksumCRC32C, want) + cmpChecksum(resp.ChecksumCRC32C, wantChksm) case minio.ChecksumCRC32: - cmpChecksum(resp.ChecksumCRC32, want) + cmpChecksum(resp.ChecksumCRC32, wantChksm) case minio.ChecksumSHA1: - cmpChecksum(resp.ChecksumSHA1, want) + cmpChecksum(resp.ChecksumSHA1, wantChksm) case minio.ChecksumSHA256: - cmpChecksum(resp.ChecksumSHA256, want) + cmpChecksum(resp.ChecksumSHA256, wantChksm) case minio.ChecksumCRC64NVME: - cmpChecksum(resp.ChecksumCRC64NVME, want) + cmpChecksum(resp.ChecksumCRC64NVME, wantChksm) } args["section"] = "HeadObject" @@ -2475,15 +2484,51 @@ func testPutMultipartObjectWithChecksums() { } switch test.cs.Base() { case minio.ChecksumCRC32C: - cmpChecksum(st.ChecksumCRC32C, want) + cmpChecksum(st.ChecksumCRC32C, wantChksm) + case minio.ChecksumCRC32: + cmpChecksum(st.ChecksumCRC32, wantChksm) + case minio.ChecksumSHA1: + cmpChecksum(st.ChecksumSHA1, wantChksm) + case minio.ChecksumSHA256: + cmpChecksum(st.ChecksumSHA256, wantChksm) + case minio.ChecksumCRC64NVME: + cmpChecksum(st.ChecksumCRC64NVME, wantChksm) + } + + // Use the CopyObject API to make a copy, in the case it was a composite checksum, + // it will change because the copy is no longer a multipart object. S3 returns the checksum + // of the full object when HeadObject is called on the copy. + args["section"] = "CopyObject" + objectCopyName := objectName + "-copy" + _, err = c.CopyObject(context.Background(), minio.CopyDestOptions{ + Bucket: bucketName, + Object: objectCopyName, + }, minio.CopySrcOptions{ + Bucket: bucketName, + Object: objectName, + }) + if err != nil { + logError(testName, function, args, startTime, "", "CopyObject failed", err) + return + } + + args["section"] = "HeadObject-Copy" + st, err = c.StatObject(context.Background(), bucketName, objectCopyName, minio.StatObjectOptions{Checksum: true}) + if err != nil { + logError(testName, function, args, startTime, "", "StatObject failed", err) + return + } + switch test.cs.Base() { + case minio.ChecksumCRC32C: + cmpChecksum(st.ChecksumCRC32C, wantFullObjectChksm) case minio.ChecksumCRC32: - cmpChecksum(st.ChecksumCRC32, want) + cmpChecksum(st.ChecksumCRC32, wantFullObjectChksm) case minio.ChecksumSHA1: - cmpChecksum(st.ChecksumSHA1, want) + cmpChecksum(st.ChecksumSHA1, wantFullObjectChksm) case minio.ChecksumSHA256: - cmpChecksum(st.ChecksumSHA256, want) + cmpChecksum(st.ChecksumSHA256, wantFullObjectChksm) case minio.ChecksumCRC64NVME: - cmpChecksum(st.ChecksumCRC64NVME, want) + cmpChecksum(st.ChecksumCRC64NVME, wantFullObjectChksm) } args["section"] = "GetObjectAttributes" @@ -2493,19 +2538,19 @@ func testPutMultipartObjectWithChecksums() { return } - if strings.ContainsRune(want, '-') { - want = want[:strings.IndexByte(want, '-')] + if strings.ContainsRune(wantChksm, '-') { + wantChksm = wantChksm[:strings.IndexByte(wantChksm, '-')] } switch test.cs { // Full Object CRC does not return anything with GetObjectAttributes case minio.ChecksumCRC32C: - cmpChecksum(s.Checksum.ChecksumCRC32C, want) + cmpChecksum(s.Checksum.ChecksumCRC32C, wantChksm) case minio.ChecksumCRC32: - cmpChecksum(s.Checksum.ChecksumCRC32, want) + cmpChecksum(s.Checksum.ChecksumCRC32, wantChksm) case minio.ChecksumSHA1: - cmpChecksum(s.Checksum.ChecksumSHA1, want) + cmpChecksum(s.Checksum.ChecksumSHA1, wantChksm) case minio.ChecksumSHA256: - cmpChecksum(s.Checksum.ChecksumSHA256, want) + cmpChecksum(s.Checksum.ChecksumSHA256, wantChksm) } // Read the data back @@ -2529,22 +2574,22 @@ func testPutMultipartObjectWithChecksums() { // Test part 2 checksum... h.Reset() h.Write(b[partSize : 2*partSize]) - want = base64.StdEncoding.EncodeToString(h.Sum(nil)) + wantChksm = base64.StdEncoding.EncodeToString(h.Sum(nil)) switch test.cs { // Full Object CRC does not return any part CRC for whatever reason. case minio.ChecksumCRC32C: - cmpChecksum(st.ChecksumCRC32C, want) + cmpChecksum(st.ChecksumCRC32C, wantChksm) case minio.ChecksumCRC32: - cmpChecksum(st.ChecksumCRC32, want) + cmpChecksum(st.ChecksumCRC32, wantChksm) case minio.ChecksumSHA1: - cmpChecksum(st.ChecksumSHA1, want) + cmpChecksum(st.ChecksumSHA1, wantChksm) case minio.ChecksumSHA256: - cmpChecksum(st.ChecksumSHA256, want) + cmpChecksum(st.ChecksumSHA256, wantChksm) case minio.ChecksumCRC64NVME: // AWS doesn't return part checksum, but may in the future. if st.ChecksumCRC64NVME != "" { - cmpChecksum(st.ChecksumCRC64NVME, want) + cmpChecksum(st.ChecksumCRC64NVME, wantChksm) } } @@ -3321,7 +3366,7 @@ func validateObjectAttributeRequest(OA *minio.ObjectAttributes, opts *minio.Obje if opts.VersionID != "" { if OA.VersionID != opts.VersionID { err = fmt.Errorf("Expected versionId %s but got versionId %s", opts.VersionID, OA.VersionID) - return + return err } } @@ -3348,12 +3393,12 @@ func validateObjectAttributeRequest(OA *minio.ObjectAttributes, opts *minio.Obje if test.HasPartChecksums { if partsMissingChecksum { err = fmt.Errorf("One or all parts were missing a checksum") - return + return err } } else { if foundPartChecksum { err = fmt.Errorf("Did not expect ObjectParts to have checksums but found one") - return + return err } } @@ -3365,52 +3410,52 @@ func validateObjectAttributeRequest(OA *minio.ObjectAttributes, opts *minio.Obje if test.HasFullChecksum { if !hasFullObjectChecksum { err = fmt.Errorf("Full object checksum not found") - return + return err } } else { if hasFullObjectChecksum { err = fmt.Errorf("Did not expect a full object checksum but we got one") - return + return err } } if OA.ETag != test.ETag { err = fmt.Errorf("Etags do not match, got %s but expected %s", OA.ETag, test.ETag) - return + return err } if test.HasParts { if len(OA.ObjectParts.Parts) < 1 { err = fmt.Errorf("Was expecting ObjectParts but none were present") - return + return err } } if OA.StorageClass == "" { err = fmt.Errorf("Was expecting a StorageClass but got none") - return + return err } if OA.ObjectSize != test.ObjectSize { err = fmt.Errorf("Was expecting a ObjectSize but got none") - return + return err } if test.HasParts { if opts.MaxParts == 0 { if len(OA.ObjectParts.Parts) != OA.ObjectParts.PartsCount { err = fmt.Errorf("expected %s parts but got %d", OA.ObjectParts.PartsCount, len(OA.ObjectParts.Parts)) - return + return err } } else if (opts.MaxParts + opts.PartNumberMarker) > OA.ObjectParts.PartsCount { if len(OA.ObjectParts.Parts) != (OA.ObjectParts.PartsCount - opts.PartNumberMarker) { err = fmt.Errorf("expected %d parts but got %d", (OA.ObjectParts.PartsCount - opts.PartNumberMarker), len(OA.ObjectParts.Parts)) - return + return err } } else if opts.MaxParts != 0 { if opts.MaxParts != len(OA.ObjectParts.Parts) { err = fmt.Errorf("expected %d parts but got %d", opts.MaxParts, len(OA.ObjectParts.Parts)) - return + return err } } } @@ -3418,18 +3463,18 @@ func validateObjectAttributeRequest(OA *minio.ObjectAttributes, opts *minio.Obje if OA.ObjectParts.NextPartNumberMarker == OA.ObjectParts.PartsCount { if OA.ObjectParts.IsTruncated { err = fmt.Errorf("Expected ObjectParts to NOT be truncated, but it was") - return + return err } } if OA.ObjectParts.NextPartNumberMarker != OA.ObjectParts.PartsCount { if !OA.ObjectParts.IsTruncated { err = fmt.Errorf("Expected ObjectParts to be truncated, but it was NOT") - return + return err } } - return + return err } // Test PutObject using a large data to trigger multipart readat @@ -3645,6 +3690,93 @@ func testPutObjectStreaming() { logSuccess(testName, function, args, startTime) } +// Test PutObject with preconditions on non-existent objects +func testPutObjectPreconditionOnNonExistent() { + startTime := time.Now() + testName := getFuncName() + function := "PutObject(bucketName, objectName, reader, size, opts) with preconditions" + args := map[string]interface{}{ + "bucketName": "", + "objectName": "", + "opts": "minio.PutObjectOptions{SetMatchETag/SetMatchETagExcept}", + } + + c, err := NewClient(ClientConfig{}) + if err != nil { + logError(testName, function, args, startTime, "", "MinIO client object creation failed", err) + return + } + + bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-") + args["bucketName"] = bucketName + + err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"}) + if err != nil { + logError(testName, function, args, startTime, "", "MakeBucket failed", err) + return + } + + defer cleanupBucket(bucketName, c) + + // Test 1: PutObject with SetMatchETag on non-existent object should fail + objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "test-object-") + args["objectName"] = objectName + + data := bytes.NewReader([]byte("test data")) + + opts := minio.PutObjectOptions{} + opts.SetMatchETag("some-etag") + + _, err = c.PutObject(context.Background(), bucketName, objectName, data, int64(data.Len()), opts) + if err == nil { + logError(testName, function, args, startTime, "", "PutObject with SetMatchETag on non-existent object should have failed", nil) + return + } + + errResp := minio.ToErrorResponse(err) + if errResp.Code != "NoSuchKey" { + logError(testName, function, args, startTime, "", fmt.Sprintf("Expected NoSuchKey error (AWS standard for non-existent objects), got %s", errResp.Code), err) + return + } + + // Test 2: PutObject with SetMatchETagExcept (If-None-Match) on non-existent object should succeed + objectName2 := randString(60, rand.NewSource(time.Now().UnixNano()), "test-object2-") + args["objectName"] = objectName2 + + data2 := bytes.NewReader([]byte("test data 2")) + opts2 := minio.PutObjectOptions{} + opts2.SetMatchETagExcept("some-etag") + + _, err = c.PutObject(context.Background(), bucketName, objectName2, data2, int64(data2.Len()), opts2) + if err != nil { + logError(testName, function, args, startTime, "", "PutObject with SetMatchETagExcept (If-None-Match) on non-existent object should have succeeded", err) + return + } + // Test 3: CompleteMultipartUpload with preconditions on non-existent object should fail + objectName3 := randString(60, rand.NewSource(time.Now().UnixNano()), "test-multipart-") + args["objectName"] = objectName3 + + data3 := bytes.Repeat([]byte("a"), 5*1024*1024+1) + reader3 := bytes.NewReader(data3) + + opts3 := minio.PutObjectOptions{} + opts3.SetMatchETag("non-existent-etag") + + _, err = c.PutObject(context.Background(), bucketName, objectName3, reader3, int64(len(data3)), opts3) + if err == nil { + logError(testName, function, args, startTime, "", "CompleteMultipartUpload with SetMatchETag on non-existent object should have failed", nil) + return + } + + errResp = minio.ToErrorResponse(err) + if errResp.Code != "NoSuchKey" { + logError(testName, function, args, startTime, "", fmt.Sprintf("Expected NoSuchKey error (AWS standard for non-existent objects) for multipart, got %s", errResp.Code), err) + return + } + + logSuccess(testName, function, args, startTime) +} + // Test get object seeker from the end, using whence set to '2'. func testGetObjectSeekEnd() { // initialize logging params @@ -8653,6 +8785,7 @@ func testCopyObjectV2() { logError(testName, function, args, startTime, "", "GetObject failed", err) return } + // Check the various fields of source object against destination object. objInfo, err = r.Stat() if err != nil { @@ -8691,6 +8824,260 @@ func testCopyObjectV2() { logSuccess(testName, function, args, startTime) } +// Tests copy object with various checksum scenarios, tries to not repeat CopyObjectV2 test and +// instead just focus on Checksum. +func testCopyObjectWithChecksums() { + startTime := time.Now() + testName := getFuncName() + function := "CopyObjectWithChecksums(destination, source)" + args := map[string]interface{}{} + + c, err := NewClient(ClientConfig{CredsV2: true}) + if err != nil { + logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) + return + } + + // Generate a new random bucket name. + bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-") + + // Make a new bucket in 'us-east-1' (source bucket). + err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"}) + if err != nil { + logError(testName, function, args, startTime, "", "MakeBucket failed", err) + return + } + defer cleanupBucket(bucketName, c) + + // Make a new bucket in 'us-east-1' (destination bucket). + err = c.MakeBucket(context.Background(), bucketName+"-copy", minio.MakeBucketOptions{Region: "us-east-1"}) + if err != nil { + logError(testName, function, args, startTime, "", "MakeBucket failed", err) + return + } + defer cleanupBucket(bucketName+"-copy", c) + + // Generate 33K of data. + bufSize := dataFileMap["datafile-33-kB"] + reader := getDataReader("datafile-33-kB") + defer reader.Close() + + // PutObject to upload the object to the bucket, this object will have a Crc64NVME checksum applied + // by default since nothing was explicitly specified. + objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "") + _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"}) + if err != nil { + logError(testName, function, args, startTime, "", "PutObject failed", err) + return + } + // GetObject to obtain the eTag + r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{}) + if err != nil { + logError(testName, function, args, startTime, "", "GetObject failed", err) + return + } + objInfo, err := r.Stat() + if err != nil { + logError(testName, function, args, startTime, "", "Stat failed", err) + return + } + r.Close() + + // Copy source options + src := minio.CopySrcOptions{ + Bucket: bucketName, + Object: objectName, + MatchModifiedSince: time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC), + MatchETag: objInfo.ETag, + } + + tests := []struct { + csType minio.ChecksumType + cs wantChecksums + }{ + {csType: minio.ChecksumCRC64NVME, cs: wantChecksums{minio.ChecksumCRC64NVME: "iRtfQH3xflQ="}}, + {csType: minio.ChecksumCRC32C, cs: wantChecksums{minio.ChecksumCRC32C: "aHnJMw=="}}, + {csType: minio.ChecksumCRC32, cs: wantChecksums{minio.ChecksumCRC32: "tIZ8hA=="}}, + {csType: minio.ChecksumSHA1, cs: wantChecksums{minio.ChecksumSHA1: "6YIIbcWH1iLaCFqs5vwq5Rwvm+o="}}, + {csType: minio.ChecksumSHA256, cs: wantChecksums{minio.ChecksumSHA256: "GKeJTopbMGPs3h4fAw4oe0R2QnnmFVJeIWkqCkp28Yo="}}, + // In S3, all copied objects without checksums and specified destination checksum algorithms + // automatically gain a CRC-64NVME checksum algorithm. Use ChecksumNone for this case. + {csType: minio.ChecksumNone, cs: wantChecksums{minio.ChecksumCRC64NVME: "iRtfQH3xflQ="}}, + } + + for _, test := range tests { + args := map[string]interface{}{} + args["srcOpts"] = src + args["section"] = "setup" + args["checksum"] = test.csType.String() + + // Copy destination options + bucketCopyName := bucketName + "-copy" + objectCopyName := objectName + "-copy-" + test.csType.String() + dst := minio.CopyDestOptions{ + Bucket: bucketCopyName, + Object: objectCopyName, + ReplaceMetadata: true, + } + if test.csType != minio.ChecksumNone { + // Request the server-side checksum on the copy. + // ChecksumNone is a flag to leave off the header + dst.ChecksumType = test.csType + } + args["destOpts"] = dst + + // Perform the Copy + args["section"] = "CopyObject" + _, err = c.CopyObject(context.Background(), dst, src) + if err != nil { + logError(testName, function, args, startTime, "", "CopyObject failed", err) + return + } + + // Checksum verification + args["section"] = "HeadObject" + st, err := c.StatObject(context.Background(), bucketCopyName, objectCopyName, minio.StatObjectOptions{Checksum: true}) + if err != nil { + logError(testName, function, args, startTime, "", "StatObject failed", err) + return + } + if st.ChecksumMode != "FULL_OBJECT" { + logError(testName, function, args, startTime, "", "ChecksumMode want: FULL_OBJECT, got "+st.ChecksumMode, nil) + return + } + err = cmpChecksum(st, test.cs) + if err != nil { + logError(testName, function, args, startTime, "", "Checksum mismatch", err) + return + } + + logSuccess(testName, function, args, startTime) + } +} + +// Tests replacing an object with CopyObject and a new Checksum type +func testReplaceObjectWithChecksums() { + startTime := time.Now() + testName := getFuncName() + function := "CopyObjectWithChecksums(destination, source)" + args := map[string]interface{}{} + + c, err := NewClient(ClientConfig{CredsV2: true}) + if err != nil { + logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) + return + } + + // Generate a new random bucket name. + bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-") + + // Make a new bucket in 'us-east-1' (source bucket). + err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"}) + if err != nil { + logError(testName, function, args, startTime, "", "MakeBucket failed", err) + return + } + defer cleanupBucket(bucketName, c) + + tests := []struct { + csType minio.ChecksumType + cs wantChecksums + }{ + {csType: minio.ChecksumCRC64NVME, cs: wantChecksums{minio.ChecksumCRC64NVME: "iRtfQH3xflQ="}}, + {csType: minio.ChecksumCRC32C, cs: wantChecksums{minio.ChecksumCRC32C: "aHnJMw=="}}, + {csType: minio.ChecksumCRC32, cs: wantChecksums{minio.ChecksumCRC32: "tIZ8hA=="}}, + {csType: minio.ChecksumSHA1, cs: wantChecksums{minio.ChecksumSHA1: "6YIIbcWH1iLaCFqs5vwq5Rwvm+o="}}, + {csType: minio.ChecksumSHA256, cs: wantChecksums{minio.ChecksumSHA256: "GKeJTopbMGPs3h4fAw4oe0R2QnnmFVJeIWkqCkp28Yo="}}, + // In S3, all copied objects without checksums and specified destination checksum algorithms + // automatically gain a CRC-64NVME checksum algorithm. Use ChecksumNone for this case. + {csType: minio.ChecksumNone, cs: wantChecksums{minio.ChecksumCRC64NVME: "iRtfQH3xflQ="}}, + } + + for _, test := range tests { + args := map[string]interface{}{} + args["section"] = "setup" + args["destOpts"] = "" + args["checksum"] = test.csType.String() + + bufSize := dataFileMap["datafile-33-kB"] + reader := getDataReader("datafile-33-kB") + defer reader.Close() + + // PutObject to upload the object to the bucket + objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "") + _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{ContentType: "binary/octet-stream"}) + if err != nil { + logError(testName, function, args, startTime, "", "PutObject failed", err) + return + } + // GetObject to obtain the eTag + r, err := c.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{}) + if err != nil { + logError(testName, function, args, startTime, "", "GetObject failed", err) + return + } + objInfo, err := r.Stat() + if err != nil { + logError(testName, function, args, startTime, "", "Stat failed", err) + return + } + r.Close() + + // Copy source options + src := minio.CopySrcOptions{ + Bucket: bucketName, + Object: objectName, + MatchModifiedSince: time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC), + MatchETag: objInfo.ETag, + } + + // Copy destination options, overwrite the existing object + dst := minio.CopyDestOptions{ + Bucket: bucketName, + Object: objectName, + // S3 requires that we send some new metadata otherwise it complains that the + // CopyObject is illegal. + UserMetadata: map[string]string{ + "TestMeta": objectName + "-meta-" + test.csType.String(), + }, + ReplaceMetadata: true, + } + if test.csType != minio.ChecksumNone { + // Request the server-side checksum on the copy. + // ChecksumNone is a flag to leave off the header + dst.ChecksumType = test.csType + } + args["destOpts"] = dst + + // Perform the Copy + args["section"] = "CopyObject" + _, err = c.CopyObject(context.Background(), dst, src) + if err != nil { + logError(testName, function, args, startTime, "", "CopyObject failed", err) + return + } + + // Checksum verification + args["section"] = "HeadObject" + st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{Checksum: true}) + if err != nil { + logError(testName, function, args, startTime, "", "StatObject failed", err) + return + } + if st.ChecksumMode != "FULL_OBJECT" { + logError(testName, function, args, startTime, "", "ChecksumMode want: FULL_OBJECT, got "+st.ChecksumMode, nil) + return + } + err = cmpChecksum(st, test.cs) + if err != nil { + logError(testName, function, args, startTime, "", "Checksum mismatch", err) + return + } + + logSuccess(testName, function, args, startTime) + } +} + func testComposeObjectErrorCasesWrapper(c *minio.Client) { // initialize logging params startTime := time.Now() @@ -8976,6 +9363,7 @@ func testEncryptedCopyObjectWrapper(c *minio.Client, bucketName string, sseSrc, testName := getFuncNameLoc(2) function := "CopyObject(destination, source)" args := map[string]interface{}{} + args["testName"] = testName var srcEncryption, dstEncryption encrypt.ServerSide // Make a new bucket in 'us-east-1' (source bucket). @@ -8990,8 +9378,19 @@ func testEncryptedCopyObjectWrapper(c *minio.Client, bucketName string, sseSrc, // 1. create an sse-c encrypted object to copy by uploading const srcSize = 1024 * 1024 buf := bytes.Repeat([]byte("abcde"), srcSize) // gives a buffer of 5MiB + + // Calculate the CRC32C checksum for the object + meta := map[string]string{} + h := minio.ChecksumCRC32C.Hasher() + h.Reset() + h.Write(buf) + meta[minio.ChecksumCRC32C.Key()] = base64.StdEncoding.EncodeToString(h.Sum(nil)) + _, err = c.PutObject(context.Background(), bucketName, "srcObject", bytes.NewReader(buf), int64(len(buf)), minio.PutObjectOptions{ ServerSideEncryption: sseSrc, + DisableMultipart: true, + DisableContentSha256: true, + UserMetadata: meta, }) if err != nil { logError(testName, function, args, startTime, "", "PutObject call failed", err) @@ -9028,7 +9427,7 @@ func testEncryptedCopyObjectWrapper(c *minio.Client, bucketName string, sseSrc, } // 3. get copied object and check if content is equal coreClient := minio.Core{Client: c} - reader, _, _, err := coreClient.GetObject(context.Background(), bucketName, "dstObject", minio.GetObjectOptions{ServerSideEncryption: dstEncryption}) + reader, oi, _, err := coreClient.GetObject(context.Background(), bucketName, "dstObject", minio.GetObjectOptions{ServerSideEncryption: dstEncryption, Checksum: true}) if err != nil { logError(testName, function, args, startTime, "", "GetObject failed", err) return @@ -9045,6 +9444,12 @@ func testEncryptedCopyObjectWrapper(c *minio.Client, bucketName string, sseSrc, } reader.Close() + err = cmpChecksum(oi, wantChecksums{minio.ChecksumCRC32C: "bSoobA=="}) + if err != nil { + logError(testName, function, args, startTime, "", "Checksum mismatch on dstObject", err) + return + } + // Test key rotation for source object in-place. var newSSE encrypt.ServerSide if sseSrc != nil && sseSrc.Type() == encrypt.SSEC { @@ -9068,7 +9473,7 @@ func testEncryptedCopyObjectWrapper(c *minio.Client, bucketName string, sseSrc, } // Get copied object and check if content is equal - reader, _, _, err = coreClient.GetObject(context.Background(), bucketName, "srcObject", minio.GetObjectOptions{ServerSideEncryption: newSSE}) + reader, oi, _, err = coreClient.GetObject(context.Background(), bucketName, "srcObject", minio.GetObjectOptions{ServerSideEncryption: newSSE, Checksum: true}) if err != nil { logError(testName, function, args, startTime, "", "GetObject failed", err) return @@ -9085,6 +9490,13 @@ func testEncryptedCopyObjectWrapper(c *minio.Client, bucketName string, sseSrc, } reader.Close() + err = cmpChecksum(oi, wantChecksums{minio.ChecksumCRC32C: "bSoobA=="}) + if err != nil { + fmt.Printf("srcObject objectInfo: %+v\n", oi) + logError(testName, function, args, startTime, "", "Checksum mismatch on srcObject for in-place", err) + return + } + // Test in-place decryption. dst = minio.CopyDestOptions{ Bucket: bucketName, @@ -9106,7 +9518,7 @@ func testEncryptedCopyObjectWrapper(c *minio.Client, bucketName string, sseSrc, } // Get copied decrypted object and check if content is equal - reader, _, _, err = coreClient.GetObject(context.Background(), bucketName, "srcObject", minio.GetObjectOptions{}) + reader, oi, _, err = coreClient.GetObject(context.Background(), bucketName, "srcObject", minio.GetObjectOptions{Checksum: true}) if err != nil { logError(testName, function, args, startTime, "", "GetObject failed", err) return @@ -9123,6 +9535,12 @@ func testEncryptedCopyObjectWrapper(c *minio.Client, bucketName string, sseSrc, return } + err = cmpChecksum(oi, wantChecksums{minio.ChecksumCRC32C: "bSoobA=="}) + if err != nil { + logError(testName, function, args, startTime, "", "Checksum mismatch for decrypted object", err) + return + } + logSuccess(testName, function, args, startTime) } @@ -9134,7 +9552,7 @@ func testUnencryptedToSSECCopyObject() { function := "CopyObject(destination, source)" args := map[string]interface{}{} - c, err := NewClient(ClientConfig{}) + c, err := NewClient(ClientConfig{TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) return @@ -9154,7 +9572,7 @@ func testUnencryptedToSSES3CopyObject() { function := "CopyObject(destination, source)" args := map[string]interface{}{} - c, err := NewClient(ClientConfig{}) + c, err := NewClient(ClientConfig{TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) return @@ -9175,7 +9593,7 @@ func testUnencryptedToUnencryptedCopyObject() { function := "CopyObject(destination, source)" args := map[string]interface{}{} - c, err := NewClient(ClientConfig{}) + c, err := NewClient(ClientConfig{TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) return @@ -9195,7 +9613,7 @@ func testEncryptedSSECToSSECCopyObject() { function := "CopyObject(destination, source)" args := map[string]interface{}{} - c, err := NewClient(ClientConfig{}) + c, err := NewClient(ClientConfig{TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) return @@ -9216,7 +9634,7 @@ func testEncryptedSSECToSSES3CopyObject() { function := "CopyObject(destination, source)" args := map[string]interface{}{} - c, err := NewClient(ClientConfig{}) + c, err := NewClient(ClientConfig{TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) return @@ -9237,7 +9655,7 @@ func testEncryptedSSECToUnencryptedCopyObject() { function := "CopyObject(destination, source)" args := map[string]interface{}{} - c, err := NewClient(ClientConfig{}) + c, err := NewClient(ClientConfig{TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) return @@ -9258,7 +9676,7 @@ func testEncryptedSSES3ToSSECCopyObject() { function := "CopyObject(destination, source)" args := map[string]interface{}{} - c, err := NewClient(ClientConfig{}) + c, err := NewClient(ClientConfig{TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) return @@ -9279,7 +9697,7 @@ func testEncryptedSSES3ToSSES3CopyObject() { function := "CopyObject(destination, source)" args := map[string]interface{}{} - c, err := NewClient(ClientConfig{}) + c, err := NewClient(ClientConfig{TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) return @@ -9300,7 +9718,7 @@ func testEncryptedSSES3ToUnencryptedCopyObject() { function := "CopyObject(destination, source)" args := map[string]interface{}{} - c, err := NewClient(ClientConfig{}) + c, err := NewClient(ClientConfig{TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) return @@ -9321,7 +9739,7 @@ func testEncryptedCopyObjectV2() { function := "CopyObject(destination, source)" args := map[string]interface{}{} - c, err := NewClient(ClientConfig{CredsV2: true}) + c, err := NewClient(ClientConfig{CredsV2: true, TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) return @@ -10080,7 +10498,7 @@ func testUnencryptedToSSECCopyObjectPart() { function := "CopyObjectPart(destination, source)" args := map[string]interface{}{} - client, err := NewClient(ClientConfig{}) + client, err := NewClient(ClientConfig{TrailingHeaders: true}) if err != nil { logError(testName, function, args, startTime, "", "MinIO v4 client object creation failed", err) return @@ -11080,7 +11498,7 @@ func testUserMetadataCopyingWrapper(c *minio.Client) { objInfo, err := c.StatObject(context.Background(), bucketName, object, minio.StatObjectOptions{}) if err != nil { logError(testName, function, args, startTime, "", "Stat failed", err) - return + return h } h = make(http.Header) for k, vs := range objInfo.Metadata { @@ -11256,7 +11674,7 @@ func testStorageClassMetadataPutObject() { objInfo, err := c.StatObject(context.Background(), bucketName, object, minio.StatObjectOptions{}) if err != nil { logError(testName, function, args, startTime, "", "Stat failed", err) - return + return h } h = make(http.Header) for k, vs := range objInfo.Metadata { @@ -11377,7 +11795,7 @@ func testStorageClassMetadataCopyObject() { args["object"] = object if err != nil { logError(testName, function, args, startTime, "", "Stat failed", err) - return + return h } h = make(http.Header) for k, vs := range objInfo.Metadata { @@ -14195,6 +14613,29 @@ func mustParseBool(str string) bool { return b } +// wantChecksums is a map of expected checksums for an object. +type wantChecksums map[minio.ChecksumType]string + +// cmpChecksum compares the checksums of an object against expected values. +func cmpChecksum(oi minio.ObjectInfo, chksums wantChecksums) error { + if oi.ChecksumCRC64NVME != chksums[minio.ChecksumCRC64NVME] { + return fmt.Errorf("Checksum mismatch for CRC64NVME, want: %s, got: %s", chksums[minio.ChecksumCRC64NVME], oi.ChecksumCRC64NVME) + } + if oi.ChecksumCRC32C != chksums[minio.ChecksumCRC32C] { + return fmt.Errorf("Checksum mismatch for CRC32C, want: %s, got: %s", chksums[minio.ChecksumCRC32C], oi.ChecksumCRC32C) + } + if oi.ChecksumCRC32 != chksums[minio.ChecksumCRC32] { + return fmt.Errorf("Checksum mismatch for CRC32, want: %s, got: %s", chksums[minio.ChecksumCRC32], oi.ChecksumCRC32) + } + if oi.ChecksumSHA1 != chksums[minio.ChecksumSHA1] { + return fmt.Errorf("Checksum mismatch for SHA1, want: %s, got: %s", chksums[minio.ChecksumSHA1], oi.ChecksumSHA1) + } + if oi.ChecksumSHA256 != chksums[minio.ChecksumSHA256] { + return fmt.Errorf("Checksum mismatch for SHA256, want: %s, got: %s", chksums[minio.ChecksumSHA256], oi.ChecksumSHA256) + } + return nil +} + func main() { slog.SetDefault(slog.New(slog.NewJSONHandler( os.Stdout, @@ -14219,6 +14660,8 @@ func main() { // execute tests if isFullMode() { + testCopyObjectWithChecksums() + testReplaceObjectWithChecksums() testCorsSetGetDelete() testCors() testListMultipartUpload() @@ -14253,6 +14696,7 @@ func main() { testPutObjectWithMetadata() testPutObjectReadAt() testPutObjectStreaming() + testPutObjectPreconditionOnNonExistent() testGetObjectSeekEnd() testGetObjectClosedTwice() testGetObjectS3Zip() diff --git a/vendor/github.com/minio/minio-go/v7/internal/json/json_goccy.go b/vendor/github.com/minio/minio-go/v7/internal/json/json_goccy.go deleted file mode 100644 index 8fc33849f..000000000 --- a/vendor/github.com/minio/minio-go/v7/internal/json/json_goccy.go +++ /dev/null @@ -1,49 +0,0 @@ -//go:build !stdlibjson - -/* - * MinIO Go Library for Amazon S3 Compatible Cloud Storage - * Copyright 2025 MinIO, Inc. - * - * 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. - */ - -package json - -import "github.com/goccy/go-json" - -// This file defines the JSON functions used internally and forwards them -// to goccy/go-json. Alternatively, the standard library can be used by setting -// the build tag stdlibjson. This can be useful for testing purposes or if -// goccy/go-json causes issues. -// -// This file does not contain all definitions from goccy/go-json; if needed, more -// can be added, but keep in mind that json_stdlib.go will also need to be -// updated. - -var ( - // Unmarshal is a wrapper around goccy/go-json Unmarshal function. - Unmarshal = json.Unmarshal - // Marshal is a wrapper around goccy/go-json Marshal function. - Marshal = json.Marshal - // NewEncoder is a wrapper around goccy/go-json NewEncoder function. - NewEncoder = json.NewEncoder - // NewDecoder is a wrapper around goccy/go-json NewDecoder function. - NewDecoder = json.NewDecoder -) - -type ( - // Encoder is an alias for goccy/go-json Encoder. - Encoder = json.Encoder - // Decoder is an alias for goccy/go-json Decoder. - Decoder = json.Decoder -) diff --git a/vendor/github.com/minio/minio-go/v7/internal/json/json_stdlib.go b/vendor/github.com/minio/minio-go/v7/internal/json/json_stdlib.go deleted file mode 100644 index a671fead3..000000000 --- a/vendor/github.com/minio/minio-go/v7/internal/json/json_stdlib.go +++ /dev/null @@ -1,49 +0,0 @@ -//go:build stdlibjson - -/* - * MinIO Go Library for Amazon S3 Compatible Cloud Storage - * Copyright 2025 MinIO, Inc. - * - * 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. - */ - -package json - -import "encoding/json" - -// This file defines the JSON functions used internally and forwards them -// to encoding/json. This is only enabled by setting the build tag stdlibjson, -// otherwise json_goccy.go applies. -// This can be useful for testing purposes or if goccy/go-json (which is used otherwise) causes issues. -// -// This file does not contain all definitions from encoding/json; if needed, more -// can be added, but keep in mind that json_goccy.go will also need to be -// updated. - -var ( - // Unmarshal is a wrapper around encoding/json Unmarshal function. - Unmarshal = json.Unmarshal - // Marshal is a wrapper around encoding/json Marshal function. - Marshal = json.Marshal - // NewEncoder is a wrapper around encoding/json NewEncoder function. - NewEncoder = json.NewEncoder - // NewDecoder is a wrapper around encoding/json NewDecoder function. - NewDecoder = json.NewDecoder -) - -type ( - // Encoder is an alias for encoding/json Encoder. - Encoder = json.Encoder - // Decoder is an alias for encoding/json Decoder. - Decoder = json.Decoder -) diff --git a/vendor/github.com/minio/minio-go/v7/pkg/credentials/file_aws_credentials.go b/vendor/github.com/minio/minio-go/v7/pkg/credentials/file_aws_credentials.go index c9a52252a..0c83fc7fa 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/credentials/file_aws_credentials.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/credentials/file_aws_credentials.go @@ -18,6 +18,7 @@ package credentials import ( + "encoding/json" "errors" "os" "os/exec" @@ -26,7 +27,6 @@ import ( "time" "github.com/go-ini/ini" - "github.com/minio/minio-go/v7/internal/json" ) // A externalProcessCredentials stores the output of a credential_process diff --git a/vendor/github.com/minio/minio-go/v7/pkg/credentials/file_minio_client.go b/vendor/github.com/minio/minio-go/v7/pkg/credentials/file_minio_client.go index 398952ee9..b78dcaccf 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/credentials/file_minio_client.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/credentials/file_minio_client.go @@ -18,11 +18,10 @@ package credentials import ( + "encoding/json" "os" "path/filepath" "runtime" - - "github.com/minio/minio-go/v7/internal/json" ) // A FileMinioClient retrieves credentials from the current user's home diff --git a/vendor/github.com/minio/minio-go/v7/pkg/credentials/iam_aws.go b/vendor/github.com/minio/minio-go/v7/pkg/credentials/iam_aws.go index edc988467..f4f7c8f7e 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/credentials/iam_aws.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/credentials/iam_aws.go @@ -20,6 +20,7 @@ package credentials import ( "bufio" "context" + "encoding/json" "errors" "fmt" "io" @@ -30,8 +31,6 @@ import ( "path" "strings" "time" - - "github.com/minio/minio-go/v7/internal/json" ) // DefaultExpiryWindow - Default expiry window. diff --git a/vendor/github.com/minio/minio-go/v7/pkg/credentials/sts_custom_identity.go b/vendor/github.com/minio/minio-go/v7/pkg/credentials/sts_custom_identity.go index 162f460ee..e9e7a1151 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/credentials/sts_custom_identity.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/credentials/sts_custom_identity.go @@ -132,7 +132,7 @@ func (c *CustomTokenIdentity) RetrieveWithCredContext(cc *CredContext) (value Va r := AssumeRoleWithCustomTokenResponse{} if err = xml.NewDecoder(resp.Body).Decode(&r); err != nil { - return + return value, err } cr := r.Result.Credentials diff --git a/vendor/github.com/minio/minio-go/v7/pkg/credentials/sts_ldap_identity.go b/vendor/github.com/minio/minio-go/v7/pkg/credentials/sts_ldap_identity.go index 31fe10ae0..7e80cd6a2 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/credentials/sts_ldap_identity.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/credentials/sts_ldap_identity.go @@ -74,6 +74,9 @@ type LDAPIdentity struct { // requested from LDAP. RequestedExpiry time.Duration + // Optional, if empty applies to default config + ConfigName string + // Optional, used for token revokation TokenRevokeType string } @@ -110,6 +113,13 @@ func LDAPIdentityExpiryOpt(d time.Duration) LDAPIdentityOpt { } } +// LDAPIdentityConfigNameOpt sets the config name for requested credentials. +func LDAPIdentityConfigNameOpt(name string) LDAPIdentityOpt { + return func(k *LDAPIdentity) { + k.ConfigName = name + } +} + // NewLDAPIdentityWithSessionPolicy returns new credentials object that uses // LDAP Identity with a specified session policy. The `policy` parameter must be // a JSON string specifying the policy document. @@ -158,6 +168,9 @@ func (k *LDAPIdentity) RetrieveWithCredContext(cc *CredContext) (value Value, er if k.TokenRevokeType != "" { v.Set("TokenRevokeType", k.TokenRevokeType) } + if k.ConfigName != "" { + v.Set("ConfigName", k.ConfigName) + } req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(v.Encode())) if err != nil { @@ -201,7 +214,7 @@ func (k *LDAPIdentity) RetrieveWithCredContext(cc *CredContext) (value Value, er r := AssumeRoleWithLDAPResponse{} if err = xml.NewDecoder(resp.Body).Decode(&r); err != nil { - return + return value, err } cr := r.Result.Credentials diff --git a/vendor/github.com/minio/minio-go/v7/pkg/encrypt/server-side.go b/vendor/github.com/minio/minio-go/v7/pkg/encrypt/server-side.go index 1fc510ae0..0a8a7baa2 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/encrypt/server-side.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/encrypt/server-side.go @@ -20,10 +20,10 @@ package encrypt import ( "crypto/md5" "encoding/base64" + "encoding/json" "errors" "net/http" - "github.com/minio/minio-go/v7/internal/json" "golang.org/x/crypto/argon2" ) diff --git a/vendor/github.com/minio/minio-go/v7/pkg/lifecycle/lifecycle.go b/vendor/github.com/minio/minio-go/v7/pkg/lifecycle/lifecycle.go index cf1ba038f..7ed98b0d1 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/lifecycle/lifecycle.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/lifecycle/lifecycle.go @@ -19,11 +19,10 @@ package lifecycle import ( + "encoding/json" "encoding/xml" "errors" "time" - - "github.com/minio/minio-go/v7/internal/json" ) var errMissingStorageClass = errors.New("storage-class cannot be empty") diff --git a/vendor/github.com/minio/minio-go/v7/pkg/peeker/peek-reader-closer.go b/vendor/github.com/minio/minio-go/v7/pkg/peeker/peek-reader-closer.go deleted file mode 100644 index 26c9cf637..000000000 --- a/vendor/github.com/minio/minio-go/v7/pkg/peeker/peek-reader-closer.go +++ /dev/null @@ -1,73 +0,0 @@ -/* - * MinIO Go Library for Amazon S3 Compatible Cloud Storage - * Copyright 2015-2025 MinIO, Inc. - * - * 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. - */ - -package peeker - -import ( - "bytes" - "errors" - "io" -) - -// PeekReadCloser offers a way to peek a ReadCloser stream and then -// return the exact stream of the underlying ReadCloser -type PeekReadCloser struct { - io.ReadCloser - - recordMode bool - recordMaxBuf int - recordBuf *bytes.Buffer -} - -// ReplayFromStart ensures next Read() will restart to stream the -// underlying ReadCloser stream from the beginning -func (prc *PeekReadCloser) ReplayFromStart() { - prc.recordMode = false -} - -func (prc *PeekReadCloser) Read(p []byte) (int, error) { - if prc.recordMode { - if prc.recordBuf.Len() > prc.recordMaxBuf { - return 0, errors.New("maximum peek buffer exceeded") - } - n, err := prc.ReadCloser.Read(p) - prc.recordBuf.Write(p[:n]) - return n, err - } - // Replay mode - if prc.recordBuf.Len() > 0 { - pn, _ := prc.recordBuf.Read(p) - return pn, nil - } - return prc.ReadCloser.Read(p) -} - -// Close releases the record buffer memory and close the underlying ReadCloser -func (prc *PeekReadCloser) Close() error { - prc.recordBuf.Reset() - return prc.ReadCloser.Close() -} - -// NewPeekReadCloser returns a new peek reader -func NewPeekReadCloser(rc io.ReadCloser, maxBufSize int) *PeekReadCloser { - return &PeekReadCloser{ - ReadCloser: rc, - recordMode: true, // recording mode by default - recordBuf: bytes.NewBuffer(make([]byte, 0, 1024)), - recordMaxBuf: maxBufSize, - } -} diff --git a/vendor/github.com/minio/minio-go/v7/pkg/replication/replication.go b/vendor/github.com/minio/minio-go/v7/pkg/replication/replication.go index 2f7993f4b..cc17a3531 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/replication/replication.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/replication/replication.go @@ -1014,7 +1014,7 @@ func (q ReplQueueStats) QStats() (r ReplQStats) { if len(q.Nodes) > 0 { r.Uptime /= int64(len(q.Nodes)) // average uptime } - return + return r } // MetricsV2 represents replication metrics for a bucket. diff --git a/vendor/github.com/minio/minio-go/v7/pkg/set/stringset.go b/vendor/github.com/minio/minio-go/v7/pkg/set/stringset.go index 8aa92212b..c12651b54 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/set/stringset.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/set/stringset.go @@ -18,10 +18,9 @@ package set import ( + "encoding/json" "fmt" "sort" - - "github.com/minio/minio-go/v7/internal/json" ) // StringSet - uses map as set of strings. diff --git a/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go b/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go index f65c36c7d..d15c99ad7 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go @@ -48,11 +48,11 @@ func encodeURL2Path(req *http.Request, virtualHost bool) (path string) { path = "/" + bucketName path += req.URL.Path path = s3utils.EncodePath(path) - return + return path } } path = s3utils.EncodePath(req.URL.Path) - return + return path } // PreSignV2 - presign the request in following style. diff --git a/vendor/github.com/minio/minio-go/v7/utils.go b/vendor/github.com/minio/minio-go/v7/utils.go index cc96005b9..6c8dc9433 100644 --- a/vendor/github.com/minio/minio-go/v7/utils.go +++ b/vendor/github.com/minio/minio-go/v7/utils.go @@ -80,7 +80,7 @@ func amzRestoreToStruct(restore string) (ongoing bool, expTime time.Time, err er return false, time.Time{}, err } } - return + return ongoing, expTime, err } // xmlDecoder provide decoded value in xml. @@ -438,7 +438,7 @@ var readFull = func(r io.Reader, buf []byte) (n int, err error) { } else if n > 0 && err == io.EOF { err = io.ErrUnexpectedEOF } - return + return n, err } // regCred matches credential string in HTTP header |
