Gitea #143, maintainer's instruction. The HBONE port and the redirected probe source are ISTIO CONSTANTS, not standards, so they move from template literals into values with documented defaults - an operator can then correct them when Istio changes a number, without waiting for a chart release. One object under one "enabled", so the two halves still cannot be set separately; two values could drift apart, which is what this design exists to prevent. Every read is null-safe (#99), so a deleted object or one carrying only "enabled: true" falls back to the defaults declared here rather than dereferencing nil - the defect #136 shipped one value away. Provenance now written on each constant: 15008 is Istio's HBONE port, not IANA-registered for this and unknown to Kubernetes; 169.254.7.127 is link-local (RFC 3927, never routed), its IPv6 counterpart carries the same digits plus 1337, and neither the NetworkPolicy API nor the kubelet knows the address - only Istio's data plane rewrites the probe source onto it. Both read for ztunnel 1.30.6, and a change in either fails silently. The exception in ci/run-networkpolicy-test.sh stays pinned to the numbers that test names, and additionally asserts this chart declares the same defaults - never "whatever the value says", which would let a mistyped port in through a configurable field. No version change: nothing is published yet, the default is still off, and the render at defaults is byte-identical - re-diffed against origin/main.
965 lines
36 KiB
JSON
965 lines
36 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft-07/schema#",
|
|
"title": "freesailarr values",
|
|
"description": "Values schema (issue #68). additionalProperties is false on every chart-owned object level, so a key the chart does not read is rejected at render time instead of being silently ignored. The objects that go verbatim into the pod spec - probes, security contexts, free-form annotations, global - stay open, because the chart does not interpret their contents. The hardening, probe and resources blocks additionally accept null: an override may erase them, and the chart then falls back to its own defaults (issue #99) rather than rendering a container without hardening, without a probe or without resource accounting.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"global": {
|
|
"description": "Helm's conventional cross-chart values. Unused by this chart, kept open so an umbrella install cannot break on it.",
|
|
"type": "object"
|
|
},
|
|
"networkPolicy": {
|
|
"description": "The chart's NetworkPolicy (Gitea #5), on by default. A ceiling on reachability and not an authentication layer: only the declared container ports are reachable at all, egress is capped by PORT (a NetworkPolicy matches CIDRs and selectors, never hostnames, so a destination host cannot be expressed), and ingress is capped by source for every port no network probe touches. Only a boundary where the cluster's CNI enforces policies - an unenforced policy is accepted by the API server and filters nothing. The template's header carries the full list of what it does not do.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"allowedNamespaces": {
|
|
"description": "Namespaces whose pods may reach this workload, in addition to the release's own namespace, which is always allowed. Matched on the kubernetes.io/metadata.name label Kubernetes sets on every namespace itself, so no labelling work is needed - and no typo protection either: a name that does not exist matches nothing and is silently ineffective. An entry opens the port to EVERY pod in that namespace; for the ingress controller use ingressController instead.",
|
|
"type": ["array", "null"],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"probeSources": {
|
|
"description": "Source CIDRs allowed to reach the port the network probes address. EMPTY MEANS OPEN, not blocked: with no entry that rule is rendered without a \"from\" selector. The kubelet's probes come from the IP of the worker node running the pod, an address the chart cannot know and must not guess - a guessed CIDR that excludes the real node blocks every probe and the pod never becomes Ready, which reads as a broken application rather than as a policy. Setting this restricts the rule, which is why the chart adds the own namespace, ingressController and allowedNamespaces to the same rule.",
|
|
"type": ["array", "null"],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"ingressController": {
|
|
"description": "The ingress controller allowed to reach the routed port once probeSources restricts it; rendered only while ingress.enabled or httpRoute.enabled is true. The two keys become ONE \"from\" entry and are therefore an AND - pods carrying podLabels IN namespace - which is the whole point: the controller's namespace in allowedNamespaces would open the port to every pod in kube-system. The default is measured on this cluster (DaemonSet rke2-traefik in kube-system, which also serves the Gateway API here), not assumed; a policy written against the wrong controller is accepted, looks strict and silently blocks all web traffic.",
|
|
"type": ["object", "null"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"namespace": {
|
|
"type": ["string", "null"]
|
|
},
|
|
"podLabels": {
|
|
"description": "Label set the controller's pods carry, rendered verbatim as a podSelector's matchLabels. Kept open because the chart does not interpret the keys.",
|
|
"type": ["object", "null"]
|
|
}
|
|
}
|
|
},
|
|
"ingress": {
|
|
"description": "The policy's INBOUND side. Three names here overlap as substrings and are not the same thing: the top-level ingress is the Ingress OBJECT this chart renders, networkPolicy.ingress is this block, and networkPolicy.ingressController is the SOURCE SELECTOR for the ingress controller's pods - a SIBLING of this block and no part of it.",
|
|
"type": ["object", "null"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"ambientMesh": {
|
|
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
|
|
"type": ["object", "null"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"description": "The switch. Everything else in this object is inert while it is false.",
|
|
"type": ["boolean", "null"]
|
|
},
|
|
"port": {
|
|
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
|
|
"type": ["integer", "null"],
|
|
"minimum": 1,
|
|
"maximum": 65535
|
|
},
|
|
"redirectedProbeSources": {
|
|
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
|
|
"type": ["array", "null"],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"egress": {
|
|
"type": ["object", "null"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"extraPorts": {
|
|
"description": "Additional outbound TCP ports, to ANY destination - ports and not destinations, because a NetworkPolicy cannot express a hostname. DNS is always allowed and has no switch. Without an entry here an outbound port the chart does not already derive is blocked, which is the cap's whole purpose and also its upgrade risk.",
|
|
"type": ["array", "null"],
|
|
"items": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"scaleDown": {
|
|
"type": "boolean"
|
|
},
|
|
"replicas": {
|
|
"type": "integer",
|
|
"minimum": 0
|
|
},
|
|
"secrets": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"gluetunSecretRef": {
|
|
"description": "op:// item path for the gluetun VPN credentials. The item must carry the fields wireguard-private-key and wireguard-addresses; the template reads exactly those names.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"puid": {
|
|
"description": "User the linuxserver.io containers drop to after their s6-overlay init, and runAsUser for seerr and recyclarr.",
|
|
"type": "integer"
|
|
},
|
|
"pgid": {
|
|
"type": "integer"
|
|
},
|
|
"timezone": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/podSecurityContextBlock"
|
|
},
|
|
"storage": {
|
|
"description": "StorageClass for the config volumeClaimTemplates. Deliberately NOT null-safe: an erased block yields an invalid claim template, which fails loudly, and issue #99 covers only the silent class.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"storageClass": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"media": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"downloads": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"existingClaim": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"subPath": {
|
|
"description": "Sub-directory of the claim to mount instead of its root. null means the claim root.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"movies": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"existingClaim": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"subPath": {
|
|
"description": "Sub-directory of the claim to mount instead of its root. null means the claim root.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"tv": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"existingClaim": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"subPath": {
|
|
"description": "Sub-directory of the claim to mount instead of its root. null means the claim root.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"ingress": {
|
|
"description": "Ingress defaults. Two shapes: one host per app (each app carries its own host), or one shared domain with sub-paths (ingress.host set, per-app hosts empty). A per-app host always wins, so both mix.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"description": "Master switch; false suppresses every app ingress regardless of the per-app toggles. Mutually exclusive with httpRoute.enabled - the render aborts when both are true, and since this defaults to true it has to be switched off explicitly to use the Gateway mode.",
|
|
"type": "boolean"
|
|
},
|
|
"host": {
|
|
"description": "Shared domain for the sub-path mode. Empty or null means the default: every app on a host of its own. Read by BOTH routing modes - it names the public address of the stack and is not an Ingress switch. This chart has no ingress.domain, so this value and the per-app <app>.ingress.host are also where the HTTPRoutes take their hostnames from.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"clusterIssuer": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"className": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"annotations": {
|
|
"description": "Extra Ingress annotations, merged with the cert-manager annotation the chart always sets. Free-form keys by nature, so this object stays open. No counterpart in Gateway mode, per-app overrides included: the Gateway API has no annotation channel, so anything set here is gone when httpRoute.enabled is used instead. The chart needs nothing from it - qBittorrent's prefix-stripping rewrite has a per-rule URLRewrite filter as its equivalent there - but an annotation you added yourself is lost silently.",
|
|
"type": "object"
|
|
}
|
|
}
|
|
},
|
|
"httpRoute": {
|
|
"description": "Gateway-API alternative to the Ingress (Gitea #114). Mutually exclusive with ingress.enabled - the render aborts when both are true, and ingress.enabled defaults to true in this chart. The chart renders only HTTPRoutes with parentRefs; class and certificate sit on the Gateway and belong to the platform. Both ingress shapes work unchanged, and the sub-path shape gets simpler: a URLRewrite filter is per rule, so qBittorrent needs no second routing object. There is deliberately no hostnames key - this chart has no ingress.domain either, and a chart-global hostname list could not say which of the ten apps it belonged to, so the hostnames stay in ingress.host and <app>.ingress.host. The Gateway-API CRDs must be present in the cluster, because the chart cannot detect them.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"parentRefs": {
|
|
"description": "The Gateway(s) every route of this release attaches to. Required while httpRoute.enabled is true, enforced in the template (an empty list aborts the render) - a JSON Schema \"required\" cannot do it, because the key ships as an empty list and is therefore present. Entries are the Gateway API's ParentReference and are passed through unchanged.",
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/parentRef"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"gluetun": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"image": {
|
|
"$ref": "#/definitions/image"
|
|
},
|
|
"vpn": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"serviceProvider": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"type": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"serverCities": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"serverCountries": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"dnsUpstreamResolvers": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"firewallOutboundSubnets": {
|
|
"description": "Comma-separated CIDRs the gluetun firewall allows outbound outside the tunnel. Set to the cluster pod and service CIDRs.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"storage": {
|
|
"$ref": "#/definitions/appStorage"
|
|
},
|
|
"env": {
|
|
"$ref": "#/definitions/env"
|
|
},
|
|
"livenessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"readinessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"startupProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
},
|
|
"qbittorrent": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"image": {
|
|
"$ref": "#/definitions/image"
|
|
},
|
|
"ingress": {
|
|
"$ref": "#/definitions/appIngress"
|
|
},
|
|
"storage": {
|
|
"$ref": "#/definitions/appStorage"
|
|
},
|
|
"networkInterface": {
|
|
"description": "Interface the BitTorrent session binds to (gluetun's tun device). Enforced on every start, because a deviating value would be a leak. null or \"\" leaves the binding unset - which lets libtorrent announce past the tunnel.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"savePath": {
|
|
"description": "Default save path for new torrents, seeded into the persisted config ONLY when the key is absent - never replaced, so a path changed in the WebUI survives a restart. null or \"\" opts out and leaves qBittorrent on its built-in default /config/Downloads, i.e. the small config volume.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"configInit": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"image": {
|
|
"$ref": "#/definitions/image"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
},
|
|
"env": {
|
|
"$ref": "#/definitions/env"
|
|
},
|
|
"livenessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"readinessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"startupProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
},
|
|
"prowlarr": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"image": {
|
|
"$ref": "#/definitions/image"
|
|
},
|
|
"ingress": {
|
|
"$ref": "#/definitions/appIngress"
|
|
},
|
|
"storage": {
|
|
"$ref": "#/definitions/appStorage"
|
|
},
|
|
"env": {
|
|
"$ref": "#/definitions/env"
|
|
},
|
|
"trustedNetworks": {
|
|
"$ref": "#/definitions/trustedNetworks"
|
|
},
|
|
"livenessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"readinessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"startupProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
},
|
|
"flaresolverr": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"image": {
|
|
"$ref": "#/definitions/image"
|
|
},
|
|
"env": {
|
|
"$ref": "#/definitions/env"
|
|
},
|
|
"livenessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"readinessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"startupProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
},
|
|
"radarr": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"image": {
|
|
"$ref": "#/definitions/image"
|
|
},
|
|
"ingress": {
|
|
"$ref": "#/definitions/appIngress"
|
|
},
|
|
"storage": {
|
|
"$ref": "#/definitions/appStorage"
|
|
},
|
|
"env": {
|
|
"$ref": "#/definitions/env"
|
|
},
|
|
"trustedNetworks": {
|
|
"$ref": "#/definitions/trustedNetworks"
|
|
},
|
|
"livenessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"readinessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"startupProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
},
|
|
"recyclarr": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"image": {
|
|
"$ref": "#/definitions/image"
|
|
},
|
|
"storage": {
|
|
"$ref": "#/definitions/appStorage"
|
|
},
|
|
"env": {
|
|
"$ref": "#/definitions/env"
|
|
},
|
|
"livenessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"readinessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"startupProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
},
|
|
"sonarr": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"image": {
|
|
"$ref": "#/definitions/image"
|
|
},
|
|
"ingress": {
|
|
"$ref": "#/definitions/appIngress"
|
|
},
|
|
"storage": {
|
|
"$ref": "#/definitions/appStorage"
|
|
},
|
|
"env": {
|
|
"$ref": "#/definitions/env"
|
|
},
|
|
"trustedNetworks": {
|
|
"$ref": "#/definitions/trustedNetworks"
|
|
},
|
|
"livenessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"readinessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"startupProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
},
|
|
"seerr": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"image": {
|
|
"$ref": "#/definitions/image"
|
|
},
|
|
"ingress": {
|
|
"$ref": "#/definitions/appIngress"
|
|
},
|
|
"storage": {
|
|
"$ref": "#/definitions/appStorage"
|
|
},
|
|
"env": {
|
|
"$ref": "#/definitions/env"
|
|
},
|
|
"livenessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"readinessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"startupProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
},
|
|
"bazarr": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"image": {
|
|
"$ref": "#/definitions/image"
|
|
},
|
|
"ingress": {
|
|
"$ref": "#/definitions/appIngress"
|
|
},
|
|
"storage": {
|
|
"$ref": "#/definitions/appStorage"
|
|
},
|
|
"env": {
|
|
"$ref": "#/definitions/env"
|
|
},
|
|
"livenessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"readinessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"startupProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
},
|
|
"mediathekarr": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"image": {
|
|
"$ref": "#/definitions/pinnedImage"
|
|
},
|
|
"ingress": {
|
|
"$ref": "#/definitions/appIngress"
|
|
},
|
|
"storage": {
|
|
"$ref": "#/definitions/appStorage"
|
|
},
|
|
"startGate": {
|
|
"description": "Delays the container until gluetun reports healthy. NOT null-safe on purpose and not merged through the fallback helper: this block decides whether the gate is rendered at all, and an erased block would silently produce a container that starts before the tunnel - the failure this chart writes three paragraphs about, and one that looks green.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"intervalSeconds": {
|
|
"type": "integer",
|
|
"minimum": 1
|
|
},
|
|
"timeoutSeconds": {
|
|
"description": "Total wait before the container exits instead of starting without a tunnel. Keep the startup probe's budget (periodSeconds x failureThreshold) above it, or the kubelet kills the container while the gate is still waiting.",
|
|
"type": "integer",
|
|
"minimum": 1
|
|
},
|
|
"settleSeconds": {
|
|
"type": "integer",
|
|
"minimum": 0
|
|
}
|
|
}
|
|
},
|
|
"downloads": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"directory": {
|
|
"description": "Sub-directory on the shared downloads claim holding incomplete/ and complete/. The chart derives DOWNLOAD_INCOMPLETE_PATH and DOWNLOAD_COMPLETE_PATH from it; complete/ is what Sonarr/Radarr are told to import from, under the identical absolute path.",
|
|
"type": "string",
|
|
"minLength": 1
|
|
}
|
|
}
|
|
},
|
|
"env": {
|
|
"$ref": "#/definitions/env"
|
|
},
|
|
"livenessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"readinessProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"startupProbe": {
|
|
"$ref": "#/definitions/probe"
|
|
},
|
|
"resources": {
|
|
"$ref": "#/definitions/resources"
|
|
},
|
|
"securityContext": {
|
|
"$ref": "#/definitions/containerSecurityContext"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"definitions": {
|
|
"parentRef": {
|
|
"description": "A Gateway API ParentReference, passed verbatim into HTTPRoute.spec.parentRefs. Deliberately left OPEN (no additionalProperties: false), for the same reason probes and security contexts are: Kubernetes owns this schema, the Gateway API adds fields to it across releases, and a closed list here would reject a field only upstream knows about. What the chart can assert is that \"name\" is there - it is required by the API and a reference without it attaches to nothing, which is the silent failure this chart aborts the render over.",
|
|
"type": "object",
|
|
"required": [
|
|
"name"
|
|
]
|
|
},
|
|
"image": {
|
|
"description": "Every image in this chart is pinned here, gluetun included; no tag comes from appVersion. This is also what puts them all under Renovate's helm-values manager, which reads exactly these two keys.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"repository": {
|
|
"type": "string"
|
|
},
|
|
"tag": {
|
|
"type": [
|
|
"string",
|
|
"number"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"appIngress": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"host": {
|
|
"description": "Host of this app's own Ingress object. Required while this app's ingress is enabled AND the shared ingress.host is not set; never demanded when this app's ingress is off, and never demanded for an app served under the shared domain. Setting it always wins over the shared domain.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"path": {
|
|
"description": "Sub-path under the shared ingress.host, e.g. /sonarr. Read only in the shared-domain mode and ignored while this app has a host of its own. Null or empty falls back to /<app>, and to / for seerr, which cannot be served anywhere else.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"clusterIssuer": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"className": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"annotations": {
|
|
"description": "Overrides the global ingress annotations for this app; free-form, so kept open.",
|
|
"type": [
|
|
"object",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"trustedNetworks": {
|
|
"description": "Comma-separated CIDRs whose X-Forwarded-For/-Proto/-Host headers this servarr app honours, rendered as <APP>__SERVER__TRUSTEDNETWORKS and only when non-empty. Empty is the default and means no proxy is trusted - the app then reports the ingress controller's pod IP as the client and sees the scheme as http behind TLS termination. Kept null-safe deliberately: an erased value is the same permissive-to-nobody state as the default, so it must not fail the render.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"appStorage": {
|
|
"description": "Size of this app's config volumeClaimTemplate. Not null-safe on purpose: an erased size yields an invalid claim template and fails loudly.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"size": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"resources": {
|
|
"description": "May be null / empty: the chart then falls back to this container's own default requests and computed limits (issue #99).",
|
|
"type": [
|
|
"object",
|
|
"null"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"limitFactor": {
|
|
"type": "number",
|
|
"exclusiveMinimum": 0
|
|
},
|
|
"requests": {
|
|
"$ref": "#/definitions/resourceList"
|
|
},
|
|
"limits": {
|
|
"$ref": "#/definitions/resourceList"
|
|
}
|
|
}
|
|
},
|
|
"resourceList": {
|
|
"description": "Both spellings of the ephemeral-storage key are accepted because the resources helper normalizes camelCase to the Kubernetes key.",
|
|
"type": [
|
|
"object",
|
|
"null"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"cpu": {
|
|
"type": [
|
|
"string",
|
|
"number",
|
|
"null"
|
|
]
|
|
},
|
|
"memory": {
|
|
"type": [
|
|
"string",
|
|
"number",
|
|
"null"
|
|
]
|
|
},
|
|
"ephemeral-storage": {
|
|
"type": [
|
|
"string",
|
|
"number",
|
|
"null"
|
|
]
|
|
},
|
|
"ephemeralStorage": {
|
|
"type": [
|
|
"string",
|
|
"number",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"env": {
|
|
"type": [
|
|
"array",
|
|
"null"
|
|
],
|
|
"items": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"name"
|
|
],
|
|
"properties": {
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"value": {
|
|
"type": "string"
|
|
},
|
|
"valueFrom": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"secretKeyRef": {
|
|
"$ref": "#/definitions/keyRef"
|
|
},
|
|
"configMapKeyRef": {
|
|
"$ref": "#/definitions/keyRef"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"keyRef": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"name",
|
|
"key"
|
|
],
|
|
"properties": {
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"key": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"podSecurityContextBlock": {
|
|
"description": "May be null / empty at any level: the chart then falls back to its pod hardening defaults (issue #99), seccompProfile included.",
|
|
"type": [
|
|
"object",
|
|
"null"
|
|
],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"pod": {
|
|
"description": "Merged onto the chart's pod hardening defaults; kept open because the chart does not interpret the individual fields.",
|
|
"type": [
|
|
"object",
|
|
"null"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"containerSecurityContext": {
|
|
"description": "Merged onto THIS container's hardening defaults; kept open because the chart does not interpret the individual fields. May be null / empty, in which case those defaults apply (issue #99). The fallbacks reproduce this chart's deliberate deviations - the linuxserver.io containers start as root and keep a writable root filesystem so their s6 init can drop to PUID/PGID, and restoring the 'clean' standard there would leave them on the image's uid 911.",
|
|
"type": [
|
|
"object",
|
|
"null"
|
|
]
|
|
},
|
|
"probe": {
|
|
"description": "Merged onto this container's probe defaults; kept open because the chart does not interpret the individual fields. May be null / empty, which means the defaults apply (issue #99) rather than the probe disappearing. The defaults carry their context - path and port, and recyclarr's exec command, since it serves no HTTP port.",
|
|
"type": [
|
|
"object",
|
|
"null"
|
|
]
|
|
},
|
|
"pinnedImage": {
|
|
"description": "Like image, plus the digest mediathekarr is pinned by: its tags are unusable (\"latest\" is a 2024 build, \"beta\" moves), so the tag stays as the readable label and the digest is what is actually pulled. Deliberately a separate definition instead of adding an optional digest to image: on every other image in this chart a digest key would be a value the templates never read - exactly the silent no-op the schema exists to reject (#68).",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"repository": {
|
|
"type": "string"
|
|
},
|
|
"tag": {
|
|
"type": [
|
|
"string",
|
|
"number"
|
|
]
|
|
},
|
|
"digest": {
|
|
"description": "Image digest, sha256:<64 hex>. Required - the template refuses to render without it.",
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|