diff --git a/.gitignore b/.gitignore index 13e88ab..ff3551f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ manifests/ apps/**/charts/ +ci/crd-schemas/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 902fe72..7b8c997 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: check-executables-have-shebangs - id: check-json - id: check-added-large-files - args: ['--maxkb=500'] + args: ['--maxkb=2048'] - id: check-merge-conflict - id: check-shebang-scripts-are-executable - id: check-symlinks diff --git a/.woodpecker/kubeconform.yaml b/.woodpecker/kubeconform.yaml index 6177e2f..856f1a8 100644 --- a/.woodpecker/kubeconform.yaml +++ b/.woodpecker/kubeconform.yaml @@ -2,6 +2,21 @@ when: - event: pull_request steps: + - name: generate-schemas + image: git.unkin.net/unkin/almalinux9-kubetest:20260319 + commands: + - make generate-schemas + backend_options: + kubernetes: + serviceAccountName: default + resources: + requests: + memory: 512Mi + cpu: 1 + limits: + memory: 2Gi + cpu: 2 + - name: kubeconform image: git.unkin.net/unkin/almalinux9-kubetest:20260319 commands: diff --git a/Makefile b/Makefile index 7c3849e..c75e3f1 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,11 @@ kubeconform: @ci/validate-apps.sh && \ ci/validate-clusters.sh +# Generate JSON schemas from CRD definitions for kubeconform +# Run when CRD versions change, then commit ci/crd-schemas/ +generate-schemas: + @ci/generate-crd-schemas.sh + # Clean all generated manifests clean: @rm -rf manifests/ diff --git a/ci/generate-crd-schemas.py b/ci/generate-crd-schemas.py new file mode 100755 index 0000000..7d064e9 --- /dev/null +++ b/ci/generate-crd-schemas.py @@ -0,0 +1,49 @@ +#!/usr/bin/env -S uv run +# /// script +# requires-python = ">=3.11" +# dependencies = ["pyyaml"] +# /// +""" +Extract OpenAPI v3 schemas from CRD YAML on stdin and write JSON schema files +to the output directory for use with kubeconform. + +Usage: kustomize build ... | ci/generate-crd-schemas.py +""" +import sys +import json +import os + +import yaml + + +def main() -> int: + output_dir = sys.argv[1] if len(sys.argv) > 1 else "ci/crd-schemas" + count = 0 + + for doc in yaml.safe_load_all(sys.stdin): + if not doc or doc.get("kind") != "CustomResourceDefinition": + continue + + group = doc["spec"]["group"] + kind = doc["spec"]["names"]["kind"] + group_dir = os.path.join(output_dir, group) + os.makedirs(group_dir, exist_ok=True) + + for ver in doc["spec"].get("versions", []): + if not ver.get("served", True): + continue + schema = ver.get("schema", {}).get("openAPIV3Schema") + if not schema: + continue + fname = os.path.join(group_dir, f"{kind.lower()}_{ver['name']}.json") + with open(fname, "w") as f: + json.dump({"$schema": "http://json-schema.org/schema#", **schema}, f, indent=2) + f.write("\n") + print(f" wrote {fname}", file=sys.stderr) + count += 1 + + return count + + +if __name__ == "__main__": + print(main()) diff --git a/ci/generate-crd-schemas.sh b/ci/generate-crd-schemas.sh new file mode 100755 index 0000000..30b7e80 --- /dev/null +++ b/ci/generate-crd-schemas.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Extract OpenAPI v3 schemas from CRD definitions in all kustomize overlays +# and write JSON schema files to ci/crd-schemas/ for kubeconform validation. +# +# Run this script whenever CRD versions change, then commit the output. +# Usage: ci/generate-crd-schemas.sh [output-dir] + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OUTPUT_DIR="${1:-${SCRIPT_DIR}/crd-schemas}" +mkdir -p "$OUTPUT_DIR" + +total=0 + +while IFS= read -r -d "" k; do + dir="$(dirname "$k")" + n=$(kustomize build --enable-helm "$dir" 2>/dev/null \ + | "$SCRIPT_DIR/generate-crd-schemas.py" "$OUTPUT_DIR") || continue + total=$((total + n)) +done < <(find apps/overlays clusters -name kustomization.yaml -print0 | sort -z) + +echo "Generated ${total} schema(s) in ${OUTPUT_DIR}" >&2 diff --git a/ci/validate-apps.sh b/ci/validate-apps.sh index a70f9ad..700fc77 100755 --- a/ci/validate-apps.sh +++ b/ci/validate-apps.sh @@ -6,6 +6,7 @@ KUBE_VERSION="1.33.7" schema_args=( -schema-location "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{{.NormalizedKubernetesVersion}}-standalone{{.StrictSuffix}}/{{.ResourceKind}}{{.KindSuffix}}.json" -schema-location "https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json" + -schema-location "file://${PWD}/ci/crd-schemas/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json" ) while IFS= read -r -d "" k; do @@ -18,6 +19,6 @@ while IFS= read -r -d "" k; do -summary \ -output pretty \ -verbose \ - -skip CustomResourceDefinition,GpuDevicePlugin,LBNodeAgent,ServiceGroup \ + -skip GpuDevicePlugin,LBNodeAgent,ServiceGroup \ "${schema_args[@]}" done < <(find apps/overlays -name kustomization.yaml -print0) diff --git a/ci/validate-clusters.sh b/ci/validate-clusters.sh index 92f4924..dfef8b8 100755 --- a/ci/validate-clusters.sh +++ b/ci/validate-clusters.sh @@ -6,6 +6,7 @@ KUBE_VERSION="1.33.7" schema_args=( -schema-location "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{{.NormalizedKubernetesVersion}}-standalone{{.StrictSuffix}}/{{.ResourceKind}}{{.KindSuffix}}.json" -schema-location "https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json" + -schema-location "file://${PWD}/ci/crd-schemas/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json" ) while IFS= read -r -d "" k; do