ops/iceberg.sh: read-only Iceberg lake queries across all environments #23
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Jinemi/thermograph#23
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/iceberg-ops"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements
infra/ops/ICEBERG-HANDOFF.md:infra/ops/iceberg.sh, a sibling ofdbq.shwith the same env-keyed dispatch, plus the README rationale section. Base isfeature/db-query-path(PR #18), which carries the handoff and thedbq.shreference this mirrors.Decisions (as the handoff demands)
<warehouse>/<table>/metadata/.iceberg.shresolves that at call time (numeric metadata version, newest wins — handles bothvN.metadata.jsonand pyiceberg's0000N-<uuid>.metadata.json) and exposes one view per table directory. Nothing to host, nothing to expose, and reads automatically track a table that is still being loaded.docker runper query; the image is built on the target host on first use from a Dockerfile embedded in the script (extensions installed at build time, so queries need no downloads). The image tag is a hash of the Dockerfile, so editing the script rolls every host forward on its next call.s3://era5-thermograph/iceberg— the single shared bucket, one warehouse for every environment. Environments are not isolated by prefix/namespace/bucket; they see the same lake and differ only in where the engine container runs. Noteera5_daily's data files live underera5/daily/(adopted in place byadd_files); only its metadata is undericeberg/.SET allowed_directories=['s3://era5-thermograph/iceberg/', 's3://era5-thermograph/era5/']; SET enable_external_access=false; SET lock_configuration=true;. Writes outside the lake prefixes (notablybackups/), all local-file access, and any attempt to lift the lockdown are refused by DuckDB itself (pasted below). Known gap: see the operator ask.docker runon the dev machine; beta/prod =docker runon the box over SSH, same dispatch asdbq.sh. No published ports, no tunnels, nothing persistent, no exec into app containers, no container names resolved.Credentials resolve at call time: the target host's
/etc/thermograph.env(THERMOGRAPH_LAKE_S3_*) wins if present (it is not rendered on either box today); otherwise the calling machine decrypts the SOPS vault and feeds the two values to the remote shell over stdin — never argv (so never visible inps), never a file on the target. This branch's vault predates theTHERMOGRAPH_LAKE_S3_*keys (they are indev's vault); until the chain merges, pointTHERMOGRAPH_LAKE_VAULTat a dev-branch vault copy or export the two keys directly.Acceptance
era5_daily, mid-load by the lake builder — counts below are snapshots of a moving table, which is the call-time metadata resolution working as intended (4,536,288 → 27,217,728 → 49,899,168 → 163,306,368 across the test session):Early acceptance ran against a throwaway 3-row table
iceberg/_ops_test/(created whileera5_dailydid not exist yet); it has been deleted from the warehouse since.The refusal is path-based and fires in the engine before any request is made (verified: identical refusal with dummy credentials). DDL against the lake is impossible by construction — no writable catalog is attached and the DuckDB iceberg extension has no write path here;
CREATE TABLEonly ever creates an in-memory scratch table that dies with the container.COPY TOinside the two lake prefixes is not refused:This is stated in the README as a known gap rather than silently accepted — see the operator ask below.
No new exposure.
sudo ss -ltnpcaptured on prod and beta before and after the full test session:diffis empty on both. No ufw changes, no Swarm service orEndpointSpecchanges, no new units — the only host-side artifact is the local engine image (thermograph-iceberg:<hash>), which listens on nothing.Non-interactive; flags and stdin pass through.
Prod resolution survives redeploys — trivially: nothing execs into app containers and no container names exist to resolve; the engine is an ephemeral container created per query.
README updated with usage and the why, same shape as the Postgres section.
Not demonstrable now, stated explicitly:
permission denied-style refusal of in-prefix writes (the Postgresthermograph_robar) — blocked on the scoped read-only keypair below.THERMOGRAPH_LAKE_S3_*in/etc/thermograph.env) — the deploy renderer does not put lake keys on the hosts yet, so all runs used the caller-side sops path.Operator asks
era5-thermograph(the object-storage equivalent ofthermograph_ro). With the current single RW keypair, read-only inside the lake prefixes is enforced convention-plus-lockdown, not identity. Once it exists, render it asTHERMOGRAPH_LAKE_S3_*on the hosts or swap it into the vault —iceberg.shneeds no code change.render-secrets.shrenderTHERMOGRAPH_LAKE_S3_*into/etc/thermograph.envon beta/prod, so remote queries stop depending on a caller-side sops decrypt.Shellcheck-clean (relevant to the open shell-lint CI PR).