Skip to content

fix(clickhouse): check for S3 grant before validating CH-bound pipe - #4629

Open
itsbilal wants to merge 2 commits into
mainfrom
DBI-427-check-s3-grants
Open

fix(clickhouse): check for S3 grant before validating CH-bound pipe#4629
itsbilal wants to merge 2 commits into
mainfrom
DBI-427-check-s3-grants

Conversation

@itsbilal

@itsbilal itsbilal commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Previously, if custom roles were selected instead of all access when creating a pipe, we did not validate that the ClickHouse user can read from the staging S3 bucket in the final step of ingestion. This would result in pipes getting stuck in provisioning without an obvious user-facing error.

This change updates the ValidateClickHousePeer function to also run a CHECK GRANT that's as version-agnostic as possible, to confirm if the user can actually read from S3. If the CHECK GRANT returns a 0, we throw a validation failure.

@itsbilal itsbilal self-assigned this Jul 27, 2026
@itsbilal
itsbilal requested a review from a team as a code owner July 27, 2026 18:59
@itsbilal
itsbilal force-pushed the DBI-427-check-s3-grants branch 2 times, most recently from 15c5f9c to 3b5b4a6 Compare July 27, 2026 19:02
@claude

claude Bot commented Jul 27, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@itsbilal
itsbilal requested a review from jgao54 July 27, 2026 19:07
@claude

claude Bot commented Jul 27, 2026

Copy link
Copy Markdown

Code review

Bug: S3 grant check runs unconditionally for all staging typesflow/pkg/clickhouse/validation.go, line 224

The new S3 grant validation loop runs unconditionally, but staging can also be GCS (see createStagingStore in staging.go). When GCS is the staging provider, ClickHouse reads staged data via url() with signed URLs (staging_gcs.go), not s3(), so the ClickHouse user does not need S3 grants.

For a GCS-staging deployment where the ClickHouse user lacks S3 grants (which is valid), CHECK GRANT READ ON S3 returns false and CHECK GRANT S3 ON *.* also returns false, causing validation to fail with a misleading "user lacks READ on S3" error — incorrectly blocking peer creation.

This check should be conditional on the staging provider being S3.

@itsbilal
itsbilal force-pushed the DBI-427-check-s3-grants branch from 3b5b4a6 to 85d539f Compare July 27, 2026 19:32
@itsbilal
itsbilal marked this pull request as draft July 27, 2026 19:36
@itsbilal

Copy link
Copy Markdown
Contributor Author

Marking as draft; I'm trying to refactor this code in such a way that it's easily reusable by the GCS side without making the imports too annoying. Should be done very soon.

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

❌ 8 Tests Failed:

