Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddf4e7ee09 |
No files matched your search
@@ -1,11 +0,0 @@
|
|||||||
schema_version = 1
|
|
||||||
|
|
||||||
project {
|
|
||||||
license = "MPL-2.0"
|
|
||||||
copyright_year = 2025
|
|
||||||
|
|
||||||
header_ignore = [
|
|
||||||
# ignoring Minio's apache licensed code that is used as is in this repo
|
|
||||||
"madmin/**",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
## PCI review checklist
|
|
||||||
|
|
||||||
<!-- heimdall_github_prtemplate:grc-pci_dss-2024-01-05 -->
|
|
||||||
|
|
||||||
- [ ] I have documented a clear reason for, and description of, the change I am making.
|
|
||||||
|
|
||||||
- [ ] If applicable, I've documented a plan to revert these changes if they require more than reverting the pull request.
|
|
||||||
|
|
||||||
- [ ] If applicable, I've documented the impact of any changes to security controls.
|
|
||||||
|
|
||||||
Examples of changes to security controls include using new access control methods, adding or removing logging pipelines, etc.
|
|
||||||
@@ -4,8 +4,6 @@ on:
|
|||||||
- workflow_dispatch
|
- workflow_dispatch
|
||||||
- push
|
- push
|
||||||
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -20,6 +18,4 @@ jobs:
|
|||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
env:
|
|
||||||
DOCKER_MIRROR: docker.mirror.hashicorp.services
|
|
||||||
run: go test -v ./...
|
run: go test -v ./...
|
||||||
-102
@@ -1,102 +0,0 @@
|
|||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
.cover
|
|
||||||
|
|
||||||
# IntelliJ IDEA project files
|
|
||||||
.idea
|
|
||||||
*.ipr
|
|
||||||
*.iml
|
|
||||||
*.iws
|
|
||||||
|
|
||||||
### Logs ###
|
|
||||||
*.log
|
|
||||||
logs/
|
|
||||||
|
|
||||||
### direnv ###
|
|
||||||
.envrc
|
|
||||||
.direnv/
|
|
||||||
|
|
||||||
### Temp directories ###
|
|
||||||
tmp/
|
|
||||||
temp/
|
|
||||||
|
|
||||||
### Visual Studio ###
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
### macOS ###
|
|
||||||
# General
|
|
||||||
.DS_Store
|
|
||||||
.AppleDouble
|
|
||||||
.LSOverride
|
|
||||||
|
|
||||||
# Icon must end with two \r
|
|
||||||
Icon
|
|
||||||
|
|
||||||
# Thumbnails
|
|
||||||
._*
|
|
||||||
|
|
||||||
### Git ###
|
|
||||||
# Created by git for backups. To disable backups in Git:
|
|
||||||
# $ git config --global mergetool.keepBackup false
|
|
||||||
*.orig
|
|
||||||
|
|
||||||
# Created by git when using merge tools for conflicts
|
|
||||||
*.BACKUP.*
|
|
||||||
*.BASE.*
|
|
||||||
*.LOCAL.*
|
|
||||||
*.REMOTE.*
|
|
||||||
*_BACKUP_*.txt
|
|
||||||
*_BASE_*.txt
|
|
||||||
*_LOCAL_*.txt
|
|
||||||
*_REMOTE_*.txt
|
|
||||||
|
|
||||||
### Go ###
|
|
||||||
# Files that shouldn't be shared
|
|
||||||
go.work
|
|
||||||
go.work.sum
|
|
||||||
|
|
||||||
# Binaries for programs and plugins
|
|
||||||
*.exe
|
|
||||||
*.exe~
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
|
||||||
*.test
|
|
||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
|
||||||
*.out
|
|
||||||
|
|
||||||
### Tags ###
|
|
||||||
# Ignore tags created by etags, ctags, gtags (GNU global) and cscope
|
|
||||||
TAGS
|
|
||||||
.TAGS
|
|
||||||
!TAGS/
|
|
||||||
tags
|
|
||||||
.tags
|
|
||||||
!tags/
|
|
||||||
gtags.files
|
|
||||||
GTAGS
|
|
||||||
GRTAGS
|
|
||||||
GPATH
|
|
||||||
GSYMS
|
|
||||||
cscope.files
|
|
||||||
cscope.out
|
|
||||||
cscope.in.out
|
|
||||||
cscope.po.out
|
|
||||||
|
|
||||||
# Log files (if you are creating logs in debug mode, uncomment this)
|
|
||||||
# *.log
|
|
||||||
|
|
||||||
### Vagrant Patch ###
|
|
||||||
*.box
|
|
||||||
|
|
||||||
### Vim ###
|
|
||||||
# Swap
|
|
||||||
[._]*.s[a-v][a-z]
|
|
||||||
[._]*.sw[a-p]
|
|
||||||
[._]s[a-rt-v][a-z]
|
|
||||||
[._]ss[a-gi-z]
|
|
||||||
[._]sw[a-p]
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# These owners will be the default owners for everything in
|
# These owners will be the default owners for everything in
|
||||||
# the repo, unless a later match takes precedence.
|
# the repo, unless a later match takes precedence.
|
||||||
|
|
||||||
* @hashicorp/boundary
|
@hashicorp/boundary
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
Copyright IBM Corp. 2025, 2026
|
|
||||||
|
|
||||||
Mozilla Public License Version 2.0
|
Mozilla Public License Version 2.0
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
This product includes software developed at MinIO, Inc.
|
|
||||||
(https://min.io/).
|
|
||||||
|
|
||||||
The Boundary MinIO Plugin contains subcomponents of the Minio madmin-go library v1.7.5
|
|
||||||
(https://github.com/minio/madmin-go/tree/v1.7.5),
|
|
||||||
licensed under the Apache License 2.0.
|
|
||||||
|
|
||||||
See `madmin/LICENSE` for the full license text.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
This repository is a community fork of hashicorp/boundary-plugin-minio
|
|
||||||
(https://github.com/hashicorp/boundary-plugin-minio), maintained for Threshold.
|
|
||||||
|
|
||||||
The original work is licensed under the Mozilla Public License 2.0 (see LICENSE).
|
|
||||||
Original copyright IBM Corp. / HashiCorp, Inc. is retained in all source file
|
|
||||||
headers and in LICENSE. Modifications are limited to the Go module path and the
|
|
||||||
import paths of the Boundary SDK/API packages.
|
|
||||||
|
|
||||||
This fork is not affiliated with, endorsed by, or supported by HashiCorp or IBM.
|
|
||||||
@@ -1,12 +1,3 @@
|
|||||||
## About this fork
|
|
||||||
|
|
||||||
This is a community fork of
|
|
||||||
[hashicorp/boundary-plugin-minio](https://github.com/hashicorp/boundary-plugin-minio)
|
|
||||||
for use with Threshold. It is licensed under the MPL-2.0 (see `LICENSE`), and
|
|
||||||
the original copyright notices are retained. It is not affiliated with,
|
|
||||||
endorsed by, or supported by HashiCorp or IBM. Changes from upstream are limited
|
|
||||||
to the Go module path and SDK import paths; see `NOTICE`.
|
|
||||||
|
|
||||||
# MinIO Plugin for HashiCorp Boundary
|
# MinIO Plugin for HashiCorp Boundary
|
||||||
|
|
||||||
This repo contains the [MinIO](https://min.io) plugin for [HashiCorp
|
This repo contains the [MinIO](https://min.io) plugin for [HashiCorp
|
||||||
|
|||||||
@@ -1,89 +1,90 @@
|
|||||||
module repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio
|
module github.com/hashicorp/boundary-plugin-minio
|
||||||
|
|
||||||
go 1.26.9
|
go 1.21.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/go-cmp v0.7.0
|
github.com/google/go-cmp v0.6.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.5.0
|
||||||
github.com/hashicorp/go-multierror v1.1.1
|
github.com/hashicorp/boundary/sdk v0.0.42
|
||||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0
|
github.com/minio/madmin-go/v3 v3.0.48
|
||||||
github.com/minio/minio-go/v7 v7.2.0
|
github.com/minio/minio-go/v7 v7.0.67
|
||||||
github.com/ory/dockertest/v3 v3.11.0
|
github.com/ory/dockertest/v3 v3.10.0
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.8.4
|
||||||
google.golang.org/grpc v1.85.0-dev.0.20260825072537-93e31b48545e
|
google.golang.org/grpc v1.59.0
|
||||||
google.golang.org/protobuf v1.36.12
|
google.golang.org/protobuf v1.32.0
|
||||||
repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/madmin v0.2.0
|
|
||||||
repo.anhonesthost.net/CyberCoveLLC/threshold/sdk v0.1.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
|
||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/containerd/continuity v0.3.0 // indirect
|
||||||
github.com/containerd/continuity v0.4.3 // indirect
|
|
||||||
github.com/containerd/errdefs v1.0.0 // indirect
|
|
||||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/docker/cli v20.10.17+incompatible // indirect
|
||||||
github.com/docker/cli v29.2.0+incompatible // indirect
|
github.com/docker/docker v20.10.7+incompatible // indirect
|
||||||
github.com/docker/go-connections v0.6.0 // indirect
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/felixge/httpsnoop v1.1.0 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-logr/logr v1.4.4 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/eventlogger v0.2.11 // indirect
|
github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0 // indirect
|
||||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0 // indirect
|
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0 // indirect
|
||||||
github.com/hashicorp/go-kms-wrapping/v2 v2.0.20 // indirect
|
github.com/hashicorp/go-kms-wrapping/v2 v2.0.14 // indirect
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
|
|
||||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||||
github.com/klauspost/compress v1.18.7 // indirect
|
github.com/imdario/mergo v0.3.13 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/crc32 v1.3.0 // indirect
|
github.com/klauspost/compress v1.17.4 // indirect
|
||||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
|
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mitchellh/pointerstructure v1.2.1 // indirect
|
github.com/mitchellh/pointerstructure v1.2.1 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
|
||||||
github.com/moby/moby/api v1.53.0 // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/moby/moby/client v0.2.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/moby/sys/user v0.4.0 // indirect
|
|
||||||
github.com/moby/term v0.5.2 // indirect
|
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||||
github.com/opencontainers/runc v1.3.6 // indirect
|
github.com/opencontainers/runc v1.1.5 // indirect
|
||||||
github.com/philhofer/fwd v1.2.0 // indirect
|
github.com/philhofer/fwd v1.1.2 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/rs/xid v1.6.0 // indirect
|
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
github.com/prometheus/client_model v0.4.0 // indirect
|
||||||
|
github.com/prometheus/common v0.44.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.9.0 // indirect
|
||||||
|
github.com/prometheus/prom2json v1.3.3 // indirect
|
||||||
|
github.com/rs/xid v1.5.0 // indirect
|
||||||
|
github.com/safchain/ethtool v0.3.0 // indirect
|
||||||
github.com/secure-io/sio-go v0.3.1 // indirect
|
github.com/secure-io/sio-go v0.3.1 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
|
||||||
github.com/tinylib/msgp v1.6.1 // indirect
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
|
github.com/tinylib/msgp v1.1.8 // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
github.com/zeebo/xxh3 v1.1.0 // indirect
|
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 // indirect
|
golang.org/x/mod v0.9.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.45.0 // indirect
|
golang.org/x/net v0.19.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.45.0 // indirect
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.45.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
golang.org/x/crypto v0.57.0 // indirect
|
golang.org/x/tools v0.7.0 // indirect
|
||||||
golang.org/x/net v0.60.0 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||||
golang.org/x/sys v0.48.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
golang.org/x/text v0.42.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260817212433-ac3dfec99bb1 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.2 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
@@ -1,145 +1,173 @@
|
|||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
|
||||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
|
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
||||||
github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||||
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
|
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||||
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
|
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
|
||||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
|
||||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M=
|
||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM=
|
github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
|
||||||
github.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||||
github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/cli v1.1.7 h1:/fZJ+hNdwfTSfsxMBa9WWMlfjUZbX8/LnUxgAd7lCVU=
|
github.com/hashicorp/boundary/sdk v0.0.42 h1:iRDdnM6/6q9qLtBP7/cpq/FWQGf69TFSsBQFxgGK8Jw=
|
||||||
github.com/hashicorp/cli v1.1.7/go.mod h1:e6Mfpga9OCT1vqzFuoGZiiF/KaG9CbUfO5s3ghU3YgU=
|
github.com/hashicorp/boundary/sdk v0.0.42/go.mod h1:QJhm4rky2YMtk9DS5Rj8QJhDKB/6zQbVrm3R0QgdYmQ=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/eventlogger v0.2.11 h1:lkK8ARM/DrCeL5deH2yVS6p40337dESi+77p9pIfLqo=
|
github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0 h1:f9oX8/3zxiQrfrWnBeyjDm4S02GAU02OBtCRoZOUwlo=
|
||||||
github.com/hashicorp/eventlogger v0.2.11/go.mod h1:Rmc3MEopz7jUnLokZVTWAsXaEP5rqd20ObGS+pcau3U=
|
github.com/hashicorp/eventlogger v0.2.6-0.20231025104552-802587e608f0/go.mod h1://CHt6/j+Q2lc0NlUB5af4aS2M0c0aVBg9/JfcpAyhM=
|
||||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0 h1:iAb287bq0TaWTnhDYuN/zVqdD2EwanQg9ncVelC60Xc=
|
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0 h1:iAb287bq0TaWTnhDYuN/zVqdD2EwanQg9ncVelC60Xc=
|
||||||
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0/go.mod h1:tMywUTIvdB/FXhwm6HMTt61C8/eODY6gitCHhXtyojg=
|
github.com/hashicorp/eventlogger/filters/encrypt v0.1.8-0.20231025104552-802587e608f0/go.mod h1:tMywUTIvdB/FXhwm6HMTt61C8/eODY6gitCHhXtyojg=
|
||||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||||
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.8 h1:/GIUjn9GkFXMk/8/irRdbdtmx8CcyeyWdVy/E5LvzyA=
|
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.5 h1:jrnDfQm2hCQ0/hEselgqzV4fK16gpZoY0OWGZpVPNHM=
|
||||||
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.8/go.mod h1:JDc9UOD4EVRDIwPVethJcT5Ibi/Nas6eQDPtA60iwP0=
|
github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.5/go.mod h1:psh1qKep5ukvuNobFY/hCybuudlkkACpmazOsCgX5Rg=
|
||||||
github.com/hashicorp/go-kms-wrapping/v2 v2.0.20 h1:afreZ1WJd0WI7v4NsMZ1aL7V/T59sxPuKmQDgGUja20=
|
github.com/hashicorp/go-kms-wrapping/v2 v2.0.14 h1:1ZuhfnZgRnLK8S0KovJkoTCRIQId5pv3sDR7pG5VQBw=
|
||||||
github.com/hashicorp/go-kms-wrapping/v2 v2.0.20/go.mod h1:NeK2Ul15t1zutp/dZzt28XQrGZHosbxE/QLNNfaWObM=
|
github.com/hashicorp/go-kms-wrapping/v2 v2.0.14/go.mod h1:0dWtzl2ilqKpavgM3id/kFK9L3tjo6fS4OhbVPSYpnQ=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA=
|
github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y=
|
||||||
github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8=
|
github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
|
||||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng=
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng=
|
||||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
|
||||||
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.13 h1:TayxZ5drfMP0G6T++WvnLESGLOWeHtdWDWWTAi2e3Qk=
|
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11 h1:uPW2Wn0YlmI9RGSkZpcIplnVRwJ7BCiGpk1vnF2TMw4=
|
||||||
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.13/go.mod h1:NDRQ/F3DXTylqjORAP0cA+puH/JFrLlT+NlqxHK2/e8=
|
github.com/hashicorp/go-secure-stdlib/configutil/v2 v2.0.11/go.mod h1:uis9dCmOzXuOaRyXq+1Foh31kcvXKoWogjNnhfjHfW8=
|
||||||
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.10 h1:2iDz+t0JLl1W0tJhvmhsh/UBgT1JgC8Qxz8HxYMWXQo=
|
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.9 h1:0S0ctJ7Ra8O7ap+/3fZUnzJ3VzJyirWS/WnNCuOYtZY=
|
||||||
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.10/go.mod h1:eZkXE+osawMrAWR4wJRmyKauUwH6mNGbjFuiDujnbPk=
|
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.9/go.mod h1:TNNdgtjLgVDbrgFcyCKrlAicIl3dZF94swJltyGUX2M=
|
||||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM=
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc=
|
||||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8/go.mod h1:aiJI+PIApBRQG7FZTEBx5GiiX+HbOHilUdNxUZi4eV0=
|
||||||
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.8 h1:pgSicufBI+MvaIG9Keykb3k9B3sWtDNjmsoyrl2/8Qw=
|
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.6 h1:ZYv2XA+tEfFXIToR2jmBgVqQU9gERt0APbWqmUoNGnY=
|
||||||
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.8/go.mod h1:sBcjk+paCXCMR9HHLcrYSfPz2FsskD8MQuotKiyM2aA=
|
github.com/hashicorp/go-secure-stdlib/pluginutil/v2 v2.0.6/go.mod h1:ggFN8dlaLWS2R1gymBbCrvXM/bkZP7hEAa4seqDwhyg=
|
||||||
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI=
|
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 h1:SMGUnbpAcat8rIKHkBPjfv81yC46a8eCNZ2hsR2l1EI=
|
||||||
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0=
|
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1/go.mod h1:Ch/bf00Qnx77MZd49JRgHYqHQjtEmTgGU2faufpVZb0=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||||
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3 h1:xbrxd0U9XQW8qL1BAz2XrAjAF/P2vcqUTAues9c24B8=
|
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3 h1:xbrxd0U9XQW8qL1BAz2XrAjAF/P2vcqUTAues9c24B8=
|
||||||
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3/go.mod h1:LWq2Sy8UoKKuK4lFuCNWSjJj57MhNNf2zzBWMtkAIX4=
|
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.3/go.mod h1:LWq2Sy8UoKKuK4lFuCNWSjJj57MhNNf2zzBWMtkAIX4=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
|
github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
|
github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
||||||
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||||
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f h1:E87tDTVS5W65euzixn7clSzK66puSt1H4I5SC0EmHH4=
|
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f h1:E87tDTVS5W65euzixn7clSzK66puSt1H4I5SC0EmHH4=
|
||||||
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f/go.mod h1:3J2qVK16Lq8V+wfiL2lPeDZ7UWMxk5LemerHa1p6N00=
|
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f/go.mod h1:3J2qVK16Lq8V+wfiL2lPeDZ7UWMxk5LemerHa1p6N00=
|
||||||
github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||||
|
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
|
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2 h1:hRGSmZu7j271trc9sneMrpOW7GN5ngLm8YUZIPzf394=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de h1:V53FWzU6KAZVi1tPp5UIsMoUWJ2/PNwYIDXnu7QuBCE=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
|
github.com/minio/madmin-go/v3 v3.0.48 h1:a7FZdWJNeWinDibsL6PeGnLB4JFgwO59cIYHWEtYb+Y=
|
||||||
|
github.com/minio/madmin-go/v3 v3.0.48/go.mod h1:ZDF7kf5fhmxLhbGTqyq5efs4ao0v4eWf7nOuef/ljJs=
|
||||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||||
github.com/minio/minio-go/v7 v7.2.0 h1:RCJM0R1XOsRs+A3x3UCaf3ZYbByDaLjFeAi+YCQEPhs=
|
github.com/minio/minio-go/v7 v7.0.67 h1:BeBvZWAS+kRJm1vGTMJYVjKUNoo0FoEt/wUWdUtfmh8=
|
||||||
github.com/minio/minio-go/v7 v7.2.0/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0=
|
github.com/minio/minio-go/v7 v7.0.67/go.mod h1:+UXocnUeZ3wHvVh5s95gcrA4YjMIbccT6ubB+1m054A=
|
||||||
|
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||||
|
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||||
|
github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng=
|
||||||
|
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
@@ -147,127 +175,224 @@ github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQh
|
|||||||
github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
|
github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
|
||||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
|
||||||
github.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w=
|
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
|
||||||
github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
|
||||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
github.com/opencontainers/runc v1.3.6 h1:SLGIymCtsk80iNPWgbc8dtjI30r+5mTVV+4dN8/17Sk=
|
github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs=
|
||||||
github.com/opencontainers/runc v1.3.6/go.mod h1:o1wyv76EDlTkcf0KTFgN8bMWLPvgF/HfX709lDv+rr4=
|
github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
|
||||||
github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNGuyA=
|
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
github.com/ory/dockertest/v3 v3.11.0/go.mod h1:VIPxS1gwT9NpPOrfD3rACs8Y9Z7yhzO4SB194iUDnUI=
|
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
|
||||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
|
||||||
|
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
||||||
|
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
||||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
|
||||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||||
|
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||||
|
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||||
|
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||||
|
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||||
|
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||||
|
github.com/prometheus/prom2json v1.3.3 h1:IYfSMiZ7sSOfliBoo89PcufjWO4eAR0gznGcETyaUgo=
|
||||||
|
github.com/prometheus/prom2json v1.3.3/go.mod h1:Pv4yIPktEkK7btWsrUTWDDDrnpUrAELaOCj+oFwlgmc=
|
||||||
|
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
|
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||||
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
|
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
|
||||||
|
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
||||||
|
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||||
github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc=
|
github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc=
|
||||||
github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs=
|
github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||||
|
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
||||||
github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY=
|
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
||||||
github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||||
|
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||||
|
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||||
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
|
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||||
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 h1:LMuyCAyfalSjDyjdC65nK6N0zoTT63+E/u95X0JovZI=
|
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0/go.mod h1:085m8qbm4hgc8rZWGDEa4vmyyo2c3nPxUslYUKUIU04=
|
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
|
|
||||||
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
|
|
||||||
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
|
|
||||||
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
|
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
|
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
|
|
||||||
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
|
|
||||||
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
|
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
|
||||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
|
||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||||
|
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
|
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||||
|
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260817212433-ac3dfec99bb1 h1:lrupDmKL3p5kEX1M92oan027eCKcouzjuPbH6YBK+Rs=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260817212433-ac3dfec99bb1/go.mod h1:q/3oV3jAi5vwelxsVAprMBC8BcM2zmNe+IjRGd+9/ks=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 h1:utmqiUzZAEINbLmRyAt1QjVkMyLQ+lvIwCpLaB1rZfw=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
google.golang.org/grpc v1.85.0-dev.0.20260825072537-93e31b48545e h1:ar+feAPij1Znzug45m6AoShUCc5AaR8FLA3I6LZd94k=
|
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
google.golang.org/grpc v1.85.0-dev.0.20260825072537-93e31b48545e/go.mod h1:7aY/KlAHanMAVIWHnk8wloQsdlfwzDz/PP9vCbJG6CU=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||||
|
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||||
|
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
|
||||||
|
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||||
|
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||||
|
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/ini.v1 v1.67.2 h1:JtOSMb9OuaCZKr7h5D/h6iii14sK0hLbplTc6frx4Ss=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/ini.v1 v1.67.2/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
|
||||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
||||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=
|
||||||
repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/madmin v0.2.0 h1:1pNNc5lPXYWeLNn6KRzKydHEvyKMeLIs3ZauC9h+/1M=
|
|
||||||
repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/madmin v0.2.0/go.mod h1:RwjAcIDH2aYt3lVdIEv/3XdB5Z2VaTWRhv9EJDFBgW0=
|
|
||||||
repo.anhonesthost.net/CyberCoveLLC/threshold/sdk v0.1.0 h1:ExuSGqE1pA/1DnS5xrSK7kEhD+4g5hIJC5TgmwH5LAU=
|
|
||||||
repo.anhonesthost.net/CyberCoveLLC/threshold/sdk v0.1.0/go.mod h1:cWxYF3tEkYEKTY76ub8kL+vdGmopO0YfJqJGGQJov7k=
|
|
||||||
@@ -1,250 +0,0 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/madmin"
|
|
||||||
"github.com/minio/minio-go/v7/pkg/signer"
|
|
||||||
)
|
|
||||||
|
|
||||||
const adminPrefix = "/minio/admin/v3"
|
|
||||||
|
|
||||||
// AddServiceAccountReq contains the values used by the plugin when creating a minio service account
|
|
||||||
type AddServiceAccountReq struct {
|
|
||||||
// Policy to apply to service account
|
|
||||||
Policy json.RawMessage `json:"policy,omitempty"`
|
|
||||||
// Name for this access key
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
// Description for this access key
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddServiceAccountResp contains the credentials associated with the added service account
|
|
||||||
type AddServiceAccountResp struct {
|
|
||||||
Cred Credential `json:"credentials"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credential contains a minio AccessKeyId and SecretAccessKey
|
|
||||||
type Credential struct {
|
|
||||||
AccessKeyId string `json:"accessKey,omitempty"`
|
|
||||||
SecretAccessKey string `json:"secretKey,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MinioError contains the error message from failed admin API requests
|
|
||||||
type MinioError struct {
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client is used to access Minio admin API
|
|
||||||
type Client struct {
|
|
||||||
endpointUrl *url.URL
|
|
||||||
accessKeyId string
|
|
||||||
secretAccessKey string
|
|
||||||
}
|
|
||||||
|
|
||||||
// getOpts - iterate the inbound Options and return a struct
|
|
||||||
func getOpts(opt ...Option) options {
|
|
||||||
opts := getDefaultOptions()
|
|
||||||
for _, o := range opt {
|
|
||||||
o(&opts)
|
|
||||||
}
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
// Option - how options are passed as arguments
|
|
||||||
type Option func(*options)
|
|
||||||
|
|
||||||
// options = how options are represented
|
|
||||||
type options struct {
|
|
||||||
withUseSsl bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultOptions() options {
|
|
||||||
return options{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithUseSsl provides an Option to use SSL.
|
|
||||||
func WithUseSsl(b bool) Option {
|
|
||||||
return func(o *options) {
|
|
||||||
o.withUseSsl = b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a Client that can be used to access Minio admin API
|
|
||||||
func New(endpointUrl, accessKeyId, secretAccessKey string, opt ...Option) (*Client, error) {
|
|
||||||
opts := getOpts(opt...)
|
|
||||||
url, err := madmin.GetEndpointURL(endpointUrl, opts.withUseSsl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &Client{endpointUrl: url, accessKeyId: accessKeyId, secretAccessKey: secretAccessKey}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteServiceAccount deletes the provided serviceAccount
|
|
||||||
func (c *Client) DeleteServiceAccount(ctx context.Context, serviceAccount string) error {
|
|
||||||
// construct the url
|
|
||||||
url := c.endpointUrl.JoinPath(adminPrefix, "delete-service-account")
|
|
||||||
query := url.Query()
|
|
||||||
query.Set("accessKey", serviceAccount)
|
|
||||||
url.RawQuery = query.Encode()
|
|
||||||
|
|
||||||
// create the request
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get sha256 for request header
|
|
||||||
sum := sha256.Sum256(nil)
|
|
||||||
req.Header.Set("X-Amz-Content-Sha256", hex.EncodeToString(sum[:]))
|
|
||||||
|
|
||||||
// Sign request as documented
|
|
||||||
// https://min.io/docs/minio/kubernetes/upstream/administration/identity-access-management.html
|
|
||||||
req = signer.SignV4(*req, c.accessKeyId, c.secretAccessKey, "", "")
|
|
||||||
|
|
||||||
// Send the request
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("request failed: %w", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
// Check response status code
|
|
||||||
if resp.StatusCode != http.StatusNoContent {
|
|
||||||
// expected no content response, unmarshal resp body for error message
|
|
||||||
respBody, _ := io.ReadAll(resp.Body)
|
|
||||||
if len(respBody) == 0 {
|
|
||||||
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
errResp := &MinioError{}
|
|
||||||
if err := json.Unmarshal(respBody, &errResp); err != nil {
|
|
||||||
return fmt.Errorf("failed to parse response: %w", err)
|
|
||||||
}
|
|
||||||
return errors.New(errResp.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddServiceAccount adds a service account to the minio server
|
|
||||||
func (c *Client) AddServiceAccount(ctx context.Context, in AddServiceAccountReq) (Credential, error) {
|
|
||||||
// construct the url
|
|
||||||
url := c.endpointUrl.JoinPath(adminPrefix, "add-service-account")
|
|
||||||
|
|
||||||
// marshal and encrypt data payload
|
|
||||||
data, err := json.Marshal(in)
|
|
||||||
if err != nil {
|
|
||||||
return Credential{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minio encrypts streaming data as described https://blog.min.io/frictionless-encryption/
|
|
||||||
payload, err := madmin.EncryptData(c.secretAccessKey, data)
|
|
||||||
if err != nil {
|
|
||||||
return Credential{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the request
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, url.String(), bytes.NewReader(payload))
|
|
||||||
if err != nil {
|
|
||||||
return Credential{}, fmt.Errorf("failed to create request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get sha256 for request header
|
|
||||||
sum := sha256.Sum256(payload)
|
|
||||||
req.Header.Set("X-Amz-Content-Sha256", hex.EncodeToString(sum[:]))
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
// Sign request as documented
|
|
||||||
// https://min.io/docs/minio/kubernetes/upstream/administration/identity-access-management.html
|
|
||||||
req = signer.SignV4(*req, c.accessKeyId, c.secretAccessKey, "", "")
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return Credential{}, fmt.Errorf("request failed: %w", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
// did not get ok, unmarshal resp body for error message
|
|
||||||
respBody, _ := io.ReadAll(resp.Body)
|
|
||||||
if len(respBody) == 0 {
|
|
||||||
return Credential{}, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
errResp := &MinioError{}
|
|
||||||
if err := json.Unmarshal(respBody, &errResp); err != nil {
|
|
||||||
return Credential{}, fmt.Errorf("failed to parse response: %w", err)
|
|
||||||
}
|
|
||||||
return Credential{}, errors.New(errResp.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// got resp lets decrypt it
|
|
||||||
result, err := madmin.DecryptData(c.secretAccessKey, resp.Body)
|
|
||||||
addResp := &AddServiceAccountResp{}
|
|
||||||
if err := json.Unmarshal(result, &addResp); err != nil {
|
|
||||||
return Credential{}, fmt.Errorf("failed to parse response: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return addResp.Cred, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnsureServiceAccount true if the account is a service account
|
|
||||||
func (c *Client) EnsureServiceAccount(ctx context.Context, serviceAccount string) error {
|
|
||||||
// construct the url
|
|
||||||
url := c.endpointUrl.JoinPath(adminPrefix, "info-service-account")
|
|
||||||
query := url.Query()
|
|
||||||
query.Set("accessKey", serviceAccount)
|
|
||||||
url.RawQuery = query.Encode()
|
|
||||||
|
|
||||||
// create the request
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get sha256 for request header
|
|
||||||
sum := sha256.Sum256(nil)
|
|
||||||
req.Header.Set("X-Amz-Content-Sha256", hex.EncodeToString(sum[:]))
|
|
||||||
|
|
||||||
// sign request as documented
|
|
||||||
// https://min.io/docs/minio/kubernetes/upstream/administration/identity-access-management.html
|
|
||||||
req = signer.SignV4(*req, c.accessKeyId, c.secretAccessKey, "", "")
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("request failed: %w", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
// did not get ok, unmarshal resp body for error message
|
|
||||||
respBody, _ := io.ReadAll(resp.Body)
|
|
||||||
if len(respBody) == 0 {
|
|
||||||
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
errResp := &MinioError{}
|
|
||||||
if err := json.Unmarshal(respBody, &errResp); err != nil {
|
|
||||||
return fmt.Errorf("failed to parse response: %w", err)
|
|
||||||
}
|
|
||||||
return errors.New(errResp.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
type args struct {
|
|
||||||
endpointUrl string
|
|
||||||
accessKeyId string
|
|
||||||
secretAccessKey string
|
|
||||||
opt []Option
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantUrl string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "fully qualified path",
|
|
||||||
args: args{endpointUrl: "https://test.com"},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no ssl",
|
|
||||||
args: args{
|
|
||||||
endpointUrl: "test.com",
|
|
||||||
accessKeyId: "username",
|
|
||||||
secretAccessKey: "password",
|
|
||||||
},
|
|
||||||
wantUrl: "http://test.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "with ssl",
|
|
||||||
args: args{
|
|
||||||
endpointUrl: "test.com",
|
|
||||||
accessKeyId: "username",
|
|
||||||
secretAccessKey: "password",
|
|
||||||
opt: []Option{WithUseSsl(true)},
|
|
||||||
},
|
|
||||||
wantUrl: "https://test.com",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := New(tt.args.endpointUrl, tt.args.accessKeyId, tt.args.secretAccessKey, tt.args.opt...)
|
|
||||||
if tt.wantErr {
|
|
||||||
require.Error(err)
|
|
||||||
assert.Nil(got)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.NoError(err)
|
|
||||||
require.NotNil(got)
|
|
||||||
assert.Equal(tt.wantUrl, got.endpointUrl.String())
|
|
||||||
assert.Equal(tt.args.accessKeyId, got.accessKeyId)
|
|
||||||
assert.Equal(tt.args.secretAccessKey, got.secretAccessKey)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_getOpts(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
t.Run("WithUseSsl", func(t *testing.T) {
|
|
||||||
testOpts := getDefaultOptions()
|
|
||||||
assert.Equal(t, false, testOpts.withUseSsl)
|
|
||||||
|
|
||||||
testOpts = getOpts()
|
|
||||||
assert.Equal(t, false, testOpts.withUseSsl)
|
|
||||||
|
|
||||||
testOpts = getOpts(WithUseSsl(true))
|
|
||||||
assert.Equal(t, true, testOpts.withUseSsl)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package client_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/internal/client"
|
|
||||||
internaltest "repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/internal/testing"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestServiceAccount(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
ctx := context.Background()
|
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
|
|
||||||
server := internaltest.NewMinioServer(t)
|
|
||||||
c, err := client.New(server.ApiAddr, server.RootUsername, server.RootPassword)
|
|
||||||
require.NoError(err)
|
|
||||||
assert.NotNil(c)
|
|
||||||
|
|
||||||
creds, err := c.AddServiceAccount(ctx, client.AddServiceAccountReq{})
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
// We just added service account it should be valid
|
|
||||||
err = c.EnsureServiceAccount(ctx, creds.AccessKeyId)
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
err = c.DeleteServiceAccount(ctx, creds.AccessKeyId)
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
// Now that we have deleted the service account it should be invalid
|
|
||||||
err = c.EnsureServiceAccount(ctx, creds.AccessKeyId)
|
|
||||||
require.Error(err)
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package errors
|
package errors
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package errors
|
package errors
|
||||||
|
|||||||
+15
-12
@@ -1,4 +1,4 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package testing
|
package testing
|
||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/internal/client"
|
"github.com/minio/madmin-go/v3"
|
||||||
"github.com/minio/minio-go/v7"
|
"github.com/minio/minio-go/v7"
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
"github.com/ory/dockertest/v3"
|
"github.com/ory/dockertest/v3"
|
||||||
@@ -46,8 +46,8 @@ type MinioServer struct {
|
|||||||
// Client is a MinIO SDK client, created with the default service account
|
// Client is a MinIO SDK client, created with the default service account
|
||||||
// credentials.
|
// credentials.
|
||||||
Client *minio.Client
|
Client *minio.Client
|
||||||
// AdminClient is a client created with the root account credentials.
|
// AdminClient is a Madmin client created with the root account credentials.
|
||||||
AdminClient *client.Client
|
AdminClient *madmin.AdminClient
|
||||||
}
|
}
|
||||||
|
|
||||||
type Option func(testing.TB, *options)
|
type Option func(testing.TB, *options)
|
||||||
@@ -58,7 +58,7 @@ type options struct {
|
|||||||
skipCleanup bool
|
skipCleanup bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultOptions(_ testing.TB) options {
|
func defaultOptions(t testing.TB) options {
|
||||||
return options{
|
return options{
|
||||||
repository: defaultRepository,
|
repository: defaultRepository,
|
||||||
version: defaultVersion,
|
version: defaultVersion,
|
||||||
@@ -139,7 +139,7 @@ func NewMinioServer(t testing.TB, inOpts ...Option) *MinioServer {
|
|||||||
server := &MinioServer{
|
server := &MinioServer{
|
||||||
RootUsername: rootUsername,
|
RootUsername: rootUsername,
|
||||||
RootPassword: rootPassword,
|
RootPassword: rootPassword,
|
||||||
ApiAddr: fmt.Sprintf("[::1]:%s", r.GetPort("9000/tcp")),
|
ApiAddr: fmt.Sprintf("localhost:%s", r.GetPort("9000/tcp")),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the instance is healthy.
|
// Ensure the instance is healthy.
|
||||||
@@ -165,20 +165,23 @@ func NewMinioServer(t testing.TB, inOpts ...Option) *MinioServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create service account.
|
// Create service account.
|
||||||
c, err := client.New(server.ApiAddr, server.RootUsername, server.RootPassword)
|
acl, err := madmin.NewWithOptions(server.ApiAddr, &madmin.Options{
|
||||||
|
Creds: credentials.NewStaticV4(server.RootUsername, server.RootPassword, ""),
|
||||||
|
Secure: false,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
server.AdminClient = c
|
server.AdminClient = acl
|
||||||
|
|
||||||
creds, err := c.AddServiceAccount(context.Background(), client.AddServiceAccountReq{
|
creds, err := acl.AddServiceAccount(context.Background(), madmin.AddServiceAccountReq{
|
||||||
Name: "Boundary MinIO Plugin Test",
|
Name: "Boundary MinIO Plugin Test",
|
||||||
Description: "MinIO credentials for Boundary MinIO plugin testing",
|
Description: "MinIO credentials for Boundary MinIO plugin testing",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
server.ServiceAccountAccessKeyId = creds.AccessKeyId
|
server.ServiceAccountAccessKeyId = creds.AccessKey
|
||||||
server.ServiceAccountSecretAccessKey = creds.SecretAccessKey
|
server.ServiceAccountSecretAccessKey = creds.SecretKey
|
||||||
|
|
||||||
cl, err := minio.New(server.ApiAddr, &minio.Options{
|
cl, err := minio.New(server.ApiAddr, &minio.Options{
|
||||||
Creds: credentials.NewStaticV4(creds.AccessKeyId, creds.SecretAccessKey, ""),
|
Creds: credentials.NewStaticV4(creds.AccessKey, creds.SecretKey, ""),
|
||||||
Secure: false,
|
Secure: false,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package values
|
package values
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
)
|
)
|
||||||
@@ -38,52 +37,6 @@ func GetStringValue(in *structpb.Struct, k string, required bool) (string, error
|
|||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBoolValue returns a boolean value and no error if the given key is found
|
|
||||||
// in the provided proto struct input. An error is returned if the key is not
|
|
||||||
// found and required is true, or the value type is not a boolean.
|
|
||||||
func GetBoolValue(in *structpb.Struct, k string, required bool) (bool, error) {
|
|
||||||
mv := in.GetFields()
|
|
||||||
v, ok := mv[k]
|
|
||||||
if !ok {
|
|
||||||
if required {
|
|
||||||
return false, fmt.Errorf("missing required value %q", k)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
b, ok := v.AsInterface().(bool)
|
|
||||||
if !ok {
|
|
||||||
return false, fmt.Errorf("unexpected type for value %q: want bool, got %T", k, v.AsInterface())
|
|
||||||
}
|
|
||||||
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTimeValue returns a time.Time object and no error if the given key is
|
|
||||||
// found in the provided proto struct input. An error is returned if the value
|
|
||||||
// in the struct isn't a string or the value is not parsable by time.Parse. The
|
|
||||||
// expected time layout is RFC3339Nano.
|
|
||||||
func GetTimeValue(in *structpb.Struct, k string) (time.Time, error) {
|
|
||||||
mv := in.GetFields()
|
|
||||||
v, ok := mv[k]
|
|
||||||
if !ok {
|
|
||||||
return time.Time{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tRaw, ok := v.AsInterface().(string)
|
|
||||||
if !ok {
|
|
||||||
return time.Time{}, fmt.Errorf("unexpected type for value %q: want string, got %T", k, v.AsInterface())
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := time.Parse(time.RFC3339Nano, tRaw)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, fmt.Errorf("could not parse time in value %q: %w", k, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructFields returns a map[string]struct{} of the proto struct input.
|
// StructFields returns a map[string]struct{} of the proto struct input.
|
||||||
func StructFields(s *structpb.Struct) map[string]struct{} {
|
func StructFields(s *structpb.Struct) map[string]struct{} {
|
||||||
m := make(map[string]struct{}, len(s.GetFields()))
|
m := make(map[string]struct{}, len(s.GetFields()))
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package values
|
package values
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
@@ -79,132 +78,3 @@ func TestGetStringValue(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetBoolValue(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
name string
|
|
||||||
in map[string]any
|
|
||||||
key string
|
|
||||||
required bool
|
|
||||||
expected bool
|
|
||||||
expectedErr string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "required missing",
|
|
||||||
in: map[string]any{},
|
|
||||||
key: "foo",
|
|
||||||
required: true,
|
|
||||||
expectedErr: "missing required value \"foo\"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "optional missing",
|
|
||||||
in: map[string]any{},
|
|
||||||
key: "foo",
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "non-bool value",
|
|
||||||
in: map[string]any{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
expectedErr: "unexpected type for value \"foo\": want bool, got string",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "good",
|
|
||||||
in: map[string]any{
|
|
||||||
"foo": true,
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
input, err := structpb.NewStruct(tc.in)
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
actual, err := GetBoolValue(input, tc.key, tc.required)
|
|
||||||
if tc.expectedErr != "" {
|
|
||||||
require.EqualError(err, tc.expectedErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(err)
|
|
||||||
require.Equal(tc.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetTimeValue(t *testing.T) {
|
|
||||||
staticTime := time.Now()
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
name string
|
|
||||||
in map[string]any
|
|
||||||
key string
|
|
||||||
expected time.Time
|
|
||||||
expectedErr string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "missing",
|
|
||||||
in: map[string]any{},
|
|
||||||
key: "foo",
|
|
||||||
expected: time.Time{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "non-time value",
|
|
||||||
in: map[string]any{
|
|
||||||
"foo": 1,
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
expectedErr: "unexpected type for value \"foo\": want string, got float64",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "bad parse",
|
|
||||||
in: map[string]any{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
expectedErr: "could not parse time in value \"foo\": parsing time \"bar\" as \"2006-01-02T15:04:05.999999999Z07:00\": cannot parse \"bar\" as \"2006\"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "good",
|
|
||||||
in: map[string]any{
|
|
||||||
"foo": staticTime.Format(time.RFC3339Nano),
|
|
||||||
},
|
|
||||||
key: "foo",
|
|
||||||
expected: func() time.Time {
|
|
||||||
u, err := time.Parse(time.RFC3339Nano, staticTime.Format(time.RFC3339Nano))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return u
|
|
||||||
}(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
input, err := structpb.NewStruct(tc.in)
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
actual, err := GetTimeValue(input, tc.key)
|
|
||||||
if tc.expectedErr != "" {
|
|
||||||
require.EqualError(err, tc.expectedErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(err)
|
|
||||||
require.Equal(tc.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-202
@@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
MinIO Cloud Storage, (C) 2014-2020 MinIO, Inc.
|
|
||||||
|
|
||||||
This product includes software developed at MinIO, Inc.
|
|
||||||
(https://min.io/).
|
|
||||||
|
|
||||||
The MinIO project contains unmodified/modified subcomponents too with
|
|
||||||
separate copyright notices and license terms. Your use of the source
|
|
||||||
code for these subcomponents is subject to the terms and conditions
|
|
||||||
of Apache License Version 2.0
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
//
|
|
||||||
// MinIO Object Storage (c) 2021 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 madmin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
/* **** SAMPLE ERROR RESPONSE ****
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Error>
|
|
||||||
<Code>AccessDenied</Code>
|
|
||||||
<Message>Access Denied</Message>
|
|
||||||
<BucketName>bucketName</BucketName>
|
|
||||||
<Key>objectName</Key>
|
|
||||||
<RequestId>F19772218238A85A</RequestId>
|
|
||||||
<HostId>GuWkjyviSiGHizehqpmsD1ndz5NClSP19DOT+s2mv7gXGQ8/X1lhbDGiIJEXpGFD</HostId>
|
|
||||||
</Error>
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ErrorResponse - Is the typed error returned by all API operations.
|
|
||||||
type ErrorResponse struct {
|
|
||||||
XMLName xml.Name `xml:"Error" json:"-"`
|
|
||||||
Code string
|
|
||||||
Message string
|
|
||||||
BucketName string
|
|
||||||
Key string
|
|
||||||
RequestID string `xml:"RequestId"`
|
|
||||||
HostID string `xml:"HostId"`
|
|
||||||
|
|
||||||
// Region where the bucket is located. This header is returned
|
|
||||||
// only in HEAD bucket and ListObjects response.
|
|
||||||
Region string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error - Returns HTTP error string
|
|
||||||
func (e ErrorResponse) Error() string {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
reportIssue = "Please report this issue at https://github.com/minio/minio/issues."
|
|
||||||
)
|
|
||||||
|
|
||||||
// httpRespToErrorResponse returns a new encoded ErrorResponse
|
|
||||||
// structure as error.
|
|
||||||
func httpRespToErrorResponse(resp *http.Response) error {
|
|
||||||
if resp == nil || resp.Body == nil {
|
|
||||||
msg := "Response is empty. " + reportIssue
|
|
||||||
return ErrInvalidArgument(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer closeResponse(resp)
|
|
||||||
// Limit to 100K
|
|
||||||
body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 100<<10))
|
|
||||||
if err != nil {
|
|
||||||
return ErrorResponse{
|
|
||||||
Code: resp.Status,
|
|
||||||
Message: fmt.Sprintf("Failed to read server response: %s.", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var errResp ErrorResponse
|
|
||||||
// Decode the json error
|
|
||||||
err = json.Unmarshal(body, &errResp)
|
|
||||||
if err != nil {
|
|
||||||
// We might get errors as XML, try that.
|
|
||||||
xmlErr := xml.Unmarshal(body, &errResp)
|
|
||||||
|
|
||||||
if xmlErr != nil {
|
|
||||||
bodyString := string(body)
|
|
||||||
if !utf8.Valid(body) {
|
|
||||||
bodyString = hex.EncodeToString(body)
|
|
||||||
}
|
|
||||||
if len(bodyString) > 1024 {
|
|
||||||
bodyString = bodyString[:1021] + "..."
|
|
||||||
}
|
|
||||||
return ErrorResponse{
|
|
||||||
Code: resp.Status,
|
|
||||||
Message: fmt.Sprintf("Failed to parse server response (%s): %s", err.Error(), bodyString),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errResp
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToErrorResponse - Returns parsed ErrorResponse struct from body and
|
|
||||||
// http headers.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
//
|
|
||||||
// import admin "github.com/minio/madmin-go"
|
|
||||||
// ...
|
|
||||||
// ...
|
|
||||||
// ss, err := adm.ServiceStatus(...)
|
|
||||||
// if err != nil {
|
|
||||||
// resp := admin.ToErrorResponse(err)
|
|
||||||
// }
|
|
||||||
// ...
|
|
||||||
func ToErrorResponse(err error) ErrorResponse {
|
|
||||||
switch err := err.(type) {
|
|
||||||
case ErrorResponse:
|
|
||||||
return err
|
|
||||||
default:
|
|
||||||
return ErrorResponse{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrInvalidArgument - Invalid argument response.
|
|
||||||
func ErrInvalidArgument(message string) error {
|
|
||||||
return ErrorResponse{
|
|
||||||
Code: "InvalidArgument",
|
|
||||||
Message: message,
|
|
||||||
RequestID: "minio",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
//
|
|
||||||
// MinIO Object Storage (c) 2021 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 madmin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/sha256"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/secure-io/sio-go"
|
|
||||||
"github.com/secure-io/sio-go/sioutil"
|
|
||||||
"golang.org/x/crypto/argon2"
|
|
||||||
"golang.org/x/crypto/pbkdf2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsEncrypted reports whether data is encrypted.
|
|
||||||
func IsEncrypted(data []byte) bool {
|
|
||||||
if len(data) <= 32 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
b := data[32]
|
|
||||||
return b == pbkdf2AESGCM || b == argon2idAESGCM || b == argon2idChaCHa20Poly1305
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptData encrypts the data with an unique key
|
|
||||||
// derived from password using the Argon2id PBKDF.
|
|
||||||
//
|
|
||||||
// The returned ciphertext data consists of:
|
|
||||||
//
|
|
||||||
// salt | AEAD ID | nonce | encrypted data
|
|
||||||
// 32 1 8 ~ len(data)
|
|
||||||
func EncryptData(password string, data []byte) ([]byte, error) {
|
|
||||||
salt := sioutil.MustRandom(32)
|
|
||||||
|
|
||||||
var (
|
|
||||||
id byte
|
|
||||||
err error
|
|
||||||
stream *sio.Stream
|
|
||||||
)
|
|
||||||
if FIPSEnabled() {
|
|
||||||
key := pbkdf2.Key([]byte(password), salt, pbkdf2Cost, 32, sha256.New)
|
|
||||||
stream, err = sio.AES_256_GCM.Stream(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
id = pbkdf2AESGCM
|
|
||||||
} else {
|
|
||||||
key := argon2.IDKey([]byte(password), salt, argon2idTime, argon2idMemory, argon2idThreads, 32)
|
|
||||||
if sioutil.NativeAES() {
|
|
||||||
stream, err = sio.AES_256_GCM.Stream(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
id = argon2idAESGCM
|
|
||||||
} else {
|
|
||||||
stream, err = sio.ChaCha20Poly1305.Stream(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
id = argon2idChaCHa20Poly1305
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce := sioutil.MustRandom(stream.NonceSize())
|
|
||||||
|
|
||||||
// ciphertext = salt || AEAD ID | nonce | encrypted data
|
|
||||||
// cLen calculation: Modified by Hashicorp in 2026 to make arithmetic operations overflow-safe by casting to int64 before addition
|
|
||||||
cLen := int64(len(salt)) + 1 + int64(len(nonce)) + int64(len(data)) + stream.Overhead(int64(len(data)))
|
|
||||||
ciphertext := bytes.NewBuffer(make([]byte, 0, cLen)) // pre-alloc correct length
|
|
||||||
|
|
||||||
// Prefix the ciphertext with salt, AEAD ID and nonce
|
|
||||||
ciphertext.Write(salt)
|
|
||||||
ciphertext.WriteByte(id)
|
|
||||||
ciphertext.Write(nonce)
|
|
||||||
|
|
||||||
w := stream.EncryptWriter(ciphertext, nonce, nil)
|
|
||||||
if _, err = w.Write(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = w.Close(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ciphertext.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrMaliciousData indicates that the stream cannot be
|
|
||||||
// decrypted by provided credentials.
|
|
||||||
var ErrMaliciousData = sio.NotAuthentic
|
|
||||||
|
|
||||||
// DecryptData decrypts the data with the key derived
|
|
||||||
// from the salt (part of data) and the password using
|
|
||||||
// the PBKDF used in EncryptData. DecryptData returns
|
|
||||||
// the decrypted plaintext on success.
|
|
||||||
//
|
|
||||||
// The data must be a valid ciphertext produced by
|
|
||||||
// EncryptData. Otherwise, the decryption will fail.
|
|
||||||
func DecryptData(password string, data io.Reader) ([]byte, error) {
|
|
||||||
var (
|
|
||||||
salt [32]byte
|
|
||||||
id [1]byte
|
|
||||||
nonce [8]byte // This depends on the AEAD but both used ciphers have the same nonce length.
|
|
||||||
)
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(data, salt[:]); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(data, id[:]); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(data, nonce[:]); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
stream *sio.Stream
|
|
||||||
)
|
|
||||||
switch {
|
|
||||||
case id[0] == argon2idAESGCM:
|
|
||||||
key := argon2.IDKey([]byte(password), salt[:], argon2idTime, argon2idMemory, argon2idThreads, 32)
|
|
||||||
stream, err = sio.AES_256_GCM.Stream(key)
|
|
||||||
case id[0] == argon2idChaCHa20Poly1305:
|
|
||||||
key := argon2.IDKey([]byte(password), salt[:], argon2idTime, argon2idMemory, argon2idThreads, 32)
|
|
||||||
stream, err = sio.ChaCha20Poly1305.Stream(key)
|
|
||||||
case id[0] == pbkdf2AESGCM:
|
|
||||||
key := pbkdf2.Key([]byte(password), salt[:], pbkdf2Cost, 32, sha256.New)
|
|
||||||
stream, err = sio.AES_256_GCM.Stream(key)
|
|
||||||
default:
|
|
||||||
err = errors.New("madmin: invalid encryption algorithm ID")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
plaintext, err := ioutil.ReadAll(stream.DecryptReader(data, nonce[:], nil))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return plaintext, err
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
argon2idAESGCM = 0x00
|
|
||||||
argon2idChaCHa20Poly1305 = 0x01
|
|
||||||
pbkdf2AESGCM = 0x02
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
argon2idTime = 1
|
|
||||||
argon2idMemory = 64 * 1024
|
|
||||||
argon2idThreads = 4
|
|
||||||
pbkdf2Cost = 8192
|
|
||||||
)
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
//
|
|
||||||
// MinIO Object Storage (c) 2021 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 madmin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var encryptDataTests = []struct {
|
|
||||||
Password string
|
|
||||||
Data []byte
|
|
||||||
}{
|
|
||||||
{Password: "", Data: nil},
|
|
||||||
{Password: "", Data: make([]byte, 256)},
|
|
||||||
{Password: `xPl.8/rhR"Q_1xLt`, Data: make([]byte, 32)},
|
|
||||||
{Password: "m69?yz4W-!k+7p0", Data: make([]byte, 1024*1024)},
|
|
||||||
{Password: `7h5oU4$te{;K}fgqlI^]`, Data: make([]byte, 256)},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncryptData(t *testing.T) {
|
|
||||||
for i, test := range encryptDataTests {
|
|
||||||
i, test := i, test
|
|
||||||
t.Run(fmt.Sprintf("Test-%d", i), func(t *testing.T) {
|
|
||||||
ciphertext, err := EncryptData(test.Password, test.Data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to encrypt data: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
plaintext, err := DecryptData(test.Password, bytes.NewReader(ciphertext))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to decrypt data: %v", err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(plaintext, test.Data) {
|
|
||||||
t.Fatal("Decrypt plaintext does not match origin data")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var decryptDataTests = []struct {
|
|
||||||
Password string
|
|
||||||
Data string
|
|
||||||
}{
|
|
||||||
{Password: "", Data: "828aa81599df0651c0461adb82283e8b89956baee9f6e719947ef9cddc849028001dc9d3ac0938f66b07bacc9751437e1985f8a9763c240e81"},
|
|
||||||
|
|
||||||
{Password: "", Data: "1793c71df6647860437134073c15688cbb15961dc0758c7ee1225e66e79c724c00d790dba9c671eae89da2c736d858286ac9bd027abacc6443" +
|
|
||||||
"0375cd41b63b67c070c7fba475a8dd66ae65ba905176c48cbe6f734fc74df87343d8ccff54bada4aeb0a04bd021633ebe6c4768e23f5dea142" +
|
|
||||||
"561d4fe3f90ed59d13dc5fb3a585dadec1742325291b9c81692bdd3420b2428127f8195e0ecd9a1c9237712ed67af7339fbbf7ff3ee1c516e1" +
|
|
||||||
"f81e69d933e057b30997e7274a2c9698e07c39f0e8d6818858f34c8191871b5a52bea9061806bd029024bfc1d9c1f230904968d6c9e10fddcb" +
|
|
||||||
"c006ba97356ff243570fd96df07dd6894e215a6b24c4ed730369519289ebd877aff6ccbd2265985e4ab1a2b7930bab9cfb767b97348a639ddf" +
|
|
||||||
"8db81bf5151da7e8f3d9638a1b86eb1dd78cc6a526f10a414c78638f"},
|
|
||||||
|
|
||||||
{Password: `xPl.8/rhR"Q_1xLt`, Data: "b5c016e93b84b473fc8a37af94936563630c36d6df1841d23a86ee51ca161f9e00ac19116b32f643ff6a56a212b265d8c56" +
|
|
||||||
"195bb0d12ce199e13dfdc5272f80c1564da2c6fc2fa18da91d8062de02af5cdafea491c6f3cae1f"},
|
|
||||||
|
|
||||||
{Password: `7h5oU4$te{;K}fgqlI^]`, Data: "c58edf7cfd557b6b655de6f48b1a3049d8d049dadb3a7bfa9ac9ccbb5baf37ec00f83086a26f43b7d6bc9075ad0" +
|
|
||||||
"38bf5741f118d502ebe94165e4072ba7f98535d6b1e3b6ae67a98115d146d9b4d90e4df4ae82df9cfa17ed7cd42" +
|
|
||||||
"465181559f7ddf09c98beec521bb4478e0cb73c4e0827af8688ff4e7a07327a10d5a180035e6ddb16d974a85257" +
|
|
||||||
"981cd9e0360a20f7b4d653190267dfb241148f018ae180568042e864b9e1b5bc05425a3abc2b0324f50c72d5679" +
|
|
||||||
"8f924405dfc0f8523f4bb564ed65af8e1b1c82a7a0640552ecf81985d95d0993d99172592ddc1393dfa63e8f0b3" +
|
|
||||||
"d744b2cc4b73384ca4693f0c1aec0e9b00e85f2937e891105d67da8f59c14ca96608e0425c42f9c1e7c2a8b3413" +
|
|
||||||
"e1381784f9cfe01de7c47cea1f8d7a7d88f5d4aca783cf55332b47f957a6b9a65269d7eb606b877b"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecryptData(t *testing.T) {
|
|
||||||
for i, test := range decryptDataTests {
|
|
||||||
i, test := i, test
|
|
||||||
t.Run(fmt.Sprintf("Test-%d", i), func(t *testing.T) {
|
|
||||||
ciphertext, err := hex.DecodeString(test.Data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to decode ciphertext data: %v", err)
|
|
||||||
}
|
|
||||||
_, err = DecryptData(test.Password, bytes.NewReader(ciphertext))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to decrypt data: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsDecrypted(t *testing.T) {
|
|
||||||
for i, test := range decryptDataTests {
|
|
||||||
i, test := i, test
|
|
||||||
t.Run(fmt.Sprintf("Test-%d", i), func(t *testing.T) {
|
|
||||||
ciphertext, err := hex.DecodeString(test.Data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to decode ciphertext data: %v", err)
|
|
||||||
}
|
|
||||||
if !IsEncrypted(ciphertext) {
|
|
||||||
t.Fatal("Ciphertext is not encrypted")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
module repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/madmin
|
|
||||||
|
|
||||||
go 1.25.0
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/minio/minio-go/v7 v7.0.83
|
|
||||||
github.com/secure-io/sio-go v0.3.1
|
|
||||||
golang.org/x/crypto v0.55.0
|
|
||||||
)
|
|
||||||
|
|
||||||
require golang.org/x/sys v0.47.0 // indirect
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
github.com/minio/minio-go/v7 v7.0.83 h1:W4Kokksvlz3OKf3OqIlzDNKd4MERlC2oN8YptwJ0+GA=
|
|
||||||
github.com/minio/minio-go/v7 v7.0.83/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY=
|
|
||||||
github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc=
|
|
||||||
github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
|
||||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
|
||||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
// MinIO Object Storage (c) 2021 MinIO, Inc.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// MinIO Object Storage (c) 2021 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.
|
|
||||||
//
|
|
||||||
|
|
||||||
//go:build !fips
|
|
||||||
// +build !fips
|
|
||||||
|
|
||||||
package madmin
|
|
||||||
|
|
||||||
// FIPSEnabled returns true if and only if FIPS 140-2 support
|
|
||||||
// is enabled.
|
|
||||||
//
|
|
||||||
// FIPS 140-2 requires that only specifc cryptographic
|
|
||||||
// primitives, like AES or SHA-256, are used and that
|
|
||||||
// those primitives are implemented by a FIPS 140-2
|
|
||||||
// certified cryptographic module.
|
|
||||||
func FIPSEnabled() bool { return false }
|
|
||||||
-119
@@ -1,119 +0,0 @@
|
|||||||
//
|
|
||||||
// MinIO Object Storage (c) 2021 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 madmin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7/pkg/s3utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AdminAPIVersion - admin api version used in the request.
|
|
||||||
const (
|
|
||||||
AdminAPIVersion = "v3"
|
|
||||||
AdminAPIVersionV2 = "v2"
|
|
||||||
adminAPIPrefix = "/" + AdminAPIVersion
|
|
||||||
kmsAPIVersion = "v1"
|
|
||||||
kmsAPIPrefix = "/" + kmsAPIVersion
|
|
||||||
)
|
|
||||||
|
|
||||||
// getEndpointURL - construct a new endpoint.
|
|
||||||
func getEndpointURL(endpoint string, secure bool) (*url.URL, error) {
|
|
||||||
if strings.Contains(endpoint, ":") {
|
|
||||||
host, _, err := net.SplitHostPort(endpoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !s3utils.IsValidIP(host) && !s3utils.IsValidDomain(host) {
|
|
||||||
msg := "Endpoint: " + endpoint + " does not follow ip address or domain name standards."
|
|
||||||
return nil, ErrInvalidArgument(msg)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !s3utils.IsValidIP(endpoint) && !s3utils.IsValidDomain(endpoint) {
|
|
||||||
msg := "Endpoint: " + endpoint + " does not follow ip address or domain name standards."
|
|
||||||
return nil, ErrInvalidArgument(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If secure is false, use 'http' scheme.
|
|
||||||
scheme := "https"
|
|
||||||
if !secure {
|
|
||||||
scheme = "http"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip the obvious :443 and :80 from the endpoint
|
|
||||||
// to avoid the signature mismatch error.
|
|
||||||
if secure && strings.HasSuffix(endpoint, ":443") {
|
|
||||||
endpoint = strings.TrimSuffix(endpoint, ":443")
|
|
||||||
}
|
|
||||||
if !secure && strings.HasSuffix(endpoint, ":80") {
|
|
||||||
endpoint = strings.TrimSuffix(endpoint, ":80")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a secured endpoint URL.
|
|
||||||
endpointURLStr := scheme + "://" + endpoint
|
|
||||||
endpointURL, err := url.Parse(endpointURLStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate incoming endpoint URL.
|
|
||||||
if err := isValidEndpointURL(endpointURL.String()); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return endpointURL, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify if input endpoint URL is valid.
|
|
||||||
func isValidEndpointURL(endpointURL string) error {
|
|
||||||
if endpointURL == "" {
|
|
||||||
return ErrInvalidArgument("Endpoint url cannot be empty.")
|
|
||||||
}
|
|
||||||
url, err := url.Parse(endpointURL)
|
|
||||||
if err != nil {
|
|
||||||
return ErrInvalidArgument("Endpoint url cannot be parsed.")
|
|
||||||
}
|
|
||||||
if url.Path != "/" && url.Path != "" {
|
|
||||||
return ErrInvalidArgument("Endpoint url cannot have fully qualified paths.")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// closeResponse close non nil response with any response Body.
|
|
||||||
// convenient wrapper to drain any remaining data on response body.
|
|
||||||
//
|
|
||||||
// Subsequently this allows golang http RoundTripper
|
|
||||||
// to re-use the same connection for future requests.
|
|
||||||
func closeResponse(resp *http.Response) {
|
|
||||||
// Callers should close resp.Body when done reading from it.
|
|
||||||
// If resp.Body is not closed, the Client's underlying RoundTripper
|
|
||||||
// (typically Transport) may not be able to re-use a persistent TCP
|
|
||||||
// connection to the server for a subsequent "keep-alive" request.
|
|
||||||
if resp != nil && resp.Body != nil {
|
|
||||||
// Drain any remaining Body and then close the connection.
|
|
||||||
// Without this closing connection would disallow re-using
|
|
||||||
// the same connection for future uses.
|
|
||||||
// - http://stackoverflow.com/a/17961593/4465767
|
|
||||||
io.Copy(ioutil.Discard, resp.Body)
|
|
||||||
resp.Body.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package madmin
|
|
||||||
|
|
||||||
import "net/url"
|
|
||||||
|
|
||||||
// GetEndpointURL is a wrapper added by Hashicorp in 2025 to export the getEndpointURL
|
|
||||||
// function in utils.go
|
|
||||||
func GetEndpointURL(endpoint string, secure bool) (*url.URL, error) {
|
|
||||||
return getEndpointURL(endpoint, secure)
|
|
||||||
}
|
|
||||||
+3
-3
@@ -1,11 +1,11 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/plugin/service/storage"
|
"github.com/hashicorp/boundary-plugin-minio/plugin/service/storage"
|
||||||
pb "repo.anhonesthost.net/CyberCoveLLC/threshold/sdk/pbs/plugin"
|
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ boundary storage-buckets create \
|
|||||||
-worker-filter '"minio-access" in "/tags/type"' \
|
-worker-filter '"minio-access" in "/tags/type"' \
|
||||||
-attr endpoint_url="https://my-minio-instance.dev/s3-api" \
|
-attr endpoint_url="https://my-minio-instance.dev/s3-api" \
|
||||||
-attr region=REGION \
|
-attr region=REGION \
|
||||||
-attr disable_credential_rotation=true \
|
|
||||||
-secret access_key_id='KEY' \
|
-secret access_key_id='KEY' \
|
||||||
-secret secret_access_key='SECRET'
|
-secret secret_access_key='SECRET'
|
||||||
```
|
```
|
||||||
@@ -25,11 +24,6 @@ The following `attributes` are valid on a MinIO storage bucket resource:
|
|||||||
- `endpoint_url` (string, required): Fully-qualified endpoint pointing to a
|
- `endpoint_url` (string, required): Fully-qualified endpoint pointing to a
|
||||||
MinIO S3 API.
|
MinIO S3 API.
|
||||||
- `region` (string, optional): The region to configure the storage bucket for.
|
- `region` (string, optional): The region to configure the storage bucket for.
|
||||||
- `disable_credential_rotation` (bool, optional): Controls whether the plugin
|
|
||||||
will rotate the incoming credentials and manage a new MinIO service account.
|
|
||||||
If this attribute is set to `false`, or not provided, the plugin will rotate
|
|
||||||
the incoming credentials, using them to create a new MinIO service account,
|
|
||||||
then delete the incoming credentials.
|
|
||||||
|
|
||||||
### Secrets
|
### Secrets
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package storage
|
package storage
|
||||||
@@ -6,10 +6,9 @@ package storage
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/internal/errors"
|
"github.com/hashicorp/boundary-plugin-minio/internal/errors"
|
||||||
"repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/internal/values"
|
"github.com/hashicorp/boundary-plugin-minio/internal/values"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
@@ -20,8 +19,6 @@ const (
|
|||||||
ConstAccessKeyId = "access_key_id"
|
ConstAccessKeyId = "access_key_id"
|
||||||
ConstSecretAccessKey = "secret_access_key"
|
ConstSecretAccessKey = "secret_access_key"
|
||||||
ConstRegion = "region"
|
ConstRegion = "region"
|
||||||
ConstDisableCredentialRotation = "disable_credential_rotation"
|
|
||||||
ConstLastRotatedTime = "creds_last_rotated_time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type StorageAttributes struct {
|
type StorageAttributes struct {
|
||||||
@@ -34,11 +31,6 @@ type StorageAttributes struct {
|
|||||||
// UseSSL determines if the MinIO SDK will use SSL when communicating with
|
// UseSSL determines if the MinIO SDK will use SSL when communicating with
|
||||||
// the server. This field is determined based on the endpoint URL prefix.
|
// the server. This field is determined based on the endpoint URL prefix.
|
||||||
UseSSL bool
|
UseSSL bool
|
||||||
// DisableCredentialRotation is used to configure whether this plugin should
|
|
||||||
// manage the MinIO credentials or not. By default, this field is set to
|
|
||||||
// false. See the plugin's OnCreateStorageBucket, OnUpdateStorageBucket and
|
|
||||||
// OnDeleteStorageBucket for how we use this.
|
|
||||||
DisableCredentialRotation bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type StorageSecrets struct {
|
type StorageSecrets struct {
|
||||||
@@ -48,10 +40,6 @@ type StorageSecrets struct {
|
|||||||
// SecretAccessKey is the MinIO Secret Access Key. This field is required
|
// SecretAccessKey is the MinIO Secret Access Key. This field is required
|
||||||
// and comes from user input.
|
// and comes from user input.
|
||||||
SecretAccessKey string
|
SecretAccessKey string
|
||||||
// LastRotatedTime is the time the secrets contained in this object were
|
|
||||||
// last rotated. Will be set to the zero-value (time.Time{}) if the
|
|
||||||
// credentials haven't been rotated.
|
|
||||||
LastRotatedTime time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsMap returns a map StorageAttributes's secret fields as a map.
|
// AsMap returns a map StorageAttributes's secret fields as a map.
|
||||||
@@ -59,17 +47,6 @@ func (sa *StorageSecrets) AsMap() map[string]any {
|
|||||||
return map[string]any{
|
return map[string]any{
|
||||||
ConstAccessKeyId: sa.AccessKeyId,
|
ConstAccessKeyId: sa.AccessKeyId,
|
||||||
ConstSecretAccessKey: sa.SecretAccessKey,
|
ConstSecretAccessKey: sa.SecretAccessKey,
|
||||||
ConstLastRotatedTime: sa.LastRotatedTime.Format(time.RFC3339Nano),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone returns a new StorageSecrets object with the same state as the incoming
|
|
||||||
// one.
|
|
||||||
func (sec *StorageSecrets) Clone() *StorageSecrets {
|
|
||||||
return &StorageSecrets{
|
|
||||||
AccessKeyId: sec.AccessKeyId,
|
|
||||||
SecretAccessKey: sec.SecretAccessKey,
|
|
||||||
LastRotatedTime: sec.LastRotatedTime,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,12 +83,6 @@ func getStorageAttributes(inAttributes *structpb.Struct) (*StorageAttributes, er
|
|||||||
}
|
}
|
||||||
delete(unknownAttrFields, ConstRegion)
|
delete(unknownAttrFields, ConstRegion)
|
||||||
|
|
||||||
disableCredentialRotation, err := values.GetBoolValue(inAttributes, ConstDisableCredentialRotation, false)
|
|
||||||
if err != nil {
|
|
||||||
badFields[fmt.Sprintf("attributes.%s", ConstDisableCredentialRotation)] = err.Error()
|
|
||||||
}
|
|
||||||
delete(unknownAttrFields, ConstDisableCredentialRotation)
|
|
||||||
|
|
||||||
for s := range unknownAttrFields {
|
for s := range unknownAttrFields {
|
||||||
badFields[fmt.Sprintf("attributes.%s", s)] = "unrecognized field"
|
badFields[fmt.Sprintf("attributes.%s", s)] = "unrecognized field"
|
||||||
}
|
}
|
||||||
@@ -123,7 +94,6 @@ func getStorageAttributes(inAttributes *structpb.Struct) (*StorageAttributes, er
|
|||||||
EndpointUrl: endpointUrl,
|
EndpointUrl: endpointUrl,
|
||||||
Region: region,
|
Region: region,
|
||||||
UseSSL: useSSL,
|
UseSSL: useSSL,
|
||||||
DisableCredentialRotation: disableCredentialRotation,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,12 +118,6 @@ func getStorageSecrets(inSecrets *structpb.Struct) (*StorageSecrets, error) {
|
|||||||
}
|
}
|
||||||
delete(unknownSecretFields, ConstSecretAccessKey)
|
delete(unknownSecretFields, ConstSecretAccessKey)
|
||||||
|
|
||||||
lastRotated, err := values.GetTimeValue(inSecrets, ConstLastRotatedTime)
|
|
||||||
if err != nil {
|
|
||||||
badFields[fmt.Sprintf("secrets.%s", ConstLastRotatedTime)] = err.Error()
|
|
||||||
}
|
|
||||||
delete(unknownSecretFields, ConstLastRotatedTime)
|
|
||||||
|
|
||||||
for s := range unknownSecretFields {
|
for s := range unknownSecretFields {
|
||||||
badFields[fmt.Sprintf("secrets.%s", s)] = "unrecognized field"
|
badFields[fmt.Sprintf("secrets.%s", s)] = "unrecognized field"
|
||||||
}
|
}
|
||||||
@@ -164,6 +128,5 @@ func getStorageSecrets(inSecrets *structpb.Struct) (*StorageSecrets, error) {
|
|||||||
return &StorageSecrets{
|
return &StorageSecrets{
|
||||||
AccessKeyId: accessKeyId,
|
AccessKeyId: accessKeyId,
|
||||||
SecretAccessKey: secretAccessKey,
|
SecretAccessKey: secretAccessKey,
|
||||||
LastRotatedTime: lastRotated,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
@@ -90,30 +89,18 @@ func TestGetStorageAttributes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expErrMsg: "attributes.endpoint_url: missing required value \"endpoint_url\"",
|
expErrMsg: "attributes.endpoint_url: missing required value \"endpoint_url\"",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "disableCredRotationBadType",
|
|
||||||
inAttributes: &structpb.Struct{
|
|
||||||
Fields: map[string]*structpb.Value{
|
|
||||||
ConstEndpointUrl: structpb.NewStringValue("http://bar"),
|
|
||||||
ConstDisableCredentialRotation: structpb.NewStringValue("notbool"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expErrMsg: "attributes.disable_credential_rotation: unexpected type for value \"disable_credential_rotation\": want bool, got string",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "successWithNoSSL",
|
name: "successWithNoSSL",
|
||||||
inAttributes: &structpb.Struct{
|
inAttributes: &structpb.Struct{
|
||||||
Fields: map[string]*structpb.Value{
|
Fields: map[string]*structpb.Value{
|
||||||
ConstEndpointUrl: structpb.NewStringValue("http://foo.bar"),
|
ConstEndpointUrl: structpb.NewStringValue("http://foo.bar"),
|
||||||
ConstRegion: structpb.NewStringValue("us-east-1"),
|
ConstRegion: structpb.NewStringValue("us-east-1"),
|
||||||
ConstDisableCredentialRotation: structpb.NewBoolValue(true),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expStorageAttributes: &StorageAttributes{
|
expStorageAttributes: &StorageAttributes{
|
||||||
EndpointUrl: "foo.bar",
|
EndpointUrl: "foo.bar",
|
||||||
Region: "us-east-1",
|
Region: "us-east-1",
|
||||||
UseSSL: false,
|
UseSSL: false,
|
||||||
DisableCredentialRotation: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -228,17 +215,6 @@ func TestGetStorageSecrets(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expErrMsg: "secrets.secret_access_key: unexpected type for value \"secret_access_key\": want string, got bool",
|
expErrMsg: "secrets.secret_access_key: unexpected type for value \"secret_access_key\": want string, got bool",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "lastRotatedBadType",
|
|
||||||
inSecrets: &structpb.Struct{
|
|
||||||
Fields: map[string]*structpb.Value{
|
|
||||||
ConstAccessKeyId: structpb.NewStringValue("foo"),
|
|
||||||
ConstSecretAccessKey: structpb.NewStringValue("bar"),
|
|
||||||
ConstLastRotatedTime: structpb.NewStringValue("nottime"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expErrMsg: "secrets.creds_last_rotated_time: could not parse time in value \"creds_last_rotated_time\"",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "unknownSecret",
|
name: "unknownSecret",
|
||||||
inSecrets: &structpb.Struct{
|
inSecrets: &structpb.Struct{
|
||||||
@@ -263,21 +239,6 @@ func TestGetStorageSecrets(t *testing.T) {
|
|||||||
SecretAccessKey: "bar",
|
SecretAccessKey: "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "successWithLastRotatedTime",
|
|
||||||
inSecrets: &structpb.Struct{
|
|
||||||
Fields: map[string]*structpb.Value{
|
|
||||||
ConstAccessKeyId: structpb.NewStringValue("foo"),
|
|
||||||
ConstSecretAccessKey: structpb.NewStringValue("bar"),
|
|
||||||
ConstLastRotatedTime: structpb.NewStringValue(time.Date(2024, 01, 02, 03, 04, 05, 06, time.UTC).Format(time.RFC3339Nano)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expStorageSecrets: &StorageSecrets{
|
|
||||||
AccessKeyId: "foo",
|
|
||||||
SecretAccessKey: "bar",
|
|
||||||
LastRotatedTime: time.Date(2024, 01, 02, 03, 04, 05, 06, time.UTC),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@@ -307,12 +268,10 @@ func TestStorageSecretsAsMap(t *testing.T) {
|
|||||||
in: &StorageSecrets{
|
in: &StorageSecrets{
|
||||||
AccessKeyId: "access_key_id_value",
|
AccessKeyId: "access_key_id_value",
|
||||||
SecretAccessKey: "secret_access_key_value",
|
SecretAccessKey: "secret_access_key_value",
|
||||||
LastRotatedTime: time.Date(2024, 01, 02, 03, 04, 05, 06, time.UTC),
|
|
||||||
},
|
},
|
||||||
expMap: map[string]any{
|
expMap: map[string]any{
|
||||||
"access_key_id": "access_key_id_value",
|
"access_key_id": "access_key_id_value",
|
||||||
"secret_access_key": "secret_access_key_value",
|
"secret_access_key": "secret_access_key_value",
|
||||||
"creds_last_rotated_time": "2024-01-02T03:04:05.000000006Z",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -324,18 +283,3 @@ func TestStorageSecretsAsMap(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTestStorageSecretsClone(t *testing.T) {
|
|
||||||
now := time.Now()
|
|
||||||
in := &StorageSecrets{
|
|
||||||
AccessKeyId: "foo",
|
|
||||||
SecretAccessKey: "bar",
|
|
||||||
LastRotatedTime: now,
|
|
||||||
}
|
|
||||||
|
|
||||||
secCl := in.Clone()
|
|
||||||
require.Equal(t, in.AccessKeyId, secCl.AccessKeyId)
|
|
||||||
require.Equal(t, in.AccessKeyId, secCl.AccessKeyId)
|
|
||||||
require.Equal(t, in.LastRotatedTime, secCl.LastRotatedTime)
|
|
||||||
require.NotSame(t, in, secCl)
|
|
||||||
}
|
|
||||||
@@ -1,29 +1,22 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/internal/client"
|
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
|
||||||
"repo.anhonesthost.net/CyberCoveLLC/threshold/sdk/pbs/controller/api/resources/storagebuckets"
|
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||||
pb "repo.anhonesthost.net/CyberCoveLLC/threshold/sdk/pbs/plugin"
|
madmin "github.com/minio/madmin-go/v3"
|
||||||
"github.com/hashicorp/go-multierror"
|
|
||||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
|
||||||
"github.com/minio/minio-go/v7"
|
"github.com/minio/minio-go/v7"
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
@@ -34,52 +27,12 @@ import (
|
|||||||
|
|
||||||
var _ pb.StoragePluginServiceServer = (*StoragePlugin)(nil)
|
var _ pb.StoragePluginServiceServer = (*StoragePlugin)(nil)
|
||||||
|
|
||||||
const (
|
|
||||||
defaultGetObjectChunkSize = uint32(1 << 20) // 1 MiB
|
|
||||||
)
|
|
||||||
|
|
||||||
// StoragePlugin implements the StoragePluginServiceServer interface for the
|
// StoragePlugin implements the StoragePluginServiceServer interface for the
|
||||||
// MinIO storage service plugin.
|
// MinIO storage service plugin.
|
||||||
type StoragePlugin struct {
|
type StoragePlugin struct {
|
||||||
pb.UnimplementedStoragePluginServiceServer
|
pb.UnimplementedStoragePluginServiceServer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NormalizeStorageBucketData is called by boundary before calling OnCreate or OnUpdate
|
|
||||||
//
|
|
||||||
// minio supports 3 attributes:
|
|
||||||
// - endpoint_url
|
|
||||||
// - region
|
|
||||||
// - disable_credential_rotation
|
|
||||||
//
|
|
||||||
// of these, only endpoint is required and normalized. region and disable_credential_rotation
|
|
||||||
// are passed as-is, if they were provided
|
|
||||||
func (sp *StoragePlugin) NormalizeStorageBucketData(ctx context.Context, req *pb.NormalizeStorageBucketDataRequest) (*pb.NormalizeStorageBucketDataResponse, error) {
|
|
||||||
sa, err := getStorageAttributes(req.GetAttributes())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := req.GetAttributes().GetFields()
|
|
||||||
|
|
||||||
// re-add the scheme since getStorageAttributes removes it
|
|
||||||
if sa.UseSSL {
|
|
||||||
sa.EndpointUrl = "https://" + sa.EndpointUrl
|
|
||||||
} else {
|
|
||||||
sa.EndpointUrl = "http://" + sa.EndpointUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
if sa.EndpointUrl, err = parseutil.NormalizeAddr(sa.EndpointUrl); err != nil {
|
|
||||||
return nil, status.New(codes.InvalidArgument, fmt.Sprintf("failed to normalize provided %s: %s", ConstEndpointUrl, err.Error())).Err()
|
|
||||||
}
|
|
||||||
fields[ConstEndpointUrl] = structpb.NewStringValue(sa.EndpointUrl)
|
|
||||||
|
|
||||||
return &pb.NormalizeStorageBucketDataResponse{
|
|
||||||
Attributes: &structpb.Struct{
|
|
||||||
Fields: fields,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnCreateStorageBucket is a hook that runs when a storage bucket is created.
|
// OnCreateStorageBucket is a hook that runs when a storage bucket is created.
|
||||||
func (sp *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCreateStorageBucketRequest) (*pb.OnCreateStorageBucketResponse, error) {
|
func (sp *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCreateStorageBucketRequest) (*pb.OnCreateStorageBucketResponse, error) {
|
||||||
bucket := req.GetBucket()
|
bucket := req.GetBucket()
|
||||||
@@ -100,14 +53,9 @@ func (sp *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCr
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteOldCredsFn := func() error { return nil }
|
err = ensureServiceAccount(ctx, sa, sec)
|
||||||
if !sa.DisableCredentialRotation {
|
|
||||||
newSec, fn, err := rotateCredentials(ctx, bucket.GetBucketName(), sa, sec)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "failed to rotate minio credentials: %v", err)
|
return nil, status.Errorf(codes.InvalidArgument, "failed to ensure service account: %v", err)
|
||||||
}
|
|
||||||
sec = newSec
|
|
||||||
deleteOldCredsFn = fn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cl, err := minio.New(sa.EndpointUrl, &minio.Options{
|
cl, err := minio.New(sa.EndpointUrl, &minio.Options{
|
||||||
@@ -119,28 +67,15 @@ func (sp *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCr
|
|||||||
return nil, status.Errorf(codes.Unknown, "failed to create minio sdk client: %v", err)
|
return nil, status.Errorf(codes.Unknown, "failed to create minio sdk client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p, dryRunErr := dryRun(ctx, cl, bucket)
|
err = dryRun(ctx, cl, bucket)
|
||||||
if dryRunErr.Len() > 0 {
|
|
||||||
st := status.New(codes.InvalidArgument, fmt.Sprintf("failed to verify provided minio environment: %v", dryRunErr.Unwrap()))
|
|
||||||
|
|
||||||
st, err = st.WithDetails(&pb.StorageBucketCredentialState{State: p})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st = status.New(codes.Unknown, fmt.Sprintf("failed to verify provided minio environment: %v", dryRunErr.Unwrap()))
|
return nil, status.Errorf(codes.Unknown, "failed to verify provided minio environment: %v", err)
|
||||||
}
|
|
||||||
return nil, st.Err()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
persistedData, err := structpb.NewStruct(sec.AsMap())
|
persistedData, err := structpb.NewStruct(sec.AsMap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed to convert storage attributes to protobuf struct: %v", err)
|
return nil, status.Errorf(codes.Internal, "failed to convert storage attributes to protobuf struct: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given that at this point everything is OK with the bucket, we shouldn't
|
|
||||||
// let a failure to delete the old credentials fail the entire process. If
|
|
||||||
// we ever establish a way to log non-critical errors in the plugin, log
|
|
||||||
// this.
|
|
||||||
_ = deleteOldCredsFn()
|
|
||||||
|
|
||||||
return &pb.OnCreateStorageBucketResponse{
|
return &pb.OnCreateStorageBucketResponse{
|
||||||
Persisted: &storagebuckets.StorageBucketPersisted{Data: persistedData},
|
Persisted: &storagebuckets.StorageBucketPersisted{Data: persistedData},
|
||||||
}, nil
|
}, nil
|
||||||
@@ -155,22 +90,24 @@ func (sp *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUp
|
|||||||
if newBucket.BucketName == "" {
|
if newBucket.BucketName == "" {
|
||||||
return nil, status.Error(codes.InvalidArgument, "new bucketName is required")
|
return nil, status.Error(codes.InvalidArgument, "new bucketName is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
oldBucket := req.GetCurrentBucket()
|
oldBucket := req.GetCurrentBucket()
|
||||||
if oldBucket == nil {
|
if oldBucket == nil {
|
||||||
return nil, status.Error(codes.InvalidArgument, "current bucket is required")
|
return nil, status.Error(codes.InvalidArgument, "current bucket is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// current attributes
|
||||||
osa, err := getStorageAttributes(oldBucket.GetAttributes())
|
osa, err := getStorageAttributes(oldBucket.GetAttributes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// new attributes and secrets
|
||||||
nsa, err := getStorageAttributes(newBucket.GetAttributes())
|
nsa, err := getStorageAttributes(newBucket.GetAttributes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sec, err := getStorageSecrets(req.GetPersisted().GetData())
|
sec, err := getStorageSecrets(newBucket.GetSecrets())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -186,64 +123,8 @@ func (sp *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUp
|
|||||||
return nil, status.Errorf(codes.InvalidArgument, "cannot update attribute EndpointUrl")
|
return nil, status.Errorf(codes.InvalidArgument, "cannot update attribute EndpointUrl")
|
||||||
}
|
}
|
||||||
|
|
||||||
if nsa.DisableCredentialRotation && newBucket.GetSecrets() == nil {
|
if err = ensureServiceAccount(ctx, nsa, sec); err != nil {
|
||||||
if !sec.LastRotatedTime.IsZero() {
|
return nil, status.Errorf(codes.InvalidArgument, "failed to ensure service account: %s", err)
|
||||||
return nil, status.Error(codes.InvalidArgument, "cannot disable rotation for already-rotated credentials")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteRotatedCredsFn := func() error { return nil }
|
|
||||||
deleteOldPersistedCredsFn := func() error { return nil }
|
|
||||||
if newBucket.GetSecrets() != nil {
|
|
||||||
// Preemptively check if we're managing the current persisted
|
|
||||||
// credentials and schedule them for later deletion if we are, since in
|
|
||||||
// both rotation and non-rotation cases below, we'll be replacing these.
|
|
||||||
if !sec.LastRotatedTime.IsZero() {
|
|
||||||
sc := sec.Clone()
|
|
||||||
deleteOldPersistedCredsFn = sync.OnceValue(func() error {
|
|
||||||
ac, err := client.New(nsa.EndpointUrl, sec.AccessKeyId, sec.SecretAccessKey, client.WithUseSsl(nsa.UseSSL))
|
|
||||||
if err != nil {
|
|
||||||
return status.Errorf(codes.InvalidArgument, "failed to create client: %v", err)
|
|
||||||
}
|
|
||||||
err = ac.DeleteServiceAccount(ctx, sc.AccessKeyId)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to delete minio service account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
newSec, err := getStorageSecrets(newBucket.GetSecrets())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !nsa.DisableCredentialRotation {
|
|
||||||
// We have new incoming credentials and we're also rotating,
|
|
||||||
// therefore we use incoming credentials to create a new service
|
|
||||||
// account, then delete the incoming credentials. The existing
|
|
||||||
// persisted credentials have already been scheduled for deletion
|
|
||||||
// above (if applicable), so we don't need to do it here.
|
|
||||||
rotatedSec, fn, err := rotateCredentials(ctx, newBucket.GetBucketName(), nsa, newSec)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "failed to rotate minio credentials: %v", err)
|
|
||||||
}
|
|
||||||
sec = rotatedSec
|
|
||||||
deleteRotatedCredsFn = fn
|
|
||||||
} else {
|
|
||||||
// We have new incoming credentials but we're not rotating. Set them
|
|
||||||
// directly for validation below. The existing persisted credentials
|
|
||||||
// have already been scheduled for deletion above (if applicable),
|
|
||||||
// so we don't need to do it here.
|
|
||||||
sec = newSec
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if osa.DisableCredentialRotation && !nsa.DisableCredentialRotation {
|
|
||||||
// Handle the case where the user enables credential rotation but
|
|
||||||
// provides no new credentials. This is an error.
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "credential rotation enabled with no new credentials provided")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cl, err := minio.New(nsa.EndpointUrl, &minio.Options{
|
cl, err := minio.New(nsa.EndpointUrl, &minio.Options{
|
||||||
@@ -255,16 +136,8 @@ func (sp *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUp
|
|||||||
return nil, status.Errorf(codes.InvalidArgument, "failed to create minio sdk client: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "failed to create minio sdk client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p, dryRunErr := dryRun(ctx, cl, newBucket); dryRunErr != nil {
|
if err = dryRun(ctx, cl, newBucket); err != nil {
|
||||||
if dryRunErr.Len() > 0 {
|
return nil, status.Errorf(codes.InvalidArgument, "failed to verify provided minio environment: %s", err)
|
||||||
st := status.New(codes.InvalidArgument, fmt.Sprintf("failed to verify provided minio environment: %v", dryRunErr.Unwrap()))
|
|
||||||
|
|
||||||
st, err = st.WithDetails(&pb.StorageBucketCredentialState{State: p})
|
|
||||||
if err != nil {
|
|
||||||
st = status.New(codes.InvalidArgument, fmt.Sprintf("failed to verify provided minio environment: %v", dryRunErr.Unwrap()))
|
|
||||||
}
|
|
||||||
return nil, st.Err()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
persisted, err := structpb.NewStruct(sec.AsMap())
|
persisted, err := structpb.NewStruct(sec.AsMap())
|
||||||
@@ -272,44 +145,14 @@ func (sp *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUp
|
|||||||
return nil, status.Errorf(codes.InvalidArgument, "failed to convert storage attributes to protobuf struct: %s", err)
|
return nil, status.Errorf(codes.InvalidArgument, "failed to convert storage attributes to protobuf struct: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given that at this point everything is OK with the bucket, we shouldn't
|
|
||||||
// let a failure to delete credentials fail the entire process. If we ever
|
|
||||||
// establish a way to log non-critical errors in the plugin, log this.
|
|
||||||
_ = deleteRotatedCredsFn()
|
|
||||||
_ = deleteOldPersistedCredsFn()
|
|
||||||
|
|
||||||
return &pb.OnUpdateStorageBucketResponse{
|
return &pb.OnUpdateStorageBucketResponse{
|
||||||
Persisted: &storagebuckets.StorageBucketPersisted{Data: persisted},
|
Persisted: &storagebuckets.StorageBucketPersisted{Data: persisted},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnDeleteStorageBucket is a hook that runs when a storage bucket is deleted.
|
// OnDeleteStorageBucket is a hook that runs when a storage bucket is deleted.
|
||||||
|
// Since this plugin manages no state at the moment, this function is a no-op.
|
||||||
func (sp *StoragePlugin) OnDeleteStorageBucket(ctx context.Context, req *pb.OnDeleteStorageBucketRequest) (*pb.OnDeleteStorageBucketResponse, error) {
|
func (sp *StoragePlugin) OnDeleteStorageBucket(ctx context.Context, req *pb.OnDeleteStorageBucketRequest) (*pb.OnDeleteStorageBucketResponse, error) {
|
||||||
bucket := req.GetBucket()
|
|
||||||
if bucket == nil {
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "no storage bucket information found")
|
|
||||||
}
|
|
||||||
|
|
||||||
sec, err := getStorageSecrets(req.GetPersisted().GetData())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sec.LastRotatedTime.IsZero() {
|
|
||||||
sa, err := getStorageAttributes(bucket.GetAttributes())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ac, err := client.New(sa.EndpointUrl, sec.AccessKeyId, sec.SecretAccessKey, client.WithUseSsl(sa.UseSSL))
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "failed to create client: %v", err)
|
|
||||||
}
|
|
||||||
err = ac.DeleteServiceAccount(ctx, sec.AccessKeyId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Unknown, "failed to delete minio service account: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &pb.OnDeleteStorageBucketResponse{}, nil
|
return &pb.OnDeleteStorageBucketResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,14 +177,10 @@ func (sp *StoragePlugin) ValidatePermissions(ctx context.Context, req *pb.Valida
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sec.LastRotatedTime.IsZero() {
|
|
||||||
// since these are existing secrets, we only want to validate the
|
|
||||||
// service account if the credentials have been rotated previously
|
|
||||||
err = ensureServiceAccount(ctx, sa, sec)
|
err = ensureServiceAccount(ctx, sa, sec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "failed to ensure service account: %v", err)
|
return nil, status.Errorf(codes.InvalidArgument, "failed to ensure service account: %v", err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cl, err := minio.New(sa.EndpointUrl, &minio.Options{
|
cl, err := minio.New(sa.EndpointUrl, &minio.Options{
|
||||||
Creds: credentials.NewStaticV4(sec.AccessKeyId, sec.SecretAccessKey, ""),
|
Creds: credentials.NewStaticV4(sec.AccessKeyId, sec.SecretAccessKey, ""),
|
||||||
@@ -352,15 +191,9 @@ func (sp *StoragePlugin) ValidatePermissions(ctx context.Context, req *pb.Valida
|
|||||||
return nil, status.Errorf(codes.Unknown, "failed to create minio sdk client: %v", err)
|
return nil, status.Errorf(codes.Unknown, "failed to create minio sdk client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p, dryRunErr := dryRun(ctx, cl, bucket)
|
err = dryRun(ctx, cl, bucket)
|
||||||
if dryRunErr.Unwrap() != nil {
|
|
||||||
st := status.New(codes.FailedPrecondition, fmt.Sprintf("failed to verify provided minio environment: %v", dryRunErr.Unwrap()))
|
|
||||||
|
|
||||||
st, err = st.WithDetails(&pb.StorageBucketCredentialState{State: p})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st = status.New(codes.Unknown, fmt.Sprintf("failed to verify provided minio environment: %v", dryRunErr.Unwrap()))
|
return nil, status.Errorf(codes.Unknown, "failed to verify provided minio environment: %v", err)
|
||||||
}
|
|
||||||
return nil, st.Err()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pb.ValidatePermissionsResponse{}, nil
|
return &pb.ValidatePermissionsResponse{}, nil
|
||||||
@@ -396,13 +229,13 @@ func (sp *StoragePlugin) HeadObject(ctx context.Context, req *pb.HeadObjectReque
|
|||||||
Region: sa.Region,
|
Region: sa.Region,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errorToStatus(codes.Unknown, "failed to create minio sdk client", err, req).Err()
|
return nil, status.Errorf(codes.Unknown, "failed to create minio sdk client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
objKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
objKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
||||||
oi, err := cl.StatObject(ctx, bucket.GetBucketName(), objKey, minio.StatObjectOptions{})
|
oi, err := cl.StatObject(ctx, bucket.GetBucketName(), objKey, minio.StatObjectOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errorToStatus(codes.Unknown, "failed to stat object", err, req).Err()
|
return nil, status.Errorf(codes.Unknown, "failed to stat object: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pb.HeadObjectResponse{
|
return &pb.HeadObjectResponse{
|
||||||
@@ -413,250 +246,7 @@ func (sp *StoragePlugin) HeadObject(ctx context.Context, req *pb.HeadObjectReque
|
|||||||
|
|
||||||
// GetObject is a hook that retrieves objects.
|
// GetObject is a hook that retrieves objects.
|
||||||
func (sp *StoragePlugin) GetObject(req *pb.GetObjectRequest, objServer pb.StoragePluginService_GetObjectServer) error {
|
func (sp *StoragePlugin) GetObject(req *pb.GetObjectRequest, objServer pb.StoragePluginService_GetObjectServer) error {
|
||||||
bucket := req.GetBucket()
|
return fmt.Errorf("unimplemented")
|
||||||
if bucket == nil {
|
|
||||||
return status.Error(codes.InvalidArgument, "no storage bucket information found")
|
|
||||||
}
|
|
||||||
if bucket.GetBucketName() == "" {
|
|
||||||
return status.Error(codes.InvalidArgument, "storage bucket name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.GetKey() == "" {
|
|
||||||
return status.Error(codes.InvalidArgument, "object key is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
sa, err := getStorageAttributes(bucket.GetAttributes())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sec, err := getStorageSecrets(bucket.GetSecrets())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
customTransport, err := newTransport(sa.UseSSL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cl, err := minio.New(sa.EndpointUrl, &minio.Options{
|
|
||||||
Creds: credentials.NewStaticV4(sec.AccessKeyId, sec.SecretAccessKey, ""),
|
|
||||||
Secure: sa.UseSSL,
|
|
||||||
Region: sa.Region,
|
|
||||||
Transport: customTransport,
|
|
||||||
MaxRetries: 20,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return errorToStatus(codes.Unknown, "failed to create minio sdk client", err, req).Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
objKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
|
||||||
obj, err := cl.GetObject(objServer.Context(), bucket.GetBucketName(), objKey, minio.GetObjectOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return errorToStatus(codes.Unknown, "failed to get object", err, req).Err()
|
|
||||||
}
|
|
||||||
defer obj.Close()
|
|
||||||
|
|
||||||
chunkSize := req.GetChunkSize()
|
|
||||||
if chunkSize == 0 {
|
|
||||||
chunkSize = defaultGetObjectChunkSize
|
|
||||||
}
|
|
||||||
|
|
||||||
rd := bufio.NewReader(obj)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-objServer.Context().Done():
|
|
||||||
return status.Errorf(codes.Canceled, "server context done while streaming: %v", objServer.Context().Err())
|
|
||||||
default:
|
|
||||||
buffer := make([]byte, chunkSize)
|
|
||||||
n, err := rd.Read(buffer)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return status.Errorf(codes.Unknown, "failed to read chunk from minio object: %v", err)
|
|
||||||
}
|
|
||||||
if n > 0 {
|
|
||||||
err := objServer.Send(&pb.GetObjectResponse{FileChunk: buffer[:n]})
|
|
||||||
if err != nil {
|
|
||||||
return status.Errorf(codes.Unknown, "failed to send chunk to client: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// listObjectsRecursive returns recursive list of all files and directories found under a prefix from MinIO.
|
|
||||||
func listObjectsRecursive(ctx context.Context, cl *minio.Client, bucketName, searchPrefix string) ([]*pb.Object, error) {
|
|
||||||
var objects []*pb.Object
|
|
||||||
dirs := make(map[string]struct{})
|
|
||||||
seenKeys := make(map[string]struct{})
|
|
||||||
|
|
||||||
minioObjects := cl.ListObjects(ctx, bucketName, minio.ListObjectsOptions{
|
|
||||||
Prefix: searchPrefix,
|
|
||||||
Recursive: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
for objectName := range minioObjects {
|
|
||||||
|
|
||||||
// Bail out if MinIO reports an error for this object entry
|
|
||||||
if objectName.Err != nil {
|
|
||||||
return nil, objectName.Err
|
|
||||||
}
|
|
||||||
key := objectName.Key
|
|
||||||
isDirFile := strings.HasSuffix(key, "/")
|
|
||||||
|
|
||||||
// Skip the directory that is exactly the prefix itself (e.g., "foo/" when prefix == "foo/").
|
|
||||||
if isDirFile && key == searchPrefix {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect parent directories (normalize), but DO NOT mark them seen here.
|
|
||||||
// This ensures an explicit directory object (e.g., "folder/") will still be appended once.
|
|
||||||
dirPaths := buildRecursiveDirectoryPaths(key, searchPrefix)
|
|
||||||
for _, dirPath := range dirPaths {
|
|
||||||
k := ensureDir(dirPath)
|
|
||||||
dirs[k] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// De-dupe only on the exact key string.
|
|
||||||
if _, exists := seenKeys[key]; exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
objects = append(objects, &pb.Object{Key: key, IsDir: isDirFile})
|
|
||||||
seenKeys[key] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append synthesized directories, but only if an explicit object with the same key wasn't added.
|
|
||||||
for dirKey := range dirs {
|
|
||||||
if _, exists := seenKeys[dirKey]; exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
objects = append(objects, &pb.Object{Key: dirKey, IsDir: true})
|
|
||||||
seenKeys[dirKey] = struct{}{}
|
|
||||||
}
|
|
||||||
return objects, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// listObjectsNonRecursive returns only top-level files and directories within the prefix from MinIO.
|
|
||||||
func listObjectsNonRecursive(ctx context.Context, cl *minio.Client, bucketName, searchPrefix string) ([]*pb.Object, error) {
|
|
||||||
var objects []*pb.Object
|
|
||||||
seenKeys := make(map[string]struct{})
|
|
||||||
|
|
||||||
minioObjects := cl.ListObjects(ctx, bucketName, minio.ListObjectsOptions{
|
|
||||||
Prefix: searchPrefix,
|
|
||||||
Recursive: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
for objectName := range minioObjects {
|
|
||||||
if objectName.Err != nil {
|
|
||||||
return nil, objectName.Err
|
|
||||||
}
|
|
||||||
key := objectName.Key
|
|
||||||
isDirFile := strings.HasSuffix(key, "/")
|
|
||||||
|
|
||||||
// Skip the prefix directory itself.
|
|
||||||
if isDirFile && key == searchPrefix {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the top-level entry under the prefix.
|
|
||||||
relativeFullPath := strings.TrimSuffix(strings.TrimPrefix(key, searchPrefix), "/")
|
|
||||||
split := strings.Split(relativeFullPath, "/")
|
|
||||||
if len(split) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
relativeParentPath := searchPrefix + split[0]
|
|
||||||
|
|
||||||
// If deeper segments or explicit dir file, normalize to a directory form.
|
|
||||||
if len(split) > 1 || isDirFile {
|
|
||||||
relativeParentPath = ensureDir(relativeParentPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, exists := seenKeys[relativeParentPath]; exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
objects = append(objects, &pb.Object{
|
|
||||||
Key: relativeParentPath,
|
|
||||||
IsDir: strings.HasSuffix(relativeParentPath, "/"),
|
|
||||||
})
|
|
||||||
seenKeys[relativeParentPath] = struct{}{}
|
|
||||||
}
|
|
||||||
return objects, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildRecursiveDirectoryPaths collects all parent directory paths from an object key
|
|
||||||
// that are within the given prefix. This is used to simulate directory structures in object storage.
|
|
||||||
func buildRecursiveDirectoryPaths(objectKey, prefix string) []string {
|
|
||||||
var dirPaths []string
|
|
||||||
prefixDir := strings.TrimSuffix(prefix, "/")
|
|
||||||
pathParts := strings.Split(objectKey, "/")
|
|
||||||
|
|
||||||
// Build all possible parent directory paths from the split parts
|
|
||||||
for i := 1; i < len(pathParts); i++ {
|
|
||||||
dirPath := strings.Join(pathParts[:i], "/")
|
|
||||||
|
|
||||||
// Only include directories that are deeper than the search prefix
|
|
||||||
if len(dirPath) > len(prefixDir) && strings.HasPrefix(dirPath+"/", prefix) {
|
|
||||||
dirPaths = append(dirPaths, dirPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dirPaths
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureDir(k string) string {
|
|
||||||
if strings.HasSuffix(k, "/") {
|
|
||||||
return k
|
|
||||||
}
|
|
||||||
return k + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListObjects dispatches to recursive or non-recursive versions.
|
|
||||||
func (sp *StoragePlugin) ListObjects(ctx context.Context, req *pb.ListObjectsRequest) (*pb.ListObjectsResponse, error) {
|
|
||||||
bucket := req.GetBucket()
|
|
||||||
if bucket == nil {
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "no storage bucket information found")
|
|
||||||
}
|
|
||||||
if bucket.GetBucketName() == "" {
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "storage bucket name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build MinIO client (same pattern as HeadObject/GetObject/PutObject)
|
|
||||||
sa, err := getStorageAttributes(bucket.GetAttributes())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sec, err := getStorageSecrets(bucket.GetSecrets())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cl, err := minio.New(sa.EndpointUrl, &minio.Options{
|
|
||||||
Creds: credentials.NewStaticV4(sec.AccessKeyId, sec.SecretAccessKey, ""),
|
|
||||||
Secure: sa.UseSSL,
|
|
||||||
Region: sa.Region,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errorToStatus(codes.Unknown, "failed to create minio client", err, req).Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix := path.Join(bucket.GetBucketPrefix(), req.GetKeyPrefix())
|
|
||||||
// path.Join cleans the path, including removing a trailing slash, if it exists. Given that a slash is used to denote a folder,
|
|
||||||
// it is required here. So, if the original keyPrefix had a trailing slash, we need to add it back.
|
|
||||||
if strings.HasSuffix(req.GetKeyPrefix(), "/") {
|
|
||||||
prefix = prefix + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
var objs []*pb.Object
|
|
||||||
if req.GetRecursive() {
|
|
||||||
objs, err = listObjectsRecursive(ctx, cl, bucket.GetBucketName(), prefix)
|
|
||||||
} else {
|
|
||||||
objs, err = listObjectsNonRecursive(ctx, cl, bucket.GetBucketName(), prefix)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, errorToStatus(codes.Unknown, "failed to list objects", err, req).Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
return &pb.ListObjectsResponse{Objects: objs}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutObject is a hook that reads a file stored on local disk and stores it to
|
// PutObject is a hook that reads a file stored on local disk and stores it to
|
||||||
@@ -709,7 +299,7 @@ func (sp *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest
|
|||||||
Region: sa.Region,
|
Region: sa.Region,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errorToStatus(codes.Internal, "failed to create minio sdk client", err, req).Err()
|
return nil, status.Errorf(codes.Internal, "failed to create minio sdk client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := sha256.New()
|
hash := sha256.New()
|
||||||
@@ -723,23 +313,23 @@ func (sp *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest
|
|||||||
}
|
}
|
||||||
|
|
||||||
key := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
key := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
||||||
res, err := cl.PutObject(ctx, bucket.GetBucketName(), key, file, info.Size(), minio.PutObjectOptions{})
|
res, err := cl.PutObject(ctx, bucket.GetBucketName(), key, file, info.Size(), minio.PutObjectOptions{
|
||||||
|
UserMetadata: map[string]string{
|
||||||
|
"x-amz-checksum-algorithm": "SHA256",
|
||||||
|
"x-amz-checksum-sha256": checksum,
|
||||||
|
},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errorToStatus(codes.Internal, "failed to put object into minio", err, req).Err()
|
return nil, status.Errorf(codes.Internal, "failed to put object into minio: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resChecksum := res.ChecksumSHA256
|
if res.ChecksumSHA256 == "" {
|
||||||
if resChecksum == "" {
|
return nil, status.Error(codes.Internal, "missing checksum response from minio")
|
||||||
// We were successful in putting the object but we didn't get a file
|
|
||||||
// SHA256 checksum from the server. Given that the call was successful,
|
|
||||||
// assume everything went ok and force the checksum to match so we can
|
|
||||||
// send it correctly back to Boundary.
|
|
||||||
resChecksum = checksum
|
|
||||||
}
|
}
|
||||||
if checksum != resChecksum {
|
if checksum != res.ChecksumSHA256 {
|
||||||
return nil, status.Error(codes.Internal, "mismatched checksum")
|
return nil, status.Error(codes.Internal, "mismatched checksum")
|
||||||
}
|
}
|
||||||
decodedChecksum, err := base64.StdEncoding.DecodeString(resChecksum)
|
decodedChecksum, err := base64.StdEncoding.DecodeString(res.ChecksumSHA256)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed to decode checksum value from minio: %v", err)
|
return nil, status.Errorf(codes.Internal, "failed to decode checksum value from minio: %v", err)
|
||||||
}
|
}
|
||||||
@@ -751,342 +341,61 @@ func (sp *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest
|
|||||||
// DeleteObjects deletes one or many files in an external object store via a
|
// DeleteObjects deletes one or many files in an external object store via a
|
||||||
// provided key prefix.
|
// provided key prefix.
|
||||||
func (sp *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjectsRequest) (*pb.DeleteObjectsResponse, error) {
|
func (sp *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjectsRequest) (*pb.DeleteObjectsResponse, error) {
|
||||||
bucket := req.GetBucket()
|
return nil, fmt.Errorf("unimplemented")
|
||||||
if bucket == nil {
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "no storage bucket information found")
|
|
||||||
}
|
|
||||||
if bucket.GetBucketName() == "" {
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "storage bucket name is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.GetKeyPrefix() == "" {
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "key prefix is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
sa, err := getStorageAttributes(bucket.GetAttributes())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sec, err := getStorageSecrets(bucket.GetSecrets())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cl, err := minio.New(sa.EndpointUrl, &minio.Options{
|
|
||||||
Creds: credentials.NewStaticV4(sec.AccessKeyId, sec.SecretAccessKey, ""),
|
|
||||||
Secure: sa.UseSSL,
|
|
||||||
Region: sa.Region,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errorToStatus(codes.Unknown, "failed to create minio sdk client", err, req).Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix := path.Join(bucket.GetBucketPrefix(), req.GetKeyPrefix())
|
|
||||||
if strings.HasSuffix(req.GetKeyPrefix(), "/") {
|
|
||||||
// path.Join ends by "cleaning" the path, including removing a trailing slash, if
|
|
||||||
// it exists. given that a slash is used to denote a folder, it is required here.
|
|
||||||
prefix += "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
if !req.Recursive {
|
|
||||||
if err := cl.RemoveObject(ctx, bucket.GetBucketName(), prefix, minio.RemoveObjectOptions{}); err != nil {
|
|
||||||
return nil, errorToStatus(codes.Unknown, "error deleting minio object", err, req).Err()
|
|
||||||
}
|
|
||||||
return &pb.DeleteObjectsResponse{
|
|
||||||
ObjectsDeleted: uint32(1),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
objects := []minio.ObjectInfo{}
|
|
||||||
for obj := range cl.ListObjects(ctx, bucket.GetBucketName(), minio.ListObjectsOptions{
|
|
||||||
Prefix: prefix,
|
|
||||||
Recursive: true,
|
|
||||||
}) {
|
|
||||||
if obj.Err != nil {
|
|
||||||
return nil, errorToStatus(codes.Unknown, "error iterating minio bucket contents", obj.Err, req).Err()
|
|
||||||
}
|
|
||||||
objects = append(objects, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
emitter := make(chan minio.ObjectInfo)
|
|
||||||
go func() {
|
|
||||||
defer close(emitter)
|
|
||||||
// we could loop the listobjects respose here, but we want to check all the errs ahead of time
|
|
||||||
for _, obj := range objects {
|
|
||||||
emitter <- obj
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
removed := 0
|
|
||||||
for res := range cl.RemoveObjectsWithResult(ctx, bucket.GetBucketName(), emitter, minio.RemoveObjectsOptions{}) {
|
|
||||||
if res.Err != nil {
|
|
||||||
return nil, errorToStatus(codes.Unknown, "error deleting minio object(s)", res.Err, req).Err()
|
|
||||||
}
|
|
||||||
removed++
|
|
||||||
}
|
|
||||||
|
|
||||||
return &pb.DeleteObjectsResponse{
|
|
||||||
ObjectsDeleted: uint32(removed),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateWritePermission checks if the plugin has write permission for an object.
|
|
||||||
func validateWritePermission(ctx context.Context, cl *minio.Client, bucket *storagebuckets.StorageBucket, objectKey string) (*pb.Permission, *multierror.Error) {
|
|
||||||
const op = "storage.validateWritePermission"
|
|
||||||
var combinedErrors *multierror.Error
|
|
||||||
|
|
||||||
if cl == nil {
|
|
||||||
err := fmt.Errorf("%s: minio client is nil", op)
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, ErrorDetails: err.Error(), CheckedAt: timestamppb.Now()}, multierror.Append(combinedErrors, err)
|
|
||||||
}
|
|
||||||
if bucket == nil {
|
|
||||||
err := fmt.Errorf("%s: storage bucket is nil", op)
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, ErrorDetails: err.Error(), CheckedAt: timestamppb.Now()}, multierror.Append(combinedErrors, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dryRun(ctx context.Context, cl *minio.Client, bucket *storagebuckets.StorageBucket) error {
|
||||||
|
objectKey := path.Join(bucket.GetBucketPrefix(), uuid.New().String())
|
||||||
rd := bytes.NewReader([]byte("hashicorp boundary minio plugin access test"))
|
rd := bytes.NewReader([]byte("hashicorp boundary minio plugin access test"))
|
||||||
_, err := cl.PutObject(ctx, bucket.GetBucketName(), objectKey, rd, rd.Size(), minio.PutObjectOptions{})
|
_, err := cl.PutObject(ctx, bucket.GetBucketName(), objectKey, rd, rd.Size(), minio.PutObjectOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
combinedErrors = multierror.Append(combinedErrors, fmt.Errorf("failed to put object at %q: %w", objectKey, err))
|
return fmt.Errorf("failed to put object at %q: %w", objectKey, err)
|
||||||
return errorToPermission(err), combinedErrors
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_OK, CheckedAt: timestamppb.Now()}, nil
|
_, err = cl.StatObject(ctx, bucket.GetBucketName(), objectKey, minio.StatObjectOptions{})
|
||||||
}
|
|
||||||
|
|
||||||
// validateReadPermission checks if the plugin has read permission for an object.
|
|
||||||
func validateReadPermission(ctx context.Context, cl *minio.Client, bucket *storagebuckets.StorageBucket, objectKey string) (*pb.Permission, *multierror.Error) {
|
|
||||||
const op = "storage.validateReadPermission"
|
|
||||||
var combinedErrors *multierror.Error
|
|
||||||
|
|
||||||
if cl == nil {
|
|
||||||
err := fmt.Errorf("%s: minio client is nil", op)
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, ErrorDetails: err.Error(), CheckedAt: timestamppb.Now()}, multierror.Append(combinedErrors, err)
|
|
||||||
}
|
|
||||||
if bucket == nil {
|
|
||||||
err := fmt.Errorf("%s: storage bucket is nil", op)
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, ErrorDetails: err.Error(), CheckedAt: timestamppb.Now()}, multierror.Append(combinedErrors, err)
|
|
||||||
}
|
|
||||||
if objectKey == "" {
|
|
||||||
err := fmt.Errorf("%s: object key is empty", op)
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, ErrorDetails: err.Error(), CheckedAt: timestamppb.Now()}, multierror.Append(combinedErrors, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
permission := &pb.Permission{State: pb.StateType_STATE_TYPE_OK}
|
|
||||||
_, err := cl.StatObject(ctx, bucket.GetBucketName(), objectKey, minio.StatObjectOptions{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
permission = errorToPermission(err)
|
return fmt.Errorf("failed to stat object at %q: %w", objectKey, err)
|
||||||
combinedErrors = multierror.Append(combinedErrors, fmt.Errorf("failed to stat object at %q: %w", objectKey, err))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = cl.GetObject(ctx, bucket.GetBucketName(), objectKey, minio.GetObjectOptions{})
|
_, err = cl.GetObject(ctx, bucket.GetBucketName(), objectKey, minio.GetObjectOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p := errorToPermission(err)
|
return fmt.Errorf("failed to get object at %q: %w", objectKey, err)
|
||||||
if permission.GetState() == pb.StateType_STATE_TYPE_OK {
|
|
||||||
permission = p
|
|
||||||
} else {
|
|
||||||
if p != nil && p.GetErrorDetails() != "" {
|
|
||||||
permission.ErrorDetails = fmt.Sprintf("%s, %s", permission.GetErrorDetails(), p.GetErrorDetails())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
combinedErrors = multierror.Append(combinedErrors, fmt.Errorf("failed to get object at %q: %w", objectKey, err))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// List just the object we created above
|
oiCh := cl.ListObjects(ctx, bucket.GetBucketName(), minio.ListObjectsOptions{Recursive: true})
|
||||||
oiCh := cl.ListObjects(ctx, bucket.GetBucketName(), minio.ListObjectsOptions{Prefix: objectKey, MaxKeys: 1})
|
|
||||||
for oi := range oiCh {
|
for oi := range oiCh {
|
||||||
if oi.Err != nil {
|
if oi.Err != nil {
|
||||||
if permission.GetState() == pb.StateType_STATE_TYPE_OK {
|
return fmt.Errorf("failed to list objects in bucket %q: %w", bucket.GetBucketName(), oi.Err)
|
||||||
permission = errorToPermission(oi.Err)
|
|
||||||
} else {
|
|
||||||
p := errorToPermission(err)
|
|
||||||
if p != nil && p.GetErrorDetails() != "" {
|
|
||||||
permission.ErrorDetails = fmt.Sprintf("%s, %s", permission.GetErrorDetails(), p.GetErrorDetails())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
combinedErrors = multierror.Append(fmt.Errorf("failed to list objects in bucket %q: %w", bucket.GetBucketName(), oi.Err))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if combinedErrors != nil {
|
err = cl.RemoveObject(ctx, bucket.GetBucketName(), objectKey, minio.RemoveObjectOptions{})
|
||||||
return permission, combinedErrors
|
|
||||||
}
|
|
||||||
|
|
||||||
permission.CheckedAt = timestamppb.Now()
|
|
||||||
return permission, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateDeletePermission checks if the plugin has delete permission for an object.
|
|
||||||
func validateDeletePermission(ctx context.Context, cl *minio.Client, bucket *storagebuckets.StorageBucket, objectKey string) (*pb.Permission, *multierror.Error) {
|
|
||||||
const op = "storage.validateDeletePermission"
|
|
||||||
|
|
||||||
var combinedErrors *multierror.Error
|
|
||||||
|
|
||||||
if cl == nil {
|
|
||||||
err := fmt.Errorf("%s: minio client is nil", op)
|
|
||||||
combinedErrors = multierror.Append(combinedErrors, err)
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, ErrorDetails: err.Error(), CheckedAt: timestamppb.Now()}, combinedErrors
|
|
||||||
}
|
|
||||||
if bucket == nil {
|
|
||||||
err := fmt.Errorf("%s: storage bucket is nil", op)
|
|
||||||
combinedErrors = multierror.Append(combinedErrors, err)
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, ErrorDetails: err.Error(), CheckedAt: timestamppb.Now()}, combinedErrors
|
|
||||||
}
|
|
||||||
if objectKey == "" {
|
|
||||||
err := fmt.Errorf("%s: object key is empty", op)
|
|
||||||
combinedErrors = multierror.Append(combinedErrors, err)
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, ErrorDetails: err.Error(), CheckedAt: timestamppb.Now()}, combinedErrors
|
|
||||||
}
|
|
||||||
|
|
||||||
err := cl.RemoveObject(ctx, bucket.GetBucketName(), objectKey, minio.RemoveObjectOptions{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
combinedErrors = multierror.Append(combinedErrors, fmt.Errorf("failed to remove object at %q: %w", objectKey, err))
|
return fmt.Errorf("failed to remove object at %q: %w", objectKey, err)
|
||||||
return errorToPermission(err), combinedErrors
|
|
||||||
}
|
|
||||||
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_OK, CheckedAt: timestamppb.Now()}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dryRun(ctx context.Context, cl *minio.Client, bucket *storagebuckets.StorageBucket) (*pb.Permissions, *multierror.Error) {
|
|
||||||
objectKey := path.Join(bucket.GetBucketPrefix(), uuid.New().String())
|
|
||||||
permissions := &pb.Permissions{
|
|
||||||
Write: &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, CheckedAt: timestamppb.Now()},
|
|
||||||
Read: &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, CheckedAt: timestamppb.Now()},
|
|
||||||
Delete: &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, CheckedAt: timestamppb.Now()},
|
|
||||||
}
|
|
||||||
|
|
||||||
var combinedErrors *multierror.Error
|
|
||||||
|
|
||||||
writePermission, writeErr := validateWritePermission(ctx, cl, bucket, objectKey)
|
|
||||||
combinedErrors = multierror.Append(combinedErrors, writeErr)
|
|
||||||
permissions.Write = writePermission
|
|
||||||
|
|
||||||
readPermission, readErr := validateReadPermission(ctx, cl, bucket, objectKey)
|
|
||||||
combinedErrors = multierror.Append(combinedErrors, readErr)
|
|
||||||
permissions.Read = readPermission
|
|
||||||
|
|
||||||
deletePermission, deleteErr := validateDeletePermission(ctx, cl, bucket, objectKey)
|
|
||||||
combinedErrors = multierror.Append(combinedErrors, deleteErr)
|
|
||||||
permissions.Delete = deletePermission
|
|
||||||
|
|
||||||
return permissions, combinedErrors
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensureServiceAccount ensugres the credentials we received belong to a MinIO
|
|
||||||
// service account. This plugin does not support using user credentials for its
|
|
||||||
// configuration.
|
|
||||||
func ensureServiceAccount(ctx context.Context, sa *StorageAttributes, sec *StorageSecrets) error {
|
|
||||||
ac, err := client.New(sa.EndpointUrl, sec.AccessKeyId, sec.SecretAccessKey, client.WithUseSsl(sa.UseSSL))
|
|
||||||
if err != nil {
|
|
||||||
return status.Errorf(codes.InvalidArgument, "failed to create client: %v", err)
|
|
||||||
}
|
|
||||||
if err := ac.EnsureServiceAccount(ctx, sec.AccessKeyId); err != nil {
|
|
||||||
return fmt.Errorf("failed to obtain service account info: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// errorToPermission converts a MinIO error to a plugin permission.
|
func newMadminClient(sa *StorageAttributes, sec *StorageSecrets) (*madmin.AdminClient, error) {
|
||||||
// errorToPermission only handles a subset of MinIO errors that have to do with
|
return madmin.NewWithOptions(sa.EndpointUrl, &madmin.Options{
|
||||||
// permissions.
|
Creds: credentials.NewStaticV4(sec.AccessKeyId, sec.SecretAccessKey, ""),
|
||||||
// If the MinIO error is not recognized, it returns a permission with an unknown state.
|
Secure: sa.UseSSL,
|
||||||
func errorToPermission(err error) *pb.Permission {
|
})
|
||||||
if err == nil {
|
|
||||||
return &pb.Permission{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
errResponse := minio.ToErrorResponse(err)
|
// ensureServiceAccount ensures the credentials we received belong to a MinIO
|
||||||
switch errResponse.Code {
|
// service account. This plugin does not support using user credentials for its
|
||||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#ErrorCodeList
|
// configuration.
|
||||||
case "AccessDenied":
|
func ensureServiceAccount(ctx context.Context, sa *StorageAttributes, sec *StorageSecrets) error {
|
||||||
fallthrough
|
cl, err := newMadminClient(sa, sec)
|
||||||
case "InvalidSecurity":
|
|
||||||
fallthrough
|
|
||||||
case "InvalidAccessKeyId":
|
|
||||||
fallthrough
|
|
||||||
case "NoSuchBucket":
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_ERROR, ErrorDetails: errResponse.Error(), CheckedAt: timestamppb.Now()}
|
|
||||||
case "InvalidObjectState":
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
return &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, ErrorDetails: errResponse.Error(), CheckedAt: timestamppb.Now()}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorToStatus converts an error to a gRPC status with the plugin permission
|
|
||||||
// attached.
|
|
||||||
// If the permission error is not recognized, a gRPC status will be returned
|
|
||||||
// with an code and error message passed in.
|
|
||||||
func errorToStatus(code codes.Code, msg string, err error, req any) *status.Status {
|
|
||||||
if err == nil {
|
|
||||||
// If we see this status, it means this function was called with a nil
|
|
||||||
// err. This is odd and should probably be investigated.
|
|
||||||
return status.New(code, fmt.Sprintf("%s: no error from storage service provider", msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
errResponse := minio.ToErrorResponse(err)
|
|
||||||
switch errResponse.Code {
|
|
||||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#ErrorCodeList
|
|
||||||
case "RequestTimeout":
|
|
||||||
return status.New(codes.DeadlineExceeded, fmt.Sprintf("%s: timeout exceeded when communicating with storage service provider", msg))
|
|
||||||
case "SlowDown":
|
|
||||||
return status.New(codes.Unavailable, fmt.Sprintf("%s: received a throttling response from storage service provider", msg))
|
|
||||||
case "BadDigest":
|
|
||||||
return status.New(codes.Aborted, fmt.Sprintf("%s: storage checksum mismatch", msg))
|
|
||||||
case "NoSuchBucket":
|
|
||||||
return status.New(codes.NotFound, fmt.Sprintf("%s: specified storage bucket does not exist", msg))
|
|
||||||
case "NoSuchKey":
|
|
||||||
return status.New(codes.NotFound, fmt.Sprintf("%s: specified object does not exist", msg))
|
|
||||||
}
|
|
||||||
if errResponse.StatusCode == 400 {
|
|
||||||
// catch-all for various different bad-request related errors
|
|
||||||
return status.New(codes.InvalidArgument, fmt.Sprintf("%s: received a bad request response from storage service provider: %v", msg, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// for all other errors, we can parse as permission state errors
|
|
||||||
permission := errorToPermission(err)
|
|
||||||
state := &pb.StorageBucketCredentialState{State: &pb.Permissions{}}
|
|
||||||
|
|
||||||
// we can use the request type to determine what kind of response we should return
|
|
||||||
switch req.(type) {
|
|
||||||
case pb.HeadObjectRequest, *pb.HeadObjectRequest:
|
|
||||||
state.State.Read = permission
|
|
||||||
case pb.GetObjectRequest, *pb.GetObjectRequest:
|
|
||||||
state.State.Read = permission
|
|
||||||
case pb.ListObjectsRequest, *pb.ListObjectsRequest:
|
|
||||||
state.State.Read = permission
|
|
||||||
case pb.PutObjectRequest, *pb.PutObjectRequest:
|
|
||||||
state.State.Write = permission
|
|
||||||
case pb.DeleteObjectsRequest, *pb.DeleteObjectsRequest:
|
|
||||||
state.State.Delete = permission
|
|
||||||
}
|
|
||||||
|
|
||||||
st, err := status.New(code, fmt.Sprintf("%s: %s", msg, err)).WithDetails(state)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st = status.New(code, fmt.Sprintf("%s: %s", msg, err))
|
return fmt.Errorf("failed to create madmin client: %w", err)
|
||||||
}
|
|
||||||
return st
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTransport returns an http Transport object. The http transport object utilizes the
|
if _, err = cl.InfoServiceAccount(ctx, sec.AccessKeyId); err != nil {
|
||||||
// default transport created by minio. The following transport fields are updated to provide
|
return fmt.Errorf("failed to obtain service account info: %w", err)
|
||||||
// more reliable connectivity when handling larger objects:
|
|
||||||
// MaxResponseHeaderBytes is set to 1MiB to prevent header reads from stalling.
|
|
||||||
// WriteBufferSize is set to 1MiB, matching the default buffer size used in Boundary storage.
|
|
||||||
// ReadBufferSize is set to 1MiB, matching the default buffer size used in Boundary storage.
|
|
||||||
// ForceAttemptHTTP2 is set to true, supporting more resilient multiplexing and avoids HOL blocking on retries.
|
|
||||||
// IdleConnTimeout is set to 2 minutes.
|
|
||||||
func newTransport(secure bool) (*http.Transport, error) {
|
|
||||||
transport, err := minio.DefaultTransport(secure)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
transport.MaxResponseHeaderBytes = 1 << 20 // 1 MiB prevents header read from stalling
|
|
||||||
transport.WriteBufferSize = 1 << 20 // 1 MiB write buffer (default is 4 KiB)
|
return nil
|
||||||
transport.ReadBufferSize = 1 << 20 // 1 MiB read buffer (default is 4 KiB)
|
|
||||||
transport.ForceAttemptHTTP2 = true // enables HTTP/2 where supported more resilient multiplexing, avoids HOL blocking on retries
|
|
||||||
transport.IdleConnTimeout = 2 * time.Minute // double the idle connection timeout, default is 1 minute
|
|
||||||
return transport, nil
|
|
||||||
}
|
}
|
||||||
+110
-2660
File diff suppressed because it is too large.
Load diff
@@ -1,91 +0,0 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/internal/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultPolicyTemplate = `{
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Action": [
|
|
||||||
"s3:GetObject",
|
|
||||||
"s3:GetObjectAttributes",
|
|
||||||
"s3:PutObject",
|
|
||||||
"s3:DeleteObject"
|
|
||||||
],
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Resource": "arn:aws:s3:::%[1]s/*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Action": "s3:ListBucket",
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Resource": "arn:aws:s3:::%[1]s"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Action": [
|
|
||||||
"admin:CreateServiceAccount",
|
|
||||||
"admin:RemoveServiceAccount"
|
|
||||||
],
|
|
||||||
"Effect": "Allow"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Version": "2012-10-17"
|
|
||||||
}`
|
|
||||||
)
|
|
||||||
|
|
||||||
type deleteInputCredsFn func() error
|
|
||||||
|
|
||||||
// rotateCredentials uses the incoming service account credentials to generate a
|
|
||||||
// new service account. It returns a new StorageSecrets object with the new
|
|
||||||
// service account credentials and a callback function to delete the service
|
|
||||||
// account provided in the input.
|
|
||||||
func rotateCredentials(ctx context.Context, bucketName string, sa *StorageAttributes, inSec *StorageSecrets) (*StorageSecrets, deleteInputCredsFn, error) {
|
|
||||||
err := ensureServiceAccount(ctx, sa, inSec)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to ensure minio service account credentials: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ac, err := client.New(sa.EndpointUrl, inSec.AccessKeyId, inSec.SecretAccessKey, client.WithUseSsl(sa.UseSSL))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to create new client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := json.RawMessage(fmt.Sprintf(defaultPolicyTemplate, bucketName))
|
|
||||||
newCreds, err := ac.AddServiceAccount(ctx, client.AddServiceAccountReq{
|
|
||||||
Name: "Boundary MinIO Service Account",
|
|
||||||
Description: fmt.Sprintf("Boundary-managed MinIO service account for storage bucket '%s'", bucketName),
|
|
||||||
Policy: policy,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to create new minio service account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
newSec := &StorageSecrets{
|
|
||||||
AccessKeyId: newCreds.AccessKeyId,
|
|
||||||
SecretAccessKey: newCreds.SecretAccessKey,
|
|
||||||
LastRotatedTime: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
inCl := inSec.Clone()
|
|
||||||
return newSec, sync.OnceValue(func() error {
|
|
||||||
ac, err := client.New(sa.EndpointUrl, inCl.AccessKeyId, inCl.SecretAccessKey, client.WithUseSsl(sa.UseSSL))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create new client: %w", err)
|
|
||||||
}
|
|
||||||
err = ac.DeleteServiceAccount(ctx, inCl.AccessKeyId)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to delete minio service account: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
// Copyright IBM Corp. 2025, 2026
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/internal/client"
|
|
||||||
internaltest "repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-minio/internal/testing"
|
|
||||||
"github.com/minio/minio-go/v7"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRotateCredentials(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
server := internaltest.NewMinioServer(t)
|
|
||||||
|
|
||||||
bucketName := "test-bucket"
|
|
||||||
err := server.Client.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
inSec *StorageSecrets
|
|
||||||
expErrMsg string
|
|
||||||
credDelErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "notUsingServiceAccount",
|
|
||||||
inSec: &StorageSecrets{
|
|
||||||
AccessKeyId: server.RootUsername,
|
|
||||||
SecretAccessKey: server.RootPassword,
|
|
||||||
},
|
|
||||||
expErrMsg: "failed to ensure minio service account credentials: failed to obtain service account info: The specified service account is not found",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "addServiceAccountFail",
|
|
||||||
inSec: func() *StorageSecrets {
|
|
||||||
creds, err := server.AdminClient.AddServiceAccount(ctx, client.AddServiceAccountReq{
|
|
||||||
Policy: json.RawMessage(`
|
|
||||||
{
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Action": [
|
|
||||||
"admin:CreateServiceAccount",
|
|
||||||
"admin:RemoveServiceAccount"
|
|
||||||
],
|
|
||||||
"Effect": "Deny"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Version": "2012-10-17"
|
|
||||||
}
|
|
||||||
`),
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
return &StorageSecrets{
|
|
||||||
AccessKeyId: creds.AccessKeyId,
|
|
||||||
SecretAccessKey: creds.SecretAccessKey,
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
expErrMsg: "failed to create new minio service account: Access Denied",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "credentialDeletionFail",
|
|
||||||
inSec: func() *StorageSecrets {
|
|
||||||
creds, err := server.AdminClient.AddServiceAccount(ctx, client.AddServiceAccountReq{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
return &StorageSecrets{
|
|
||||||
AccessKeyId: creds.AccessKeyId,
|
|
||||||
SecretAccessKey: creds.SecretAccessKey,
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
credDelErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "success",
|
|
||||||
inSec: func() *StorageSecrets {
|
|
||||||
creds, err := server.AdminClient.AddServiceAccount(ctx, client.AddServiceAccountReq{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
return &StorageSecrets{
|
|
||||||
AccessKeyId: creds.AccessKeyId,
|
|
||||||
SecretAccessKey: creds.SecretAccessKey,
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
sa := &StorageAttributes{
|
|
||||||
EndpointUrl: server.ApiAddr,
|
|
||||||
UseSSL: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
outSec, delFn, err := rotateCredentials(context.Background(), bucketName, sa, tt.inSec)
|
|
||||||
if tt.expErrMsg != "" {
|
|
||||||
require.ErrorContains(t, err, tt.expErrMsg)
|
|
||||||
require.Nil(t, outSec)
|
|
||||||
require.Nil(t, delFn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, outSec)
|
|
||||||
require.NotNil(t, delFn)
|
|
||||||
require.NotEqual(t, outSec.AccessKeyId, tt.inSec.AccessKeyId)
|
|
||||||
require.NotEqual(t, outSec.SecretAccessKey, tt.inSec.SecretAccessKey)
|
|
||||||
require.NotEmpty(t, outSec.LastRotatedTime)
|
|
||||||
|
|
||||||
// Ensure old creds still exist since the callback hasn't been
|
|
||||||
// called. We then make the delete call and finally assert that the
|
|
||||||
// credential was removed if there was no error when attempting to
|
|
||||||
// delete.
|
|
||||||
err = server.AdminClient.EnsureServiceAccount(ctx, tt.inSec.AccessKeyId)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
if tt.credDelErr {
|
|
||||||
sa.EndpointUrl = "localhost:1"
|
|
||||||
require.ErrorContains(t, delFn(), "failed to delete minio service account")
|
|
||||||
require.ErrorContains(t, delFn(), "failed to delete minio service account") // 2nd call should return the same state (due to sync.Once).
|
|
||||||
|
|
||||||
err := server.AdminClient.EnsureServiceAccount(ctx, tt.inSec.AccessKeyId)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
require.NoError(t, delFn())
|
|
||||||
require.NoError(t, delFn()) // 2nd call should return the same state (due to sync.Once).
|
|
||||||
}
|
|
||||||
|
|
||||||
err = server.AdminClient.EnsureServiceAccount(ctx, tt.inSec.AccessKeyId)
|
|
||||||
require.ErrorContains(t, err, "service account is not found")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Copyright IBM Corp. 2025, 2026
|
# Copyright (c) HashiCorp, Inc.
|
||||||
# SPDX-License-Identifier: MPL-2.0
|
# SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
# This shell script instantiates a MinIO instance using Docker and ensures it is
|
# This shell script instantiates a MinIO instance using Docker and ensures it is
|
||||||
@@ -82,7 +82,7 @@ docker run -dt --rm \
|
|||||||
-e "MINIO_CONSOLE_ADDRESS=:9090" \
|
-e "MINIO_CONSOLE_ADDRESS=:9090" \
|
||||||
-e 'MINIO_VOLUMES=/mnt/data' \
|
-e 'MINIO_VOLUMES=/mnt/data' \
|
||||||
--name "$container_name" \
|
--name "$container_name" \
|
||||||
"${DOCKER_MIRROR:+$DOCKER_MIRROR/}minio/minio" server
|
minio/minio server
|
||||||
echo -e 'Done!'
|
echo -e 'Done!'
|
||||||
|
|
||||||
echo 'Waiting for MinIO instance to be healthy...'
|
echo 'Waiting for MinIO instance to be healthy...'
|
||||||
|
|||||||
Reference in new issue
Block a user