feat(validation): add validation scripts for Wild Cloud app packages
This commit is contained in:
284
admin/scripts/validation-rules.yaml
Normal file
284
admin/scripts/validation-rules.yaml
Normal file
@@ -0,0 +1,284 @@
|
||||
# Wild Cloud App Validation Rules
|
||||
#
|
||||
# Loaded by validate-apps.py at runtime.
|
||||
# Run `validate-apps.py --list-rules` for a formatted summary.
|
||||
#
|
||||
# To suppress a rule for an app, add its ID to ignoreRules in app.yaml:
|
||||
# ignoreRules:
|
||||
# - WC-HELM # upstream Helm-rendered manifests
|
||||
#
|
||||
# Fields:
|
||||
# severity: error (must fix before submitting) | warning (strong suggestion)
|
||||
# description: What the rule checks for
|
||||
# remediation: How to fix the issue
|
||||
|
||||
# ── app.yaml ──────────────────────────────────────────────────────────────────
|
||||
|
||||
WC-APP-MISS:
|
||||
severity: error
|
||||
description: app.yaml file is missing
|
||||
remediation: Create app.yaml with name, description, category, latest, and icon fields
|
||||
|
||||
WC-IGN:
|
||||
severity: error
|
||||
description: app.yaml ignoreRules contains an unknown rule ID
|
||||
remediation: Remove the stale entry from ignoreRules, or check for a typo in the rule ID (run --list-rules to see valid IDs)
|
||||
|
||||
WC-APP-PARSE:
|
||||
severity: error
|
||||
description: app.yaml could not be parsed as valid YAML
|
||||
remediation: Fix YAML syntax errors in app.yaml
|
||||
|
||||
WC-APP-EMPTY:
|
||||
severity: error
|
||||
description: app.yaml is empty
|
||||
remediation: Populate app.yaml — it is empty
|
||||
|
||||
WC-REQ:
|
||||
severity: error
|
||||
description: app.yaml missing one or more required fields
|
||||
remediation: "Add the missing fields: name, is, description, category, and latest are all required"
|
||||
|
||||
WC-NAME:
|
||||
severity: error
|
||||
description: app.yaml name does not match the directory name
|
||||
remediation: "Set 'name:' in app.yaml to exactly match the directory name"
|
||||
|
||||
WC-ICON:
|
||||
severity: warning
|
||||
description: app.yaml missing icon field
|
||||
remediation: "Add 'icon: <url>' to app.yaml pointing to the app's logo"
|
||||
|
||||
# ── versions ──────────────────────────────────────────────────────────────────
|
||||
|
||||
WC-VER:
|
||||
severity: error
|
||||
description: versions/ directory is missing
|
||||
remediation: Create a versions/ subdirectory with at least one version slot directory inside it
|
||||
|
||||
WC-LATEST:
|
||||
severity: error
|
||||
description: latest version directory does not exist
|
||||
remediation: "Create the version slot directory named in 'latest:', or update 'latest:' to an existing slot"
|
||||
|
||||
WC-VIA:
|
||||
severity: error
|
||||
description: upgrade.from[].via references a slot directory that does not exist
|
||||
remediation: "Create the waypoint slot directory, or fix the via: value to match an existing slot"
|
||||
|
||||
WC-SLOT:
|
||||
severity: warning
|
||||
description: version slot directory name looks like a full version string or packaging revision
|
||||
remediation: |
|
||||
Rename the slot to the major version only (e.g. '1', 'v1', 'v0').
|
||||
Full versions ('1.2.3') and packaging revisions ('1-2') do not belong in directory names.
|
||||
The actual version string lives in manifest.yaml.
|
||||
|
||||
# ── manifest.yaml ─────────────────────────────────────────────────────────────
|
||||
|
||||
WC-NSFILE:
|
||||
severity: warning
|
||||
description: no Namespace resource defined — workloads will deploy into an unmanaged namespace
|
||||
remediation: |
|
||||
Add a namespace.yaml listing it in kustomization.yaml resources:
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: {{ .namespace }}
|
||||
|
||||
# ── manifest.yaml ─────────────────────────────────────────────────────────────
|
||||
|
||||
WC-MAN-MISS:
|
||||
severity: error
|
||||
description: manifest.yaml is missing
|
||||
remediation: "Create manifest.yaml with at minimum: version, defaultConfig.namespace"
|
||||
|
||||
WC-MAN-PARSE:
|
||||
severity: error
|
||||
description: manifest.yaml could not be parsed
|
||||
remediation: Fix YAML syntax errors in manifest.yaml
|
||||
|
||||
WC-MAN-EMPTY:
|
||||
severity: error
|
||||
description: manifest.yaml is empty
|
||||
remediation: Populate manifest.yaml — it is empty
|
||||
|
||||
WC-MAN-REQ:
|
||||
severity: error
|
||||
description: manifest.yaml missing a required field
|
||||
remediation: "Add the missing field to manifest.yaml (at minimum: version)"
|
||||
|
||||
WC-TVAR:
|
||||
severity: warning
|
||||
description: template variable in resource file not found in defaultConfig
|
||||
remediation: |
|
||||
Add the missing variable to defaultConfig in manifest.yaml, or check for a typo in the template.
|
||||
Only top-level variable names are checked — e.g. .db in {{ .db.host }} must appear as a key under defaultConfig.
|
||||
Global namespaces (cloud, cluster, operator, apps, secrets, app) are always available and exempt.
|
||||
|
||||
WC-CFG:
|
||||
severity: warning
|
||||
description: manifest.yaml defaultConfig is empty or missing
|
||||
remediation: "Add a 'defaultConfig:' block to manifest.yaml"
|
||||
|
||||
WC-CFG-NS:
|
||||
severity: warning
|
||||
description: manifest.yaml defaultConfig missing namespace field
|
||||
remediation: "Add 'namespace: <app-name>' under defaultConfig in manifest.yaml"
|
||||
|
||||
WC-SSL:
|
||||
severity: error
|
||||
description: postgres URL missing ?sslmode=disable
|
||||
remediation: |
|
||||
Append ?sslmode=disable to the postgres connection URL:
|
||||
postgresql://user:pass@host:5432/db?sslmode=disable
|
||||
|
||||
WC-DOT:
|
||||
severity: error
|
||||
description: requiredSecret does not use <app-ref>.<key> dot notation
|
||||
remediation: "Use <app-ref>.<key> format in requiredSecrets (e.g. postgres.password, not just 'password')"
|
||||
|
||||
# ── kustomization.yaml ────────────────────────────────────────────────────────
|
||||
|
||||
WC-KUS-MISS:
|
||||
severity: error
|
||||
description: kustomization.yaml is missing
|
||||
remediation: Create kustomization.yaml listing all resource files with Wild Cloud labels
|
||||
|
||||
WC-KUS-PARSE:
|
||||
severity: error
|
||||
description: kustomization.yaml could not be parsed
|
||||
remediation: Fix YAML syntax errors in kustomization.yaml
|
||||
|
||||
WC-KUS-EMPTY:
|
||||
severity: error
|
||||
description: kustomization.yaml is empty
|
||||
remediation: "Add a resources: list and labels: block to kustomization.yaml"
|
||||
|
||||
WC-RES:
|
||||
severity: error
|
||||
description: a resource listed in kustomization.yaml does not exist on disk
|
||||
remediation: "Create the missing file, or remove it from resources: in kustomization.yaml"
|
||||
|
||||
WC-RES-PARSE:
|
||||
severity: error
|
||||
description: a resource file listed in kustomization.yaml could not be parsed as valid YAML
|
||||
remediation: Fix YAML syntax errors in the resource file
|
||||
|
||||
WC-NS:
|
||||
severity: warning
|
||||
description: no namespace in kustomization.yaml (has Deployment/StatefulSet)
|
||||
remediation: "Add 'namespace: {{ .namespace }}' to kustomization.yaml"
|
||||
|
||||
WC-SEL:
|
||||
severity: error
|
||||
description: labels.includeSelectors true missing (required for Deployment/StatefulSet)
|
||||
remediation: |
|
||||
Add to kustomization.yaml:
|
||||
labels:
|
||||
- includeSelectors: true
|
||||
pairs:
|
||||
app: <app-name>
|
||||
managedBy: kustomize
|
||||
partOf: wild-cloud
|
||||
|
||||
WC-PAIRS:
|
||||
severity: warning
|
||||
description: a standard label pair is missing or has the wrong value
|
||||
remediation: "Ensure labels.pairs includes managedBy: kustomize and partOf: wild-cloud"
|
||||
|
||||
WC-LBL:
|
||||
severity: warning
|
||||
description: label pair 'app' does not match the app directory name
|
||||
remediation: "Set 'app:' in label pairs to match the app directory name"
|
||||
|
||||
# ── workloads ─────────────────────────────────────────────────────────────────
|
||||
|
||||
WC-SC-POD:
|
||||
severity: warning
|
||||
description: no pod-level securityContext defined
|
||||
remediation: |
|
||||
Add to the pod spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: <uid>
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
WC-SC-CTR:
|
||||
severity: warning
|
||||
description: no container-level securityContext defined
|
||||
remediation: |
|
||||
Add to each container spec:
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: [ALL]
|
||||
|
||||
WC-RWO:
|
||||
severity: error
|
||||
description: Deployment using a ReadWriteOnce PVC must use strategy Recreate
|
||||
remediation: "Set strategy: type: Recreate on the Deployment — RWO volumes cannot attach to two pods at once"
|
||||
|
||||
WC-HELM:
|
||||
severity: error
|
||||
description: Helm-style app.kubernetes.io/* labels found in pod template
|
||||
remediation: "Remove app.kubernetes.io/* labels from pod template; use simple labels (e.g. component: web) instead"
|
||||
|
||||
WC-IMG:
|
||||
severity: error
|
||||
description: container image uses a mutable or missing version tag
|
||||
remediation: "Pin the image to a specific release tag (e.g. myimage:1.2.3), not latest/main/stable/etc."
|
||||
|
||||
WC-BITNAMI:
|
||||
severity: warning
|
||||
description: container image is from Bitnami — prefer an official image
|
||||
remediation: |
|
||||
Replace the Bitnami image with the official upstream image.
|
||||
Bitnami images require Docker Hub authentication since 2023, use non-standard filesystem layouts,
|
||||
and are often a sign of a Helm chart that wasn't fully adapted. Check Docker Hub or the project's
|
||||
docs for the official image (e.g. postgres:15, redis:alpine, mariadb:11).
|
||||
|
||||
# ── ingress ───────────────────────────────────────────────────────────────────
|
||||
|
||||
WC-ING-ANN:
|
||||
severity: error
|
||||
description: ingress uses deprecated kubernetes.io/ingress.class annotation
|
||||
remediation: "Replace the annotation with spec.ingressClassName: traefik in the Ingress spec"
|
||||
|
||||
WC-ING-CLS:
|
||||
severity: warning
|
||||
description: ingress spec.ingressClassName should be traefik
|
||||
remediation: "Add 'ingressClassName: traefik' under spec: in the Ingress"
|
||||
|
||||
WC-CERT:
|
||||
severity: error
|
||||
description: ingress uses cert-manager annotation (wildcard cert is pre-distributed)
|
||||
remediation: "Remove the cert-manager.io/cluster-issuer annotation — wildcard TLS is pre-distributed to namespaces"
|
||||
|
||||
WC-DNS:
|
||||
severity: warning
|
||||
description: ingress missing external-dns.alpha.kubernetes.io/target annotation
|
||||
remediation: |
|
||||
Add to Ingress metadata.annotations:
|
||||
external-dns.alpha.kubernetes.io/target: '{{ .externalDnsDomain }}'
|
||||
|
||||
# ── db-init ───────────────────────────────────────────────────────────────────
|
||||
|
||||
WC-DKRDNS:
|
||||
severity: error
|
||||
description: Docker DNS resolver (127.0.0.11) used — not available in Kubernetes
|
||||
remediation: |
|
||||
Remove `resolver 127.0.0.11` and avoid nginx variables in proxy_pass.
|
||||
Use `proxy_pass http://backend-name:port;` (no variable) so nginx resolves via CoreDNS at startup.
|
||||
If a runtime-resolved variable is unavoidable, use the cluster DNS resolver instead:
|
||||
resolver kube-dns.kube-system.svc.cluster.local valid=30s;
|
||||
See docs/nginx.md for the full pattern.
|
||||
|
||||
WC-DBIN:
|
||||
severity: error
|
||||
description: MySQL db-init uses CREATE USER without ALTER USER (passwords won't update on redeploy)
|
||||
remediation: |
|
||||
Add ALTER USER after CREATE USER so passwords update on redeploy:
|
||||
CREATE USER IF NOT EXISTS '${USER}'@'%' IDENTIFIED BY '${PASSWORD}';
|
||||
ALTER USER '${USER}'@'%' IDENTIFIED BY '${PASSWORD}';
|
||||
Reference in New Issue
Block a user