Tests completed Failed Passed Skipped
3176 8 3168 393
View the top 3 failed test(s) by shortest run time
github.com/PeerDB-io/peerdb/flow/e2e::TestApiMariaDB
Stack Traces | 0.01s run time
=== RUN   TestApiMariaDB
=== PAUSE TestApiMariaDB
=== CONT  TestApiMariaDB
--- FAIL: TestApiMariaDB (0.01s)
github.com/PeerDB-io/peerdb/flow/e2e::TestApiMongo
Stack Traces | 0.01s run time
=== RUN   TestApiMongo
=== PAUSE TestApiMongo
=== CONT  TestApiMongo
--- FAIL: TestApiMongo (0.01s)
2026/07/28 13:25:10 INFO Received AWS credentials from peer for connector: ci x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/28 13:25:10 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_api_17mxmi2p.t1
2026/07/28 13:25:10 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_machcl_57auvwvu.test_schema_as_column
github.com/PeerDB-io/peerdb/flow/e2e::TestApiMy
Stack Traces | 0.01s run time
=== RUN   TestApiMy
=== PAUSE TestApiMy
=== CONT  TestApiMy
--- FAIL: TestApiMy (0.01s)
2026/07/28 13:19:56 INFO Received AWS credentials from peer for connector: ci x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/28 13:19:56 INFO Received AWS credentials from peer for connector: clickhouse x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/28 13:19:56 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_mychcl_r2og7fae.test_myisam
github.com/PeerDB-io/peerdb/flow/e2e::TestApiPg
Stack Traces | 0.01s run time
=== RUN   TestApiPg
=== PAUSE TestApiPg
=== CONT  TestApiPg
--- FAIL: TestApiPg (0.01s)
2026/07/28 13:22:28 INFO Received AWS credentials from peer for connector: ci x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/28 13:22:28 INFO Received AWS credentials from peer for connector: clickhouse x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
github.com/PeerDB-io/peerdb/flow/e2e::TestApiMariaDB/TestCancelTableAdditionDuringSetupFlow
Stack Traces | 10.1s run time
=== RUN   TestApiMariaDB/TestCancelTableAdditionDuringSetupFlow
=== PAUSE TestApiMariaDB/TestCancelTableAdditionDuringSetupFlow
=== CONT  TestApiMariaDB/TestCancelTableAdditionDuringSetupFlow
2026/07/28 13:24:45 INFO Received AWS credentials from peer for connector: ci x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/28 13:24:45 INFO Received AWS credentials from peer for connector: clickhouse x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/28 13:24:45 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_api_eccw9ruw.resync_paused_snap_cfg
    cancel_table_addition_test.go:1163: WaitFor wait for initial load to finish 2026-07-28 13:24:49.186208338 +0000 UTC m=+697.490741031
2026/07/28 13:24:49 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_api_g5nmvi60.original
    cancel_table_addition_test.go:1173: WaitFor wait for pause for add table 2026-07-28 13:24:49.201849072 +0000 UTC m=+697.506381765
    cancel_table_addition_test.go:1195: Edited peer with bad host
    cancel_table_addition_test.go:1215: WaitFor wait for table addition to be stuck 2026-07-28 13:24:51.211006105 +0000 UTC m=+699.515538798
    cancel_table_addition_test.go:1227: WaitFor wait for table addition cancellation to finish 2026-07-28 13:24:53.128160059 +0000 UTC m=+701.432692742
    cancel_table_addition_test.go:1256: WaitFor wait for pause for peer restore 2026-07-28 13:24:53.132884514 +0000 UTC m=+701.437417207
    cancel_table_addition_test.go:1271: 
        	Error Trace:	.../flow/e2e/cancel_table_addition_test.go:1271
        	Error:      	Received unexpected error:
        	            	rpc error: code = Internal desc = internal server error
        	Test:       	TestApiMariaDB/TestCancelTableAdditionDuringSetupFlow
    api_test.go:51: begin tearing down postgres schema api_g5nmvi60
--- FAIL: TestApiMariaDB/TestCancelTableAdditionDuringSetupFlow (10.10s)
github.com/PeerDB-io/peerdb/flow/e2e::TestApiMongo/TestCancelTableAdditionDuringSetupFlow
Stack Traces | 10.1s run time
=== RUN   TestApiMongo/TestCancelTableAdditionDuringSetupFlow
=== PAUSE TestApiMongo/TestCancelTableAdditionDuringSetupFlow
=== CONT  TestApiMongo/TestCancelTableAdditionDuringSetupFlow
2026/07/28 13:24:09 INFO Received AWS credentials from peer for connector: ci x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/28 13:24:09 INFO Received AWS credentials from peer for connector: clickhouse x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
    cancel_table_addition_test.go:1163: WaitFor wait for initial load to finish 2026-07-28 13:24:13.874019697 +0000 UTC m=+662.178552390
    cancel_table_addition_test.go:1173: WaitFor wait for pause for add table 2026-07-28 13:24:13.888011139 +0000 UTC m=+662.192543822
    cancel_table_addition_test.go:1195: Edited peer with bad host
    cancel_table_addition_test.go:1215: WaitFor wait for table addition to be stuck 2026-07-28 13:24:15.900357204 +0000 UTC m=+664.204889897
