Skip to content

Instantly share code, notes, and snippets.

@venkata-qa
Last active April 9, 2025 13:17
Show Gist options
  • Save venkata-qa/2626d21e517f80be3fc106660af50d8d to your computer and use it in GitHub Desktop.
Save venkata-qa/2626d21e517f80be3fc106660af50d8d to your computer and use it in GitHub Desktop.
#!/bin/bash
NAMESPACE=$1
if [[ -z "$NAMESPACE" ]]; then
echo "❌ Usage: $0 <namespace>"
exit 1
fi
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
REPORT_FILE="k8s_namespace_report_${NAMESPACE}_$(date +%Y%m%d).md"
echo "πŸ› οΈ Generating Markdown Kubernetes report for namespace: $NAMESPACE"
echo "πŸ“„ Report will be saved to: $REPORT_FILE"
# Markdown Helpers
add_heading() {
echo -e "\n## $1\n" >> "$REPORT_FILE"
}
add_subheading() {
echo -e "\n### $1\n" >> "$REPORT_FILE"
}
add_codeblock() {
echo -e "\n\`\`\`bash\n$1\n\`\`\`\n" >> "$REPORT_FILE"
}
# Report Header
cat <<EOF > "$REPORT_FILE"
# Kubernetes Namespace Report
- **Namespace**: \`$NAMESPACE\`
- **Generated**: \`$TIMESTAMP\`
- **Cluster Context**: \`$(kubectl config current-context 2>/dev/null)\`
---
EOF
# 1. Namespace Overview
add_heading "1. Namespace Overview"
kubectl describe namespace $NAMESPACE --request-timeout=30s 2>/dev/null >> "$REPORT_FILE"
# 2. Deployment Summary
add_heading "2. Deployment Summary"
add_subheading "2.1 Deployment Table"
echo '| Name | Ready Replicas | Image | Version |' >> "$REPORT_FILE"
echo '|------|----------------|-------|---------|' >> "$REPORT_FILE"
kubectl get deployments -n $NAMESPACE -o json --request-timeout=30s | jq -r '
.items[] |
{name: .metadata.name, replicas: .status.readyReplicas, images: [.spec.template.spec.containers[].image]} |
[.name, .replicas, (.images[] | split(":")[0]), (.images[] | split(":")[1] // "latest")] | @tsv' | \
awk -F'\t' '{ printf "| %s | %s | %s | %s |\n", $1, $2, $3, $4 }' >> "$REPORT_FILE"
# 3. Pod Status
add_heading "3. Pod Status"
add_subheading "3.1 Pod Summary Table"
echo '| Pod Name | Status | Ready | Restarts | Node | IP | Age |' >> "$REPORT_FILE"
echo '|----------|--------|--------|----------|------|----|-----|' >> "$REPORT_FILE"
kubectl get pods -n $NAMESPACE -o json --request-timeout=30s | jq -r '
.items[] |
{
name: .metadata.name,
status: .status.phase,
ready: ([.status.containerStatuses[]?.ready] | map(tostring) | join(",")),
restarts: ([.status.containerStatuses[]?.restartCount] | add),
node: .spec.nodeName,
ip: .status.podIP,
age: .metadata.creationTimestamp
} |
[.name, .status, .ready, (.restarts // 0), .node, (.ip // "-"), .age] | @tsv' |
while IFS=$'\t' read -r name status ready restarts node ip age; do
printf "| %s | %s | %s | %s | %s | %s | %s |\n" "$name" "$status" "$ready" "$restarts" "$node" "$ip" "$age"
done >> "$REPORT_FILE"
add_subheading "3.2 CrashLoopBackOff Pods (Grouped by App with Logs + Reason)"
echo "" >> "$REPORT_FILE"
kubectl get pods -n "$NAMESPACE" -o json --request-timeout=30s |
jq -r --arg now "$(date -u +%s)" '
.items[] |
{
name: .metadata.name,
app: (.metadata.labels.app // "N/A"),
startTime: .metadata.creationTimestamp,
phase: .status.phase,
containerStatus: .status.containerStatuses[0],
node: .spec.nodeName,
ip: .status.podIP,
image: (.spec.containers[0].image // ""),
} |
{
name,
app,
phase,
status: (if .containerStatus.state.waiting.reason then .containerStatus.state.waiting.reason else .phase end),
reason: (.containerStatus.state.waiting.reason // "-"),
ready: (.containerStatus.ready | tostring),
restarts: (.containerStatus.restartCount // 0),
node,
ip,
version: ((.image | split(":"))[1] // "latest"),
baseImage: (.image | split(":")[0]),
age: (
try (
($now | tonumber) - (.startTime | sub("Z$"; "") | sub("T"; " ") | strptime("%Y-%m-%d %H:%M:%S") | mktime)
) catch (0)
)
} |
select(.reason == "CrashLoopBackOff") |
[.app, .name, .status, .reason, .ready, .restarts, .node, (.ip // "-"), .baseImage, .version,
(if .age < 60 then (.age|tostring)+"s"
elif .age < 3600 then ((.age/60|floor)|tostring)+"m"
elif .age < 86400 then ((.age/3600|floor)|tostring)+"h"
else ((.age/86400|floor)|tostring)+"d" end)
] | @tsv' |
sort | while IFS=$'\t' read -r app name status reason ready restarts node ip image version age; do
if [[ "$current_app" != "$app" ]]; then
current_app="$app"
echo -e "\n#### App: \`$app\`\n" >> "$REPORT_FILE"
echo '| Pod Name | Status | Reason | Ready | Restarts | Node | IP | Image | Version | Age |' >> "$REPORT_FILE"
echo '|----------|--------|--------|--------|----------|------|----|-------|---------|-----|' >> "$REPORT_FILE"
fi
printf "| %s | %s | %s | %s | %s | %s | %s | %s | %s | %s |\n" \
"$name" "$status" "$reason" "$ready" "$restarts" "$node" "$ip" "$image" "$version" "$age" >> "$REPORT_FILE"
echo -e "\n<details>\n<summary>πŸ” Logs for \`$name\`</summary>\n" >> "$REPORT_FILE"
echo '```log' >> "$REPORT_FILE"
kubectl logs -n "$NAMESPACE" "$name" -p --tail=20 2>&1 >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
echo "</details>" >> "$REPORT_FILE"
done
add_subheading "3.3 Application Containers"
echo '| Pod Name | Container | Image | Version |' >> "$REPORT_FILE"
echo '|----------|-----------|-------|---------|' >> "$REPORT_FILE"
kubectl get pods -n "$NAMESPACE" -o json --request-timeout=30s | jq -r '
.items[] |
{
podName: .metadata.name,
containers: .spec.containers
} |
.containers[] as $c |
{
podName: .podName,
container: $c.name,
image: $c.image,
version: (
if ($c.image | contains(":")) then ($c.image | split(":")[1]) else "latest" end
),
imageName: ($c.image | split(":")[0]),
isSidecar: ($c.image | test("istio|proxy|envoy|sidecar"))
} |
select(.isSidecar | not) |
[.podName, .container, .imageName, .version] | @tsv' |
while IFS=$'\t' read -r pod container image version; do
printf "| %s | %s | %s | %s |\n" "$pod" "$container" "$image" "$version"
done >> "$REPORT_FILE"
add_subheading "3.4 Sidecar Containers"
echo '| Pod Name | Container | Image | Version |' >> "$REPORT_FILE"
echo '|----------|-----------|-------|---------|' >> "$REPORT_FILE"
kubectl get pods -n "$NAMESPACE" -o json --request-timeout=30s | jq -r '
.items[] |
{
podName: .metadata.name,
containers: .spec.containers
} |
.containers[] as $c |
{
podName: .podName,
container: $c.name,
image: $c.image,
version: (
if ($c.image | contains(":")) then ($c.image | split(":")[1]) else "latest" end
),
imageName: ($c.image | split(":")[0]),
isSidecar: ($c.image | test("istio|proxy|envoy|sidecar"))
} |
select(.isSidecar) |
[.podName, .container, .imageName, .version] | @tsv' |
while IFS=$'\t' read -r pod container image version; do
printf "| %s | %s | %s | %s |\n" "$pod" "$container" "$image" "$version"
done >> "$REPORT_FILE"
# 4. Services
add_heading "4. Services"
echo '```bash' >> "$REPORT_FILE"
kubectl get services -n $NAMESPACE -o wide --request-timeout=30s >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
# 5. Persistent Volume Claims
add_heading "5. Persistent Volume Claims"
echo '```bash' >> "$REPORT_FILE"
kubectl get pvc -n $NAMESPACE -o wide --request-timeout=30s >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
# 6. ConfigMaps & Secrets
add_heading "6. ConfigMaps and Secrets"
add_subheading "6.1 ConfigMaps"
echo '```bash' >> "$REPORT_FILE"
kubectl get configmaps -n $NAMESPACE --request-timeout=30s >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
add_subheading "6.2 Secrets"
echo '```bash' >> "$REPORT_FILE"
kubectl get secrets -n $NAMESPACE --request-timeout=30s >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
# 7. Events
add_heading "7. Recent Events (Last 20)"
echo '```bash' >> "$REPORT_FILE"
kubectl get events -n $NAMESPACE --sort-by='.lastTimestamp' --request-timeout=30s | tail -n 20 >> "$REPORT_FILE"
echo '```' >> "$REPORT_FILE"
# 8. Recommendations
add_heading "8. Recommendations"
cat <<EOF >> "$REPORT_FILE"
**Detected Non-Running Pods:**
\`\`\`bash
$(kubectl get pods -n $NAMESPACE --field-selector=status.phase!=Running -o name --request-timeout=30s)
\`\`\`
**Suggested Actions:**
1. Check logs of failed pods:
\`kubectl logs -n $NAMESPACE <pod-name> -p\`
2. Investigate image pull failures (ImagePullBackOff):
Validate image name, tag, and registry access.
3. For pods with high restart counts, review application logs.
4. View full event history for a pod:
\`kubectl describe pod -n $NAMESPACE <pod-name>\`
EOF
echo -e "\nβœ… Markdown report generated successfully!"
echo "πŸ“„ File: $REPORT_FILE"
HTML_FILE="${REPORT_FILE%.md}.html"
echo "πŸ§ͺ Generating HTML version: $HTML_FILE"
{
echo "<!DOCTYPE html>"
echo "<html><head><meta charset='UTF-8'>"
echo "<title>K8s Report: $NAMESPACE</title>"
echo "<style>
body { font-family: sans-serif; padding: 1rem; line-height: 1.6; }
h1, h2, h3, h4 { color: #2c3e50; }
table { width: 100%; border-collapse: collapse; margin: 1em 0; font-size: 14px; }
th, td { border: 1px solid #ccc; padding: 6px 10px; text-align: left; }
th { background-color: #f4f4f4; }
details { margin-bottom: 1em; }
summary { font-weight: bold; cursor: pointer; }
pre, code { background: #f8f8f8; padding: 8px; display: block; overflow-x: auto; }
</style></head><body>"
echo "<h1>Kubernetes Namespace Report</h1>"
echo "<p><strong>Namespace:</strong> <code>$NAMESPACE</code><br>"
echo "<strong>Generated:</strong> $(date)<br>"
echo "<strong>Cluster:</strong> <code>$(kubectl config current-context 2>/dev/null)</code></p>"
awk '
BEGIN { inside_code=0 }
{
if ($0 ~ /^### /) { gsub(/^### /, "<h3>"); print $0 "</h3>" }
else if ($0 ~ /^## /) { gsub(/^## /, "<h2>"); print $0 "</h2>" }
else if ($0 ~ /^# /) { gsub(/^# /, "<h1>"); print $0 "</h1>" }
else if ($0 ~ /^```/) {
if (inside_code) { print "</pre>"; inside_code=0 }
else { print "<pre>"; inside_code=1 }
}
else if ($0 ~ /^<details>/ || $0 ~ /^<\/details>/ || $0 ~ /^<summary>/) {
print $0
}
else if ($0 ~ /^\|/) {
gsub(/\|/, "</td><td>")
sub(/^<\/td>/, "<tr><td>")
print $0 "</td></tr>"
}
else if (inside_code) {
print $0
}
else {
print "<p>" $0 "</p>"
}
}' "$REPORT_FILE"
echo "</body></html>"
} > "$HTML_FILE"
echo "βœ… HTML report saved: $HTML_FILE"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment