From 65de8ded087edb82a1e060b47b6458359a09377f Mon Sep 17 00:00:00 2001 From: Kevin Hannon Date: Sat, 8 Nov 2025 21:25:49 -0500 Subject: [PATCH] add a script to verify PRR has all the sections --- Makefile | 7 +- hack/prr-baseline.txt | 372 ++++++++++++++++++++++++++++++++ hack/verify-prr-template.py | 413 ++++++++++++++++++++++++++++++++++++ 3 files changed, 790 insertions(+), 2 deletions(-) create mode 100644 hack/prr-baseline.txt create mode 100755 hack/verify-prr-template.py diff --git a/Makefile b/Makefile index 54286a84186..077abd92a45 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ NOCOLOR:=\\033[0m ##@ KEPs -.PHONY: update-toc verify-toc verify-metadata +.PHONY: update-toc verify-toc verify-metadata verify-prr-template update-toc: ## Updates KEP Table of Contents. ${REPO_ROOT}/hack/update-toc.sh @@ -36,6 +36,9 @@ verify-toc: ## Verifies the Table of Contents is in the correct format. verify-metadata: ## Verifies the KEP metadata is valid yaml. ${REPO_ROOT}/hack/verify-kep-metadata.sh +verify-prr-template: ## Verifies KEPs have complete Production Readiness Review sections. + ${REPO_ROOT}/hack/verify-prr-template.py + ##@ Verify .PHONY: add-verify-hook verify verify-boilerplate verify-build verify-golangci-lint verify-go-mod verify-shellcheck verify-spelling @@ -50,7 +53,7 @@ verify: ## Runs all verification tests. ${REPO_ROOT}/hack/verify.sh # TODO(lint): Uncomment verify-shellcheck once we finish shellchecking the repo. -verify: tools verify-boilerplate verify-build verify-golangci-lint verify-go-mod #verify-shellcheck ## Runs verification scripts to ensure correct execution +verify: tools verify-boilerplate verify-build verify-golangci-lint verify-go-mod verify-prr-template #verify-shellcheck ## Runs verification scripts to ensure correct execution verify-boilerplate: ## Runs the file header check ${REPO_ROOT}/hack/verify-boilerplate.sh diff --git a/hack/prr-baseline.txt b/hack/prr-baseline.txt new file mode 100644 index 00000000000..c7397960d9c --- /dev/null +++ b/hack/prr-baseline.txt @@ -0,0 +1,372 @@ +# This file contains KEPs that are allowed to fail PRR validation +# These are existing KEPs that were created before the requirement was enforced +# New KEPs must pass validation - do not add new entries to this file manually +# +# This file is auto-generated by hack/verify-prr-template.py +# + +keps/sig-api-machinery/1027-api-unions/README.md +keps/sig-api-machinery/1101-immutable-fields/README.md +keps/sig-api-machinery/1152-less-object-serializations/README.md +keps/sig-api-machinery/1164-remove-selflink/README.md +keps/sig-api-machinery/1281-network-proxy/README.md +keps/sig-api-machinery/1295-insecure-backend-proxy/README.md +keps/sig-api-machinery/1601-client-go-context/README.md +keps/sig-api-machinery/1623-standardize-conditions/README.md +keps/sig-api-machinery/1693-warnings/README.md +keps/sig-api-machinery/1872-manifest-based-admission-webhooks/README.md +keps/sig-api-machinery/1904-efficient-watch-resumption/README.md +keps/sig-api-machinery/1929-built-in-default/README.md +keps/sig-api-machinery/1965-kube-apiserver-identity/README.md +keps/sig-api-machinery/2155-clientgo-apply/README.md +keps/sig-api-machinery/2161-apiserver-default-labels/README.md +keps/sig-api-machinery/2330-migrating-api-objects-to-latest-storage-version/README.md +keps/sig-api-machinery/2332-pruning-for-custom-resources/README.md +keps/sig-api-machinery/2333-legacyflags-kflag/README.md +keps/sig-api-machinery/2334-graduate-server-side-get-and-partial-objects-to-GA/README.md +keps/sig-api-machinery/2335-vanilla-crd-openapi-subset-structural-schemas/README.md +keps/sig-api-machinery/2336-OwnerReference-resource-field/README.md +keps/sig-api-machinery/2337-k8s.io-group-protection/README.md +keps/sig-api-machinery/2338-graduate-API-gzip-compression-support-to-GA/README.md +keps/sig-api-machinery/2341-enabling-clients-to-tell-if-resource-endpoints-serve-the-same-set-of-objects/README.md +keps/sig-api-machinery/2342-exposing-hashed-storage-versions-via-the-discovery-API/README.md +keps/sig-api-machinery/2343-automated-storage-version-migration-with-storage-version-hash/README.md +keps/sig-api-machinery/2523-consistent-resource-versions-semantics/README.md +keps/sig-api-machinery/2558-publish-version-openapi/README.md +keps/sig-api-machinery/2885-server-side-unknown-field-validation/README.md +keps/sig-api-machinery/2887-openapi-enum-types/README.md +keps/sig-api-machinery/3037-client-go-alternative-services/README.md +keps/sig-api-machinery/3156-http3/README.md +keps/sig-api-machinery/3157-watch-list/README.md +keps/sig-api-machinery/3716-admission-webhook-match-conditions/README.md +keps/sig-api-machinery/492-admission-webhooks/README.md +keps/sig-api-machinery/5073-declarative-validation-with-validation-gen/README.md +keps/sig-api-machinery/5504-comparable-resource-version/README.md +keps/sig-api-machinery/555-server-side-apply/README.md +keps/sig-api-machinery/575-crd-defaulting/README.md +keps/sig-api-machinery/576-dry-run/README.md +keps/sig-api-machinery/598-crd-conversion-webhook/README.md +keps/sig-api-machinery/692-crd-openapi-schema/README.md +keps/sig-api-machinery/95-custom-resource-definitions/README.md +keps/sig-api-machinery/956-watch-bookmark/README.md +keps/sig-apps/1591-daemonset-surge/README.md +keps/sig-apps/1847-autoremove-statefulset-pvcs/README.md +keps/sig-apps/19-Graduate-CronJob-to-Stable/README.md +keps/sig-apps/2214-indexed-job/README.md +keps/sig-apps/2232-suspend-jobs/README.md +keps/sig-apps/2255-pod-cost/README.md +keps/sig-apps/2307-job-tracking-without-lingering-pods/README.md +keps/sig-apps/2360-optional-service-environment-variables/README.md +keps/sig-apps/2599-minreadyseconds-for-statefulsets/README.md +keps/sig-apps/2804-consolidate-workload-controllers-status/README.md +keps/sig-apps/3017-pod-healthy-policy-for-pdb/README.md +keps/sig-apps/3140-TimeZone-support-in-CronJob/README.md +keps/sig-apps/3715-elastic-indexed-job/README.md +keps/sig-apps/3939-allow-replacement-when-fully-terminated/README.md +keps/sig-apps/4017-pod-index-label/README.md +keps/sig-apps/5440-mutable-job-pod-resource-updates/README.md +keps/sig-apps/592-ttl-after-finish/README.md +keps/sig-apps/706-portable-service-definitions/README.md +keps/sig-apps/85-Graduate-PDB-to-Stable/README.md +keps/sig-apps/981-poddisruptionbudget-for-custom-resources/README.md +keps/sig-architecture/0000-kep-process/README.md +keps/sig-architecture/1143-node-role-labels/README.md +keps/sig-architecture/1333-conformance-without-beta/README.md +keps/sig-architecture/1618-conformance-profiles/README.md +keps/sig-architecture/1635-prevent-permabeta/README.md +keps/sig-architecture/1659-standard-topology-labels/README.md +keps/sig-architecture/2527-clarify-status-observations-vs-rbac/README.md +keps/sig-architecture/3136-beta-apis-off-by-default/README.md +keps/sig-architecture/5241-beta-featuregate-promotion-requirements/README.md +keps/sig-architecture/617-improve-kep-implementation/README.md +keps/sig-architecture/917-go-modules/README.md +keps/sig-architecture/960-conformance-behaviors/README.md +keps/sig-auth/1205-bound-service-account-tokens/README.md +keps/sig-auth/1314-node-restriction-pods/README.md +keps/sig-auth/1393-oidc-discovery/README.md +keps/sig-auth/1513-certificate-signing-request/README.md +keps/sig-auth/1687-hierarchical-namespaces-subproject/README.md +keps/sig-auth/2579-psp-replacement/README.md +keps/sig-auth/266-kubelet-client-certificate-bootstrap-rotation/README.md +keps/sig-auth/2718-20210511-client-exec-proxy/README.md +keps/sig-auth/2784-csr-duration/README.md +keps/sig-auth/279-limit-node-access/README.md +keps/sig-auth/2799-reduction-of-secret-based-service-account-token/README.md +keps/sig-auth/2907-secrets-store-csi-driver/README.md +keps/sig-auth/3130-kms-observability/README.md +keps/sig-auth/3299-kms-v2-improvements/README.md +keps/sig-auth/3325-self-subject-attributes-review-api/README.md +keps/sig-auth/3766-referencegrant/README.md +keps/sig-auth/541-external-credential-providers/README.md +keps/sig-auth/600-dynamic-audit-configuration/README.md +keps/sig-auth/740-service-account-external-signing/README.md +keps/sig-auth/789-harden-default-discover-bindings/README.md +keps/sig-autoscaling/117-hpa-metrics-specificity/README.md +keps/sig-autoscaling/2702-graduate-hpa-api-to-GA/README.md +keps/sig-autoscaling/853-configurable-hpa-scale-velocity/README.md +keps/sig-cli/1020-kubectl-staging/README.md +keps/sig-cli/1802-kustomize-components/README.md +keps/sig-cli/2206-openapi-features-in-kustomize/README.md +keps/sig-cli/2227-kubectl-default-container/README.md +keps/sig-cli/2229-kubectl-xdg-base-dir/README.md +keps/sig-cli/2257-kui/README.md +keps/sig-cli/2299-kustomize-plugin-composition/README.md +keps/sig-cli/2377-Kustomize/README.md +keps/sig-cli/2379-kubectl-plugins/README.md +keps/sig-cli/2380-data-driven-commands-for-kubectl/README.md +keps/sig-cli/2381-future-of-kubectl-cp/README.md +keps/sig-cli/2382-kustomize-exec-secret-generator/README.md +keps/sig-cli/2383-extend-kustomize-patches-to-multiple-targets/README.md +keps/sig-cli/2384-kustomize-file-processing-integration/README.md +keps/sig-cli/2385-kustomize-secret-generator-plugins/README.md +keps/sig-cli/2386-kustomize-subcommand-integration/README.md +keps/sig-cli/2551-return-code-normalization/README.md +keps/sig-cli/2590-kubectl-subresource/README.md +keps/sig-cli/2906-kustomize-function-catalog/README.md +keps/sig-cli/2953-kustomize-plugin-graduation/README.md +keps/sig-cli/2985-public-krm-functions-registry/README.md +keps/sig-cli/3659-kubectl-apply-prune/README.md +keps/sig-cli/3805-ssa-default/README.md +keps/sig-cli/491-kubectl-diff/README.md +keps/sig-cli/859-kubectl-headers/README.md +keps/sig-cli/993-kustomize-generators-transformers/README.md +keps/sig-cloud-provider/1179-building-without-in-tree-providers/README.md +keps/sig-cloud-provider/1771-versioning-policy-for-external-cloud-providers/README.md +keps/sig-cloud-provider/1959-service-lb-class-field/README.md +keps/sig-cloud-provider/2025-extend-konnectivity-for-both-directions/README.md +keps/sig-cloud-provider/2133-out-of-tree-credential-provider/README.md +keps/sig-cloud-provider/2390-reporting-conformance-test-results-to-testgrid/README.md +keps/sig-cloud-provider/2392-cloud-controller-manager/README.md +keps/sig-cloud-provider/2393-cloud-provider-documentation/README.md +keps/sig-cloud-provider/2394-support-out-of-tree-AWS-cloud-provider/README.md +keps/sig-cloud-provider/2436-controller-manager-leader-migration/README.md +keps/sig-cloud-provider/2699-add-webhook-hosting-to-ccm/README.md +keps/sig-cloud-provider/668-out-of-tree-gce/README.md +keps/sig-cloud-provider/669-out-of-tree-openstack/README.md +keps/sig-cloud-provider/670-out-of-tree-vsphere/README.md +keps/sig-cloud-provider/671-out-of-tree-ibm/README.md +keps/sig-cloud-provider/837-cloud-provider-labels/README.md +keps/sig-contributor-experience/0000-community-forum/README.md +keps/sig-contributor-experience/1553-issue-triage/README.md +keps/sig-contributor-experience/2225-contributor-site/README.md +keps/sig-docs/1326-third-party-content-in-docs/README.md +keps/sig-etcd/4326-downgrade/README.md +keps/sig-etcd/4331-livez-readyz/README.md +keps/sig-etcd/4578-server-feature-gate/README.md +keps/sig-etcd/4743-kuberernetes-etcd-interface/README.md +keps/sig-instrumentation/1013-metrics-watch-api/README.md +keps/sig-instrumentation/1206-metrics-overhaul/README.md +keps/sig-instrumentation/1209-metrics-stability/README.md +keps/sig-instrumentation/1602-structured-logging/README.md +keps/sig-instrumentation/1748-pod-resource-metrics/README.md +keps/sig-instrumentation/1753-logs-sanitization/README.md +keps/sig-instrumentation/2831-kubelet-tracing/README.md +keps/sig-instrumentation/2845-deprecate-klog-specific-flags-in-k8s-components/README.md +keps/sig-instrumentation/3466-kubernetes-component-health-slis/README.md +keps/sig-instrumentation/3498-extending-stability/README.md +keps/sig-instrumentation/383-new-event-api-ga-graduation/README.md +keps/sig-instrumentation/4785-resource-state-metrics/README.md +keps/sig-instrumentation/647-apiserver-tracing/README.md +keps/sig-multicluster/1645-multi-cluster-services-api/README.md +keps/sig-multicluster/2149-clusterid/README.md +keps/sig-multicluster/5339-clusterprofile-plugin-credentials/README.md +keps/sig-network/0752-endpointslices/README.md +keps/sig-network/1024-nodelocal-cache-dns/README.md +keps/sig-network/1138-ipv6/README.md +keps/sig-network/1435-mixed-protocol-lb/README.md +keps/sig-network/1453-ingress-api/README.md +keps/sig-network/1507-app-protocol/README.md +keps/sig-network/1611-network-policy-validation/README.md +keps/sig-network/1669-proxy-terminating-endpoints/README.md +keps/sig-network/1672-tracking-terminating-endpoints/README.md +keps/sig-network/1864-disable-lb-node-ports/README.md +keps/sig-network/2079-network-policy-port-range/README.md +keps/sig-network/2086-service-internal-traffic-policy/README.md +keps/sig-network/2091-admin-network-policy/README.md +keps/sig-network/2200-externalips-admission/README.md +keps/sig-network/2365-ingressclass-namespaced-params/README.md +keps/sig-network/2433-topology-aware-hints/README.md +keps/sig-network/2438-dual-stack-apiserver/README.md +keps/sig-network/2447-Make-kube-proxy-service-abstraction-optional/README.md +keps/sig-network/2448-Remove-kube-proxy-automatic-clean-up-logic/README.md +keps/sig-network/2449-move-externalDNS-out-of-kubernetes-incubator/README.md +keps/sig-network/2450-Remove-knowledge-of-pod-cluster-CIDR-from-iptables-rules/README.md +keps/sig-network/2593-multiple-cluster-cidrs/README.md +keps/sig-network/2595-expanded-dns-config/README.md +keps/sig-network/265-ipvs-based-load-balancing/README.md +keps/sig-network/2829-gateway-api-to-k8s-io/README.md +keps/sig-network/2943-networkpolicy-status/README.md +keps/sig-network/3015-prefer-same-node/README.md +keps/sig-network/3070-reserved-service-ip-range/README.md +keps/sig-network/3685-endpointslice-reconciler-to-staging/README.md +keps/sig-network/3726-standard-application-protocols/README.md +keps/sig-network/3866-nftables-proxy/README.md +keps/sig-network/4004-deprecate-kube-proxy-version/README.md +keps/sig-network/427-coredns/README.md +keps/sig-network/4444-service-traffic-distribution/README.md +keps/sig-network/4974-deprecate-endpoints/README.md +keps/sig-network/504-configurable-pod-dns/README.md +keps/sig-network/536-topology-aware-routing/README.md +keps/sig-network/563-dual-stack/README.md +keps/sig-network/566-coredns-default/README.md +keps/sig-network/580-pod-readiness-gates/README.md +keps/sig-network/614-SCTP-support/README.md +keps/sig-network/758-ingress-api-group/README.md +keps/sig-network/980-load-balancer-finalizers/README.md +keps/sig-node/1287-in-place-update-pod-resources/README.md +keps/sig-node/135-seccomp/README.md +keps/sig-node/1539-hugepages/README.md +keps/sig-node/1547-building-kubelet-without-docker/README.md +keps/sig-node/1558-streaming-proxy-redirects/README.md +keps/sig-node/166-taint-based-eviction/README.md +keps/sig-node/1797-configure-fqdn-as-hostname-for-pods/README.md +keps/sig-node/1867-disable-accelerator-usage-metrics/README.md +keps/sig-node/1898-hardened-exec/README.md +keps/sig-node/1967-size-memory-backed-volumes/README.md +keps/sig-node/1972-kubelet-exec-probe-timeouts/README.md +keps/sig-node/2000-graceful-node-shutdown/README.md +keps/sig-node/2040-kubelet-cri/README.md +keps/sig-node/2043-pod-resource-concrete-assigments/README.md +keps/sig-node/2053-downward-api-hugepages/README.md +keps/sig-node/2129-remove-cadvisor-json-metrics/README.md +keps/sig-node/213-run-as-group/README.md +keps/sig-node/2133-kubelet-credential-providers/README.md +keps/sig-node/2221-remove-dockershim/README.md +keps/sig-node/2238-liveness-probe-grace-period/README.md +keps/sig-node/2254-cgroup-v2/README.md +keps/sig-node/2371-cri-pod-container-stats/README.md +keps/sig-node/2400-node-swap/README.md +keps/sig-node/2411-cri-container-log-rotation/README.md +keps/sig-node/2413-seccomp-by-default/README.md +keps/sig-node/2535-ensure-secret-pulled-images/README.md +keps/sig-node/2712-pod-priority-based-graceful-node-shutdown/README.md +keps/sig-node/277-ephemeral-containers/README.md +keps/sig-node/281-dynamic-kubelet-configuration/README.md +keps/sig-node/2902-cpumanager-distribute-cpus-policy-option/README.md +keps/sig-node/3063-dynamic-resource-allocation/README.md +keps/sig-node/3288-separate-stdout-from-stderr/README.md +keps/sig-node/3327-align-by-socket/README.md +keps/sig-node/34-sysctl-fields/README.md +keps/sig-node/3570-cpumanager/README.md +keps/sig-node/3573-device-plugin/README.md +keps/sig-node/4188-kubelet-pod-readiness-api/README.md +keps/sig-node/4216-image-pull-per-runtime-class/README.md +keps/sig-node/4369-allow-special-characters-environment-variable/README.md +keps/sig-node/4540-strict-cpu-reservation/README.md +keps/sig-node/4569-cgroup-v1-maintenance-mode/README.md +keps/sig-node/4580-deprecate-kubelet-runonce/README.md +keps/sig-node/495-pod-pid-namespace/README.md +keps/sig-node/5573-remove-cgroup-v1/README.md +keps/sig-node/585-runtime-class/README.md +keps/sig-node/589-efficient-node-heartbeats/README.md +keps/sig-node/688-pod-overhead/README.md +keps/sig-node/757-pid-limiting/README.md +keps/sig-node/793-node-os-arch-labels/README.md +keps/sig-node/950-liveness-probe-holdoff/README.md +keps/sig-release/0000-anago-to-krel-migration/README.md +keps/sig-release/1498-kubernetes-yearly-support-period/README.md +keps/sig-release/1729-rebase-images-to-distroless/README.md +keps/sig-release/1732-artifact-management/README.md +keps/sig-release/1733-release-notes/README.md +keps/sig-release/1734-k8s-image-promoter/README.md +keps/sig-release/2572-release-cadence/README.md +keps/sig-release/2818-reducing-build-maintenance/README.md +keps/sig-release/2853-k-core-branch-rename/README.md +keps/sig-release/3000-artifact-distribution/README.md +keps/sig-release/3027-slsa-compliance/README.md +keps/sig-release/3031-signing-release-artifacts/README.md +keps/sig-release/3720-freezing-k8s-gcr-io/README.md +keps/sig-scheduling/1258-default-pod-topology-spread/README.md +keps/sig-scheduling/1451-multi-scheduling-profiles/README.md +keps/sig-scheduling/1819-scheduler-extender/README.md +keps/sig-scheduling/1923-prefer-nominated-node/README.md +keps/sig-scheduling/2249-pod-affinity-namespace-selector/README.md +keps/sig-scheduling/2372-node-labels-quota/README.md +keps/sig-scheduling/2458-node-resource-score-strategy/README.md +keps/sig-scheduling/268-priority-preemption/README.md +keps/sig-scheduling/2891-simplified-config/README.md +keps/sig-scheduling/2926-job-mutable-scheduling-directives/README.md +keps/sig-scheduling/3280-guarantee-pdb-when-preemption-happens/README.md +keps/sig-scheduling/382-taint-node-by-condition/README.md +keps/sig-scheduling/4815-dra-partitionable-devices/README.md +keps/sig-scheduling/5142-pop-backoffq-when-activeq-empty/README.md +keps/sig-scheduling/5471-enable-sla-based-scheduling/README.md +keps/sig-scheduling/548-schedule-daemonset-pods/README.md +keps/sig-scheduling/583-coscheduling/README.md +keps/sig-scheduling/624-scheduling-framework/README.md +keps/sig-scheduling/785-scheduler-component-config-api/README.md +keps/sig-scheduling/895-pod-topology-spread/README.md +keps/sig-scheduling/902-non-preempting-priorityclass/README.md +keps/sig-scheduling/964-binpacking-priority/README.md +keps/sig-scheduling/986-resource-quota-scope-selectors/README.md +keps/sig-security/1933-secret-logging-static-analysis/README.md +keps/sig-security/2763-ambient-capabilities/README.md +keps/sig-security/3203-auto-refreshing-official-cve-feed/README.md +keps/sig-storage/121-local-persistent-volumes/README.md +keps/sig-storage/1412-immutable-secrets-and-configmaps/README.md +keps/sig-storage/1432-volume-health-monitor/README.md +keps/sig-storage/1472-storage-capacity-tracking/README.md +keps/sig-storage/1487-csi-migration-aws/README.md +keps/sig-storage/1488-csi-migration-gce-pd/README.md +keps/sig-storage/1489-csi-migration-cinder/README.md +keps/sig-storage/1490-csi-migration-azuredisk/README.md +keps/sig-storage/1491-csi-migration-vsphere/README.md +keps/sig-storage/1495-volume-populators/README.md +keps/sig-storage/1682-csi-driver-skip-permission/README.md +keps/sig-storage/1698-generic-ephemeral-volumes/README.md +keps/sig-storage/1710-selinux-relabeling/README.md +keps/sig-storage/177-volume-snapshot/README.md +keps/sig-storage/1790-recover-resize-failure/README.md +keps/sig-storage/1845-prioritization-on-volume-capacity/README.md +keps/sig-storage/1855-csi-driver-service-account-token/README.md +keps/sig-storage/1885-csi-migration-azurefile/README.md +keps/sig-storage/1900-volume-snapshot-validation-webhook/README.md +keps/sig-storage/1979-object-storage-support/README.md +keps/sig-storage/2261-staging-mount-library/README.md +keps/sig-storage/2263-volume-scale-testing/README.md +keps/sig-storage/2264-csi-release-ci-process/README.md +keps/sig-storage/2317-fsgroup-on-mount/README.md +keps/sig-storage/2451-service-account-token-volumes/README.md +keps/sig-storage/284-enable-volume-expansion/README.md +keps/sig-storage/2923-csi-migration-ceph-rbd/README.md +keps/sig-storage/2924-csi-migration-cephfs/README.md +keps/sig-storage/3107-csi-nodeexpandsecret/README.md +keps/sig-storage/3294-provision-volumes-from-cross-namespace-snapshots/README.md +keps/sig-storage/3333-retroactive-default-storage-class/README.md +keps/sig-storage/3476-volume-group-snapshot/README.md +keps/sig-storage/351-raw-block-support/README.md +keps/sig-storage/361-local-ephemeral-storage-isolation/README.md +keps/sig-storage/3751-volume-attributes-class/README.md +keps/sig-storage/531-online-pv-resizing/README.md +keps/sig-storage/5538-csi-sa-tokens-secrets-field/README.md +keps/sig-storage/554-maximum-volume-count/README.md +keps/sig-storage/556-csi-volume-resizing/README.md +keps/sig-storage/557-csi-topology/README.md +keps/sig-storage/559-volume-subpath-expansion/README.md +keps/sig-storage/565-csi-block-support/README.md +keps/sig-storage/596-csi-inline-volumes/README.md +keps/sig-storage/603-csi-pod-info/README.md +keps/sig-storage/625-csi-migration/README.md +keps/sig-storage/695-skip-permission-change/README.md +keps/sig-storage/770-csi-skip-attach/README.md +keps/sig-storage/962-execution-hook/README.md +keps/sig-storage/989-extend-datasource/README.md +keps/sig-testing/2290-new-label-for-trusted-PR-identification/README.md +keps/sig-testing/2291-presubmit-config-inside-the-tested-repo/README.md +keps/sig-testing/2420-reducing-kubernetes-build-maintenance/README.md +keps/sig-testing/2464-kubetest2-ci-migration/README.md +keps/sig-testing/2539-continuously-deploy-k8s-prow/README.md +keps/sig-testing/3041-node-conformance-and-features/README.md +keps/sig-testing/714-break-test-tarball/README.md +keps/sig-windows/1001-windows-cri-containerd/README.md +keps/sig-windows/1043-windows-security-context/README.md +keps/sig-windows/1122-windows-csi-support/README.md +keps/sig-windows/116-windows-node-support/README.md +keps/sig-windows/1301-windows-runtime-class/README.md +keps/sig-windows/1981-windows-privileged-container-support/README.md +keps/sig-windows/2258-node-log-query/README.md +keps/sig-windows/2578-windows-conformance/README.md +keps/sig-windows/2802-identify-windows-pods-apiserver-admission/README.md +keps/sig-windows/3503-host-network-support-for-windows-pods/README.md +keps/sig-windows/4802-windows-node-shutdown/README.md +keps/sig-windows/689-windows-gmsa/README.md diff --git a/hack/verify-prr-template.py b/hack/verify-prr-template.py new file mode 100755 index 00000000000..92ff7cec1a5 --- /dev/null +++ b/hack/verify-prr-template.py @@ -0,0 +1,413 @@ +#!/usr/bin/env python3 + +""" +Verify that KEPs have all required Production Readiness Review (PRR) sections and questions. + +This script validates that all KEPs contain the complete Production Readiness Review +Questionnaire with all required subsections and questions as defined in the KEP template. + +For existing KEPs that don't meet the requirements, they are tracked in a baseline file +(hack/prr-baseline.txt). Only NEW KEPs that fail validation will cause this script to fail. +This allows us to enforce the standard going forward while grandfathering existing KEPs. +""" + +import os +import re +import sys +from pathlib import Path +from typing import List, Dict, Tuple, Set + +# Define the expected PRR structure +# Each subsection contains a list of required question patterns (first few words to match) +PRR_STRUCTURE = { + "Feature Enablement and Rollback": [ + "How can this feature be enabled / disabled", + "Does enabling the feature change any default behavior", + "Can the feature be disabled once it has been enabled", + "What happens if we reenable the feature", + "Are there any tests for feature enablement/disablement", + ], + "Rollout, Upgrade and Rollback Planning": [ + "How can a rollout or rollback fail", + "What specific metrics should inform a rollback", + "Were upgrade and rollback tested", + "Is the rollout accompanied by any deprecations", + ], + "Monitoring Requirements": [ + "How can an operator determine if the feature is in use", + "How can someone using this feature know that it is working", + "What are the reasonable SLOs", + "What are the SLIs", + "Are there any missing metrics", + ], + "Dependencies": [ + "Does this feature depend on any specific services", + ], + "Scalability": [ + "Will enabling / using this feature result in any new API calls", + "Will enabling / using this feature result in introducing new API types", + "Will enabling / using this feature result in any new calls to the cloud provider", + "Will enabling / using this feature result in increasing size or count", + "Will enabling / using this feature result in increasing time taken", + "Will enabling / using this feature result in non-negligible increase of resource usage", + "Can enabling / using this feature result in resource exhaustion", + ], + "Troubleshooting": [ + "How does this feature react if the API server", + "What are other known failure modes", + "What steps should be taken if SLOs are not being met", + ], +} + + +class KEPValidator: + def __init__(self, keps_dir: Path, baseline_file: Path): + self.keps_dir = keps_dir + self.baseline_file = baseline_file + self.failed_keps = [] + self.passed_keps = [] + self.skipped_keps = [] + self.grandfathered_keps = [] # KEPs that fail but are in baseline + self.new_failures = [] # KEPs that fail and are NOT in baseline + self.baseline = self.load_baseline() + + def load_baseline(self) -> Set[str]: + """ + Load the baseline file containing KEPs that are allowed to fail validation. + + Returns: + Set of relative KEP paths that are in the baseline + """ + if not self.baseline_file.exists(): + return set() + + try: + with open(self.baseline_file, 'r') as f: + # Read lines, strip whitespace, and ignore empty lines and comments + return { + line.strip() + for line in f + if line.strip() and not line.strip().startswith('#') + } + except Exception as e: + print(f"Warning: Failed to read baseline file: {e}") + return set() + + def save_baseline(self, failed_kep_paths: List[str]) -> None: + """ + Save the baseline file with the current list of failing KEPs. + + Args: + failed_kep_paths: List of relative KEP paths that are failing validation + """ + try: + with open(self.baseline_file, 'w') as f: + f.write("# This file contains KEPs that are allowed to fail PRR validation\n") + f.write("# These are existing KEPs that were created before the requirement was enforced\n") + f.write("# New KEPs must pass validation - do not add new entries to this file manually\n") + f.write("#\n") + f.write("# This file is auto-generated by hack/verify-prr-template.py\n") + f.write("#\n\n") + for kep_path in sorted(failed_kep_paths): + f.write(f"{kep_path}\n") + print(f"\nBaseline file created: {self.baseline_file}") + print(f"Added {len(failed_kep_paths)} KEPs to baseline") + except Exception as e: + print(f"Error: Failed to write baseline file: {e}") + sys.exit(1) + + def find_all_keps(self) -> List[Path]: + """Find all KEP README.md files in the repository.""" + kep_files = [] + + # Find all sig-* directories + for sig_dir in self.keps_dir.glob("sig-*"): + if not sig_dir.is_dir(): + continue + + # Find all KEP directories (matching pattern NNNN-*) + for kep_dir in sig_dir.glob("*"): + if not kep_dir.is_dir(): + continue + + readme = kep_dir / "README.md" + if readme.exists(): + kep_files.append(readme) + + # Also check for KEPs directly in keps/ directory + for kep_dir in self.keps_dir.glob("*"): + if not kep_dir.is_dir() or kep_dir.name.startswith("sig-"): + continue + + readme = kep_dir / "README.md" + if readme.exists(): + kep_files.append(readme) + + return sorted(kep_files) + + def parse_markdown_headings(self, content: str) -> List[Tuple[int, str, int]]: + """ + Parse markdown headings from content. + + Returns: + List of tuples (level, text, line_number) + """ + headings = [] + lines = content.split('\n') + + for line_num, line in enumerate(lines, 1): + # Match ATX-style headings (## Heading) + match = re.match(r'^(#{1,6})\s+(.+?)$', line.strip()) + if match: + level = len(match.group(1)) + text = match.group(2).strip() + headings.append((level, text, line_num)) + + return headings + + def find_prr_section(self, headings: List[Tuple[int, str, int]]) -> Tuple[int, int]: + """ + Find the start and end indices of the PRR section in the headings list. + + Returns: + Tuple of (start_index, end_index) or (-1, -1) if not found + """ + start_idx = -1 + end_idx = -1 + + for idx, (level, text, _) in enumerate(headings): + # Look for the PRR section (level 2 heading) + if level == 2 and "Production Readiness Review Questionnaire" in text: + start_idx = idx + + # Find the end of this section (next level 2 heading) + for end_idx in range(idx + 1, len(headings)): + if headings[end_idx][0] <= 2: + return (start_idx, end_idx) + + # If no next level 2 heading found, PRR section extends to end + return (start_idx, len(headings)) + + return (-1, -1) + + def validate_prr_structure(self, headings: List[Tuple[int, str, int]], + start_idx: int, end_idx: int) -> Tuple[bool, List[str]]: + """ + Validate that all required PRR subsections and questions are present. + + Returns: + Tuple of (is_valid, list_of_errors) + """ + errors = [] + prr_headings = headings[start_idx:end_idx] + + # Track which subsections and questions we've found + found_subsections = set() + found_questions = {subsection: set() for subsection in PRR_STRUCTURE.keys()} + + current_subsection = None + + for level, text, line_num in prr_headings[1:]: # Skip the PRR heading itself + # Level 3 headings are subsections + if level == 3: + # Check if this matches any expected subsection + for subsection in PRR_STRUCTURE.keys(): + if subsection.lower() in text.lower(): + current_subsection = subsection + found_subsections.add(subsection) + break + + # Level 6 headings are questions + elif level == 6 and current_subsection: + # Check if this matches any expected question in the current subsection + for question_pattern in PRR_STRUCTURE[current_subsection]: + if question_pattern.lower() in text.lower(): + found_questions[current_subsection].add(question_pattern) + break + + # Check for missing subsections + for subsection in PRR_STRUCTURE.keys(): + if subsection not in found_subsections: + errors.append(f"Missing subsection: '{subsection}'") + + # Check for missing questions in each subsection + for subsection, questions in PRR_STRUCTURE.items(): + if subsection in found_subsections: + for question_pattern in questions: + if question_pattern not in found_questions[subsection]: + errors.append(f"Missing question in '{subsection}': '{question_pattern}...'") + + return (len(errors) == 0, errors) + + def validate_kep(self, kep_path: Path) -> Tuple[bool, List[str]]: + """ + Validate a single KEP file. + + Returns: + Tuple of (is_valid, list_of_errors) + """ + errors = [] + + # Read the KEP file + try: + content = kep_path.read_text(encoding='utf-8') + except Exception as e: + return (False, [f"Failed to read file: {e}"]) + + # Parse headings + headings = self.parse_markdown_headings(content) + + if not headings: + return (False, ["No headings found in file"]) + + # Find PRR section + start_idx, end_idx = self.find_prr_section(headings) + + if start_idx == -1: + return (False, ["Production Readiness Review Questionnaire section not found"]) + + # Validate PRR structure + is_valid, prr_errors = self.validate_prr_structure(headings, start_idx, end_idx) + + return (is_valid, prr_errors) + + def should_skip_kep(self, kep_path: Path) -> Tuple[bool, str]: + """ + Determine if a KEP should be skipped from validation. + + Returns: + Tuple of (should_skip, reason) + """ + # Skip the template KEP + if "kep-template" in str(kep_path).lower(): + return (True, "template KEP") + + # Skip prod-readiness directory (not a KEP, just documentation) + if "prod-readiness" in str(kep_path): + return (True, "not a KEP") + + return (False, "") + + def validate_all(self) -> int: + """ + Validate all KEPs in the repository. + + Returns: + Exit code (0 for success, 1 for new failures) + """ + kep_files = self.find_all_keps() + + if not kep_files: + print("No KEP files found!") + return 1 + + # Check if baseline exists, if not we'll need to create it + baseline_exists = self.baseline_file.exists() + + if not baseline_exists: + print(f"Baseline file not found at {self.baseline_file}") + print("Creating baseline from current failures...\n") + + print(f"Found {len(kep_files)} KEP files to validate\n") + + for kep_path in kep_files: + # Check if we should skip this KEP + should_skip, skip_reason = self.should_skip_kep(kep_path) + if should_skip: + self.skipped_keps.append((kep_path, skip_reason)) + continue + + # Get relative path for display and baseline comparison + try: + rel_path = kep_path.relative_to(self.keps_dir.parent) + except ValueError: + rel_path = kep_path + + rel_path_str = str(rel_path) + + # Validate the KEP + is_valid, errors = self.validate_kep(kep_path) + + if is_valid: + self.passed_keps.append(kep_path) + print(f"✓ {rel_path}") + else: + # Check if this failure is in the baseline (grandfathered) + if rel_path_str in self.baseline: + self.grandfathered_keps.append((kep_path, errors)) + print(f"⊗ {rel_path} (grandfathered)") + else: + self.new_failures.append((kep_path, errors)) + self.failed_keps.append((kep_path, errors)) + print(f"✗ {rel_path} (NEW FAILURE)") + for error in errors: + print(f" - {error}") + print() + + # If baseline doesn't exist, create it with all current failures + if not baseline_exists: + all_failing_paths = [] + for kep_path, _ in self.grandfathered_keps + self.new_failures: + try: + rel_path = kep_path.relative_to(self.keps_dir.parent) + all_failing_paths.append(str(rel_path)) + except ValueError: + all_failing_paths.append(str(kep_path)) + + if all_failing_paths: + self.save_baseline(all_failing_paths) + + # Print summary + print("\n" + "=" * 80) + print("SUMMARY") + print("=" * 80) + print(f"Total KEPs found: {len(kep_files)}") + print(f"Skipped: {len(self.skipped_keps)}") + print(f"Validated: {len(self.passed_keps) + len(self.grandfathered_keps) + len(self.new_failures)}") + print(f"Passed: {len(self.passed_keps)}") + print(f"Grandfathered: {len(self.grandfathered_keps)} (existing KEPs allowed to fail)") + print(f"New Failures: {len(self.new_failures)} (NEW KEPs that must be fixed)") + + if self.new_failures: + print("\n" + "=" * 80) + print("NEW FAILURES (Must be fixed!)") + print("=" * 80) + for kep_path, errors in self.new_failures: + try: + rel_path = kep_path.relative_to(self.keps_dir.parent) + except ValueError: + rel_path = kep_path + print(f"\n{rel_path}:") + for error in errors: + print(f" - {error}") + + # Return exit code - only fail if there are NEW failures + if self.new_failures: + print("\n" + "=" * 80) + print("VALIDATION FAILED: New KEPs must have complete PRR sections") + print("=" * 80) + return 1 + + print("\n" + "=" * 80) + print("VALIDATION PASSED: No new KEPs with missing PRR sections") + print("=" * 80) + return 0 + + +def main(): + """Main entry point.""" + # Find the keps directory + script_dir = Path(__file__).parent.parent + keps_dir = script_dir / "keps" + baseline_file = script_dir / "hack" / "prr-baseline.txt" + + if not keps_dir.exists(): + print(f"Error: KEPs directory not found at {keps_dir}") + return 1 + + validator = KEPValidator(keps_dir, baseline_file) + return validator.validate_all() + + +if __name__ == "__main__": + sys.exit(main())