2026/07/28 13:24:16 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_mach_jyznvp3j.test_mysql_schema_changes
    cancel_table_addition_test.go:1227: WaitFor wait for table addition cancellation to finish 2026-07-28 13:24:17.827246538 +0000 UTC m=+666.131779231
    cancel_table_addition_test.go:1256: WaitFor wait for pause for peer restore 2026-07-28 13:24:17.841701826 +0000 UTC m=+666.146234520
    cancel_table_addition_test.go:1271: 
        	Error Trace:	.../flow/e2e/cancel_table_addition_test.go:1271
        	Error:      	Received unexpected error:
        	            	rpc error: code = Internal desc = internal server error
        	Test:       	TestApiMongo/TestCancelTableAdditionDuringSetupFlow
    api_test.go:51: begin tearing down postgres schema api_m0tljijd
--- FAIL: TestApiMongo/TestCancelTableAdditionDuringSetupFlow (10.14s)
github.com/PeerDB-io/peerdb/flow/e2e::TestApiMy/TestCancelTableAdditionDuringSetupFlow
Stack Traces | 10.1s run time
=== RUN   TestApiMy/TestCancelTableAdditionDuringSetupFlow
=== PAUSE TestApiMy/TestCancelTableAdditionDuringSetupFlow
=== CONT  TestApiMy/TestCancelTableAdditionDuringSetupFlow
2026/07/28 13:19:01 INFO Received AWS credentials from peer for connector: ci x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/28 13:19:01 INFO Received AWS credentials from peer for connector: clickhouse x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
    cancel_table_addition_test.go:1163: WaitFor wait for initial load to finish 2026-07-28 13:19:05.472804115 +0000 UTC m=+353.777336798
2026/07/28 13:19:05 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_api_cyy7ukoc.original
    cancel_table_addition_test.go:1173: WaitFor wait for pause for add table 2026-07-28 13:19:05.483605906 +0000 UTC m=+353.788138599
    cancel_table_addition_test.go:1195: Edited peer with bad host
    cancel_table_addition_test.go:1215: WaitFor wait for table addition to be stuck 2026-07-28 13:19:07.495321127 +0000 UTC m=+355.799853820
    cancel_table_addition_test.go:1227: WaitFor wait for table addition cancellation to finish 2026-07-28 13:19:09.460135886 +0000 UTC m=+357.764668579
    cancel_table_addition_test.go:1256: WaitFor wait for pause for peer restore 2026-07-28 13:19:09.465408915 +0000 UTC m=+357.769941608
    cancel_table_addition_test.go:1271: 
        	Error Trace:	.../flow/e2e/cancel_table_addition_test.go:1271
        	Error:      	Received unexpected error:
        	            	rpc error: code = Internal desc = internal server error
        	Test:       	TestApiMy/TestCancelTableAdditionDuringSetupFlow
    api_test.go:51: begin tearing down postgres schema api_cyy7ukoc
--- FAIL: TestApiMy/TestCancelTableAdditionDuringSetupFlow (10.14s)
github.com/PeerDB-io/peerdb/flow/e2e::TestApiPg/TestCancelTableAdditionDuringSetupFlow
Stack Traces | 10.2s run time
=== RUN   TestApiPg/TestCancelTableAdditionDuringSetupFlow
=== PAUSE TestApiPg/TestCancelTableAdditionDuringSetupFlow
=== CONT  TestApiPg/TestCancelTableAdditionDuringSetupFlow
2026/07/28 13:21:17 INFO Received AWS credentials from peer for connector: ci x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/28 13:21:17 INFO Received AWS credentials from peer for connector: clickhouse x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/28 13:21:18 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_mychcl_eeogv8at.test_nullengine
    cancel_table_addition_test.go:1163: WaitFor wait for initial load to finish 2026-07-28 13:21:22.056319336 +0000 UTC m=+490.360852029
