refactor(schema): comprehensive validation with enums, patterns, and detailed documentation

Significantly improve values.schema.json:

- Add enums for all constrained values (pullPolicy, service.type, dnsPolicy,
  deploymentStrategy, ipFamilyPolicy, pathType, persistence.type, accessMode)
- Add regex patterns for Kubernetes-specific formats (size, CIDR, intervals)
- Add port range validation (1-65535, nodePort 30000-32767)
- Add hostname format validation for Ingress/HTTPRoute
- Add required fields for arrays (env.name, imagePullSecrets.name)
- Add detailed structure for envFrom, env, imagePullSecrets
- Add comprehensive descriptions for every field with defaults and examples
- Add uniqueItems, minItems, maxItems constraints where applicable

Validated against:
- Invalid size patterns (rejects non-Kubernetes formats)
- Invalid enums (rejects 'nfs' for persistence.type)
- Invalid CIDR notation (IPv4/IPv6 regex)
- Mutual exclusivity (NetworkPolicy vs hostNetwork/DLNA)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
This commit is contained in:
Aleksei SviridkinandClaude committed 2025-11-08 16:27:49 +03:00 Unverified
1 parent f9d897c321
commit 29d292e5f8
1 file changed
+437 -77
+437 -77
View File
@@ -1,108 +1,134 @@
{ {
"$schema": "https://json-schema.org/draft-07/schema#", "$schema": "https://json-schema.org/draft-07/schema#",
"title": "Jellyfin Helm Chart Values", "title": "Jellyfin Helm Chart Values",
"description": "Schema for Jellyfin Helm chart values.yaml with validation for conflicting configurations", "description": "JSON Schema for Jellyfin Helm chart values.yaml with comprehensive validation and mutual exclusivity checks",
"type": "object", "type": "object",
"properties": { "properties": {
"replicaCount": { "replicaCount": {
"type": "integer", "type": "integer",
"minimum": 1, "minimum": 1,
"default": 1, "default": 1,
"description": "Number of Jellyfin replicas to start. Should be left at 1." "description": "Number of Jellyfin replicas. Should remain at 1 as Jellyfin does not support horizontal scaling."
}, },
"revisionHistoryLimit": { "revisionHistoryLimit": {
"type": "integer", "type": "integer",
"minimum": 0, "minimum": 0,
"default": 3, "default": 3,
"description": "Number of old ReplicaSets to retain for rollback history" "description": "Number of old ReplicaSets to retain for rollback history. Set to 0 to disable (not recommended)."
}, },
"imagePullSecrets": { "imagePullSecrets": {
"type": "array", "type": "array",
"default": [], "default": [],
"items": { "items": {
"type": "object" "type": "object",
"required": ["name"],
"properties": {
"name": {
"type": "string",
"description": "Name of the secret containing registry credentials"
}
}
}, },
"description": "Image pull secrets to authenticate with private repositories" "description": "Image pull secrets for authenticating with private container registries"
}, },
"image": { "image": {
"type": "object", "type": "object",
"required": ["repository", "pullPolicy"],
"properties": { "properties": {
"repository": { "repository": {
"type": "string", "type": "string",
"default": "docker.io/jellyfin/jellyfin" "default": "docker.io/jellyfin/jellyfin",
"pattern": "^[a-z0-9.-]+(/[a-z0-9._-]+)*$",
"description": "Container image repository for Jellyfin"
}, },
"tag": { "tag": {
"type": "string", "type": "string",
"default": "" "default": "",
"description": "Image tag. Leave empty to use appVersion from Chart.yaml"
}, },
"pullPolicy": { "pullPolicy": {
"type": "string", "type": "string",
"enum": ["Always", "IfNotPresent", "Never"], "enum": ["Always", "IfNotPresent", "Never"],
"default": "IfNotPresent" "default": "IfNotPresent",
"description": "Image pull policy"
} }
} }
}, },
"nameOverride": { "nameOverride": {
"type": "string", "type": "string",
"default": "" "default": "",
"description": "Override the chart name in resource names"
}, },
"fullnameOverride": { "fullnameOverride": {
"type": "string", "type": "string",
"default": "" "default": "",
"description": "Override the full resource name (namespace-name)"
}, },
"serviceAccount": { "serviceAccount": {
"type": "object", "type": "object",
"properties": { "properties": {
"create": { "create": {
"type": "boolean", "type": "boolean",
"default": true "default": true,
"description": "Create a dedicated service account"
}, },
"automount": { "automount": {
"type": "boolean", "type": "boolean",
"default": true "default": true,
"description": "Automatically mount service account token"
}, },
"annotations": { "annotations": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Annotations to add to the service account"
}, },
"name": { "name": {
"type": "string", "type": "string",
"default": "" "default": "",
"description": "Custom name for service account. Auto-generated if empty."
} }
} }
}, },
"podAnnotations": { "podAnnotations": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Annotations to add to Jellyfin pods"
}, },
"podLabels": { "podLabels": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Additional labels for Jellyfin pods"
}, },
"podSecurityContext": { "podSecurityContext": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Security context for the pod (fsGroup, runAsUser, etc.)"
}, },
"securityContext": { "securityContext": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Security context for the container (capabilities, readOnlyRootFilesystem, etc.)"
}, },
"runtimeClassName": { "runtimeClassName": {
"type": "string", "type": "string",
"default": "" "default": "",
"description": "RuntimeClass for the pod (e.g., nvidia, kata)"
}, },
"priorityClassName": { "priorityClassName": {
"type": "string", "type": "string",
"default": "" "default": "",
"description": "PriorityClass name for pod scheduling priority"
}, },
"dnsConfig": { "dnsConfig": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Custom DNS configuration for the pod"
}, },
"dnsPolicy": { "dnsPolicy": {
"type": "string", "type": "string",
"default": "", "default": "",
"enum": ["", "ClusterFirst", "ClusterFirstWithHostNet", "Default", "None"] "enum": ["", "ClusterFirst", "ClusterFirstWithHostNet", "Default", "None"],
"description": "DNS policy for the pod"
}, },
"deploymentStrategy": { "deploymentStrategy": {
"type": "object", "type": "object",
@@ -110,13 +136,15 @@
"type": { "type": {
"type": "string", "type": "string",
"enum": ["RollingUpdate", "Recreate"], "enum": ["RollingUpdate", "Recreate"],
"default": "RollingUpdate" "default": "RollingUpdate",
"description": "Deployment update strategy"
} }
} }
}, },
"deploymentAnnotations": { "deploymentAnnotations": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Annotations for the Deployment resource"
}, },
"service": { "service": {
"type": "object", "type": "object",
@@ -124,16 +152,32 @@
"type": { "type": {
"type": "string", "type": "string",
"enum": ["ClusterIP", "NodePort", "LoadBalancer"], "enum": ["ClusterIP", "NodePort", "LoadBalancer"],
"default": "ClusterIP" "default": "ClusterIP",
"description": "Kubernetes service type"
}, },
"port": { "port": {
"type": "integer", "type": "integer",
"default": 8096 "minimum": 1,
"maximum": 65535,
"default": 8096,
"description": "Service port for Jellyfin web interface"
},
"nodePort": {
"type": "integer",
"minimum": 30000,
"maximum": 32767,
"description": "NodePort when service type is NodePort"
},
"annotations": {
"type": "object",
"default": {},
"description": "Service annotations"
}, },
"ipFamilyPolicy": { "ipFamilyPolicy": {
"type": "string", "type": "string",
"enum": ["", "SingleStack", "PreferDualStack", "RequireDualStack"], "enum": ["", "SingleStack", "PreferDualStack", "RequireDualStack"],
"default": "" "default": "",
"description": "IP family policy for dual-stack support"
}, },
"ipFamilies": { "ipFamilies": {
"type": "array", "type": "array",
@@ -141,7 +185,10 @@
"type": "string", "type": "string",
"enum": ["IPv4", "IPv6"] "enum": ["IPv4", "IPv6"]
}, },
"default": [] "uniqueItems": true,
"maxItems": 2,
"default": [],
"description": "IP families (IPv4, IPv6, or both)"
} }
} }
}, },
@@ -150,7 +197,54 @@
"properties": { "properties": {
"enabled": { "enabled": {
"type": "boolean", "type": "boolean",
"default": false "default": false,
"description": "Enable Ingress resource"
},
"className": {
"type": "string",
"description": "IngressClass name (e.g., nginx, traefik)"
},
"annotations": {
"type": "object",
"default": {},
"description": "Ingress annotations"
},
"hosts": {
"type": "array",
"items": {
"type": "object",
"required": ["host"],
"properties": {
"host": {
"type": "string",
"format": "hostname",
"description": "Hostname for Ingress"
},
"paths": {
"type": "array",
"items": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "URL path"
},
"pathType": {
"type": "string",
"enum": ["Prefix", "Exact", "ImplementationSpecific"],
"description": "Path matching type"
}
}
}
}
}
}
},
"tls": {
"type": "array",
"items": {
"type": "object"
}
} }
} }
}, },
@@ -159,43 +253,81 @@
"properties": { "properties": {
"enabled": { "enabled": {
"type": "boolean", "type": "boolean",
"default": false "default": false,
"description": "Enable Gateway API HTTPRoute"
},
"parentRefs": {
"type": "array",
"items": {
"type": "object"
}
},
"hostnames": {
"type": "array",
"items": {
"type": "string",
"format": "hostname"
}
},
"rules": {
"type": "array",
"items": {
"type": "object"
}
} }
} }
}, },
"resources": { "resources": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "CPU/Memory resource requests and limits"
}, },
"startupProbe": { "startupProbe": {
"type": ["object", "null"], "type": ["object", "null"],
"default": null "default": null,
"description": "Startup probe configuration. Useful for large media libraries with slow startup."
}, },
"livenessProbe": { "livenessProbe": {
"type": "object" "type": "object",
"description": "Liveness probe configuration"
}, },
"readinessProbe": { "readinessProbe": {
"type": "object" "type": "object",
"description": "Readiness probe configuration"
}, },
"volumes": { "volumes": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "object"
},
"description": "Additional volumes for the pod"
}, },
"volumeMounts": { "volumeMounts": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "object"
},
"description": "Additional volume mounts for the container"
}, },
"nodeSelector": { "nodeSelector": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Node labels for pod assignment"
}, },
"tolerations": { "tolerations": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "object"
},
"description": "Tolerations for pod assignment"
}, },
"affinity": { "affinity": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Affinity rules for pod assignment"
}, },
"podPrivileges": { "podPrivileges": {
"type": "object", "type": "object",
@@ -203,12 +335,12 @@
"hostIPC": { "hostIPC": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Enable hostIPC namespace" "description": "Enable hostIPC namespace. Required for NVIDIA MPS GPU sharing."
}, },
"hostNetwork": { "hostNetwork": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Enable hostNetwork. Mutually exclusive with NetworkPolicy." "description": "Enable hostNetwork. Required for DLNA. Mutually exclusive with NetworkPolicy."
}, },
"hostPID": { "hostPID": {
"type": "boolean", "type": "boolean",
@@ -223,23 +355,78 @@
"enableDLNA": { "enableDLNA": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Enable DLNA. Requires host network. Mutually exclusive with NetworkPolicy." "description": "Enable DLNA support. Requires hostNetwork=true. Mutually exclusive with NetworkPolicy."
}, },
"command": { "command": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "string"
},
"description": "Override container entrypoint"
}, },
"args": { "args": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "string"
},
"description": "Additional arguments for entrypoint"
}, },
"envFrom": { "envFrom": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "object",
"properties": {
"configMapRef": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"optional": {
"type": "boolean"
}
}
},
"secretRef": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"optional": {
"type": "boolean"
}
}
},
"prefix": {
"type": "string"
}
}
},
"description": "Load environment variables from ConfigMap or Secret"
}, },
"env": { "env": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "object",
"required": ["name"],
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
},
"valueFrom": {
"type": "object"
}
}
},
"description": "Additional environment variables"
} }
} }
}, },
@@ -251,7 +438,44 @@
"properties": { "properties": {
"enabled": { "enabled": {
"type": "boolean", "type": "boolean",
"default": true "default": true,
"description": "Enable persistent storage for config. False uses emptyDir."
},
"type": {
"type": "string",
"enum": ["pvc", "hostPath", "emptyDir"],
"default": "pvc",
"description": "Storage type: pvc, hostPath, or emptyDir"
},
"accessMode": {
"type": "string",
"enum": ["ReadWriteOnce", "ReadOnlyMany", "ReadWriteMany", "ReadWriteOncePod"],
"default": "ReadWriteOnce",
"description": "PVC access mode"
},
"size": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?(Ki|Mi|Gi|Ti|Pi|Ei|K|M|G|T|P|E)?$",
"default": "5Gi",
"description": "PVC size (e.g., 5Gi, 10Gi)"
},
"storageClass": {
"type": "string",
"default": "",
"description": "StorageClass name. Empty uses default provisioner."
},
"existingClaim": {
"type": "string",
"description": "Use existing PVC instead of creating new"
},
"hostPath": {
"type": "string",
"description": "Host path when type is hostPath"
},
"annotations": {
"type": "object",
"default": {},
"description": "PVC annotations"
} }
} }
}, },
@@ -260,7 +484,44 @@
"properties": { "properties": {
"enabled": { "enabled": {
"type": "boolean", "type": "boolean",
"default": true "default": true,
"description": "Enable persistent storage for media"
},
"type": {
"type": "string",
"enum": ["pvc", "hostPath", "emptyDir"],
"default": "pvc",
"description": "Storage type: pvc, hostPath, or emptyDir"
},
"accessMode": {
"type": "string",
"enum": ["ReadWriteOnce", "ReadOnlyMany", "ReadWriteMany", "ReadWriteOncePod"],
"default": "ReadWriteOnce",
"description": "PVC access mode"
},
"size": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?(Ki|Mi|Gi|Ti|Pi|Ei|K|M|G|T|P|E)?$",
"default": "25Gi",
"description": "PVC size"
},
"storageClass": {
"type": "string",
"default": "",
"description": "StorageClass name"
},
"existingClaim": {
"type": "string",
"description": "Use existing PVC"
},
"hostPath": {
"type": "string",
"description": "Host path when type is hostPath"
},
"annotations": {
"type": "object",
"default": {},
"description": "PVC annotations"
} }
} }
}, },
@@ -269,7 +530,44 @@
"properties": { "properties": {
"enabled": { "enabled": {
"type": "boolean", "type": "boolean",
"default": false "default": false,
"description": "Enable dedicated cache volume for better performance"
},
"type": {
"type": "string",
"enum": ["pvc", "hostPath", "emptyDir"],
"default": "pvc",
"description": "Storage type"
},
"accessMode": {
"type": "string",
"enum": ["ReadWriteOnce", "ReadOnlyMany", "ReadWriteMany", "ReadWriteOncePod"],
"default": "ReadWriteOnce",
"description": "PVC access mode"
},
"size": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?(Ki|Mi|Gi|Ti|Pi|Ei|K|M|G|T|P|E)?$",
"default": "10Gi",
"description": "PVC size"
},
"storageClass": {
"type": "string",
"default": "",
"description": "StorageClass name (can use faster storage)"
},
"existingClaim": {
"type": "string",
"description": "Use existing PVC"
},
"hostPath": {
"type": "string",
"description": "Host path when type is hostPath"
},
"annotations": {
"type": "object",
"default": {},
"description": "PVC annotations"
} }
} }
} }
@@ -280,14 +578,34 @@
"properties": { "properties": {
"enabled": { "enabled": {
"type": "boolean", "type": "boolean",
"default": false "default": false,
"description": "Enable metrics collection"
}, },
"serviceMonitor": { "serviceMonitor": {
"type": "object", "type": "object",
"properties": { "properties": {
"enabled": { "enabled": {
"type": "boolean", "type": "boolean",
"default": false "default": false,
"description": "Create Prometheus ServiceMonitor"
},
"interval": {
"type": "string",
"pattern": "^[0-9]+(s|m|h)$",
"description": "Scrape interval (e.g., 30s, 1m)"
},
"scrapeTimeout": {
"type": "string",
"pattern": "^[0-9]+(s|m|h)$",
"description": "Scrape timeout"
},
"labels": {
"type": "object",
"description": "Additional labels for ServiceMonitor"
},
"annotations": {
"type": "object",
"description": "ServiceMonitor annotations"
} }
} }
} }
@@ -296,15 +614,26 @@
"initContainers": { "initContainers": {
"type": "array", "type": "array",
"default": [], "default": [],
"description": "DEPRECATED: Use extraInitContainers instead" "items": {
"type": "object"
},
"description": "DEPRECATED: Use extraInitContainers instead. Will be removed after 2030."
}, },
"extraInitContainers": { "extraInitContainers": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "object"
},
"description": "Additional init containers"
}, },
"extraContainers": { "extraContainers": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "object"
},
"description": "Additional sidecar containers"
}, },
"networkPolicy": { "networkPolicy": {
"type": "object", "type": "object",
@@ -312,7 +641,7 @@
"enabled": { "enabled": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Enable NetworkPolicy. Mutually exclusive with hostNetwork and DLNA." "description": "Enable NetworkPolicy for network isolation. Mutually exclusive with hostNetwork and DLNA."
}, },
"policyTypes": { "policyTypes": {
"type": "array", "type": "array",
@@ -320,26 +649,36 @@
"type": "string", "type": "string",
"enum": ["Ingress", "Egress"] "enum": ["Ingress", "Egress"]
}, },
"default": ["Ingress", "Egress"] "uniqueItems": true,
"minItems": 1,
"default": ["Ingress", "Egress"],
"description": "Policy types to enforce"
}, },
"ingress": { "ingress": {
"type": "object", "type": "object",
"properties": { "properties": {
"allowExternal": { "allowExternal": {
"type": "boolean", "type": "boolean",
"default": true "default": true,
"description": "Allow access from any pod/namespace. Set false for production."
}, },
"podSelector": { "podSelector": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Pod selector for allowed ingress (when allowExternal=false)"
}, },
"namespaceSelector": { "namespaceSelector": {
"type": "object", "type": "object",
"default": {} "default": {},
"description": "Namespace selector for allowed ingress"
}, },
"customRules": { "customRules": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "object"
},
"description": "Additional custom ingress rules"
} }
} }
}, },
@@ -348,45 +687,58 @@
"properties": { "properties": {
"allowDNS": { "allowDNS": {
"type": "boolean", "type": "boolean",
"default": true "default": true,
"description": "Allow DNS resolution. Highly recommended."
}, },
"dnsNamespace": { "dnsNamespace": {
"type": "string", "type": "string",
"default": "kube-system" "default": "kube-system",
"description": "Namespace where DNS service runs"
}, },
"dnsPodSelector": { "dnsPodSelector": {
"type": "object", "type": "object",
"default": { "default": {
"k8s-app": "kube-dns" "k8s-app": "kube-dns"
} },
"description": "Pod selector for DNS pods"
}, },
"allowAllEgress": { "allowAllEgress": {
"type": "boolean", "type": "boolean",
"default": true "default": true,
"description": "Allow all internet access for metadata/subtitles"
}, },
"restrictedEgress": { "restrictedEgress": {
"type": "object", "type": "object",
"properties": { "properties": {
"allowMetadata": { "allowMetadata": {
"type": "boolean", "type": "boolean",
"default": true "default": true,
"description": "Allow HTTPS/443 for metadata providers"
}, },
"allowInCluster": { "allowInCluster": {
"type": "boolean", "type": "boolean",
"default": true "default": true,
"description": "Allow pod-to-pod communication"
}, },
"allowedCIDRs": { "allowedCIDRs": {
"type": "array", "type": "array",
"default": [],
"items": { "items": {
"type": "string" "type": "string",
"pattern": "^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$|^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}/[0-9]{1,3}$",
"description": "CIDR notation (e.g., 10.0.0.0/8)"
}, },
"default": [] "description": "Additional allowed IP ranges"
} }
} }
}, },
"customRules": { "customRules": {
"type": "array", "type": "array",
"default": [] "default": [],
"items": {
"type": "object"
},
"description": "Additional custom egress rules"
} }
} }
}, },
@@ -395,13 +747,15 @@
"properties": { "properties": {
"namespace": { "namespace": {
"type": "string", "type": "string",
"default": "" "default": "",
"description": "Prometheus namespace. Empty uses same as Jellyfin."
}, },
"podSelector": { "podSelector": {
"type": "object", "type": "object",
"default": { "default": {
"app.kubernetes.io/name": "prometheus" "app.kubernetes.io/name": "prometheus"
} },
"description": "Prometheus pod selector"
} }
} }
} }
@@ -417,9 +771,11 @@
"enabled": { "enabled": {
"const": true "const": true
} }
},
"required": ["enabled"]
} }
} },
} "required": ["networkPolicy"]
}, },
"then": { "then": {
"not": { "not": {
@@ -431,9 +787,11 @@
"enableDLNA": { "enableDLNA": {
"const": true "const": true
} }
},
"required": ["enableDLNA"]
} }
} },
} "required": ["jellyfin"]
}, },
{ {
"properties": { "properties": {
@@ -442,9 +800,11 @@
"hostNetwork": { "hostNetwork": {
"const": true "const": true
} }
},
"required": ["hostNetwork"]
} }
} },
} "required": ["podPrivileges"]
} }
] ]
} }