diff --git a/scripts/generate-schema.py b/scripts/generate-schema.py new file mode 100755 index 0000000..c566793 --- /dev/null +++ b/scripts/generate-schema.py @@ -0,0 +1,335 @@ +#!/usr/bin/env python3 +"""Generate values.schema.json from Helm template .Values references. + +Scans all templates for .Values.* usage, infers types from context +(pgdog.intval → integer, | quote → string, toYaml → object, etc.), +and merges manually maintained overrides (enums, descriptions, required +fields) from scripts/schema-overrides.yaml. + +Usage: + python3 scripts/generate-schema.py # writes values.schema.json + python3 scripts/generate-schema.py --check # exits non-zero if schema is stale +""" + +import json +import os +import re +import sys +from pathlib import Path + +# Optional: PyYAML for overrides. Falls back gracefully if not installed. +try: + import yaml +except ImportError: + yaml = None + +REPO_ROOT = Path(__file__).resolve().parent.parent +TEMPLATES_DIR = REPO_ROOT / "templates" +SCHEMA_PATH = REPO_ROOT / "values.schema.json" +OVERRIDES_PATH = REPO_ROOT / "scripts" / "schema-overrides.yaml" + +# --------------------------------------------------------------------------- +# 1. Extract .Values references and their template context +# --------------------------------------------------------------------------- + +# Matches .Values.foo.bar.baz (captures the dotted path after .Values.) +VALUES_RE = re.compile(r"\.Values\.([a-zA-Z_][\w]*(?:\.[a-zA-Z_][\w]*)*)") + +# Context patterns that hint at a type. Order matters: first match wins. +# NOTE: boolean is never auto-inferred — it must come from overrides. +# Template `if .Values.X` is a truthiness check, not a type indicator. +CONTEXT_PATTERNS = [ + # pgdog.intval → integer (accepts int or underscore-separated string) + (re.compile(r'include\s+"pgdog\.intval".*\.Values\.{path}'), "integer"), + (re.compile(r'\.Values\.{path}.*\|\s*int'), "integer"), + # | toYaml / with .Values.X → object + (re.compile(r"\.Values\.{path}\s*\|\s*toYaml"), "object"), + (re.compile(r"toYaml\s+\.Values\.{path}"), "object"), + (re.compile(r"with\s+\.Values\.{path}\s"), "object"), + # | toToml → array + (re.compile(r"\.Values\.{path}\s*\|\s*toToml"), "array"), + # range .Values.X → array + (re.compile(r"range\s+\.Values\.{path}"), "array"), + # | quote → string + (re.compile(r"\.Values\.{path}\s*\|\s*quote"), "string"), +] + + +def scan_templates(): + """Return {dotted_path: set_of_inferred_types} from all template files.""" + refs = {} # path → set of type hints + + for tpl in TEMPLATES_DIR.rglob("*"): + if tpl.is_dir() or tpl.suffix not in (".yaml", ".yml", ".tpl", ".txt"): + continue + text = tpl.read_text() + + for m in VALUES_RE.finditer(text): + path = m.group(1) + if path not in refs: + refs[path] = set() + + # Try each context pattern to infer type + for pattern, typ in CONTEXT_PATTERNS: + concrete = pattern.pattern.replace("{path}", re.escape(path)) + if re.search(concrete, text): + refs[path].add(typ) + break + + return refs + + +# --------------------------------------------------------------------------- +# 2. Build a nested property tree +# --------------------------------------------------------------------------- + + +def build_tree(refs): + """Convert flat dotted paths into a nested dict. + + Each leaf is {"_types": set(), "_is_leaf": True}. + Intermediate nodes are plain dicts. + """ + tree = {} + for dotted, types in refs.items(): + parts = dotted.split(".") + node = tree + for i, part in enumerate(parts): + if part not in node: + node[part] = {} + node = node[part] + if i == len(parts) - 1: + node["_types"] = types + node["_is_leaf"] = True + return tree + + +# --------------------------------------------------------------------------- +# 3. Resolve types +# --------------------------------------------------------------------------- + +# Fields whose template usage looks boolean (if .Values.X) but are actually +# strings/numbers are handled via overrides. This function just picks the +# best single type from the inferred set. + +TYPE_PRIORITY = {"integer": 0, "string": 1, "array": 2, "object": 3, "boolean": 4} + + +def pick_type(types): + """Pick the most specific type from a set of inferred types.""" + if not types: + return "string" # safe default for unknown + # If we have both object and string (e.g. toYaml + quote in different + # contexts), prefer object since quote may be from a sub-field. + if "object" in types: + return "object" + if "array" in types: + return "array" + if "integer" in types: + return "integer" + # Return the highest-priority (most specific) type + return min(types, key=lambda t: TYPE_PRIORITY.get(t, 99)) + + +# --------------------------------------------------------------------------- +# 4. Load overrides +# --------------------------------------------------------------------------- + + +def load_overrides(): + """Load schema-overrides.yaml if it exists and PyYAML is available.""" + if yaml is None: + print("WARNING: PyYAML not installed; skipping overrides", file=sys.stderr) + return {} + if not OVERRIDES_PATH.exists(): + return {} + with open(OVERRIDES_PATH) as f: + data = yaml.safe_load(f) or {} + return data + + +# --------------------------------------------------------------------------- +# 5. Generate JSON Schema +# --------------------------------------------------------------------------- + +# Shared $defs that are referenced via $ref +DEFS = { + "resources": { + "type": "object", + "description": "Kubernetes resource requests and limits", + "additionalProperties": False, + "properties": { + "requests": { + "type": "object", + "additionalProperties": False, + "properties": { + "cpu": {"type": ["string", "number"]}, + "memory": {"type": "string"}, + }, + }, + "limits": { + "type": "object", + "additionalProperties": False, + "properties": { + "cpu": {"type": ["string", "number"]}, + "memory": {"type": "string"}, + }, + }, + }, + }, + "awsLb": { + "type": "object", + "description": "AWS Load Balancer Controller configuration", + "additionalProperties": False, + "properties": { + "enabled": {"type": "boolean", "description": "Enable AWS LB annotations"}, + "scheme": { + "type": "string", + "description": "Load balancer scheme", + "enum": ["internet-facing", "internal"], + }, + }, + }, +} + +# Properties that should use a $ref instead of being auto-generated +REF_MAP = { + "resources": "#/$defs/resources", + "prometheusResources": "#/$defs/resources", + "gateway.resources": "#/$defs/resources", + "prometheusCollector.resources": "#/$defs/resources", + "service.aws": "#/$defs/awsLb", + "prometheusCollector.service.aws": "#/$defs/awsLb", +} + + +def apply_overrides(prop, override): + """Merge override keys into a property dict.""" + for key in ("description", "enum", "minimum", "maximum", "pattern", + "minItems", "maxItems"): + if key in override: + prop[key] = override[key] + if "type" in override: + prop["type"] = override["type"] + if "required" in override: + prop["required"] = override["required"] + if "items" in override: + prop["items"] = override["items"] + + +def tree_to_schema(tree, overrides, path_prefix=""): + """Recursively convert property tree to JSON Schema properties dict.""" + properties = {} + + for key, node in sorted(tree.items()): + if key.startswith("_"): + continue + + full_path = f"{path_prefix}.{key}" if path_prefix else key + override = overrides.get(full_path, {}) + + # Check if this should be a $ref + ref_path = REF_MAP.get(full_path) + if ref_path: + prop = {"$ref": ref_path} + if "description" in override: + prop["description"] = override["description"] + properties[key] = prop + continue + + is_leaf = node.get("_is_leaf", False) + types = node.get("_types", set()) + + # Filter out internal keys to find children + children = {k: v for k, v in node.items() + if not k.startswith("_") and isinstance(v, dict)} + + if children and not is_leaf: + # Pure object node + child_props = tree_to_schema(children, overrides, full_path) + prop = { + "type": "object", + "additionalProperties": False, + "properties": child_props, + } + elif children and is_leaf: + # Node that is both used directly AND has children + # This means the templates access both .Values.X and .Values.X.foo + # Treat as object with children + child_props = tree_to_schema(children, overrides, full_path) + prop = { + "type": "object", + "additionalProperties": False, + "properties": child_props, + } + else: + # Leaf node + resolved = pick_type(types) + prop = {"type": resolved} + + if resolved == "integer": + prop["minimum"] = 0 + if resolved == "array": + prop["items"] = {"type": "object"} + + apply_overrides(prop, override) + properties[key] = prop + + return properties + + +def generate(): + print("Scanning templates...", file=sys.stderr) + refs = scan_templates() + print(f" Found {len(refs)} .Values.* references", file=sys.stderr) + + tree = build_tree(refs) + overrides = load_overrides() + + properties = tree_to_schema(tree, overrides) + + schema = { + "$schema": "https://json-schema.org/draft-07/schema#", + "title": "PgDog Helm Chart Values", + "description": ( + "Schema for validating PgDog Helm chart values. " + "All object levels use additionalProperties: false to catch typos. " + "Generated by scripts/generate-schema.py — do not edit by hand." + ), + "type": "object", + "additionalProperties": False, + "properties": properties, + "$defs": DEFS, + } + + return schema + + +def main(): + check_mode = "--check" in sys.argv + + schema = generate() + new_content = json.dumps(schema, indent=2, ensure_ascii=False) + "\n" + + if check_mode: + if SCHEMA_PATH.exists(): + existing = SCHEMA_PATH.read_text() + if existing == new_content: + print("values.schema.json is up to date.", file=sys.stderr) + sys.exit(0) + else: + print( + "values.schema.json is STALE. Run: python3 scripts/generate-schema.py", + file=sys.stderr, + ) + sys.exit(1) + else: + print("values.schema.json does not exist.", file=sys.stderr) + sys.exit(1) + + SCHEMA_PATH.write_text(new_content) + print(f"Wrote {SCHEMA_PATH}", file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/scripts/schema-overrides.yaml b/scripts/schema-overrides.yaml new file mode 100644 index 0000000..a3770d7 --- /dev/null +++ b/scripts/schema-overrides.yaml @@ -0,0 +1,657 @@ +# Schema overrides for values that can't be fully inferred from templates. +# Keys are dotted paths matching .Values.* references. +# Supported fields: type, description, enum, minimum, maximum, pattern, +# required, items, minItems, maxItems + +# --- Top-level settings --- +nameOverride: + description: Override the chart name +fullnameOverride: + description: Override the full release name +labels: + description: Custom labels for resources +selectorLabels: + description: Custom selector labels for resources +annotations: + description: Custom annotations for the deployment +podLabels: + description: Custom labels for pods +podAnnotations: + description: Custom annotations for pods +restartOnConfigChange: + description: Trigger rolling restart when pgdog config changes +clusterName: + description: Kubernetes cluster name, added as a label to Prometheus metrics + +# --- Image --- +image.repository: + type: string + description: Docker image repository +image.tag: + type: string + description: Docker image tag (defaults to Chart appVersion) +image.digest: + type: string + description: Image digest (overrides tag when specified) +image.pullPolicy: + enum: ["Always", "IfNotPresent", "Never"] + description: Image pull policy +image.name: + type: string + description: "DEPRECATED: Full image name (use repository and tag instead)" + +# --- Ports --- +port: + type: integer + description: Port on which PgDog will run + minimum: 1 + maximum: 65535 +healthcheckPort: + type: integer + description: Port for healthcheck endpoint + minimum: 1 + maximum: 65535 +openMetricsPort: + type: integer + description: Port for OpenMetrics (Prometheus) export + minimum: 1 + maximum: 65535 +prometheusPort: + type: integer + description: Port for the Prometheus sidecar + minimum: 1 + maximum: 65535 + +# --- Replicas --- +replicas: + type: integer + description: Number of PgDog replicas + minimum: 1 + +# --- Scheduling --- +priorityClassName: + type: string + description: PriorityClass name for pgdog pods +terminationGracePeriodSeconds: + type: integer + description: Grace period for pod termination +preStopSleepSeconds: + type: integer + description: Delay before stopping container to allow endpoint updates + +# --- Probes (free-form objects) --- +livenessProbe: + type: object + description: Container liveness probe configuration +readinessProbe: + type: object + description: Container readiness probe configuration +startupProbe: + type: object + description: Container startup probe configuration +strategy: + type: object + description: Deployment rollout strategy + +# --- StatefulSet --- +statefulSet.enabled: + type: boolean + description: Deploy as StatefulSet instead of Deployment + +# --- Env --- +env: + type: array + description: Custom environment variables for the pgdog container + items: + type: object + required: ["name"] + properties: + name: + type: string + value: + type: string + valueFrom: + type: object + +# --- Extra containers/volumes --- +extraInitContainers: + type: array + description: Additional init containers + items: + type: object +extraVolumes: + type: array + description: Additional volumes for the pod + items: + type: object +extraVolumeMounts: + type: array + description: Additional volume mounts for the pgdog container + items: + type: object + +# --- Resources --- +noCpuLimits: + type: boolean + description: Remove CPU limits from containers +resources: + description: Resource requests and limits for the pgdog container +prometheusResources: + description: Resource requests and limits for the prometheus sidecar + +# --- Image pull secrets --- +imagePullSecrets: + type: array + description: Image pull secrets for private registries + items: + type: object + additionalProperties: false + required: ["name"] + properties: + name: + type: string + +# --- Logging --- +logFormat: + enum: ["text", "json"] + description: Log output format +logLevel: + enum: ["error", "warn", "info", "debug", "trace", "off"] + description: Log level (RUST_LOG syntax) +logConnections: + type: ["boolean", "string"] + description: Log connections +logDisconnections: + type: ["boolean", "string"] + description: Log disconnections + +# --- Pool & connection settings --- +poolerMode: + enum: ["transaction", "session", "statement"] + description: Connection pooler mode +loadBalancingStrategy: + enum: ["round_robin", "random", "least_active_connections"] + description: Load balancing strategy +workers: + type: integer + minimum: 1 + description: Number of worker threads +defaultPoolSize: + type: integer + minimum: 1 + description: Default connection pool size +connectAttempts: + type: integer + minimum: 1 + description: Number of connection attempts + +# --- Boolean/string fields where templates use both forms --- +dryRun: + type: ["boolean", "string"] + description: Enable dry run mode +crossShardDisabled: + type: ["boolean", "string"] + description: Disable cross-shard queries +expandedExplain: + type: ["boolean", "string"] + description: Enable expanded explain +tlsClientRequired: + type: ["boolean", "string"] + description: Require TLS client certificates +mirrorExposure: + type: ["number", "string"] + description: Mirror traffic exposure ratio + +# --- Query parser --- +queryParser: + type: string + enum: ["auto", "on", "off", "session_control", "session_control_and_locks"] + description: Query parser mode +queryParserEnabled: + type: boolean + description: "DEPRECATED: use queryParser instead" + +# --- TLS --- +tlsGenerateSelfSignedCert: + type: boolean + description: Generate self-signed TLS certificate +rdsCertificateBundle.enabled: + type: boolean + description: Mount the RDS CA bundle +rdsCertificateBundle.type: + enum: ["global", "govcloud"] + description: RDS CA bundle type + +# --- Databases array --- +databases: + type: array + description: Database entries for pgdog.toml + items: + type: object + additionalProperties: false + required: ["name", "host"] + properties: + name: + type: string + host: + type: string + port: + type: integer + minimum: 1 + maximum: 65535 + shard: + type: integer + minimum: 0 + databaseName: + type: string + user: + type: string + password: + type: string + poolSize: + type: integer + minimum: 0 + minPoolSize: + type: integer + minimum: 0 + poolerMode: + type: string + statementTimeout: + type: integer + minimum: 0 + idleTimeout: + type: integer + minimum: 0 + readOnly: + type: boolean + role: + type: string + serverLifetime: + type: integer + minimum: 0 + reshardingOnly: + type: boolean + lbWeight: + type: integer + minimum: 0 + +# --- Users array --- +users: + type: array + description: User entries for users.toml + items: + type: object + +# --- Mirrors array --- +mirrors: + type: array + description: Mirror database entries + items: + type: object + additionalProperties: false + required: ["sourceDb", "destinationDb"] + properties: + sourceDb: + type: string + destinationDb: + type: string + queueLength: + type: integer + minimum: 0 + queueDepth: + type: integer + minimum: 0 + exposure: + type: number + level: + type: string + +# --- Sharded schemas --- +shardedSchemas: + type: array + description: Sharded schema entries + items: + type: object + additionalProperties: false + required: ["database", "shard"] + properties: + database: + type: string + name: + type: string + shard: + type: integer + minimum: 0 + +# --- Sharded tables --- +shardedTables: + type: array + description: Sharded table entries + items: + type: object + additionalProperties: false + required: ["database", "column", "dataType"] + properties: + database: + type: string + name: + type: string + column: + type: string + dataType: + type: string + +# --- Sharded mappings --- +shardedMappings: + type: array + description: Sharded mapping entries + items: + type: object + additionalProperties: false + required: ["database", "column", "kind", "shard"] + properties: + database: + type: string + column: + type: string + kind: + type: string + enum: ["list", "range"] + shard: + type: integer + minimum: 0 + values: + type: array + start: + type: integer + end: + type: integer + +# --- Omnisharded tables --- +omnishardedTables: + type: array + description: Omnisharded table entries + items: + type: object + additionalProperties: false + required: ["database", "tables"] + properties: + database: + type: string + sticky: + type: boolean + tables: + type: array + items: + type: string + +# --- Plugins --- +plugins: + type: array + description: Plugin entries + items: + type: object + additionalProperties: false + required: ["name"] + properties: + name: + type: string + config: + type: string + +# --- Service --- +service.type: + enum: ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"] + description: Service type +service.trafficDistribution: + type: string + description: Traffic distribution mode + +# --- Node scheduling --- +nodeSelector: + type: object + description: Node selector for pod scheduling +tolerations: + type: array + description: Pod tolerations + items: + type: object +affinity: + type: object + description: Pod affinity rules +topologySpreadConstraints: + type: array + description: Topology spread constraints + items: + type: object + +# --- Pod anti-affinity --- +podAntiAffinity.enabled: + type: boolean + description: Enable default anti-affinity rules +podAntiAffinity.type: + enum: ["soft", "hard"] + description: Anti-affinity type + +# --- PDB --- +podDisruptionBudget.enabled: + type: boolean + description: Enable PDB +podDisruptionBudget.minAvailable: + type: ["integer", "string"] + description: "Minimum available pods (number or percentage string)" +podDisruptionBudget.maxUnavailable: + type: ["integer", "string"] + description: "Maximum unavailable pods (number or percentage string)" + +# --- Service account --- +serviceAccount.create: + type: boolean +serviceAccount.name: + type: string + +# --- RBAC --- +rbac.create: + type: boolean + +# --- External secrets --- +externalSecrets.enabled: + type: boolean +externalSecrets.create: + type: boolean +externalSecrets.name: + type: string +externalSecrets.secretName: + type: string +externalSecrets.refreshInterval: + type: string +externalSecrets.secretStoreRef.name: + type: string +externalSecrets.secretStoreRef.kind: + enum: ["SecretStore", "ClusterSecretStore"] +externalSecrets.remoteRefs: + type: array + items: + type: object + +# --- Grafana remote write --- +grafanaRemoteWrite.url: + type: string +grafanaRemoteWrite.basicAuth.username: + type: string +grafanaRemoteWrite.basicAuth.password: + type: string +grafanaRemoteWrite.queueConfig.capacity: + type: integer + minimum: 1 +grafanaRemoteWrite.queueConfig.maxShards: + type: integer + minimum: 1 +grafanaRemoteWrite.queueConfig.minShards: + type: integer + minimum: 1 +grafanaRemoteWrite.queueConfig.maxSamplesPerSend: + type: integer + minimum: 1 +grafanaRemoteWrite.queueConfig.batchSendDeadline: + type: string +grafanaRemoteWrite.queueConfig.minBackoff: + type: string +grafanaRemoteWrite.queueConfig.maxBackoff: + type: string + +# --- Service monitor --- +serviceMonitor.enabled: + type: boolean + +# --- Gateway labels/scheduling --- +gateway.labels: + type: object +gateway.selectorLabels: + type: object +gateway.nodeSelector: + type: object +gateway.tolerations: + type: array + items: + type: object +gateway.affinity: + type: object +gateway.service.annotations: + type: object + +# --- Gateway --- +gateway.enabled: + type: boolean +gateway.connectFromPgdog: + type: boolean +gateway.image.pullPolicy: + enum: ["Always", "IfNotPresent", "Never"] +gateway.image.name: + type: string +gateway.port: + type: integer + minimum: 1 + maximum: 65535 +gateway.bindAddress: + type: string +gateway.service.type: + enum: ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"] +gateway.tls.existingSecret: + type: string +gateway.tls.certPath: + type: string +gateway.tls.keyPath: + type: string +gateway.control.url: + type: string +gateway.control.token: + type: string +gateway.stats.activeQueriesInterval: + type: integer + minimum: 0 +gateway.stats.pingInterval: + type: integer + minimum: 0 +gateway.stats.pushInterval: + type: integer + minimum: 0 +gateway.stats.configInterval: + type: integer + minimum: 0 +gateway.stats.queryStatsInterval: + type: integer + minimum: 0 + +# --- Prometheus collector labels/scheduling --- +prometheusCollector.labels: + type: object +prometheusCollector.selectorLabels: + type: object +prometheusCollector.podAnnotations: + type: object +prometheusCollector.nodeSelector: + type: object +prometheusCollector.tolerations: + type: array + items: + type: object +prometheusCollector.affinity: + type: object +prometheusCollector.service.annotations: + type: object + +# --- Prometheus collector --- +prometheusCollector.enabled: + type: boolean +prometheusCollector.image.pullPolicy: + enum: ["Always", "IfNotPresent", "Never"] +prometheusCollector.port: + type: integer + minimum: 1 + maximum: 65535 +prometheusCollector.scrapeInterval: + type: string +prometheusCollector.evaluationInterval: + type: string +prometheusCollector.basicAuth.enabled: + type: boolean +prometheusCollector.basicAuth.username: + type: string +prometheusCollector.basicAuth.password: + type: string +prometheusCollector.basicAuth.passwordHash: + type: string +prometheusCollector.tls.enabled: + type: boolean +prometheusCollector.storage.size: + type: string +prometheusCollector.retention.time: + type: string +prometheusCollector.retention.size: + type: string +prometheusCollector.service.type: + enum: ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"] + +# --- Security contexts (free-form) --- +securityContext: + type: object + description: Container security context +podSecurityContext: + type: object + description: Pod security context + +# --- OpenTelemetry --- +otel.headers: + type: object + description: Custom headers for OTEL export + +# --- Rewrite --- +rewrite.enabled: + type: ["boolean", "string"] + +# --- QoS --- +qos.enabled: + type: boolean +queryStats.enabled: + type: boolean +control.enabled: + type: boolean +control.endpoint: + type: string +control.token: + type: string + +# --- Two-phase commit booleans --- +twoPhaseCommit: + type: boolean +twoPhaseCommitAuto: + type: boolean +omnishardedSticky: + type: boolean +reloadSchemaOnDdl: + type: boolean +cutoverSaveConfig: + type: boolean +tcpKeepalive: + type: boolean diff --git a/values.schema.json b/values.schema.json new file mode 100644 index 0000000..dacd759 --- /dev/null +++ b/values.schema.json @@ -0,0 +1,1585 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "title": "PgDog Helm Chart Values", + "description": "Schema for validating PgDog Helm chart values. All object levels use additionalProperties: false to catch typos. Generated by scripts/generate-schema.py — do not edit by hand.", + "type": "object", + "additionalProperties": false, + "properties": { + "adminPassword": { + "type": "string" + }, + "affinity": { + "type": "object", + "description": "Pod affinity rules" + }, + "annotations": { + "type": "object", + "description": "Custom annotations for the deployment" + }, + "authType": { + "type": "string" + }, + "banReplicaLag": { + "type": "integer", + "minimum": 0 + }, + "banReplicaLagBytes": { + "type": "integer", + "minimum": 0 + }, + "banTimeout": { + "type": "integer", + "minimum": 0 + }, + "checkoutTimeout": { + "type": "integer", + "minimum": 0 + }, + "clientConnectionRecovery": { + "type": "string" + }, + "clientIdleInTransactionTimeout": { + "type": "integer", + "minimum": 0 + }, + "clientIdleTimeout": { + "type": "integer", + "minimum": 0 + }, + "clientLoginTimeout": { + "type": "integer", + "minimum": 0 + }, + "clusterName": { + "type": "string", + "description": "Kubernetes cluster name, added as a label to Prometheus metrics" + }, + "connectAttemptDelay": { + "type": "integer", + "minimum": 0 + }, + "connectAttempts": { + "type": "integer", + "minimum": 1, + "description": "Number of connection attempts" + }, + "connectTimeout": { + "type": "integer", + "minimum": 0 + }, + "connectionRecovery": { + "type": "string" + }, + "control": { + "type": "object", + "additionalProperties": false, + "properties": { + "activeQueriesInterval": { + "type": "integer", + "minimum": 0 + }, + "enabled": { + "type": "boolean" + }, + "endpoint": { + "type": "string" + }, + "errorsInterval": { + "type": "integer", + "minimum": 0 + }, + "metricsInterval": { + "type": "integer", + "minimum": 0 + }, + "queryTimingsChunkSize": { + "type": "integer", + "minimum": 0 + }, + "queryTimingsNewQueryQueueSize": { + "type": "integer", + "minimum": 0 + }, + "requestTimeout": { + "type": "integer", + "minimum": 0 + }, + "statsInterval": { + "type": "integer", + "minimum": 0 + }, + "token": { + "type": "string" + } + } + }, + "crossShardDisabled": { + "type": [ + "boolean", + "string" + ], + "description": "Disable cross-shard queries" + }, + "cutoverLastTransactionDelay": { + "type": "integer", + "minimum": 0 + }, + "cutoverReplicationLagThreshold": { + "type": "integer", + "minimum": 0 + }, + "cutoverSaveConfig": { + "type": "boolean" + }, + "cutoverTimeout": { + "type": "integer", + "minimum": 0 + }, + "cutoverTimeoutAction": { + "type": "string" + }, + "cutoverTrafficStopThreshold": { + "type": "integer", + "minimum": 0 + }, + "databases": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "host" + ], + "properties": { + "name": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "shard": { + "type": "integer", + "minimum": 0 + }, + "databaseName": { + "type": "string" + }, + "user": { + "type": "string" + }, + "password": { + "type": "string" + }, + "poolSize": { + "type": "integer", + "minimum": 0 + }, + "minPoolSize": { + "type": "integer", + "minimum": 0 + }, + "poolerMode": { + "type": "string" + }, + "statementTimeout": { + "type": "integer", + "minimum": 0 + }, + "idleTimeout": { + "type": "integer", + "minimum": 0 + }, + "readOnly": { + "type": "boolean" + }, + "role": { + "type": "string" + }, + "serverLifetime": { + "type": "integer", + "minimum": 0 + }, + "reshardingOnly": { + "type": "boolean" + }, + "lbWeight": { + "type": "integer", + "minimum": 0 + } + } + }, + "description": "Database entries for pgdog.toml" + }, + "defaultPoolSize": { + "type": "integer", + "minimum": 1, + "description": "Default connection pool size" + }, + "dnsTtl": { + "type": "integer", + "minimum": 0 + }, + "dryRun": { + "type": [ + "boolean", + "string" + ], + "description": "Enable dry run mode" + }, + "env": { + "type": "array", + "items": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "valueFrom": { + "type": "object" + } + } + }, + "description": "Custom environment variables for the pgdog container" + }, + "expandedExplain": { + "type": [ + "boolean", + "string" + ], + "description": "Enable expanded explain" + }, + "externalSecrets": { + "type": "object", + "additionalProperties": false, + "properties": { + "create": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "refreshInterval": { + "type": "string" + }, + "remoteRefs": { + "type": "array", + "items": { + "type": "object" + } + }, + "secretName": { + "type": "string" + }, + "secretStoreRef": { + "type": "object", + "additionalProperties": false, + "properties": { + "kind": { + "type": "string", + "enum": [ + "SecretStore", + "ClusterSecretStore" + ] + }, + "name": { + "type": "string" + } + } + } + } + }, + "extraInitContainers": { + "type": "array", + "description": "Additional init containers", + "items": { + "type": "object" + } + }, + "extraVolumeMounts": { + "type": "array", + "description": "Additional volume mounts for the pgdog container", + "items": { + "type": "object" + } + }, + "extraVolumes": { + "type": "array", + "description": "Additional volumes for the pod", + "items": { + "type": "object" + } + }, + "fullnameOverride": { + "type": "string", + "description": "Override the full release name" + }, + "gateway": { + "type": "object", + "additionalProperties": false, + "properties": { + "affinity": { + "type": "object" + }, + "bindAddress": { + "type": "string" + }, + "connectFromPgdog": { + "type": "boolean" + }, + "control": { + "type": "object", + "additionalProperties": false, + "properties": { + "token": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "enabled": { + "type": "boolean" + }, + "image": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "pullPolicy": { + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "labels": { + "type": "object" + }, + "nodeSelector": { + "type": "object" + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "resources": { + "$ref": "#/$defs/resources" + }, + "selectorLabels": { + "type": "object" + }, + "service": { + "type": "object", + "additionalProperties": false, + "properties": { + "annotations": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "ClusterIP", + "NodePort", + "LoadBalancer", + "ExternalName" + ] + } + } + }, + "stats": { + "type": "object", + "additionalProperties": false, + "properties": { + "activeQueriesInterval": { + "type": "integer", + "minimum": 0 + }, + "configInterval": { + "type": "integer", + "minimum": 0 + }, + "pingInterval": { + "type": "integer", + "minimum": 0 + }, + "pushInterval": { + "type": "integer", + "minimum": 0 + }, + "queryStatsInterval": { + "type": "integer", + "minimum": 0 + } + } + }, + "tls": { + "type": "object", + "additionalProperties": false, + "properties": { + "certPath": { + "type": "string" + }, + "existingSecret": { + "type": "string" + }, + "keyPath": { + "type": "string" + } + } + }, + "tolerations": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "grafanaRemoteWrite": { + "type": "object", + "additionalProperties": false, + "properties": { + "basicAuth": { + "type": "object", + "additionalProperties": false, + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "queueConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "batchSendDeadline": { + "type": "string" + }, + "capacity": { + "type": "integer", + "minimum": 1 + }, + "maxBackoff": { + "type": "string" + }, + "maxSamplesPerSend": { + "type": "integer", + "minimum": 1 + }, + "maxShards": { + "type": "integer", + "minimum": 1 + }, + "minBackoff": { + "type": "string" + }, + "minShards": { + "type": "integer", + "minimum": 1 + } + } + }, + "url": { + "type": "string" + } + } + }, + "healthcheckInterval": { + "type": "integer", + "minimum": 0 + }, + "healthcheckPort": { + "type": "integer", + "minimum": 1, + "description": "Port for healthcheck endpoint", + "maximum": 65535 + }, + "healthcheckTimeout": { + "type": "integer", + "minimum": 0 + }, + "idleHealthcheckDelay": { + "type": "integer", + "minimum": 0 + }, + "idleHealthcheckInterval": { + "type": "integer", + "minimum": 0 + }, + "idleTimeout": { + "type": "integer", + "minimum": 0 + }, + "image": { + "type": "object", + "additionalProperties": false, + "properties": { + "digest": { + "type": "string", + "description": "Image digest (overrides tag when specified)" + }, + "name": { + "type": "string", + "description": "DEPRECATED: Full image name (use repository and tag instead)" + }, + "pullPolicy": { + "type": "string", + "description": "Image pull policy", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] + }, + "repository": { + "type": "string", + "description": "Docker image repository" + }, + "tag": { + "type": "string", + "description": "Docker image tag (defaults to Chart appVersion)" + } + } + }, + "imagePullSecrets": { + "type": "array", + "description": "Image pull secrets for private registries", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + } + }, + "labels": { + "type": "object", + "description": "Custom labels for resources" + }, + "livenessProbe": { + "type": "object", + "description": "Container liveness probe configuration" + }, + "loadBalancingStrategy": { + "type": "string", + "description": "Load balancing strategy", + "enum": [ + "round_robin", + "random", + "least_active_connections" + ] + }, + "loadSchema": { + "type": "string" + }, + "logConnections": { + "type": [ + "boolean", + "string" + ], + "description": "Log connections" + }, + "logDedupThreshold": { + "type": "integer", + "minimum": 0 + }, + "logDedupWindow": { + "type": "integer", + "minimum": 0 + }, + "logDisconnections": { + "type": [ + "boolean", + "string" + ], + "description": "Log disconnections" + }, + "logFormat": { + "type": "string", + "description": "Log output format", + "enum": [ + "text", + "json" + ] + }, + "logLevel": { + "type": "string", + "description": "Log level (RUST_LOG syntax)", + "enum": [ + "error", + "warn", + "info", + "debug", + "trace", + "off" + ] + }, + "lsnCheckDelay": { + "type": "integer", + "minimum": 0 + }, + "lsnCheckInterval": { + "type": "integer", + "minimum": 0 + }, + "lsnCheckTimeout": { + "type": "integer", + "minimum": 0 + }, + "memoryMessageBuffer": { + "type": "integer", + "minimum": 0 + }, + "memoryNetBuffer": { + "type": "integer", + "minimum": 0 + }, + "memoryStackSize": { + "type": "integer", + "minimum": 0 + }, + "minPoolSize": { + "type": "integer", + "minimum": 0 + }, + "mirrorExposure": { + "type": [ + "number", + "string" + ], + "description": "Mirror traffic exposure ratio" + }, + "mirrorQueue": { + "type": "integer", + "minimum": 0 + }, + "mirrors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "sourceDb", + "destinationDb" + ], + "properties": { + "sourceDb": { + "type": "string" + }, + "destinationDb": { + "type": "string" + }, + "queueLength": { + "type": "integer", + "minimum": 0 + }, + "queueDepth": { + "type": "integer", + "minimum": 0 + }, + "exposure": { + "type": "number" + }, + "level": { + "type": "string" + } + } + }, + "description": "Mirror database entries" + }, + "multiTenant": { + "type": "object", + "additionalProperties": false, + "properties": { + "column": { + "type": "string" + } + } + }, + "nameOverride": { + "type": "string", + "description": "Override the chart name" + }, + "noCpuLimits": { + "type": "boolean", + "description": "Remove CPU limits from containers" + }, + "nodeSelector": { + "type": "object", + "description": "Node selector for pod scheduling" + }, + "omnishardedSticky": { + "type": "boolean" + }, + "omnishardedTables": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "database", + "tables" + ], + "properties": { + "database": { + "type": "string" + }, + "sticky": { + "type": "boolean" + }, + "tables": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "description": "Omnisharded table entries" + }, + "openMetricsNamespace": { + "type": "string" + }, + "openMetricsPort": { + "type": "integer", + "minimum": 1, + "description": "Port for OpenMetrics (Prometheus) export", + "maximum": 65535 + }, + "otel": { + "type": "object", + "additionalProperties": false, + "properties": { + "datadogApiKey": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "headers": { + "type": "object", + "description": "Custom headers for OTEL export" + }, + "namespace": { + "type": "string" + }, + "pushInterval": { + "type": "integer", + "minimum": 0 + } + } + }, + "passthroughAuth": { + "type": "string" + }, + "plugins": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "config": { + "type": "string" + } + } + }, + "description": "Plugin entries" + }, + "podAnnotations": { + "type": "object", + "description": "Custom annotations for pods" + }, + "podAntiAffinity": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable default anti-affinity rules" + }, + "type": { + "type": "string", + "description": "Anti-affinity type", + "enum": [ + "soft", + "hard" + ] + } + } + }, + "podDisruptionBudget": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable PDB" + }, + "maxUnavailable": { + "type": [ + "integer", + "string" + ], + "description": "Maximum unavailable pods (number or percentage string)" + }, + "minAvailable": { + "type": [ + "integer", + "string" + ], + "description": "Minimum available pods (number or percentage string)" + } + } + }, + "podLabels": { + "type": "object", + "description": "Custom labels for pods" + }, + "podSecurityContext": { + "type": "object", + "description": "Pod security context" + }, + "poolerMode": { + "type": "string", + "description": "Connection pooler mode", + "enum": [ + "transaction", + "session", + "statement" + ] + }, + "port": { + "type": "integer", + "minimum": 1, + "description": "Port on which PgDog will run", + "maximum": 65535 + }, + "preStopSleepSeconds": { + "type": "integer", + "description": "Delay before stopping container to allow endpoint updates" + }, + "preparedStatements": { + "type": "integer", + "minimum": 0 + }, + "preparedStatementsLimit": { + "type": "integer", + "minimum": 0 + }, + "priorityClassName": { + "type": "string", + "description": "PriorityClass name for pgdog pods" + }, + "prometheusCollector": { + "type": "object", + "additionalProperties": false, + "properties": { + "affinity": { + "type": "object" + }, + "basicAuth": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "password": { + "type": "string" + }, + "passwordHash": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "enabled": { + "type": "boolean" + }, + "evaluationInterval": { + "type": "string" + }, + "image": { + "type": "object", + "additionalProperties": false, + "properties": { + "pullPolicy": { + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "labels": { + "type": "object" + }, + "nodeSelector": { + "type": "object" + }, + "podAnnotations": { + "type": "object" + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "resources": { + "$ref": "#/$defs/resources" + }, + "retention": { + "type": "object", + "additionalProperties": false, + "properties": { + "size": { + "type": "string" + }, + "time": { + "type": "string" + } + } + }, + "scrapeInterval": { + "type": "string" + }, + "selectorLabels": { + "type": "object" + }, + "service": { + "type": "object", + "additionalProperties": false, + "properties": { + "annotations": { + "type": "object" + }, + "aws": { + "$ref": "#/$defs/awsLb" + }, + "type": { + "type": "string", + "enum": [ + "ClusterIP", + "NodePort", + "LoadBalancer", + "ExternalName" + ] + } + } + }, + "storage": { + "type": "object", + "additionalProperties": false, + "properties": { + "size": { + "type": "string" + } + } + }, + "tls": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "tolerations": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "prometheusPort": { + "type": "integer", + "description": "Port for the Prometheus sidecar", + "minimum": 1, + "maximum": 65535 + }, + "prometheusResources": { + "$ref": "#/$defs/resources", + "description": "Resource requests and limits for the prometheus sidecar" + }, + "pubSubChannelSize": { + "type": "integer", + "minimum": 0 + }, + "qos": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "maxEntries": { + "type": "integer", + "minimum": 0 + } + } + }, + "queryCacheLimit": { + "type": "integer", + "minimum": 0 + }, + "queryParser": { + "type": "string", + "description": "Query parser mode", + "enum": [ + "auto", + "on", + "off", + "session_control", + "session_control_and_locks" + ] + }, + "queryParserEnabled": { + "type": "boolean", + "description": "DEPRECATED: use queryParser instead" + }, + "queryParserEngine": { + "type": "string" + }, + "queryStats": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "maxEntries": { + "type": "integer", + "minimum": 0 + }, + "maxErrorAge": { + "type": "integer", + "minimum": 0 + }, + "maxErrors": { + "type": "integer", + "minimum": 0 + }, + "queryPlanMaxAge": { + "type": "integer", + "minimum": 0 + }, + "queryPlanThreshold": { + "type": "integer", + "minimum": 0 + }, + "queryPlansCache": { + "type": "integer", + "minimum": 0 + } + } + }, + "queryTimeout": { + "type": "integer", + "minimum": 0 + }, + "rbac": { + "type": "object", + "additionalProperties": false, + "properties": { + "create": { + "type": "boolean" + } + } + }, + "rdsCertificateBundle": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Mount the RDS CA bundle" + }, + "type": { + "type": "string", + "description": "RDS CA bundle type", + "enum": [ + "global", + "govcloud" + ] + } + } + }, + "readWriteSplit": { + "type": "string" + }, + "readWriteStrategy": { + "type": "string" + }, + "readinessProbe": { + "type": "object", + "description": "Container readiness probe configuration" + }, + "regexParserLimit": { + "type": "integer", + "minimum": 0 + }, + "reloadSchemaOnDdl": { + "type": "boolean" + }, + "replicas": { + "type": "integer", + "description": "Number of PgDog replicas", + "minimum": 1 + }, + "reshardingCopyFormat": { + "type": "string" + }, + "reshardingCopyRetryMaxAttempts": { + "type": "integer", + "minimum": 0 + }, + "reshardingCopyRetryMinDelay": { + "type": "integer", + "minimum": 0 + }, + "reshardingParallelCopies": { + "type": "integer", + "minimum": 0 + }, + "resources": { + "$ref": "#/$defs/resources", + "description": "Resource requests and limits for the pgdog container" + }, + "restartOnConfigChange": { + "type": "string", + "description": "Trigger rolling restart when pgdog config changes" + }, + "rewrite": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": [ + "boolean", + "string" + ] + }, + "primaryKey": { + "type": "string" + }, + "shardKey": { + "type": "string" + }, + "splitInserts": { + "type": "string" + } + } + }, + "rewriteShardKeyUpdates": { + "type": "string" + }, + "rollbackTimeout": { + "type": "integer", + "minimum": 0 + }, + "securityContext": { + "type": "object", + "description": "Container security context" + }, + "selectorLabels": { + "type": "object", + "description": "Custom selector labels for resources" + }, + "serverLifetime": { + "type": "integer", + "minimum": 0 + }, + "service": { + "type": "object", + "additionalProperties": false, + "properties": { + "annotations": { + "type": "object" + }, + "aws": { + "$ref": "#/$defs/awsLb" + }, + "trafficDistribution": { + "type": "string", + "description": "Traffic distribution mode" + }, + "type": { + "type": "string", + "description": "Service type", + "enum": [ + "ClusterIP", + "NodePort", + "LoadBalancer", + "ExternalName" + ] + } + } + }, + "serviceAccount": { + "type": "object", + "additionalProperties": false, + "properties": { + "annotations": { + "type": "object" + }, + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "serviceMonitor": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "shardedMappings": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "database", + "column", + "kind", + "shard" + ], + "properties": { + "database": { + "type": "string" + }, + "column": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "list", + "range" + ] + }, + "shard": { + "type": "integer", + "minimum": 0 + }, + "values": { + "type": "array" + }, + "start": { + "type": "integer" + }, + "end": { + "type": "integer" + } + } + }, + "description": "Sharded mapping entries" + }, + "shardedSchemas": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "database", + "shard" + ], + "properties": { + "database": { + "type": "string" + }, + "name": { + "type": "string" + }, + "shard": { + "type": "integer", + "minimum": 0 + } + } + }, + "description": "Sharded schema entries" + }, + "shardedTables": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "database", + "column", + "dataType" + ], + "properties": { + "database": { + "type": "string" + }, + "name": { + "type": "string" + }, + "column": { + "type": "string" + }, + "dataType": { + "type": "string" + } + } + }, + "description": "Sharded table entries" + }, + "shutdownTerminationTimeout": { + "type": "integer", + "minimum": 0 + }, + "shutdownTimeout": { + "type": "integer", + "minimum": 0 + }, + "startupProbe": { + "type": "object", + "description": "Container startup probe configuration" + }, + "statefulSet": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Deploy as StatefulSet instead of Deployment" + } + } + }, + "statsPeriod": { + "type": "integer", + "minimum": 0 + }, + "strategy": { + "type": "object", + "description": "Deployment rollout strategy" + }, + "systemCatalogs": { + "type": "string" + }, + "tcpCongestionControl": { + "type": "string" + }, + "tcpInterval": { + "type": "integer", + "minimum": 0 + }, + "tcpKeepalive": { + "type": "boolean" + }, + "tcpRetries": { + "type": "integer", + "minimum": 0 + }, + "tcpTime": { + "type": "integer", + "minimum": 0 + }, + "tcpUserTimeout": { + "type": "integer", + "minimum": 0 + }, + "terminationGracePeriodSeconds": { + "type": "integer", + "description": "Grace period for pod termination" + }, + "tlsCertificate": { + "type": "string" + }, + "tlsClientRequired": { + "type": [ + "boolean", + "string" + ], + "description": "Require TLS client certificates" + }, + "tlsGenerateSelfSignedCert": { + "type": "boolean", + "description": "Generate self-signed TLS certificate" + }, + "tlsPrivateKey": { + "type": "string" + }, + "tlsServerCaCertificate": { + "type": "string" + }, + "tlsVerify": { + "type": "string" + }, + "tolerations": { + "type": "array", + "description": "Pod tolerations", + "items": { + "type": "object" + } + }, + "topologySpreadConstraints": { + "type": "array", + "description": "Topology spread constraints", + "items": { + "type": "object" + } + }, + "twoPhaseCommit": { + "type": "boolean", + "minimum": 0 + }, + "twoPhaseCommitAuto": { + "type": "boolean" + }, + "twoPhaseCommitWalCheckpointInterval": { + "type": "integer", + "minimum": 0 + }, + "twoPhaseCommitWalDir": { + "type": "string" + }, + "twoPhaseCommitWalFsyncInterval": { + "type": "integer", + "minimum": 0 + }, + "twoPhaseCommitWalSegmentSize": { + "type": "integer", + "minimum": 0 + }, + "uniqueIdFunction": { + "type": "string" + }, + "uniqueIdMin": { + "type": "integer", + "minimum": 0 + }, + "users": { + "type": "array", + "items": { + "type": "object" + }, + "description": "User entries for users.toml" + }, + "workers": { + "type": "integer", + "minimum": 1, + "description": "Number of worker threads" + } + }, + "$defs": { + "resources": { + "type": "object", + "description": "Kubernetes resource requests and limits", + "additionalProperties": false, + "properties": { + "requests": { + "type": "object", + "additionalProperties": false, + "properties": { + "cpu": { + "type": [ + "string", + "number" + ] + }, + "memory": { + "type": "string" + } + } + }, + "limits": { + "type": "object", + "additionalProperties": false, + "properties": { + "cpu": { + "type": [ + "string", + "number" + ] + }, + "memory": { + "type": "string" + } + } + } + } + }, + "awsLb": { + "type": "object", + "description": "AWS Load Balancer Controller configuration", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable AWS LB annotations" + }, + "scheme": { + "type": "string", + "description": "Load balancer scheme", + "enum": [ + "internet-facing", + "internal" + ] + } + } + } + } +}