2026/07/28 13:21:22 INFO Executing and processing query x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_0xadb54j.\"original\" ORDER BY id"
2026/07/28 13:21:22 INFO Executing and processing query stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_0xadb54j.\"original\" ORDER BY id"
2026/07/28 13:21:22 INFO [pg_query_executor] declared cursor x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursorQuery="DECLARE peerdb_cursor_7188601693660539315 CURSOR FOR SELECT id,val FROM e2e_test_api_0xadb54j.\"original\" ORDER BY id" args=[]
2026/07/28 13:21:22 INFO [pg_query_executor] fetching rows start x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_0xadb54j.\"original\" ORDER BY id" channelLen=0
2026/07/28 13:21:22 INFO [pg_query_executor] fetching from cursor x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_7188601693660539315
2026/07/28 13:21:22 INFO processed row stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_7188601693660539315 records=1 bytes=9 channelLen=0
2026/07/28 13:21:22 INFO [pg_query_executor] fetched rows x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_0xadb54j.\"original\" ORDER BY id" rows=1 bytes=9 channelLen=0
2026/07/28 13:21:22 INFO [pg_query_executor] fetching from cursor x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_7188601693660539315
2026/07/28 13:21:22 INFO processed row stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_7188601693660539315 records=0 bytes=0 channelLen=0
2026/07/28 13:21:22 INFO [pg_query_executor] fetched rows x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_0xadb54j.\"original\" ORDER BY id" rows=0 bytes=0 channelLen=0
2026/07/28 13:21:22 INFO [pg_query_executor] committing transaction x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart
2026/07/28 13:21:22 INFO [pg_query_executor] committed transaction for query x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_0xadb54j.\"original\" ORDER BY id" rows=1 bytes=9 channelLen=0
    cancel_table_addition_test.go:1173: WaitFor wait for pause for add table 2026-07-28 13:21:22.073204749 +0000 UTC m=+490.377737432
2026/07/28 13:21:22 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_mychcl_eeogv8at.test_nullengine
    cancel_table_addition_test.go:1195: Edited peer with bad host
2026/07/28 13:21:24 INFO processed row stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_5240471157797527712 records=1 bytes=9 channelLen=0
2026/07/28 13:21:24 INFO [pg_query_executor] fetched rows x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_veiegggs.\"t3\" ORDER BY id" rows=1 bytes=9 channelLen=0
2026/07/28 13:21:24 INFO [pg_query_executor] fetching from cursor x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_5240471157797527712
2026/07/28 13:21:24 INFO processed row stream x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart cursor=peerdb_cursor_5240471157797527712 records=0 bytes=0 channelLen=0
2026/07/28 13:21:24 INFO [pg_query_executor] fetched rows x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_veiegggs.\"t3\" ORDER BY id" rows=0 bytes=0 channelLen=0
2026/07/28 13:21:24 INFO [pg_query_executor] committing transaction x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart
2026/07/28 13:21:24 INFO [pg_query_executor] committed transaction for query x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} partitionId=testpart query="SELECT id,val FROM e2e_test_api_veiegggs.\"t3\" ORDER BY id" rows=1 bytes=9 channelLen=0
    cancel_table_addition_test.go:1215: WaitFor wait for table addition to be stuck 2026-07-28 13:21:24.083806985 +0000 UTC m=+492.388339678
    cancel_table_addition_test.go:1227: WaitFor wait for table addition cancellation to finish 2026-07-28 13:21:26.056169067 +0000 UTC m=+494.360701890
    cancel_table_addition_test.go:1256: WaitFor wait for pause for peer restore 2026-07-28 13:21:26.060966881 +0000 UTC m=+494.365499574
    cancel_table_addition_test.go:1271: 
        	Error Trace:	.../flow/e2e/cancel_table_addition_test.go:1271
        	Error:      	Received unexpected error:
        	            	rpc error: code = Internal desc = internal server error
        	Test:       	TestApiPg/TestCancelTableAdditionDuringSetupFlow
    api_test.go:51: begin tearing down postgres schema api_0xadb54j
