Support optional typed secret paths and Fastly store mappings - #344
Support optional typed secret paths and Fastly store mappings#344ChristianPavilonis wants to merge 2 commits into
Conversation
2b24957 to
0d6ebf9
Compare
prk-Jr
left a comment
There was a problem hiding this comment.
PR Review
Summary
Three separable changes: an OptionalField secret-path segment threaded through core + CLI, env_config_from_runtime_dictionary made public, and Fastly provision/staged-deploy persistence of non-default logical→physical store-name mappings. The secret-path half is clean and symmetric — CLI and runtime walkers grew matching arms with matching tests. The Fastly store-mapping half introduces a new write into a store the ACTIVE version reads, sourced from ambient process env, with no reconciliation of stale entries; that is where the blocking findings are.
All four CI gates pass locally on 0d6ebf9b.
😃 Praise
collect_secret_leafextraction (crates/edgezero-cli/src/config.rs:1636) avoids a fourth copy of the leaf-resolution block, and threadingoptional_segmentseparately fromfield.optionalkeeps "this segment is optional" distinct from "this leaf isOption<String>" — which makes the required-Fieldarm provably unchanged.- CLI/runtime symmetry maintained exactly:
collect_secret_leavesandresolve_secret_fieldgrew the same two arms in the same order, each with a test on both sides. That lockstep is the hard part of this subsystem. staging_entries_from_productionkept pure, withoverlay_runtime_store_name_entriesas a separate testable function.
Findings
Blocking
- 🔧
provisionrewrites live production store resolution from ambient shell env —crates/edgezero-adapter-fastly/src/cli.rs:598.EnvConfig::from_env()(crates/edgezero-cli/src/provision.rs:96) +resolve_kind(provision.rs:145) mean anyEDGEZERO__STORES__*__NAMEin the operator's shell is written intoedgezero_runtime_env, read by the ACTIVE version. New behavior: before this PRprovisioncreated the store but wrote no entries into it. - 🔧 Stale
__NAMEmappings never removed —cli.rs:3640. Dropping an override leaves the old entry live; production has no equivalent of the staging mirror's upsert-then-delete reconciliation. - 🔧 Override validation weaker than the runtime's, fails open —
cli.rs:3487. Control characters pass the CLI check but are silently rejected byis_blank_or_control(crates/edgezero-core/src/env_config.rs:144), so the runtime falls back to the logical id — wrong store, no diagnostic. - ❓
OptionalFieldhas no derive path —crates/edgezero-core/src/app_config.rs:41. The macro only emitsField/ArrayEach, andnested_child_type(crates/edgezero-macros/src/app_config.rs:453) doesn't unwrapOption<T>, so#[app_config(nested)] x: Option<Inner>fails theAppConfigRootbound. Reachable only from a hand-writtenimpl AppConfigMeta— is that the intended surface? - 🔧 Add
#[non_exhaustive]toSecretPathSegment—app_config.rs:35-42. Public enum, exhaustively matchable; this PR is itself the breaking change. - 🔧 Docs now contradict the code —
docs/guide/configuration.md:393-401still states thatOption<Inner>is unsupported and that "only the leaf's ownOption<String>is skippable. A missing ornullintermediate object/array … is aConfigOutOfDateerror, not a silent skip — bothconfig validateand the runtime reject it." Both halves are now false forOptionalField. (No inline comment — the file is untouched by this PR.)
Non-blocking
- 🤔
env::vars()panics on non-UTF-8 environment —cli.rs:3586. - ♻️ Hoist that env read into the caller so
mirror_production_to_stagingstays pure and end-to-end testable — currently the only untested line in the new staging path. - 🤔
is_runtime_store_name_key(cli.rs:3465) accepts undeclared store ids, so a typo'd override silently no-ops. - ⛏
#[inline]onenv_config_from_runtime_dictionary(crates/edgezero-adapter-fastly/src/lib.rs:184) buys nothing. - ⛏ The
```ignoredoc example (lib.rs:177-182) references a non-existentMyApp::stores()and can never be caught rotting. - 🌱 Test gaps: no coverage of the non-dry-run provision write path, nor of
is_runtime_store_name_keyrejecting near-miss keys (…__A__B__NAME,…__NAME__EXTRA, lowercase kind). The dry-run test atcli.rs:6753covers the happy shape only.
CI Status
Run locally against 0d6ebf9b:
cargo fmt --all -- --check: PASScargo clippy --workspace --all-targets --all-features -- -D warnings: PASScargo test --workspace --all-targets: PASScargo check --workspace --all-targets --features "fastly cloudflare spin": PASS
The three unchecked boxes in the PR description's test plan (cargo test --workspace --all-targets, the feature check, and examples/app-demo) — the first two pass; examples/app-demo was not run.
b47cd19 to
d4dc05d
Compare
Summary
This PR is stacked on #316 because Trusted Server currently pins that branch.
Changes
edgezero-coreSecretPathSegment::OptionalFieldand teach runtime extraction to skip absent or null optional containers atomically.edgezero-cliedgezero_runtime_env.edgezero-adapter-fastlyenv_config_from_runtime_dictionary, persist store mappings in local Fastly manifests, and overlay validated process mappings into the staged runtime-env twin before relinking.Related
Test plan
cargo test --workspace --all-targetscargo clippy --workspace --all-targets --all-features -- -D warningscargo fmt --all -- --checkcargo check --workspace --all-targets --features "fastly cloudflare spin"wasm32-wasip1(Fastly)examples/app-demoworkspace:cd examples/app-demo && cargo test --workspace --all-targetscd docs && npm run lint && npm run format && npm run buildedgezero serve --adapter axumcargo test -p edgezero-core,cargo test -p edgezero-cli,cargo test -p edgezero-macros, native Fastly adapter tests, Fastly WASM checks, live staged runtime mapping verification, and package-specific Clippy checks with-D warningsChecklist
{id}syntax (not:id) — no route changesedgezero_core(nothttpcrate)