| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- package kubecost
- import (
- "sync"
- "time"
- "golang.org/x/exp/slices"
- )
- // AuditType the types of Audits, each of which should be contained in an AuditSet
- type AuditType string
- const (
- AuditAllocationReconciliation AuditType = "AuditAllocationReconciliation"
- AuditAllocationTotalStore AuditType = "AuditAllocationTotalStore"
- AuditAllocationAggStore AuditType = "AuditAllocationAggStore"
- AuditAssetReconciliation AuditType = "AuditAssetReconciliation"
- AuditAssetTotalStore AuditType = "AuditAssetTotalStore"
- AuditAssetAggStore AuditType = "AuditAssetAggStore"
- AuditClusterEquality AuditType = "AuditClusterEquality"
- AuditAll AuditType = ""
- AuditInvalidType AuditType = "InvalidType"
- )
- // ToAuditType converts a string to an Audit type
- func ToAuditType(check string) AuditType {
- switch check {
- case string(AuditAllocationReconciliation):
- return AuditAllocationReconciliation
- case string(AuditAllocationTotalStore):
- return AuditAllocationTotalStore
- case string(AuditAllocationAggStore):
- return AuditAllocationAggStore
- case string(AuditAssetReconciliation):
- return AuditAssetReconciliation
- case string(AuditAssetTotalStore):
- return AuditAssetTotalStore
- case string(AuditAssetAggStore):
- return AuditAssetAggStore
- case string(AuditClusterEquality):
- return AuditClusterEquality
- case string(AuditAll):
- return AuditAll
- default:
- return AuditInvalidType
- }
- }
- // AuditStatus are possible outcomes of an audit
- type AuditStatus string
- const (
- FailedStatus AuditStatus = "Failed"
- WarningStatus = "Warning"
- PassedStatus = "Passed"
- )
- // AuditMissingValue records when a value that should be present in a store or in the audit generated results are missing
- type AuditMissingValue struct {
- Description string
- Key string
- }
- // AuditFloatResult structure for holding the results of a failed audit on a float value, Expected should be the value
- // calculated by the Audit func while Actual is what is contained in the relevant store.
- type AuditFloatResult struct {
- Expected float64
- Actual float64
- }
- // Clone returns a deep copy of the caller
- func (afr *AuditFloatResult) Clone() *AuditFloatResult {
- return &AuditFloatResult{
- Expected: afr.Expected,
- Actual: afr.Actual,
- }
- }
- // AllocationReconciliationAudit records the differences of between compute resources (cpu, ram, gpu) costs between
- // allocations by nodes and node assets keyed on node name and compute resource
- type AllocationReconciliationAudit struct {
- Status AuditStatus
- Description string
- LastRun time.Time
- Resources map[string]map[string]*AuditFloatResult
- MissingValues []*AuditMissingValue
- }
- // Clone returns a deep copy of the caller
- func (ara *AllocationReconciliationAudit) Clone() *AllocationReconciliationAudit {
- if ara == nil {
- return nil
- }
- resources := make(map[string]map[string]*AuditFloatResult, len(ara.Resources))
- for node, resourceMap := range ara.Resources {
- copyResourceMap := make(map[string]*AuditFloatResult, len(resourceMap))
- for resourceName, val := range resourceMap {
- copyResourceMap[resourceName] = val.Clone()
- }
- resources[node] = copyResourceMap
- }
- return &AllocationReconciliationAudit{
- Status: ara.Status,
- Description: ara.Description,
- LastRun: ara.LastRun,
- Resources: resources,
- MissingValues: slices.Clone(ara.MissingValues),
- }
- }
- // TotalAudit records the differences between a total store and the totaled results of the store that it is based on
- // keyed by cluster and node names
- type TotalAudit struct {
- Status AuditStatus
- Description string
- LastRun time.Time
- TotalByNode map[string]*AuditFloatResult
- TotalByCluster map[string]*AuditFloatResult
- MissingValues []*AuditMissingValue
- }
- // Clone returns a deep copy of the caller
- func (ta *TotalAudit) Clone() *TotalAudit {
- if ta == nil {
- return nil
- }
- tbn := make(map[string]*AuditFloatResult, len(ta.TotalByNode))
- for k, v := range ta.TotalByNode {
- tbn[k] = v
- }
- tbc := make(map[string]*AuditFloatResult, len(ta.TotalByNode))
- for k, v := range ta.TotalByCluster {
- tbc[k] = v
- }
- return &TotalAudit{
- Status: ta.Status,
- Description: ta.Description,
- LastRun: ta.LastRun,
- TotalByNode: tbn,
- TotalByCluster: tbc,
- MissingValues: slices.Clone(ta.MissingValues),
- }
- }
- // AggAudit contains the results of an Audit on an AggStore keyed on aggregation prop and Allocation key
- type AggAudit struct {
- Status AuditStatus
- Description string
- LastRun time.Time
- Results map[string]map[string]*AuditFloatResult
- MissingValues []*AuditMissingValue
- }
- // Clone returns a deep copy of the caller
- func (aa *AggAudit) Clone() *AggAudit {
- if aa == nil {
- return nil
- }
- res := make(map[string]map[string]*AuditFloatResult, len(aa.Results))
- for aggType, aggResults := range aa.Results {
- copyAggResult := make(map[string]*AuditFloatResult, len(aggResults))
- for aggName, auditFloatResult := range aggResults {
- copyAggResult[aggName] = auditFloatResult
- }
- res[aggType] = copyAggResult
- }
- return &AggAudit{
- Status: aa.Status,
- Description: aa.Description,
- LastRun: aa.LastRun,
- Results: res,
- MissingValues: slices.Clone(aa.MissingValues),
- }
- }
- // AssetReconciliationAudit records differences in assets and the Cloud
- type AssetReconciliationAudit struct {
- Status AuditStatus
- Description string
- LastRun time.Time
- Results map[string]map[string]*AuditFloatResult
- MissingValues []*AuditMissingValue
- }
- // Clone returns a deep copy of the caller
- func (ara *AssetReconciliationAudit) Clone() *AssetReconciliationAudit {
- res := make(map[string]map[string]*AuditFloatResult, len(ara.Results))
- for aggType, aggResults := range ara.Results {
- copyAggResult := make(map[string]*AuditFloatResult, len(aggResults))
- for aggName, auditFloatResult := range aggResults {
- copyAggResult[aggName] = auditFloatResult
- }
- res[aggType] = copyAggResult
- }
- return &AssetReconciliationAudit{
- Status: ara.Status,
- Description: ara.Description,
- LastRun: ara.LastRun,
- Results: res,
- MissingValues: slices.Clone(ara.MissingValues),
- }
- }
- // EqualityAudit records the difference in cost between Allocations and Assets aggregated by cluster and keyed on cluster
- type EqualityAudit struct {
- Status AuditStatus
- Description string
- LastRun time.Time
- Clusters map[string]*AuditFloatResult
- MissingValues []*AuditMissingValue
- }
- // Clone returns a deep copy of the caller
- func (ea *EqualityAudit) Clone() *EqualityAudit {
- if ea == nil {
- return nil
- }
- clusters := make(map[string]*AuditFloatResult, len(ea.Clusters))
- for k, v := range ea.Clusters {
- clusters[k] = v
- }
- return &EqualityAudit{
- Status: ea.Status,
- Description: ea.Description,
- LastRun: ea.LastRun,
- Clusters: clusters,
- MissingValues: slices.Clone(ea.MissingValues),
- }
- }
- // AuditCoverage tracks coverage of each audit type
- type AuditCoverage struct {
- AllocationReconciliation Window `json:"allocationReconciliation"`
- AllocationAgg Window `json:"allocationAgg"`
- AllocationTotal Window `json:"allocationTotal"`
- AssetTotal Window `json:"assetTotal"`
- AssetReconciliation Window `json:"assetReconciliation"`
- ClusterEquality Window `json:"clusterEquality"`
- }
- // NewAuditCoverage create default AuditCoverage
- func NewAuditCoverage() *AuditCoverage {
- return &AuditCoverage{}
- }
- // Update expands the coverage of each Window in the coverage that the given AuditSet's Window if the corresponding Audit is not nil
- // Note: This means of determining coverage can lead to holes in the given window
- func (ac *AuditCoverage) Update(as *AuditSet) {
- if as != nil && as.AllocationReconciliation != nil {
- ac.AllocationReconciliation.Expand(as.Window)
- ac.AllocationAgg.Expand(as.Window)
- ac.AllocationTotal.Expand(as.Window)
- ac.AssetTotal.Expand(as.Window)
- ac.AssetReconciliation.Expand(as.Window)
- ac.ClusterEquality.Expand(as.Window)
- }
- }
- // AuditSet is a ETLSet which contains all kind of Audits for a given Window
- type AuditSet struct {
- sync.RWMutex
- AllocationReconciliation *AllocationReconciliationAudit `json:"allocationReconciliation"`
- AllocationAgg *AggAudit `json:"allocationAgg"`
- AllocationTotal *TotalAudit `json:"allocationTotal"`
- AssetTotal *TotalAudit `json:"assetTotal"`
- AssetReconciliation *AssetReconciliationAudit `json:"assetReconciliation"`
- ClusterEquality *EqualityAudit `json:"clusterEquality"`
- Window Window `json:"window"`
- }
- // NewAuditSet creates an empty AuditSet with the given window
- func NewAuditSet(start, end time.Time) *AuditSet {
- return &AuditSet{
- Window: NewWindow(&start, &end),
- }
- }
- // UpdateAuditSet overwrites any audit fields in the caller with those in the given AuditSet which are not nil
- func (as *AuditSet) UpdateAuditSet(that *AuditSet) *AuditSet {
- if as == nil {
- return that
- }
- if that.AllocationReconciliation != nil {
- as.AllocationReconciliation = that.AllocationReconciliation
- }
- if that.AllocationAgg != nil {
- as.AllocationAgg = that.AllocationAgg
- }
- if that.AllocationTotal != nil {
- as.AllocationTotal = that.AllocationTotal
- }
- if that.AssetTotal != nil {
- as.AssetTotal = that.AssetTotal
- }
- if that.AssetReconciliation != nil {
- as.AssetReconciliation = that.AssetReconciliation
- }
- if that.ClusterEquality != nil {
- as.ClusterEquality = that.ClusterEquality
- }
- return as
- }
- // ConstructSet fulfills the ETLSet interface to provide an empty version of itself so that it can be initialized in its
- // generic form.
- func (as *AuditSet) ConstructSet() ETLSet {
- return &AuditSet{}
- }
- // IsEmpty returns true if any of the audits are non-nil
- func (as *AuditSet) IsEmpty() bool {
- return as == nil || (as.AllocationReconciliation == nil &&
- as.AllocationAgg == nil &&
- as.AllocationTotal == nil &&
- as.AssetTotal == nil &&
- as.AssetReconciliation == nil &&
- as.ClusterEquality == nil)
- }
- // GetWindow returns AuditSet Window
- func (as *AuditSet) GetWindow() Window {
- return as.Window
- }
- // Clone returns a deep copy of the caller
- func (as *AuditSet) Clone() *AuditSet {
- if as == nil {
- return nil
- }
- as.RLock()
- defer as.RUnlock()
- return &AuditSet{
- AllocationReconciliation: as.AllocationReconciliation.Clone(),
- AllocationAgg: as.AllocationAgg.Clone(),
- AllocationTotal: as.AllocationTotal.Clone(),
- AssetTotal: as.AssetTotal.Clone(),
- AssetReconciliation: as.AssetReconciliation.Clone(),
- ClusterEquality: as.ClusterEquality.Clone(),
- Window: as.Window.Clone(),
- }
- }
- // CloneSet returns a deep copy of the caller and returns set
- func (as *AuditSet) CloneSet() ETLSet {
- return as.Clone()
- }
- // AuditSetRange SetRange of AuditSets
- type AuditSetRange struct {
- SetRange[*AuditSet]
- }
|