README
¶
OpenAPI Linter
Overview
The OpenAPI Linter validates OpenAPI schema files and Custom Resource Definitions (CRDs) to ensure compliance with Kubernetes API conventions, Deckhouse standards, and best practices. This linter checks schema files in the openapi/ and crds/ directories, validating enum values, special field constraints, deprecated key usage, and CRD metadata.
Proper OpenAPI schema validation is critical for module configuration, ensuring type safety, consistent API design, and compatibility with Kubernetes conventions. The linter helps prevent configuration errors and maintains API consistency across all Deckhouse modules.
Rules
| Rule | Description | Configurable | Default |
|---|---|---|---|
| enum | Validates enum values follow Kubernetes CamelCase conventions | ✅ | enabled |
| high-availability | Validates highAvailability field has no default value | ✅ | enabled |
| keys | Validates property names don't use banned names | ✅ | enabled |
| deckhouse-crds | Validates Deckhouse CRD structure and metadata | ✅ | enabled |
Rule Details
enum
Purpose: Ensures enum values in OpenAPI schemas follow Kubernetes API conventions for consistency, readability, and compatibility with Kubernetes tooling. Proper enum formatting makes APIs predictable and maintainable.
Description:
Validates that all enum values in OpenAPI schema files follow the Kubernetes CamelCase convention. According to Kubernetes API conventions, enum values must be strings in CamelCase with an initial uppercase letter.
What it checks:
- Scans all
.yamland.ymlfiles inopenapi/andcrds/directories - Identifies enum fields in OpenAPI schemas
- Validates each enum value follows CamelCase convention:
- Must start with an uppercase letter (if it starts with a letter)
- Must contain only letters and numbers
- Must not contain spaces, hyphens, or underscores (except in special cases)
- Numbers can include dots for float values (e.g.,
1.5)
Kubernetes API Conventions:
From the Kubernetes API conventions:
Some fields will have a list of allowed values (enumerations). These values will be strings, and they will be in CamelCase, with an initial uppercase letter. Examples:
ClusterFirst,Pending,ClientIP.When an acronym or initialism, each letter in the acronym should be uppercase, such as with
ClientIPorTCPDelay.When a proper name or the name of a command-line executable is used as a constant, the proper name should be represented in consistent casing - examples:
systemd,iptables,IPVS,cgroupfs,Docker(as a generic concept),docker(as the command-line executable). If a proper name is used which has mixed capitalization likeeBPF, that should be preserved in a longer constant such aseBPFDelegation.
Why it matters:
- Consistency: Follows Kubernetes API conventions used throughout the ecosystem
- Readability: CamelCase enum values are easier to read and understand
- Tooling Compatibility: Kubernetes tools expect enum values in this format
- API Stability: Consistent naming reduces confusion and API changes
Examples:
❌ Incorrect - Invalid enum values:
# openapi/config-values.yaml
properties:
logLevel:
type: string
enum:
- debug # ❌ Must start with uppercase
- info # ❌ Must start with uppercase
- WARNING # ❌ All caps (unless it's an acronym)
- error-level # ❌ Contains hyphen
Error:
Error: enum 'properties.logLevel.enum' is invalid: value 'debug' must start with Capital letter
Error: enum 'properties.logLevel.enum' is invalid: value 'info' must start with Capital letter
Error: enum 'properties.logLevel.enum' is invalid: value 'error-level' must be in CamelCase
❌ Incorrect - Invalid special characters:
properties:
storageType:
type: string
enum:
- local_storage # ❌ Contains underscore
- network-storage # ❌ Contains hyphen
- cloud storage # ❌ Contains space
Error:
Error: enum 'properties.storageType.enum' is invalid: value 'local_storage' must be in CamelCase
Error: enum 'properties.storageType.enum' is invalid: value 'network-storage' must be in CamelCase
Error: enum 'properties.storageType.enum' is invalid: value 'cloud storage' must be in CamelCase
✅ Correct - Valid enum values:
# openapi/config-values.yaml
properties:
logLevel:
type: string
enum:
- Debug
- Info
- Warning
- Error
✅ Correct - Acronyms and proper names:
properties:
networkPolicy:
type: string
enum:
- ClusterFirst # Standard CamelCase
- ClientIP # Acronym - all caps
- TCPDelay # Acronym - all caps
✅ Correct - Proper names and executables:
properties:
runtime:
type: string
enum:
- Docker # Proper name (generic concept)
- docker # Executable name (command-line)
- containerd # Executable name
- systemd # Executable name
- iptables # Executable name
✅ Correct - Mixed capitalization proper names:
properties:
ebpfMode:
type: string
enum:
- eBPFDelegation # Preserves eBPF capitalization
- eBPFNative
✅ Correct - Numbers in enum values:
properties:
version:
type: string
enum:
- Version1 # With number
- Version2
- TLS1.2 # With dot in number
- TLS1.3
Configuration:
# .dmt.yaml
linters-settings:
openapi:
impact: error
To exclude specific enum fields:
# .dmt.yaml
linters-settings:
openapi:
exclude-rules:
enum:
- "properties.storageClass.properties.type"
- "properties.legacy.properties.mode"
To exclude enum fields with array wildcards:
# .dmt.yaml
linters-settings:
openapi:
exclude-rules:
enum:
# Exclude specific array item properties
- "properties.items[*].properties.type"
- "properties.provision.items.oneOf[*].properties.mode"
high-availability
Purpose: Ensures the highAvailability field in OpenAPI schemas doesn't have a default value. This field must be explicitly set by users to avoid unintended behavior and resource allocation in high-availability configurations.
Description:
Validates that the properties.highAvailability field in OpenAPI schemas doesn't define a default value. High availability settings should always be explicitly configured by users, never assumed by default.
What it checks:
- Identifies
properties.highAvailabilityfields in OpenAPI schemas - Validates the field has no
defaultkey defined - Ensures users must explicitly enable or disable high availability
Why it matters:
- Explicit Configuration: High availability requires deliberate choice
- Resource Impact: HA configurations consume more resources
- Production Safety: Prevents accidental HA enablement in development
- Cost Awareness: Users should consciously decide on HA for cost implications
Examples:
❌ Incorrect - Has default value:
# openapi/config-values.yaml
properties:
highAvailability:
type: boolean
default: true # ❌ Must not have default value
description: Enable high availability mode
Error:
Error: properties.highAvailability is invalid: must have no default value
❌ Incorrect - Default in complex schema:
properties:
highAvailability:
type: object
default: {} # ❌ Must not have default value
properties:
enabled:
type: boolean
Error:
Error: properties.highAvailability is invalid: must have no default value
✅ Correct - No default value:
# openapi/config-values.yaml
properties:
highAvailability:
type: boolean
description: |
Enable high availability mode.
When enabled, runs multiple replicas with anti-affinity.
✅ Correct - Complex schema without default:
properties:
highAvailability:
type: object
description: High availability configuration
properties:
enabled:
type: boolean
description: Enable high availability
replicas:
type: integer
minimum: 2
description: Number of replicas in HA mode
Configuration:
# .dmt.yaml
linters-settings:
openapi:
exclude-rules:
ha-absolute-keys:
- "properties.highAvailability" # Exclude specific HA field
keys
Purpose: Prevents use of banned names in enum values within OpenAPI schemas. This ensures enum values don't conflict with reserved keywords or cause confusion.
Description:
Validates that enum values in OpenAPI schema files (specifically in CRD files in crds/ directory) don't use banned keywords. Banned names are typically reserved words that could cause conflicts with Kubernetes or Deckhouse internals.
What it checks:
- Scans enum fields in CRD OpenAPI schemas
- Checks each enum value against the banned names list
- Recursively validates nested structures
- Reports any usage of banned keywords in enum values
Why it matters:
- Conflict Prevention: Avoids conflicts with reserved keywords
- API Clarity: Prevents confusing or ambiguous property names
- Future Compatibility: Reserved names may be used in future versions
- Standard Compliance: Maintains naming convention standards
Examples:
❌ Incorrect - Using banned name in enum:
# Assume "default" is a banned name
properties:
mode:
type: string
enum:
- Standard
- default # ❌ Banned name
- Custom
Error:
Error: default is invalid name for property default
❌ Incorrect - Banned name in property:
properties:
settings:
type: object
properties:
default: # ❌ Banned property name
type: string
Error:
Error: validation error: wrong property: default is invalid name for property default
✅ Correct - Valid property names:
properties:
mode:
type: string
enum:
- Standard
- Custom
- Advanced
settings:
type: object
properties:
preset: # Not banned
type: string
configuration:
type: string
Configuration:
Define which names should be banned in enum values:
# .dmt.yaml
linters-settings:
openapi:
exclude-rules:
key-banned-names:
- "default" # Ban "default" as an enum value
- "type" # Ban "type" as an enum value
Note: The key-banned-names list defines which names are not allowed as enum values. Common banned names typically include:
default- Reserved for schema defaultstype- Reserved for OpenAPI type definitions- Other context-specific reserved words
deckhouse-crds
Purpose: Ensures Custom Resource Definitions (CRDs) for Deckhouse follow proper structure, use current API versions, have required labels, and don't use deprecated fields. This maintains CRD quality and compatibility with Deckhouse standards.
Description:
Validates CRDs in the crds/ directory that belong to Deckhouse (contain deckhouse.io in the name). Checks API version, required labels, and validates against use of deprecated deprecated key in favor of the Deckhouse-specific x-doc-deprecated annotation.
What it checks:
- CRD API version is
apiextensions.k8s.io/v1(not deprecated versions) - CRD has required
modulelabel matching the module name - CRD doesn't use the deprecated
deprecatedkey in properties - Validates that
x-doc-deprecated: trueis used instead ofdeprecated
Why it matters:
- API Compatibility: Current API versions ensure Kubernetes compatibility
- Module Tracking: Labels enable proper resource management
- Documentation Standards: Deckhouse uses custom deprecation annotations
- Future-Proofing: Prevents use of deprecated Kubernetes features
Examples:
❌ Incorrect - Deprecated API version:
# crds/my-resource.yaml
apiVersion: apiextensions.k8s.io/v1beta1 # ❌ Deprecated
kind: CustomResourceDefinition
metadata:
name: myresources.deckhouse.io
spec:
group: deckhouse.io
names:
kind: MyResource
Error:
Error: CRD specified using deprecated api version, wanted "apiextensions.k8s.io/v1"
❌ Incorrect - Missing module label:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: myresources.deckhouse.io
labels:
# ❌ Missing: module label
app: my-app
Error:
Error: CRD should contain "module = my-module" label
❌ Incorrect - Wrong module label value:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: myresources.deckhouse.io
labels:
module: wrong-name # ❌ Should match actual module name
Error:
Error: CRD should contain "module = my-module" label, but got "module = wrong-name"
❌ Incorrect - Using deprecated key:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: myresources.deckhouse.io
spec:
versions:
- name: v1
schema:
openAPIV3Schema:
properties:
spec:
properties:
oldField:
type: string
deprecated: true # ❌ Use x-doc-deprecated instead
Error:
Error: CRD contains "deprecated" key at path "spec.versions[].schema.openAPIV3Schema.properties.spec.properties.oldField", use "x-doc-deprecated: true" instead
✅ Correct - Proper CRD structure:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: myresources.deckhouse.io
labels:
module: my-module
heritage: deckhouse
spec:
group: deckhouse.io
scope: Cluster
names:
plural: myresources
singular: myresource
kind: MyResource
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
enabled:
type: boolean
description: Enable the resource
✅ Correct - Using Deckhouse deprecation:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: myresources.deckhouse.io
labels:
module: my-module
spec:
versions:
- name: v1
schema:
openAPIV3Schema:
properties:
spec:
properties:
oldField:
type: string
x-doc-deprecated: true # ✅ Correct Deckhouse deprecation
description: |
DEPRECATED: Use newField instead.
This field will be removed in v2.
newField:
type: string
description: Replacement for oldField
✅ Correct - Multiple versions:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: myresources.deckhouse.io
labels:
module: my-module
spec:
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
field1:
type: string
- name: v1alpha1
served: true
storage: false
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
legacyField:
type: string
x-doc-deprecated: true
Configuration:
# .dmt.yaml
linters-settings:
openapi:
exclude-rules:
crd-names:
- projects.deckhouse.io # Exclude specific CRD
- projecttemplates.deckhouse.io
- legacy-resources.deckhouse.io
Note: Only CRDs with deckhouse.io in their name are validated by this rule. Third-party CRDs are automatically skipped.
Configuration
The OpenAPI linter can be configured at the module level with rule-specific exclusions.
Module-Level Settings
Configure the overall impact level for the openapi linter:
# .dmt.yaml
linters-settings:
openapi:
impact: error # Options: error, warning, info, ignored
Impact levels:
error: Violations fail the validation and return a non-zero exit codewarning: Violations are reported but don't fail the validationinfo: Violations are reported as informational messagesignored: The linter is completely disabled
Rule-Level Exclusions
Each rule supports excluding specific schema paths or CRD names:
Enum Rule Exclusions
Exclude specific enum fields by their schema path:
# .dmt.yaml
linters-settings:
openapi:
exclude-rules:
enum:
# Exact path to enum field
- "properties.storageClass.properties.type"
# Path with array index
- "properties.items[0].properties.mode"
# Path with wildcard for any array index
- "properties.provision.items[*].properties.type"
- "properties.config.oneOf[*].properties.format"
High Availability Exclusions
Exclude specific highAvailability fields:
# .dmt.yaml
linters-settings:
openapi:
exclude-rules:
ha-absolute-keys:
- "properties.highAvailability"
- "properties.internal.properties.highAvailability"
Key Banned Names Configuration
Define which property names are banned in enum values:
# .dmt.yaml
linters-settings:
openapi:
exclude-rules:
key-banned-names:
- "default" # Ban "default" as an enum value
- "type" # Ban "type" as an enum value
- "name" # Ban "name" as an enum value
Note: This list defines which names are not allowed in enum values, not paths to exclude from validation.
CRD Exclusions
Exclude specific CRDs from validation:
# .dmt.yaml
linters-settings:
openapi:
exclude-rules:
crd-names:
- projects.deckhouse.io
- projecttemplates.deckhouse.io
- legacy-resources.deckhouse.io
Complete Configuration Example
# .dmt.yaml
linters-settings:
openapi:
# Global impact level
impact: error
# Rule-specific exclusions
exclude-rules:
# Enum value exclusions (paths to exclude from enum validation)
enum:
- "properties.storageClass.properties.provision.items.properties.type"
- "properties.storageClass.properties.provision.items.oneOf[*].properties.type"
- "properties.legacy.properties.mode"
# High availability field exclusions
ha-absolute-keys:
- "properties.internal.properties.highAvailability"
# Banned names in enum values (names that are not allowed)
key-banned-names:
- "default"
- "type"
# CRD name exclusions
crd-names:
- projects.deckhouse.io
- projecttemplates.deckhouse.io
- experimental-resources.deckhouse.io
Configuration in Module Directory
You can also place a .dmt.yaml configuration file directly in your module directory:
# modules/my-module/.dmt.yaml
linters-settings:
openapi:
impact: warning # More lenient for this specific module
exclude-rules:
enum:
- "properties.legacy.properties.type"
crd-names:
- legacy-resource.deckhouse.io
Common Issues
Issue: Invalid enum case
Symptom:
Error: enum 'properties.logLevel.enum' is invalid: value 'debug' must start with Capital letter
Cause: Enum values don't follow Kubernetes CamelCase convention.
Solutions:
-
Capitalize enum values:
# Before enum: - debug - info - warning # After enum: - Debug - Info - Warning -
Use proper CamelCase for multi-word values:
# Before enum: - cluster_first - dns_default # After enum: - ClusterFirst - DNSDefault
Issue: Enum with special characters
Symptom:
Error: enum 'properties.mode.enum' is invalid: value 'local-storage' must be in CamelCase
Cause: Enum values contain hyphens, underscores, or spaces.
Solutions:
-
Remove special characters and use CamelCase:
# Before enum: - local-storage - network_storage - cloud storage # After enum: - LocalStorage - NetworkStorage - CloudStorage -
For proper names, follow Kubernetes conventions:
# Before enum: - ip-tables - e-bpf # After enum: - iptables # Executable name - eBPF # Proper name with mixed case
Issue: HighAvailability has default value
Symptom:
Error: properties.highAvailability is invalid: must have no default value
Cause: The highAvailability field has a default key defined.
Solutions:
-
Remove the default value:
# Before properties: highAvailability: type: boolean default: true # After properties: highAvailability: type: boolean description: Enable high availability mode -
Document the required explicit configuration:
properties: highAvailability: type: boolean description: | Enable high availability mode. This must be explicitly set - there is no default value. HA mode increases resource usage and should be consciously enabled.
Issue: Deprecated API version in CRD
Symptom:
Error: CRD specified using deprecated api version, wanted "apiextensions.k8s.io/v1"
Cause: CRD uses apiextensions.k8s.io/v1beta1 instead of v1.
Solutions:
-
Update to current API version:
# Before apiVersion: apiextensions.k8s.io/v1beta1 # After apiVersion: apiextensions.k8s.io/v1 -
Check Kubernetes migration guide:
- Review CRD v1beta1 to v1 migration
- Update schema structure if needed (v1 has stricter requirements)
Issue: Missing module label in CRD
Symptom:
Error: CRD should contain "module = my-module" label
Cause: CRD is missing the required module label.
Solutions:
-
Add the module label:
apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: myresources.deckhouse.io labels: module: my-module # Add this label heritage: deckhouse -
Use Helm templates to ensure consistency:
# crds/myresource.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: myresources.deckhouse.io labels: module: {{ .Chart.Name }} heritage: deckhouse
Issue: Using deprecated key instead of x-doc-deprecated
Symptom:
Error: CRD contains "deprecated" key at path "spec.versions[].schema.openAPIV3Schema.properties.spec.properties.oldField", use "x-doc-deprecated: true" instead
Cause: CRD uses Kubernetes deprecated key instead of Deckhouse x-doc-deprecated.
Solutions:
-
Replace with x-doc-deprecated:
# Before properties: oldField: type: string deprecated: true # After properties: oldField: type: string x-doc-deprecated: true description: | DEPRECATED: Use newField instead. This field will be removed in v2. -
Add comprehensive deprecation documentation:
properties: oldField: type: string x-doc-deprecated: true x-doc-deprecated-version: "v1.5.0" description: | DEPRECATED since v1.5.0: Use newField instead. This field is maintained for backward compatibility and will be removed in v2.0.0. Please migrate to using newField. Migration guide: newField accepts the same values as oldField. newField: type: string description: Replacement for the deprecated oldField
Issue: Enum validation in complex nested structures
Symptom:
Error: enum 'properties.items[5].properties.type.enum' is invalid: value 'custom_type' must be in CamelCase
Cause: Enum in deeply nested structure has invalid value.
Solutions:
-
Fix the enum value:
# Before properties: items: type: array items: properties: type: enum: - custom_type # After properties: items: type: array items: properties: type: enum: - CustomType -
Exclude specific path if needed:
# .dmt.yaml linters-settings: openapi: exclude-rules: enum: # Use wildcard for array indices - "properties.items[*].properties.type"
Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type OpenAPI ¶
type OpenAPI struct {
ErrorList *errors.LintRuleErrorsList
// contains filtered or unexported fields
}
OpenAPI linter
func New ¶
func New(cfg *pkg.OpenAPILinterConfig, errorList *errors.LintRuleErrorsList) *OpenAPI