--- FAIL: TestApiPg/TestCancelTableAdditionDuringSetupFlow (10.23s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@github-actions

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: The MySQL_CH binlog test failed during connection setup with a transient TCP handshake EOF ("connection was bad") to the MySQL container—an infrastructure flake unrelated to the S3-grants PR, and only 1 of 3 matrix jobs was affected.
Confidence: 0.92

✅ Automatically retrying the workflow

View workflow run

@itsbilal
itsbilal force-pushed the DBI-427-check-s3-grants branch from 85d539f to 4c48db8 Compare July 27, 2026 22:19
@github-actions

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: Not flaky — the build fails to compile in flow/connectors/clickhouse because the StagingValidator API gained logger/ClickhouseGrantChecker parameters but clickhouse.go:85, staging_gcs.go:126 and staging_s3.go:214 still use the old signatures, so no tests ran in any matrix job.
Confidence: 0.99

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

@itsbilal
itsbilal force-pushed the DBI-427-check-s3-grants branch 2 times, most recently from 14f776a to 9bd1178 Compare July 28, 2026 13:08
@github-actions

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: Real bug: TestCancelTableAdditionDuringSetupFlow failed identically in all 4 API suites across all 3 matrix jobs at cancel_table_addition_test.go:1271 with rpc error: code = Internal desc = internal server error on the validated ClickHouse CreatePeer call, consistent with the new S3 grant check introduced by the DBI-427-check-s3-grants branch.
Confidence: 0.93

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

@github-actions

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: Not flaky — the PR's new S3 grants check makes peer validation fail deterministically, so TestCancelTableAdditionDuringSetupFlow errors with rpc error: code = Internal at cancel_table_addition_test.go:1271 in all four API suites across all three matrix jobs.
Confidence: 0.93

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

@itsbilal

Copy link
Copy Markdown
Contributor Author

Test failure aside, this is ready for a review.

@itsbilal
itsbilal marked this pull request as ready for review July 28, 2026 13:40
Previously, if custom roles were selected instead of all access when
creating a pipe, we did not validate that the ClickHouse user can read
from the staging S3 bucket in the final step of ingestion. This would
result in pipes getting stuck in provisioning without an obvious user-facing
error.

This change updates the ValidateClickHousePeer function to also run a CHECK
GRANT that's as version-agnostic as possible, to confirm if the user can actually
read from S3. If the CHECK GRANT returns a 0, we throw a validation failure.
@itsbilal
itsbilal force-pushed the DBI-427-check-s3-grants branch from 9bd1178 to a341317 Compare July 28, 2026 16:10
Comment on lines +82 to +84
if err := validateBucketGrant(ctx, log, checker, "S3"); err != nil {
return err
}

@jgao54 jgao54 Jul 29, 2026

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.

I don't think we want to thread the clickhouse permission check here thru the objectstore validators. In Discovery today we do not use the s3/gcs validator, this was intentional because s3 is considered internal infra, whereas discovery validation is intended for validating user setup. So adding it here would have it silently skipped today.

Instead, can call the check directly inside ValidateClickHousePeer:

if stagingBucketIsS3 {
        if err := validateS3ReadGrant(ctx, logger, conn); err != nil {
            return err
        }
}

One more NIT is when i read:

	if err := validateBucketGrant(ctx, log, checker, "GCS"); err != nil {
		return err
	}

i assumed that we were trying to check READ ON GCS until i saw:

if checker == nil {
	return nil
}

I find this a bit implicit and it's better to just hard-code it to S3 given that's all we care about right now.

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.

Agreed with moving to ValidateClickHousePeer and the duplication between checker and the object. For GCS, we actually need to validate READ ON URL instead, it's a separate grant that's not guaranteed

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.

If we do need a validation in the GCS case (CHECK GRANT READ on URL) should we keep the broad structure the way it is right now, remove the if checker == nil { return nil } , and just have the gcs path also call validateBucketGrant correctly?

I think that'd be cleanest, but also happy to move the logic to ValidateClickHousePeer if y'all think that's better - only reason I didn't want to encode it there is because it feels off-place to have an S3/GCS switch there when we already delegate S3/GCS specific logic to the appropriate StagingStore.

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.

I've made this change - let me know how you feel about this approach. Also happy to move this logic out of StagingStore entirely and move it in ValidateClickHousePeer if that's still preferred.

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.

move the logic to ValidateClickHousePeer

To clarify, today in Discovery we use NoStagingValidator, not S3StagingValidator/GCSStagingValidator, so validateBucketGrant would never be called here; which is why moving it to ValidateClickHousePeer might make more sense.

@jgao54 jgao54 Jul 30, 2026

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.

it feels off-place to have an S3/GCS switch there when we already delegate S3/GCS specific logic to the appropriate StagingStore

if there's a better way to introduce just the s3/gcp perm check logic into the staging store and only only trigger that path, that works too. (I don't feel too strong either way, because currently the validator check write perm using the object storage client; vs. the read perm check you introduced is purely a clickhouse permission check; so i can see it belong to either)

