Improve Jeannie explain readability
This commit is contained in:
parent
86e061dd72
commit
d0004d99f8
|
|
@ -2,6 +2,7 @@ pattern meaning diagnose_commands jeannie_fix_commands other_fix_commands risk a
|
||||||
unsupported template variable Generated docs found a template placeholder that render-docs does not support. ./jeannie validate none Edit README.md.tmpl or scripts/render-docs low no Usually a docs/template mismatch rather than an infrastructure issue.
|
unsupported template variable Generated docs found a template placeholder that render-docs does not support. ./jeannie validate none Edit README.md.tmpl or scripts/render-docs low no Usually a docs/template mismatch rather than an infrastructure issue.
|
||||||
generated README.md is stale README.md does not match README.md.tmpl rendering. ./jeannie validate none scripts/render-docs low candidate A future docs-render Jeannie command could safely wrap this.
|
generated README.md is stale README.md does not match README.md.tmpl rendering. ./jeannie validate none scripts/render-docs low candidate A future docs-render Jeannie command could safely wrap this.
|
||||||
missing_resource_policy Workloads are missing resource requests or limits, so capacity planning is unreliable. ./jeannie resource-budget;./jeannie capacity none Edit app manifests or Helm values medium no Add requests first, then limits for workloads you own.
|
missing_resource_policy Workloads are missing resource requests or limits, so capacity planning is unreliable. ./jeannie resource-budget;./jeannie capacity none Edit app manifests or Helm values medium no Add requests first, then limits for workloads you own.
|
||||||
|
pods missing requests/limits Some Kubernetes pods are missing resource requests or limits, so the scheduler and capacity reports cannot fully trust workload sizing. ./jeannie resource-budget --details;./jeannie capacity none Add requests/limits in the app manifests, Helm values, or platform chart values medium no Yes, this is fixed from code/config by adding resources to workloads you own; avoid auto-generating limits blindly.
|
||||||
missing requests One or more containers do not declare CPU or memory requests. ./jeannie resource-budget --details;./jeannie capacity none Edit app manifests or Helm values medium no Start with application namespaces before system namespaces.
|
missing requests One or more containers do not declare CPU or memory requests. ./jeannie resource-budget --details;./jeannie capacity none Edit app manifests or Helm values medium no Start with application namespaces before system namespaces.
|
||||||
missing limits One or more containers do not declare CPU or memory limits. ./jeannie resource-budget --details;./jeannie capacity none Edit app manifests or Helm values medium no Limits are useful but can cause throttling; set them deliberately.
|
missing limits One or more containers do not declare CPU or memory limits. ./jeannie resource-budget --details;./jeannie capacity none Edit app manifests or Helm values medium no Limits are useful but can cause throttling; set them deliberately.
|
||||||
resource policy Capacity or resource policy needs attention. ./jeannie resource-budget;./jeannie capacity none Edit infra/resource-budgets.yml or app manifests medium no Do not hide pressure by only changing the budget file.
|
resource policy Capacity or resource policy needs attention. ./jeannie resource-budget;./jeannie capacity none Edit infra/resource-budgets.yml or app manifests medium no Do not hide pressure by only changing the budget file.
|
||||||
|
|
|
||||||
|
|
|
@ -105,8 +105,12 @@ def read_tsv(path, key):
|
||||||
with open(path, encoding="utf-8", newline="") as handle:
|
with open(path, encoding="utf-8", newline="") as handle:
|
||||||
return {row[key].lower(): row for row in csv.DictReader(handle, delimiter="\t")}
|
return {row[key].lower(): row for row in csv.DictReader(handle, delimiter="\t")}
|
||||||
|
|
||||||
|
def read_tsv_rows(path):
|
||||||
|
with open(path, encoding="utf-8", newline="") as handle:
|
||||||
|
return list(csv.DictReader(handle, delimiter="\t"))
|
||||||
|
|
||||||
scorecard_info = read_tsv(scorecard_map, "label")
|
scorecard_info = read_tsv(scorecard_map, "label")
|
||||||
finding_info = read_tsv(findings_map, "pattern")
|
finding_rows = read_tsv_rows(findings_map)
|
||||||
|
|
||||||
with open(input_file, encoding="utf-8") as handle:
|
with open(input_file, encoding="utf-8") as handle:
|
||||||
text = handle.read()
|
text = handle.read()
|
||||||
|
|
@ -116,30 +120,32 @@ lines = text.splitlines()
|
||||||
def split_commands(value):
|
def split_commands(value):
|
||||||
return [item.strip() for item in (value or "").split(";") if item.strip() and item.strip() != "none"]
|
return [item.strip() for item in (value or "").split(";") if item.strip() and item.strip() != "none"]
|
||||||
|
|
||||||
def print_info(info):
|
def print_info(info, indent=" "):
|
||||||
diagnose = split_commands(info.get("diagnose_commands"))
|
diagnose = split_commands(info.get("diagnose_commands"))
|
||||||
jeannie_fix = split_commands(info.get("jeannie_fix_commands"))
|
jeannie_fix = split_commands(info.get("jeannie_fix_commands"))
|
||||||
other_fix = split_commands(info.get("other_fix_commands"))
|
other_fix = split_commands(info.get("other_fix_commands"))
|
||||||
|
|
||||||
print(f" means: {info.get('meaning') or 'No explain entry exists yet.'}")
|
print(f"{indent}Why: {info.get('meaning') or 'No explain entry exists yet.'}")
|
||||||
if diagnose:
|
if diagnose:
|
||||||
print(" diagnose:")
|
print(f"{indent}Next command: {diagnose[0]}")
|
||||||
for command in diagnose:
|
if len(diagnose) > 1:
|
||||||
print(f" {command}")
|
print(f"{indent}More checks:")
|
||||||
|
for command in diagnose[1:]:
|
||||||
|
print(f"{indent} {command}")
|
||||||
if jeannie_fix:
|
if jeannie_fix:
|
||||||
print(" Jeannie fix command exists: yes")
|
print(f"{indent}Jeannie fix command:")
|
||||||
for command in jeannie_fix:
|
for command in jeannie_fix:
|
||||||
print(f" {command}")
|
print(f"{indent} {command}")
|
||||||
else:
|
else:
|
||||||
print(" Jeannie fix command exists: no")
|
print(f"{indent}Jeannie fix command: none yet")
|
||||||
if other_fix:
|
if other_fix:
|
||||||
print(" non-Jeannie fix:")
|
print(f"{indent}Code/config fix:")
|
||||||
for command in other_fix:
|
for command in other_fix:
|
||||||
print(f" {command}")
|
print(f"{indent} {command}")
|
||||||
print(f" risk: {info.get('risk') or 'unknown'}")
|
print(f"{indent}Risk: {info.get('risk') or 'unknown'}")
|
||||||
print(f" auto-fix safe: {info.get('auto_fix') or 'no'}")
|
print(f"{indent}Auto-fix safe: {info.get('auto_fix') or 'no'}")
|
||||||
if info.get("notes"):
|
if info.get("notes"):
|
||||||
print(f" note: {info['notes']}")
|
print(f"{indent}Note: {info['notes']}")
|
||||||
|
|
||||||
def parse_scorecard():
|
def parse_scorecard():
|
||||||
rows = []
|
rows = []
|
||||||
|
|
@ -191,11 +197,35 @@ def interesting_lines():
|
||||||
def match_findings(line):
|
def match_findings(line):
|
||||||
lowered = line.lower()
|
lowered = line.lower()
|
||||||
matches = []
|
matches = []
|
||||||
for pattern, info in finding_info.items():
|
for info in finding_rows:
|
||||||
|
pattern = (info.get("pattern") or "").lower()
|
||||||
if pattern and pattern in lowered:
|
if pattern and pattern in lowered:
|
||||||
matches.append((pattern, info))
|
matches.append((pattern, info))
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
def best_finding(line):
|
||||||
|
matches = match_findings(line)
|
||||||
|
if not matches:
|
||||||
|
return None
|
||||||
|
return sorted(matches, key=lambda item: (len(item[0]), item[0]), reverse=True)[0]
|
||||||
|
|
||||||
|
def parse_report_line(line):
|
||||||
|
report_match = re.match(r"^(?P<status>ok|warn|fail|skip)\s+(?P<check>.+?)(?:\s{2,}(?P<summary>.+))?$", line)
|
||||||
|
if report_match:
|
||||||
|
return (
|
||||||
|
report_match.group("check").strip(),
|
||||||
|
report_match.group("status").strip(),
|
||||||
|
(report_match.group("summary") or "").strip(),
|
||||||
|
)
|
||||||
|
scorecard_match = re.match(r"^(?P<label>.{1,28}) (?P<status>pass|warn|fail)(?: - (?P<summary>.*))?$", line)
|
||||||
|
if scorecard_match:
|
||||||
|
return (
|
||||||
|
scorecard_match.group("label").strip(),
|
||||||
|
scorecard_match.group("status").strip(),
|
||||||
|
(scorecard_match.group("summary") or "").strip(),
|
||||||
|
)
|
||||||
|
return (line[:72], "", "")
|
||||||
|
|
||||||
print(f"Jeannie Explain: {topic}")
|
print(f"Jeannie Explain: {topic}")
|
||||||
print("=" * (17 + len(topic)))
|
print("=" * (17 + len(topic)))
|
||||||
print("mode: read-only")
|
print("mode: read-only")
|
||||||
|
|
@ -204,13 +234,13 @@ if topic == "scorecard":
|
||||||
rows = parse_scorecard()
|
rows = parse_scorecard()
|
||||||
if not rows:
|
if not rows:
|
||||||
print("\nNo scorecard warnings or failures found.")
|
print("\nNo scorecard warnings or failures found.")
|
||||||
for label, status, summary in rows:
|
for index, (label, status, summary) in enumerate(rows, start=1):
|
||||||
info = scorecard_info.get(label.lower(), {})
|
info = scorecard_info.get(label.lower(), {})
|
||||||
print(f"\n{label}")
|
print(f"\n{index}. {label}")
|
||||||
print(f" status: {status}")
|
print(f" Status: {status}")
|
||||||
if summary:
|
if summary:
|
||||||
print(f" observed: {summary}")
|
print(f" Observed: {summary}")
|
||||||
print_info(info)
|
print_info(info, " ")
|
||||||
raise SystemExit(0)
|
raise SystemExit(0)
|
||||||
|
|
||||||
found_lines = interesting_lines()
|
found_lines = interesting_lines()
|
||||||
|
|
@ -218,17 +248,26 @@ seen_patterns = set()
|
||||||
matched_any = False
|
matched_any = False
|
||||||
|
|
||||||
for line in found_lines:
|
for line in found_lines:
|
||||||
matches = match_findings(line)
|
title, status, summary = parse_report_line(line)
|
||||||
if not matches:
|
match = best_finding(summary) if summary else None
|
||||||
|
if not match:
|
||||||
|
match = best_finding(line)
|
||||||
|
if not match:
|
||||||
continue
|
continue
|
||||||
print(f"\nFinding: {line}")
|
pattern, info = match
|
||||||
for pattern, info in matches:
|
|
||||||
if pattern in seen_patterns:
|
if pattern in seen_patterns:
|
||||||
continue
|
continue
|
||||||
seen_patterns.add(pattern)
|
seen_patterns.add(pattern)
|
||||||
matched_any = True
|
matched_any = True
|
||||||
print(f" matched: {pattern}")
|
print(f"\n{len(seen_patterns)}. {title}")
|
||||||
print_info(info)
|
if status:
|
||||||
|
print(f" Status: {status}")
|
||||||
|
if summary:
|
||||||
|
print(f" Observed: {summary}")
|
||||||
|
else:
|
||||||
|
print(f" Observed: {line}")
|
||||||
|
print(f" Matched rule: {pattern}")
|
||||||
|
print_info(info, " ")
|
||||||
|
|
||||||
if not matched_any:
|
if not matched_any:
|
||||||
if found_lines:
|
if found_lines:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue