Skip to content

fix(s3): remove hardcoded dummy credentials, add miniotesting#593

Open
Dav-14 wants to merge 3 commits into
mainfrom
fix/s3-dummy-credentials-and-miniotesting
Open

fix(s3): remove hardcoded dummy credentials, add miniotesting#593
Dav-14 wants to merge 3 commits into
mainfrom
fix/s3-dummy-credentials-and-miniotesting

Conversation

@Dav-14

@Dav-14 Dav-14 commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Remove hardcoded dummy S3 credentials in pkg/fx/storagefx/s3.go — the s3AWSModule unconditionally injected StaticCredentialsProvider("dummy", "dummy", "dummy") when an endpoint override was set, overriding real credentials from IAM flags. This broke S3 operations against MinIO or any auth-requiring S3-compatible store.
  • Make path-style addressing configurable — added --s3-bucket-path-style flag. Path style is always enabled for real AWS S3 (no endpoint override). For S3-compatible services (endpoint override), it's now explicitly configurable instead of being hardcoded.
  • Add pkg/testing/platform/miniotesting — reusable package for spinning up MinIO containers in tests, following the same pattern as localstacktesting and pgtesting.

Why

The dummy credentials were intended for LocalStack (which doesn't need auth), but they silently overwrote real IAM credentials when used with MinIO. This caused InvalidAccessKeyId errors in integration tests of services like terraform-hcp-proxy.

Credential configuration

Credentials are no longer magically injected. They must be provided explicitly via:

  • CLI flags: --aws-access-key-id / --aws-secret-access-key
  • Environment variables: AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
  • AWS default credential chain (IAM role, shared config, etc.)

Path-style addressing

  • Real AWS S3 (no endpoint override): always UsePathStyle = true
  • S3-compatible services (endpoint override): configurable via --s3-bucket-path-style flag (default: false)

Test plan

  • Unit tests: config options, AWS config construction, S3 client creation
  • Integration tests: create bucket, put/get object, S3ModuleFromFlags with real MinIO credentials and path-style flag
  • All 8 tests pass against a real MinIO container

🤖 Generated with Claude Code

The s3AWSModule unconditionally injected dummy static credentials when an
endpoint override was set, overriding any real credentials provided via IAM
flags. This broke S3 operations against MinIO (or any S3-compatible store
that requires auth) in integration tests and local dev.

Also adds a reusable miniotesting package for spinning up MinIO containers
in tests, with helpers for bucket creation and S3 client construction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Dav-14 Dav-14 requested review from a team as code owners April 23, 2026 07:54
@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown

Walkthrough

Removes conditional dummy credentials injection in S3 FX wiring and adds a new MinIO testing suite: a MinioServer helper, Docker-backed startup, Ginkgo helper, and integration tests exercising S3 client creation and object operations.

Changes

Cohort / File(s) Summary
S3 FX change
pkg/fx/storagefx/s3.go
Stops augmenting AWS config with a dummy credentials provider; adjusts UsePathStyle behavior based on endpoint override and pathStyle flag.
S3 module flags
pkg/storage/s3/module.go
Adds S3BucketPathStyleFlag (s3-bucket-path-style) and registers a boolean CLI flag for path-style behavior.
MinIO server implementation
pkg/testing/platform/miniotesting/minio.go
New MinioServer type and helpers: static creds, GetAWSConfig, NewS3Client (path-style endpoint), CreateBucket, and CreateMinioServer that runs a Docker MinIO container with readiness check and functional options.
Ginkgo helper
pkg/testing/platform/miniotesting/ginkgo.go
Adds WithNewMinioServer(fn func(p *deferred.Deferred[*MinioServer]), opts ...Option) bool which sets up a MinioServer in a Ginkgo Context and exposes it via a deferred wrapper.
Integration tests
pkg/testing/platform/miniotesting/minio_test.go
New tests that start a MinIO container, verify credential/flag behaviors, perform S3 bucket and object operations, and validate storagefx.S3ModuleFromFlags-driven client against the running MinIO server.

Sequence Diagram(s)

sequenceDiagram
  participant Test as Test Runner
  participant Pool as Docker Pool
  participant Container as MinIO Container
  participant SDK as AWS SDK / s3.Client
  Test->>Pool: CreateMinioServer(opts)
  Pool->>Container: Start minio/minio with env creds
  Container->>Container: Health check /live/health returns 200
  Container-->>Test: Endpoint mapped (host:port)
  Test->>SDK: s3.NewClient(endpoint override, pathStyle)
  Test->>SDK: CreateBucket / PutObject / GetObject
  SDK->>Container: HTTP S3 requests (path-style)
  Container-->>SDK: 200 OK / object data
  SDK-->>Test: Operation results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped in code to spin a tiny server bright,
Docker warmed the tub and MinIO took flight,
Flags set the path, the client found its way,
Buckets filled with bytes — a merry testing day! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main changes: removing hardcoded dummy credentials and adding a MinIO testing utility package.
Description check ✅ Passed The description is directly related to the changeset, clearly explaining the motivation for removing dummy credentials and introducing the miniotesting package.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/s3-dummy-credentials-and-miniotesting

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.11.4)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.55556% with 31 lines in your changes missing coverage. Please review.
✅ Project coverage is 28.76%. Comparing base (7c19ac9) to head (20cf42f).

