Files
troubleshoot/pkg/analyze/host_kernel_configs.go
a8fb5210f4 Advanced analysis clean (#1868)
* created roadmap and yaml claude agent

* Update roadmap.md

* feat: Clean advanced analysis implementation - core agents, engine, artifacts

* Remove unrelated files - keep only advanced analysis implementation

* fix: Fix goroutine leak in hosted agent rate limiter

- Added stop channel and stopped flag to RateLimiter struct
- Modified replenishTokens to listen for stop signal and exit cleanly
- Added Stop() method to gracefully shutdown rate limiter
- Added Stop() method to HostedAgent to cleanup rate limiter on shutdown

Fixes cursor bot issue: Rate Limiter Goroutine Leak

* fix: Fix analyzer config and model validation bugs

Bug 1: Analyzer Config Missing File Path
- Added filePath to DeploymentStatus analyzer config in convertAnalyzerToSpec
- Sets namespace-specific path (cluster-resources/deployments/{namespace}.json)
- Falls back to generic path (cluster-resources/deployments.json) if no namespace
- Fixes LocalAgent.analyzeDeploymentStatus backward compatibility

Bug 2: HealthCheck Fails Model Validation
- Changed Ollama model validation from prefix match to exact match
- Prevents false positives where llama2:13b would match request for llama2:7b
- Ensures agent only reports healthy when exact model is available

Both fixes address cursor bot reported issues and maintain backward compatibility.

* fixing lint errors

* fixing lint errors

* adding CLI flags

* fix: resolve linting errors for CI

- Remove unnecessary nil check in host_kernel_configs.go (len() for nil slices is zero)
- Remove unnecessary fmt.Sprintf() calls in ceph.go for static strings
- Apply go fmt formatting fixes

Fixes failing lint CI check

* fix: resolve CI failures in build-test workflow and Ollama tests

1. Fix GitHub Actions workflow logic error:
   - Replace problematic contains() expression with explicit job result checks
   - Properly handle failure and cancelled states for each job
   - Prevents false positive failures in success summary job

2. Fix Ollama agent parseLLMResponse panics:
   - Add proper error handling for malformed JSON in LLM responses
   - Return error when JSON is found but invalid (instead of silent fallback)
   - Add error when no meaningful content can be parsed from response
   - Prevents nil pointer dereference in test assertions

Fixes failing build-test/success and build-test/test CI checks

* fix: resolve all CI failures and cursor bot issues

1. Fix disable-ollama flag logic bug:
   - Remove disable-ollama from advanced analysis trigger condition
   - Prevents unintended advanced analysis mode when no agents registered
   - Allows proper fallback to legacy analysis

2. Fix diff test consistency:
   - Update test expectations to match function behavior (lines with newlines)
   - Ensures consistency between streaming and non-streaming diff paths

3. Fix Ollama agent error handling:
   - Add proper error return for malformed JSON in LLM responses
   - Add meaningful content validation for markdown parsing
   - Prevents nil pointer panics in test assertions

4. Fix analysis engine mock agent:
   - Mock agent now processes and returns results for all provided analyzers
   - Fixes test expectation mismatch (expected 8 results, got 1)

Resolves all failing CI checks: lint, test, and success workflow logic

---------

Co-authored-by: Noah Campbell <noah.edward.campbell@gmail.com>
2025-09-30 11:44:46 -05:00

124 lines
3.5 KiB
Go

package analyzer
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
"k8s.io/klog/v2"
)
type AnalyzeHostKernelConfigs struct {
hostAnalyzer *troubleshootv1beta2.KernelConfigsAnalyze
}
var kConfigRegex = regexp.MustCompile("^(CONFIG_[A-Z0-9_]+)=([ymn]+)$")
func (a *AnalyzeHostKernelConfigs) Title() string {
return hostAnalyzerTitleOrDefault(a.hostAnalyzer.AnalyzeMeta, "Kernel Configs")
}
func (a *AnalyzeHostKernelConfigs) IsExcluded() (bool, error) {
return isExcluded(a.hostAnalyzer.Exclude)
}
func (a *AnalyzeHostKernelConfigs) Analyze(
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
) ([]*AnalyzeResult, error) {
collectedContents, err := retrieveCollectedContents(
getCollectedFileContents,
collect.HostKernelConfigsPath,
collect.NodeInfoBaseDir,
collect.HostKernelConfigsFileName,
)
if err != nil {
return []*AnalyzeResult{{Title: a.Title()}}, err
}
var results []*AnalyzeResult
for _, content := range collectedContents {
currentTitle := a.Title()
if content.NodeName != "" {
currentTitle = fmt.Sprintf("%s - Node %s", a.Title(), content.NodeName)
}
result, err := a.analyzeSingleNode(content, currentTitle)
if err != nil {
return nil, errors.Wrapf(err, "failed to analyze kernel configs for %s", currentTitle)
}
if result != nil {
results = append(results, result...)
}
}
return results, nil
}
func addMissingKernelConfigs(message string, missingConfigs []string) string {
if message == "" && len(missingConfigs) == 0 {
return message
}
return strings.ReplaceAll(message, "{{ .ConfigsNotFound }}", strings.Join(missingConfigs, ", "))
}
func (a *AnalyzeHostKernelConfigs) analyzeSingleNode(content collectedContent, currentTitle string) ([]*AnalyzeResult, error) {
hostAnalyzer := a.hostAnalyzer
kConfigs := collect.KConfigs{}
if err := json.Unmarshal(content.Data, &kConfigs); err != nil {
return nil, errors.Wrap(err, "failed to read kernel configs")
}
var configsNotFound []string
for _, config := range hostAnalyzer.SelectedConfigs {
matches := kConfigRegex.FindStringSubmatch(config)
// zero tolerance for invalid kernel config
if len(matches) < 3 {
return nil, errors.Errorf("invalid kernel config: %s", config)
}
key := matches[1]
values := matches[2] // values can contain multiple values in any order y, m, n
// check if the kernel config exists
if _, ok := kConfigs[key]; !ok {
configsNotFound = append(configsNotFound, config)
continue
}
// check if the kernel config value matches
if !strings.Contains(values, kConfigs[key]) {
klog.V(2).Infof("collected kernel config %s=%s does not in expected values %s", key, kConfigs[key], values)
configsNotFound = append(configsNotFound, config)
}
}
var results []*AnalyzeResult
for _, outcome := range hostAnalyzer.Outcomes {
result := &AnalyzeResult{
Title: currentTitle,
Strict: a.hostAnalyzer.Strict.BoolOrDefaultFalse(),
}
if outcome.Pass != nil && len(configsNotFound) == 0 {
result.IsPass = true
result.Message = outcome.Pass.Message
results = append(results, result)
break
}
if outcome.Fail != nil && len(configsNotFound) > 0 {
result.IsFail = true
result.Message = addMissingKernelConfigs(outcome.Fail.Message, configsNotFound)
results = append(results, result)
break
}
}
return results, nil
}