Fix/exec textanalyze path clean (#1865)

* created roadmap and yaml claude agent

* Update roadmap.md

* Fix textAnalyze analyzer to auto-match exec collector nested paths

- Auto-detect exec output files (*-stdout.txt, *-stderr.txt, *-errors.json)
- Convert simple filenames to wildcard patterns automatically
- Preserve existing wildcard patterns
- Fixes 'No matching file' errors for exec + textAnalyze workflows

---------

Co-authored-by: Noah Campbell <noah.edward.campbell@gmail.com>
This commit is contained in:
Benjamin Yang
2025-09-29 19:49:54 -05:00
committed by GitHub
parent 266647ef2f
commit 17518349ff
2 changed files with 129 additions and 1 deletions

View File

@@ -37,9 +37,23 @@ func analyzeTextAnalyze(
analyzer *troubleshootv1beta2.TextAnalyze, getCollectedFileContents getChildCollectedFileContents, title string,
) ([]*AnalyzeResult, error) {
fullPath := filepath.Join(analyzer.CollectorName, analyzer.FileName)
// Auto-handle exec collector output files which are nested deeper than expected
// Exec collectors store files in: {collectorName}/{namespace}/{podName}/{fileName}
// But textAnalyze expects: {collectorName}/{fileName}
// If the fileName looks like exec output and doesn't already have wildcards, make it work automatically
if isLikelyExecOutput(analyzer.FileName) && !containsWildcards(analyzer.FileName) && !containsWildcards(fullPath) {
fullPath = filepath.Join(analyzer.CollectorName, "*", "*", analyzer.FileName)
}
excludeFiles := []string{}
for _, excludeFile := range analyzer.ExcludeFiles {
excludeFiles = append(excludeFiles, filepath.Join(analyzer.CollectorName, excludeFile))
excludePath := filepath.Join(analyzer.CollectorName, excludeFile)
// Apply same logic to exclude files
if isLikelyExecOutput(excludeFile) && !containsWildcards(excludeFile) && !containsWildcards(excludePath) {
excludePath = filepath.Join(analyzer.CollectorName, "*", "*", excludeFile)
}
excludeFiles = append(excludeFiles, excludePath)
}
collected, err := getCollectedFileContents(fullPath, excludeFiles)
@@ -108,6 +122,20 @@ func analyzeTextAnalyze(
}, nil
}
// isLikelyExecOutput checks if a filename looks like exec collector output
func isLikelyExecOutput(fileName string) bool {
return strings.HasSuffix(fileName, "-stdout.txt") ||
strings.HasSuffix(fileName, "-stderr.txt") ||
strings.HasSuffix(fileName, "-errors.json")
}
// containsWildcards checks if a path contains glob wildcards
func containsWildcards(path string) bool {
return strings.Contains(path, "*") ||
strings.Contains(path, "?") ||
strings.Contains(path, "[")
}
func analyzeRegexPattern(pattern string, collected []byte, outcomes []*troubleshootv1beta2.Outcome, checkName string) (*AnalyzeResult, error) {
re, err := regexp.Compile(pattern)
if err != nil {

View File

@@ -776,6 +776,106 @@ func Test_textAnalyze(t *testing.T) {
"text-collector-1/cfile-2.txt": []byte("Yes it all succeeded"),
},
},
{
name: "exec collector auto-path matching for stdout",
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "Command output found",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "Command output not found",
},
},
},
CollectorName: "netbox-branch-check",
FileName: "netbox-branch-check-stdout.txt", // Simple filename, but file is nested deeper
RegexPattern: "success",
},
expectResult: []AnalyzeResult{
{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "netbox-branch-check",
Message: "Command output found",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
// File is stored in exec-style nested path: {collector}/{namespace}/{pod}/{collector}-stdout.txt
"netbox-branch-check/netbox-enterprise/netbox-enterprise-858bcb8d4-cdgk7/netbox-branch-check-stdout.txt": []byte("operation success completed"),
},
},
{
name: "exec collector auto-path matching for stderr",
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "No errors in stderr",
When: "false",
},
},
{
Fail: &troubleshootv1beta2.SingleOutcome{
Message: "Error found in stderr",
When: "true",
},
},
},
CollectorName: "my-exec-collector",
FileName: "my-exec-collector-stderr.txt",
RegexPattern: "error",
},
expectResult: []AnalyzeResult{
{
IsPass: false,
IsWarn: false,
IsFail: true,
Title: "my-exec-collector",
Message: "Error found in stderr",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"my-exec-collector/default/my-pod-12345/my-exec-collector-stderr.txt": []byte("connection error occurred"),
},
},
{
name: "exec collector no auto-match when wildcards already present",
analyzer: troubleshootv1beta2.TextAnalyze{
Outcomes: []*troubleshootv1beta2.Outcome{
{
Pass: &troubleshootv1beta2.SingleOutcome{
Message: "Found with existing wildcard",
},
},
},
CollectorName: "test-collector",
FileName: "*/test-collector-stdout.txt", // Already has wildcard, should not be modified
RegexPattern: "output",
},
expectResult: []AnalyzeResult{
{
IsPass: true,
IsWarn: false,
IsFail: false,
Title: "test-collector",
Message: "Found with existing wildcard",
IconKey: "kubernetes_text_analyze",
IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg",
},
},
files: map[string][]byte{
"test-collector/something/test-collector-stdout.txt": []byte("some output here"),
},
},
}
for _, test := range tests {