feat: add S3-compatible storage backend - #4716
Conversation
|
I tried it manually with a local minio: I've:
We need to test the :
|
Enregistrement.de.l.ecran.2026-03-30.a.18.51.51.mov |
81d2b27 to
1dacd7c
Compare
| @@ -0,0 +1,53 @@ | |||
| package vfss3 | |||
There was a problem hiding this comment.
and a little bit confusing to havein s3.go inly provate delete functions
There was a problem hiding this comment.
or, maybe I understood, there is duplication with appfs, the same functions.
Does it make sense then to move them to pkg/s3 or pkg/s3util package with
func EnsureBucket(ctx, client, bucket, region) error
func DeleteObjects(ctx, client, bucket, names) error
func WrapNotFound(err) error
There was a problem hiding this comment.
| domain string | ||
| prefix string // DBPrefix — used as key prefix in the bucket | ||
| contextName string | ||
| ctx context.Context |
There was a problem hiding this comment.
I see that we have context in all other vfs, but it's an execution context, not data. Let's do not spread anti-pattern and if not change interface, but at least just pass the context.Background as is
There was a problem hiding this comment.
I understand the concern — storing a context in a struct is generally considered an anti-pattern in Go. However, the VFS interface (model/vfs/vfs.go) does not pass a context.Context in its method signatures, so all backends that need one store it as a field. The Swift implementation (vfsswift/impl_v3.go) uses the exact same pattern: ctx: context.Background() set at creation time. Changing this would require updating the VFS interface, which feels out of scope for this PR - and I'm not confortable with that - . Happy to discuss if you think it's worth a broader refactor though.
| @@ -17,6 +17,7 @@ import ( | |||
|
|
|||
There was a problem hiding this comment.
Here, we don't test any error cases during upload. But we run all the PuObject in the background goroutine, so it would be gread to add test when miniio is down during upload to check that all errors are propagated to client
f1d60b9 to
e82461e
Compare
|
I need to retry every thing since the refacto / changes |
dad1319 to
e622bb4
Compare
Tests in this package call lifecycle.Create / instance.Get directly without going through testutils.NewSetup → GetTestInstance → stack.Start, so they never trigger couchdb.InitGlobalDB themselves. They've historically relied on the side effect of earlier model/* test packages bootstrapping the global DB and on the design doc persisting in the shared CouchDB service across test binaries. Go's test result cache (persisted via actions/setup-go cache) can let those packages be skipped, breaking the implicit dependency. The CI flake on PR #4716 manifested as TestSyncCreatedOrgContact failing with "CouchDB(not_found): missing" because instance.Service.Get queried _design/domain-and-aliases on a global instances DB where that design doc had never been created. A more robust fix would be to make instance.Service.Get treat any CouchDB "not_found" as ErrNotFound (it currently only handles no_db_file / "Database does not exist."). That would remove the implicit dependency for every package, not just this one. The repercussions on other Get callers haven't been fully audited yet, so this localized bootstrap stays in place until the broader change is vetted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tests in this package call lifecycle.Create / instance.Get directly without going through testutils.NewSetup → GetTestInstance → stack.Start, so they never trigger couchdb.InitGlobalDB themselves. They've historically relied on the side effect of earlier model/* test packages bootstrapping the global DB and on the design doc persisting in the shared CouchDB service across test binaries. Go's test result cache (persisted via actions/setup-go cache) can let those packages be skipped, breaking the implicit dependency. The CI flake on PR #4716 manifested as TestSyncCreatedOrgContact failing with "CouchDB(not_found): missing" because instance.Service.Get queried _design/domain-and-aliases on a global instances DB where that design doc had never been created. A more robust fix would be to make instance.Service.Get treat any CouchDB "not_found" as ErrNotFound (it currently only handles no_db_file / "Database does not exist."). That would remove the implicit dependency for every package, not just this one. The repercussions on other Get callers haven't been fully audited yet, so this localized bootstrap stays in place until the broader change is vetted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tests in this package call lifecycle.Create / instance.Get directly without going through testutils.NewSetup → GetTestInstance → stack.Start, so they never trigger couchdb.InitGlobalDB themselves. They've historically relied on the side effect of earlier model/* test packages bootstrapping the global DB and on the design doc persisting in the shared CouchDB service across test binaries. Go's test result cache (persisted via actions/setup-go cache) can let those packages be skipped, breaking the implicit dependency. The CI flake on PR #4716 manifested as TestSyncCreatedOrgContact failing with "CouchDB(not_found): missing" because instance.Service.Get queried _design/domain-and-aliases on a global instances DB where that design doc had never been created. A more robust fix would be to make instance.Service.Get treat any CouchDB "not_found" as ErrNotFound (it currently only handles no_db_file / "Database does not exist."). That would remove the implicit dependency for every package, not just this one. The repercussions on other Get callers haven't been fully audited yet, so this localized bootstrap stays in place until the broader change is vetted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@shepilov For your information we've tested this version of the stack and it seems to work. I'll redo my git history and also, maybe I'll try to be able to chose between s3 and swift not only on env level but also on instance. Like that, I'll be able to move some production instances to S3 in order to check & validate in real usage. WDYT? |
f310be3 to
52655f5
Compare
Implement a complete vfs.VFS backend for S3-compatible object storage (OVH, MinIO, Scaleway, ...) using minio-go/v7, mirroring the Swift V3 implementation. - config: add SchemeS3 and an S3 connection singleton, configured via the fs.url query params (access_key, secret_key, region, bucket_prefix, use_ssl) - model/vfs/vfss3: full VFS implementation with a bucket per orgId, a key prefix per instance, streaming CreateFile via io.Pipe, and multipart uploads with bounded memory - wire SchemeS3 into every storage dispatch point: VFS, avatars, thumbnails, apps copier and file servers, archiver, cache, dynamic assets and capabilities - pkg/s3util: shared S3 helpers (EnsureBucket, DeleteObjects, DeletePrefixObjects, IsNotFound, WrapNotFound) - pkg/appfs and pkg/assets/dynamic: S3 app installation and dynamic assets storage - security hardening: path-traversal protection, S3 error sanitization, MD5 integrity verification and bounded reads, plus the code-review fixes - tests: add the S3 backend to the VFS integration suite (MinIO testcontainer) - docs: document the S3 storage backend architecture and configuration
The model/vfs package now exercises three backends (afero, swift, s3) plus a second MinIO container for S3UploadErrorPropagation, pushing the package past the previous 5-minute per-package limit on GH runners.
Adds a workflow that builds the production Dockerfile and pushes the
resulting image to ghcr.io/<repo>:s3-test on every push to the
feat/s3-vfs-backend branch, plus a workflow_dispatch trigger that
accepts a custom tag.
Also publishes a ${tag}-<short-sha> tag for traceability.
scripts/build.sh runs `git describe` / `git rev-parse` to derive the build version string. With recent git versions the COPY'd working tree trips the "detected dubious ownership" safety check inside the container (the host user that owned the source no longer owns the files), causing the build step to exit 128 before producing the binary. Whitelisting /app as safe restores the previous behaviour.
scripts/build.sh derives the version from git describe / git rev-parse on the COPY'd working tree, which fails inside the buildx container (exit 128 with no captured output). Inline `go build` with a build-arg version string sidesteps the whole bash + git chain. The workflow passes VERSION_STRING=<tag>-<sha> so the running binary reports a recognizable version.
52655f5 to
4c7b7a9
Compare
Summary
minio-go/v7— no AWS SDK dependency. Targets OVH S3, MinIO, Scaleway, and any S3-compatible provider<prefix>-<orgId>), key prefix per instance (<DBPrefix>/)What's included
docs/s3.md)Configuration
All buckets (apps, assets, previews, exports) are created automatically at startup.
Bucket layout
<prefix>-<orgId><prefix>-apps-web<prefix>-apps-konnectors<prefix>-assets<prefix>-previews<prefix>-exportsSee
docs/s3.mdfor full documentation including a local MinIO setup tutorial.Test plan
/filesAPI🤖 Generated with Claude Code