Reference Scorecard
Cross-resource validation checks
The checks every cluster evaluation runs over the finished manifests, and what each one skips.
Every cluster evaluation runs these checks over the finished manifest list:
nix build, and every kix command that evaluates a cluster, including
kix build, kix check and kix deploy. They ask whether the object a name
points at exists in the build, whether a selector matches a pod template in
the build, and whether a custom resource depends on a declaration of its
kind. They are always on, and scorecard.maxSeverity does not cap them.
This page is hand-maintained. Check kix/lib/eval/validate.nix when in doubt;
kix/lib/tests/validate.nix (checks 1 to 18) and
kix/lib/tests/units/lib-reference-checks.nix (checks 18 to 23) hold a
failing and a passing case for each check.
Checks
Section titled “Checks”| # | Check | Skips |
|---|---|---|
| 1 | A workload’s serviceAccountName names a ServiceAccount in its namespace. | default; unset |
| 2 | A RoleBinding or ClusterRoleBinding roleRef names a Role or ClusterRole in the build. | names starting system: or ending -psp; admin, edit, view, cluster-admin |
| 3 | volumes[].configMap.name names a ConfigMap in the namespace. | Secrets |
| 4 | A Service selector matches at least one pod template. | no selector; the control-plane scrape selectors (kube-dns, kube-scheduler and similar); a Service that declares kix.run/operand-of |
| 5 | An HPA scaleTargetRef exists. | |
| 6 | A StatefulSet serviceName names a headless Service (clusterIP: None) in the namespace. | unset |
| 7 | No two resources share kind, namespace and name. | |
| 8 | Every resource carries app.kubernetes.io/managed-by: kix. | |
| 9 | A workload’s selector.matchLabels is within its pod template labels (Deployment, StatefulSet, DaemonSet, Job). | |
| 10 | Ingress backends name a Service in the namespace. | |
| 11 | Every metadata.namespace has a Namespace resource in the build. | _-prefixed namespaces used for cluster-level instances |
| 12 | A NetworkPolicy podSelector matches at least one pod template. | empty selector |
| 13 | A named Service targetPort exists on a container of a selected pod. | numeric ports; Services that select no pod template in the build |
| 14 | A PodDisruptionBudget selector.matchLabels matches at least one pod template. | no matchLabels; a PodDisruptionBudget that declares kix.run/operand-of |
| 15 | A Job’s pod template labels satisfy no Service selector in its namespace, since the Service would route traffic to the Job’s pod. | Services without a selector |
| 16 | kix.run/rerun appears on Jobs only, with the value on-change. | |
| 17 | A workload’s priorityClassName names a PriorityClass in the build. | system-cluster-critical, system-node-critical |
| 18 | A resource of a namespaced kind sets metadata.namespace. | kinds whose scope Kix does not know (see below) |
| 19 | A workload’s volumes[].persistentVolumeClaim.claimName names a PersistentVolumeClaim in its namespace. | |
| 20 | The storageClassName of a PersistentVolumeClaim, of a StatefulSet’s volumeClaimTemplates, or of a generic ephemeral volume’s volumeClaimTemplate, names a StorageClass in the build or imported, or a PersistentVolume in the build carries it. | unset and ""; classes inside operator custom resources (an SGCluster’s, a VMCluster’s) |
| 21 | A resource outside the built-in API groups depends, through kix.run/depends-on, on a declaration of its group and kind that serves its version (see below). | built-in groups |
| 22 | A key a container reads by name exists in its Secret or ConfigMap: env[].valueFrom.secretKeyRef/configMapKeyRef, items[].key of a Secret, ConfigMap or projected volume, and the first segment of a subPath into a Secret or ConfigMap volume without items (see below). | optional: true; whole use (envFrom, a volume without items) |
| 23 | At most one PriorityClass sets globalDefault: true, at most one StorageClass carries storageclass.kubernetes.io/is-default-class: "true" (or the beta storageclass.beta.kubernetes.io/is-default-class, which Kubernetes still honours), and at most one IngressClass carries ingressclass.kubernetes.io/is-default-class: "true". |
Check 18 and the deploy-time check
Section titled “Check 18 and the deploy-time check”Check 18 covers built-in namespaced kinds, matched by API group, custom
kinds whose CustomResourceDefinition in the same build declares
spec.scope: Namespaced, and custom kinds a scope.mkCRDRef declares with
crdScope = "Namespaced" (the default). It fails, for example, when
scope.mkClusterResource builds a ConfigMap. Create such a resource with a
namespaced scope (scope.mkResource) or set metadata.namespace.
Any other custom kind is checked against the cluster instead. kix deploy reads the
cluster’s API discovery and refuses the whole plan before applying anything
when such a resource has no namespace. kix diff against a live cluster
prints the same error for each one.
Check 21: custom resources and their declarations
Section titled “Check 21: custom resources and their declarations”The apiserver rejects an object whose kind it does not serve, and kix deploy
applies a custom resource after its CRD only when the resource depends on the
CRD. So every resource outside the built-in API groups ("", apps, batch,
policy, autoscaling, the *.k8s.io groups Kubernetes serves, and Kix’s
own group) must depend, directly or through other resources, on one of:
- a CustomResourceDefinition in the build with its group and kind
(
scope.mkCRD, vendored CRDs, a chart’s own CRDs); - a CRD reference marker for its group and kind (
scope.mkCRDRef, or akix.mkImport { crds }entry); - an APIService for its group (a resource or an import marker).
The resource’s version must be one the declaration serves: a version in the
CRD’s spec.versions that is not served: false, or one of the reference’s
servedVersions. Building the resource with the declaring package’s builder
(scope.mkCR dep.out.crds.<Kind>, or dep.out.mk<Kind> where it exists) adds
the dependency.
A Helm chart’s custom resources depend on the chart’s own CRDs. A chart that renders a resource of another package’s kind (a ServiceMonitor behind a values toggle, for example) fails this check, because a chart resource cannot take a dependency: turn the toggle off and build the resource with the owner’s builder in a package of your own.
The failure names the resource, the instance that built it, and the declaration and its instance when one exists:
- [validate] ServiceMonitor apps/web (instance apps/web) does not depend on the CRD ref crd-ref-prometheus-operator-servicemonitors-monitoring-coreos-com for servicemonitors.monitoring.coreos.com (instance monitoring/prometheus-operator), which declares its kind, so the deploy can apply it before the CRD is served. ...Check 22: named keys
Section titled “Check 22: named keys”Keys are known for a Secret built with scope.mkSecret (its declared keys),
for a scope.mkSecretRef that lists keys, and for a ConfigMap in the build
(its data and binaryData). A pod that reads a key of a scope.mkSecretRef
with keys = [ ] fails too: list the keys it reads, so the deploy can check
the live Secret holds them.
These are not checked: a name declared with kix.untrackedRef (a Secret a
controller creates); a name with no object in the build, which check 3 and
the deploy own; a Secret without declared keys, such as a Helm chart’s; an
object whose kix.run/cede-fields cedes data, binaryData or
stringData to another manager.
How names resolve
Section titled “How names resolve”A reference is looked up in the referrer’s namespace only, unless the referenced kind is a built-in cluster-scoped kind, such as a ClusterRole named by a RoleBinding or a StorageClass named by a claim. A Service, NetworkPolicy or PodDisruptionBudget selector matches pod templates in its own namespace only.
These also count as present:
- a name wrapped in
kix.untrackedRef, in the referring resource’s namespace, whatever its kind; - a
scope.mkServiceReforscope.mkSecretRefmarker, as the Service or Secret it describes, and its declared pod selector as pods for checks 4 and 12; - a
kix.mkImportmarker withkindandout.name, as that object; for a built-in cluster-scoped kind (a StorageClass, an IngressClass) the marker counts from every namespace; - a custom resource named by
kix.run/operand-of: <group>/<Kind>/<name>on a Service or PodDisruptionBudget, when that resource is in the build in the same namespace, for checks 4 and 14.
Check 6 reads clusterIP: None from a rendered Service, so a marker does not
satisfy it.
Failure shape
Section titled “Failure shape”Failures stop the evaluation and are listed together:
error: Cluster 'ludo-kind' has 1 failed assertion(s): - [validate] Service data/postgres-metrics selector {"app":"pg"} matches no workload pod template