feat: support newer provider plugin-protocol features via targetVersions (RFC-04)#296
feat: support newer provider plugin-protocol features via targetVersions (RFC-04)#296so0k wants to merge 10 commits into
Conversation
Merged sweep observations + source-verified CLI serializer history for the provider plugin-protocol capability families (provider functions, ephemeral resources, write-only attributes, resource identity, list resources, actions, state stores) across Terraform 1.5.7-1.15.x and OpenTofu 1.6.0-1.12.x. Source dataset for the providerFeatureConstraints map in packages/cdktn. Sweep tooling, fixtures, report and proposal live in open-constructs/cdktn-planning RFCS/04-provider-feature-availability (same split as tools/generate-function-bindings/function-availability). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New public base class for ephemeral resources (Terraform >=1.10 / OpenTofu >=1.11), synthesizing into the top-level `ephemeral` key of cdk.tf.json and an `ephemeral "type" "name"` block in HCL output. References resolve as ephemeral.<type>.<id>.<attr>. Ephemeral blocks support no provisioners/connection and are not importable/movable, so those APIs are omitted. The constructor registers ValidateFeatureTargetSupport (from #269) unconditionally: synth fails when the project's declared targetVersions admit releases without ephemeral resources, naming the offending range. providerFeatureConstraints is the hand-maintained per-product constraint map for the provider-protocol feature families, sourced from tools/provider-feature-availability/features-matrix.json; internal by design (generated bindings reach it by extending base classes). Part of RFC-04 (provider feature availability), rollout item 2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ions
Phase 0 of RFC-04 — schema acquisition correctness:
- Type the newer providers-schema sections in @cdktn/commons:
ephemeral_resource_schemas, functions (FunctionSignature),
resource_identity_schemas, and the write_only attribute flag.
List/action/state-store sections stay untyped (nothing consumes them;
they pass through the in-place sanitizer untouched).
- Extend sanitizeProviderSchema's attribute-doubling walk to
ephemeral_resource_schemas.
- Stamp fetched schemas with the fetching CLI ({cli_name, cli_version},
best-effort) — emission of the new sections is a property of the
fetching CLI, not just the provider.
- Make the experimental schema cache honest: the cache key now includes
the fetching CLI product+minor, so upgrading the CLI re-fetches richer
schemas instead of serving section-less ones forever.
- Warn at fetch time when the CLI is the bottleneck: if targetVersions
admit versions whose sections the fetching binary structurally cannot
emit, log which features will be missing and the minimum CLI versions
that would emit them. (Nothing passes targetVersions yet; the CLI
thread-through is a follow-up rollout item.)
Part of RFC-04 (provider feature availability), rollout item 3.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Third schema family in buildResourceModels, mirroring the data-source pipeline with an ephemeral_ type prefix: random_password generates EphemeralRandomPassword in ephemeral-random-password/, extending cdktn.TerraformEphemeralResource (which enforces targetVersions at synth). Generated Config interfaces extend TerraformEphemeralMetaArguments (no provisioners/connection) and no generateConfigForImport is emitted — ephemeral resources are not importable. Ephemeral models are appended after resources and data sources so class-name dedup stays order-stable (zero churn in existing snapshots). Snapshot-tested against a real terraform 1.15.6 providers-schema fragment of hashicorp/random (ephemeral + managed coexistence). Part of RFC-04 (provider feature availability), rollout item 4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Providers shipping schema `functions` now get a per-provider
provider-functions namespace: TimeProviderFunctions.rfc3339Parse(ts)
renders ${provider::time::rfc3339_parse(...)}. Methods default the
provider:: namespace to the registry short name and take an optional
providerLocalName override (local names change the namespace, aliases
do not). Type mapping follows the Fn bindings rules, extended with an
object/map/dynamic -> any branch (the real time provider functions all
return objects).
Runtime flows through one public jsii chokepoint,
TerraformProviderFunction.invoke, which records usage in a dedicated
registry; TerraformStack now registers
ValidateProviderFunctionTargetSupport unconditionally, so synth fails
when provider functions are used and the declared targetVersions admit
releases without them (terraform >=1.8.0 / opentofu >=1.7.0 — OpenTofu
language support predates its schema emission, hence the asymmetry with
the fetch-time boundary).
Snapshot-tested against the real terraform 1.15.6 hashicorp/time
schema fragment plus a synthetic fixture covering variadic parameters,
primitive/list returns and reserved parameter names.
Part of RFC-04 (provider feature availability), rollout item 5.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…idate usage write_only attributes (e.g. the vault *_wo pairs) now flow into AttributeModel. The setter stays; the getter is emitted @deprecated with the protocol contract spelled out — providers never persist write-only values, so every read of that token is null and the state-backed getter is a trap. Removing it outright is JSII-breaking for regenerated/prebuilt providers, so: deprecate now, remove at the next prebuilt major. Setting a write-only attribute (via setter or constructor config, which bypasses the setter) calls the new protected TerraformResource.registerProviderFeatureUsage hook, which registers ValidateFeatureTargetSupport once per construct: synth fails when targetVersions admit terraform <1.11 / opentofu <1.11 with the standard upgrade-or-narrow-targets message. Generated bindings reach the validation machinery only by extending base classes, per the RFC guiding decision. Struct-level (nested) write-only attributes get the deprecated getter but no usage registration (no construct node) — accepted v1 limitation. Snapshot-tested against the real terraform 1.15.6 hashicorp/vault vault_alicloud_secret_backend fragment (secret_key_wo + secret_key_wo_version). Part of RFC-04 (provider feature availability), rollout item 6. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GetOptions gains targetVersions, threaded from cdktf.json at all three creation sites (get, provider upgrade, runGetInDir) into ConstructsMaker and down to readSchema — driving the fetch-time warning when the fetching CLI structurally cannot emit schema sections the declared targets admit. constraints.json is additionally stamped with the targets and the fetching CLI identity for diagnostics and cache debugging. Both fields are diagnostics-only: targetVersions does not affect the generated surface (full surface always generated, narrowing happens at synth), so filterAlreadyGenerated's staleness check deliberately ignores them and legacy constraints.json files stay valid. Part of RFC-04 (provider feature availability), rollout item 7 — the last item of the RFC's PR split. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Draft PR #296 review notes / verification resultsI built and published a small reusable demo harness against this PR head so the new provider-protocol features can be exercised with real generated AWS bindings: https://github.com/sakul-learning/cdktn-provider-features-demo PR head used for the run: Verification performedUsing the PR-head packages directly, not npmjs:
The AWS provider schema from Terraform
Generated bindings confirmed:
OpenTofu UX / integration notes from the demoThese are non-blocking, but important feedback from trying to use the generated bindings in a real app:
Non-blocking findings / suggested follow-ups
Overall: the core provider-function and ephemeral-resource generated bindings worked in real synth/plan flows for Terraform and OpenTofu once the UX issues above were accounted for. The largest current generated-coverage gap is that Terraform exposes AWS list resources, actions, and resource identities, but I did not see first-class generated bindings for those schema sections yet. |
… path CdktfConfig.targetVersions (used by runGetInDir) returned the raw cdktf.json value, bypassing the parseConfig validation the main get handler goes through — and a malformed range then made semver.intersects throw inside the fetch-time emission check, crashing cdktn get with a raw stack trace. Two layers: the getter now validates via commons validateTargetVersions (warn + ignore invalid targets; generation proceeds, only the warning/ stamp lose them), and checkSchemaEmissionGapFamilies treats an invalid target range as not-wanted instead of throwing — a best-effort diagnostics path must never break get. Found in draft-PR verification (#296 review notes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Terraform ephemeral blocks support only precondition/postcondition in lifecycle — createBeforeDestroy, preventDestroy, ignoreChanges and replaceTriggeredBy are state-oriented concepts that do not apply to stateless ephemeral resources. New TerraformEphemeralResourceLifecycle replaces the full TerraformResourceLifecycle on the ephemeral API; narrowing later would be jsii-breaking, so it lands before first release. Found in draft-PR verification (#296 review notes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…generated JSDoc Calling a provider-defined function inside the configuration block of the same provider asks Terraform/OpenTofu to evaluate the function while that provider is still being configured — a self-referential cycle. Generated method JSDoc and TerraformProviderFunction.invoke now carry the caveat. Also documents why write-only usage registration deliberately skips ephemeral resources: write-only is a state concept, ephemeral resources have no state, and no provider schema in the RFC-04 sweep combines the two. From draft-PR verification (#296 review notes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| const SELF_REFERENCE_CYCLE_JSDOC = | ||
| "Note: provider-defined functions are evaluated by the provider itself — do not call this inside the configuration of the same provider (Terraform reports a self-referential cycle)."; | ||
|
|
There was a problem hiding this comment.
not sure if useful to have this in every JSDocs - it's a very limited edge case
Implements RFC-04 — provider feature availability: support the newer provider plugin-protocol capability families with
targetVersions-aware codegen and synth-time validation. Proposal, dataset, sweep tooling and interactive report live in open-constructs/cdktn-planning →RFCS/04-provider-feature-availability/(only the merged matrix is vendored here, matching the function-availability split).Builds directly on the #269 foundation (
targetVersions+ValidateFeatureTargetSupport) and the #268 usage-registry pattern.Guiding decision
Generate the full surface the schema offers; narrow per project at synth time via
targetVersions. Generation-time filtering would fork the generated API by project configuration — impossible for prebuilt providers and hostile to caching. Constraints baked into the validations are the per-product>=ranges from the sweep dataset.Review guide (one commit per RFC rollout item)
chore: vendor provider-feature availability matrix— dataset digest + README only, no behavior change.feat(lib): TerraformEphemeralResource— new public base class synthesizing to the top-levelephemeralkey (JSON + HCL renderer), refs asephemeral.<type>.<id>.<attr>, no provisioners/connection/import/move. Constructor registers the target-version validation unconditionally (new API surface — only fires on use, no feature flag per RFC). InternalproviderFeatureConstraintsmap sourced from the vendored matrix.feat(provider-generator): acquire newer provider-protocol schema sections(Phase 0, a bugfix on its own) — commons types forfunctions/ephemeral_resource_schemas/resource_identity_schemas/write_only; sanitizer walks ephemeral schemas; fetched schemas stamped with the fetching CLI; cache key now includes CLI product+minor (a schema fetched once with an old CLI no longer poisons every later generation); fetch-time warning when the fetching binary structurally cannot emit sections the targets admit.feat(provider-generator): generate ephemeral resource bindings— third schema family mirroring thedata_pipeline (EphemeralRandomPasswordinephemeral-random-password/), config extendsTerraformEphemeralMetaArguments, nogenerateConfigForImport. Zero churn in existing snapshots (ephemeral models append after resources/data sources; class-name dedup is order-dependent).feat(provider-generator): generate provider-defined function bindings—TimeProviderFunctions.rfc3339Parse(ts)→${provider::time::rfc3339_parse(...)}; namespace defaults to the registry short name with aproviderLocalNameoverride (local names change the namespace, aliases don't). Runtime flows through one public jsii chokepoint (TerraformProviderFunction.invoke) feeding a usage registry;TerraformStackvalidates usage againstterraform >=1.8.0/opentofu >=1.7.0— note the deliberate asymmetry: OpenTofu language support (1.7.0) predates its schema emission (1.8.0), so generation and validation use different boundaries.feat(provider-generator): deprecate write-only attribute getters, validate usage— providers never persist write-only values (every read isnullby protocol contract), so the state-backed getter is a trap: emitted@deprecatednow, removal rides the next prebuilt major (JSII-breaking otherwise). Setting one (setter or constructor config) registers usage via a new protectedTerraformResource.registerProviderFeatureUsagehook — generated code reaches the validation machinery only by extending base classes.feat(cli): thread targetVersions into cdktn get—GetOptions.targetVersions→ConstructsMaker→readSchema, driving the Phase 0 fetch-time warning;constraints.jsongains diagnostic stamps (targetVersions, fetchingcli) without affecting thefilterAlreadyGeneratedstaleness logic (targets don't change codegen output, so they must not force regeneration).Test coverage
terraform providers schema -jsonfragments from the sweep (random ephemeral, time functions incl. object returns, vault*_wo), plus a synthetic fixture for variadic/reserved-name mapping branches. Zero churn in pre-existing snapshots.validations.test.ts(admit/exclude per product, hint text, the OpenTofu 1.7.0 asymmetry, registry resets).Known/deferred
matchers.test.ts › toPlanSuccessfullyfails in this environment before and after these changes (downloads the real docker provider; verified pre-existing via stash on the unmodified tree).hcl2cdkconversion ofephemeral {}blocks; ephemeral entry in the edge-provider schema (cross-language compile coverage); atypescript/synth-app-style integration test gated on CI Terraform >= 1.10 (repo pins 1.7.5 via mise).🤖 Generated with Claude Code
Review findings addressed (from the demo-harness verification comment)
targetVersionsvalidation bypass:CdktfConfig.targetVersions(therunGetInDirpath) now validates via commonsvalidateTargetVersions(warn + ignore), and the emission-gap check treats invalid ranges as not-wanted instead of throwing — a malformed range previously crashedcdktn getviasemver.intersects.TerraformEphemeralResourceLifecycle(precondition/postconditiononly) replaces the full managed-resource lifecycle on the ephemeral API, before it ships as jsii surface.TerraformProviderFunction.invokenow warn against calling a provider's functions inside that same provider's configuration block.write_onlyinside nested blocks gets the deprecated getter but skips usage registration; deep config scanning deserves its own PR (a miss degrades to the plan-time error, not silent breakage).Fn.ephemeralasnull+sensitiveon outputs is Terraform semantics (docs candidate); list resources / actions / resource identity codegen is the RFC Phase 4 deferral noted above.