feat(schema): generate config JSON Schema from the provider registry - #507
Closed
viniciusdc wants to merge 6 commits into
Closed
feat(schema): generate config JSON Schema from the provider registry#507viniciusdc wants to merge 6 commits into
viniciusdc wants to merge 6 commits into
Conversation
Re-home the config schema-generation pipeline onto the current provider
layout (pkg/providers/{cluster,dns}), superseding the stale branch behind
#362 which was 41 commits behind main and built on the removed
pkg/provider/ layout.
- Add ConfigType() reflect.Type to the cluster and DNS provider interfaces,
with one-line implementations on all seven registered providers.
- pkg/configschema wraps invopop/jsonschema: reads yaml tags, package-
qualifies $defs keys so aws.Config and longhorn.Config don't collide,
emits additionalProperties:false, and pulls field descriptions from godoc.
- pkg/nic/config_types.go exposes RegisteredConfigTypes through the registry,
so the generator enumerates providers with no hard-coded list.
- cmd/schemagen writes schemas/manifest.json, nebari-config.json, and
providers/<name>.json. `make schemas` regenerates; a paths-filtered CI
workflow fails on drift.
- schemas/ is committed in-tree for the docs site to consume.
Content fixes surfaced while rendering these schemas in the docs:
- enum tags on certificate.type and the AWS node-group taint effect, so the
allowed values are structured instead of buried in prose.
- drop an internal issue link from the trust_bundle godoc that was leaking
onto the generated, user-facing schema.
Refs #40, #360.
viniciusdc
requested review from
aktech,
dcmcand,
marcelovilla,
oldsj and
tylerpotts
as code owners
July 24, 2026 18:49
7 tasks
Add jsonschema default/enum tags for fields whose defaults are applied in code, so the config reference shows sane values instead of blanks: - aws load_balancer_scheme: enum internet-facing|internal, default internet-facing (LoadBalancerSchemeOrDefault). - aws load-balancer-controller chart_version: default 3.2.1 (defaultLBCChartVersion). - existing storage_class: default standard (defaultStorageClass). - azure node pool mode: enum System|User, default User. Values are taken from the defaulting code, not the example comments, so the documented default matches what the CLI actually applies. Regenerated schemas/ accordingly.
- cmd/schemagen: tighten generated-file permissions to satisfy gosec
(G301 dir 0o750, G306 files 0o600). Git records mode 0644 for the
committed schemas regardless, so the artifact is unaffected.
- Bump the vulnerability gate's govulncheck to v1.6.0. v1.4.0 crashes
("ForEachElement called on type containing *types.TypeParam") on the
generic types pulled in transitively by invopop/jsonschema, which the
schema generator now depends on. v1.6.0 analyzes the module cleanly and
the gate stays green (no reachable, fixable vulnerabilities).
- Drop an unnecessary type parameter from sortedKeys now that its only
caller passes map[string]reflect.Type.
6 tasks
…iles
Reduce the footprint of the schema generator:
- Remove ConfigType() from the cluster and DNS provider interfaces and the
seven per-provider configtype.go files. schemagen now references the
provider Config types directly (clusterConfigTypes / dnsConfigTypes maps),
so the runtime interfaces carry no schema-only method. As an internal build
tool a small hard-coded map is an acceptable trade; keep it in sync with
pkg/nic/registry.go (CI drift check surfaces staleness).
- Delete pkg/nic/config_types.go (RegisteredConfigTypes), now unused.
- Mark the generated output: add schemas/README.md ("do not edit by hand")
and a "_comment" field in manifest.json, since JSON has no comment syntax.
The docs consumer ignores the extra field.
Provider schemas are byte-identical; only manifest.json changes (the marker).
…bility Replace schemagen's hard-coded provider->type map (a second place to keep in sync) with a registry-driven lookup, without putting a schema-only method on the Provider interface: - Add an optional ConfigTyped capability interface to the cluster and dns packages (ConfigType() reflect.Type). It is deliberately not part of the Provider interface, mirroring the existing optional-interface pattern in pkg/nic, so out-of-tree providers (ADR-0004) and test fakes are unaffected. - Each in-tree provider implements ConfigType() inline in its provider.go, so the config type lives with the provider - the single source of truth - and is reusable later by `nic init`. - pkg/nic/config_types.go walks the registry and reads each provider's type through a ConfigTyped type assertion; cmd/schemagen consumes that instead of importing every provider package and listing them by hand. Generated schemas are byte-identical; this is purely a wiring change.
…yped RegisteredConfigTypes skips providers without a config type, so a provider added to the registry that forgets ConfigType() would be silently omitted from the generated schemas. Assert every registered cluster and DNS provider is covered, so the omission fails loudly in CI. Verified it fails when a provider's ConfigType method is removed.
viniciusdc
commented
Jul 25, 2026
Comment on lines
61
to
+66
| // Name returns the provider name | ||
| // ConfigType reports the Go type of this provider's configuration struct | ||
| // (the optional cluster.ConfigTyped capability used by schema generation and | ||
| // config scaffolding). | ||
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | ||
|
|
Collaborator
Author
There was a problem hiding this comment.
Suggested change
| // Name returns the provider name | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional cluster.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional cluster.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // Name returns the provider name |
viniciusdc
commented
Jul 25, 2026
Comment on lines
33
to
+38
| // Name returns the provider name used in cluster.azure: dispatch. | ||
| // ConfigType reports the Go type of this provider's configuration struct | ||
| // (the optional cluster.ConfigTyped capability used by schema generation and | ||
| // config scaffolding). | ||
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | ||
|
|
Collaborator
Author
There was a problem hiding this comment.
Suggested change
| // Name returns the provider name used in cluster.azure: dispatch. | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional cluster.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional cluster.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // Name returns the provider name used in cluster.azure: dispatch. |
viniciusdc
commented
Jul 25, 2026
Comment on lines
30
to
+35
| // Name returns the provider name. | ||
| // ConfigType reports the Go type of this provider's configuration struct | ||
| // (the optional cluster.ConfigTyped capability used by schema generation and | ||
| // config scaffolding). | ||
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | ||
|
|
Collaborator
Author
There was a problem hiding this comment.
Suggested change
| // Name returns the provider name. | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional cluster.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional cluster.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // Name returns the provider name. |
viniciusdc
commented
Jul 25, 2026
Comment on lines
25
to
+30
| // Name returns the provider name | ||
| // ConfigType reports the Go type of this provider's configuration struct | ||
| // (the optional cluster.ConfigTyped capability used by schema generation and | ||
| // config scaffolding). | ||
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | ||
|
|
Collaborator
Author
There was a problem hiding this comment.
Suggested change
| // Name returns the provider name | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional cluster.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional cluster.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // Name returns the provider name |
viniciusdc
commented
Jul 25, 2026
| // (the optional cluster.ConfigTyped capability used by schema generation and | ||
| // config scaffolding). | ||
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | ||
|
|
Collaborator
Author
There was a problem hiding this comment.
Suggested change
| // Name returns the provider name |
viniciusdc
commented
Jul 25, 2026
Comment on lines
51
to
+56
| // Name returns the provider name | ||
| // ConfigType reports the Go type of this provider's configuration struct | ||
| // (the optional cluster.ConfigTyped capability used by schema generation and | ||
| // config scaffolding). | ||
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | ||
|
|
Collaborator
Author
There was a problem hiding this comment.
Suggested change
| // Name returns the provider name | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional cluster.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional cluster.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // Name returns the provider name |
viniciusdc
commented
Jul 25, 2026
Comment on lines
40
to
+45
| // Name returns the provider name. | ||
| // ConfigType reports the Go type of this provider's configuration struct | ||
| // (the optional dns.ConfigTyped capability used by schema generation and | ||
| // config scaffolding). | ||
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | ||
|
|
Collaborator
Author
There was a problem hiding this comment.
Suggested change
| // Name returns the provider name. | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional dns.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // ConfigType reports the Go type of this provider's configuration struct | |
| // (the optional dns.ConfigTyped capability used by schema generation and | |
| // config scaffolding). | |
| func (p *Provider) ConfigType() reflect.Type { return reflect.TypeFor[Config]() } | |
| // Name returns the provider name. |
This was referenced Aug 5, 2026
Contributor
|
@viniciusdc should this be closed in favor of #567 ? |
This was referenced Aug 13, 2026
viniciusdc
force-pushed
the
feat/config-schema-gen-v2
branch
from
August 15, 2026 17:25
4845ea2 to
ef457d2
Compare
6 tasks
viniciusdc
force-pushed
the
feat/config-schema-gen-v2
branch
from
August 15, 2026 17:51
ef457d2 to
4845ea2
Compare
Collaborator
Author
|
superseeded by #600 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Generates JSON Schema documents for
nebari-config.yamland every registered provider'sConfigstruct, straight from the Go types, and commits them underschemas/for the docs site to consume.This supersedes #362. That branch was 41 commits behind
mainand built entirely on the oldpkg/provider/(singular) layout, which the provider refactor removed in favour ofpkg/providers/{cluster,dns}/. Rather than rebase two commits across a 41-commit layout change, this re-homes the pipeline onto currentmainand folds in the content fixes the docs rendering work surfaced.Changes
ConfigType() reflect.Typeon the cluster and DNS provider interfaces, with one-line implementations on all seven registered providers (aws, azure, gcp, hetzner, local, existing, cloudflare). Added as a smallconfigtype.goper package to keep the diff local.pkg/configschemawrapsinvopop/jsonschema: readsyamltags, package-qualifies$defskeys (soaws.Configandlonghorn.Configdon't merge), emitsadditionalProperties: false, and pulls field descriptions from godoc viaAddGoComments.pkg/nic/config_types.goexposesRegisteredConfigTypesthrough the registry, so the generator enumerates providers with no hard-coded list - adding a provider extends the output automatically.cmd/schemagenwritesschemas/manifest.json({providers, dns, top_level}),nebari-config.json, andproviders/<name>.json.make schemasregenerates;.github/workflows/schemas.ymlis a paths-filtered drift gate that fails if the committed output diverges from the code (kubebuilder CRD-regen pattern, no auto-commit).schemas/committed in-tree (6 cluster providers + Cloudflare + top-level + manifest).Content fixes surfaced by the docs rendering
enumtags oncertificate.type(selfsigned/letsencrypt/existing) and the AWS node-grouptaint.effect(NO_SCHEDULE/NO_EXECUTE/PREFER_NO_SCHEDULE), so the allowed values are structured rather than prose the reader has to parse.trust_bundlegodoc that was leaking onto the generated, user-facing schema.Docs consumption
The docs site renders these schemas live - see the config-schema reference PoC and its Netlify preview (nebari-docs PR: https://redirect.github.com/nebari-dev/nebari-docs/pull/710). It currently tracks this branch via
raw.githubusercontent.com; once this lands on a tagged release the docs pin to the tag.Test plan
go build ./...clean;go vet ./...clean;gofmtcleango test ./...passes (the interface change compiles against all providers and test doubles)make schemasis deterministic - regenerating on a clean tree yields no diff (the CI drift gate)main(e.g. Cloudflarezone_namerequired; certificate/taint enums present)go run ./cmd/schemagen -providers aws,hetznernarrows correctlyDeferred (follow-ups)
FormatYAML(the commented-YAML reference) is still stubbed.examples/defaultstruct tags and provider godoc coverage - most provider fields still have no description; the schema surfaces them the moment the godoc lands.schemas/per release tag + aschema-versions.jsonso the docs version toggle can list real versions (currently the docs track this branch).