Add JSON schema generation for kubeconform CRD validation
- ci/generate-schemas.sh generates schemas from CRD manifests and K8s swagger - Sources: ArgoCD v3.3.2, Gateway API v1.5.1, Kubernetes v1.33.7 - Optionally fetches live cluster CRDs via kubectl when available - Generated schemas committed to schemas/ for CI use - Run `make schemas` to regenerate after CRD version bumps - validate-apps.sh and validate-clusters.sh check local schemas first - Remove CustomResourceDefinition from kubeconform skip list
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object"
|
||||
},
|
||||
"spec": {
|
||||
"properties": {
|
||||
"hostnames": {
|
||||
"items": {
|
||||
"maxLength": 253,
|
||||
"minLength": 1,
|
||||
"pattern": "^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
|
||||
"type": "string"
|
||||
},
|
||||
"maxItems": 16,
|
||||
"minItems": 1,
|
||||
"type": "array",
|
||||
"x-kubernetes-list-type": "atomic",
|
||||
"x-kubernetes-validations": [
|
||||
{
|
||||
"message": "Hostnames cannot contain an IP",
|
||||
"rule": "self.all(h, !isIP(h))"
|
||||
},
|
||||
{
|
||||
"message": "Hostnames must be valid based on RFC-1123",
|
||||
"rule": "self.all(h, !h.contains('*') ? h.matches('^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$') : true)"
|
||||
},
|
||||
{
|
||||
"message": "Wildcards on hostnames must be the first label, and the rest of hostname must be valid based on RFC-1123",
|
||||
"rule": "self.all(h, h.contains('*') ? (h.startsWith('*.') && h.substring(2).matches('^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$')) : true)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"parentRefs": {
|
||||
"items": {
|
||||
"properties": {
|
||||
"group": {
|
||||
"default": "gateway.networking.k8s.io",
|
||||
"maxLength": 253,
|
||||
"pattern": "^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"default": "Gateway",
|
||||
"maxLength": 63,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"maxLength": 253,
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"maxLength": 63,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"format": "int32",
|
||||
"maximum": 65535,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"sectionName": {
|
||||
"maxLength": 253,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"maxItems": 32,
|
||||
"type": "array",
|
||||
"x-kubernetes-list-type": "atomic",
|
||||
"x-kubernetes-validations": [
|
||||
{
|
||||
"message": "sectionName must be specified when parentRefs includes 2 or more references to the same parent",
|
||||
"rule": "self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) || p1.sectionName == '') == (!has(p2.sectionName) || p2.sectionName == '')) : true))"
|
||||
},
|
||||
{
|
||||
"message": "sectionName must be unique when parentRefs includes 2 or more references to the same parent",
|
||||
"rule": "self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName))))"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"items": {
|
||||
"properties": {
|
||||
"backendRefs": {
|
||||
"items": {
|
||||
"properties": {
|
||||
"group": {
|
||||
"default": "",
|
||||
"maxLength": 253,
|
||||
"pattern": "^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"default": "Service",
|
||||
"maxLength": 63,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"maxLength": 253,
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"maxLength": 63,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"format": "int32",
|
||||
"maximum": 65535,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"weight": {
|
||||
"default": 1,
|
||||
"format": "int32",
|
||||
"maximum": 1000000,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"type": "object",
|
||||
"x-kubernetes-validations": [
|
||||
{
|
||||
"message": "Must have port for Service reference",
|
||||
"rule": "(size(self.group) == 0 && self.kind == 'Service') ? has(self.port) : true"
|
||||
}
|
||||
]
|
||||
},
|
||||
"maxItems": 16,
|
||||
"minItems": 1,
|
||||
"type": "array",
|
||||
"x-kubernetes-list-type": "atomic"
|
||||
},
|
||||
"name": {
|
||||
"maxLength": 253,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"backendRefs"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"maxItems": 1,
|
||||
"minItems": 1,
|
||||
"type": "array",
|
||||
"x-kubernetes-list-type": "atomic"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"hostnames",
|
||||
"rules"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"status": {
|
||||
"properties": {
|
||||
"parents": {
|
||||
"items": {
|
||||
"properties": {
|
||||
"conditions": {
|
||||
"items": {
|
||||
"properties": {
|
||||
"lastTransitionTime": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"maxLength": 32768,
|
||||
"type": "string"
|
||||
},
|
||||
"observedGeneration": {
|
||||
"format": "int64",
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"reason": {
|
||||
"maxLength": 1024,
|
||||
"minLength": 1,
|
||||
"pattern": "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"enum": [
|
||||
"True",
|
||||
"False",
|
||||
"Unknown"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"maxLength": 316,
|
||||
"pattern": "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"lastTransitionTime",
|
||||
"message",
|
||||
"reason",
|
||||
"status",
|
||||
"type"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"maxItems": 8,
|
||||
"minItems": 1,
|
||||
"type": "array",
|
||||
"x-kubernetes-list-map-keys": [
|
||||
"type"
|
||||
],
|
||||
"x-kubernetes-list-type": "map"
|
||||
},
|
||||
"controllerName": {
|
||||
"maxLength": 253,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\\/[A-Za-z0-9\\/\\-._~%!$&'()*+,;=:]+$",
|
||||
"type": "string"
|
||||
},
|
||||
"parentRef": {
|
||||
"properties": {
|
||||
"group": {
|
||||
"default": "gateway.networking.k8s.io",
|
||||
"maxLength": 253,
|
||||
"pattern": "^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"default": "Gateway",
|
||||
"maxLength": 63,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"maxLength": 253,
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"maxLength": 63,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"format": "int32",
|
||||
"maximum": 65535,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"sectionName": {
|
||||
"maxLength": 253,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"conditions",
|
||||
"controllerName",
|
||||
"parentRef"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"maxItems": 32,
|
||||
"type": "array",
|
||||
"x-kubernetes-list-type": "atomic"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"parents"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"spec"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
Reference in New Issue
Block a user