package facts import ( "metis/pkg/inventory" ) // ClassSummary captures aggregated sentinel facts per class. type ClassSummary struct { Class string `json:"class"` Nodes []string `json:"nodes"` Kernels map[string]int `json:"kernels,omitempty"` OSImages map[string]int `json:"os_images,omitempty"` Containerd map[string]int `json:"containerd,omitempty"` K3sVersions map[string]int `json:"k3s_versions,omitempty"` PackageStats map[string]map[string]int `json:"package_stats,omitempty"` // pkg -> version -> count } // Aggregate groups snapshots by inventory class and tallies version drift. func Aggregate(inv *inventory.Inventory, snaps []Snapshot) map[string]*ClassSummary { result := map[string]*ClassSummary{} for _, s := range snaps { class := "unknown" if inv != nil { if node, cls, err := inv.FindNode(s.Hostname); err == nil && cls != nil && node != nil { class = cls.Name } } sum, ok := result[class] if !ok { sum = &ClassSummary{ Class: class, Kernels: map[string]int{}, OSImages: map[string]int{}, Containerd: map[string]int{}, K3sVersions: map[string]int{}, PackageStats: map[string]map[string]int{}, } result[class] = sum } sum.Nodes = append(sum.Nodes, s.Hostname) if s.Kernel != "" { sum.Kernels[s.Kernel]++ } if s.OSImage != "" { sum.OSImages[s.OSImage]++ } if s.Containerd != "" { sum.Containerd[s.Containerd]++ } if s.K3sVersion != "" { sum.K3sVersions[s.K3sVersion]++ } for pkg, ver := range s.PackageSample { if sum.PackageStats[pkg] == nil { sum.PackageStats[pkg] = map[string]int{} } if ver != "" { sum.PackageStats[pkg][ver]++ } } } return result }