From b244bb58aa161d1d978a3b7353555ae50015d78d Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 3 Feb 2026 15:30:04 -0800 Subject: [PATCH] s3tables: redesign Iceberg REST Catalog using iceberg-go and automate integration tests (#8197) * full integration with iceberg-go * Table Commit Operations (handleUpdateTable) * s3tables: fix Iceberg v2 compliance and namespace properties This commit ensures SeaweedFS Iceberg REST Catalog is compliant with Iceberg Format Version 2 by: - Using iceberg-go's table.NewMetadataWithUUID for strict v2 compliance. - Explicitly initializing namespace properties to empty maps. - Removing omitempty from required Iceberg response fields. - Fixing CommitTableRequest unmarshaling using table.Requirements and table.Updates. * s3tables: automate Iceberg integration tests - Added Makefile for local test execution and cluster management. - Added docker-compose for PyIceberg compatibility kit. - Added Go integration test harness for PyIceberg. - Updated GitHub CI to run Iceberg catalog tests automatically. * s3tables: update PyIceberg test suite for compatibility - Updated test_rest_catalog.py to use latest PyIceberg transaction APIs. - Updated Dockerfile to include pyarrow and pandas dependencies. - Improved namespace and table handling in integration tests. * s3tables: address review feedback on Iceberg Catalog - Implemented robust metadata version parsing and incrementing. - Ensured table metadata changes are persisted during commit (handleUpdateTable). - Standardized namespace property initialization for consistency. - Fixed unused variable and incorrect struct field build errors. * s3tables: finalize Iceberg REST Catalog and optimize tests - Implemented robust metadata versioning and persistence. - Standardized namespace property initialization. - Optimized integration tests using pre-built Docker image. - Added strict property persistence validation to test suite. - Fixed build errors from previous partial updates. * Address PR review: fix Table UUID stability, implement S3Tables UpdateTable, and support full metadata persistence individually * fix: Iceberg catalog stable UUIDs, metadata persistence, and file writing - Ensure table UUIDs are stable (do not regenerate on load). - Persist full table metadata (Iceberg JSON) in s3tables extended attributes. - Add `MetadataVersion` to explicitly track version numbers, replacing regex parsing. - Implement `saveMetadataFile` to persist metadata JSON files to the Filer on commit. - Update `CreateTable` and `UpdateTable` handlers to use the new logic. * test: bind weed mini to 0.0.0.0 in integration tests to fix Docker connectivity * Iceberg: fix metadata handling in REST catalog - Add nil guard in createTable - Fix updateTable to correctly load existing metadata from storage - Ensure full metadata persistence on updates - Populate loadTable result with parsed metadata * S3Tables: add auth checks and fix response fields in UpdateTable - Add CheckPermissionWithContext to UpdateTable handler - Include TableARN and MetadataLocation in UpdateTable response - Use ErrCodeConflict (409) for version token mismatches * Tests: improve Iceberg catalog test infrastructure and cleanup - Makefile: use PID file for precise process killing - test_rest_catalog.py: remove unused variables and fix f-strings * Iceberg: fix variable shadowing in UpdateTable - Rename inner loop variable `req` to `requirement` to avoid shadowing outer request variable * S3Tables: simplify MetadataVersion initialization - Use `max(req.MetadataVersion, 1)` instead of anonymous function * Tests: remove unicode characters from S3 tables integration test logs - Remove unicode checkmarks from test output for cleaner logs * Iceberg: improve metadata persistence robustness - Fix MetadataLocation in LoadTableResult to fallback to generated location - Improve saveMetadataFile to ensure directory hierarchy existence and robust error handling --- .github/workflows/s3-tables-tests.yml | 8 +- go.mod | 28 +- go.sum | 316 +++++++++++++- test/s3tables/catalog/Dockerfile.pyiceberg | 13 + test/s3tables/catalog/Makefile | 37 ++ .../s3tables/catalog/docker-compose.test.yaml | 33 ++ test/s3tables/catalog/iceberg_catalog_test.go | 1 + test/s3tables/catalog/pyiceberg_test.go | 80 ++++ test/s3tables/catalog/test_rest_catalog.py | 236 ++++++++++ .../s3tables_integration_test.go | 52 +-- weed/s3api/iceberg/iceberg.go | 404 ++++++++++++++++-- weed/s3api/iceberg/types.go | 179 ++++---- weed/s3api/s3tables/handler.go | 2 + weed/s3api/s3tables/handler_bucket_create.go | 2 +- .../handler_bucket_get_list_delete.go | 6 +- weed/s3api/s3tables/handler_namespace.go | 14 +- weed/s3api/s3tables/handler_policy.go | 28 +- weed/s3api/s3tables/handler_table.go | 226 +++++++++- weed/s3api/s3tables/iceberg_layout.go | 2 +- weed/s3api/s3tables/types.go | 39 +- weed/s3api/s3tables/utils.go | 13 +- 21 files changed, 1486 insertions(+), 233 deletions(-) create mode 100644 test/s3tables/catalog/Dockerfile.pyiceberg create mode 100644 test/s3tables/catalog/Makefile create mode 100644 test/s3tables/catalog/docker-compose.test.yaml create mode 100644 test/s3tables/catalog/pyiceberg_test.go create mode 100644 test/s3tables/catalog/test_rest_catalog.py diff --git a/.github/workflows/s3-tables-tests.yml b/.github/workflows/s3-tables-tests.yml index acb4a0db8..8b445b85b 100644 --- a/.github/workflows/s3-tables-tests.yml +++ b/.github/workflows/s3-tables-tests.yml @@ -84,10 +84,6 @@ jobs: go-version-file: 'go.mod' id: go - - name: Install SeaweedFS - run: | - go install -buildvcs=false ./weed - - name: Run Iceberg Catalog Integration Tests timeout-minutes: 25 working-directory: test/s3tables/catalog @@ -100,8 +96,8 @@ jobs: df -h echo "=== Starting Iceberg Catalog Tests ===" - # Run Iceberg catalog integration tests - go test -v -timeout 20m . 2>&1 | tee test-output.log || { + # Run Iceberg catalog integration tests using Makefile (handles build) + make test 2>&1 | tee test-output.log || { echo "Iceberg catalog integration tests failed" exit 1 } diff --git a/go.mod b/go.mod index a8a321cba..a244a069e 100644 --- a/go.mod +++ b/go.mod @@ -120,6 +120,7 @@ require ( github.com/ThreeDotsLabs/watermill v1.5.1 github.com/a-h/templ v0.3.977 github.com/apache/cassandra-gocql-driver/v2 v2.0.0 + github.com/apache/iceberg-go v0.4.0 github.com/apple/foundationdb/bindings/go v0.0.0-20250911184653-27f7192f47c3 github.com/arangodb/go-driver v1.6.9 github.com/armon/go-metrics v0.4.1 @@ -168,11 +169,19 @@ require ( require github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect require ( + atomicgo.dev/cursor v0.2.0 // indirect + atomicgo.dev/keyboard v0.2.9 // indirect + atomicgo.dev/schedule v0.1.0 // indirect cloud.google.com/go/longrunning v0.7.0 // indirect cloud.google.com/go/pubsub/v2 v2.2.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest/to v0.4.1 // indirect github.com/a1ex3/zstd-seekable-format-go/pkg v0.10.0 // indirect github.com/anchore/go-lzo v0.1.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + github.com/apache/arrow-go/v18 v18.4.1 // indirect + github.com/apache/thrift v0.22.0 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bazelbuild/rules_go v0.46.0 // indirect @@ -184,11 +193,12 @@ require ( github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect - github.com/cockroachdb/apd/v3 v3.1.0 // indirect + github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/version v0.0.0-20250314144055-3860cd14adf2 // indirect + github.com/containerd/console v1.0.5 // indirect github.com/dave/dst v0.27.2 // indirect github.com/diskfs/go-diskfs v1.7.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect @@ -196,8 +206,10 @@ require ( github.com/goccy/go-yaml v1.18.0 // indirect github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/gookit/color v1.5.4 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/hamba/avro/v2 v2.30.0 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect @@ -208,15 +220,20 @@ require ( github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jaegertracing/jaeger v1.47.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/klauspost/asmfmt v1.3.2 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // indirect github.com/lithammer/shortuuid/v3 v3.0.7 // indirect + github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect + github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/parquet-go/bitpack v1.0.0 // indirect github.com/parquet-go/jsonlite v1.0.0 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pierrre/geohash v1.0.0 // indirect github.com/pquerna/otp v1.5.0 // indirect + github.com/pterm/pterm v0.12.81 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.57.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect @@ -224,12 +241,16 @@ require ( github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/smarty/assertions v1.15.0 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/substrait-io/substrait v0.69.0 // indirect + github.com/substrait-io/substrait-go/v4 v4.4.0 // indirect + github.com/substrait-io/substrait-protobuf/go v0.71.0 // indirect github.com/twpayne/go-geom v1.4.1 // indirect github.com/twpayne/go-kml v1.5.2 // indirect github.com/ulikunitz/xz v0.5.15 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect @@ -238,6 +259,7 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/mod v0.31.0 // indirect + golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc // indirect gonum.org/v1/gonum v0.16.0 // indirect ) @@ -432,7 +454,7 @@ require ( github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/twmb/murmur3 v1.1.3 // indirect + github.com/twmb/murmur3 v1.1.8 // indirect github.com/ugorji/go/codec v1.3.0 // indirect github.com/unknwon/goconfig v1.0.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect diff --git a/go.sum b/go.sum index a781ae1e8..7e3d19b08 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,11 @@ +atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= +atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= +atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= +atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= +atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= +atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -536,11 +544,17 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= +github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= @@ -560,6 +574,12 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.3/go.mod h1:URuDvhmATV github.com/Azure/azure-sdk-for-go/sdk/storage/azfile v1.5.3 h1:sxgSqOB9CDToiaVFpxuvb5wGgGqWa3lCShcm5o0n3bE= github.com/Azure/azure-sdk-for-go/sdk/storage/azfile v1.5.3/go.mod h1:XdED8i399lEVblYHTZM8eXaP07gv4Z58IL6ueMlVlrg= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +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/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest/to v0.4.1 h1:CxNHBqdzTr7rLtdrtb5CMjJcDut+WNGCVv7OmS5+lTc= +github.com/Azure/go-autorest/autorest/to v0.4.1/go.mod h1:EtaofgU4zmtvn1zT2ARsjRFdq9vXx0YWtmElwL+GZ9M= github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A= github.com/Azure/go-ntlmssp v0.1.0/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= @@ -574,6 +594,8 @@ github.com/DATA-DOG/go-sqlmock v1.3.2 h1:2L2f5t3kKnCLxnClDD/PrDfExFFa1wjESgxHG/B github.com/DATA-DOG/go-sqlmock v1.3.2/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e h1:rd4bOvKmDIx0WeTv9Qz+hghsgyjikFiPrseXHlKepO0= +github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e/go.mod h1:blbwPQh4DTlCZEfk1BLU4oMIhLda2U+A840Uag9DsZw= github.com/Files-com/files-sdk-go/v3 v3.2.264 h1:lMHTplAYI9FtmCo/QOcpRxmPA5REVAct1r2riQmDQKw= github.com/Files-com/files-sdk-go/v3 v3.2.264/go.mod h1:wGqkOzRu/ClJibvDgcfuJNAqI2nLhe8g91tPlDKRCdE= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= @@ -589,11 +611,20 @@ github.com/IBM/go-sdk-core/v5 v5.21.0/go.mod h1:Q3BYO6iDA2zweQPDGbNTtqft5tDcEpm6 github.com/Jille/raft-grpc-transport v1.6.1 h1:gN3sjapb+fVbiebS7AfQQgbV2ecTOI7ur7NPPC7Mhoc= github.com/Jille/raft-grpc-transport v1.6.1/go.mod h1:HbOjEdu/yzCJ/mjTF6wEOJNbAUpHfU2UOA2hVD4CNFg= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= +github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= +github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= +github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= +github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= +github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= +github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd h1:nzE1YQBdx1bq9IlZinHa+HVffy+NmVRoKr+wHN8fpLE= github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd/go.mod h1:C8yoIfvESpM3GD07OCHU7fqI7lhwyZ2Td1rbNbTAhnc= @@ -636,6 +667,8 @@ github.com/aalpar/deheap v0.0.0-20210914013432-0cc84d79dec3 h1:hhdWprfSpFbN7lz3W github.com/aalpar/deheap v0.0.0-20210914013432-0cc84d79dec3/go.mod h1:XaUnRxSCYgL3kkgX0QHIV0D+znljPIDImxlv2kbGv0Y= github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0= github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -655,10 +688,20 @@ github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUS github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= +github.com/apache/arrow-go/v18 v18.4.1 h1:q/jVkBWCJOB9reDgaIZIdruLQUb1kbkvOnOFezVH1C4= +github.com/apache/arrow-go/v18 v18.4.1/go.mod h1:tLyFubsAl17bvFdUAy24bsSvA/6ww95Iqi67fTpGu3E= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/cassandra-gocql-driver/v2 v2.0.0 h1:Omnzb1Z/P90Dr2TbVNu54ICQL7TKVIIsJO231w484HU= github.com/apache/cassandra-gocql-driver/v2 v2.0.0/go.mod h1:QH/asJjB3mHvY6Dot6ZKMMpTcOrWJ8i9GhsvG1g0PK4= +github.com/apache/iceberg-go v0.4.0 h1:O3RwMyoc4gZpu9b1aBDJBiH14riga68BWypZVaSHZE0= +github.com/apache/iceberg-go v0.4.0/go.mod h1:TInpnxHnMdPrsmW/klToy9mM8gs6/6FsI+O2kwtlHMQ= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/apache/thrift v0.22.0 h1:r7mTJdj51TMDe6RtcmNdQxgn9XcyfGDOzegMDRg47uc= +github.com/apache/thrift v0.22.0/go.mod h1:1e7J/O1Ae6ZQMTYdy9xa3w9k+XHWPfRvdPyJeynQ+/g= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/apple/foundationdb/bindings/go v0.0.0-20250911184653-27f7192f47c3 h1:WZaTKNHCfcw7fWSR6/RKnCldVzvYZC+Y20Su4lffEIg= github.com/apple/foundationdb/bindings/go v0.0.0-20250911184653-27f7192f47c3/go.mod h1:OMVSB21p9+xQUIqlGizHPZfjK+SHws1ht+ZytVDoz9U= github.com/appscode/go-querystring v0.0.0-20170504095604-0126cfb3f1dc h1:LoL75er+LKDHDUfU5tRvFwxH0LjPpZN8OoG8Ll+liGU= @@ -669,6 +712,7 @@ github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e h1:Xg+hGrY2 github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:mq7Shfa/CaixoDxiyAAc5jZ6CVBAyPaNQCGS7mkj4Ho= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU= @@ -749,6 +793,8 @@ github.com/buengese/sgzip v0.1.1 h1:ry+T8l1mlmiWEsDrH/YHZnCVWD2S3im1KLsyO+8ZmTU= github.com/buengese/sgzip v0.1.1/go.mod h1:i5ZiXGF3fhV7gL1xaRRL1nDnmpNj0X061FQzOS8VMas= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY= +github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= @@ -813,8 +859,8 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= -github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= -github.com/cockroachdb/apd/v3 v3.1.0/go.mod h1:6qgPBMXjATAdD/VefbRP9NoSLKjbB4LCoA7gN4LpHs4= +github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= +github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k= @@ -827,11 +873,36 @@ github.com/cognusion/imaging v1.0.2 h1:BQwBV8V8eF3+dwffp8Udl9xF1JKh5Z0z5JkJwAi98 github.com/cognusion/imaging v1.0.2/go.mod h1:mj7FvH7cT2dlFogQOSUQRtotBxJ4gFQ2ySMSmBm5dSk= github.com/colinmarc/hdfs/v2 v2.4.0 h1:v6R8oBx/Wu9fHpdPoJJjpGSUxo8NhHIwrwsfhFvU9W0= github.com/colinmarc/hdfs/v2 v2.4.0/go.mod h1:0NAO+/3knbMx6+5pCv+Hcbaz4xn/Zzbn9+WIib2rKVI= +github.com/compose-spec/compose-go/v2 v2.6.0 h1:/+oBD2ixSENOeN/TlJqWZmUak0xM8A7J08w/z661Wd4= +github.com/compose-spec/compose-go/v2 v2.6.0/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= +github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0= +github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= +github.com/containerd/containerd/v2 v2.0.5 h1:2vg/TjUXnaohAxiHnthQg8K06L9I4gdYEMcOLiMc8BQ= +github.com/containerd/containerd/v2 v2.0.5/go.mod h1:Qqo0UN43i2fX1FLkrSTCg6zcHNfjN7gEnx3NPRZI+N0= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= +github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E= +github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= +github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= +github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= +github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk= github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= @@ -858,12 +929,36 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8= github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/docker/buildx v0.22.0 h1:pGTcGZa+kxpYUlM/6ACsp1hXhkEDulz++RNXPdE8Afk= +github.com/docker/buildx v0.22.0/go.mod h1:ThbnUe4kNiStlq6cLXruElyEdSTdPL3k/QerNUmPvHE= +github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A= +github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0= +github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc= +github.com/docker/compose/v2 v2.35.0 h1:bU23OeFrbGyHYrKijMSEwkOeDg2TLhAGntU2F3hwX1o= +github.com/docker/compose/v2 v2.35.0/go.mod h1:S5ejUILn9KTYC6noX3IxznWu3/sb3FxdZqIYbq4seAk= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v28.5.0+incompatible h1:ZdSQoRUE9XxhFI/B8YLvhnEFMmYN9Pp8Egd2qcaFk1E= +github.com/docker/docker v28.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= +github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= +github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= +github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dropbox/dropbox-sdk-go-unofficial/v6 v6.0.5 h1:FT+t0UEDykcor4y3dMVKXIiWJETBpRgERYTGlmMd7HU= github.com/dropbox/dropbox-sdk-go-unofficial/v6 v6.0.5/go.mod h1:rSS3kM9XMzSQ6pw91Qgd6yB5jdt70N4OdtrAf74As5M= @@ -880,6 +975,8 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg= +github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo= github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY= @@ -888,6 +985,8 @@ github.com/emersion/go-message v0.18.2 h1:rl55SQdjd9oJcIoQNhubD2Acs1E6IzlZISRTK7 github.com/emersion/go-message v0.18.2/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA= github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff h1:4N8wnS3f1hNHSmFD5zgFkWCyA4L1kCDkImPAtK7D6tg= github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -938,10 +1037,16 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsevents v0.2.0 h1:BRlvlqjvNTfogHfeBOFvSC9N0Ddy+wzQCQukyoD7o/c= +github.com/fsnotify/fsevents v0.2.0/go.mod h1:B3eEk39i4hz8y1zaWS/wPrAP4O6wkIl7HQwKBr1qH/w= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= +github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik= github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/geoffgarside/ber v1.2.0 h1:/loowoRcs/MWLYmGX9QtIAbA+V/FrnVLsMMPhwiRm64= @@ -995,8 +1100,14 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/errors v0.22.4 h1:oi2K9mHTOb5DPW2Zjdzs/NIvwi2N3fARKaTJLdNabaM= github.com/go-openapi/errors v0.22.4/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ= github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= @@ -1057,8 +1168,8 @@ github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -1104,6 +1215,8 @@ github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl76 github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers/go v0.0.0-20230108230133-3b8644d32c50 h1:T0YCYlZLzmdsd0bsozI4ecxk03KYOiszof14y7ekQFw= github.com/google/flatbuffers/go v0.0.0-20230108230133-3b8644d32c50/go.mod h1:qmRCJW6OqZkfBt584Cmq1im0f4367CLrdABrq5lMOWo= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -1157,6 +1270,8 @@ github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -1186,6 +1301,10 @@ github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81 github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= @@ -1200,6 +1319,8 @@ github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pw github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= @@ -1208,6 +1329,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4Zs github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= +github.com/hamba/avro/v2 v2.30.0 h1:OaIdh0+dZIJ331FO/+YYBwZZRdGVyyHuRSyHsjZLJoA= +github.com/hamba/avro/v2 v2.30.0/go.mod h1:X6gDhYv6DQVAT56VqOKuW+PLnQrEQqGB9l1nhlMdAdQ= 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/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -1245,6 +1368,8 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b 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/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= @@ -1275,8 +1400,14 @@ github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/in-toto/in-toto-golang v0.5.0 h1:hb8bgwr0M2hGdDsLjkJ3ZqJ8JFLL/tgYdAxF/XEFBbY= +github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1GdHMCq8+WPxw8/BE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s= +github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -1303,6 +1434,8 @@ github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5Xum github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jlaffaye/ftp v0.2.1-0.20240918233326-1b970516f5d3 h1:ZxO6Qr2GOXPdcW80Mcn3nemvilMPvpWqxrNfK2ZnNNs= github.com/jlaffaye/ftp v0.2.1-0.20240918233326-1b970516f5d3/go.mod h1:dvLUr/8Fs9a2OBrEnCC5duphbkz/k/mSy5OkXg3PAgI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -1341,16 +1474,20 @@ github.com/karlseguin/ccache/v2 v2.0.8 h1:lT38cE//uyf6KcFok0rlgXtGFBWxkI6h/qg4tb github.com/karlseguin/ccache/v2 v2.0.8/go.mod h1:2BDThcfQMf/c0jnZowt16eW405XIqZPavt+HoYEtcxQ= github.com/karlseguin/expect v1.0.2-0.20190806010014-778a5f0c6003 h1:vJ0Snvo+SLMY72r5J4sEfkuE7AFbixEP2qRbEcum/wA= github.com/karlseguin/expect v1.0.2-0.20190806010014-778a5f0c6003/go.mod h1:zNBxMY8P21owkeogJELCLeHIt+voOSduHYTFUbwRAV8= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= 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/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/reedsolomon v1.13.0 h1:E0Cmgf2kMuhZTj6eefnvpKC4/Q4jhCi9YIjcZjK4arc= @@ -1391,6 +1528,8 @@ github.com/linkedin/goavro/v2 v2.14.1 h1:/8VjDpd38PRsy02JS0jflAu7JZPfJcGTwqWgMkF github.com/linkedin/goavro/v2 v2.14.1/go.mod h1:KXx+erlq+RPlGSPmLF7xGo6SAbh8sCQ53x064+ioxhk= github.com/linxGnu/grocksdb v1.10.3 h1:0laII9AQ6kFxo5SjhdTfSh9EgF20piD6TMHK6YuDm+4= github.com/linxGnu/grocksdb v1.10.3/go.mod h1:OLQKZwiKwaJiAVCsOzWKvwiLwfZ5Vz8Md5TYR7t7pM8= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8= github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts= github.com/lpar/date v1.0.0 h1:bq/zVqFTUmsxvd/CylidY4Udqpr9BOFrParoP6p0x/I= @@ -1400,6 +1539,8 @@ github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIv github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= @@ -1413,13 +1554,22 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= +github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= @@ -1430,13 +1580,43 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY= github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mmcloughlin/geohash v0.9.0 h1:FihR004p/aE1Sju6gcVq5OLDqGcMnpBY+8moBqIsVOs= github.com/mmcloughlin/geohash v0.9.0/go.mod h1:oNZxQo5yWJh0eMQEP/8hwQuVx9Z9tjwFUqcTB1SmG0c= +github.com/moby/buildkit v0.20.1 h1:sT0ZXhhNo5rVbMcYfgttma3TdUHfO5JjFA0UAL8p9fY= +github.com/moby/buildkit v0.20.1/go.mod h1:Rq9nB/fJImdk6QeM0niKtOHJqwKeYMrK847hTTDVuA4= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= +github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= +github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk= +github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= +github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= +github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU= +github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1446,10 +1626,14 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nats-io/jwt/v2 v2.5.0 h1:WQQ40AAlqqfx+f6ku+i0pOVm+ASirD4fUh+oQsiE9Ak= github.com/nats-io/jwt/v2 v2.5.0/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI= github.com/nats-io/nats-server/v2 v2.9.23 h1:6Wj6H6QpP9FMlpCyWUaNu2yeZ/qGj+mdRkZ1wbikExU= @@ -1480,7 +1664,11 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +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/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= @@ -1504,6 +1692,8 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pengsrc/go-shared v0.2.1-0.20190131101655-1999055a4a14 h1:XeOYlK9W1uCmhjJSsY78Mcuh7MVkNjTzmHx1yBzizSU= @@ -1592,12 +1782,25 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= +github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= +github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= +github.com/pterm/pterm v0.12.81 h1:ju+j5I2++FO1jBKMmscgh5h5DPFDFMB7epEjSoKehKA= +github.com/pterm/pterm v0.12.81/go.mod h1:TyuyrPjnxfwP+ccJdBTeWHtd/e0ybQHkOS/TakajZCw= github.com/putdotio/go-putio/putio v0.0.0-20200123120452-16d982cac2b8 h1:Y258uzXU/potCYnQd1r6wlAnoMB68BiCkCcCnKx1SH8= github.com/putdotio/go-putio/putio v0.0.0-20200123120452-16d982cac2b8/go.mod h1:bSJjRokAHHOhA+XFxplld8w2R/dXLH7Z3BZ532vhFwU= +github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= +github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/quic-go v0.57.0 h1:AsSSrrMs4qI/hLrKlTH/TGQeTMY0ib1pAOX7vA3AdqE= github.com/quic-go/quic-go v0.57.0/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= +github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc h1:zAsgcP8MhzAbhMnB1QQ2O7ZhWYVGYSR2iVcjzQuPV+o= +github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc/go.mod h1:S8xSOnV3CgpNrWd0GQ/OoQfMtlg2uPRSuTzcSGrzwK8= github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= github.com/rclone/rclone v1.72.1 h1:Cc/NshKd3/TP3CC0cx9Jg9nTLG8YQ8yLYMTm6Z/LdHk= @@ -1622,6 +1825,7 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rfjakob/eme v1.1.2 h1:SxziR8msSOElPayZNFfQw4Tjx/Sbaeeh3eRvrHVMUs4= github.com/rfjakob/eme v1.1.2/go.mod h1:cVvpasglm/G3ngEfcfT/Wt0GwhkuO32pf/poW6Nyk1k= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -1654,10 +1858,16 @@ github.com/seaweedfs/goexif v1.0.3 h1:ve/OjI7dxPW8X9YQsv3JuVMaxEyF9Rvfd04ouL+Bz3 github.com/seaweedfs/goexif v1.0.3/go.mod h1:Oni780Z236sXpIQzk1XoJlTwqrJ02smEin9zQeff7Fk= github.com/seaweedfs/raft v1.1.6 h1:e83Xn0boscPnuSiBllUPeWRVS6JKrqJPYBozgFyBiC0= github.com/seaweedfs/raft v1.1.6/go.mod h1:9cYlEBA+djJbnf/5tWsCybtbL7ICYpi+Uxcg3MxjuNs= +github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE= +github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= +github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= +github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/shirou/gopsutil/v4 v4.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo= github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -1726,6 +1936,12 @@ github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08Yu github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/substrait-io/substrait v0.69.0 h1:qfwUe1qKa3PsCclMpubQOF6nqIqS14geUuvzJ1P7gsM= +github.com/substrait-io/substrait v0.69.0/go.mod h1:MPFNw6sToJgpD5Z2rj0rQrdP/Oq8HG7Z2t3CAEHtkHw= +github.com/substrait-io/substrait-go/v4 v4.4.0 h1:mFArMNFxlOLyTuhPcaPzZCwYh6kUopTExTy7XOqtYBM= +github.com/substrait-io/substrait-go/v4 v4.4.0/go.mod h1:GzpaFqO5VRtMkEjATgRxGK5p82OmEtCmszAVYxE+iWc= +github.com/substrait-io/substrait-protobuf/go v0.71.0 h1:vkYGEEPJ8lWSwaJvX7Y+hEmwmrz5/qeDmGI43JpKJZE= +github.com/substrait-io/substrait-protobuf/go v0.71.0/go.mod h1:hn+Szm1NmZZc91FwWK9EXD/lmuGBSRTJ5IvHhlG1YnQ= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/t3rm1n4l/go-mega v0.0.0-20251031123324-a804aaa87491 h1:rrGZv6xYk37hx0tW2sYfgbO0PqStbHqz6Bq6oc9Hurg= @@ -1735,8 +1951,14 @@ github.com/tarantool/go-iproto v1.1.0 h1:HULVOIHsiehI+FnHfM7wMDntuzUddO09DKqu2Wn github.com/tarantool/go-iproto v1.1.0/go.mod h1:LNCtdyZxojUed8SbOiYHoc3v9NvaZTB7p96hUySMlIo= github.com/tarantool/go-tarantool/v2 v2.4.1 h1:Bk9mh+gMPVmHTSefHvVBpEkf6P2UZA/8xa5kqgyQtyo= github.com/tarantool/go-tarantool/v2 v2.4.1/go.mod h1:MTbhdjFc3Jl63Lgi/UJr5D+QbT+QegqOzsNJGmaw7VM= +github.com/testcontainers/testcontainers-go v0.39.0 h1:uCUJ5tA+fcxbFAB0uP3pIK3EJ2IjjDUHFSZ1H1UxAts= +github.com/testcontainers/testcontainers-go v0.39.0/go.mod h1:qmHpkG7H5uPf/EvOORKvS6EuDkBUPE3zpVGaH9NL7f8= +github.com/testcontainers/testcontainers-go/modules/compose v0.39.0 h1:N9Kn9UOIq24o3Y01SFDYF5y3hpq4dNBzDS4pynHb/OQ= +github.com/testcontainers/testcontainers-go/modules/compose v0.39.0/go.mod h1:7OreVKOBlnD0EmOYXMSBbJkHM8JZQINr34vB0X/NJRs= github.com/the42/cartconvert v0.0.0-20131203171324-aae784c392b8 h1:I4DY8wLxJXCrMYzDM6lKCGc3IQwJX0PlTLsd3nQqI3c= github.com/the42/cartconvert v0.0.0-20131203171324-aae784c392b8/go.mod h1:fWO/msnJVhHqN1yX6OBoxSyfj7TEj1hHiL8bJSQsK30= +github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c= +github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= github.com/tiancaiamao/gp v0.0.0-20221230034425-4025bc8a4d4a h1:J/YdBZ46WKpXsxsW93SG+q0F8KI+yFrcIDT4c/RNoc4= github.com/tiancaiamao/gp v0.0.0-20221230034425-4025bc8a4d4a/go.mod h1:h4xBhSNtOeEosLJ4P7JyKXX7Cabg7AVkWCK5gV2vOrM= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -1750,6 +1972,8 @@ github.com/tikv/client-go/v2 v2.0.7 h1:nNTx/AR6n8Ew5VtHanFPG8NkFLLXbaNs5/K43DDma github.com/tikv/client-go/v2 v2.0.7/go.mod h1:9JNUWtHN8cx8eynHZ9xzdPi5YY6aiN1ILQyhfPUBcMo= github.com/tikv/pd/client v0.0.0-20230329114254-1948c247c2b1 h1:bzlSSzw+6qTwPs8pMcPI1bt27TAOhSdAEwdPCz6eBlg= github.com/tikv/pd/client v0.0.0-20230329114254-1948c247c2b1/go.mod h1:3cTcfo8GRA2H/uSttqA3LvMfMSHVBJaXk3IgkFXFVxo= +github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA= +github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/tinylib/msgp v1.5.0 h1:GWnqAE54wmnlFazjq2+vgr736Akg58iiHImh+kPY2pc= github.com/tinylib/msgp v1.5.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o= @@ -1757,13 +1981,25 @@ github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYI github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= +github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI= +github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY= +github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a h1:EfGw4G0x/8qXWgtcZ6KVaPS+wpWOQMaypczzP8ojkMY= +github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a/go.mod h1:Dl/9oEjK7IqnjAm21Okx/XIxUCFJzvh+XdVHUlBwXTw= +github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8= +github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE= +github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0= +github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk= +github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw= +github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc= github.com/tsuna/gohbase v0.0.0-20201125011725-348991136365 h1:6iRwZdrFUzbcVYZwa8dXTIILGIxmmhjyUPJEcwzPGaU= github.com/tsuna/gohbase v0.0.0-20201125011725-348991136365/go.mod h1:zj0GJHGvyf1ed3Jm/Tb4830c/ZKDq+YoLsCt2rGQuT0= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/twmb/murmur3 v1.1.3 h1:D83U0XYKcHRYwYIpBKf3Pks91Z0Byda/9SJ8B6EMRcA= -github.com/twmb/murmur3 v1.1.3/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= +github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/twpayne/go-geom v1.4.1 h1:LeivFqaGBRfyg0XJJ9pkudcptwhSSrYN9KZUW6HcgdA= github.com/twpayne/go-geom v1.4.1/go.mod h1:k/zktXdL+qnA6OgKsdEGUTA17jbQ2ZPTUa3CCySuGpE= github.com/twpayne/go-kml v1.5.2 h1:rFMw2/EwgkVssGS2MT6YfWSPZz6BgcJkLxQ53jnE8rQ= @@ -1778,6 +2014,22 @@ github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/unknwon/goconfig v1.0.0 h1:rS7O+CmUdli1T+oDm7fYj1MwqNWtEJfNj+FqcUHML8U= github.com/unknwon/goconfig v1.0.0/go.mod h1:qu2ZQ/wcC/if2u32263HTVC39PeOQRSmidQk3DuDFQ8= +github.com/uptrace/bun v1.2.15 h1:Ut68XRBLDgp9qG9QBMa9ELWaZOmzHNdczHQdrOZbEFE= +github.com/uptrace/bun v1.2.15/go.mod h1:Eghz7NonZMiTX/Z6oKYytJ0oaMEJ/eq3kEV4vSqG038= +github.com/uptrace/bun/dialect/mssqldialect v1.2.15 h1:QbXtaIlBwx8z0PctUzAQrg4uxRRAKUhkOV4WJvkNo74= +github.com/uptrace/bun/dialect/mssqldialect v1.2.15/go.mod h1:PJxf6utV3uwiBww37CQVD5jvarUKkJHNqSWDO1GkmN4= +github.com/uptrace/bun/dialect/mysqldialect v1.2.15 h1:z/Seg0ljdqoATl0RGPBLHkod1bT0RofL5nNvqdt+UcM= +github.com/uptrace/bun/dialect/mysqldialect v1.2.15/go.mod h1:VUi7mXAL3ttEphcdDta+dXeB7wyI/uvQiE6G8S8ipSQ= +github.com/uptrace/bun/dialect/oracledialect v1.2.15 h1:M7V0BZXlBWirFg5TM/7KZzvB37bHsjslBufOTb/cQqU= +github.com/uptrace/bun/dialect/oracledialect v1.2.15/go.mod h1:M79iy3e2GOkQ4ItatKh5Nz3hKrrV14hnCJVhHbpk538= +github.com/uptrace/bun/dialect/pgdialect v1.2.15 h1:er+/3giAIqpfrXJw+KP9B7ujyQIi5XkPnFmgjAVL6bA= +github.com/uptrace/bun/dialect/pgdialect v1.2.15/go.mod h1:QSiz6Qpy9wlGFsfpf7UMSL6mXAL1jDJhFwuOVacCnOQ= +github.com/uptrace/bun/dialect/sqlitedialect v1.2.15 h1:7upGMVjFRB1oI78GQw6ruNLblYn5CR+kxqcbbeBBils= +github.com/uptrace/bun/dialect/sqlitedialect v1.2.15/go.mod h1:c7YIDaPNS2CU2uI1p7umFuFWkuKbDcPDDvp+DLHZnkI= +github.com/uptrace/bun/driver/sqliteshim v1.2.15 h1:M/rZJSjOPV4OmfTVnDPtL+wJmdMTqDUn8cuk5ycfABA= +github.com/uptrace/bun/driver/sqliteshim v1.2.15/go.mod h1:YqwxFyvM992XOCpGJtXyKPkgkb+aZpIIMzGbpaw1hIk= +github.com/uptrace/bun/extra/bundebug v1.2.15 h1:IY2Z/pVyVg0ApWnQ/pEnwe6BWxlDDATCz7IFZghutCs= +github.com/uptrace/bun/extra/bundebug v1.2.15/go.mod h1:JuE+BT7NjTZ9UKr74eC8s9yZ9dnQCeufDwFRTC8w3Xo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/viant/assertly v0.9.0 h1:uB3jO+qmWQcrSCHQRxA2kk88eXAdaklUUDxxCU5wBHQ= @@ -1794,6 +2046,8 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/ github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -1802,12 +2056,18 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -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-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/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 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/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yandex-cloud/go-genproto v0.0.0-20211115083454-9ca41db5ed9e h1:9LPdmD1vqadsDQUva6t2O9MbnyvoOgo8nFNPaOIH5U8= @@ -1839,6 +2099,8 @@ github.com/yunify/qingstor-sdk-go/v3 v3.2.0 h1:9sB2WZMgjwSUNZhrgvaNGazVltoFUUfuS github.com/yunify/qingstor-sdk-go/v3 v3.2.0/go.mod h1:KciFNuMu6F4WLk9nGwwK69sCGKLCdd9f97ac/wfumS4= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w= +github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/assert v1.3.1 h1:vukIABvugfNMZMQO1ABsyQDJDTVQbn+LWSMy1ol1h6A= github.com/zeebo/assert v1.3.1/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= @@ -1877,14 +2139,22 @@ go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 h1:zG8GlgXCJQd5BU98C0hZnBbElszTmUgCNCfYneaDL0A= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0/go.mod h1:hOfBCz8kv/wuq73Mx2H2QnWokh/kHZxkh6SNF2bdKtw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 h1:ZsXq73BERAiNuuFXYqP4MR5hBrjXfMGSO+Cx7qoOZiM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0/go.mod h1:hg1zaDMpyZJuUzjFxFsRYBoccE86tM9Uf4IqNMUxvrY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0 h1:wm/Q0GAAykXv83wzcKzGGqAnnfLFyFe7RslekZuv+VI= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0/go.mod h1:ra3Pa40+oKjvYh+ZD3EdxFZZB0xdMfuileHAm4nNN7w= go.opentelemetry.io/otel/exporters/zipkin v1.36.0 h1:s0n95ya5tOG03exJ5JySOdJFtwGo4ZQ+KeY7Zro4CLI= @@ -2212,6 +2482,7 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2221,6 +2492,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2251,7 +2523,11 @@ golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc h1:bH6xUXay0AIFMElXG2rQ4uiE+7ncwtiOdPfYK1NK2XA= +golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= @@ -2652,6 +2928,8 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y= +gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI= 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= @@ -2661,6 +2939,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +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/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= @@ -2694,6 +2974,18 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= +k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= +k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= +k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/b v1.0.0 h1:vpvqeyp17ddcQWF29Czawql4lDdABCDRbXRAS4+aF2o= @@ -2766,6 +3058,10 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= storj.io/common v0.0.0-20251107171817-6221ae45072c h1:UDXSrdeLJe3QFouavSW10fYdpclK0YNu3KvQHzqq2+k= @@ -2780,3 +3076,5 @@ storj.io/picobuf v0.0.4 h1:qswHDla+YZ2TovGtMnU4astjvrADSIz84FXRn0qgP6o= storj.io/picobuf v0.0.4/go.mod h1:hSMxmZc58MS/2qSLy1I0idovlO7+6K47wIGUyRZa6mg= storj.io/uplink v1.13.1 h1:C8RdW/upALoCyuF16Lod9XGCXEdbJAS+ABQy9JO/0pA= storj.io/uplink v1.13.1/go.mod h1:x0MQr4UfFsQBwgVWZAtEsLpuwAn6dg7G0Mpne1r516E= +tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc= +tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0= diff --git a/test/s3tables/catalog/Dockerfile.pyiceberg b/test/s3tables/catalog/Dockerfile.pyiceberg new file mode 100644 index 000000000..89b69e19c --- /dev/null +++ b/test/s3tables/catalog/Dockerfile.pyiceberg @@ -0,0 +1,13 @@ +# PyIceberg test container for Iceberg REST Catalog compatibility testing +FROM python:3.11-slim + +WORKDIR /app + +# Install PyIceberg with S3 support and dependencies +RUN pip install --no-cache-dir "pyiceberg[s3fs]" pyarrow pandas + +# Copy the test script +COPY test_rest_catalog.py /app/ + +# Default command +CMD ["python3", "/app/test_rest_catalog.py", "--help"] diff --git a/test/s3tables/catalog/Makefile b/test/s3tables/catalog/Makefile new file mode 100644 index 000000000..20126a14a --- /dev/null +++ b/test/s3tables/catalog/Makefile @@ -0,0 +1,37 @@ +# Makefile for Iceberg REST Catalog Integration Tests +# +# This Makefile provides easy-to-use targets for running integration tests +# and starting a local development environment. + +.PHONY: build mini test test-pyiceberg clean + +# Root directory of the SeaweedFS project +PROJECT_ROOT = ../../.. +WEED_BIN = $(PROJECT_ROOT)/weed/weed + +# Build the weed binary +build: + @echo "Building SeaweedFS binary..." + cd $(PROJECT_ROOT)/weed && go build -v + +# Start SeaweedFS mini cluster for manual testing +# Uses default ports: S3=8333, Iceberg=8182 +mini: build + @echo "Starting SeaweedFS mini cluster..." + $(WEED_BIN) mini -s3.port.iceberg=8182 & echo $$! > weed-mini.pid; wait + +# Run all integration tests in this directory +test: build + @echo "Running all Iceberg catalog integration tests..." + go test -v . + +# Run only the PyIceberg compatibility tests (requires Docker) +test-pyiceberg: build + @echo "Running PyIceberg compatibility tests..." + go test -v -run TestPyIcebergRestCatalog . + +# Clean up temporary data and stop weed mini if running +clean: + @echo "Cleaning up..." + @test -f weed-mini.pid && kill $$(cat weed-mini.pid) && rm weed-mini.pid || true + @rm -rf /tmp/seaweed-iceberg-test-* diff --git a/test/s3tables/catalog/docker-compose.test.yaml b/test/s3tables/catalog/docker-compose.test.yaml new file mode 100644 index 000000000..08f3c9c8d --- /dev/null +++ b/test/s3tables/catalog/docker-compose.test.yaml @@ -0,0 +1,33 @@ +# Iceberg REST Catalog Compatibility Test Docker Compose +# +# This compose file sets up a test environment for validating +# the SeaweedFS Iceberg REST Catalog implementation. +# +# Usage: +# docker compose -f docker-compose.test.yaml up --build +# +# Note: SeaweedFS must be running on the host with the Iceberg REST port exposed. +# Set ICEBERG_CATALOG_URL environment variable to point to your catalog. + +services: + # PyIceberg-based REST catalog test + pyiceberg-test: + build: + context: . + dockerfile: Dockerfile.pyiceberg + environment: + - CATALOG_URL=${CATALOG_URL:-http://host.docker.internal:8182} + - WAREHOUSE=${WAREHOUSE:-s3://test-bucket/} + - PREFIX=${PREFIX:-pyiceberg-test} + - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-test} + - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-test} + - AWS_REGION=${AWS_REGION:-us-east-1} + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ./test_rest_catalog.py:/app/test_rest_catalog.py:ro + command: > + python3 /app/test_rest_catalog.py + --catalog-url ${CATALOG_URL:-http://host.docker.internal:8182} + --warehouse ${WAREHOUSE:-s3://test-bucket/} + --prefix ${PREFIX:-pyiceberg-test} diff --git a/test/s3tables/catalog/iceberg_catalog_test.go b/test/s3tables/catalog/iceberg_catalog_test.go index e5b213275..592526a93 100644 --- a/test/s3tables/catalog/iceberg_catalog_test.go +++ b/test/s3tables/catalog/iceberg_catalog_test.go @@ -171,6 +171,7 @@ func (env *TestEnvironment) StartSeaweedFS(t *testing.T) { "-s3.port", fmt.Sprintf("%d", env.s3Port), "-s3.port.grpc", fmt.Sprintf("%d", env.s3GrpcPort), "-s3.port.iceberg", fmt.Sprintf("%d", env.icebergPort), + "-ip.bind", "0.0.0.0", "-dir", env.dataDir, ) cmd.Stdout = os.Stdout diff --git a/test/s3tables/catalog/pyiceberg_test.go b/test/s3tables/catalog/pyiceberg_test.go new file mode 100644 index 000000000..03bb35718 --- /dev/null +++ b/test/s3tables/catalog/pyiceberg_test.go @@ -0,0 +1,80 @@ +// Package catalog provides integration tests for the Iceberg REST Catalog API. +// This file adds PyIceberg-based compatibility tests using Docker. +package catalog + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" +) + +// TestPyIcebergRestCatalog tests the Iceberg REST Catalog using PyIceberg client in Docker. +// This provides a more comprehensive test than DuckDB as PyIceberg fully exercises the REST API. +// +// Prerequisites: +// - Docker must be available +// - SeaweedFS must be running with Iceberg REST enabled +// +// To run manually: +// +// cd test/s3tables/catalog +// docker compose -f docker-compose.test.yaml up --build +func TestPyIcebergRestCatalog(t *testing.T) { + if testing.Short() { + t.Skip("Skipping integration test in short mode") + } + + env := NewTestEnvironment(t) + defer env.Cleanup(t) + + if !env.dockerAvailable { + t.Skip("Docker not available, skipping PyIceberg integration test") + } + + env.StartSeaweedFS(t) + + // Create the test bucket first + bucketName := "pyiceberg-compat-test" + createTableBucket(t, env, bucketName) + + // Build the test working directory path + testDir := filepath.Join(env.seaweedDir, "test", "s3tables", "catalog") + + // Run PyIceberg test using Docker + catalogURL := fmt.Sprintf("http://host.docker.internal:%d", env.icebergPort) + s3Endpoint := fmt.Sprintf("http://host.docker.internal:%d", env.s3Port) + warehouse := fmt.Sprintf("s3://%s/", bucketName) + + // Build the test image first for faster repeated runs + buildCmd := exec.Command("docker", "build", "-t", "iceberg-rest-test", "-f", "Dockerfile.pyiceberg", ".") + buildCmd.Dir = testDir + if out, err := buildCmd.CombinedOutput(); err != nil { + t.Fatalf("Failed to build test image: %v\n%s", err, string(out)) + } + + cmd := exec.Command("docker", "run", "--rm", + "--add-host", "host.docker.internal:host-gateway", + "-e", fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", "test"), + "-e", fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", "test"), + "-e", fmt.Sprintf("AWS_ENDPOINT_URL=%s", s3Endpoint), + "-v", fmt.Sprintf("%s:/app:ro", testDir), + "iceberg-rest-test", + "python3", "/app/test_rest_catalog.py", + "--catalog-url", catalogURL, + "--warehouse", warehouse, + "--prefix", bucketName, + ) + cmd.Dir = testDir + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + t.Logf("Running PyIceberg REST catalog test...") + t.Logf(" Catalog URL: %s", catalogURL) + t.Logf(" Warehouse: %s", warehouse) + + if err := cmd.Run(); err != nil { + t.Errorf("PyIceberg test failed: %v", err) + } +} diff --git a/test/s3tables/catalog/test_rest_catalog.py b/test/s3tables/catalog/test_rest_catalog.py new file mode 100644 index 000000000..b07ad30e4 --- /dev/null +++ b/test/s3tables/catalog/test_rest_catalog.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +""" +Iceberg REST Catalog Compatibility Test for SeaweedFS + +This script tests the Iceberg REST Catalog API compatibility of the +SeaweedFS Iceberg REST Catalog implementation. + +Usage: + python3 test_rest_catalog.py --catalog-url http://localhost:8182 + +Requirements: + pip install pyiceberg[s3fs] +""" + +import argparse +import sys +from pyiceberg.catalog import load_catalog +from pyiceberg.schema import Schema +from pyiceberg.types import ( + IntegerType, + LongType, + StringType, + NestedField, +) +from pyiceberg.exceptions import ( + NamespaceAlreadyExistsError, + NoSuchNamespaceError, + TableAlreadyExistsError, + NoSuchTableError, +) + + +def test_config_endpoint(catalog): + """Test that the catalog config endpoint returns valid configuration.""" + print("Testing /v1/config endpoint...") + # The catalog is already loaded which means config endpoint worked + print(" /v1/config endpoint working") + return True + + +def test_namespace_operations(catalog, prefix): + """Test namespace CRUD operations.""" + print("Testing namespace operations...") + namespace = (f"{prefix.replace('-', '_')}_test_ns",) + + # List initial namespaces + namespaces = catalog.list_namespaces() + print(f" Initial namespaces: {namespaces}") + + # Create namespace + try: + catalog.create_namespace(namespace) + print(f" Created namespace: {namespace}") + except NamespaceAlreadyExistsError: + print(f" ! Namespace already exists: {namespace}") + + # List namespaces (should include our new one) + namespaces = catalog.list_namespaces() + if namespace in namespaces: + print(" Namespace appears in list") + else: + print(f" Namespace not found in list: {namespaces}") + return False + + # Get namespace properties + try: + props = catalog.load_namespace_properties(namespace) + print(f" Loaded namespace properties: {props}") + except NoSuchNamespaceError: + print(f" Failed to load namespace properties") + return False + + return True + + +def test_table_operations(catalog, prefix): + """Test table CRUD operations.""" + print("Testing table operations...") + namespace = (f"{prefix.replace('-', '_')}_test_ns",) + table_name = "test_table" + table_id = namespace + (table_name,) + + # Define a simple schema + schema = Schema( + NestedField(field_id=1, name="id", field_type=LongType(), required=True), + NestedField(field_id=2, name="name", field_type=StringType(), required=False), + NestedField(field_id=3, name="age", field_type=IntegerType(), required=False), + ) + + # Create table + try: + table = catalog.create_table( + identifier=table_id, + schema=schema, + ) + print(f" Created table: {table_id}") + except TableAlreadyExistsError: + print(f" ! Table already exists: {table_id}") + _ = catalog.load_table(table_id) + + # List tables + tables = catalog.list_tables(namespace) + if table_name in [t[1] for t in tables]: + print(" Table appears in list") + else: + print(f" Table not found in list: {tables}") + return False + + # Load table + try: + loaded_table = catalog.load_table(table_id) + print(f" Loaded table: {loaded_table.name()}") + print(f" Schema: {loaded_table.schema()}") + print(f" Location: {loaded_table.location()}") + except NoSuchTableError: + print(f" Failed to load table") + return False + + return True + + +def test_table_update(catalog, prefix): + """Test table update/commit operations.""" + print("Testing table update operations...") + namespace = (f"{prefix.replace('-', '_')}_test_ns",) + table_name = "test_table" + table_id = namespace + (table_name,) + + try: + table = catalog.load_table(table_id) + + # Update table properties + with table.transaction() as transaction: + transaction.set_properties({"test.property": "test.value"}) + + print(" Updated table properties") + + # Reload and verify + table = catalog.load_table(table_id) + if table.properties.get("test.property") == "test.value": + print(" Property update verified") + else: + print(" ! Property update failed or not persisted") + return False + + except Exception as e: + print(f" Table update failed: {e}") + return False + + return True + + +def test_cleanup(catalog, prefix): + """Test table and namespace deletion.""" + print("Testing cleanup operations...") + namespace = (f"{prefix.replace('-', '_')}_test_ns",) + table_id = namespace + ("test_table",) + + # Drop table + try: + catalog.drop_table(table_id) + print(f" Dropped table: {table_id}") + except NoSuchTableError: + print(f" ! Table already deleted: {table_id}") + + # Drop namespace + try: + catalog.drop_namespace(namespace) + print(f" Dropped namespace: {namespace}") + except NoSuchNamespaceError: + print(f" ! Namespace already deleted: {namespace}") + except Exception as e: + print(f" ? Namespace drop error (may be expected): {e}") + + return True + + +def main(): + parser = argparse.ArgumentParser(description="Test Iceberg REST Catalog compatibility") + parser.add_argument("--catalog-url", required=True, help="Iceberg REST Catalog URL (e.g., http://localhost:8182)") + parser.add_argument("--warehouse", default="s3://iceberg-test/", help="Warehouse location") + parser.add_argument("--prefix", required=True, help="Table bucket prefix") + parser.add_argument("--skip-cleanup", action="store_true", help="Skip cleanup at the end") + args = parser.parse_args() + + print(f"Connecting to Iceberg REST Catalog at: {args.catalog_url}") + print(f"Warehouse: {args.warehouse}") + print(f"Prefix: {args.prefix}") + print() + + # Load the REST catalog + catalog = load_catalog( + "rest", + **{ + "type": "rest", + "uri": args.catalog_url, + "warehouse": args.warehouse, + "prefix": args.prefix, + } + ) + + # Run tests + tests = [ + ("Config Endpoint", lambda: test_config_endpoint(catalog)), + ("Namespace Operations", lambda: test_namespace_operations(catalog, args.prefix)), + ("Table Operations", lambda: test_table_operations(catalog, args.prefix)), + ("Table Update", lambda: test_table_update(catalog, args.prefix)), + ] + + if not args.skip_cleanup: + tests.append(("Cleanup", lambda: test_cleanup(catalog, args.prefix))) + + passed = 0 + failed = 0 + + for name, test_fn in tests: + print(f"\n{'='*50}") + try: + if test_fn(): + passed += 1 + print(f"PASSED: {name}") + else: + failed += 1 + print(f"FAILED: {name}") + except Exception as e: + failed += 1 + print(f"ERROR in {name}: {e}") + + print(f"\n{'='*50}") + print(f"Results: {passed} passed, {failed} failed") + + return 0 if failed == 0 else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/test/s3tables/table-buckets/s3tables_integration_test.go b/test/s3tables/table-buckets/s3tables_integration_test.go index 660957391..26d7606a2 100644 --- a/test/s3tables/table-buckets/s3tables_integration_test.go +++ b/test/s3tables/table-buckets/s3tables_integration_test.go @@ -77,13 +77,13 @@ func testTableBucketLifecycle(t *testing.T, client *S3TablesClient) { createResp, err := client.CreateTableBucket(bucketName, nil) require.NoError(t, err, "Failed to create table bucket") assert.Contains(t, createResp.ARN, bucketName) - t.Logf("✓ Created table bucket: %s", createResp.ARN) + t.Logf("Created table bucket: %s", createResp.ARN) // Get table bucket getResp, err := client.GetTableBucket(createResp.ARN) require.NoError(t, err, "Failed to get table bucket") assert.Equal(t, bucketName, getResp.Name) - t.Logf("✓ Got table bucket: %s", getResp.Name) + t.Logf("Got table bucket: %s", getResp.Name) // List table buckets listResp, err := client.ListTableBuckets("", "", 0) @@ -96,12 +96,12 @@ func testTableBucketLifecycle(t *testing.T, client *S3TablesClient) { } } assert.True(t, found, "Created bucket should appear in list") - t.Logf("✓ Listed table buckets, found %d buckets", len(listResp.TableBuckets)) + t.Logf("Listed table buckets, found %d buckets", len(listResp.TableBuckets)) // Delete table bucket err = client.DeleteTableBucket(createResp.ARN) require.NoError(t, err, "Failed to delete table bucket") - t.Logf("✓ Deleted table bucket: %s", bucketName) + t.Logf("Deleted table bucket: %s", bucketName) // Verify bucket is deleted _, err = client.GetTableBucket(createResp.ARN) @@ -123,13 +123,13 @@ func testNamespaceLifecycle(t *testing.T, client *S3TablesClient) { createNsResp, err := client.CreateNamespace(bucketARN, []string{namespaceName}) require.NoError(t, err, "Failed to create namespace") assert.Equal(t, []string{namespaceName}, createNsResp.Namespace) - t.Logf("✓ Created namespace: %s", namespaceName) + t.Logf("Created namespace: %s", namespaceName) // Get namespace getNsResp, err := client.GetNamespace(bucketARN, []string{namespaceName}) require.NoError(t, err, "Failed to get namespace") assert.Equal(t, []string{namespaceName}, getNsResp.Namespace) - t.Logf("✓ Got namespace: %v", getNsResp.Namespace) + t.Logf("Got namespace: %v", getNsResp.Namespace) // List namespaces listNsResp, err := client.ListNamespaces(bucketARN, "", "", 0) @@ -142,12 +142,12 @@ func testNamespaceLifecycle(t *testing.T, client *S3TablesClient) { } } assert.True(t, found, "Created namespace should appear in list") - t.Logf("✓ Listed namespaces, found %d namespaces", len(listNsResp.Namespaces)) + t.Logf("Listed namespaces, found %d namespaces", len(listNsResp.Namespaces)) // Delete namespace err = client.DeleteNamespace(bucketARN, []string{namespaceName}) require.NoError(t, err, "Failed to delete namespace") - t.Logf("✓ Deleted namespace: %s", namespaceName) + t.Logf("Deleted namespace: %s", namespaceName) // Verify namespace is deleted _, err = client.GetNamespace(bucketARN, []string{namespaceName}) @@ -188,14 +188,14 @@ func testTableLifecycle(t *testing.T, client *S3TablesClient) { require.NoError(t, err, "Failed to create table") assert.NotEmpty(t, createTableResp.TableARN) assert.NotEmpty(t, createTableResp.VersionToken) - t.Logf("✓ Created table: %s (version: %s)", createTableResp.TableARN, createTableResp.VersionToken) + t.Logf("Created table: %s (version: %s)", createTableResp.TableARN, createTableResp.VersionToken) // Get table getTableResp, err := client.GetTable(bucketARN, []string{namespaceName}, tableName) require.NoError(t, err, "Failed to get table") assert.Equal(t, tableName, getTableResp.Name) assert.Equal(t, "ICEBERG", getTableResp.Format) - t.Logf("✓ Got table: %s (format: %s)", getTableResp.Name, getTableResp.Format) + t.Logf("Got table: %s (format: %s)", getTableResp.Name, getTableResp.Format) // List tables listTablesResp, err := client.ListTables(bucketARN, []string{namespaceName}, "", "", 0) @@ -208,12 +208,12 @@ func testTableLifecycle(t *testing.T, client *S3TablesClient) { } } assert.True(t, found, "Created table should appear in list") - t.Logf("✓ Listed tables, found %d tables", len(listTablesResp.Tables)) + t.Logf("Listed tables, found %d tables", len(listTablesResp.Tables)) // Delete table err = client.DeleteTable(bucketARN, []string{namespaceName}, tableName) require.NoError(t, err, "Failed to delete table") - t.Logf("✓ Deleted table: %s", tableName) + t.Logf("Deleted table: %s", tableName) // Verify table is deleted _, err = client.GetTable(bucketARN, []string{namespaceName}, tableName) @@ -234,18 +234,18 @@ func testTableBucketPolicy(t *testing.T, client *S3TablesClient) { policy := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":"*","Action":"s3tables:*","Resource":"*"}]}` err = client.PutTableBucketPolicy(bucketARN, policy) require.NoError(t, err, "Failed to put table bucket policy") - t.Logf("✓ Put table bucket policy") + t.Logf("Put table bucket policy") // Get bucket policy getPolicyResp, err := client.GetTableBucketPolicy(bucketARN) require.NoError(t, err, "Failed to get table bucket policy") assert.Equal(t, policy, getPolicyResp.ResourcePolicy) - t.Logf("✓ Got table bucket policy") + t.Logf("Got table bucket policy") // Delete bucket policy err = client.DeleteTableBucketPolicy(bucketARN) require.NoError(t, err, "Failed to delete table bucket policy") - t.Logf("✓ Deleted table bucket policy") + t.Logf("Deleted table bucket policy") // Verify policy is deleted _, err = client.GetTableBucketPolicy(bucketARN) @@ -285,34 +285,34 @@ func testTablePolicy(t *testing.T, client *S3TablesClient) { require.NoError(t, err, "Failed to create table") defer client.DeleteTable(bucketARN, []string{namespaceName}, tableName) - t.Logf("✓ Created table: %s", createTableResp.TableARN) + t.Logf("Created table: %s", createTableResp.TableARN) // Verify no policy exists initially _, err = client.GetTablePolicy(bucketARN, []string{namespaceName}, tableName) assert.Error(t, err, "Policy should not exist initially") - t.Logf("✓ Verified no policy exists initially") + t.Logf("Verified no policy exists initially") // Put table policy policy := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":"*","Action":"s3tables:*","Resource":"*"}]}` err = client.PutTablePolicy(bucketARN, []string{namespaceName}, tableName, policy) require.NoError(t, err, "Failed to put table policy") - t.Logf("✓ Put table policy") + t.Logf("Put table policy") // Get table policy getPolicyResp, err := client.GetTablePolicy(bucketARN, []string{namespaceName}, tableName) require.NoError(t, err, "Failed to get table policy") assert.Equal(t, policy, getPolicyResp.ResourcePolicy) - t.Logf("✓ Got table policy") + t.Logf("Got table policy") // Delete table policy err = client.DeleteTablePolicy(bucketARN, []string{namespaceName}, tableName) require.NoError(t, err, "Failed to delete table policy") - t.Logf("✓ Deleted table policy") + t.Logf("Deleted table policy") // Verify policy is deleted _, err = client.GetTablePolicy(bucketARN, []string{namespaceName}, tableName) assert.Error(t, err, "Policy should not exist after deletion") - t.Logf("✓ Verified policy deletion") + t.Logf("Verified policy deletion") } func testTagging(t *testing.T, client *S3TablesClient) { @@ -330,25 +330,25 @@ func testTagging(t *testing.T, client *S3TablesClient) { listTagsResp, err := client.ListTagsForResource(bucketARN) require.NoError(t, err, "Failed to list tags") assert.Equal(t, "test", listTagsResp.Tags["Environment"]) - t.Logf("✓ Listed tags: %v", listTagsResp.Tags) + t.Logf("Listed tags: %v", listTagsResp.Tags) // Add more tags newTags := map[string]string{"Department": "Engineering"} err = client.TagResource(bucketARN, newTags) require.NoError(t, err, "Failed to tag resource") - t.Logf("✓ Added tags") + t.Logf("Added tags") // Verify tags listTagsResp, err = client.ListTagsForResource(bucketARN) require.NoError(t, err, "Failed to list tags") assert.Equal(t, "test", listTagsResp.Tags["Environment"]) assert.Equal(t, "Engineering", listTagsResp.Tags["Department"]) - t.Logf("✓ Verified tags: %v", listTagsResp.Tags) + t.Logf("Verified tags: %v", listTagsResp.Tags) // Remove a tag err = client.UntagResource(bucketARN, []string{"Environment"}) require.NoError(t, err, "Failed to untag resource") - t.Logf("✓ Removed tag") + t.Logf("Removed tag") // Verify tag is removed listTagsResp, err = client.ListTagsForResource(bucketARN) @@ -356,7 +356,7 @@ func testTagging(t *testing.T, client *S3TablesClient) { _, hasEnvironment := listTagsResp.Tags["Environment"] assert.False(t, hasEnvironment, "Environment tag should be removed") assert.Equal(t, "Engineering", listTagsResp.Tags["Department"]) - t.Logf("✓ Verified tag removal") + t.Logf("Verified tag removal") } func testTargetOperations(t *testing.T, client *S3TablesClient) { diff --git a/weed/s3api/iceberg/iceberg.go b/weed/s3api/iceberg/iceberg.go index 6fc1011da..7b80482d8 100644 --- a/weed/s3api/iceberg/iceberg.go +++ b/weed/s3api/iceberg/iceberg.go @@ -4,11 +4,16 @@ package iceberg import ( + "context" "encoding/json" "fmt" "net/http" + "os" "strings" + "time" + "github.com/apache/iceberg-go" + "github.com/apache/iceberg-go/table" "github.com/google/uuid" "github.com/gorilla/mux" "github.com/seaweedfs/seaweedfs/weed/glog" @@ -148,6 +153,86 @@ func (s *Server) Auth(handler http.HandlerFunc) http.HandlerFunc { } } +// saveMetadataFile saves the Iceberg metadata JSON file to the filer. +// It constructs the correct filler path from the S3 location components. +func (s *Server) saveMetadataFile(ctx context.Context, bucketName, namespace, tableName, metadataFileName string, content []byte) error { + // Construct filer path: /table-buckets////metadata/ + // Note: s3tables.TablesPath is "/table-buckets" + + // Create context with timeout for file operations + opCtx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + + return s.filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + // 1. Ensure table directory exists: /table-buckets///
+ tableDir := fmt.Sprintf("/table-buckets/%s/%s/%s", bucketName, namespace, tableName) + resp, err := client.CreateEntry(opCtx, &filer_pb.CreateEntryRequest{ + Directory: fmt.Sprintf("/table-buckets/%s/%s", bucketName, namespace), + Entry: &filer_pb.Entry{ + Name: tableName, + IsDirectory: true, + Attributes: &filer_pb.FuseAttributes{ + Mtime: time.Now().Unix(), + Crtime: time.Now().Unix(), + FileMode: uint32(0755 | os.ModeDir), + }, + }, + }) + if err != nil { + return fmt.Errorf("failed to create table directory: %w", err) + } + if resp.Error != "" && !strings.Contains(resp.Error, "exist") { + return fmt.Errorf("failed to create table directory: %s", resp.Error) + } + + // 2. Ensure metadata directory exists: /table-buckets///
/metadata + metadataDir := fmt.Sprintf("%s/metadata", tableDir) + resp, err = client.CreateEntry(opCtx, &filer_pb.CreateEntryRequest{ + Directory: tableDir, + Entry: &filer_pb.Entry{ + Name: "metadata", + IsDirectory: true, + Attributes: &filer_pb.FuseAttributes{ + Mtime: time.Now().Unix(), + Crtime: time.Now().Unix(), + FileMode: uint32(0755 | os.ModeDir), + }, + }, + }) + if err != nil { + return fmt.Errorf("failed to create metadata directory: %w", err) + } + if resp.Error != "" && !strings.Contains(resp.Error, "exist") { + return fmt.Errorf("failed to create metadata directory: %s", resp.Error) + } + + // 3. Write the file + resp, err = client.CreateEntry(opCtx, &filer_pb.CreateEntryRequest{ + Directory: metadataDir, + Entry: &filer_pb.Entry{ + Name: metadataFileName, + Attributes: &filer_pb.FuseAttributes{ + Mtime: time.Now().Unix(), + Crtime: time.Now().Unix(), + FileMode: uint32(0644), + FileSize: uint64(len(content)), + }, + Content: content, + Extended: map[string][]byte{ + "Mime-Type": []byte("application/json"), + }, + }, + }) + if err != nil { + return fmt.Errorf("failed to write metadata file context: %w", err) + } + if resp.Error != "" { + return fmt.Errorf("failed to write metadata file: %s", resp.Error) + } + return nil + }) +} + // parseNamespace parses the namespace from path parameter. // Iceberg uses unit separator (0x1F) for multi-level namespaces. // Note: mux already decodes URL-encoded path parameters, so we only split by unit separator. @@ -176,9 +261,12 @@ func writeJSON(w http.ResponseWriter, status int, v interface{}) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) if v != nil { - if err := json.NewEncoder(w).Encode(v); err != nil { + data, err := json.Marshal(v) + if err != nil { glog.Errorf("Iceberg: failed to encode response: %v", err) + return } + w.Write(data) } } @@ -303,9 +391,15 @@ func (s *Server) handleCreateNamespace(w http.ResponseWriter, r *http.Request) { return } + // Standardize property initialization for consistency with GetNamespace + props := req.Properties + if props == nil { + props = make(map[string]string) + } + result := CreateNamespaceResponse{ Namespace: req.Namespace, - Properties: req.Properties, + Properties: props, } writeJSON(w, http.StatusOK, result) } @@ -349,7 +443,7 @@ func (s *Server) handleGetNamespace(w http.ResponseWriter, r *http.Request) { result := GetNamespaceResponse{ Namespace: namespace, - Properties: map[string]string{}, + Properties: make(map[string]string), } writeJSON(w, http.StatusOK, result) } @@ -513,30 +607,51 @@ func (s *Server) handleCreateTable(w http.ResponseWriter, r *http.Request) { bucketARN := buildTableBucketARN(bucketName) // Generate UUID for the new table - tableUUID := uuid.New().String() + tableUUID := uuid.New() location := fmt.Sprintf("s3://%s/%s/%s", bucketName, encodeNamespace(namespace), req.Name) - metadata := TableMetadata{ - FormatVersion: 2, - TableUUID: tableUUID, - Location: location, + // Build proper Iceberg table metadata using iceberg-go types + metadata := newTableMetadata(tableUUID, location, req.Schema, req.PartitionSpec, req.WriteOrder, req.Properties) + if metadata == nil { + writeError(w, http.StatusInternalServerError, "InternalServerError", "Failed to build table metadata") + return } + // Serialize metadata to JSON + metadataBytes, err := json.Marshal(metadata) + if err != nil { + writeError(w, http.StatusInternalServerError, "InternalServerError", "Failed to serialize metadata: "+err.Error()) + return + } + + // 1. Save metadata file to filer + tableName := req.Name + metadataFileName := "v1.metadata.json" // Initial version is always 1 + if err := s.saveMetadataFile(r.Context(), bucketName, encodeNamespace(namespace), tableName, metadataFileName, metadataBytes); err != nil { + writeError(w, http.StatusInternalServerError, "InternalServerError", "Failed to save metadata file: "+err.Error()) + return + } + + metadataLocation := fmt.Sprintf("s3://%s/%s/%s/metadata/%s", bucketName, encodeNamespace(namespace), tableName, metadataFileName) + // Use S3 Tables manager to create table createReq := &s3tables.CreateTableRequest{ TableBucketARN: bucketARN, Namespace: namespace, - Name: req.Name, + Name: tableName, Format: "ICEBERG", Metadata: &s3tables.TableMetadata{ Iceberg: &s3tables.IcebergMetadata{ - TableUUID: tableUUID, + TableUUID: tableUUID.String(), }, + FullMetadata: metadataBytes, }, + MetadataLocation: metadataLocation, + MetadataVersion: 1, } var createResp s3tables.CreateTableResponse - err := s.filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + err = s.filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { mgrClient := s3tables.NewManagerClient(client) return s.tablesManager.Execute(r.Context(), mgrClient, "CreateTable", createReq, &createResp, "") }) @@ -551,10 +666,16 @@ func (s *Server) handleCreateTable(w http.ResponseWriter, r *http.Request) { return } + // Use returned location if available, otherwise fallback to local one + finalLocation := createResp.MetadataLocation + if finalLocation == "" { + finalLocation = metadataLocation + } + result := LoadTableResult{ - MetadataLocation: createResp.MetadataLocation, + MetadataLocation: finalLocation, Metadata: metadata, - Config: map[string]string{}, + Config: make(iceberg.Properties), } writeJSON(w, http.StatusOK, result) } @@ -598,27 +719,38 @@ func (s *Server) handleLoadTable(w http.ResponseWriter, r *http.Request) { return } - // Build table metadata + // Build table metadata using iceberg-go types location := fmt.Sprintf("s3://%s/%s/%s", bucketName, encodeNamespace(namespace), tableName) - tableUUID := "" - if getResp.Metadata != nil && getResp.Metadata.Iceberg != nil { - tableUUID = getResp.Metadata.Iceberg.TableUUID - } - // Fallback if UUID is not found (e.g. for tables created before UUID persistence) - if tableUUID == "" { - tableUUID = uuid.New().String() + tableUUID := uuid.Nil + if getResp.Metadata != nil && getResp.Metadata.Iceberg != nil && getResp.Metadata.Iceberg.TableUUID != "" { + if parsed, err := uuid.Parse(getResp.Metadata.Iceberg.TableUUID); err == nil { + tableUUID = parsed + } } - - metadata := TableMetadata{ - FormatVersion: 2, - TableUUID: tableUUID, - Location: location, + // Use Nil UUID if not found in storage (legacy table) + // Stability is guaranteed by not generating random UUIDs on read + + var metadata table.Metadata + if getResp.Metadata != nil && len(getResp.Metadata.FullMetadata) > 0 { + var err error + metadata, err = table.ParseMetadataBytes(getResp.Metadata.FullMetadata) + if err != nil { + glog.Warningf("Iceberg: Failed to parse persisted metadata for %s: %v", tableName, err) + // Attempt to reconstruct from IcebergMetadata if available, otherwise synthetic + // TODO: Extract schema/spec from getResp.Metadata.Iceberg if FullMetadata fails but partial info exists? + // For now, fallback to empty metadata + metadata = newTableMetadata(tableUUID, location, nil, nil, nil, nil) + } + } else { + // No full metadata, create synthetic + // TODO: If we had stored schema in IcebergMetadata, we would pass it here + metadata = newTableMetadata(tableUUID, location, nil, nil, nil, nil) } result := LoadTableResult{ MetadataLocation: getResp.MetadataLocation, Metadata: metadata, - Config: map[string]string{}, + Config: make(iceberg.Properties), } writeJSON(w, http.StatusOK, result) } @@ -701,11 +833,225 @@ func (s *Server) handleDropTable(w http.ResponseWriter, r *http.Request) { } // handleUpdateTable commits updates to a table. +// Implements the Iceberg REST Catalog commit protocol. func (s *Server) handleUpdateTable(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + namespace := parseNamespace(vars["namespace"]) + tableName := vars["table"] + + if len(namespace) == 0 || tableName == "" { + writeError(w, http.StatusBadRequest, "BadRequestException", "Namespace and table name are required") + return + } + bucketName := getBucketFromPrefix(r) if !s.checkAuth(w, r, s3_constants.ACTION_WRITE, bucketName) { return } - // Return 501 Not Implemented - writeError(w, http.StatusNotImplemented, "UnsupportedOperationException", "Table update/commit not implemented") + + // Parse the commit request + var req CommitTableRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + writeError(w, http.StatusBadRequest, "BadRequestException", "Invalid request body: "+err.Error()) + return + } + + bucketARN := buildTableBucketARN(bucketName) + + // First, load current table metadata + getReq := &s3tables.GetTableRequest{ + TableBucketARN: bucketARN, + Namespace: namespace, + Name: tableName, + } + var getResp s3tables.GetTableResponse + + err := s.filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + mgrClient := s3tables.NewManagerClient(client) + return s.tablesManager.Execute(r.Context(), mgrClient, "GetTable", getReq, &getResp, "") + }) + + if err != nil { + if strings.Contains(err.Error(), "not found") { + writeError(w, http.StatusNotFound, "NoSuchTableException", fmt.Sprintf("Table does not exist: %s", tableName)) + return + } + glog.V(1).Infof("Iceberg: CommitTable GetTable error: %v", err) + writeError(w, http.StatusInternalServerError, "InternalServerError", err.Error()) + return + } + + // Build the current metadata + location := fmt.Sprintf("s3://%s/%s/%s", bucketName, encodeNamespace(namespace), tableName) + tableUUID := uuid.Nil + if getResp.Metadata != nil && getResp.Metadata.Iceberg != nil && getResp.Metadata.Iceberg.TableUUID != "" { + if parsed, err := uuid.Parse(getResp.Metadata.Iceberg.TableUUID); err == nil { + tableUUID = parsed + } + } + if tableUUID == uuid.Nil { + tableUUID = uuid.New() + } + + var currentMetadata table.Metadata + if getResp.Metadata != nil && len(getResp.Metadata.FullMetadata) > 0 { + var err error + currentMetadata, err = table.ParseMetadataBytes(getResp.Metadata.FullMetadata) + if err != nil { + glog.Errorf("Iceberg: Failed to parse current metadata for %s: %v", tableName, err) + writeError(w, http.StatusInternalServerError, "InternalServerError", "Failed to parse current metadata") + return + } + } else { + // Fallback for tables without persisted full metadata (legacy or error state) + currentMetadata = newTableMetadata(tableUUID, location, nil, nil, nil, nil) + } + + if currentMetadata == nil { + writeError(w, http.StatusInternalServerError, "InternalServerError", "Failed to build current metadata") + return + } + + // Validate all requirements against current metadata + for _, requirement := range req.Requirements { + if err := requirement.Validate(currentMetadata); err != nil { + writeError(w, http.StatusConflict, "CommitFailedException", "Requirement failed: "+err.Error()) + return + } + } + + // Apply updates using MetadataBuilder + builder, err := table.MetadataBuilderFromBase(currentMetadata, getResp.MetadataLocation) + if err != nil { + writeError(w, http.StatusInternalServerError, "InternalServerError", "Failed to create metadata builder: "+err.Error()) + return + } + + for _, update := range req.Updates { + if err := update.Apply(builder); err != nil { + writeError(w, http.StatusBadRequest, "BadRequestException", "Failed to apply update: "+err.Error()) + return + } + } + + // Build the new metadata + newMetadata, err := builder.Build() + if err != nil { + writeError(w, http.StatusBadRequest, "BadRequestException", "Failed to build new metadata: "+err.Error()) + return + } + + // Determine next metadata version + metadataVersion := getResp.MetadataVersion + 1 + metadataFileName := fmt.Sprintf("v%d.metadata.json", metadataVersion) + newMetadataLocation := fmt.Sprintf("s3://%s/%s/%s/metadata/%s", + bucketName, encodeNamespace(namespace), tableName, metadataFileName) + + // Serialize metadata to JSON + metadataBytes, err := json.Marshal(newMetadata) + if err != nil { + writeError(w, http.StatusInternalServerError, "InternalServerError", "Failed to serialize metadata: "+err.Error()) + return + } + + // 1. Save metadata file to filer + if err := s.saveMetadataFile(r.Context(), bucketName, encodeNamespace(namespace), tableName, metadataFileName, metadataBytes); err != nil { + writeError(w, http.StatusInternalServerError, "InternalServerError", "Failed to save metadata file: "+err.Error()) + return + } + + // Persist the new metadata and update the table reference + updateReq := &s3tables.UpdateTableRequest{ + TableBucketARN: bucketARN, + Namespace: namespace, + Name: tableName, + VersionToken: getResp.VersionToken, + Metadata: &s3tables.TableMetadata{ + Iceberg: &s3tables.IcebergMetadata{ + TableUUID: tableUUID.String(), + }, + FullMetadata: metadataBytes, + }, + MetadataVersion: metadataVersion, + MetadataLocation: newMetadataLocation, + } + + err = s.filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + mgrClient := s3tables.NewManagerClient(client) + // 1. Write metadata file (this would normally be an S3 PutObject, + // but s3tables manager handles the metadata storage logic) + // For now, we assume s3tables.UpdateTable handles the reference update. + return s.tablesManager.Execute(r.Context(), mgrClient, "UpdateTable", updateReq, nil, "") + }) + + if err != nil { + glog.Errorf("Iceberg: CommitTable UpdateTable error: %v", err) + writeError(w, http.StatusInternalServerError, "InternalServerError", "Failed to commit table update: "+err.Error()) + return + } + + // Return the new metadata + result := CommitTableResponse{ + MetadataLocation: newMetadataLocation, + Metadata: newMetadata, + } + writeJSON(w, http.StatusOK, result) +} + +// loadTableResultJSON is used for JSON serialization of LoadTableResult. +// It wraps table.Metadata (which is an interface) for proper JSON output. +type loadTableResultJSON struct { + MetadataLocation string `json:"metadata-location,omitempty"` + Metadata table.Metadata `json:"metadata"` + Config iceberg.Properties `json:"config,omitempty"` +} + +// newTableMetadata creates a new table.Metadata object with the given parameters. +// Uses iceberg-go's MetadataBuilder pattern for proper spec compliance. +func newTableMetadata( + tableUUID uuid.UUID, + location string, + schema *iceberg.Schema, + partitionSpec *iceberg.PartitionSpec, + sortOrder *table.SortOrder, + props iceberg.Properties, +) table.Metadata { + // Add schema - use provided or create empty schema + var s *iceberg.Schema + if schema != nil { + s = schema + } else { + s = iceberg.NewSchema(0) + } + + // Add partition spec + var pSpec *iceberg.PartitionSpec + if partitionSpec != nil { + pSpec = partitionSpec + } else { + unpartitioned := iceberg.NewPartitionSpecID(0) + pSpec = &unpartitioned + } + + // Add sort order + var so table.SortOrder + if sortOrder != nil { + so = *sortOrder + } else { + so = table.UnsortedSortOrder + } + + // Create properties map if nil + if props == nil { + props = make(iceberg.Properties) + } + + // Create metadata directly using the constructor which ensures spec compliance for V2 + metadata, err := table.NewMetadataWithUUID(s, pSpec, so, location, props, tableUUID) + if err != nil { + glog.Errorf("Failed to create metadata: %v", err) + return nil + } + + return metadata } diff --git a/weed/s3api/iceberg/types.go b/weed/s3api/iceberg/types.go index 12119054a..d1083f0ea 100644 --- a/weed/s3api/iceberg/types.go +++ b/weed/s3api/iceberg/types.go @@ -1,6 +1,14 @@ // Package iceberg defines types for the Iceberg REST Catalog API. +// This package uses types from github.com/apache/iceberg-go for spec compliance. package iceberg +import ( + "encoding/json" + + "github.com/apache/iceberg-go" + "github.com/apache/iceberg-go/table" +) + // CatalogConfig is returned by GET /v1/config. type CatalogConfig struct { Defaults map[string]string `json:"defaults"` @@ -45,13 +53,13 @@ type CreateNamespaceRequest struct { // CreateNamespaceResponse is returned by POST /v1/namespaces. type CreateNamespaceResponse struct { Namespace Namespace `json:"namespace"` - Properties map[string]string `json:"properties,omitempty"` + Properties map[string]string `json:"properties"` } // GetNamespaceResponse is returned by GET /v1/namespaces/{namespace}. type GetNamespaceResponse struct { Namespace Namespace `json:"namespace"` - Properties map[string]string `json:"properties,omitempty"` + Properties map[string]string `json:"properties"` } // ListTablesResponse is returned by GET /v1/namespaces/{namespace}/tables. @@ -60,115 +68,88 @@ type ListTablesResponse struct { Identifiers []TableIdentifier `json:"identifiers"` } -// Schema represents an Iceberg table schema. -type Schema struct { - Type string `json:"type"` - SchemaID int `json:"schema-id"` - Fields []SchemaField `json:"fields,omitempty"` -} - -// SchemaField represents a field in a schema. -type SchemaField struct { - ID int `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Required bool `json:"required"` - Doc string `json:"doc,omitempty"` -} - -// PartitionSpec represents partition specification. -type PartitionSpec struct { - SpecID int `json:"spec-id"` - Fields []PartitionSpecField `json:"fields,omitempty"` -} - -// PartitionSpecField represents a partition field. -type PartitionSpecField struct { - FieldID int `json:"field-id"` - SourceID int `json:"source-id"` - Name string `json:"name"` - Transform string `json:"transform"` -} - -// SortOrder represents sort order specification. -type SortOrder struct { - OrderID int `json:"order-id"` - Fields []SortOrderField `json:"fields,omitempty"` -} - -// SortOrderField represents a sort field. -type SortOrderField struct { - SourceID int `json:"source-id"` - Transform string `json:"transform"` - Direction string `json:"direction"` - NullOrder string `json:"null-order"` -} - -// Snapshot represents an Iceberg table snapshot. -type Snapshot struct { - SnapshotID int64 `json:"snapshot-id"` - ParentSnapshotID *int64 `json:"parent-snapshot-id,omitempty"` - SequenceNumber int64 `json:"sequence-number,omitempty"` - TimestampMs int64 `json:"timestamp-ms"` - ManifestList string `json:"manifest-list,omitempty"` - Summary map[string]string `json:"summary,omitempty"` - SchemaID *int `json:"schema-id,omitempty"` -} - -// TableMetadata represents Iceberg table metadata. -type TableMetadata struct { - FormatVersion int `json:"format-version"` - TableUUID string `json:"table-uuid"` - Location string `json:"location,omitempty"` - LastUpdatedMs int64 `json:"last-updated-ms,omitempty"` - Properties map[string]string `json:"properties,omitempty"` - Schemas []Schema `json:"schemas,omitempty"` - CurrentSchemaID int `json:"current-schema-id,omitempty"` - LastColumnID int `json:"last-column-id,omitempty"` - PartitionSpecs []PartitionSpec `json:"partition-specs,omitempty"` - DefaultSpecID int `json:"default-spec-id,omitempty"` - LastPartitionID int `json:"last-partition-id,omitempty"` - SortOrders []SortOrder `json:"sort-orders,omitempty"` - DefaultSortOrderID int `json:"default-sort-order-id,omitempty"` - Snapshots []Snapshot `json:"snapshots,omitempty"` - CurrentSnapshotID *int64 `json:"current-snapshot-id,omitempty"` - LastSequenceNumber int64 `json:"last-sequence-number,omitempty"` -} - // CreateTableRequest is sent to POST /v1/namespaces/{namespace}/tables. +// Uses iceberg-go types for Schema, PartitionSpec, and SortOrder. type CreateTableRequest struct { - Name string `json:"name"` - Location string `json:"location,omitempty"` - Schema *Schema `json:"schema,omitempty"` - PartitionSpec *PartitionSpec `json:"partition-spec,omitempty"` - WriteOrder *SortOrder `json:"write-order,omitempty"` - StageCreate bool `json:"stage-create,omitempty"` - Properties map[string]string `json:"properties,omitempty"` + Name string `json:"name"` + Location string `json:"location,omitempty"` + Schema *iceberg.Schema `json:"schema,omitempty"` + PartitionSpec *iceberg.PartitionSpec `json:"partition-spec,omitempty"` + WriteOrder *table.SortOrder `json:"write-order,omitempty"` + StageCreate bool `json:"stage-create,omitempty"` + Properties iceberg.Properties `json:"properties,omitempty"` } -// LoadTableResult is returned by GET/POST table endpoints. type LoadTableResult struct { - MetadataLocation string `json:"metadata-location,omitempty"` - Metadata TableMetadata `json:"metadata"` - Config map[string]string `json:"config,omitempty"` + MetadataLocation string `json:"metadata-location,omitempty"` + Metadata table.Metadata `json:"metadata"` + Config iceberg.Properties `json:"config"` +} + +// loadTableResultAlias is used for custom JSON unmarshaling. +type loadTableResultAlias struct { + MetadataLocation string `json:"metadata-location,omitempty"` + RawMetadata json.RawMessage `json:"metadata"` + Config iceberg.Properties `json:"config,omitempty"` +} + +// UnmarshalJSON implements custom unmarshaling for LoadTableResult +// to properly parse table.Metadata using iceberg-go's parser. +func (r *LoadTableResult) UnmarshalJSON(data []byte) error { + var alias loadTableResultAlias + if err := json.Unmarshal(data, &alias); err != nil { + return err + } + + r.MetadataLocation = alias.MetadataLocation + r.Config = alias.Config + + if len(alias.RawMetadata) > 0 { + metadata, err := table.ParseMetadataBytes(alias.RawMetadata) + if err != nil { + return err + } + r.Metadata = metadata + } + + return nil } // CommitTableRequest is sent to POST /v1/namespaces/{namespace}/tables/{table}. type CommitTableRequest struct { Identifier *TableIdentifier `json:"identifier,omitempty"` - Requirements []TableRequirement `json:"requirements"` - Updates []TableUpdate `json:"updates"` + Requirements table.Requirements `json:"requirements"` + Updates table.Updates `json:"updates"` } -// TableRequirement represents a requirement for table commit. -type TableRequirement struct { - Type string `json:"type"` - Ref string `json:"ref,omitempty"` - SnapshotID *int64 `json:"snapshot-id,omitempty"` +// CommitTableResponse is returned by POST table commit operations. +type CommitTableResponse struct { + MetadataLocation string `json:"metadata-location"` + Metadata table.Metadata `json:"metadata"` } -// TableUpdate represents an update to table metadata. -type TableUpdate struct { - Action string `json:"action"` - // Additional fields depend on the action type +// commitTableResponseAlias is used for custom JSON unmarshaling. +type commitTableResponseAlias struct { + MetadataLocation string `json:"metadata-location"` + RawMetadata json.RawMessage `json:"metadata"` +} + +// UnmarshalJSON implements custom unmarshaling for CommitTableResponse. +func (r *CommitTableResponse) UnmarshalJSON(data []byte) error { + var alias commitTableResponseAlias + if err := json.Unmarshal(data, &alias); err != nil { + return err + } + + r.MetadataLocation = alias.MetadataLocation + + if len(alias.RawMetadata) > 0 { + metadata, err := table.ParseMetadataBytes(alias.RawMetadata) + if err != nil { + return err + } + r.Metadata = metadata + } + + return nil } diff --git a/weed/s3api/s3tables/handler.go b/weed/s3api/s3tables/handler.go index 4e74a2bf4..dae11d562 100644 --- a/weed/s3api/s3tables/handler.go +++ b/weed/s3api/s3tables/handler.go @@ -127,6 +127,8 @@ func (h *S3TablesHandler) HandleRequest(w http.ResponseWriter, r *http.Request, err = h.handleGetTable(w, r, filerClient) case "ListTables": err = h.handleListTables(w, r, filerClient) + case "UpdateTable": + err = h.handleUpdateTable(w, r, filerClient) case "DeleteTable": err = h.handleDeleteTable(w, r, filerClient) diff --git a/weed/s3api/s3tables/handler_bucket_create.go b/weed/s3api/s3tables/handler_bucket_create.go index 802532ecc..1e953f47c 100644 --- a/weed/s3api/s3tables/handler_bucket_create.go +++ b/weed/s3api/s3tables/handler_bucket_create.go @@ -33,7 +33,7 @@ func (h *S3TablesHandler) handleCreateTableBucket(w http.ResponseWriter, r *http return err } - bucketPath := getTableBucketPath(req.Name) + bucketPath := GetTableBucketPath(req.Name) // Check if bucket already exists and ensure no conflict with object store buckets tableBucketExists := false diff --git a/weed/s3api/s3tables/handler_bucket_get_list_delete.go b/weed/s3api/s3tables/handler_bucket_get_list_delete.go index 2591b39ff..7399c0ae3 100644 --- a/weed/s3api/s3tables/handler_bucket_get_list_delete.go +++ b/weed/s3api/s3tables/handler_bucket_get_list_delete.go @@ -32,7 +32,7 @@ func (h *S3TablesHandler) handleGetTableBucket(w http.ResponseWriter, r *http.Re return err } - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) var metadata tableBucketMetadata var bucketPolicy string @@ -177,7 +177,7 @@ func (h *S3TablesHandler) handleListTableBuckets(w http.ResponseWriter, r *http. continue } - bucketPath := getTableBucketPath(entry.Entry.Name) + bucketPath := GetTableBucketPath(entry.Entry.Name) bucketPolicy := "" policyData, err := h.getExtendedAttribute(r.Context(), client, bucketPath, ExtendedKeyPolicy) if err != nil { @@ -261,7 +261,7 @@ func (h *S3TablesHandler) handleDeleteTableBucket(w http.ResponseWriter, r *http return err } - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) // Check if bucket exists and perform ownership + emptiness check in one block var metadata tableBucketMetadata diff --git a/weed/s3api/s3tables/handler_namespace.go b/weed/s3api/s3tables/handler_namespace.go index a68f97a6c..a732edc6e 100644 --- a/weed/s3api/s3tables/handler_namespace.go +++ b/weed/s3api/s3tables/handler_namespace.go @@ -43,7 +43,7 @@ func (h *S3TablesHandler) handleCreateNamespace(w http.ResponseWriter, r *http.R } // Check if table bucket exists - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) var bucketMetadata tableBucketMetadata var bucketPolicy string var bucketTags map[string]string @@ -93,7 +93,7 @@ func (h *S3TablesHandler) handleCreateNamespace(w http.ResponseWriter, r *http.R return ErrAccessDenied } - namespacePath := getNamespacePath(bucketName, namespaceName) + namespacePath := GetNamespacePath(bucketName, namespaceName) // Check if namespace already exists err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { @@ -177,8 +177,8 @@ func (h *S3TablesHandler) handleGetNamespace(w http.ResponseWriter, r *http.Requ return err } - namespacePath := getNamespacePath(bucketName, namespaceName) - bucketPath := getTableBucketPath(bucketName) + namespacePath := GetNamespacePath(bucketName, namespaceName) + bucketPath := GetTableBucketPath(bucketName) // Get namespace and bucket policy var metadata namespaceMetadata @@ -264,7 +264,7 @@ func (h *S3TablesHandler) handleListNamespaces(w http.ResponseWriter, r *http.Re maxNamespaces = 100 } - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) // Check permission (check bucket ownership) var bucketMetadata tableBucketMetadata @@ -446,8 +446,8 @@ func (h *S3TablesHandler) handleDeleteNamespace(w http.ResponseWriter, r *http.R return err } - namespacePath := getNamespacePath(bucketName, namespaceName) - bucketPath := getTableBucketPath(bucketName) + namespacePath := GetNamespacePath(bucketName, namespaceName) + bucketPath := GetTableBucketPath(bucketName) // Check if namespace exists and get metadata for permission check var metadata namespaceMetadata diff --git a/weed/s3api/s3tables/handler_policy.go b/weed/s3api/s3tables/handler_policy.go index f7fbed749..68aa22582 100644 --- a/weed/s3api/s3tables/handler_policy.go +++ b/weed/s3api/s3tables/handler_policy.go @@ -66,7 +66,7 @@ func (h *S3TablesHandler) handlePutTableBucketPolicy(w http.ResponseWriter, r *h } // Check if bucket exists and get metadata for ownership check - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) var bucketMetadata tableBucketMetadata err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { data, err := h.getExtendedAttribute(r.Context(), client, bucketPath, ExtendedKeyMetadata) @@ -133,7 +133,7 @@ func (h *S3TablesHandler) handleGetTableBucketPolicy(w http.ResponseWriter, r *h return err } - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) var policy []byte var bucketMetadata tableBucketMetadata @@ -204,7 +204,7 @@ func (h *S3TablesHandler) handleDeleteTableBucketPolicy(w http.ResponseWriter, r return err } - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) // Check if bucket exists and get metadata for ownership check var bucketMetadata tableBucketMetadata @@ -301,8 +301,8 @@ func (h *S3TablesHandler) handlePutTablePolicy(w http.ResponseWriter, r *http.Re h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error()) return err } - tablePath := getTablePath(bucketName, namespaceName, tableName) - bucketPath := getTableBucketPath(bucketName) + tablePath := GetTablePath(bucketName, namespaceName, tableName) + bucketPath := GetTableBucketPath(bucketName) var metadata tableMetadataInternal var bucketPolicy string @@ -396,8 +396,8 @@ func (h *S3TablesHandler) handleGetTablePolicy(w http.ResponseWriter, r *http.Re h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error()) return err } - tablePath := getTablePath(bucketName, namespaceName, tableName) - bucketPath := getTableBucketPath(bucketName) + tablePath := GetTablePath(bucketName, namespaceName, tableName) + bucketPath := GetTableBucketPath(bucketName) var policy []byte var metadata tableMetadataInternal var bucketPolicy string @@ -497,8 +497,8 @@ func (h *S3TablesHandler) handleDeleteTablePolicy(w http.ResponseWriter, r *http h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error()) return err } - tablePath := getTablePath(bucketName, namespaceName, tableName) - bucketPath := getTableBucketPath(bucketName) + tablePath := GetTablePath(bucketName, namespaceName, tableName) + bucketPath := GetTableBucketPath(bucketName) // Check if table exists var metadata tableMetadataInternal @@ -604,7 +604,7 @@ func (h *S3TablesHandler) handleTagResource(w http.ResponseWriter, r *http.Reque // Fetch bucket policy if we have a bucket name if bucketName != "" { - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) policyData, err := h.getExtendedAttribute(r.Context(), client, bucketPath, ExtendedKeyPolicy) if err != nil { if !errors.Is(err, ErrAttributeNotFound) { @@ -722,7 +722,7 @@ func (h *S3TablesHandler) handleListTagsForResource(w http.ResponseWriter, r *ht // Fetch bucket policy if we have a bucket name if bucketName != "" { - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) policyData, err := h.getExtendedAttribute(r.Context(), client, bucketPath, ExtendedKeyPolicy) if err != nil { if !errors.Is(err, ErrAttributeNotFound) { @@ -828,7 +828,7 @@ func (h *S3TablesHandler) handleUntagResource(w http.ResponseWriter, r *http.Req // Fetch bucket policy if we have a bucket name if bucketName != "" { - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) policyData, err := h.getExtendedAttribute(r.Context(), client, bucketPath, ExtendedKeyPolicy) if err != nil { if !errors.Is(err, ErrAttributeNotFound) { @@ -914,13 +914,13 @@ func (h *S3TablesHandler) resolveResourcePath(resourceARN string) (path string, // Try parsing as table ARN first bucketName, namespace, tableName, err := parseTableFromARN(resourceARN) if err == nil { - return getTablePath(bucketName, namespace, tableName), ExtendedKeyTags, ResourceTypeTable, nil + return GetTablePath(bucketName, namespace, tableName), ExtendedKeyTags, ResourceTypeTable, nil } // Try parsing as bucket ARN bucketName, err = parseBucketNameFromARN(resourceARN) if err == nil { - return getTableBucketPath(bucketName), ExtendedKeyTags, ResourceTypeBucket, nil + return GetTableBucketPath(bucketName), ExtendedKeyTags, ResourceTypeBucket, nil } return "", "", "", fmt.Errorf("invalid resource ARN: %s", resourceARN) diff --git a/weed/s3api/s3tables/handler_table.go b/weed/s3api/s3tables/handler_table.go index b1c4f4c74..10e0df530 100644 --- a/weed/s3api/s3tables/handler_table.go +++ b/weed/s3api/s3tables/handler_table.go @@ -63,7 +63,7 @@ func (h *S3TablesHandler) handleCreateTable(w http.ResponseWriter, r *http.Reque } // Check if namespace exists - namespacePath := getNamespacePath(bucketName, namespaceName) + namespacePath := GetNamespacePath(bucketName, namespaceName) var namespaceMetadata namespaceMetadata err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { data, err := h.getExtendedAttribute(r.Context(), client, namespacePath, ExtendedKeyMetadata) @@ -87,7 +87,7 @@ func (h *S3TablesHandler) handleCreateTable(w http.ResponseWriter, r *http.Reque // Authorize table creation using policy framework (namespace + bucket policies) accountID := h.getAccountID(r) - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) namespacePolicy := "" bucketPolicy := "" bucketTags := map[string]string{} @@ -160,7 +160,7 @@ func (h *S3TablesHandler) handleCreateTable(w http.ResponseWriter, r *http.Reque return ErrAccessDenied } - tablePath := getTablePath(bucketName, namespaceName, tableName) + tablePath := GetTablePath(bucketName, namespaceName, tableName) // Check if table already exists err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { @@ -171,7 +171,7 @@ func (h *S3TablesHandler) handleCreateTable(w http.ResponseWriter, r *http.Reque if err == nil { h.writeError(w, http.StatusConflict, ErrCodeTableAlreadyExists, fmt.Sprintf("table %s already exists", tableName)) return fmt.Errorf("table already exists") - } else if !errors.Is(err, filer_pb.ErrNotFound) { + } else if !errors.Is(err, filer_pb.ErrNotFound) && !errors.Is(err, ErrAttributeNotFound) { h.writeError(w, http.StatusInternalServerError, ErrCodeInternalError, fmt.Sprintf("failed to check table: %v", err)) return err } @@ -181,14 +181,16 @@ func (h *S3TablesHandler) handleCreateTable(w http.ResponseWriter, r *http.Reque versionToken := generateVersionToken() metadata := &tableMetadataInternal{ - Name: tableName, - Namespace: namespaceName, - Format: req.Format, - CreatedAt: now, - ModifiedAt: now, - OwnerAccountID: namespaceMetadata.OwnerAccountID, // Inherit namespace owner for consistency - VersionToken: versionToken, - Metadata: req.Metadata, + Name: tableName, + Namespace: namespaceName, + Format: req.Format, + CreatedAt: now, + ModifiedAt: now, + OwnerAccountID: namespaceMetadata.OwnerAccountID, // Inherit namespace owner for consistency + VersionToken: versionToken, + MetadataVersion: max(req.MetadataVersion, 1), + MetadataLocation: req.MetadataLocation, + Metadata: req.Metadata, } metadataBytes, err := json.Marshal(metadata) @@ -284,7 +286,7 @@ func (h *S3TablesHandler) handleGetTable(w http.ResponseWriter, r *http.Request, return fmt.Errorf("missing required parameters") } - tablePath := getTablePath(bucketName, namespace, tableName) + tablePath := GetTablePath(bucketName, namespace, tableName) var metadata tableMetadataInternal err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { @@ -309,7 +311,7 @@ func (h *S3TablesHandler) handleGetTable(w http.ResponseWriter, r *http.Request, // Authorize access to the table using policy framework accountID := h.getAccountID(r) - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) tablePolicy := "" bucketPolicy := "" bucketTags := map[string]string{} @@ -395,6 +397,7 @@ func (h *S3TablesHandler) handleGetTable(w http.ResponseWriter, r *http.Request, ModifiedAt: metadata.ModifiedAt, OwnerAccountID: metadata.OwnerAccountID, MetadataLocation: metadata.MetadataLocation, + MetadataVersion: metadata.MetadataVersion, VersionToken: metadata.VersionToken, Metadata: metadata.Metadata, } @@ -454,8 +457,8 @@ func (h *S3TablesHandler) handleListTables(w http.ResponseWriter, r *http.Reques if len(req.Namespace) > 0 { // Namespace has already been validated above - namespacePath := getNamespacePath(bucketName, namespaceName) - bucketPath := getTableBucketPath(bucketName) + namespacePath := GetNamespacePath(bucketName, namespaceName) + bucketPath := GetTableBucketPath(bucketName) var nsMeta namespaceMetadata var bucketMeta tableBucketMetadata var namespacePolicy, bucketPolicy string @@ -521,7 +524,7 @@ func (h *S3TablesHandler) handleListTables(w http.ResponseWriter, r *http.Reques tables, paginationToken, err = h.listTablesInNamespaceWithClient(r, client, bucketName, namespaceName, req.Prefix, req.ContinuationToken, maxTables) } else { // List tables across all namespaces in bucket - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) var bucketMeta tableBucketMetadata var bucketPolicy string bucketTags := map[string]string{} @@ -588,7 +591,7 @@ func (h *S3TablesHandler) handleListTables(w http.ResponseWriter, r *http.Reques // listTablesInNamespaceWithClient lists tables in a specific namespace func (h *S3TablesHandler) listTablesInNamespaceWithClient(r *http.Request, client filer_pb.SeaweedFilerClient, bucketName, namespaceName, prefix, continuationToken string, maxTables int) ([]TableSummary, string, error) { - namespacePath := getNamespacePath(bucketName, namespaceName) + namespacePath := GetNamespacePath(bucketName, namespaceName) return h.listTablesWithClient(r, client, namespacePath, bucketName, namespaceName, prefix, continuationToken, maxTables) } @@ -685,7 +688,7 @@ func (h *S3TablesHandler) listTablesWithClient(r *http.Request, client filer_pb. } func (h *S3TablesHandler) listTablesInAllNamespaces(r *http.Request, client filer_pb.SeaweedFilerClient, bucketName, prefix, continuationToken string, maxTables int) ([]TableSummary, string, error) { - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) ctx := r.Context() var continuationNamespace string @@ -801,7 +804,7 @@ func (h *S3TablesHandler) handleDeleteTable(w http.ResponseWriter, r *http.Reque return err } - tablePath := getTablePath(bucketName, namespaceName, tableName) + tablePath := GetTablePath(bucketName, namespaceName, tableName) // Check if table exists and enforce VersionToken if provided var metadata tableMetadataInternal @@ -843,7 +846,7 @@ func (h *S3TablesHandler) handleDeleteTable(w http.ResponseWriter, r *http.Reque return err } - bucketPath := getTableBucketPath(bucketName) + bucketPath := GetTableBucketPath(bucketName) data, err = h.getExtendedAttribute(r.Context(), client, bucketPath, ExtendedKeyMetadata) if err == nil { if err := json.Unmarshal(data, &bucketMetadata); err != nil { @@ -917,3 +920,184 @@ func (h *S3TablesHandler) handleDeleteTable(w http.ResponseWriter, r *http.Reque h.writeJSON(w, http.StatusOK, nil) return nil } + +// handleUpdateTable updates table metadata +func (h *S3TablesHandler) handleUpdateTable(w http.ResponseWriter, r *http.Request, filerClient FilerClient) error { + var req UpdateTableRequest + if err := h.readRequestBody(r, &req); err != nil { + h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error()) + return err + } + + if req.TableBucketARN == "" || len(req.Namespace) == 0 || req.Name == "" { + h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, "tableBucketARN, namespace, and name are required") + return fmt.Errorf("missing required parameters") + } + + namespaceName, err := validateNamespace(req.Namespace) + if err != nil { + h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error()) + return err + } + + bucketName, err := parseBucketNameFromARN(req.TableBucketARN) + if err != nil { + h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error()) + return err + } + + tableName, err := validateTableName(req.Name) + if err != nil { + h.writeError(w, http.StatusBadRequest, ErrCodeInvalidRequest, err.Error()) + return err + } + + tablePath := GetTablePath(bucketName, namespaceName, tableName) + + // Load existing metadata and policies for authorization + var metadata tableMetadataInternal + var tablePolicy string + var bucketPolicy string + var bucketTags map[string]string + var tableTags map[string]string + var bucketMetadata tableBucketMetadata + + err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + // 1. Get Table Metadata + data, err := h.getExtendedAttribute(r.Context(), client, tablePath, ExtendedKeyMetadata) + if err != nil { + return err + } + if err := json.Unmarshal(data, &metadata); err != nil { + return fmt.Errorf("failed to unmarshal table metadata: %w", err) + } + + // 2. Get Table Policy & Tags + policyData, err := h.getExtendedAttribute(r.Context(), client, tablePath, ExtendedKeyPolicy) + if err == nil { + tablePolicy = string(policyData) + } else if !errors.Is(err, ErrAttributeNotFound) { + return fmt.Errorf("failed to fetch table policy: %w", err) + } + tableTags, err = h.readTags(r.Context(), client, tablePath) + if err != nil { + return err + } + + // 3. Get Bucket Metadata, Policy & Tags + bucketPath := GetTableBucketPath(bucketName) + data, err = h.getExtendedAttribute(r.Context(), client, bucketPath, ExtendedKeyMetadata) + if err == nil { + if err := json.Unmarshal(data, &bucketMetadata); err != nil { + return fmt.Errorf("failed to unmarshal bucket metadata: %w", err) + } + } else if !errors.Is(err, ErrAttributeNotFound) { + return fmt.Errorf("failed to fetch bucket metadata: %w", err) + } + policyData, err = h.getExtendedAttribute(r.Context(), client, bucketPath, ExtendedKeyPolicy) + if err == nil { + bucketPolicy = string(policyData) + } else if !errors.Is(err, ErrAttributeNotFound) { + return fmt.Errorf("failed to fetch bucket policy: %w", err) + } + bucketTags, err = h.readTags(r.Context(), client, bucketPath) + if err != nil { + return err + } + + return nil + }) + + if err != nil { + if errors.Is(err, filer_pb.ErrNotFound) { + h.writeError(w, http.StatusNotFound, ErrCodeNoSuchTable, "table not found") + } else { + h.writeError(w, http.StatusInternalServerError, ErrCodeInternalError, err.Error()) + } + return err + } + + // Authorization Check + tableARN := h.generateTableARN(metadata.OwnerAccountID, bucketName, namespaceName+"/"+tableName) + bucketARN := h.generateTableBucketARN(bucketMetadata.OwnerAccountID, bucketName) + principal := h.getAccountID(r) + identityActions := getIdentityActions(r) + + tableAllowed := CheckPermissionWithContext("UpdateTable", principal, metadata.OwnerAccountID, tablePolicy, tableARN, &PolicyContext{ + TableBucketName: bucketName, + Namespace: namespaceName, + TableName: tableName, + TableBucketTags: bucketTags, + ResourceTags: tableTags, + IdentityActions: identityActions, + }) + bucketAllowed := CheckPermissionWithContext("UpdateTable", principal, bucketMetadata.OwnerAccountID, bucketPolicy, bucketARN, &PolicyContext{ + TableBucketName: bucketName, + Namespace: namespaceName, + TableName: tableName, + TableBucketTags: bucketTags, + ResourceTags: tableTags, + IdentityActions: identityActions, + }) + + if !tableAllowed && !bucketAllowed { + h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to update table") + return NewAuthError("UpdateTable", principal, "not authorized to update table") + } + + // Check version token if provided + if req.VersionToken != "" && req.VersionToken != metadata.VersionToken { + h.writeError(w, http.StatusConflict, ErrCodeConflict, "Version token mismatch") + return ErrVersionTokenMismatch + } + + // Update metadata + if req.Metadata != nil { + if metadata.Metadata == nil { + metadata.Metadata = &TableMetadata{} + } + if req.Metadata.Iceberg != nil { + if metadata.Metadata.Iceberg == nil { + metadata.Metadata.Iceberg = &IcebergMetadata{} + } + if req.Metadata.Iceberg.TableUUID != "" { + metadata.Metadata.Iceberg.TableUUID = req.Metadata.Iceberg.TableUUID + } + } + if len(req.Metadata.FullMetadata) > 0 { + metadata.Metadata.FullMetadata = req.Metadata.FullMetadata + } + } + if req.MetadataLocation != "" { + metadata.MetadataLocation = req.MetadataLocation + } + if req.MetadataVersion > 0 { + metadata.MetadataVersion = req.MetadataVersion + } else if metadata.MetadataVersion == 0 { + metadata.MetadataVersion = 1 + } + metadata.ModifiedAt = time.Now() + metadata.VersionToken = generateVersionToken() + + metadataBytes, err := json.Marshal(metadata) + if err != nil { + h.writeError(w, http.StatusInternalServerError, ErrCodeInternalError, "failed to marshal metadata") + return err + } + + err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + return h.setExtendedAttribute(r.Context(), client, tablePath, ExtendedKeyMetadata, metadataBytes) + }) + + if err != nil { + h.writeError(w, http.StatusInternalServerError, ErrCodeInternalError, "failed to update metadata") + return err + } + + h.writeJSON(w, http.StatusOK, &UpdateTableResponse{ + TableARN: tableARN, + MetadataLocation: metadata.MetadataLocation, + VersionToken: metadata.VersionToken, + }) + return nil +} diff --git a/weed/s3api/s3tables/iceberg_layout.go b/weed/s3api/s3tables/iceberg_layout.go index 6473c8a60..0385bc7f2 100644 --- a/weed/s3api/s3tables/iceberg_layout.go +++ b/weed/s3api/s3tables/iceberg_layout.go @@ -358,7 +358,7 @@ func (v *TableBucketFileValidator) ValidateTableBucketUploadWithClient( } // Verify the table exists and has ICEBERG format by checking its metadata - tablePath := getTablePath(bucket, namespace, table) + tablePath := GetTablePath(bucket, namespace, table) dir, name := splitPath(tablePath) resp, err := filer_pb.LookupEntry(ctx, client, &filer_pb.LookupDirectoryEntryRequest{ diff --git a/weed/s3api/s3tables/types.go b/weed/s3api/s3tables/types.go index 627bb4c74..55c0f5f19 100644 --- a/weed/s3api/s3tables/types.go +++ b/weed/s3api/s3tables/types.go @@ -1,6 +1,9 @@ package s3tables -import "time" +import ( + "encoding/json" + "time" +) // Table bucket types @@ -140,7 +143,8 @@ type IcebergMetadata struct { } type TableMetadata struct { - Iceberg *IcebergMetadata `json:"iceberg,omitempty"` + Iceberg *IcebergMetadata `json:"iceberg,omitempty"` + FullMetadata json.RawMessage `json:"fullMetadata,omitempty"` } type Table struct { @@ -156,12 +160,14 @@ type Table struct { } type CreateTableRequest struct { - TableBucketARN string `json:"tableBucketARN"` - Namespace []string `json:"namespace"` - Name string `json:"name"` - Format string `json:"format"` - Metadata *TableMetadata `json:"metadata,omitempty"` - Tags map[string]string `json:"tags,omitempty"` + TableBucketARN string `json:"tableBucketARN"` + Namespace []string `json:"namespace"` + Name string `json:"name"` + Format string `json:"format"` + Metadata *TableMetadata `json:"metadata,omitempty"` + MetadataVersion int `json:"metadataVersion,omitempty"` + MetadataLocation string `json:"metadataLocation,omitempty"` + Tags map[string]string `json:"tags,omitempty"` } type CreateTableResponse struct { @@ -187,6 +193,7 @@ type GetTableResponse struct { OwnerAccountID string `json:"ownerAccountId"` MetadataLocation string `json:"metadataLocation,omitempty"` VersionToken string `json:"versionToken"` + MetadataVersion int `json:"metadataVersion"` Metadata *TableMetadata `json:"metadata,omitempty"` } @@ -219,6 +226,22 @@ type DeleteTableRequest struct { VersionToken string `json:"versionToken,omitempty"` } +type UpdateTableRequest struct { + TableBucketARN string `json:"tableBucketARN"` + Namespace []string `json:"namespace"` + Name string `json:"name"` + VersionToken string `json:"versionToken,omitempty"` + Metadata *TableMetadata `json:"metadata,omitempty"` + MetadataVersion int `json:"metadataVersion,omitempty"` + MetadataLocation string `json:"metadataLocation,omitempty"` +} + +type UpdateTableResponse struct { + TableARN string `json:"tableARN"` + VersionToken string `json:"versionToken"` + MetadataLocation string `json:"metadataLocation,omitempty"` +} + // Table policy types type PutTablePolicyRequest struct { diff --git a/weed/s3api/s3tables/utils.go b/weed/s3api/s3tables/utils.go index 93965b915..0fd78f0c1 100644 --- a/weed/s3api/s3tables/utils.go +++ b/weed/s3api/s3tables/utils.go @@ -79,18 +79,18 @@ func parseTableFromARN(arn string) (bucketName, namespace, tableName string, err // Path helpers -// getTableBucketPath returns the filer path for a table bucket -func getTableBucketPath(bucketName string) string { +// GetTableBucketPath returns the filer path for a table bucket +func GetTableBucketPath(bucketName string) string { return path.Join(TablesPath, bucketName) } -// getNamespacePath returns the filer path for a namespace -func getNamespacePath(bucketName, namespace string) string { +// GetNamespacePath returns the filer path for a namespace +func GetNamespacePath(bucketName, namespace string) string { return path.Join(TablesPath, bucketName, namespace) } -// getTablePath returns the filer path for a table -func getTablePath(bucketName, namespace, tableName string) string { +// GetTablePath returns the filer path for a table +func GetTablePath(bucketName, namespace, tableName string) string { return path.Join(TablesPath, bucketName, namespace, tableName) } @@ -118,6 +118,7 @@ type tableMetadataInternal struct { ModifiedAt time.Time `json:"modifiedAt"` OwnerAccountID string `json:"ownerAccountId"` VersionToken string `json:"versionToken"` + MetadataVersion int `json:"metadataVersion"` MetadataLocation string `json:"metadataLocation,omitempty"` Metadata *TableMetadata `json:"metadata,omitempty"` }