Validation System Internals
Purpose: For contributors, explains the CLI validation pipeline and how to add new validators.
What the validation system does
The CLI validates cluster configurations before generating infrastructure artifacts. Validation catches misconfigurations early — before Terraform or Kubespray runs — reducing feedback loops from minutes to seconds.
Validation runs automatically during opencenter cluster validate and as a pre-check in opencenter cluster setup.
Two validation layers
1. Struct validation (go-playground/validator)
The primary validation mechanism uses struct tags from go-playground/validator/v10. Tags are declared directly on the config struct fields in internal/config/.
type ClusterConfig struct {
ClusterName string `yaml:"cluster_name" validate:"required,dns_rfc1035"`
Provider string `yaml:"provider" validate:"required,oneof=openstack vmware aws kind"`
KubeVersion string `yaml:"kube_version" validate:"required,semver"`
}
Common validator tags used in the CLI:
| Tag | Meaning |
|---|---|
required | Field must not be zero-value |
oneof=a b c | Value must be one of the listed options |
semver | Must be a valid semantic version |
url | Must be a valid URL |
cidrv4 | Must be a valid IPv4 CIDR |
ip | Must be a valid IP address |
min=N, max=N | Numeric or length bounds |
dns_rfc1035 | Valid DNS label |
2. JSON Schema validation
The CLI generates a JSON Schema from the Go structs (via invopop/jsonschema) and stores it in schema/cluster.schema.json. This schema is used for:
- Editor autocompletion and inline validation (VS Code, etc.)
- External tooling that consumes cluster configs
- Documentation of the configuration format
Generate the schema:
mise run schema
Validation pipeline
When opencenter cluster validate runs, the pipeline executes in order:
- Load the YAML config file
- Unmarshal into the
ClusterConfigstruct - Run go-playground/validator on the struct (checks all
validatetags) - Run cross-field validators (e.g., "if provider is openstack, then auth_url is required")
- Run provider-specific validators (e.g., OpenStack endpoint reachability)
- Report all errors with field paths and human-readable messages
Errors include the full field path so you can locate the problem in the YAML:
validation error: opencenter.infrastructure.cloud.openstack.auth_url is required when provider is "openstack"
Adding a new validator
To add validation for a new config field:
- Add the
validatestruct tag to the field ininternal/config/ - If the built-in tags aren't sufficient, register a custom validator function with the validator instance
- Add a test in the corresponding
*_test.gothat verifies both valid and invalid inputs - Run
mise run schemato regenerate the JSON Schema
For cross-field validation (where one field's validity depends on another), add logic to the validation pipeline in internal/config/ rather than using struct tags.
Trade-offs
- Struct tags handle ~90% of validation needs with zero custom code. They're declarative and self-documenting.
- Cross-field validators require imperative code but handle conditional requirements that tags can't express.
- JSON Schema provides external validation but lags behind the Go struct if you forget to regenerate it. The
mise run schematask should be part of any config change workflow.
Common misconceptions
- Validation doesn't modify the config. It only reports errors. Default values are applied during config loading, before validation runs.
- The
validatetag andyamltag are independent. A field can be optional in YAML (omitempty) but required by validation (required) — this means the field has a default that gets set during loading. - Custom validators registered with go-playground/validator are available in struct tags just like built-in ones. Check
internal/config/for examples of custom tag registration.