Skip to content

feat: ClickHouse bring-your-own-warehouse connections#8020

Merged
Zaimwa9 merged 25 commits into
mainfrom
feat/clickhouse-byo-warehouse-backend
Jul 20, 2026
Merged

feat: ClickHouse bring-your-own-warehouse connections#8020
Zaimwa9 merged 25 commits into
mainfrom
feat/clickhouse-byo-warehouse-backend

Conversation

@Zaimwa9

@Zaimwa9 Zaimwa9 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Thanks for submitting a PR! Please check the boxes below:

  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Changes

Adds the clickhouse warehouse connection type so customers can bring their own ClickHouse instance for experimentation. This PR covers connection setup and credentials management; event routing is a follow-up in flagsmith-analytics-pipeline, and the frontend ships separately behind the clickhouse_warehouse flag.

  • New EncryptedJSONField stores connection credentials encrypted at rest, keyed off SECRET_KEY — no new configuration required.
  • The serializer accepts config (host required, sensible defaults for port/database/username/TLS) and a write-only credentials: {password} that is never returned by the API. Omitting credentials on PATCH keeps the stored password; switching the connection to another type clears it.
  • Connections are verified with SELECT 1 on create, on config or credential change, and via the existing test-connection endpoint, setting the status to connected or errored.
  • New verification counter metric and structlog events.

How did you test this code?

Unit tests (100% diff coverage), make lint, make typecheck.

Manually: POST /api/v1/environments/<api_key>/warehouse-connections/ with a clickhouse payload returns 201 with "status": "connected" or "errored"; re-verify via POST …/test-warehouse-connection/.

@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Jul 20, 2026 1:07pm
flagsmith-frontend-preview Ignored Ignored Preview Jul 20, 2026 1:07pm
flagsmith-frontend-staging Ignored Ignored Preview Jul 20, 2026 1:07pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds encrypted ClickHouse credentials, status details, typed defaults, shared internal-address validation, and warehouse-specific serializer validation. ClickHouse connections are verified during relevant lifecycle actions and explicit testing, with bounded queries, error details, status updates, metrics, logging, and write throttling. API schemas and observability catalogues are updated, alongside migrations and unit/API coverage. Webhook SSRF validation now uses the shared network helper.

Estimated code review effort: 5 (Critical) | ~120 minutes


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.

Comment thread openapi.yaml
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.65%. Comparing base (0838195) to head (90f5b6d).

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #8020    +/-   ##
========================================
  Coverage   98.64%   98.65%            
========================================
  Files        1506     1512     +6     
  Lines       59573    59971   +398     
========================================
+ Hits        58767    59165   +398     
  Misses        806      806            

☔ View full report in Codecov by Harness.
📢 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Zaimwa9
Zaimwa9 marked this pull request as ready for review July 16, 2026 10:27
@Zaimwa9
Zaimwa9 requested review from a team as code owners July 16, 2026 10:27
@Zaimwa9
Zaimwa9 requested review from matthewelwell and removed request for a team July 16, 2026 10:27
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 16, 2026
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-api-test:pr-8020 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-e2e:pr-8020 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-frontend:pr-8020 Finished ✅ Results
ghcr.io/flagsmith/flagsmith:pr-8020 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-api:pr-8020 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-private-cloud:pr-8020 Finished ✅ Results

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  4 passed

Details

stats  4 tests across 3 suites
duration  41.8 seconds
commit  f7a302b
info  🔄 Run: #18346 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  5 passed

Details

stats  5 tests across 3 suites
duration  41.4 seconds
commit  f7a302b
info  🔄 Run: #18346 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  6 passed

Details

stats  6 tests across 5 suites
duration  18.1 seconds
commit  f7a302b
info  🔄 Run: #18346 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  6 passed

Details

stats  6 tests across 5 suites
duration  12 seconds
commit  f7a302b
info  🔄 Run: #18346 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  32 seconds
commit  f1cd2a5
info  🔄 Run: #18489 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  46.7 seconds
commit  f1cd2a5
info  🔄 Run: #18489 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  36 seconds
commit  f1cd2a5
info  🔄 Run: #18489 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  45.4 seconds
commit  f1cd2a5
info  🔄 Run: #18489 (attempt 1)

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Visual Regression

19 screenshots compared. See report for details.
View full report

@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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/experimentation/serializers.py (1)

66-87: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fully normalise state when changing warehouse type.

The transition currently clears credentials but can retain configuration and verification state belonging to the previous backend.

  • api/experimentation/serializers.py#L66-L87: preserve omitted config only when the warehouse type is unchanged; otherwise validate or clear it for the new type.
  • api/experimentation/views.py#L110-L119: reset status and status_detail when the type changes.
  • api/tests/unit/experimentation/test_views.py#L1686-L1709: assert configuration and status reset alongside credential clearing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2a79b4d5-56fe-458d-83ce-a1457dc09356

📥 Commits

Reviewing files that changed from the base of the PR and between 2c000a2 and f7a302b.

📒 Files selected for processing (21)
  • api/app/settings/common.py
  • api/app/settings/test.py
  • api/core/fields.py
  • api/experimentation/metrics.py
  • api/experimentation/migrations/0010_warehouse_connection_credentials.py
  • api/experimentation/migrations/0011_warehouse_connection_status_detail.py
  • api/experimentation/models.py
  • api/experimentation/serializers.py
  • api/experimentation/services.py
  • api/experimentation/types.py
  • api/experimentation/views.py
  • api/experimentation/warehouse_validation.py
  • api/tests/unit/core/test_fields.py
  • api/tests/unit/experimentation/conftest.py
  • api/tests/unit/experimentation/test_models.py
  • api/tests/unit/experimentation/test_services.py
  • api/tests/unit/experimentation/test_views.py
  • api/webhooks/fields.py
  • docs/docs/deployment-self-hosting/observability/_events-catalogue.md
  • docs/docs/deployment-self-hosting/observability/_metrics-catalogue.md
  • openapi.yaml

