Feature spec: Dynamic Bicep Extensions Served by the Radius Control Plane#11907
Feature spec: Dynamic Bicep Extensions Served by the Radius Control Plane#11907zachcasper wants to merge 4 commits into
Conversation
Signed-off-by: Zach Casper <zachcasper@microsoft.com>
There was a problem hiding this comment.
Pull request overview
Adds a new design note proposing a shift from release-time/manual publishing of Radius Bicep extensions to a control-plane-served, on-demand extension artifact that the rad CLI downloads to a workstation-global .tgz and references via a single radius alias.
Changes:
- Introduces a proposed control-plane API that generates a Bicep extension artifact from the currently registered resource types.
- Proposes new/updated CLI behaviors (
rad resource-type update, kubecontext-awarerad init, and implicit refresh duringrad deploy/rad run) to keep the local extension current. - Proposes consolidating the four
radius*aliases into a singleradiusalias and removingrad bicep publish-extension.
Signed-off-by: Zach Casper <zachcasper@microsoft.com>
|
|
||
| Developer (steady state, type set changes server-side): | ||
|
|
||
| 1. Run `rad resource-type update` to refresh now, or just run the next `rad deploy` / `rad run`, which refresh `~/.rad/bicep-extension.tgz` as a side effect. |
There was a problem hiding this comment.
I like the idea of having a separate command that imports the server-side type library to developer workstations, but I think it should be separate from the rad resource-type CRUDL command.
rad resource-type update implies that the developer is updating the type schema on the server. In an enterprise setup, platform engineers would typically use RBAC to restrict who can create or update resource type definitions.
I think it would be better to call this something like rad extension update or rad extension import implying what the command is actually doing - pulling the extension from the Radius control plane and updating the local extension configuration on the developer machine.
It also maps to the existing extension radius experience in application definition, so the mental model stays the same .
There was a problem hiding this comment.
Yes, I debated many options here. I'm open to suggestions. Here is what was considered:
rad resource-type update — command-naming debate
Options considered
| # | Candidate | Pros | Cons |
|---|---|---|---|
| 1 | rad bicep extension sync (original) |
Most precise; explicitly names artifact + namespace. | Verbose. Only makes sense if more rad bicep extension <verb> siblings (show, path) are likely. |
| 2 | rad bicep sync |
Shorter; discoverable next to other rad bicep … commands. |
Still ties the verb to the artifact format. Hides that the catalog (not the file) is the real input. |
| 3 | rad sync |
Reads cleanly; leaves room for future syncs. | Vague; future-proofing was speculative (YAGNI). No concrete second target identified. |
| 4 | rad resource-type sync |
Names what is synced (the catalog), not the file format. Mirrors rad resource-type create. Leaves room for non-Bicep outputs (Terraform schema, LSP index) without renaming. |
sync has weak prior art for schema/catalog caches — the ecosystem (helm repo update, apt update, brew update, terraform init -upgrade) overwhelmingly uses update. |
| 5 | rad resource-type refresh |
Semantically precise; no collision. | Less familiar verb than sync or update in modern CLIs. |
| 6 | rad extension update |
Short; uses the established update verb. |
"extension" collides hard with Bicep's own vocabulary (extension keyword, extensions block in bicepconfig.json, "extension types"); ambiguous with a future rad plugin system. Names a file artifact rather than the catalog. |
| 7 | rad resource-type update (selected) |
Names the catalog, not the artifact. Mirrors rad resource-type create (symmetric: platform engineer creates server-side, developer updates client-side). Uses update — the established verb in this ecosystem for "pull fresh catalog from server." Singular form matches existing CLI conventions. Preserves optionality for future outputs (Terraform, LSP) under the same noun. |
Verb collides loosely with the idea of "modify a resource type" — mitigated because schema edits happen via rad resource-type create -f manifest.yaml in practice, not via an update subcommand. |
Why option 7 won
- Catalog, not artifact. The thing being kept fresh is the resource-type catalog. The
.tgzis one current serialization; in the future the same command can emit other formats. - Symmetric with
rad resource-type create. Platform engineer creates types; developer updates the local mirror. Same noun on both sides. updateis the dominant verb in the ecosystem.helm repo update,apt update,brew update,terraform init -upgrade.syncis mostly used for filesystem mirrors (rclone sync,aws s3 sync) and fork-to-upstream (gh repo sync), neither of which matches our shape.- Avoids the overloaded word "extension." Reserves that term for Bicep's own meaning and for a future
radplugin namespace. - Lets
rad initabsorb the first-run case. Becauserad initbecame kubecontext-aware in the same change,rad resource-type updateis only needed for explicit refresh — the everyday developer never has to type it, which lowers the cost of choosing a slightly longer name.
|
|
||
| ## Key dependencies and risks | ||
|
|
||
| * **Dependency – Bicep CLI local-file extension references and parent-directory `bicepconfig.json` resolution.** The integration point is Bicep's existing support for a local-file `extensions` entry plus its existing parent-walk to find `bicepconfig.json`. Bicep's walk reaches `$HOME` for any `.bicep` source under `$HOME`, but does not search `~/.bicep/` or XDG paths. No change to the upstream Bicep CLI or Bicep VS Code extension is required. Risk: if Bicep changes its parent-walk semantics, every workstation breaks. Mitigation: this is documented Bicep behavior; pin Bicep version testing in CI. |
There was a problem hiding this comment.
As I understand here - bicepconfig.json will still be maintained at the repo level and the extensions in .rad/ will be refreshed by rad init or an explicit command to update extensions locally. We maintain the bicepconfig.json outside of .rad/ because of Bicep's config resolution?
There was a problem hiding this comment.
No, rad init will now write bicepconfig.json in the users' home directory. The contents will be:
{
"extensions": {
"radius": ".rad/bicep-extension.tgz"
}
}
The downloaded Bicep extension will be ~/.rad/bicep-extension.tgz.
There was a problem hiding this comment.
Oh I was thinking about this from Repo Radius perspective. How this would be created and maintained for repositories with radius installed? May be some clarity around that would help.
| * **Dependency – Authentication path `rad` already uses for control-plane calls.** The new extension API reuses the same auth. The Bicep CLI itself never authenticates to the control plane (it only sees the local file). | ||
| * **Risk – Stale `~/.rad/bicep-extension.tgz` on a workstation that has not run `rad` since the control plane changed.** Mitigated by routine `rad` commands (`deploy`, `run`) refreshing as a side-effect and by `rad resource-type update` being a single command. | ||
| * **Risk – `.bicep` files outside `$HOME` are not covered automatically.** Mitigated by documentation; advanced users place a `bicepconfig.json` somewhere in their source tree by hand. | ||
| * **Risk – Devcontainer / Codespaces / remote-SSH workspaces have a different `$HOME`.** `rad init` must be re-run inside the container; the artifact is per-environment. Documented limitation. |
There was a problem hiding this comment.
Would this be a risk if bicepconfig.json is maintained at per repo level ?
There was a problem hiding this comment.
If a is running in a dev container (or another workstation for that matter), they will not have the Bicep extension in their environment. This is obvious and a somewhat silly risk to be documented.
Description
Add a design document proposing that the Radius control plane become the single source of truth for Bicep extensions, replacing the current dual-pipeline model (release-time ACR publishes + manual
rad bicep publish-extension).Under the proposal:
radalready uses.rad resource-type updateCLI command fetches that artifact and writes it to~/.rad/bicep-extension.tgz.rad deployandrad runrefresh it as a side effect, closing the staleness window in the normal authoring → deploy loop.rad initbecomes kubecontext-aware: it installs Radius if the current kubecontext has none, otherwise skips the install step. In both cases it writes~/.rad/bicep-extension.tgzand creates or merges~/bicepconfig.jsonwith a singleextensions.radiusentry pointing at that file. Bicep's existing parent-directorybicepconfig.jsonresolution covers every.bicepfile under$HOMEautomatically.radius*aliases (radius,radiusCompute,radiusData,radiusSecurity) are consolidated into a singleradiusalias; legacy entries in an existing~/bicepconfig.jsonare removed byrad initas an intentional forcing function.rad bicep publish-extensionis removed from the CLI. Previously-published artifacts onbiceptypes.azurecr.ioremain readable for users on olderradversions.The document records the four other delivery models that were evaluated (long-running proxy daemon, Ingress + anonymous OCI endpoint, Kubernetes aggregation layer, and control-plane-pushes-to-customer-registry) and why each was rejected for v1.
No code changes; design document only.
Type of change
Fixes: #TBD
Contributor checklist
eng/design-notes/in this repository, if new APIs are being introduced.