@ilidemi ilidemi Jul 30, 2026

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.

There are two kinds of staging store validations: can PeerDB access the store and does CH user have permissions to do read queries. The first one is only relevant in OSS, as people would do ground-up S3 setups or self-host MinIO, and in our managed environment S3 is set up uniformly and the service doing the validation can't even access it. Only the second one is relevant there.

Reading through the code again, I realized that the whole StagingValidator infra was a result of a misunderstanding of how it all works e2e, and it doesn't need to be in pkg. This must've been confusing to read and try to align with, submitted a cleanup in #4643. ValidateClickHousePeer would lose the last arg, and you'd be able to cleanly add the "S3"/"URL" (or a boolean/enum) instead.

Hopefully now it's clearer how we don't really need extra abstractions besides doing a CH query and validating it with various kinds of users in real CH.

Comment thread flow/pkg/objectstore/validation.go Outdated
Comment on lines +82 to +84
if err := validateBucketGrant(ctx, log, checker, "S3"); err != nil {
return err
}

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.

Agreed with moving to ValidateClickHousePeer and the duplication between checker and the object. For GCS, we actually need to validate READ ON URL instead, it's a separate grant that's not guaranteed

chHost, strings.Join(allowedDomains, ","))
}

type grantChecker struct {

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.

Was trying to understand the need for more abstractions here and in the tests, and realized it's because we want to test something but the package doesn't have the integration test infra hooked up to do proper CH calls and keep the actual validation change to 1-2 simple functions. We only sorted out the test infra in the recent couple of months, so the only reason it's not simple is historical. Added scaffolding in #4639, let's make the tests set up an actual user with/without grants and see how the validation responds.

@itsbilal
itsbilal force-pushed the DBI-427-check-s3-grants branch from 9e2009a to bb6ec66 Compare July 29, 2026 17:01
@github-actions

Copy link
Copy Markdown
Contributor

🔄 Flaky Test Detected

Analysis: Two unrelated timing races in separate matrix legs — a MySQL testcontainer accepted a connection before mysqld finished handshake init, and a Temporal ListWorkflow assertion read the eventually-consistent visibility store with no retry — neither related to the PR's S3-grants change, and the third matrix leg passed.
Confidence: 0.92

✅ Automatically retrying the workflow

View workflow run

@itsbilal
itsbilal requested review from ilidemi and jgao54 July 29, 2026 21:32
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.

3 participants