Comment thread api/core/fields.py
Comment thread api/experimentation/services.py
Comment thread api/experimentation/views.py
Comment thread api/experimentation/warehouse_validation.py Outdated
Comment thread api/experimentation/warehouse_validation.py Outdated
Comment thread api/experimentation/warehouse_validation.py
Comment thread api/tests/unit/experimentation/test_views.py Outdated
Comment thread api/webhooks/fields.py Outdated
Zaimwa9 and others added 6 commits July 20, 2026 14:11
The required-field check ran on the submitted config dict before merging
with the stored config, so any PATCH that omitted account_identifier got
a spurious 400 even when the stored value was valid. Mirrors the
ClickHouse validator pattern: validate on merged, not on incoming.
Use a separate env var for warehouse credential encryption instead
of deriving the Fernet key from Django's SECRET_KEY. Defaults to
SECRET_KEY for backwards compatibility.
@Zaimwa9
Zaimwa9 force-pushed the feat/clickhouse-byo-warehouse-backend branch from c9cb041 to 4b54dc7 Compare July 20, 2026 12:18
@github-actions github-actions Bot added docs Documentation updates feature New feature or request and removed feature New feature or request docs Documentation updates labels Jul 20, 2026

@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: 2

♻️ Duplicate comments (3)
api/core/network.py (1)

20-24: 🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

Bound the DNS lookup with a timeout.

socket.getaddrinfo has no timeout here, so a slow/malicious resolver can block the calling thread indefinitely. This function is invoked synchronously from webhook URL validation and ClickHouse verification (api/experimentation/services.py), so a single hostname can tie up API workers.

🔧 Suggested approach
+import socket
+
+_DNS_TIMEOUT_SECONDS = 2
+
 def is_internal_address(...):
     ...
     try:
-        results = socket.getaddrinfo(hostname, None, socket.AF_UNSPEC)
+        socket.setdefaulttimeout(_DNS_TIMEOUT_SECONDS)
+        results = socket.getaddrinfo(hostname, None, socket.AF_UNSPEC)
     except socket.gaierror:
         return False
+    except socket.timeout:
+        return False

Note: socket.setdefaulttimeout is process-global; a more robust fix would resolve in a bounded worker/thread with its own timeout.

api/experimentation/services.py (2)

821-863: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Synchronous verification still blocks the request thread.

Up to CLICKHOUSE_CONNECT_TIMEOUT_SECONDS + CLICKHOUSE_VERIFY_TIMEOUT_SECONDS (~10s) of network I/O runs inline whenever perform_create/perform_update/test_warehouse_connection call this (see api/experimentation/views.py). A slow or unreachable customer ClickHouse host will block API workers on every write.


831-842: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

DNS-rebinding gap between the internal-address check and the actual connection.

is_internal_address(config["host"], include_shared=True) and Client(config["host"], ...) each independently resolve the hostname. An attacker-controlled domain can return a public address for the check and a private/internal address when the driver connects moments later.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3f042ff0-f231-4251-a399-e7810b67ee56

📥 Commits

Reviewing files that changed from the base of the PR and between c9cb041 and 4b54dc7.

📒 Files selected for processing (23)
  • api/app/settings/common.py
  • api/app/settings/test.py
  • api/core/fields.py
  • api/core/network.py
  • api/experimentation/metrics.py
  • api/experimentation/migrations/0010_warehouse_connection_credentials_and_status_detail.py
  • api/experimentation/models.py
  • api/experimentation/serializers.py
  • api/experimentation/services.py
  • api/experimentation/types.py
  • api/experimentation/views.py
  • api/experimentation/warehouse_validation.py
  • api/tests/unit/core/test_fields.py
  • api/tests/unit/experimentation/conftest.py
  • api/tests/unit/experimentation/test_models.py
  • api/tests/unit/experimentation/test_services.py
  • api/tests/unit/experimentation/test_views.py
  • api/tests/unit/webhooks/test_unit_webhooks.py
  • api/tests/unit/webhooks/test_webhooks_fields.py
  • api/webhooks/fields.py
  • docs/docs/deployment-self-hosting/observability/_events-catalogue.md
  • docs/docs/deployment-self-hosting/observability/_metrics-catalogue.md
  • openapi.yaml

Comment thread api/webhooks/fields.py
Comment thread openapi.yaml
Points at the same Secrets Manager key as DJANGO_SECRET_KEY for now.
Update the ARN when a dedicated secret is provisioned.
@Zaimwa9
Zaimwa9 requested a review from a team as a code owner July 20, 2026 13:07
@Zaimwa9
Zaimwa9 requested review from khvn26 and removed request for a team July 20, 2026 13:07
@github-actions github-actions Bot added infrastructure feature New feature or request and removed feature New feature or request infrastructure docs Documentation updates labels Jul 20, 2026
@Zaimwa9
Zaimwa9 removed the request for review from khvn26 July 20, 2026 13:23
@Zaimwa9
Zaimwa9 merged commit 10a36ab into main Jul 20, 2026
35 checks passed
@Zaimwa9
Zaimwa9 deleted the feat/clickhouse-byo-warehouse-backend branch July 20, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Issue related to the REST API feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants