feat: cloud object stores, credential chains, and config secret injection - #8
Conversation
GCS/Azure object stores + credential chain docs (PR #8): - GCS backend with ADC / Workload Identity Federation - Azure backend (az:// and abfs://) with DefaultAzureCredential chain - S3 default credential chain docs (env -> IMDS -> IRSA) Config secret injection (PR #9): - Secret newtype for credential fields: plain string or { file = "path" } TOML forms, Debug redacted, expose() accessors - K2I_* env var overrides with warnings on invalid numeric/enum values and unrecognized variables (typo detection) - Mutex-serialized env-var tests - docs/kubernetes.md: projected volumes, env injection, Secrets Store CSI Driver, full variable table - azure_access_key uses Secret for consistency Closes #1
e5d1077 to
919957a
Compare
|
@manudiv16 are you ready for us to review this now? |
Yes, the PR is ready |
The in-bucket warehouse prefix was applied inside generate_file_path, but
that path is also what goes to the catalog, the transaction log, and the
read path — all of which resolve it by joining against warehouse_path.
For a warehouse of s3://bucket/warehouse the catalog recorded
s3://bucket/warehouse/warehouse/data/... while the upload landed at
s3://bucket/warehouse/data/..., so every committed file was unreadable.
Apply the prefix in a dedicated storage_path() used only by upload_file,
keeping every externally-visible path warehouse-relative.
Also fixes three defects in the env-override layer:
- K2I_MONITORING_LOG_FORMAT never took effect: the tracing subscriber is
configured before the config is loaded and parsed the TOML directly.
- K2I_RPC_ENABLED read any unrecognized value as false, so =yes silently
disabled an RPC server the TOML had enabled.
- The cloud object-store fields had no overrides at all, including the
Azure-required azure_storage_account_name.
And suppresses the spurious unrecognized-variable warnings for K2I_E2E_*
and the other harness variables that share the engine's environment.
Docs: docs/configuration.md promised ${VAR} shell substitution, which does
not exist — following it would have authenticated with the literal string.
Replaced with the two real mechanisms. README.md, docs/architecture.md and
docs/configuration.md still described GCS/Azure as unwired.
Verified locally: 319 tests (up from 289), clippy -D warnings, fmt,
cargo-audit, cargo-semver-checks, the Docker Kafka integration tests, and
all five docker/e2e suites.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… for real Three gaps remained after the warehouse-prefix fix. Secret redacted Debug but not Serialize, and Config derives Serialize, so any code that dumped or echoed the configuration would emit credentials in the clear. Redact in both, following the secrecy crate's convention that emitting a secret must be a conscious act. A serialized Config no longer round-trips; that trade-off is deliberate and documented, since a visibly broken credential beats a silently leaked one. Cloud warehouse settings that cannot be derived from the path are now checked in Config::validate rather than at writer construction. Azure needs a storage account name that neither URL form carries; previously a long-running ingest reported healthy and failed minutes later on its first flush. Warehouse-path parsing now lives in one place and is shared with the writer, so validation and store construction cannot drift. The prefix fix was only unit-tested. Added container-backed S3 round-trip tests (MinIO) for a prefixed warehouse, a multi-segment prefix, and a bucket-root warehouse, each asserting that warehouse_path joined with the reported path resolves to a real object and that nothing landed at the doubled-prefix or bucket-root locations. Verified they fail against the previous behaviour with the exact production symptom: a 404 on s3://bucket/warehouse/prod/warehouse/prod/data/... Those tests would not have run in CI: every container-backed test is #[ignore = "requires Docker"], and the integration job never passed --include-ignored, so it provisioned Docker and ran no Docker test at all, including the pre-existing Kafka ones. Fixed, and the workflow now also triggers on docs/ and config/ since a test asserts every K2I_* variable is documented. Verified locally: 326 tests, the CI integration command (13 tests incl. 5 container-backed), clippy -D warnings, fmt, cargo-audit, cargo-semver-checks, and all five docker/e2e suites. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thanks @manudiv16 — merged and shipped as v0.3.0 🎉 The GCS/Azure backends, the credential chains, and the 1. The one blocking issue: the warehouse prefix was applied one layer too highYou correctly spotted a real pre-existing bug — on S3, uploads landed at The fix applied the prefix inside
So the prefix got applied twice. For Net effect: every committed file unreadable, on all cloud backends, while the pipeline reports success. The local-filesystem path was unaffected, which is why the existing tests stayed green. Fix ( // generate_file_path() → "data/db/tbl/....parquet" (catalog, txlog, read path)
// storage_path(...) → "warehouse/data/db/tbl/....parquet" (object store only)Worth internalising for next time: the parse-helper unit tests were good, but they tested the helpers in isolation — they couldn't catch a defect in where the helper output was used. That needed a test that composes the pieces. 2. Now covered by a real S3 round-trip testAdded container-backed MinIO tests ( I verified they fail against the original code with the exact production symptom: 3. Smaller fixes in the env-override layer
4.
|
Combined PR merging the cloud object-store work with the config secret-injection work (supersedes #9).
1. GCS / Azure object stores + credential chains
gs://warehouse paths, optionalgcs_bucket_nameoverride andgcs_service_account_path; falls through to Application Default Credentials (GKE Workload Identity Federation,GOOGLE_APPLICATION_CREDENTIALS, gcloud).az://andabfs://container@account.dfs.core.windows.net/forms,azure_storage_account_name(required), optionalazure_container_nameoverride andazure_access_key; falls through toDefaultAzureCredential(env → Managed Identity → Azure CLI).2. Config secret injection (from #9)
Secretnewtype for credential fields — deserializes from a plain string or{ file = "path" }(Kubernetes projected volumes),Debugredacted (Secret(REDACTED)), values read viaexpose(). Coverskafka.security.sasl_username/sasl_password,iceberg.aws_access_key_id/aws_secret_access_key/azure_access_key,iceberg.rest.credential/oauth2_client_id/oauth2_client_secret.K2I_*env var overrides with warnings on invalid numeric/enum values (TOML preserved) and on unrecognizedK2I_*variables (typo detection).docs/kubernetes.md— projectedSecretvolumes, env/secretKeyRefinjection, Secrets Store CSI Driver, full variable table, rotation/visibility notes.Merge resolution notes
example.toml: combined S3 credential-chain docs with{ file = ... }syntax and GCS/Azure sections.writer.rs: kept the IRSA/IMDS comment, usesSecret::expose()at builder call sites.azure_access_keyis aSecretfor consistency with the other credential fields, plusK2I_ICEBERG_AZURE_ACCESS_KEY.Verification
cargo check --workspace --tests— clean, zero warningscargo test --workspace— 289 passed, 0 failedcargo fmt --all --check— cleank2i validate --config config/example.toml— OKK2I_KAFKA_TOPC, validate passes