Files with missing lines Patch % Lines
pkg/fx/storagefx/s3.go 0.00% 12 Missing ⚠️
pkg/testing/platform/miniotesting/ginkgo.go 0.00% 12 Missing ⚠️
pkg/testing/platform/miniotesting/minio.go 90.76% 4 Missing and 2 partials ⚠️
pkg/storage/s3/module.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #593      +/-   ##
==========================================
+ Coverage   28.34%   28.76%   +0.42%     
==========================================
  Files         180      182       +2     
  Lines        8493     8572      +79     
==========================================
+ Hits         2407     2466      +59     
- Misses       5971     5989      +18     
- Partials      115      117       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread pkg/testing/platform/miniotesting/minio.go Outdated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
pkg/testing/platform/miniotesting/minio_test.go (1)

34-44: This test doesn't actually cover CreateMinioServer defaults.

TestDefaultConfig constructs a config literal in-place using the Default* constants and asserts they equal the same constants – it only verifies that assert.Equal works. To actually guard against regressions in the defaults applied by CreateMinioServer, assert against srv (or on a cfg produced by applying zero options), e.g.:

-func TestDefaultConfig(t *testing.T) {
-	cfg := &config{
-		accessKey: DefaultAccessKey,
-		secretKey: DefaultSecretKey,
-		tag:       "latest",
-	}
-
-	assert.Equal(t, "minioadmin", cfg.accessKey)
-	assert.Equal(t, "minioadmin", cfg.secretKey)
-	assert.Equal(t, "latest", cfg.tag)
-}
+func TestDefaultConfig(t *testing.T) {
+	assert.Equal(t, DefaultAccessKey, srv.AccessKey)
+	assert.Equal(t, DefaultSecretKey, srv.SecretKey)
+	assert.Equal(t, DefaultRegion, srv.Region)
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testing/platform/miniotesting/minio_test.go` around lines 34 - 44,
TestDefaultConfig currently builds a config literal and only compares constants;
instead call CreateMinioServer (with no options / zero opts) to obtain the
server (or the config it constructs) and assert the server's effective config
fields equal DefaultAccessKey, DefaultSecretKey and "latest". Update
TestDefaultConfig to invoke CreateMinioServer, extract the resulting config (or
srv.cfg) and assert on those fields (accessKey, secretKey, tag) rather than
comparing the constants to themselves.
pkg/testing/platform/miniotesting/minio.go (2)

103-114: Use an HTTP client with a timeout and drain the body for the readiness probe.

http.Get uses http.DefaultClient, which has no timeout. If the MinIO container accepts the TCP connection but stalls the HTTP response (not uncommon during early startup), the check goroutine can block indefinitely and prevent dockertest's retry loop from making progress. Also, closing the body without draining it prevents keep-alive reuse on subsequent retries (minor).

🛡️ Proposed fix
 		CheckFn: func(ctx context.Context, resource *dockertest.Resource) error {
 			endpoint := fmt.Sprintf("http://127.0.0.1:%s", resource.GetPort("9000/tcp"))
-			resp, err := http.Get(endpoint + "/minio/health/live")
+			req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint+"/minio/health/live", nil)
+			if err != nil {
+				return err
+			}
+			client := &http.Client{Timeout: 2 * time.Second}
+			resp, err := client.Do(req)
 			if err != nil {
 				return err
 			}
-			_ = resp.Body.Close()
+			_, _ = io.Copy(io.Discard, resp.Body)
+			_ = resp.Body.Close()
 			if resp.StatusCode != http.StatusOK {
 				return fmt.Errorf("minio health check returned %d", resp.StatusCode)
 			}
 			return nil
 		},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testing/platform/miniotesting/minio.go` around lines 103 - 114, In the
CheckFn readiness probe replace the blocking http.Get with a short-timeout
http.Client and perform the request using http.NewRequestWithContext(ctx, ...)
so the probe respects the provided context; set a client.Timeout (e.g. 5s), call
client.Do(req), and on success always io.Copy(io.Discard, resp.Body) before
resp.Body.Close() to drain the body and allow connection reuse; keep the same
endpoint construction using resource.GetPort("9000/tcp") and return errors as
before.

93-115: Consider exposing console port (9001) and guard WithTag() against empty strings.

Two issues with CreateMinioServer:

  1. WithTag("") will pass an empty string directly to RunOptions.Tag, overriding the default "latest". Guard against empty strings by falling back to "latest" in the option.
  2. Port exposure for 9000/tcp relies on implicit behavior; if you plan to expose the console port (9001/tcp), add explicit port configuration to HostConfigOptions or document that only the API port is available.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testing/platform/miniotesting/minio.go` around lines 93 - 115, Change
CreateMinioServer so WithTag("") cannot overwrite the default tag: when building
RunOptions.Tag (the value passed from WithTag) treat empty string as "latest"
and set RunOptions.Tag = "latest" if the option value is empty; also explicitly
expose the MinIO console port by adding port configuration to
RunOptions.HostConfigOptions (or equivalent HostConfig/PortBindings) to include
"9001/tcp" in addition to "9000/tcp" so both the API and console are available,
leaving the existing CheckFn/health check intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/testing/platform/miniotesting/minio_test.go`:
- Around line 34-44: TestDefaultConfig currently builds a config literal and
only compares constants; instead call CreateMinioServer (with no options / zero
opts) to obtain the server (or the config it constructs) and assert the server's
effective config fields equal DefaultAccessKey, DefaultSecretKey and "latest".
Update TestDefaultConfig to invoke CreateMinioServer, extract the resulting
config (or srv.cfg) and assert on those fields (accessKey, secretKey, tag)
rather than comparing the constants to themselves.

In `@pkg/testing/platform/miniotesting/minio.go`:
- Around line 103-114: In the CheckFn readiness probe replace the blocking
http.Get with a short-timeout http.Client and perform the request using
http.NewRequestWithContext(ctx, ...) so the probe respects the provided context;
set a client.Timeout (e.g. 5s), call client.Do(req), and on success always
io.Copy(io.Discard, resp.Body) before resp.Body.Close() to drain the body and
allow connection reuse; keep the same endpoint construction using
resource.GetPort("9000/tcp") and return errors as before.
- Around line 93-115: Change CreateMinioServer so WithTag("") cannot overwrite
the default tag: when building RunOptions.Tag (the value passed from WithTag)
treat empty string as "latest" and set RunOptions.Tag = "latest" if the option
value is empty; also explicitly expose the MinIO console port by adding port
configuration to RunOptions.HostConfigOptions (or equivalent
HostConfig/PortBindings) to include "9001/tcp" in addition to "9000/tcp" so both
the API and console are available, leaving the existing CheckFn/health check
intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c08424ef-7c40-4e82-ab80-afbf73930787

📥 Commits

Reviewing files that changed from the base of the PR and between 7c19ac9 and 7ec2a35.

📒 Files selected for processing (4)
  • pkg/fx/storagefx/s3.go
  • pkg/testing/platform/miniotesting/ginkgo.go
  • pkg/testing/platform/miniotesting/minio.go
  • pkg/testing/platform/miniotesting/minio_test.go

@Dav-14 Dav-14 enabled auto-merge April 23, 2026 09:27
Comment thread pkg/fx/storagefx/s3.go
))
}
return loadOptions
return []func(*config.LoadOptions) error{optFn}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we do that we break the regular dev env for banking bridge right?
minIO is only s3 isn't it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Here's how this is addressed:

