From devops-skills
Validates Kubernetes YAML manifests (Deployment, Service, ConfigMap, CRD) via linting, schema checks, dry-run, and CRD lookup. Report-only with fix suggestions.
npx claudepluginhub akin-ozer/cc-devops-skills --plugin devops-skillsThis skill uses the workspace's default tool permissions.
This skill provides a comprehensive validation workflow for Kubernetes YAML resources, combining syntax linting, schema validation, cluster dry-run testing, and intelligent CRD documentation lookup. Validate any Kubernetes manifest with confidence before applying it to the cluster.
references/k8s_best_practices.mdreferences/validation_workflow.mdscripts/count_yaml_documents.pyscripts/detect_crd.pyscripts/detect_crd_wrapper.shscripts/setup_tools.shscripts/test_count_yaml_documents.pytest/certificate-crd-test.yamltest/comprehensive-test.yamltest/deployment-test.yamltest/document-counter-edge-cases.yamltest/schema-errors-test.yamlSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
This skill provides a comprehensive validation workflow for Kubernetes YAML resources, combining syntax linting, schema validation, cluster dry-run testing, and intelligent CRD documentation lookup. Validate any Kubernetes manifest with confidence before applying it to the cluster.
IMPORTANT: This is a REPORT-ONLY validation tool. Do NOT modify files, do NOT use Edit tool, do NOT use AskUserQuestion to offer fixes. Generate a comprehensive validation report with suggested fixes shown as before/after code blocks, then let the user decide what to do next.
Use this skill when prompts look like:
Invoke this skill when:
This skill is strictly report-only:
Run with explicit paths so commands are repeatable:
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
SKILL_DIR="$REPO_ROOT/devops-skills-plugin/skills/k8s-yaml-validator"
TARGET_FILE="$REPO_ROOT/<relative/path/to/file.yaml>"
Path checks:
REPO_ROOT is empty, stop and ask for repository root.SKILL_DIR does not exist, stop and report path mismatch.TARGET_FILE does not exist, stop and ask for the correct file.Follow this sequential validation workflow. Each stage catches different types of issues:
Before running validators, count documents using the bundled script:
python3 "$SKILL_DIR/scripts/count_yaml_documents.py" "$TARGET_FILE"
Expected output (example):
{
"file": ".../manifests.yaml",
"documents": 3,
"separators": 2
}
Gate rules:
documents >= 3, load references/validation_workflow.md before Stage 1.python3 is unavailable, use fallback:awk 'BEGIN{d=0;seen=0} /^[[:space:]]*---[[:space:]]*$/ {if(seen){d++;seen=0}; next} /^[[:space:]]*#/ {next} NF{seen=1} END{if(seen)d++; print d}' "$TARGET_FILE"
and mark the count as estimated in the report.
Before starting validation, verify required tools are installed:
bash "$SKILL_DIR/scripts/setup_tools.sh"
Required tools:
If tools are missing, display installation guidance from script output and continue with available tools. Document missing tools and skipped stages in the report.
Validate YAML syntax and formatting using yamllint:
yamllint -c "$SKILL_DIR/assets/.yamllint" "$TARGET_FILE"
Common issues caught:
Reporting approach:
Before schema validation, detect if the YAML contains Custom Resource Definitions:
bash "$SKILL_DIR/scripts/detect_crd_wrapper.sh" "$TARGET_FILE"
The wrapper script automatically handles Python dependencies by creating a temporary virtual environment if PyYAML is not available.
Resilient Parsing: The script is resilient to syntax errors in individual documents. If a multi-document YAML file has some valid and some invalid documents, the script will:
The script outputs JSON with resource information and parse status:
{
"resources": [
{
"kind": "Certificate",
"apiVersion": "cert-manager.io/v1",
"group": "cert-manager.io",
"version": "v1",
"isCRD": true,
"name": "example-cert"
}
],
"parseErrors": [
{
"document": 1,
"start_line": 2,
"error_line": 6,
"error": "mapping values are not allowed in this context"
}
],
"summary": {
"totalDocuments": 3,
"parsedSuccessfully": 2,
"parseErrors": 1,
"crdsDetected": 1
}
}
For each detected CRD:
Try Context7 MCP first (preferred):
mcp__context7__resolve-library-idlibraryName: CRD project name (example: cert-manager for cert-manager.io)mcp__context7__query-docslibraryId: resolved library ID from previous stepquery: include CRD kind, group, and version (example: Certificate cert-manager.io v1 required fields in spec)Fallback to web.search_query if Context7 fails or returns insufficient details:
Search query pattern:
"<kind>" "<group>" kubernetes CRD "<version>" documentation spec
Example:
"Certificate" "cert-manager.io" kubernetes CRD "v1" documentation spec
Extract key information:
specSecondary CRD Detection via kubeconform: If detect_crd_wrapper.sh cannot identify CRDs (for example, syntax errors in all documents), but kubeconform still validates a CRD resource, look up docs for that CRD anyway. Parse kubeconform output to identify validated CRDs and perform Context7/web.search_query lookups.
Why this matters: CRDs have custom schemas not available in standard Kubernetes validation tools. Understanding the CRD's spec requirements prevents validation errors and ensures correct resource configuration.
Validate against Kubernetes schemas using kubeconform:
kubeconform \
-schema-location default \
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
-strict \
-ignore-missing-schemas \
-summary \
-verbose \
"$TARGET_FILE"
Options explained:
-strict: Reject unknown fields (recommended for production - catches typos)-ignore-missing-schemas: Skip validation for CRDs without available schemas-kubernetes-version 1.30.0: Validate against specific K8s versionCommon issues caught:
For CRDs: If kubeconform reports "no schema found", this is expected. Use the documentation from Stage 3 to manually validate the spec fields.
kubeconform line number behavior — two distinct cases:
kubeconform does NOT report file-absolute line numbers. You must translate:
Parse errors (e.g. error converting YAML to JSON: yaml: line N):
N is document-relative (line N within that document's content).file_line = doc_start_line + N - 1doc_start_line comes from the start_line field in detect_crd_wrapper.sh output.yaml: line 5 →
file-absolute line = 4 + 5 − 1 = line 8 (matches yamllint output).Schema validation errors (e.g. got string, want integer):
at '/spec/template/spec/containers/0/ports/0/containerPort': got string, want integercontainerPort) within
the relevant document section, using file-absolute line numbers from the surrounding context.Always present line numbers as file-absolute in the validation report even when translating from kubeconform's document-relative output.
IMPORTANT: Always try server-side dry-run first. Server-side validation catches more issues than client-side because it runs through admission controllers and webhooks.
Decision Tree:
1. Try server-side dry-run first:
kubectl apply --dry-run=server -f "$TARGET_FILE"
└─ If SUCCESS → Use results, continue to Stage 6
└─ If FAILS with connection error (e.g., "connection refused",
"unable to connect", "no configuration"):
│
├─ 2. Attempt client-side dry-run (parse-only fallback):
│ kubectl apply --dry-run=client --validate=false -f "$TARGET_FILE"
│
│ ├─ If SUCCESS:
│ │ Document in report: "Server-side validation skipped (no cluster access); client fallback ran in parse-only mode"
│ │
│ └─ If FAILS with discovery/openapi error (e.g., "unable to recognize",
│ "failed to download openapi", "couldn't get current server API group list"):
│ Document in report: "Dry-run skipped (cluster discovery unavailable)"
│ Continue to Stage 6
│
└─ If FAILS with validation error (e.g., "admission webhook denied",
"resource quota exceeded", "invalid value"):
└─ Record the error, continue to Stage 6
└─ If FAILS with parse error (e.g., "error converting YAML to JSON",
"yaml: line X: mapping values are not allowed"):
└─ Record the error, skip client-side dry-run (same error will occur)
Document in report: "Dry-run blocked by YAML syntax errors - fix syntax first"
Continue to Stage 6
Note: Parse errors from earlier stages (yamllint, kubeconform) will also cause dry-run to fail. Do NOT attempt client-side dry-run as a fallback for parse errors - it will produce the same error. Parse errors must be fixed before dry-run validation can proceed.
Server-side dry-run catches:
Client-side dry-run with --validate=false catches (fallback, when command succeeds):
kubectl can process and submit the manifest shape in client mode--validate=false disables schema/type/required-field validation and still does NOT catch admission controller or policy issues.Document in your report which mode was used:
--validate=false: "Limited parse-only validation (no cluster access) - schema and admission policies not checked"For updates to existing resources:
kubectl diff -f "$TARGET_FILE"
This shows what would change, helping catch unintended modifications.
After completing all validation stages, generate a comprehensive report. This is a REPORT-ONLY stage.
NEVER do any of the following:
ALWAYS do the following:
Summarize all issues found across all stages in a table format:
| Severity | Stage | Location | Issue | Suggested Fix |
|----------|-------|----------|-------|---------------|
| Error | Syntax | file.yaml:5 | Indentation error | Use 2 spaces |
| Error | Schema | file.yaml:21 | Wrong type | Change to integer |
| Warning | Best Practice | file.yaml:30 | Missing labels | Add app label |
Categorize by severity:
Show before/after code blocks for each issue:
For every issue, display explicit before/after YAML snippets showing the suggested fix:
**Issue 1: deployment.yaml:21 - Wrong field type (Error)**
Current:
```yaml
- containerPort: "80"
Suggested Fix:
- containerPort: 80
Why: containerPort must be an integer, not a string. Kubernetes will reject string values. Reference: See k8s_best_practices.md "Invalid Values" section.
Provide validation summary:
## Validation Report Summary
File: deployment.yaml
Resources Analyzed: 3 (Deployment, Service, Certificate)
| Stage | Status | Issues Found |
|-------|--------|--------------|
| YAML Syntax | ❌ Failed | 2 errors |
| CRD Detection | ✅ Passed | 1 CRD detected (Certificate) |
| Schema Validation | ❌ Failed | 1 error |
| Dry-Run | ❌ Failed | 1 error |
Total Issues: 4 errors, 2 warnings
## Detailed Findings
[List each issue with before/after code blocks as shown above]
## Next Steps
1. Fix the 4 errors listed above (deployment will fail without these)
2. Consider addressing the 2 warnings for best practices
3. Re-run validation after fixes to confirm resolution
Do NOT modify files - this is a reporting tool only
Use this table to keep stage decisions deterministic:
| Stage | Required | Command | Pass/Fail Criteria | Fallback |
|---|---|---|---|---|
| 0 Resource Count | Yes | python3 "$SKILL_DIR/scripts/count_yaml_documents.py" "$TARGET_FILE" | Pass when count output is produced and documents is recorded. | Use AWK estimator and mark estimated. |
| 1 Tool Check | Yes | bash "$SKILL_DIR/scripts/setup_tools.sh" | Pass when command runs and tool availability is known. | Continue with available tools and log skips. |
| 2 YAML Syntax | If yamllint available | yamllint -c "$SKILL_DIR/assets/.yamllint" "$TARGET_FILE" | Pass on exit code 0; fail on lint errors. | Skip with explicit reason if missing binary. |
| 3 CRD Detection | If python3 available | bash "$SKILL_DIR/scripts/detect_crd_wrapper.sh" "$TARGET_FILE" | Pass when JSON output includes summary. | Skip CRD extraction and rely on kubeconform clues. |
| 4 Schema | If kubeconform available | kubeconform command from Stage 4 | Pass when kubeconform reports valid resources. | Skip and record as coverage gap if missing binary. |
| 5 Dry-Run | If kubectl available | kubectl apply --dry-run=server -f "$TARGET_FILE" | Pass on successful server dry-run. | Attempt parse-only client fallback with --dry-run=client --validate=false; if discovery still fails, mark stage skipped. |
| 6 Report | Yes | Report generation | Pass when summary + per-issue snippets + next steps are provided. | No fallback; this stage is mandatory. |
| Constraint | Action | Report Language |
|---|---|---|
python3 unavailable | Skip count_yaml_documents.py and CRD parser scripts. Use AWK count only. | Python runtime unavailable; CRD parser skipped, resource count is estimated. |
yamllint unavailable | Skip Stage 2; continue with schema/dry-run stages if available. | YAML lint skipped because yamllint is not installed. |
kubeconform unavailable | Skip Stage 4; run lint and dry-run only. | Schema validation skipped because kubeconform is not installed. |
kubectl unavailable | Skip Stage 5 entirely. | Dry-run skipped because kubectl is not installed. |
| No cluster connectivity | Run server-side first, then attempt parse-only client fallback with --dry-run=client --validate=false; if it still fails, skip dry-run and continue. | Server-side dry-run unavailable due cluster access; parse-only client-side dry-run attempted (schema checks disabled). |
| Client dry-run still requires discovery | Treat dry-run as unavailable and rely on lint + schema stages. | Dry-run skipped (cluster discovery unavailable); lint and schema results used. |
| External docs unavailable | Continue local validation and state documentation gap. | CRD documentation lookup deferred due tooling/network limitation. |
For detailed Kubernetes YAML best practices, load the reference:
Read "$SKILL_DIR/references/k8s_best_practices.md"
This reference includes:
When to load (ALWAYS load in these cases):
For in-depth workflow details and error handling strategies, load the reference:
Read "$SKILL_DIR/references/validation_workflow.md"
This reference includes:
When to load (ALWAYS load in these cases):
When a YAML file contains multiple resources (separated by ---):
When a multi-document YAML file has some valid and some invalid documents:
Expected behavior:
detect_crd.py) will parse valid documents and skip invalid onesExample scenario: A file with 3 documents where document 1 has a syntax error:
Expected output:
In your report:
| Document | Resource | Parsing | Validation |
|----------|----------|---------|------------|
| 1 | Deployment | ❌ Syntax error (line 8) | Skipped |
| 2 | Service | ✅ Parsed | ✅ Valid |
| 3 | Certificate | ✅ Parsed | ✅ Valid |
Line Number Reference Style:
This ensures users get maximum validation feedback even when some documents have issues.
bash "$SKILL_DIR/scripts/setup_tools.sh" to check availability--dry-run=client --validate=false--validate=false disables schema/type/required-field checks)kubectl get crd <crd-name>.group -o yaml
kubectl explain <kind>
When presenting validation results:
Be clear and concise about what was found
Explain why issues matter (e.g., "This will cause pod creation to fail")
Provide context from best practices when relevant
Group related issues (e.g., all missing label issues together)
Use file:line references for all issues
Show fix complexity - Include a complexity indicator in the issue header:
Example format in issue header:
**Issue 1: deployment.yaml:8 - Wrong indentation (Error) [Simple]**
**Issue 2: deployment.yaml:15-25 - Missing security context (Warning) [Medium]**
**Issue 3: deployment.yaml - Selector mismatch with Service (Error) [Complex]**
Always provide a comprehensive report including:
NEVER offer to apply fixes - this is strictly a reporting tool
For improved validation speed, some stages can be executed in parallel:
Can run in parallel (no dependencies):
yamllint (Stage 2) and detect_crd_wrapper.sh (Stage 3) can run simultaneouslyExample parallel execution:
# Run these in parallel (using & and wait, or parallel tool calls):
yamllint -c "$SKILL_DIR/assets/.yamllint" "$TARGET_FILE"
bash "$SKILL_DIR/scripts/detect_crd_wrapper.sh" "$TARGET_FILE"
Must run sequentially:
When to parallelize:
Always consider Kubernetes version compatibility:
extensions/v1beta1 → apps/v1)kubectl api-versions to list available API versions in the clusterThe test/ directory contains example files to exercise all validation paths. Use these to verify skill behavior.
| Test File | Purpose | Expected Behavior |
|---|---|---|
deployment-test.yaml | Valid standard K8s resource | All stages pass, no errors |
certificate-crd-test.yaml | Valid CRD resource | CRD detected, Context7 lookup performed, no errors |
comprehensive-test.yaml | Multi-resource with intentional YAML syntax error | Syntax error detected, partial parsing works, CRD found |
schema-errors-test.yaml | Valid YAML with intentional schema type errors | yamllint passes; kubeconform fails with 2 JSON-path errors (replicas, containerPort) |
deployment-test.yamlcd "$SKILL_DIR"
python3 scripts/count_yaml_documents.py test/deployment-test.yaml
yamllint -c assets/.yamllint test/deployment-test.yaml
bash scripts/detect_crd_wrapper.sh test/deployment-test.yaml
kubeconform \
-schema-location default \
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
-strict -ignore-missing-schemas -summary -verbose \
test/deployment-test.yaml
kubectl apply --dry-run=server -f test/deployment-test.yaml
certificate-crd-test.yamlmcp__context7__resolve-library-id and mcp__context7__query-docs usedcd "$SKILL_DIR"
python3 scripts/count_yaml_documents.py test/certificate-crd-test.yaml
bash scripts/detect_crd_wrapper.sh test/certificate-crd-test.yaml
kubeconform \
-schema-location default \
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
-strict -ignore-missing-schemas -summary -verbose \
test/certificate-crd-test.yaml
comprehensive-test.yamlcd "$SKILL_DIR"
python3 scripts/count_yaml_documents.py test/comprehensive-test.yaml
yamllint -c assets/.yamllint test/comprehensive-test.yaml
bash scripts/detect_crd_wrapper.sh test/comprehensive-test.yaml
kubeconform \
-schema-location default \
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
-strict -ignore-missing-schemas -summary -verbose \
test/comprehensive-test.yaml
kubectl apply --dry-run=server -f test/comprehensive-test.yaml
comprehensive-test.yaml (has 3 resources, 1 with syntax error)cd "$SKILL_DIR"
python3 scripts/count_yaml_documents.py test/comprehensive-test.yaml
bash scripts/detect_crd_wrapper.sh test/comprehensive-test.yaml
schema-errors-test.yamlcd "$SKILL_DIR"
python3 scripts/count_yaml_documents.py test/schema-errors-test.yaml
yamllint -c assets/.yamllint test/schema-errors-test.yaml
bash scripts/detect_crd_wrapper.sh test/schema-errors-test.yaml
kubeconform \
-schema-location default \
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
-strict -ignore-missing-schemas -summary -verbose \
test/schema-errors-test.yaml
cd "$SKILL_DIR"
KUBECONFIG=/tmp/nonexistent-kubeconfig kubectl apply --dry-run=server -f test/deployment-test.yaml
KUBECONFIG=/tmp/nonexistent-kubeconfig kubectl apply --dry-run=client --validate=false -f test/deployment-test.yaml
cd "$SKILL_DIR"
PATH="/usr/bin:/bin" bash scripts/setup_tools.sh
When adding test files:
<scenario>-test.yamlFor any validation, the report should include:
Validation is complete only when all conditions are true:
count_yaml_documents.py (or documented AWK fallback).mcp__context7__resolve-library-id + mcp__context7__query-docs, with web.search_query fallback only when needed.detect_crd_wrapper.sh
bash "$SKILL_DIR/scripts/detect_crd_wrapper.sh" "$TARGET_FILE"detect_crd.py
python3 "$SKILL_DIR/scripts/detect_crd.py" "$TARGET_FILE"count_yaml_documents.py
python3 "$SKILL_DIR/scripts/count_yaml_documents.py" "$TARGET_FILE"setup_tools.sh
bash "$SKILL_DIR/scripts/setup_tools.sh"k8s_best_practices.md
validation_workflow.md
.yamllint
yamllint -c "$SKILL_DIR/assets/.yamllint" "$TARGET_FILE"