Skip to content

feat(data-warehouse): implement fly_io import source - #70000

Merged
Gilbert09 merged 6 commits into
masterfrom
posthog-code/implement-fly-io-source
Jul 14, 2026
Merged

feat(data-warehouse): implement fly_io import source#70000
Gilbert09 merged 6 commits into
masterfrom
posthog-code/implement-fly-io-source

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

Fly.io was scaffolded as a data warehouse source but had no sync logic (empty source.py, unreleasedSource=True). Fly.io is a popular developer cloud, and teams want to pull their infrastructure inventory (apps, machines, volumes) into the warehouse to query alongside product data and for fleet auditing.

Changes

Fills in the Fly.io source end to end against the Fly.io Machines API (https://api.machines.dev/v1, Bearer auth):

  • Streams: apps, machines, volumes.
  • Architecture: the standard source.py / settings.py / fly_io.py split. SimpleSource, full-refresh only.
  • Uses org-level aggregate endpoints (GET /apps?org_slug=, GET /orgs/{org_slug}/machines, GET /orgs/{org_slug}/volumes) rather than a per-app fan-out for machines/volumes. Each returns every resource in the org in a single cursor-paginated call and stamps app_name onto each row, so it carries app context with far fewer requests and stays comfortably within Fly.io's per-action rate limits for orgs with many apps.
  • All outbound HTTP goes through make_tracked_session(); tenacity retries on 429/5xx and transient network errors; get_non_retryable_errors() covers 401/403.
  • machines/volumes partition on the stable created_at; apps carries no timestamp so it isn't partitioned. Primary key id (globally unique per Fly.io).
  • Canonical table/column descriptions from the Fly.io docs; lists_tables_without_credentials = True so the public docs render the supported-tables catalog.
  • Ships releaseStatus=alpha and kept behind unreleasedSource=True (not yet verified against a live account end to end).
  • SOURCES.md updated (moved from Scaffolded to Implemented, HTTP / requests / tracked).

Full refresh, not incremental: the org machines/volumes endpoints document an updated_after param, but the API states cursor takes precedence over it (so it could only bound the first page) and it could not be curl-verified to actually filter without a live token. Per the implementing-warehouse-sources guidance, all streams ship full refresh. This is noted in a code comment.

Not included / follow-ups

  • Icon: no frontend/public/services/fly_io.* asset was added — the sanctioned path is Logo.dev with a user-supplied API key, which wasn't available, so no placeholder or hardcoded key was committed. iconPath still points at fly_io.png; the asset needs to be added separately.
  • Docs: the user-facing doc lives in the posthog.com repo, which isn't checked out here. The doc content (following the documenting-warehouse-sources template) needs to land at contents/docs/cdp/sources/fly-io.md with sourceId: FlyIo — matching the docsUrl set on the source. audit_source_docs could not be run here.

How did you test this code?

Automated tests only (no live Fly.io account was available for manual sync verification):

  • tests/test_fly_io.py (transport): org-in-path vs org-in-query URL building, validate_credentials status mapping, _fetch_page retry-vs-raise behavior, cursor pagination (follows next_cursor, terminates, apps stays single-request), and per-endpoint SourceResponse partition/primary-key shape.
  • tests/test_fly_io_source.py (source class): source type, config fields (token is a PASSWORD/secret), full-refresh schemas, credential/pipeline plumbing, canonical-descriptions coverage, documented-tables rendering, and non-retryable-error matching.

Ran the targeted suite (32 passed) plus the registry-wide test_source_categories / config-generator suite. ruff check and ruff format pass on all changed files. pnpm run generate:source-configs could not run in this sandbox (no DB + a missing optional dep during full source import), so the FlyIoSourceConfig entry in generated_configs.py was written to match the generator's deterministic output for two required string fields — CI regenerates it. hogli / ci:preflight weren't available in this environment.

The enum, schema, and generated-config wiring for the source type were already merged on master, so no new Django migration was needed.

Docs update

Doc needs to be added in the posthog.com repo (see follow-ups above).

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Authored with Claude Code. Skills invoked: /implementing-warehouse-sources (workflow, architecture contract, tracked-transport rule), /writing-tests (test value gate), and /documenting-warehouse-sources (doc template).

Key decision: the original research notes assumed no org-level machines/volumes endpoints and a per-app fan-out. Inspecting the live Fly.io OpenAPI spec showed GET /orgs/{org_slug}/machines and .../volumes do exist, carry app_name + created_at/updated_at, and support cursor pagination — so the implementation uses those instead of fan-out (fewer requests, better rate-limit behavior). The org endpoints also document updated_after, but since cursor takes precedence over it and it couldn't be verified against a live token, incremental sync was deliberately left off in favor of full refresh.


Created with PostHog Code

@github-actions

Copy link
Copy Markdown
Contributor

Hey @Gilbert09! 👋

It looks like your git author email on this PR isn't your @posthog.com address (owerstom@gmail.com). Since you're on the PostHog team, it's worth pointing your local git author email at your @posthog.com address. Why it matters:

  • Consistent work identity in git history — internal tooling that attributes commits to team members keys off your @posthog.com address.
  • Keeps team contributions easy to tell apart from external community ones when scanning history.

You can fix it for this repo with:

git config user.email "you@posthog.com"

Or set it globally with git config --global user.email "you@posthog.com". No need to redo this PR — just a nudge for next time. 🙂

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team July 10, 2026 12:58
@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Security Review

The org slug is validated through an encoded query parameter but later used raw in path-based endpoints. The host is fixed, but reserved path characters can make machines and volumes requests target a different API path than the validated org.

Reviews (1): Last reviewed commit: "feat(data-warehouse): implement fly_io i..." | Re-trigger Greptile

Comment thread products/warehouse_sources/backend/temporal/data_imports/sources/fly_io/fly_io.py Outdated
Comment thread products/warehouse_sources/backend/temporal/data_imports/sources/fly_io/fly_io.py Outdated
@trunk-io

trunk-io Bot commented Jul 10, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

@veria-ai

veria-ai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 2 · PR risk: 0/10

@estefaniarabadan estefaniarabadan left a comment

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.

Isolated new data-warehouse source scaffold — self-contained under its own source directory with only the expected shared-registry additions (SOURCES.md, generated_configs.py, icon), tests included, CI green. Reviewed as part of a batch. LGTM 👍

@Gilbert09
Gilbert09 force-pushed the posthog-code/implement-fly-io-source branch from d1421aa to 0936fc5 Compare July 14, 2026 10:18
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Backend coverage — 98.0% of changed backend lines covered — 4 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ████████████████████ 98.0% (371 / 375)

File Patch Uncovered changed lines
products/warehouse_sources/backend/temporal/data_imports/sources/fly_io/fly_io.py 96.2% 190–191, 234, 239

🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 29350189059 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
warehouse_sources ███████████████████░ 96.1% 215,006 / 223,695

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

Fill in the scaffolded Fly.io data warehouse source end to end: syncs apps,
machines, and volumes for an organization from the Fly.io Machines API.

Uses the org-level aggregate endpoints (GET /apps, GET /orgs/{org_slug}/machines,
GET /orgs/{org_slug}/volumes) instead of per-app fan-out — each returns every
resource in the org in one cursor-paginated call and stamps app_name onto each
row, which is fewer requests and stays within Fly.io's per-action rate limits.

SimpleSource, full refresh only (no verified server-side time filter), routed
through make_tracked_session. Ships alpha and behind unreleasedSource.

Generated-By: PostHog Code
Task-Id: 2af164a9-60a3-4a93-a3a1-e1226e106277
Address review feedback on the fly_io source:

- Percent-encode organization_slug in path-scoped URLs so a reserved
  character can't retarget requests to a different API path than the one
  credential validation checked.
- Raise on an unexpected top-level response shape instead of silently
  syncing zero rows.
- Add connection_host_fields = ["organization_slug"] so changing the org
  re-requires the token, preventing a preserved token from being
  retargeted at another org.

Also fix two mypy errors in the tests (HTTPError needs response=, and a
var-annotation for pages).

Generated-By: PostHog Code
Task-Id: 12e6e049-0cc3-48ca-8473-3c8e16e2c86b
@Gilbert09
Gilbert09 force-pushed the posthog-code/implement-fly-io-source branch from 0936fc5 to 4c2ed0a Compare July 14, 2026 11:35
Gilbert09 and others added 2 commits July 14, 2026 12:35
A Fly machine's config can embed deployment secrets (env vars, per-process env,
and inline file contents in config.files[].raw_value). Reduce each machine row to
a safe operational config allowlist before yielding, and exclude the machines
stream from HTTP sample capture, so secrets reach neither the warehouse nor the
sample-capture pipeline.

Generated-By: PostHog Code
Task-Id: b5ea24fe-da19-47b2-ba5c-be66bcb7579f
The machine config allowlist kept `metadata`, `services`, and `checks`
verbatim. Fly metadata is a free-form user key/value map, and service and
health-check definitions can carry request headers, so a value like
`metadata.api_token` or a check `Authorization` header still reached the
warehouse where any project member could query it.

Keep only Fly's platform-set metadata keys and recursively drop every nested
`headers` map from the config before a machine row is yielded. Update the
canonical `config` description and add tests for both vectors.

Generated-By: PostHog Code
Task-Id: 18ff2c05-99a0-40c6-8bf3-33c01102248c
Remove unreleasedSource=True so the finished source is visible in the
connector catalog, and drop any test asserting the hidden state.
@Gilbert09
Gilbert09 merged commit 182dd44 into master Jul 14, 2026
213 of 214 checks passed
@Gilbert09
Gilbert09 deleted the posthog-code/implement-fly-io-source branch July 14, 2026 18:33
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 14, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-14 19:03 UTC Run
prod-us ✅ Deployed 2026-07-14 19:24 UTC Run
prod-eu ✅ Deployed 2026-07-14 20:00 UTC Run

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