Credentials: The dummy credentials (dummy/dummy/dummy) were removed because they silently overrode real IAM credentials passed via --aws-access-key-id/--aws-secret-access-key flags. For dev with MinIO, credentials now need to be provided explicitly — either via CLI flags or env vars (AWS_ACCESS_KEY_ID=minioadmin, etc.). The config.LoadDefaultConfig() won't crash without credentials, but S3 calls will fail at runtime if none are resolved through the AWS default chain.

Path style: Was hardcoded to true whenever an endpoint override was set. Now it's split:

  • Real AWS S3 (no endpoint override): always UsePathStyle = true
  • S3-compatible services (endpoint override like MinIO): configurable via new --s3-bucket-path-style flag (default false)

So for banking bridge dev env with MinIO, the fix is to pass --aws-access-key-id=minioadmin --aws-secret-access-key=minioadmin --s3-bucket-path-style=true (or equivalent env vars).

Path style is always enabled for real AWS S3 (no endpoint override).
For S3-compatible services (endpoint override), it is now configurable
via the new --s3-bucket-path-style flag (default: false).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/testing/platform/miniotesting/minio.go`:
- Around line 3-7: The readiness probe uses http.Get without honoring the
provided ctx or any timeout (causing potential CI hangs); change the probe to
build the request with http.NewRequestWithContext(ctx, "GET", url, nil) and
execute it via an http.Client that either respects ctx or has a reasonable
Timeout, ensure you call client.Do(req) instead of http.Get, handle errors and
close resp.Body, and use the existing dockertest context variable (ctx) used by
the surrounding function (e.g., the MinIO startup/wait helper) so dockertest
retries/cancels properly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9b32865e-fcb7-436f-abf3-21660b94fb21

📥 Commits

Reviewing files that changed from the base of the PR and between 7ec2a35 and 20cf42f.

📒 Files selected for processing (4)
  • pkg/fx/storagefx/s3.go
  • pkg/storage/s3/module.go
  • pkg/testing/platform/miniotesting/minio.go
  • pkg/testing/platform/miniotesting/minio_test.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/storage/s3/module.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/fx/storagefx/s3.go
  • pkg/testing/platform/miniotesting/minio_test.go

Comment on lines +3 to +7
import (
"context"
"fmt"
"net/http"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify readiness probes are not using context-free http.Get calls.

rg -n -C3 '\bhttp\.Get\s*\(' --iglob 'minio.go'
rg -n -C5 'NewRequestWithContext|WithTimeout|http\.Client\{.*Timeout' --iglob 'minio.go'

Repository: formancehq/go-libs

Length of output: 648


🏁 Script executed:

head -20 pkg/testing/platform/miniotesting/minio.go

Repository: formancehq/go-libs

Length of output: 507


🏁 Script executed:

sed -n '100,115p' pkg/testing/platform/miniotesting/minio.go

Repository: formancehq/go-libs

Length of output: 585


Bind the readiness probe to the dockertest context.

Line 107 uses http.Get without a timeout or the provided ctx. If MinIO's mapped port accepts the connection but stalls before responding, this can hang CI indefinitely instead of allowing dockertest to retry or cancel cleanly.

🛠️ Proposed fix
 import (
 	"context"
 	"fmt"
 	"net/http"
+	"time"
 
 	"github.com/aws/aws-sdk-go-v2/aws"
 		CheckFn: func(ctx context.Context, resource *dockertest.Resource) error {
 			endpoint := fmt.Sprintf("http://127.0.0.1:%s", resource.GetPort("9000/tcp"))
-			resp, err := http.Get(endpoint + "/minio/health/live")
+			reqCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
+			defer cancel()
+
+			req, err := http.NewRequestWithContext(reqCtx, http.MethodGet, endpoint+"/minio/health/live", nil)
+			if err != nil {
+				return err
+			}
+
+			resp, err := http.DefaultClient.Do(req)
 			if err != nil {
 				return err
 			}
-			_ = resp.Body.Close()
+			defer resp.Body.Close()
 			if resp.StatusCode != http.StatusOK {
 				return fmt.Errorf("minio health check returned %d", resp.StatusCode)
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testing/platform/miniotesting/minio.go` around lines 3 - 7, The readiness
probe uses http.Get without honoring the provided ctx or any timeout (causing
potential CI hangs); change the probe to build the request with
http.NewRequestWithContext(ctx, "GET", url, nil) and execute it via an
http.Client that either respects ctx or has a reasonable Timeout, ensure you
call client.Do(req) instead of http.Get, handle errors and close resp.Body, and
use the existing dockertest context variable (ctx) used by the surrounding
function (e.g., the MinIO startup/wait helper) so dockertest retries/cancels
properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants