Add docs and project files - force for Culurien
This commit is contained in:
599
docs/4_LOG/_originals_archive.backup/code_examples.md
Normal file
599
docs/4_LOG/_originals_archive.backup/code_examples.md
Normal file
@@ -0,0 +1,599 @@
|
||||
# RedFlag Agent - Code Implementation Examples
|
||||
|
||||
## 1. Main Loop (Entry Point)
|
||||
|
||||
**File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go`
|
||||
**Lines**: 410-549
|
||||
|
||||
The agent's main loop runs continuously, checking in with the server at regular intervals:
|
||||
|
||||
```go
|
||||
// Lines 410-549: Main check-in loop
|
||||
for {
|
||||
// Add jitter to prevent thundering herd
|
||||
jitter := time.Duration(rand.Intn(30)) * time.Second
|
||||
time.Sleep(jitter)
|
||||
|
||||
// Check if we need to send detailed system info update (hourly)
|
||||
if time.Since(lastSystemInfoUpdate) >= systemInfoUpdateInterval {
|
||||
log.Printf("Updating detailed system information...")
|
||||
if err := reportSystemInfo(apiClient, cfg); err != nil {
|
||||
log.Printf("Failed to report system info: %v\n", err)
|
||||
} else {
|
||||
lastSystemInfoUpdate = time.Now()
|
||||
log.Printf("✓ System information updated\n")
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Checking in with server... (Agent v%s)", AgentVersion)
|
||||
|
||||
// Collect lightweight system metrics (every check-in)
|
||||
sysMetrics, err := system.GetLightweightMetrics()
|
||||
var metrics *client.SystemMetrics
|
||||
if err == nil {
|
||||
metrics = &client.SystemMetrics{
|
||||
CPUPercent: sysMetrics.CPUPercent,
|
||||
MemoryPercent: sysMetrics.MemoryPercent,
|
||||
MemoryUsedGB: sysMetrics.MemoryUsedGB,
|
||||
MemoryTotalGB: sysMetrics.MemoryTotalGB,
|
||||
DiskUsedGB: sysMetrics.DiskUsedGB,
|
||||
DiskTotalGB: sysMetrics.DiskTotalGB,
|
||||
DiskPercent: sysMetrics.DiskPercent,
|
||||
Uptime: sysMetrics.Uptime,
|
||||
Version: AgentVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// Get commands from server
|
||||
commands, err := apiClient.GetCommands(cfg.AgentID, metrics)
|
||||
if err != nil {
|
||||
// Handle token renewal if needed
|
||||
// ... error handling code ...
|
||||
}
|
||||
|
||||
// Process each command
|
||||
for _, cmd := range commands {
|
||||
log.Printf("Processing command: %s (%s)\n", cmd.Type, cmd.ID)
|
||||
|
||||
switch cmd.Type {
|
||||
case "scan_updates":
|
||||
if err := handleScanUpdates(...); err != nil {
|
||||
log.Printf("Error scanning updates: %v\n", err)
|
||||
}
|
||||
case "install_updates":
|
||||
if err := handleInstallUpdates(...); err != nil {
|
||||
log.Printf("Error installing updates: %v\n", err)
|
||||
}
|
||||
// ... other command types ...
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for next check-in
|
||||
time.Sleep(time.Duration(getCurrentPollingInterval(cfg)) * time.Second)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. The Monolithic handleScanUpdates Function
|
||||
|
||||
**File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go`
|
||||
**Lines**: 551-709
|
||||
|
||||
This function orchestrates all scanner subsystems in a monolithic manner:
|
||||
|
||||
```go
|
||||
func handleScanUpdates(
|
||||
apiClient *client.Client, cfg *config.Config,
|
||||
aptScanner *scanner.APTScanner, dnfScanner *scanner.DNFScanner,
|
||||
dockerScanner *scanner.DockerScanner,
|
||||
windowsUpdateScanner *scanner.WindowsUpdateScanner,
|
||||
wingetScanner *scanner.WingetScanner,
|
||||
commandID string) error {
|
||||
|
||||
log.Println("Scanning for updates...")
|
||||
|
||||
var allUpdates []client.UpdateReportItem
|
||||
var scanErrors []string
|
||||
var scanResults []string
|
||||
|
||||
// MONOLITHIC PATTERN 1: APT Scanner (lines 559-574)
|
||||
if aptScanner.IsAvailable() {
|
||||
log.Println(" - Scanning APT packages...")
|
||||
updates, err := aptScanner.Scan()
|
||||
if err != nil {
|
||||
errorMsg := fmt.Sprintf("APT scan failed: %v", err)
|
||||
log.Printf(" %s\n", errorMsg)
|
||||
scanErrors = append(scanErrors, errorMsg)
|
||||
} else {
|
||||
resultMsg := fmt.Sprintf("Found %d APT updates", len(updates))
|
||||
log.Printf(" %s\n", resultMsg)
|
||||
scanResults = append(scanResults, resultMsg)
|
||||
allUpdates = append(allUpdates, updates...)
|
||||
}
|
||||
} else {
|
||||
scanResults = append(scanResults, "APT scanner not available")
|
||||
}
|
||||
|
||||
// MONOLITHIC PATTERN 2: DNF Scanner (lines 576-592)
|
||||
// [SAME PATTERN REPEATS - lines 576-592]
|
||||
if dnfScanner.IsAvailable() {
|
||||
log.Println(" - Scanning DNF packages...")
|
||||
updates, err := dnfScanner.Scan()
|
||||
if err != nil {
|
||||
errorMsg := fmt.Sprintf("DNF scan failed: %v", err)
|
||||
log.Printf(" %s\n", errorMsg)
|
||||
scanErrors = append(scanErrors, errorMsg)
|
||||
} else {
|
||||
resultMsg := fmt.Sprintf("Found %d DNF updates", len(updates))
|
||||
log.Printf(" %s\n", resultMsg)
|
||||
scanResults = append(scanResults, resultMsg)
|
||||
allUpdates = append(allUpdates, updates...)
|
||||
}
|
||||
} else {
|
||||
scanResults = append(scanResults, "DNF scanner not available")
|
||||
}
|
||||
|
||||
// MONOLITHIC PATTERN 3: Docker Scanner (lines 594-610)
|
||||
// [SAME PATTERN REPEATS - lines 594-610]
|
||||
if dockerScanner != nil && dockerScanner.IsAvailable() {
|
||||
log.Println(" - Scanning Docker images...")
|
||||
updates, err := dockerScanner.Scan()
|
||||
if err != nil {
|
||||
errorMsg := fmt.Sprintf("Docker scan failed: %v", err)
|
||||
log.Printf(" %s\n", errorMsg)
|
||||
scanErrors = append(scanErrors, errorMsg)
|
||||
} else {
|
||||
resultMsg := fmt.Sprintf("Found %d Docker image updates", len(updates))
|
||||
log.Printf(" %s\n", resultMsg)
|
||||
scanResults = append(scanResults, resultMsg)
|
||||
allUpdates = append(allUpdates, updates...)
|
||||
}
|
||||
} else {
|
||||
scanResults = append(scanResults, "Docker scanner not available")
|
||||
}
|
||||
|
||||
// MONOLITHIC PATTERN 4: Windows Update Scanner (lines 612-628)
|
||||
// [SAME PATTERN REPEATS - lines 612-628]
|
||||
if windowsUpdateScanner.IsAvailable() {
|
||||
log.Println(" - Scanning Windows updates...")
|
||||
updates, err := windowsUpdateScanner.Scan()
|
||||
if err != nil {
|
||||
errorMsg := fmt.Sprintf("Windows Update scan failed: %v", err)
|
||||
log.Printf(" %s\n", errorMsg)
|
||||
scanErrors = append(scanErrors, errorMsg)
|
||||
} else {
|
||||
resultMsg := fmt.Sprintf("Found %d Windows updates", len(updates))
|
||||
log.Printf(" %s\n", resultMsg)
|
||||
scanResults = append(scanResults, resultMsg)
|
||||
allUpdates = append(allUpdates, updates...)
|
||||
}
|
||||
} else {
|
||||
scanResults = append(scanResults, "Windows Update scanner not available")
|
||||
}
|
||||
|
||||
// MONOLITHIC PATTERN 5: Winget Scanner (lines 630-646)
|
||||
// [SAME PATTERN REPEATS - lines 630-646]
|
||||
if wingetScanner.IsAvailable() {
|
||||
log.Println(" - Scanning Winget packages...")
|
||||
updates, err := wingetScanner.Scan()
|
||||
if err != nil {
|
||||
errorMsg := fmt.Sprintf("Winget scan failed: %v", err)
|
||||
log.Printf(" %s\n", errorMsg)
|
||||
scanErrors = append(scanErrors, errorMsg)
|
||||
} else {
|
||||
resultMsg := fmt.Sprintf("Found %d Winget package updates", len(updates))
|
||||
log.Printf(" %s\n", resultMsg)
|
||||
scanResults = append(scanResults, resultMsg)
|
||||
allUpdates = append(allUpdates, updates...)
|
||||
}
|
||||
} else {
|
||||
scanResults = append(scanResults, "Winget scanner not available")
|
||||
}
|
||||
|
||||
// Report scan results (lines 648-677)
|
||||
success := len(allUpdates) > 0 || len(scanErrors) == 0
|
||||
var combinedOutput string
|
||||
|
||||
if len(scanResults) > 0 {
|
||||
combinedOutput += "Scan Results:\n" + strings.Join(scanResults, "\n")
|
||||
}
|
||||
if len(scanErrors) > 0 {
|
||||
if combinedOutput != "" {
|
||||
combinedOutput += "\n"
|
||||
}
|
||||
combinedOutput += "Scan Errors:\n" + strings.Join(scanErrors, "\n")
|
||||
}
|
||||
if len(allUpdates) > 0 {
|
||||
if combinedOutput != "" {
|
||||
combinedOutput += "\n"
|
||||
}
|
||||
combinedOutput += fmt.Sprintf("Total Updates Found: %d", len(allUpdates))
|
||||
}
|
||||
|
||||
// Create scan log entry
|
||||
logReport := client.LogReport{
|
||||
CommandID: commandID,
|
||||
Action: "scan_updates",
|
||||
Result: map[bool]string{true: "success", false: "failure"}[success],
|
||||
Stdout: combinedOutput,
|
||||
Stderr: strings.Join(scanErrors, "\n"),
|
||||
ExitCode: map[bool]int{true: 0, false: 1}[success],
|
||||
DurationSeconds: 0,
|
||||
}
|
||||
|
||||
// Report the scan log
|
||||
if err := apiClient.ReportLog(cfg.AgentID, logReport); err != nil {
|
||||
log.Printf("Failed to report scan log: %v\n", err)
|
||||
}
|
||||
|
||||
// Report updates (lines 686-708)
|
||||
if len(allUpdates) > 0 {
|
||||
report := client.UpdateReport{
|
||||
CommandID: commandID,
|
||||
Timestamp: time.Now(),
|
||||
Updates: allUpdates,
|
||||
}
|
||||
|
||||
if err := apiClient.ReportUpdates(cfg.AgentID, report); err != nil {
|
||||
return fmt.Errorf("failed to report updates: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("✓ Reported %d updates to server\n", len(allUpdates))
|
||||
} else {
|
||||
log.Println("✓ No updates found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
**Key Issues**:
|
||||
1. Pattern repeats 5 times verbatim (lines 559-646)
|
||||
2. No abstraction for common scanner pattern
|
||||
3. Sequential execution (each scanner waits for previous)
|
||||
4. Tight coupling between orchestrator and individual scanners
|
||||
5. All error handling mixed with business logic
|
||||
|
||||
---
|
||||
|
||||
## 3. Modular Scanner - APT Implementation
|
||||
|
||||
**File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/apt.go`
|
||||
**Lines**: 1-91
|
||||
|
||||
Individual scanners ARE modular:
|
||||
|
||||
```go
|
||||
package scanner
|
||||
|
||||
// APTScanner scans for APT package updates
|
||||
type APTScanner struct{}
|
||||
|
||||
// NewAPTScanner creates a new APT scanner
|
||||
func NewAPTScanner() *APTScanner {
|
||||
return &APTScanner{}
|
||||
}
|
||||
|
||||
// IsAvailable checks if APT is available on this system
|
||||
func (s *APTScanner) IsAvailable() bool {
|
||||
_, err := exec.LookPath("apt")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Scan scans for available APT updates
|
||||
func (s *APTScanner) Scan() ([]client.UpdateReportItem, error) {
|
||||
// Update package cache (sudo may be required, but try anyway)
|
||||
updateCmd := exec.Command("apt-get", "update")
|
||||
updateCmd.Run() // Ignore errors
|
||||
|
||||
// Get upgradable packages
|
||||
cmd := exec.Command("apt", "list", "--upgradable")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to run apt list: %w", err)
|
||||
}
|
||||
|
||||
return parseAPTOutput(output)
|
||||
}
|
||||
|
||||
func parseAPTOutput(output []byte) ([]client.UpdateReportItem, error) {
|
||||
var updates []client.UpdateReportItem
|
||||
scanner := bufio.NewScanner(bytes.NewReader(output))
|
||||
|
||||
// Regex to parse apt output
|
||||
re := regexp.MustCompile(`^([^\s/]+)/([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+\[upgradable from:\s+([^\]]+)\]`)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "Listing...") {
|
||||
continue
|
||||
}
|
||||
|
||||
matches := re.FindStringSubmatch(line)
|
||||
if len(matches) < 6 {
|
||||
continue
|
||||
}
|
||||
|
||||
packageName := matches[1]
|
||||
repository := matches[2]
|
||||
newVersion := matches[3]
|
||||
oldVersion := matches[5]
|
||||
|
||||
// Determine severity (simplified)
|
||||
severity := "moderate"
|
||||
if strings.Contains(repository, "security") {
|
||||
severity = "important"
|
||||
}
|
||||
|
||||
update := client.UpdateReportItem{
|
||||
PackageType: "apt",
|
||||
PackageName: packageName,
|
||||
CurrentVersion: oldVersion,
|
||||
AvailableVersion: newVersion,
|
||||
Severity: severity,
|
||||
RepositorySource: repository,
|
||||
Metadata: map[string]interface{}{
|
||||
"architecture": matches[4],
|
||||
},
|
||||
}
|
||||
|
||||
updates = append(updates, update)
|
||||
}
|
||||
|
||||
return updates, nil
|
||||
}
|
||||
```
|
||||
|
||||
**Good Aspects**:
|
||||
- Self-contained in single file
|
||||
- Clear interface (IsAvailable, Scan)
|
||||
- No dependencies on other scanners
|
||||
- Error handling encapsulated
|
||||
- Could be swapped out easily
|
||||
|
||||
---
|
||||
|
||||
## 4. Complex Scanner - Windows Update (WUA API)
|
||||
|
||||
**File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/scanner/windows_wua.go`
|
||||
**Lines**: 33-67, 70-211
|
||||
|
||||
```go
|
||||
// Scan scans for available Windows updates using WUA API
|
||||
func (s *WindowsUpdateScannerWUA) Scan() ([]client.UpdateReportItem, error) {
|
||||
if !s.IsAvailable() {
|
||||
return nil, fmt.Errorf("WUA scanner is only available on Windows")
|
||||
}
|
||||
|
||||
// Initialize COM
|
||||
comshim.Add(1)
|
||||
defer comshim.Done()
|
||||
|
||||
ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_SPEED_OVER_MEMORY)
|
||||
defer ole.CoUninitialize()
|
||||
|
||||
// Create update session
|
||||
session, err := windowsupdate.NewUpdateSession()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create Windows Update session: %w", err)
|
||||
}
|
||||
|
||||
// Create update searcher
|
||||
searcher, err := session.CreateUpdateSearcher()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create update searcher: %w", err)
|
||||
}
|
||||
|
||||
// Search for available updates (IsInstalled=0 means not installed)
|
||||
searchCriteria := "IsInstalled=0 AND IsHidden=0"
|
||||
result, err := searcher.Search(searchCriteria)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to search for updates: %w", err)
|
||||
}
|
||||
|
||||
// Convert results to our format
|
||||
updates := s.convertWUAResult(result)
|
||||
return updates, nil
|
||||
}
|
||||
|
||||
// Convert results - rich metadata extraction (lines 70-211)
|
||||
func (s *WindowsUpdateScannerWUA) convertWUAUpdate(update *windowsupdate.IUpdate) *client.UpdateReportItem {
|
||||
// Get update information
|
||||
title := update.Title
|
||||
description := update.Description
|
||||
kbArticles := s.getKBArticles(update)
|
||||
updateIdentity := update.Identity
|
||||
|
||||
// Use MSRC severity if available
|
||||
severity := s.mapMsrcSeverity(update.MsrcSeverity)
|
||||
if severity == "" {
|
||||
severity = s.determineSeverityFromCategories(update)
|
||||
}
|
||||
|
||||
// Create metadata with WUA-specific information
|
||||
metadata := map[string]interface{}{
|
||||
"package_manager": "windows_update",
|
||||
"detected_via": "wua_api",
|
||||
"kb_articles": kbArticles,
|
||||
"update_identity": updateIdentity.UpdateID,
|
||||
"revision_number": updateIdentity.RevisionNumber,
|
||||
"download_size": update.MaxDownloadSize,
|
||||
"api_source": "windows_update_agent",
|
||||
"scan_timestamp": time.Now().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
// Add MSRC severity if available
|
||||
if update.MsrcSeverity != "" {
|
||||
metadata["msrc_severity"] = update.MsrcSeverity
|
||||
}
|
||||
|
||||
// Add security bulletin IDs (includes CVEs)
|
||||
if len(update.SecurityBulletinIDs) > 0 {
|
||||
metadata["security_bulletins"] = update.SecurityBulletinIDs
|
||||
cveList := make([]string, 0)
|
||||
for _, bulletin := range update.SecurityBulletinIDs {
|
||||
if strings.HasPrefix(bulletin, "CVE-") {
|
||||
cveList = append(cveList, bulletin)
|
||||
}
|
||||
}
|
||||
if len(cveList) > 0 {
|
||||
metadata["cve_list"] = cveList
|
||||
}
|
||||
}
|
||||
|
||||
// ... more metadata extraction ...
|
||||
|
||||
updateItem := &client.UpdateReportItem{
|
||||
PackageType: "windows_update",
|
||||
PackageName: title,
|
||||
PackageDescription: description,
|
||||
CurrentVersion: currentVersion,
|
||||
AvailableVersion: availableVersion,
|
||||
Severity: severity,
|
||||
RepositorySource: "Microsoft Update",
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
return updateItem
|
||||
}
|
||||
```
|
||||
|
||||
**Key Characteristics**:
|
||||
- Complex internal logic but clean external interface
|
||||
- Rich metadata extraction (KB articles, CVEs, MSRC severity)
|
||||
- Windows-specific (COM interop)
|
||||
- Still follows IsAvailable/Scan pattern
|
||||
- Encapsulates complexity
|
||||
|
||||
---
|
||||
|
||||
## 5. System Info Reporting (Hourly)
|
||||
|
||||
**File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/cmd/agent/main.go`
|
||||
**Lines**: 1357-1407
|
||||
|
||||
```go
|
||||
// reportSystemInfo collects and reports detailed system information
|
||||
func reportSystemInfo(apiClient *client.Client, cfg *config.Config) error {
|
||||
// Collect detailed system information
|
||||
sysInfo, err := system.GetSystemInfo(AgentVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get system info: %w", err)
|
||||
}
|
||||
|
||||
// Create system info report
|
||||
report := client.SystemInfoReport{
|
||||
Timestamp: time.Now(),
|
||||
CPUModel: sysInfo.CPUInfo.ModelName,
|
||||
CPUCores: sysInfo.CPUInfo.Cores,
|
||||
CPUThreads: sysInfo.CPUInfo.Threads,
|
||||
MemoryTotal: sysInfo.MemoryInfo.Total,
|
||||
DiskTotal: uint64(0),
|
||||
DiskUsed: uint64(0),
|
||||
IPAddress: sysInfo.IPAddress,
|
||||
Processes: sysInfo.RunningProcesses,
|
||||
Uptime: sysInfo.Uptime,
|
||||
Metadata: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// Add primary disk info
|
||||
if len(sysInfo.DiskInfo) > 0 {
|
||||
primaryDisk := sysInfo.DiskInfo[0]
|
||||
report.DiskTotal = primaryDisk.Total
|
||||
report.DiskUsed = primaryDisk.Used
|
||||
report.Metadata["disk_mount"] = primaryDisk.Mountpoint
|
||||
report.Metadata["disk_filesystem"] = primaryDisk.Filesystem
|
||||
}
|
||||
|
||||
// Add metadata
|
||||
report.Metadata["collected_at"] = time.Now().Format(time.RFC3339)
|
||||
report.Metadata["hostname"] = sysInfo.Hostname
|
||||
report.Metadata["os_type"] = sysInfo.OSType
|
||||
report.Metadata["os_version"] = sysInfo.OSVersion
|
||||
report.Metadata["os_architecture"] = sysInfo.OSArchitecture
|
||||
|
||||
// Report to server
|
||||
if err := apiClient.ReportSystemInfo(cfg.AgentID, report); err != nil {
|
||||
return fmt.Errorf("failed to report system info: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
**Timing**:
|
||||
- Runs hourly (line 407-408: `const systemInfoUpdateInterval = 1 * time.Hour`)
|
||||
- Triggered in main loop (lines 417-425)
|
||||
- Separate from scan operations
|
||||
|
||||
---
|
||||
|
||||
## 6. System Info Data Structures
|
||||
|
||||
**File**: `/home/memory/Desktop/Projects/RedFlag/aggregator-agent/internal/system/info.go`
|
||||
**Lines**: 13-57
|
||||
|
||||
```go
|
||||
// SystemInfo contains detailed system information
|
||||
type SystemInfo struct {
|
||||
Hostname string `json:"hostname"`
|
||||
OSType string `json:"os_type"`
|
||||
OSVersion string `json:"os_version"`
|
||||
OSArchitecture string `json:"os_architecture"`
|
||||
AgentVersion string `json:"agent_version"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
CPUInfo CPUInfo `json:"cpu_info"`
|
||||
MemoryInfo MemoryInfo `json:"memory_info"`
|
||||
DiskInfo []DiskInfo `json:"disk_info"` // MULTIPLE DISKS!
|
||||
RunningProcesses int `json:"running_processes"`
|
||||
Uptime string `json:"uptime"`
|
||||
RebootRequired bool `json:"reboot_required"`
|
||||
RebootReason string `json:"reboot_reason"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
|
||||
// CPUInfo contains CPU information
|
||||
type CPUInfo struct {
|
||||
ModelName string `json:"model_name"`
|
||||
Cores int `json:"cores"`
|
||||
Threads int `json:"threads"`
|
||||
}
|
||||
|
||||
// MemoryInfo contains memory information
|
||||
type MemoryInfo struct {
|
||||
Total uint64 `json:"total"`
|
||||
Available uint64 `json:"available"`
|
||||
Used uint64 `json:"used"`
|
||||
UsedPercent float64 `json:"used_percent"`
|
||||
}
|
||||
|
||||
// DiskInfo contains disk information for modular storage management
|
||||
type DiskInfo struct {
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
Total uint64 `json:"total"`
|
||||
Available uint64 `json:"available"`
|
||||
Used uint64 `json:"used"`
|
||||
UsedPercent float64 `json:"used_percent"`
|
||||
Filesystem string `json:"filesystem"`
|
||||
IsRoot bool `json:"is_root"` // Primary system disk
|
||||
IsLargest bool `json:"is_largest"` // Largest storage disk
|
||||
DiskType string `json:"disk_type"` // SSD, HDD, NVMe, etc.
|
||||
Device string `json:"device"` // Block device name
|
||||
}
|
||||
```
|
||||
|
||||
**Important Notes**:
|
||||
- Supports multiple disks (DiskInfo is a slice)
|
||||
- Each disk tracked separately (mount point, filesystem type, device)
|
||||
- Reports primary (IsRoot) and largest (IsLargest) disk separately
|
||||
- Well-structured for expansion
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Monolithic**: The orchestration function (handleScanUpdates) that combines all scanners
|
||||
**Modular**: Individual scanner implementations and system info collection
|
||||
**Missing**: Formal subsystem abstraction layer and lifecycle management
|
||||
|
||||
Reference in New Issue
Block a user