Documentation
¶
Overview ¶
Package engine provides the core cost calculation logic for FinFocus.
The engine orchestrates between plugins and local pricing specifications to calculate both projected and actual infrastructure costs.
Projected Cost Calculation ¶
For projected costs, the engine:
- Validates resource descriptors
- Queries registered plugins for cost estimates
- Falls back to local YAML specs when plugins don't provide pricing
- Aggregates results into a unified cost breakdown
Actual Cost Pipeline ¶
For actual costs, the engine supports:
- Time range queries with start/end dates
- Tag-based filtering using "tag:key=value" syntax
- Grouping by resource, type, provider, or date
- Cross-provider cost aggregation with currency validation
Output Formats ¶
Results can be rendered in three formats:
- table: Human-readable tabular output
- json: Structured JSON for programmatic use
- ndjson: Newline-delimited JSON for streaming
Timeouts ¶
Operations are protected by context timeouts:
- 60s overall query timeout
- 30s per-plugin call timeout
- 5s per-resource calculation timeout
Index ¶
- Constants
- Variables
- func AggregateHealth(budgets []*pbc.Budget) pbc.BudgetHealthStatus
- func AggregateHealthStatuses(statuses []pbc.BudgetHealthStatus) pbc.BudgetHealthStatus
- func ApplyChangesToRows(rows []OverviewRow, statusByURN map[string]ResourceStatus)
- func ApplyDefaultThresholds(budget *pbc.Budget) *pbc.Budget
- func ApplyDismissalDeltaToRow(row *OverviewRow, records map[string]*config.DismissalRecord)
- func ApplyProjectedPropertiesToRows(rows []OverviewRow, projectedPropsByURN map[string]map[string]interface{})
- func ApplyPropertyDiffsToRows(rows []OverviewRow, diffsByURN map[string][]PropertyDiff)
- func BuildProjectedPropertiesByURN(planSteps []PlanStep) map[string]map[string]interface{}
- func BuildPropertyDiffsByURN(planSteps []PlanStep) map[string][]PropertyDiff
- func BuildStatusByURN(planSteps []PlanStep) map[string]ResourceStatus
- func CalculateBudgetHealth(budget *pbc.Budget) pbc.BudgetHealthStatus
- func CalculateBudgetHealthFromPercentage(percentageUsed float64) pbc.BudgetHealthStatus
- func CalculateBudgetSummary(ctx context.Context, budgets []*pbc.Budget) *pbc.BudgetSummary
- func CalculateForecastedPercentage(forecastedSpend float64, budgetLimit float64) float64
- func CalculateForecastedSpend(currentSpend float64, periodStart time.Time, periodEnd time.Time) float64
- func CalculateForecastedSpendAt(currentSpend float64, periodStart time.Time, periodEnd time.Time, ...) float64
- func CalculateHealthFromPercentage(percentage float64) pbc.BudgetHealthStatus
- func CalculateOverallHealth(result *ScopedBudgetResult) pbc.BudgetHealthStatus
- func CalculateProjectedDelta(rows []OverviewRow, currentDayOfMonth int) (float64, string)
- func CalculateRowDelta(row OverviewRow, dayOfMonth int) (float64, bool)
- func ConfigBudgetToProto(status *BudgetStatus, name, id string) *pbc.Budget
- func ConvertToProto(properties map[string]interface{}) map[string]string
- func ConvertValueToString(v interface{}) string
- func CountRecommendations(results []CostResult) int
- func CountRecsActiveAndDismissed(recs []Recommendation) (int, int)
- func DefaultThresholds() []*pbc.BudgetThreshold
- func DetectPendingChanges(ctx context.Context, planSteps []PlanStep) (bool, int)
- func EnrichOverviewRow(ctx context.Context, row *OverviewRow, eng *Engine, dateRange DateRange)
- func ExtractCreatedTimestamp(resource ResourceDescriptor) (time.Time, error)
- func ExtractProvider(resourceType string) string
- func ExtractProviderFromResourceType(resourceType string) string
- func FilterBudgets(budgets []*pbc.Budget, filter *pbc.BudgetFilter) []*pbc.Budget
- func FilterBudgetsByProvider(ctx context.Context, budgets []*pbc.Budget, providers []string) []*pbc.Budget
- func FilterBudgetsByTags(ctx context.Context, budgets []*pbc.Budget, tags map[string]string) []*pbc.Budget
- func FindEarliestCreatedTimestamp(resources []ResourceDescriptor) (time.Time, error)
- func ForceExtrapolateActual(row OverviewRow, dayOfMonth int) float64
- func FormatOverviewCurrency(amount float64) string
- func FormatOverviewDelta(amount float64) string
- func FormatPeriod(from, to time.Time) string
- func FormatRecommendationCount(count int) string
- func GetBaselineProjectedMonthlyCost(row OverviewRow) (float64, bool)
- func GetExtrapolatedActual(row OverviewRow, dayOfMonth int) float64
- func GetProjectedMonthlyCost(row OverviewRow) float64
- func HealthStatusLabel(h pbc.BudgetHealthStatus) string
- func IdentifyCriticalScopes(result *ScopedBudgetResult) []string
- func IsExternalResource(resource ResourceDescriptor) bool
- func MatchesProvider(budget *pbc.Budget, providers []string) bool
- func MatchesTags(resource ResourceDescriptor, tags map[string]string) bool
- func PopulateComputedDeltas(rows []OverviewRow, dayOfMonth int)
- func RenderActualCostJSON(writer io.Writer, results []CostResult, showConfidence bool) error
- func RenderActualCostNDJSON(writer io.Writer, results []CostResult, showConfidence bool) error
- func RenderActualCostResults(writer io.Writer, format OutputFormat, results []CostResult, ...) error
- func RenderCrossProviderAggregation(writer io.Writer, format OutputFormat, aggregations []CrossProviderAggregation, ...) error
- func RenderOverviewAsJSON(ctx context.Context, w io.Writer, rows []OverviewRow, stackCtx StackContext, ...) error
- func RenderOverviewAsNDJSON(w io.Writer, rows []OverviewRow) error
- func RenderOverviewAsTable(w io.Writer, rows []OverviewRow, stackCtx StackContext) error
- func RenderResults(writer io.Writer, format OutputFormat, results []CostResult) error
- func RenderResultsWithContext(ctx context.Context, writer io.Writer, format OutputFormat, ...) error
- func StatusIcon(status ResourceStatus) string
- func UpdateBudgetForecast(ctx context.Context, budget *pbc.Budget, periodStart, periodEnd time.Time)
- func ValidateBudget(budget *pbc.Budget) error
- func ValidateBudgetCurrency(budget *pbc.Budget) error
- func ValidateCurrency(code string) error
- func ValidateFilter(filter string) error
- type ActualCostData
- type ActualCostRequest
- type AggregatedResults
- type BudgetAllocation
- type BudgetEngine
- type BudgetFilterOptions
- type BudgetHealthResult
- type BudgetHealthSummary
- type BudgetResult
- type BudgetStatus
- func (s *BudgetStatus) CappedPercentage() float64
- func (s *BudgetStatus) ExitReason() string
- func (s *BudgetStatus) GetExceededAlerts() []ThresholdStatus
- func (s *BudgetStatus) GetExitCode() int
- func (s *BudgetStatus) GetHighestExceededThreshold() float64
- func (s *BudgetStatus) HasApproachingAlerts() bool
- func (s *BudgetStatus) HasExceededAlerts() bool
- func (s *BudgetStatus) IsForecastOverBudget() bool
- func (s *BudgetStatus) IsOverBudget() bool
- func (s *BudgetStatus) ShouldExit() bool
- type Confidence
- type ContextKey
- type CostDelta
- type CostDriftData
- type CostResult
- type CostResultWithErrors
- type CostSummary
- type CrossProviderAggregation
- type DateRange
- type DefaultBudgetEngine
- type DismissRequest
- type DismissResult
- type Engine
- func (e *Engine) DismissRecommendation(ctx context.Context, store *config.DismissalStore, req DismissRequest) (*DismissResult, error)
- func (e *Engine) EstimateCost(ctx context.Context, request *EstimateRequest) (*EstimateResult, error)
- func (e *Engine) GetActualCost(ctx context.Context, resources []ResourceDescriptor, from, to time.Time) ([]CostResult, error)
- func (e *Engine) GetActualCostWithOptions(ctx context.Context, request ActualCostRequest) ([]CostResult, error)
- func (e *Engine) GetActualCostWithOptionsAndErrors(ctx context.Context, request ActualCostRequest) (*CostResultWithErrors, error)
- func (e *Engine) GetBudgets(ctx context.Context, filter *BudgetFilterOptions) (*BudgetResult, error)
- func (e *Engine) GetProjectedCost(ctx context.Context, resources []ResourceDescriptor) ([]CostResult, error)
- func (e *Engine) GetProjectedCostWithErrors(ctx context.Context, resources []ResourceDescriptor) (*CostResultWithErrors, error)
- func (e *Engine) GetRecommendationHistory(_ context.Context, store *config.DismissalStore, recommendationID string) ([]config.LifecycleEvent, error)
- func (e *Engine) GetRecommendationsForResources(ctx context.Context, resources []ResourceDescriptor) (*RecommendationsResult, error)
- func (e *Engine) GroupResults(results []CostResult, groupBy GroupBy) []CostResult
- func (e *Engine) UndismissRecommendation(ctx context.Context, store *config.DismissalStore, recommendationID string) (*UndismissResult, error)
- func (e *Engine) WithCache(cacheStore cache.Cache) *Engine
- func (e *Engine) WithDismissalStore(store *config.DismissalStore) *Engine
- func (e *Engine) WithJobs(jobs int) *Engine
- func (e *Engine) WithRouter(router Router) *Engine
- type ErrorDetail
- type ErrorType
- type EstimateRequest
- type EstimateResult
- type ExtendedBudgetSummary
- type GroupBy
- type OutputFormat
- type OverviewJSONOutput
- type OverviewMetadata
- type OverviewRow
- func EnrichOverviewRows(ctx context.Context, rows []OverviewRow, eng *Engine, dateRange DateRange, ...) []OverviewRow
- func MergeResourcesForOverview(ctx context.Context, stateResources []StateResource, planSteps []PlanStep) ([]OverviewRow, error)
- func NewRowsFromState(ctx context.Context, stateResources []StateResource) []OverviewRow
- type OverviewRowError
- type OverviewRowUpdate
- type OverviewSummary
- type PlanStep
- type PluginMatch
- type PricingSpec
- type ProjectedCostData
- type ProjectedCostRequest
- type PropertyDiff
- type Recommendation
- type RecommendationError
- type RecommendationStatus
- type RecommendationsResult
- type ResourceDescriptor
- type ResourceStatus
- type Router
- type ScopeType
- type ScopedBudgetEvaluator
- func (e *ScopedBudgetEvaluator) AllocateCostToProvider(ctx context.Context, resourceType string, cost float64) *BudgetAllocation
- func (e *ScopedBudgetEvaluator) AllocateCostToTag(ctx context.Context, resourceType string, tags map[string]string, cost float64) *BudgetAllocation
- func (e *ScopedBudgetEvaluator) AllocateCostToType(ctx context.Context, resourceType string, cost float64) *BudgetAllocation
- func (e *ScopedBudgetEvaluator) AllocateCosts(ctx context.Context, resourceType string, tags map[string]string, cost float64) *BudgetAllocation
- func (e *ScopedBudgetEvaluator) GetProviderBudget(provider string) *config.ScopedBudget
- func (e *ScopedBudgetEvaluator) GetTypeBudget(resourceType string) *config.ScopedBudget
- func (e *ScopedBudgetEvaluator) MatchTagBudgets(_ context.Context, tags map[string]string) []config.TagBudget
- func (e *ScopedBudgetEvaluator) SelectHighestPriorityTagBudget(ctx context.Context, matches []config.TagBudget) (*config.TagBudget, []string)
- type ScopedBudgetResult
- type ScopedBudgetStatus
- func CalculateProviderBudgetStatus(provider string, budget *config.ScopedBudget, currentSpend float64) *ScopedBudgetStatus
- func CalculateTagBudgetStatus(tagBudget *config.TagBudget, currentSpend float64) *ScopedBudgetStatus
- func CalculateTypeBudgetStatus(resourceType string, budget *config.ScopedBudget, currentSpend float64) *ScopedBudgetStatus
- type SpecLoader
- type StackContext
- type StateCostInput
- type StateCostResult
- type StateResource
- type StructuredError
- type SustainabilityMetric
- type ThresholdEvaluationResult
- type ThresholdStatus
- type ThresholdStatusValue
- type UndismissResult
Constants ¶
const ( // HealthThresholdWarning is the utilization percentage at which a budget transitions to WARNING. // Budgets at or above 80% utilization but below 90% are considered WARNING. HealthThresholdWarning = 80.0 // HealthThresholdCritical is the utilization percentage at which a budget transitions to CRITICAL. // Budgets at or above 90% utilization but below 100% are considered CRITICAL. HealthThresholdCritical = 90.0 // HealthThresholdExceeded is the utilization percentage at which a budget transitions to EXCEEDED. // Budgets at or above 100% utilization are considered EXCEEDED. HealthThresholdExceeded = 100.0 )
Health threshold constants define the boundaries for budget health status. These thresholds match the proto enum semantics for BudgetHealthStatus.
const ( DefaultThreshold50 = 50.0 DefaultThreshold80 = 80.0 DefaultThreshold100 = 100.0 )
Default threshold percentages.
const ( // PropertyPulumiCreated is the property key for resource creation timestamp. PropertyPulumiCreated = "pulumi:created" // PropertyPulumiModified is the property key for resource modification timestamp. PropertyPulumiModified = "pulumi:modified" // PropertyPulumiExternal indicates the resource was imported (not created by Pulumi). PropertyPulumiExternal = "pulumi:external" )
Property keys for Pulumi metadata in ResourceDescriptor.Properties.
const ( ErrCodePluginError = "PLUGIN_ERROR" ErrCodeValidationError = "VALIDATION_ERROR" ErrCodeTimeoutError = "TIMEOUT_ERROR" ErrCodeNoCostData = "NO_COST_DATA" )
Error code constants for StructuredError. These are stable, additive-only identifiers per FR-005 — existing codes will never be removed or renamed.
const ApproachingThresholdBuffer = 5.0
ApproachingThresholdBuffer is the percentage buffer for "approaching" status. If current spend is within this percentage of a threshold, it's "approaching".
const CurrencyCodeLength = 3
CurrencyCodeLength is the required length for valid ISO 4217 currency codes.
const ( // ExitCodeBudgetEvaluationError is returned when budget evaluation fails (FR-009). // This is distinct from the configured exit code for threshold violations. ExitCodeBudgetEvaluationError = 1 )
Exit code constants for CLI integration.
const HoursPerMonth = 730
HoursPerMonth is the canonical number of hours in a standard business month used for all monthly cost projections.
const PercentageMultiplier = 100.0
PercentageMultiplier is used to convert ratios to percentages.
const UptimeAssumptionNote = "Note: Estimate assumes 100% uptime. Stopped/restarted resources are not tracked."
UptimeAssumptionNote is the standard note documenting the 100% uptime assumption. Per spec T023a, this should be included in all state-based cost estimates.
Variables ¶
var ( // ErrCurrencyMismatch is returned when spend currency doesn't match budget currency. ErrCurrencyMismatch = errors.New("currency mismatch between budget and actual spend") // ErrInvalidSpend is returned when the spend value is invalid. ErrInvalidSpend = errors.New("spend value is invalid") // ErrBudgetDisabled is returned when trying to evaluate a disabled budget. ErrBudgetDisabled = errors.New("budget is disabled (amount is 0)") )
Budget evaluation error types.
var ( // ErrNoCostData is returned when no cost data is available for a resource. ErrNoCostData = errors.New("no cost data available") // ErrMixedCurrencies is returned when cross-provider aggregation encounters multiple currencies. ErrMixedCurrencies = errors.New("mixed currencies not supported in cross-provider aggregation") // ErrInvalidGroupBy is returned when a non-time-based grouping is used for cross-provider aggregation. ErrInvalidGroupBy = errors.New("invalid groupBy type for cross-provider aggregation") // ErrEmptyResults is returned when aggregation is attempted on empty results. ErrEmptyResults = errors.New("empty results provided for aggregation") // ErrInvalidDateRange is returned when the end date is before the start date. ErrInvalidDateRange = errors.New("invalid date range: end date must be after start date") )
var ErrInvalidBudget = errors.New("invalid budget")
ErrInvalidBudget is returned when a budget fails validation.
var ErrInvalidCurrency = errors.New("invalid currency code")
ErrInvalidCurrency is returned when a currency code fails validation.
var ( // ErrNoTimestampedResources is returned when no resources have timestamps. ErrNoTimestampedResources = errors.New("no resources have created timestamps") )
Errors for state cost calculation.
var ErrOverviewValidation = errors.New("overview validation failed")
ErrOverviewValidation is returned when overview type validation fails.
var ErrResourceValidation = errors.New("resource validation failed")
ErrResourceValidation is returned when resource validation fails.
Functions ¶
func AggregateHealth ¶ added in v0.2.5
func AggregateHealth(budgets []*pbc.Budget) pbc.BudgetHealthStatus
AggregateHealth returns the worst-case health status across all budgets. If budgets is empty, returns UNSPECIFIED.
The aggregation uses "worst wins" logic:
- EXCEEDED > CRITICAL > WARNING > OK > UNSPECIFIED
func AggregateHealthStatuses ¶ added in v0.2.6
func AggregateHealthStatuses(statuses []pbc.BudgetHealthStatus) pbc.BudgetHealthStatus
AggregateHealthStatuses returns the worst-case health status from a list of statuses. If statuses is empty, returns UNSPECIFIED.
func ApplyChangesToRows ¶ added in v0.3.2
func ApplyChangesToRows(rows []OverviewRow, statusByURN map[string]ResourceStatus)
ApplyChangesToRows updates the Status field of existing OverviewRows in-place using a map of URN → ResourceStatus derived from plan steps. Rows whose URN is not in statusByURN retain their current Status.
This is used for Phase 2 when preview completes after initial TUI display.
Thread safety: The Bubble Tea Update() method is single-threaded by design, so this function is safe to call from OverviewChangesReadyMsg handling in the TUI. Callers in other contexts must ensure no concurrent reads on rows during this call.
No-op if rows is nil.
func ApplyDefaultThresholds ¶ added in v0.2.5
ApplyDefaultThresholds adds default thresholds to a budget if none exist.
This function MUTATES the input budget in place by setting budget.Thresholds to DefaultThresholds() when the budget has no thresholds defined. The same pointer is returned for convenience (allows chaining).
Returns nil if budget is nil. Returns the same budget pointer (unchanged) if thresholds already exist. Returns the same budget pointer (modified) if defaults were applied.
func ApplyDismissalDeltaToRow ¶ added in v0.3.2
func ApplyDismissalDeltaToRow(row *OverviewRow, records map[string]*config.DismissalRecord)
ApplyDismissalDeltaToRow appends dismissed and snoozed recommendations to the row's Recommendations slice by looking up each non-active DismissalRecord whose LastKnown.ResourceID matches the row's ResourceID (or URN as fallback).
This enables the count badge to show the total including dismissed recs, e.g. "3(-1)" when 3 total and 1 is dismissed, without listing the dismissed ones in full in the detail view. It is a no-op when records is empty or nil.
func ApplyProjectedPropertiesToRows ¶ added in v0.3.3
func ApplyProjectedPropertiesToRows(rows []OverviewRow, projectedPropsByURN map[string]map[string]interface{})
ApplyProjectedPropertiesToRows updates the ProjectedProperties field of existing OverviewRows in-place using a map of URN → projected properties derived from plan steps. Rows whose URN is not in projectedPropsByURN retain their current ProjectedProperties.
No-op if rows is nil.
func ApplyPropertyDiffsToRows ¶ added in v0.3.3
func ApplyPropertyDiffsToRows(rows []OverviewRow, diffsByURN map[string][]PropertyDiff)
ApplyPropertyDiffsToRows updates the PropertyDiffs field of existing OverviewRows in-place using a map of URN → []PropertyDiff derived from plan steps. Rows whose URN is not in diffsByURN retain their current PropertyDiffs.
This is used alongside ApplyChangesToRows for Phase 2 when preview completes after initial TUI display.
No-op if rows is nil.
func BuildProjectedPropertiesByURN ¶ added in v0.3.3
BuildProjectedPropertiesByURN converts a slice of PlanSteps to a map of URN → projected properties. For replace flows where multiple operations may appear for the same URN, the first non-empty projected properties are kept.
func BuildPropertyDiffsByURN ¶ added in v0.3.3
func BuildPropertyDiffsByURN(planSteps []PlanStep) map[string][]PropertyDiff
BuildPropertyDiffsByURN converts a slice of PlanSteps to a map of URN → []PropertyDiff. Unlike BuildStatusByURN, this uses diff-specific precedence: for a replace flow (create-replacement + delete-replaced), the create-replacement step carries the PropertyDiffs while delete-replaced has none. Using the status precedence (which favors delete-replaced) would silently drop diffs. Instead, we keep the first non-empty PropertyDiffs found for each URN.
func BuildStatusByURN ¶ added in v0.3.2
func BuildStatusByURN(planSteps []PlanStep) map[string]ResourceStatus
BuildStatusByURN converts a slice of PlanSteps to a map of URN → ResourceStatus using the same precedence rules as MergeResourcesForOverview. When the same URN appears with multiple operations (e.g., create-replacement + delete-replaced for a replace), the highest-precedence operation wins.
func CalculateBudgetHealth ¶ added in v0.2.5
func CalculateBudgetHealth(budget *pbc.Budget) pbc.BudgetHealthStatus
CalculateBudgetHealth determines the health status of a single budget based on its current utilization percentage.
Thresholds:
- OK: 0-79%
- WARNING: 80-89%
- CRITICAL: 90-99%
- EXCEEDED: 100%+
Returns UNSPECIFIED if budget has no status or invalid data.
func CalculateBudgetHealthFromPercentage ¶ added in v0.2.5
func CalculateBudgetHealthFromPercentage(percentageUsed float64) pbc.BudgetHealthStatus
CalculateBudgetHealthFromPercentage calculates health status from a raw utilization percentage.
Thresholds:
- OK: 0-79%
- WARNING: 80-89%
- CRITICAL: 90-99%
- EXCEEDED: 100%+
Negative percentages are treated as 0% (OK status).
func CalculateBudgetSummary ¶ added in v0.2.3
CalculateBudgetSummary computes a BudgetSummary for the given budgets. It sets TotalBudgets to the number of provided budgets and increments the appropriate health counters (BudgetsOk, BudgetsWarning, BudgetsCritical, BudgetsExceeded) based on each budget's status.Health. Budgets with a nil status or a health of UNSPECIFIED are logged and excluded from the health counts. It returns a pointer to the populated BudgetSummary.
func CalculateForecastedPercentage ¶ added in v0.2.5
CalculateForecastedPercentage calculates forecasted utilization percentage. Returns 0 if budgetLimit <= 0.
func CalculateForecastedSpend ¶ added in v0.2.5
func CalculateForecastedSpend(currentSpend float64, periodStart time.Time, periodEnd time.Time) float64
CalculateForecastedSpend predicts end-of-period spending using linear extrapolation. Uses current time for calculation.
func CalculateForecastedSpendAt ¶ added in v0.2.5
func CalculateForecastedSpendAt( currentSpend float64, periodStart time.Time, periodEnd time.Time, now time.Time, ) float64
CalculateForecastedSpendAt predicts end-of-period spending relative to a specific time. Exposed for testing.
Formula: forecastedSpend = (currentSpend / elapsedDuration) * totalDuration
Edge cases:
- Period not started (now < periodStart): returns currentSpend
- No elapsed time: returns currentSpend (avoids division by zero)
- Zero current spend: returns 0
func CalculateHealthFromPercentage ¶ added in v0.2.6
func CalculateHealthFromPercentage(percentage float64) pbc.BudgetHealthStatus
CalculateHealthFromPercentage calculates health status from a raw utilization percentage. This is a convenience wrapper around CalculateBudgetHealthFromPercentage for scoped budgets.
func CalculateOverallHealth ¶ added in v0.2.6
func CalculateOverallHealth(result *ScopedBudgetResult) pbc.BudgetHealthStatus
CalculateOverallHealth calculates the worst-case health status across all scopes. Uses "worst wins" aggregation: EXCEEDED > CRITICAL > WARNING > OK > UNSPECIFIED.
func CalculateProjectedDelta ¶ added in v0.3.0
func CalculateProjectedDelta(rows []OverviewRow, currentDayOfMonth int) (float64, string)
CalculateProjectedDelta computes the aggregate change-impact delta for a set of overview rows by examining pending changes.
For each row with a pending operation:
- Updating/Replacing: delta += projected(after) - projected(current) when baseline projected data is available; otherwise falls back to projected(after) - extrapolated_actual.
- Creating: delta += projected
- Deleting: delta -= extrapolated_actual
The currency is taken from the first non-empty cost currency in rows that contributed a delta. Mixed currencies cannot reach this function because upstream returns ErrMixedCurrencies before aggregation.
To match per-row delta behavior, extrapolation always uses ForceExtrapolateActual, including day 1-2.
func CalculateRowDelta ¶ added in v0.3.3
func CalculateRowDelta(row OverviewRow, dayOfMonth int) (float64, bool)
CalculateRowDelta computes a per-row cost delta for display purposes.
For resources with pending changes (updating, replacing, creating, deleting), the delta represents the cost impact of the change. For updating/replacing rows, this prefers projected(after) - projected(current) when baseline projected data is available; otherwise it falls back to projected(after) - extrapolated actual.
For active resources, CostDrift.Delta is used if available.
Returns (delta, true) when a meaningful delta can be computed, or (0, false) when no delta is available (e.g., active resource without drift data).
func ConfigBudgetToProto ¶ added in v0.3.2
func ConfigBudgetToProto(status *BudgetStatus, name, id string) *pbc.Budget
ConfigBudgetToProto converts a config-evaluated BudgetStatus into a pbc.Budget proto suitable for TUI rendering. This bridges the config-based budget evaluation (DefaultBudgetEngine.Evaluate) with the plugin-based proto representation used by the TUI budget footer.
func ConvertToProto ¶ added in v0.2.5
ConvertToProto converts a map[string]interface{} to map[string]string for gRPC. It handles nested structures from protobuf Struct conversions by extracting meaningful values from common patterns.
func ConvertValueToString ¶ added in v0.2.5
func ConvertValueToString(v interface{}) string
ConvertValueToString converts an interface{} value to a string representation. It handles nested maps and slices that may come from protobuf Struct conversions.
func CountRecommendations ¶ added in v0.3.0
func CountRecommendations(results []CostResult) int
CountRecommendations returns the total number of recommendations across all results.
func CountRecsActiveAndDismissed ¶ added in v0.3.2
func CountRecsActiveAndDismissed(recs []Recommendation) (int, int)
CountRecsActiveAndDismissed returns the count of active and dismissed/snoozed recommendations in a slice. Active means Status is empty or "Active"; dismissed means Status is "Dismissed" or "Snoozed".
func DefaultThresholds ¶ added in v0.2.5
func DefaultThresholds() []*pbc.BudgetThreshold
DefaultThresholds returns the standard thresholds to apply when none configured. Returns: 50% ACTUAL, 80% ACTUAL, 100% ACTUAL.
func DetectPendingChanges ¶ added in v0.3.0
DetectPendingChanges inspects a set of plan steps and reports whether any mutating operations are pending, along with the count.
func EnrichOverviewRow ¶ added in v0.3.0
func EnrichOverviewRow(ctx context.Context, row *OverviewRow, eng *Engine, dateRange DateRange)
EnrichOverviewRow enriches a single OverviewRow by fetching actual costs, projected costs, and recommendations from the engine concurrently (up to 4 goroutines per call for updating/replacing rows). When used with EnrichOverviewRows' worker pool, the maximum concurrent goroutines is approximately overviewConcurrencyLimit * 4. Partial failures from actual/projected cost are captured in row.Error with actual cost errors taking precedence; recommendation failures are logged but do not set row.Error.
func ExtractCreatedTimestamp ¶
func ExtractCreatedTimestamp(resource ResourceDescriptor) (time.Time, error)
ExtractCreatedTimestamp extracts the pulumi:created timestamp from resource properties. Returns zero time if the property is missing. Returns an error if the timestamp is present but cannot be parsed.
func ExtractProvider ¶ added in v0.2.6
ExtractProvider extracts the provider name from a resource type string. Examples:
- "aws:ec2/instance" -> "aws"
- "gcp:compute/instance" -> "gcp"
- "azure:compute/virtualMachine" -> "azure"
- "unknown" -> "unknown"
- ":ec2/instance" -> "" (colon at start, no provider)
func ExtractProviderFromResourceType ¶ added in v0.3.0
ExtractProviderFromResourceType returns the provider name extracted from resourceType, or an empty string if no provider can be determined.
func FilterBudgets ¶ added in v0.2.3
FilterBudgets returns the subset of budgets that satisfy the given BudgetFilter. If filter is nil, the original budgets slice is returned unchanged.
func FilterBudgetsByProvider ¶ added in v0.2.5
func FilterBudgetsByProvider(ctx context.Context, budgets []*pbc.Budget, providers []string) []*pbc.Budget
FilterBudgetsByProvider filters budgets by provider name(s).
Behavior:
- Case-insensitive matching ("aws" matches "AWS", "Aws", etc.)
- OR logic: budget matches if it matches ANY provider in the list
- Empty providers list returns all budgets (no filtering)
- Returns empty slice if no budgets match
func FilterBudgetsByTags ¶ added in v0.3.0
func FilterBudgetsByTags(ctx context.Context, budgets []*pbc.Budget, tags map[string]string) []*pbc.Budget
FilterBudgetsByTags filters budgets by metadata tags.
Behavior:
- Case-sensitive matching for both keys and values
- AND logic: budget must match ALL specified tags
- Supports glob patterns in values (e.g., "prod-*" matches "prod-us")
- Empty tags map returns all budgets (no filtering)
- Returns empty slice if no budgets match
func FindEarliestCreatedTimestamp ¶
func FindEarliestCreatedTimestamp(resources []ResourceDescriptor) (time.Time, error)
FindEarliestCreatedTimestamp finds the earliest Created timestamp from a set of resources. This is used to auto-detect the --from date when using --pulumi-state. Returns ErrNoTimestampedResources if no resources have timestamps.
func ForceExtrapolateActual ¶ added in v0.3.3
func ForceExtrapolateActual(row OverviewRow, dayOfMonth int) float64
ForceExtrapolateActual extrapolates the MTD actual cost to a full month, even on early-month days (day 1-2). Used for resources with PropertyDiffs where the config change is a known signal and an approximate delta is more useful than suppressing it entirely.
func FormatOverviewCurrency ¶ added in v0.3.0
FormatOverviewCurrency formats an amount as "$X,XXX.XX". Negative values are formatted as "-$X,XXX.XX".
func FormatOverviewDelta ¶ added in v0.3.0
FormatOverviewDelta formats a delta amount with a +/- prefix. Positive values get "+$", negative get "-$", zero gets "$0.00".
func FormatPeriod ¶
FormatPeriod formats a time duration into a human-readable period string.
func FormatRecommendationCount ¶ added in v0.3.0
FormatRecommendationCount returns the recommendation count as a string for table display. FormatRecommendationCount returns "-" when count is zero; otherwise it returns the decimal string representation of count.
func GetBaselineProjectedMonthlyCost ¶ added in v0.3.3
func GetBaselineProjectedMonthlyCost(row OverviewRow) (float64, bool)
GetBaselineProjectedMonthlyCost extracts current-state projected monthly cost.
func GetExtrapolatedActual ¶ added in v0.3.3
func GetExtrapolatedActual(row OverviewRow, dayOfMonth int) float64
GetExtrapolatedActual extrapolates the MTD actual cost to a full month. If the day of month is too early for reliable extrapolation, returns the raw MTD cost.
Note: This function uses defaultDaysPerMonth (30) for extrapolation, which differs from CalculateCostDrift that uses the actual daysInMonth (28-31). This is intentional: delta calculations use a standardised month length for consistent comparisons, while drift calculations use calendar-accurate month length for precision.
func GetProjectedMonthlyCost ¶ added in v0.3.3
func GetProjectedMonthlyCost(row OverviewRow) float64
GetProjectedMonthlyCost safely extracts the projected monthly cost from a row.
func HealthStatusLabel ¶ added in v0.3.2
func HealthStatusLabel(h pbc.BudgetHealthStatus) string
HealthStatusLabel converts a BudgetHealthStatus enum to its short label. Labels produced are "OK", "WARNING", "CRITICAL", "EXCEEDED", and "UNKNOWN" for unspecified or unrecognized values.
func IdentifyCriticalScopes ¶ added in v0.2.6
func IdentifyCriticalScopes(result *ScopedBudgetResult) []string
IdentifyCriticalScopes returns the scope identifiers that have CRITICAL or EXCEEDED status. Results are sorted for deterministic output.
func IsExternalResource ¶
func IsExternalResource(resource ResourceDescriptor) bool
IsExternalResource checks if a resource was imported (not created by Pulumi). Returns true if the pulumi:external property is set to "true" or true.
func MatchesProvider ¶ added in v0.2.5
MatchesProvider checks if a budget matches any of the given providers. Returns true if providers is empty (no filtering). Returns false if budget is nil.
func MatchesTags ¶
func MatchesTags(resource ResourceDescriptor, tags map[string]string) bool
MatchesTags checks if resource properties match the specified tag filters.
func PopulateComputedDeltas ¶ added in v0.3.3
func PopulateComputedDeltas(rows []OverviewRow, dayOfMonth int)
PopulateComputedDeltas computes and stores per-row delta values. Must be called after enrichment, before any rendering. All renderers (table, JSON, NDJSON, TUI) then read row.ComputedDelta instead of independently recomputing deltas — guaranteeing consistency.
func RenderActualCostJSON ¶
func RenderActualCostJSON(writer io.Writer, results []CostResult, showConfidence bool) error
RenderActualCostJSON renders actual cost results as a JSON array with optional confidence field.
func RenderActualCostNDJSON ¶
func RenderActualCostNDJSON(writer io.Writer, results []CostResult, showConfidence bool) error
RenderActualCostNDJSON renders actual cost results as NDJSON with optional confidence field.
func RenderActualCostResults ¶
func RenderActualCostResults(writer io.Writer, format OutputFormat, results []CostResult, showConfidence bool) error
RenderActualCostResults renders actual cost results using the specified output format. It dispatches to the table, pretty JSON, or NDJSON renderer based on the provided format. Supported formats are OutputTable, OutputJSON, and OutputNDJSON; an error is returned for unsupported formats. Parameters:
- format: the desired OutputFormat (e.g., OutputTable, OutputJSON, OutputNDJSON).
- results: the slice of CostResult values to render.
RenderActualCostResults dispatches actual cost results to the renderer that corresponds to the provided output format.
The format parameter selects the output format and must be one of OutputTable, OutputJSON, or OutputNDJSON. The results parameter is the slice of actual cost results to be rendered.
It returns an error if the selected renderer fails or if the format is unsupported.
func RenderCrossProviderAggregation ¶
func RenderCrossProviderAggregation( writer io.Writer, format OutputFormat, aggregations []CrossProviderAggregation, groupBy GroupBy, ) error
RenderCrossProviderAggregation renders cross-provider aggregation data using the specified output format. It supports table, JSON, and NDJSON formats and dispatches to the corresponding renderer.
Parameters:
- format: desired output format (e.g., OutputTable, OutputJSON, OutputNDJSON).
- aggregations: slice of cross-provider aggregation records to render.
- groupBy: grouping granularity used for table output (e.g., daily or monthly).
RenderCrossProviderAggregation dispatches cross-provider aggregation data to the renderer corresponding to the given OutputFormat.
The `format` parameter selects the output renderer: table, JSON, or NDJSON. `aggregations` is the slice of cross-provider aggregation records to render. `groupBy` controls the temporal grouping used by table renderers (for example, daily vs monthly).
It returns an error if the specified format is not supported or if the selected renderer fails.
func RenderOverviewAsJSON ¶ added in v0.3.0
func RenderOverviewAsJSON( ctx context.Context, w io.Writer, rows []OverviewRow, stackCtx StackContext, budgetResult *BudgetResult, ) error
RenderOverviewAsJSON renders the overview rows as a structured JSON object with metadata, resource array, summary, optional budgets, and errors.
Parameters:
- ctx: context for logging and tracing; threaded to CalculateBudgetHealthResults.
- w: destination writer for the JSON output.
- rows: slice of OverviewRow to include as resources; may be nil (will serialize as an empty array).
- stackCtx: stack context and metadata used in the output; GeneratedAt will be populated with the current time if zero.
- budgetResult: optional budget data; when non-nil and non-empty, converted budgets are included in the `budgets` field. May be nil, in which case no budget entries are emitted.
Returns an error if aggregating totals fails or if encoding/writing the JSON output fails.
func RenderOverviewAsNDJSON ¶ added in v0.3.0
func RenderOverviewAsNDJSON(w io.Writer, rows []OverviewRow) error
RenderOverviewAsNDJSON renders each overview row as a separate JSON line with no metadata wrapper or summary. ComputedDelta must be populated before calling this function (via PopulateComputedDeltas).
func RenderOverviewAsTable ¶ added in v0.3.0
func RenderOverviewAsTable(w io.Writer, rows []OverviewRow, stackCtx StackContext) error
RenderOverviewAsTable writes a formatted ASCII table of the overview rows.
func RenderResults ¶
func RenderResults(writer io.Writer, format OutputFormat, results []CostResult) error
RenderResults renders the given cost results using the specified output format. RenderResults aggregates the results for table and JSON summary outputs, emits NDJSON as individual records, and writes the output to the specified writer. The writer parameter specifies where the output should be written. The format parameter selects one of the supported OutputFormat values (OutputTable, OutputJSON, OutputNDJSON). The results parameter is the slice of CostResult to be rendered. It returns an error if rendering fails or if the provided format is unsupported.
func RenderResultsWithContext ¶ added in v0.2.6
func RenderResultsWithContext(ctx context.Context, writer io.Writer, format OutputFormat, results []CostResult) error
RenderResultsWithContext renders the given cost results using the specified output format with context. The ctx parameter enables trace ID propagation for debug logging.
func StatusIcon ¶ added in v0.3.0
func StatusIcon(status ResourceStatus) string
StatusIcon returns a single-character icon for a ResourceStatus.
func UpdateBudgetForecast ¶ added in v0.2.5
func UpdateBudgetForecast(ctx context.Context, budget *pbc.Budget, periodStart, periodEnd time.Time)
UpdateBudgetForecast updates a budget's status with calculated forecast. Modifies budget.Status in place. Creates Status if nil.
func ValidateBudget ¶ added in v0.2.5
ValidateBudget validates required budget fields.
func ValidateBudgetCurrency ¶ added in v0.2.5
ValidateBudgetCurrency validates the currency in a budget's amount. Returns nil if valid or amount is nil, error otherwise.
func ValidateCurrency ¶ added in v0.2.5
ValidateCurrency checks if a currency code is valid ISO 4217 format.
Valid format: exactly 3 uppercase letters (A-Z). Examples: "USD", "EUR", "GBP" are valid; "usd", "US", "USDD" are invalid.
Returns nil if valid, error with descriptive message if invalid.
func ValidateFilter ¶
ValidateFilter validates the syntax of a filter expression. It returns an error if the filter is malformed: missing '=' separator, or if either the key or value is empty (for example: `type=aws:ec2/instance` or `tag:env=prod`).
Types ¶
type ActualCostData ¶ added in v0.3.0
type ActualCostData struct {
MTDCost float64 `json:"mtdCost"`
Currency string `json:"currency"`
Period DateRange `json:"period"`
Breakdown map[string]float64 `json:"breakdown,omitempty"`
}
ActualCostData holds month-to-date actual cost information for a resource.
func (*ActualCostData) Validate ¶ added in v0.3.0
func (a *ActualCostData) Validate() error
Validate checks that the ActualCostData fields are consistent.
type ActualCostRequest ¶
type ActualCostRequest struct {
Resources []ResourceDescriptor
From time.Time
To time.Time
Adapter string
GroupBy string
Tags map[string]string
EstimateConfidence bool // Show confidence level in output
FallbackEstimate bool // When true, include $0 placeholder results for resources with no plugin data
}
ActualCostRequest contains parameters for querying historical actual costs with filtering and grouping.
type AggregatedResults ¶
type AggregatedResults struct {
Summary CostSummary `json:"summary"`
Resources []CostResult `json:"resources"`
}
AggregatedResults contains cost results with summary and aggregation data.
func AggregateResults ¶
func AggregateResults(results []CostResult) *AggregatedResults
AggregateResults aggregates a slice of CostResult into an AggregatedResults summary.
AggregateResults computes total monthly and hourly costs, and builds maps of monthly costs grouped by provider, service, and adapter. The returned AggregatedResults contains the original Resources slice and a CostSummary populated with:
- Currency: taken from the first result (or defaultCurrency when input is empty).
- TotalMonthly and TotalHourly: summed across all results.
- ByProvider, ByService, ByAdapter: maps of monthly totals keyed by provider, service, and adapter respectively.
If the input slice is empty, AggregateResults returns an AggregatedResults with an AggregateResults aggregates a slice of CostResult into an AggregatedResults summary.
If results is empty, it returns an AggregatedResults with zero totals, empty maps, an empty Resources slice, and Currency set to defaultCurrency. For a non-empty input, totals (TotalMonthly, TotalHourly) are summed across results, ByProvider, ByService, and ByAdapter maps accumulate monthly totals, Currency is taken from the first result, and Resources contains the original input slice.
type BudgetAllocation ¶ added in v0.2.6
type BudgetAllocation struct {
// ResourceID is the unique identifier of the resource.
ResourceID string `json:"resource_id,omitempty"`
// ResourceType is the full type string (e.g., "aws:ec2/instance").
ResourceType string `json:"resource_type"`
// Provider is the extracted provider from the resource type.
Provider string `json:"provider,omitempty"`
// Cost is the resource's cost that was allocated.
Cost float64 `json:"cost"`
// AllocatedScopes lists all scopes that received this resource's cost.
// Format: "global", "provider:aws", "tag:team:platform", "type:aws:ec2/instance"
AllocatedScopes []string `json:"allocated_scopes,omitempty"`
// MatchedTags lists all tags that matched tag budgets for this resource.
// If multiple matched, only the highest priority receives cost.
MatchedTags []string `json:"matched_tags,omitempty"`
// SelectedTagBudget is the tag budget that received the cost allocation.
// Empty if no tag budget matched or no tag budgets configured.
SelectedTagBudget string `json:"selected_tag_budget,omitempty"`
// Warnings contains any warnings generated during allocation.
// e.g., "overlapping tag budgets without priority"
Warnings []string `json:"warnings,omitempty"`
}
BudgetAllocation tracks cost allocation for a single resource.
type BudgetEngine ¶ added in v0.2.4
type BudgetEngine interface {
// Evaluate compares current spend against the configured budget and alerts.
// It returns a BudgetStatus or an error if evaluation fails.
Evaluate(budget config.BudgetConfig, currentSpend float64, currency string) (*BudgetStatus, error)
}
BudgetEngine defines the interface for budget evaluation operations.
type BudgetFilterOptions ¶ added in v0.2.5
type BudgetFilterOptions struct {
Providers []string // Filter by provider names (case-insensitive, OR logic)
Tags map[string]string // Filter by metadata tags (case-sensitive, AND logic, supports glob patterns)
}
BudgetFilterOptions contains criteria for filtering budgets.
type BudgetHealthResult ¶ added in v0.2.5
type BudgetHealthResult struct {
BudgetID string `json:"budgetID"`
BudgetName string `json:"budgetName"`
Provider string `json:"provider"`
Health pbc.BudgetHealthStatus `json:"-"` // serialized via custom MarshalJSON
Utilization float64 `json:"utilization"`
Forecasted float64 `json:"forecasted"`
Currency string `json:"currency"`
Limit float64 `json:"limit"`
CurrentSpend float64 `json:"currentSpend"`
}
BudgetHealthResult contains health assessment for a single budget.
func CalculateBudgetHealthResults ¶ added in v0.2.5
func CalculateBudgetHealthResults(ctx context.Context, budgets []*pbc.Budget) []BudgetHealthResult
CalculateBudgetHealthResults calculates health results for multiple budgets. It returns a BudgetHealthResult for each budget with all relevant health information.
func (BudgetHealthResult) MarshalJSON ¶ added in v0.3.2
func (b BudgetHealthResult) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler, converting Health to a string label.
func (*BudgetHealthResult) UnmarshalJSON ¶ added in v0.3.2
func (b *BudgetHealthResult) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler, parsing Health from a string label. Returns an error for unrecognized non-empty health labels to prevent silent data loss.
type BudgetHealthSummary ¶ added in v0.3.2
type BudgetHealthSummary struct {
OverallHealth string `json:"overallHealth,omitempty"`
TotalBudgets int `json:"totalBudgets"`
CriticalCount int `json:"criticalCount"`
}
BudgetHealthSummary provides a lightweight budget health summary for StackContext JSON serialization. It captures the overall health status, total number of budgets evaluated, and count of critical/exceeded budgets.
type BudgetResult ¶ added in v0.2.5
type BudgetResult struct {
Budgets []*pbc.Budget // Filtered budgets with health status
Summary *ExtendedBudgetSummary // Aggregated statistics
Errors []error // Any errors during processing
}
BudgetResult contains the complete budget health response.
func BuildConfigBudgetResult ¶ added in v0.3.2
func BuildConfigBudgetResult( ctx context.Context, budgetsCfg *config.BudgetsConfig, totalProjectedCost float64, ) *BudgetResult
BuildConfigBudgetResult evaluates the config-based global budget against a total projected cost and returns a BudgetResult suitable for TUI rendering. Returns nil when no config budget is enabled.
type BudgetStatus ¶ added in v0.2.4
type BudgetStatus struct {
// Budget is the original budget configuration.
Budget config.BudgetConfig
// CurrentSpend is the actual spend amount.
CurrentSpend float64
// Percentage is the percentage of budget consumed (0-100+).
Percentage float64
// ForecastedSpend is the estimated total spend by end of period (0 if forecasting not applicable).
ForecastedSpend float64
// ForecastPercentage is the percentage of budget forecasted to be consumed.
ForecastPercentage float64
// Alerts contains the status of each configured alert threshold.
Alerts []ThresholdStatus
// Currency is the currency of the spend (validated to match budget).
Currency string
}
BudgetStatus represents the result of evaluating a budget against current spend.
func (*BudgetStatus) CappedPercentage ¶ added in v0.2.4
func (s *BudgetStatus) CappedPercentage() float64
CappedPercentage returns the percentage capped at 100 for display purposes. The actual Percentage field may exceed 100 for over-budget scenarios.
func (*BudgetStatus) ExitReason ¶ added in v0.2.5
func (s *BudgetStatus) ExitReason() string
ExitReason returns a human-readable reason for the exit code. Returns an empty string if no exit should occur. A zero exit code represents a warning-only condition. Used for debug logging and user feedback.
func (*BudgetStatus) GetExceededAlerts ¶ added in v0.2.4
func (s *BudgetStatus) GetExceededAlerts() []ThresholdStatus
GetExceededAlerts returns all alerts with EXCEEDED status.
func (*BudgetStatus) GetExitCode ¶ added in v0.2.5
func (s *BudgetStatus) GetExitCode() int
GetExitCode returns the appropriate exit code based on budget evaluation. Returns 0 if no exit should occur (ShouldExit() is false). Returns the configured exit code if a threshold is exceeded and ExitOnThreshold is enabled. Note: A zero exit code representing a warning-only condition is explicitly allowed.
func (*BudgetStatus) GetHighestExceededThreshold ¶ added in v0.2.4
func (s *BudgetStatus) GetHighestExceededThreshold() float64
GetHighestExceededThreshold returns the highest threshold that has been exceeded. Returns 0 if no thresholds are exceeded.
func (*BudgetStatus) HasApproachingAlerts ¶ added in v0.2.4
func (s *BudgetStatus) HasApproachingAlerts() bool
HasApproachingAlerts returns true if any alert has APPROACHING status.
func (*BudgetStatus) HasExceededAlerts ¶ added in v0.2.4
func (s *BudgetStatus) HasExceededAlerts() bool
HasExceededAlerts returns true if any alert has EXCEEDED status.
func (*BudgetStatus) IsForecastOverBudget ¶ added in v0.2.4
func (s *BudgetStatus) IsForecastOverBudget() bool
IsForecastOverBudget returns true if forecasted spend exceeds the budget amount.
func (*BudgetStatus) IsOverBudget ¶ added in v0.2.4
func (s *BudgetStatus) IsOverBudget() bool
IsOverBudget returns true if current spend exceeds the budget amount.
func (*BudgetStatus) ShouldExit ¶ added in v0.2.5
func (s *BudgetStatus) ShouldExit() bool
ShouldExit returns true if the CLI should "exit" due to an exceeded budget threshold. It returns true when ExitOnThreshold is enabled in the budget config and any threshold has been exceeded, regardless of whether the configured exit code is zero (warning-only).
type Confidence ¶
type Confidence string
Confidence represents the reliability level of a cost estimate.
Confidence levels help users understand how reliable a cost estimate is:
- HIGH: Backed by real billing data from cloud provider APIs
- MEDIUM: Calculated from runtime using Pulumi state timestamps
- LOW: Estimated for imported resources where creation time is unknown
const ( // ConfidenceHigh indicates the cost is backed by real billing data. // This is the most reliable estimate, coming from actual cloud billing APIs. ConfidenceHigh Confidence = "high" // ConfidenceMedium indicates the cost is calculated from runtime. // The estimate uses hourly_rate × runtime from Pulumi state timestamps. // Reliable for resources created by Pulumi. ConfidenceMedium Confidence = "medium" // ConfidenceLow indicates reduced reliability for imported resources. // The "Created" timestamp reflects import time, not actual resource creation, // so runtime calculations may significantly underestimate actual usage. ConfidenceLow Confidence = "low" // ConfidenceUnknown indicates confidence could not be determined. // Used when no cost data is available or for error cases. ConfidenceUnknown Confidence = "" )
Confidence level constants.
func DetermineConfidence ¶
func DetermineConfidence(hasBillingData, isExternal bool) Confidence
DetermineConfidence calculates the appropriate confidence level based on data source.
The logic is:
- HIGH: hasBillingData=true (real billing API data overrides all other factors)
- MEDIUM: hasBillingData=false, isExternal=false (runtime estimate, non-imported)
- LOW: hasBillingData=false, isExternal=true (runtime estimate, imported resource)
Parameters:
- hasBillingData: true if cost came from actual billing APIs (e.g., AWS Cost Explorer)
- isExternal: true if the resource was imported (pulumi:external=true)
func DetermineConfidenceFromResult ¶
func DetermineConfidenceFromResult(result CostResult, isExternal bool) Confidence
DetermineConfidenceFromResult calculates confidence level from a CostResult.
Uses TotalCost > 0 as indicator of billing data (from GetActualCost API calls). The isExternal parameter indicates if the resource was imported.
Parameters:
- result: CostResult containing cost data
- isExternal: true if the resource was imported (pulumi:external=true)
func (Confidence) DisplayLabel ¶
func (c Confidence) DisplayLabel() string
DisplayLabel returns an uppercase label for UI display. Returns "-" for unknown confidence to indicate missing data.
func (Confidence) IsValid ¶
func (c Confidence) IsValid() bool
IsValid returns true if the Confidence value is valid.
func (Confidence) String ¶
func (c Confidence) String() string
String returns the string representation of the Confidence level.
type ContextKey ¶
type ContextKey string
ContextKey is a custom type for context keys to avoid collisions.
const ( // ContextKeyUtilization is the context key for passing utilization rate. ContextKeyUtilization ContextKey = "utilization" )
type CostDelta ¶ added in v0.3.0
type CostDelta struct {
// Property is the name of the property that was changed
Property string `json:"property"`
// OriginalValue is the value before the change
OriginalValue string `json:"originalValue"`
// NewValue is the value after the change
NewValue string `json:"newValue"`
// CostChange is the monthly cost difference
// Positive = increase, negative = savings
CostChange float64 `json:"costChange"`
}
CostDelta represents the cost impact of changing a single property.
Usage Examples:
delta := CostDelta{
Property: "instanceType",
OriginalValue: "t3.micro",
NewValue: "m5.large",
CostChange: 65.70, // Positive means cost increase
}
type CostDriftData ¶ added in v0.3.0
type CostDriftData struct {
ExtrapolatedMonthly float64 `json:"extrapolatedMonthly"`
Projected float64 `json:"projected"`
Delta float64 `json:"delta"`
PercentDrift float64 `json:"percentDrift"`
IsWarning bool `json:"isWarning"`
}
CostDriftData captures the difference between extrapolated actual spend and the projected monthly cost. It is only populated when the absolute drift exceeds the warning threshold (10%).
func CalculateCostDrift ¶ added in v0.3.0
func CalculateCostDrift(actualMTD, projected float64, dayOfMonth, daysInMonth int) (*CostDriftData, error)
CalculateCostDrift computes the cost drift between an extrapolated month-to-date actual spend and the projected monthly cost normalized to the current calendar month.
Parameters:
- actualMTD: month-to-date actual cost for the resource.
- projected: projected monthly cost expressed on the package's canonical month basis.
- dayOfMonth: current day of the month (1-31); used to extrapolate actualMTD to a full month.
- daysInMonth: total days in the current calendar month (28-31); used to normalize the projection.
Return values:
- (*CostDriftData, nil) when drift exceeds the warning threshold (10%).
- (nil, nil) when drift is not meaningful: both sides zero, one side zero (new/deleted resource), or drift within the warning threshold.
- (nil, error) with ErrOverviewValidation when inputs are invalid: dayOfMonth < driftMinDay, daysInMonth outside 28..31, or dayOfMonth > daysInMonth.
func CalculateCostDriftWithElapsedDays ¶ added in v0.3.3
func CalculateCostDriftWithElapsedDays( actualMTD, projected, elapsedDays float64, daysInMonth int, ) (*CostDriftData, error)
CalculateCostDriftWithElapsedDays computes run-rate drift using a fractional elapsed day denominator. This avoids early-month bias from integer dayOfMonth rounding (for example, March 3 at 06:00 should use 2.25 elapsed days, not 3).
Parameters:
- actualMTD: actual cost accumulated over the elapsed window.
- projected: projected monthly cost expressed on the canonical 730h basis.
- elapsedDays: elapsed runtime window in days; may be fractional.
- daysInMonth: total days in the reference calendar month (28-31).
Return values match CalculateCostDrift.
func (*CostDriftData) Validate ¶ added in v0.3.0
func (c *CostDriftData) Validate() error
Validate checks that the CostDriftData fields are consistent.
type CostResult ¶
type CostResult struct {
ResourceType string `json:"resourceType"`
ResourceID string `json:"resourceId"`
Adapter string `json:"adapter"`
Currency string `json:"currency"`
Monthly float64 `json:"monthly"`
Hourly float64 `json:"hourly"`
Notes string `json:"notes"`
Breakdown map[string]float64 `json:"breakdown"`
Sustainability map[string]SustainabilityMetric `json:"sustainability,omitempty"`
// Recommendations contains cost optimization suggestions from plugins.
// This field is populated when plugins provide actionable recommendations
// alongside cost estimates (e.g., right-sizing, termination suggestions).
Recommendations []Recommendation `json:"recommendations,omitempty"`
// Error contains a machine-readable structured error for JSON/NDJSON output.
// When non-nil, callers should prefer this structured error for programmatic
// handling. The Notes field may still contain ERROR: or VALIDATION: prefixes
// for backward compatibility (see internal/proto/adapter.go).
Error *StructuredError `json:"error,omitempty"`
// Actual cost specific fields
TotalCost float64 `json:"totalCost,omitempty"`
DailyCosts []float64 `json:"dailyCosts,omitempty"`
CostPeriod string `json:"costPeriod,omitempty"`
StartDate time.Time `json:"startDate,omitempty"`
EndDate time.Time `json:"endDate,omitempty"`
// Delta represents the cost change (trend) compared to a baseline, in the same currency as the cost.
// Positive values indicate cost increase, negative values indicate decrease.
// The baseline depends on context (e.g., previous period for actual costs, budget for projected costs).
Delta float64 `json:"delta,omitempty"`
// Confidence indicates the reliability level of this cost estimate.
// HIGH: Real billing data from cloud APIs
// MEDIUM: Runtime-based estimate from Pulumi timestamps
// LOW: Imported resource (timestamp may be inaccurate)
Confidence Confidence `json:"confidence,omitempty"`
// ExpiresAt is a caching hint from the plugin indicating when this cost data
// becomes stale. When non-nil, the engine uses it to calculate a custom cache
// TTL via cache.CalculatePluginTTL instead of the store default. Nil means
// the plugin did not provide a hint and the default TTL applies.
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
}
CostResult contains the calculated cost information for a single resource.
func AggregateResultsInternal ¶
func AggregateResultsInternal(results []CostResult, groupName string) CostResult
AggregateResultsInternal aggregates multiple CostResult entries into a single CostResult. AggregateResultsInternal sums numeric totals (Monthly, Hourly, TotalCost), merges breakdown maps by summing values for matching keys, and combines daily cost series by aligning indices and summing per-day values. The returned CostResult preserves the Currency, StartDate, EndDate and CostPeriod from the first entry in results, sets ResourceType to groupName, ResourceID to "aggregated-<N>-resources", Adapter to "aggregated", and Notes to indicate the number of AggregateResultsInternal aggregates multiple CostResult entries into a single CostResult that represents the grouped resources.
AggregateResultsInternal sums numeric totals (Monthly, Hourly, TotalCost), merges Breakdown maps by summing values for matching keys, and aligns/sums DailyCosts across results, extending the slice to match the longest daily-cost series. The returned result preserves Currency, StartDate, EndDate, and CostPeriod from the first entry, and sets ResourceType to the provided groupName, ResourceID to "aggregated-N-resources", Adapter to "aggregated", and Notes to indicate the number of aggregated resources.
Parameters:
- results: slice of CostResult entries to aggregate. If empty, an empty CostResult is returned.
- groupName: name to assign to the aggregated ResourceType.
Returns:
The aggregated CostResult combining all provided entries.
func MergeRecommendations ¶
func MergeRecommendations( costs []CostResult, recommendations []Recommendation, ) []CostResult
MergeRecommendations merges recommendations into CostResults by matching ResourceID.
type CostResultWithErrors ¶
type CostResultWithErrors struct {
Results []CostResult
Errors []ErrorDetail
}
CostResultWithErrors wraps results and any errors encountered during cost calculation.
func (*CostResultWithErrors) ErrorSummary ¶
func (c *CostResultWithErrors) ErrorSummary() string
ErrorSummary returns a human-readable summary of errors. Truncates the output after maxErrorsToDisplay errors to keep it readable.
func (*CostResultWithErrors) HasErrors ¶
func (c *CostResultWithErrors) HasErrors() bool
HasErrors returns true if any errors were encountered during cost calculation.
type CostSummary ¶
type CostSummary struct {
TotalMonthly float64 `json:"totalMonthly"`
TotalHourly float64 `json:"totalHourly"`
Currency string `json:"currency"`
ByProvider map[string]float64 `json:"byProvider"`
ByService map[string]float64 `json:"byService"`
ByAdapter map[string]float64 `json:"byAdapter"`
Resources []CostResult `json:"resources"`
}
CostSummary provides aggregated cost totals grouped by provider, service, and adapter.
type CrossProviderAggregation ¶
type CrossProviderAggregation struct {
Period string `json:"period"` // Date (2024-01-01) or Month (2024-01)
Providers map[string]float64 `json:"providers"` // Provider name -> cost
Total float64 `json:"total"` // Total cost for this period
Currency string `json:"currency"` // Currency for all costs
}
CrossProviderAggregation represents daily/monthly cost aggregation across providers.
This type enables comprehensive cost analysis across multiple cloud providers (AWS, Azure, GCP, etc.) with time-based aggregation for trend analysis and cost optimization insights.
Usage Examples:
// Daily aggregation showing multi-provider costs
agg := CrossProviderAggregation{
Period: "2024-01-15",
Providers: map[string]float64{
"aws": 245.67,
"azure": 156.23,
"gcp": 89.45,
},
Total: 491.35,
Currency: "USD",
}
// Monthly aggregation for cost trending
agg := CrossProviderAggregation{
Period: "2024-01",
Providers: map[string]float64{
"aws": 7620.15,
"azure": 4843.67,
},
Total: 12463.82,
Currency: "USD",
}
func CreateCrossProviderAggregation ¶
func CreateCrossProviderAggregation( results []CostResult, groupBy GroupBy, ) ([]CrossProviderAggregation, error)
CreateCrossProviderAggregation creates daily/monthly cross-provider aggregation from cost results.
This function aggregates costs across multiple cloud providers and time periods, enabling comprehensive cost analysis and reporting. It supports both daily and monthly aggregation with built-in currency validation and input validation.
Parameters:
- results: Slice of CostResult objects containing cost data from various providers. Each result must have consistent currency and valid date ranges.
- groupBy: Must be GroupByDaily or GroupByMonthly. Other grouping types will return ErrInvalidGroupBy.
Returns:
- []CrossProviderAggregation: Sorted aggregations by time period, each containing:
- Period: Date ("2006-01-02") for daily or month ("2006-01") for monthly
- Providers: Map of provider names to their costs for that period
- Total: Sum of all provider costs for the period
- Currency: Consistent currency across all results
- error: Various validation errors:
- ErrEmptyResults: If results slice is empty
- ErrInvalidGroupBy: If groupBy is not time-based (daily/monthly)
- ErrMixedCurrencies: If results contain different currencies
- ErrInvalidDateRange: If any result has EndDate before StartDate
Usage Examples:
// Daily aggregation across AWS and Azure costs
results := []CostResult{
{ResourceType: "aws:ec2:Instance", TotalCost: 100.0, Currency: "USD", StartDate: jan1, EndDate: jan2},
{ResourceType: "azure:compute:VirtualMachine", TotalCost: 150.0, Currency: "USD", StartDate: jan1, EndDate: jan2},
}
aggregations, err := CreateCrossProviderAggregation(results, GroupByDaily)
if err != nil {
return fmt.Errorf("aggregation failed: %w", err)
}
// Result: [{Period: "2024-01-01", Providers: {"aws": 100.0, "azure": 150.0}, Total: 250.0, Currency: "USD"}]
// Monthly aggregation for cost trend analysis
aggregations, err := CreateCrossProviderAggregation(results, GroupByMonthly)
// Result: [{Period: "2024-01", Providers: {"aws": 3100.0, "azure": 4650.0}, Total: 7750.0, Currency: "USD"}]
Error Scenarios:
- Mixed currencies: Results with different currencies (USD vs EUR) will fail validation
- Invalid grouping: Using GroupByResource or GroupByType will return ErrInvalidGroupBy
- Date validation: Results with EndDate before StartDate will fail
- Empty input: Empty results slice returns ErrEmptyResults
Performance Considerations:
- Efficient for datasets up to 10,000 cost results
- Memory usage scales linearly with unique time periods
CreateCrossProviderAggregation groups cost results into time-based cross-provider aggregations.
CreateCrossProviderAggregation validates the input results and the provided time-based grouping, ensures all results use a single currency, then groups costs by period (daily or monthly) and provider. It produces a sorted slice of CrossProviderAggregation entries where each entry contains the period string, per-provider totals, the period total, and the common currency.
Parameters:
- results: the list of CostResult entries to aggregate.
- groupBy: a time-based GroupBy value (daily or monthly) that determines the period granularity.
Returns:
- a sorted slice of CrossProviderAggregation entries ordered by period.
CreateCrossProviderAggregation groups a slice of CostResult entries by the specified time period and returns cross-provider aggregations for each period sorted by period key.
The function accepts:
- results: the cost results to aggregate (must be non-empty).
- groupBy: the time-based grouping to apply (daily or monthly).
It validates inputs, enforces that all results use a single currency (empty currency values are treated as the package default), and groups costs by period and provider. Each returned CrossProviderAggregation contains the period key, per-provider totals, the overall total, and the resolved currency.
Possible errors:
- ErrEmptyResults if `results` is empty.
- ErrInvalidGroupBy if `groupBy` is not a time-based grouping.
- ErrInvalidDateRange if any result has an EndDate not after its StartDate.
- the results contain more than one distinct currency (ErrMixedCurrencies).
type DefaultBudgetEngine ¶ added in v0.2.4
type DefaultBudgetEngine struct {
// contains filtered or unexported fields
}
DefaultBudgetEngine implements BudgetEngine with standard evaluation logic.
func NewBudgetEngine ¶ added in v0.2.4
func NewBudgetEngine() *DefaultBudgetEngine
NewBudgetEngine returns a DefaultBudgetEngine configured to use time.Now as the time source.
func NewBudgetEngineWithTime ¶ added in v0.2.4
func NewBudgetEngineWithTime(nowFunc func() time.Time) *DefaultBudgetEngine
NewBudgetEngineWithTime creates a new DefaultBudgetEngine with a custom time function. NewBudgetEngineWithTime creates a DefaultBudgetEngine that uses nowFunc as the source of current time. nowFunc is called whenever the engine needs the current time and enables deterministic behavior for tests. If nowFunc is nil, time.Now is used as the default. It returns a pointer to a DefaultBudgetEngine configured to use the provided time function.
func (*DefaultBudgetEngine) Evaluate ¶ added in v0.2.4
func (e *DefaultBudgetEngine) Evaluate( budget config.BudgetConfig, currentSpend float64, currency string, ) (*BudgetStatus, error)
Evaluate compares current spend against the configured budget and alerts. It returns a BudgetStatus with threshold evaluations or an error if validation fails.
Errors are returned when:
- budget.Currency != currency (currency mismatch)
- budget.Amount is <= 0 (budget disabled or invalid)
- currentSpend is negative (invalid spend)
type DismissRequest ¶ added in v0.3.0
type DismissRequest struct {
// RecommendationID is the unique identifier of the recommendation to dismiss.
RecommendationID string `json:"recommendationId"`
// Reason is the CLI flag value (e.g., "business-constraint").
Reason string `json:"reason"`
// CustomReason is the free-text explanation from the --note flag.
CustomReason string `json:"customReason,omitempty"`
// ExpiresAt is the snooze expiry date; nil means permanent dismissal.
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
// Recommendation is the current recommendation details for the LastKnown snapshot.
Recommendation *Recommendation `json:"recommendation,omitempty"`
}
DismissRequest contains parameters for dismissing a recommendation.
type DismissResult ¶ added in v0.3.0
type DismissResult struct {
// RecommendationID is the ID of the dismissed recommendation.
RecommendationID string `json:"recommendationId"`
// PluginDismissed is true if a plugin accepted the dismissal via RPC.
PluginDismissed bool `json:"pluginDismissed"`
// PluginName identifies which plugin handled the dismissal.
PluginName string `json:"pluginName,omitempty"`
// PluginMessage is the plugin's response message.
PluginMessage string `json:"pluginMessage,omitempty"`
// PluginFailed is true if a plugin dismiss RPC was attempted but failed.
// Local persistence still proceeds, but the upstream state may be inconsistent.
PluginFailed bool `json:"pluginFailed"`
// LocalPersisted is true if the dismissal was saved locally.
LocalPersisted bool `json:"localPersisted"`
// Warning contains a non-fatal warning (e.g., plugin failed but local succeeded).
Warning string `json:"warning,omitempty"`
}
DismissResult contains the outcome of a dismiss operation.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine orchestrates cost calculations between plugins and local pricing specifications.
func New ¶
func New(clients []*pluginhost.Client, loader SpecLoader) *Engine
New creates a new Engine with the given plugin clients and spec loader.
func (*Engine) DismissRecommendation ¶ added in v0.3.0
func (e *Engine) DismissRecommendation( ctx context.Context, store *config.DismissalStore, req DismissRequest, ) (*DismissResult, error)
DismissRecommendation dismisses a recommendation by ID. When a connected plugin advertises the dismiss_recommendations capability, the DismissRecommendation RPC is called first. The dismissal is always persisted locally regardless of plugin result.
func (*Engine) EstimateCost ¶ added in v0.3.0
func (e *Engine) EstimateCost( ctx context.Context, request *EstimateRequest, ) (*EstimateResult, error)
EstimateCost performs what-if cost analysis with property overrides.
It first attempts to use the EstimateCost RPC if the plugin implements it. If the RPC is unimplemented, it falls back to calling GetProjectedCost twice: once with original properties (baseline) and once with overrides applied (modified).
Parameters:
- ctx: Context for cancellation and tracing
- request: The estimate request containing resource and property overrides
Returns:
- *EstimateResult: The estimation result with baseline, modified costs, and deltas
- error: Any error encountered during estimation
The method logs at appropriate levels:
- DEBUG: Entry/exit, fallback decisions
- INFO: Successful estimations
- WARN: Fallback usage
- ERROR: Failed estimations
func (*Engine) GetActualCost ¶
func (e *Engine) GetActualCost( ctx context.Context, resources []ResourceDescriptor, from, to time.Time, ) ([]CostResult, error)
GetActualCost retrieves historical actual costs from plugins for the specified time range.
func (*Engine) GetActualCostWithOptions ¶
func (e *Engine) GetActualCostWithOptions( ctx context.Context, request ActualCostRequest, ) ([]CostResult, error)
GetActualCostWithOptions retrieves actual costs with advanced filtering, grouping, and time range options.
func (*Engine) GetActualCostWithOptionsAndErrors ¶
func (e *Engine) GetActualCostWithOptionsAndErrors( ctx context.Context, request ActualCostRequest, ) (*CostResultWithErrors, error)
GetActualCostWithOptionsAndErrors retrieves actual costs with comprehensive error tracking. It returns results for all resources (with placeholders for failures) and aggregated error details.
func (*Engine) GetBudgets ¶ added in v0.2.5
func (e *Engine) GetBudgets(ctx context.Context, filter *BudgetFilterOptions) (*BudgetResult, error)
GetBudgets retrieves budgets from plugins and applies health calculations. This is the main entry point for budget health functionality.
func (*Engine) GetProjectedCost ¶
func (e *Engine) GetProjectedCost( ctx context.Context, resources []ResourceDescriptor, ) ([]CostResult, error)
GetProjectedCost calculates projected costs for the given resources using plugins or specs.
func (*Engine) GetProjectedCostWithErrors ¶
func (e *Engine) GetProjectedCostWithErrors( ctx context.Context, resources []ResourceDescriptor, ) (*CostResultWithErrors, error)
GetProjectedCostWithErrors calculates projected costs with comprehensive error tracking.
func (*Engine) GetRecommendationHistory ¶ added in v0.3.0
func (e *Engine) GetRecommendationHistory( _ context.Context, store *config.DismissalStore, recommendationID string, ) ([]config.LifecycleEvent, error)
GetRecommendationHistory returns the lifecycle events for a recommendation.
func (*Engine) GetRecommendationsForResources ¶
func (e *Engine) GetRecommendationsForResources( ctx context.Context, resources []ResourceDescriptor, ) (*RecommendationsResult, error)
GetRecommendationsForResources fetches cost optimization recommendations for the given resources. For large datasets (>100 resources), it uses batch processing to improve performance and memory usage.
func (*Engine) GroupResults ¶
func (e *Engine) GroupResults(results []CostResult, groupBy GroupBy) []CostResult
GroupResults groups cost results by the specified grouping strategy.
func (*Engine) UndismissRecommendation ¶ added in v0.3.0
func (e *Engine) UndismissRecommendation( ctx context.Context, store *config.DismissalStore, recommendationID string, ) (*UndismissResult, error)
UndismissRecommendation re-enables a previously dismissed or snoozed recommendation.
func (*Engine) WithCache ¶ added in v0.2.5
WithCache sets the cache store for the engine. This is optional - if not set, no caching will be performed.
func (*Engine) WithDismissalStore ¶ added in v0.3.0
func (e *Engine) WithDismissalStore(store *config.DismissalStore) *Engine
WithDismissalStore sets the dismissal store for the engine. This is optional - if not set, a store is created on demand per call.
func (*Engine) WithJobs ¶ added in v0.3.0
WithJobs sets an explicit worker count override for concurrent operations. When jobs > 0, this value is used instead of the auto-calculated worker count. When jobs == 0 (default), the auto calculation based on NumCPU is used. Negative inputs are normalized to 0 (auto).
func (*Engine) WithRouter ¶ added in v0.2.6
WithRouter sets the router for intelligent plugin selection. This is optional - if not set, all plugins are queried for each resource.
type ErrorDetail ¶
type ErrorDetail struct {
ResourceType string
ResourceID string
PluginName string
Error error
Timestamp time.Time
}
ErrorDetail captures information about a failed resource cost calculation.
type ErrorType ¶ added in v0.3.0
type ErrorType int
ErrorType categorises the kind of error encountered for a resource.
const ( // ErrorTypeAuth indicates an authentication or authorisation failure. ErrorTypeAuth ErrorType = iota // ErrorTypeNetwork indicates a network connectivity failure. ErrorTypeNetwork // ErrorTypeRateLimit indicates a rate-limit / throttle response. ErrorTypeRateLimit // ErrorTypeUnknown indicates an unclassified error. ErrorTypeUnknown )
func (ErrorType) MarshalJSON ¶ added in v0.3.0
MarshalJSON implements json.Marshaler to output ErrorType as string.
func (*ErrorType) UnmarshalJSON ¶ added in v0.3.0
UnmarshalJSON implements json.Unmarshaler to parse ErrorType from string.
type EstimateRequest ¶ added in v0.3.0
type EstimateRequest struct {
// Resource is the base resource descriptor
Resource *ResourceDescriptor `json:"resource,omitempty"`
// PropertyOverrides are the changes to evaluate
PropertyOverrides map[string]string `json:"propertyOverrides,omitempty"`
// UsageProfile optionally provides context (dev, prod, etc.)
UsageProfile string `json:"usageProfile,omitempty"`
}
EstimateRequest encapsulates parameters for EstimateCost.
This is the internal request structure used by the engine layer for what-if cost analysis operations.
type EstimateResult ¶ added in v0.3.0
type EstimateResult struct {
// Resource is the resource being estimated
Resource *ResourceDescriptor `json:"resource"`
// Baseline is the cost with original properties
Baseline *CostResult `json:"baseline"`
// Modified is the cost with property overrides applied
Modified *CostResult `json:"modified"`
// TotalChange is the difference between modified and baseline monthly costs
// Positive = increase, negative = savings
TotalChange float64 `json:"totalChange"`
// Deltas contains per-property cost impact breakdown
Deltas []CostDelta `json:"deltas"`
// UsedFallback indicates if EstimateCost RPC was unavailable
// and the result was computed from two GetProjectedCost calls
UsedFallback bool `json:"usedFallback,omitempty"`
}
EstimateResult represents the result of a what-if cost estimation. It contains baseline and modified costs along with per-property deltas.
Usage Examples:
result := EstimateResult{
Resource: &ResourceDescriptor{Provider: "aws", Type: "ec2:Instance"},
Baseline: &CostResult{Monthly: 8.32, Currency: "USD"},
Modified: &CostResult{Monthly: 83.22, Currency: "USD"},
TotalChange: 74.90,
Deltas: []CostDelta{
{Property: "instanceType", OriginalValue: "t3.micro", NewValue: "m5.large", CostChange: 74.90},
},
}
type ExtendedBudgetSummary ¶ added in v0.2.5
type ExtendedBudgetSummary struct {
*pbc.BudgetSummary // Embedded proto summary
ByProvider map[string]*pbc.BudgetSummary // Per-provider breakdown
ByCurrency map[string]*pbc.BudgetSummary // Per-currency breakdown
OverallHealth pbc.BudgetHealthStatus // Worst-case health
CriticalBudgets []string // IDs of critical/exceeded budgets
}
func CalculateExtendedSummary ¶ added in v0.2.5
func CalculateExtendedSummary(ctx context.Context, budgets []*pbc.Budget) *ExtendedBudgetSummary
CalculateExtendedSummary provides detailed breakdown by provider and currency. Includes list of critical/exceeded budget IDs for immediate attention.
type GroupBy ¶
type GroupBy string
GroupBy defines the available grouping strategies for cost result aggregation.
This type enables flexible cost analysis by grouping results along different dimensions such as resources, providers, or time periods. Each grouping type provides unique insights for cost optimization and analysis.
const ( GroupByResource GroupBy = "resource" GroupByType GroupBy = "type" GroupByProvider GroupBy = "provider" GroupByDate GroupBy = "date" // Deprecated: use GroupByDaily GroupByDaily GroupBy = "daily" GroupByMonthly GroupBy = "monthly" GroupByNone GroupBy = "" )
GroupBy constants define all supported aggregation strategies.
Resource Groupings:
- GroupByResource: Groups by individual resource (ResourceType/ResourceID)
- GroupByType: Groups by resource type (e.g., "aws:ec2:Instance")
- GroupByProvider: Groups by cloud provider (e.g., "aws", "azure", "gcp")
Time-Based Groupings:
- GroupByDaily: Groups by calendar date ("2006-01-02") for daily trends
- GroupByMonthly: Groups by month ("2006-01") for monthly analysis
- GroupByDate: Deprecated legacy date-key grouping (non time-based for cross-provider). Prefer GroupByDaily for time-based aggregations.
Special Values:
- GroupByNone: No grouping (empty string) - returns results as-is
Usage Guidelines:
- Use resource groupings for cost attribution and resource optimization
- Use time-based groupings for trend analysis and forecasting
- Time-based groupings require results with valid StartDate/EndDate fields
- Cross-provider aggregation only supports time-based groupings
func (GroupBy) IsTimeBasedGrouping ¶
IsTimeBasedGrouping returns true if the GroupBy requires time-based aggregation.
Time-based groupings require cost results with valid StartDate and EndDate fields and are the only grouping types supported by cross-provider aggregation functions. This method is used internally to validate aggregation requests and determine processing strategies.
Time-Based GroupBy Values:
- GroupByDaily: Requires daily cost data aggregation
- GroupByMonthly: Requires monthly cost data aggregation
- GroupByDate: Deprecated legacy date-key grouping (non time-based for cross-provider)
Non-Time-Based GroupBy Values:
- GroupByResource: Groups by resource identity
- GroupByType: Groups by resource type
- GroupByProvider: Groups by cloud provider
- GroupByNone: No grouping applied
Returns:
- true: For GroupByDaily and GroupByMonthly only
- false: For all other GroupBy values
Usage Examples:
// Validate for cross-provider aggregation
if !groupBy.IsTimeBasedGrouping() {
return ErrInvalidGroupBy
}
// Route to appropriate processing
if groupBy.IsTimeBasedGrouping() {
return CreateCrossProviderAggregation(results, groupBy)
} else {
return engine.GroupResults(results, groupBy)
}
func (GroupBy) IsValid ¶
IsValid returns true if the GroupBy value is valid.
This method provides compile-time safety for GroupBy values and enables validation before processing. All defined GroupBy constants are considered valid, including the empty string (GroupByNone).
Returns:
- true: For all defined GroupBy constants
- false: For any other string values
Usage Examples:
// Validate user input
groupBy := GroupBy(userInput)
if !groupBy.IsValid() {
return fmt.Errorf("invalid groupBy: %s", userInput)
}
// Safe to use in switch statements
switch groupBy {
case GroupByDaily, GroupByMonthly:
// Time-based processing
case GroupByResource, GroupByType, GroupByProvider:
// Resource-based processing
}
func (GroupBy) String ¶
String returns the string representation of the GroupBy.
This method implements the Stringer interface and provides a consistent string representation for logging, debugging, and serialization.
Returns:
- string: The underlying string value of the GroupBy
- "": Empty string for GroupByNone
Usage Examples:
// Logging and debugging
log.Printf("Processing with groupBy: %s", groupBy.String())
// CLI flag validation
if groupBy.String() == "" {
groupBy = GroupByResource // Default
}
// JSON serialization (automatic via json package)
type Request struct {
GroupBy GroupBy `json:"groupBy"`
}
type OutputFormat ¶
type OutputFormat string
OutputFormat specifies the output format for cost results (table, JSON, NDJSON).
const ( // OutputTable renders results in a formatted table. OutputTable OutputFormat = "table" // OutputJSON renders results as pretty-printed JSON. OutputJSON OutputFormat = "json" // OutputNDJSON renders results as newline-delimited JSON for streaming. OutputNDJSON OutputFormat = "ndjson" )
type OverviewJSONOutput ¶ added in v0.3.0
type OverviewJSONOutput struct {
Metadata OverviewMetadata `json:"metadata"`
Resources []OverviewRow `json:"resources"`
Summary OverviewSummary `json:"summary"`
Budgets []BudgetHealthResult `json:"budgets,omitempty"`
Errors []OverviewRowError `json:"errors"`
}
OverviewJSONOutput is the top-level JSON output structure.
type OverviewMetadata ¶ added in v0.3.0
type OverviewMetadata struct {
StackContext
}
OverviewMetadata holds metadata information for the JSON output. It embeds StackContext so field promotion avoids duplication. GeneratedAt is promoted from StackContext.
type OverviewRow ¶ added in v0.3.0
type OverviewRow struct {
URN string `json:"urn"`
Type string `json:"type"`
ResourceID string `json:"resourceId,omitempty"`
Status ResourceStatus `json:"status"`
Properties map[string]interface{} `json:"properties,omitempty"`
// ProjectedProperties stores preview-merged properties used for projected
// cost calls (deep-merge of old state and new inputs). Actual cost calls
// continue to use Properties from current state.
ProjectedProperties map[string]interface{} `json:"projectedProperties,omitempty"`
ActualCost *ActualCostData `json:"actualCost,omitempty"`
ProjectedCost *ProjectedCostData `json:"projectedCost,omitempty"`
// BaselineProjectedCost captures projected monthly cost using current-state
// properties (before pending changes). It is internal to delta math and is
// intentionally omitted from serialized output.
BaselineProjectedCost *ProjectedCostData `json:"-"`
Recommendations []Recommendation `json:"recommendations,omitempty"`
CostDrift *CostDriftData `json:"costDrift,omitempty"`
Error *OverviewRowError `json:"error,omitempty"`
PropertyDiffs []PropertyDiff `json:"propertyDiffs,omitempty"`
// ComputedDelta is the per-row cost delta populated by PopulateComputedDeltas
// after enrichment, before any rendering. All renderers (table, JSON, NDJSON,
// TUI) read this value instead of computing deltas independently.
// Nil when no meaningful delta exists (e.g., active resource without drift).
ComputedDelta *float64 `json:"delta,omitempty"`
// CreatedAt tracks when the resource was first added to Pulumi state.
// Used by enrichCostDrift to suppress drift for very new resources with
// insufficient data points in the current billing window.
// Nil for resources from Pulumi < v3.60.0 or plan-only resources.
CreatedAt *time.Time `json:"createdAt,omitempty"`
}
OverviewRow represents a single resource row in the unified cost overview. Each row combines state, plan, actual costs, projected costs, drift, and recommendations for a resource.
func EnrichOverviewRows ¶ added in v0.3.0
func EnrichOverviewRows( ctx context.Context, rows []OverviewRow, eng *Engine, dateRange DateRange, progressChan chan<- OverviewRowUpdate, ) []OverviewRow
EnrichOverviewRows concurrently enriches each OverviewRow in rows with cost and recommendation data. It runs a fixed-size worker pool (capped by overviewConcurrencyLimit or the number of rows), respects context cancellation, and records any per-row failures in each row's Error field without returning an error. If progressChan is non-nil, the function sends OverviewRowUpdate messages as rows are processed and closes progressChan before returning.
Parameters:
- ctx: the context to observe for cancellation and logging.
- rows: the slice of OverviewRow to enrich; enrichment is performed in-place and the same slice is returned.
- eng: the Engine used to fetch costs and recommendations.
- dateRange: the date range used for cost calculations.
- progressChan: optional channel that receives progress updates for each enriched row; may be nil.
Returns:
The input slice of OverviewRow populated with enrichment results (ActualCost, ProjectedCost, Recommendations, CostDrift) and any per-row Error values set for partial failures.
func MergeResourcesForOverview ¶ added in v0.3.0
func MergeResourcesForOverview( ctx context.Context, stateResources []StateResource, planSteps []PlanStep, ) ([]OverviewRow, error)
MergeResourcesForOverview builds skeleton OverviewRow entries by combining current Pulumi state resources with pending plan steps.
The merge preserves the order of state resources (FR-011), appending any newly-created resources from the plan that are not already present in state. Only custom resources (cloud resources, not providers or components) are included.
The returned rows have URN, Type, ResourceID, and Status populated; cost fields are left nil for later enrichment.
func NewRowsFromState ¶ added in v0.3.2
func NewRowsFromState(ctx context.Context, stateResources []StateResource) []OverviewRow
NewRowsFromState creates skeleton OverviewRows from state resources only, with no plan data applied. All rows are assigned StatusActive.
This is used for Phase 1 (state-first) loading before pulumi preview runs. The rows are structurally identical to those produced by MergeResourcesForOverview and can be enriched and later updated with change status via ApplyChangesToRows.
Only custom resources are included — the same filter as MergeResourcesForOverview.
func (*OverviewRow) Validate ¶ added in v0.3.0
func (r *OverviewRow) Validate() error
Validate checks that the OverviewRow fields are well-formed. It validates required fields, length constraints, status range, and recursively validates nested types when present.
type OverviewRowError ¶ added in v0.3.0
type OverviewRowError struct {
URN string `json:"urn"`
ErrorType ErrorType `json:"errorType"`
Message string `json:"message"`
Retryable bool `json:"retryable"`
}
OverviewRowError captures an error that occurred while fetching cost data for a specific resource.
func (*OverviewRowError) Validate ¶ added in v0.3.0
func (e *OverviewRowError) Validate() error
Validate checks that the OverviewRowError fields are well-formed.
type OverviewRowUpdate ¶ added in v0.3.0
type OverviewRowUpdate struct {
Index int
Row OverviewRow
}
OverviewRowUpdate carries a row update on a progress channel, pairing the row index with the updated row data.
type OverviewSummary ¶ added in v0.3.0
type OverviewSummary struct {
TotalActualMTD float64 `json:"totalActualMTD"`
ProjectedMonthly float64 `json:"projectedMonthly"`
ProjectedDelta float64 `json:"projectedDelta"`
PotentialSavings float64 `json:"potentialSavings"`
Currency string `json:"currency"`
}
OverviewSummary holds aggregated summary statistics for the JSON output.
type PlanStep ¶ added in v0.3.0
type PlanStep struct {
URN string `json:"urn,omitempty"`
Op string `json:"op,omitempty"`
Type string `json:"type,omitempty"`
PropertyDiffs []PropertyDiff `json:"propertyDiffs,omitempty"`
// ProjectedProperties carries preview properties (deep-merge of old state
// and new inputs) used to price pending changes accurately.
ProjectedProperties map[string]interface{} `json:"projectedProperties,omitempty"`
}
PlanStep represents a step from a Pulumi plan for overview merging. This is a lightweight projection of ingest.PulumiStep to avoid an import cycle (ingest already imports engine).
type PluginMatch ¶ added in v0.2.6
type PluginMatch struct {
Client *pluginhost.Client
Priority int
Fallback bool
MatchReason string
Source string
}
PluginMatch represents a matched plugin from the router. This mirrors router.PluginMatch to avoid circular imports.
type PricingSpec ¶
type PricingSpec = spec.PricingSpec
PricingSpec is an alias to the PricingSpec from the spec package to ensure type consistency.
type ProjectedCostData ¶ added in v0.3.0
type ProjectedCostData struct {
MonthlyCost float64 `json:"monthlyCost"`
Currency string `json:"currency"`
Breakdown map[string]float64 `json:"breakdown,omitempty"`
}
ProjectedCostData holds projected monthly cost information for a resource.
func (*ProjectedCostData) Validate ¶ added in v0.3.0
func (p *ProjectedCostData) Validate() error
Validate checks that the ProjectedCostData fields are consistent.
type ProjectedCostRequest ¶
type ProjectedCostRequest struct {
Resources []ResourceDescriptor
SpecDir string
Adapter string
}
ProjectedCostRequest contains resources for which projected costs should be calculated.
type PropertyDiff ¶ added in v0.3.3
type PropertyDiff struct {
Key string `json:"key"`
OldValue string `json:"oldValue"`
NewValue string `json:"newValue"`
}
PropertyDiff represents a single property change from a Pulumi plan. It captures the before and after values for a property that differs between OldState.Inputs and NewState.Inputs in an update or replace operation.
type Recommendation ¶
type Recommendation struct {
// ResourceID identifies the resource this recommendation applies to.
ResourceID string `json:"resourceId,omitempty"`
// Type categorizes the recommendation (e.g., "Right-sizing", "Terminate",
// "Purchase Commitment", "Delete Unused", "Adjust Requests")
Type string `json:"type"`
// Description provides actionable text explaining the recommendation
Description string `json:"description"`
// EstimatedSavings is the projected monthly savings if the recommendation
// is implemented. Zero indicates savings cannot be estimated.
EstimatedSavings float64 `json:"estimatedSavings,omitempty"`
// Currency is the ISO 4217 code for EstimatedSavings (e.g., "USD").
// Empty if EstimatedSavings is zero.
Currency string `json:"currency,omitempty"`
// Status indicates the lifecycle state of this recommendation.
// Empty or "Active" for active recommendations, "Dismissed" or "Snoozed"
// for dismissed/snoozed recommendations shown via --include-dismissed.
Status RecommendationStatus `json:"status,omitempty"`
// Reasoning carries plugin-provided warnings and caveats explaining
// prerequisites or risks for implementing this recommendation
// (e.g., "Ensure application compatibility with ARM64 architecture").
Reasoning []string `json:"reasoning,omitempty"`
}
Recommendation represents a single cost optimization suggestion.
Recommendations are provided by plugins to suggest ways to reduce costs, such as right-sizing instances, terminating idle resources, or purchasing reserved capacity.
Usage Examples:
rec := Recommendation{
Type: "Right-sizing",
Description: "Switch to t3.small to reduce costs",
EstimatedSavings: 15.00,
Currency: "USD",
}
type RecommendationError ¶
type RecommendationError struct {
PluginName string `json:"pluginName"`
Error string `json:"error"`
}
RecommendationError captures error information when fetching recommendations from a plugin.
type RecommendationStatus ¶ added in v0.3.0
type RecommendationStatus string
RecommendationStatus represents the lifecycle state of a recommendation.
const ( // RecommendationStatusActive indicates an active (non-dismissed) recommendation. RecommendationStatusActive RecommendationStatus = "Active" // RecommendationStatusDismissed indicates a permanently dismissed recommendation. RecommendationStatusDismissed RecommendationStatus = "Dismissed" // RecommendationStatusSnoozed indicates a temporarily snoozed recommendation. RecommendationStatusSnoozed RecommendationStatus = "Snoozed" )
func (RecommendationStatus) IsValid ¶ added in v0.3.0
func (rs RecommendationStatus) IsValid() bool
IsValid returns true if the status is a known value.
func (RecommendationStatus) String ¶ added in v0.3.0
func (rs RecommendationStatus) String() string
String returns the string representation of the status.
type RecommendationsResult ¶
type RecommendationsResult struct {
Recommendations []Recommendation `json:"recommendations"`
Errors []RecommendationError `json:"errors"`
TotalSavings float64 `json:"totalSavings"`
Currency string `json:"currency"`
}
RecommendationsResult contains the results of fetching recommendations from multiple plugins.
func (*RecommendationsResult) ErrorSummary ¶
func (r *RecommendationsResult) ErrorSummary() string
ErrorSummary returns a string summary of errors.
func (*RecommendationsResult) HasErrors ¶
func (r *RecommendationsResult) HasErrors() bool
HasErrors returns true if any errors were encountered.
type ResourceDescriptor ¶
type ResourceDescriptor struct {
Type string `json:"type"`
ID string `json:"id"`
Provider string `json:"provider"`
Properties map[string]interface{} `json:"properties"`
}
ResourceDescriptor represents a cloud resource with its type, provider, and properties.
func FilterResources ¶
func FilterResources(resources []ResourceDescriptor, filter string) []ResourceDescriptor
FilterResources selects resources that match the provided filter expression. The filter is a single key=value expression (for example "provider=aws" or "tag:env=prod"). An empty filter returns the input slice unchanged. The returned slice contains only the resources that satisfy the filter.
func (*ResourceDescriptor) Validate ¶
func (r *ResourceDescriptor) Validate() error
Validate checks that the ResourceDescriptor has valid fields and returns an error if validation fails. It validates:
- Type is not empty and within length limits
- ID is within length limits (can be empty for some resources)
- Properties count does not exceed maximum
- Property keys are valid identifiers and within length limits
- Property values do not exceed size limits
type ResourceStatus ¶ added in v0.3.0
type ResourceStatus int
ResourceStatus represents the lifecycle state of a resource in the overview.
const ( // StatusActive indicates the resource exists and has no pending changes. StatusActive ResourceStatus = iota // StatusCreating indicates the resource is being created. StatusCreating // StatusUpdating indicates the resource is being updated. StatusUpdating // StatusDeleting indicates the resource is being deleted. StatusDeleting // StatusReplacing indicates the resource is being replaced (delete + create). StatusReplacing )
func MapOperationToStatus ¶ added in v0.3.0
func MapOperationToStatus(op string) ResourceStatus
MapOperationToStatus converts a Pulumi plan operation string to a ResourceStatus value. Unknown operations default to StatusActive.
func (ResourceStatus) MarshalJSON ¶ added in v0.3.0
func (s ResourceStatus) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler to output ResourceStatus as string.
func (ResourceStatus) String ¶ added in v0.3.0
func (s ResourceStatus) String() string
String returns the human-readable label for a ResourceStatus.
func (*ResourceStatus) UnmarshalJSON ¶ added in v0.3.0
func (s *ResourceStatus) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler to parse ResourceStatus from string.
type Router ¶ added in v0.2.6
type Router interface {
// SelectPlugins returns plugins that match a resource for a given feature.
// Results are ordered by priority (highest first).
SelectPlugins(ctx context.Context, resource ResourceDescriptor, feature string) []PluginMatch
// ShouldFallback returns true if fallback is enabled for a plugin.
ShouldFallback(pluginName string) bool
}
Router selects appropriate plugins for resources. This interface is implemented by the router package.
type ScopeType ¶ added in v0.2.6
type ScopeType string
ScopeType identifies the category of a budget scope.
const ( // ScopeTypeGlobal represents the global budget that all resources count toward. ScopeTypeGlobal ScopeType = "global" // ScopeTypeProvider represents a per-cloud-provider budget (aws, gcp, azure). ScopeTypeProvider ScopeType = "provider" // ScopeTypeTag represents a tag-based budget with priority ordering. ScopeTypeTag ScopeType = "tag" // ScopeTypeType represents a per-resource-type budget (e.g., aws:ec2/instance). ScopeTypeType ScopeType = "type" )
Budget scope type constants.
type ScopedBudgetEvaluator ¶ added in v0.2.6
type ScopedBudgetEvaluator struct {
// contains filtered or unexported fields
}
ScopedBudgetEvaluator provides methods for evaluating scoped budgets.
func NewScopedBudgetEvaluator ¶ added in v0.2.6
func NewScopedBudgetEvaluator(cfg *config.BudgetsConfig) *ScopedBudgetEvaluator
NewScopedBudgetEvaluator creates a new evaluator for the given configuration. Note: Invalid tag selectors are logged as warnings and skipped. The configuration should be validated with BudgetsConfig.Validate() before creating an evaluator to catch syntax errors early. A future enhancement may return an error for invalid selectors to fail fast on configuration errors.
func (*ScopedBudgetEvaluator) AllocateCostToProvider ¶ added in v0.2.6
func (e *ScopedBudgetEvaluator) AllocateCostToProvider( ctx context.Context, resourceType string, cost float64, ) *BudgetAllocation
AllocateCostToProvider allocates a resource's cost to its provider budget. Returns a BudgetAllocation with provider scope if a matching budget exists.
func (*ScopedBudgetEvaluator) AllocateCostToTag ¶ added in v0.2.6
func (e *ScopedBudgetEvaluator) AllocateCostToTag( ctx context.Context, resourceType string, tags map[string]string, cost float64, ) *BudgetAllocation
AllocateCostToTag allocates a resource's cost to the highest-priority matching tag budget. Returns a BudgetAllocation with tag scope if a matching budget exists. If multiple tag budgets match with the same priority, warnings are emitted.
func (*ScopedBudgetEvaluator) AllocateCostToType ¶ added in v0.2.6
func (e *ScopedBudgetEvaluator) AllocateCostToType( ctx context.Context, resourceType string, cost float64, ) *BudgetAllocation
AllocateCostToType allocates a resource's cost to its resource type budget. Returns a BudgetAllocation with type scope if a matching budget exists.
func (*ScopedBudgetEvaluator) AllocateCosts ¶ added in v0.2.6
func (e *ScopedBudgetEvaluator) AllocateCosts( ctx context.Context, resourceType string, tags map[string]string, cost float64, ) *BudgetAllocation
AllocateCosts allocates a resource's cost to all applicable budget scopes. This is the main entry point for multi-scope cost allocation. Returns a BudgetAllocation with all scopes that received the cost. Returns an empty allocation (no scopes) if the context is cancelled.
func (*ScopedBudgetEvaluator) GetProviderBudget ¶ added in v0.2.6
func (e *ScopedBudgetEvaluator) GetProviderBudget(provider string) *config.ScopedBudget
GetProviderBudget returns the budget for a provider, or nil if not configured.
func (*ScopedBudgetEvaluator) GetTypeBudget ¶ added in v0.2.6
func (e *ScopedBudgetEvaluator) GetTypeBudget(resourceType string) *config.ScopedBudget
GetTypeBudget returns the budget for a resource type, or nil if not configured.
func (*ScopedBudgetEvaluator) MatchTagBudgets ¶ added in v0.2.6
func (e *ScopedBudgetEvaluator) MatchTagBudgets(_ context.Context, tags map[string]string) []config.TagBudget
MatchTagBudgets returns all tag budgets that match the given tags. Results are returned in priority order (highest first). Uses pre-parsed selectors for efficiency (parsed once in NewScopedBudgetEvaluator).
func (*ScopedBudgetEvaluator) SelectHighestPriorityTagBudget ¶ added in v0.2.6
func (e *ScopedBudgetEvaluator) SelectHighestPriorityTagBudget( ctx context.Context, matches []config.TagBudget, ) (*config.TagBudget, []string)
SelectHighestPriorityTagBudget selects the tag budget with highest priority from matches. Returns nil if no matches provided. Emits a warning if multiple budgets have the same highest priority.
type ScopedBudgetResult ¶ added in v0.2.6
type ScopedBudgetResult struct {
// Global is the global budget status (always present if configured).
Global *ScopedBudgetStatus `json:"global,omitempty"`
// ByProvider maps provider names to their budget statuses.
ByProvider map[string]*ScopedBudgetStatus `json:"by_provider,omitempty"`
// ByTag contains tag budget statuses in priority order.
ByTag []*ScopedBudgetStatus `json:"by_tag,omitempty"`
// ByType maps resource types to their budget statuses.
ByType map[string]*ScopedBudgetStatus `json:"by_type,omitempty"`
// OverallHealth is the worst health status across all scopes.
OverallHealth pbc.BudgetHealthStatus `json:"overall_health"`
// CriticalScopes lists scope identifiers with CRITICAL or EXCEEDED status.
CriticalScopes []string `json:"critical_scopes,omitempty"`
// Allocations contains per-resource allocation details (debug mode only).
Allocations []BudgetAllocation `json:"allocations,omitempty"`
// Warnings contains all warnings generated during evaluation.
Warnings []string `json:"warnings,omitempty"`
}
ScopedBudgetResult contains all evaluated scoped budgets and summaries.
func (*ScopedBudgetResult) AllScopes ¶ added in v0.2.6
func (r *ScopedBudgetResult) AllScopes() []*ScopedBudgetStatus
AllScopes returns all scoped budget statuses in a flat list.
WARNING: The returned slice contains pointers to internal state. Callers MUST NOT modify the returned ScopedBudgetStatus objects. This design is intentional for performance in rendering scenarios. If modification is needed, callers should create their own copies.
func (*ScopedBudgetResult) HasCriticalBudgets ¶ added in v0.2.6
func (r *ScopedBudgetResult) HasCriticalBudgets() bool
HasCriticalBudgets returns true if any budget has CRITICAL or EXCEEDED health status.
func (*ScopedBudgetResult) HasExceededBudgets ¶ added in v0.2.6
func (r *ScopedBudgetResult) HasExceededBudgets() bool
HasExceededBudgets returns true if any budget has EXCEEDED health status.
type ScopedBudgetStatus ¶ added in v0.2.6
type ScopedBudgetStatus struct {
// ScopeType identifies the budget scope category.
ScopeType ScopeType `json:"scope_type"`
// ScopeKey is the identifier within the scope type.
// For provider: "aws", "gcp", etc.
// For tag: "team:platform", "env:prod", etc.
// For type: "aws:ec2/instance", etc.
// For global: empty string.
ScopeKey string `json:"scope_key,omitempty"`
// Budget is the configured budget for this scope.
Budget config.ScopedBudget `json:"budget"`
// CurrentSpend is the total cost allocated to this scope.
CurrentSpend float64 `json:"current_spend"`
// Percentage is CurrentSpend / Budget.Amount * 100.
Percentage float64 `json:"percentage"`
// ForecastedSpend is the projected end-of-period spend.
ForecastedSpend float64 `json:"forecasted_spend,omitempty"`
// ForecastPercentage is ForecastedSpend / Budget.Amount * 100.
ForecastPercentage float64 `json:"forecast_percentage,omitempty"`
// Health is the overall health status (OK, WARNING, CRITICAL, EXCEEDED).
Health pbc.BudgetHealthStatus `json:"health"`
// Alerts is the list of evaluated threshold statuses.
Alerts []ThresholdStatus `json:"alerts,omitempty"`
// MatchedResources is the count of resources allocated to this scope.
MatchedResources int `json:"matched_resources,omitempty"`
// Currency is the budget currency for display.
Currency string `json:"currency,omitempty"`
}
ScopedBudgetStatus represents the evaluated state of a scoped budget.
func CalculateProviderBudgetStatus ¶ added in v0.2.6
func CalculateProviderBudgetStatus( provider string, budget *config.ScopedBudget, currentSpend float64, ) *ScopedBudgetStatus
CalculateProviderBudgetStatus calculates the budget status for a provider scope.
func CalculateTagBudgetStatus ¶ added in v0.2.6
func CalculateTagBudgetStatus( tagBudget *config.TagBudget, currentSpend float64, ) *ScopedBudgetStatus
CalculateTagBudgetStatus calculates the budget status for a tag scope.
func CalculateTypeBudgetStatus ¶ added in v0.2.6
func CalculateTypeBudgetStatus( resourceType string, budget *config.ScopedBudget, currentSpend float64, ) *ScopedBudgetStatus
CalculateTypeBudgetStatus calculates the budget status for a resource type scope.
func (*ScopedBudgetStatus) HasExceededAlerts ¶ added in v0.2.6
func (s *ScopedBudgetStatus) HasExceededAlerts() bool
HasExceededAlerts returns true if any alert has EXCEEDED status.
func (*ScopedBudgetStatus) IsOverBudget ¶ added in v0.2.6
func (s *ScopedBudgetStatus) IsOverBudget() bool
IsOverBudget returns true if current spend exceeds the budget amount.
func (*ScopedBudgetStatus) ScopeIdentifier ¶ added in v0.2.6
func (s *ScopedBudgetStatus) ScopeIdentifier() string
ScopeIdentifier returns a string identifier for this scope in format "type:key".
type SpecLoader ¶
SpecLoader is an interface for loading pricing specifications from local YAML files.
type StackContext ¶ added in v0.3.0
type StackContext struct {
StackName string `json:"stackName"`
Region string `json:"region,omitempty"`
TimeWindow DateRange `json:"timeWindow"`
HasChanges bool `json:"hasChanges"`
TotalResources int `json:"totalResources"`
PendingChanges int `json:"pendingChanges"`
GeneratedAt time.Time `json:"generatedAt,omitempty"`
// IsStateOnly is true when no pulumi preview was run; costs reflect the current state only.
IsStateOnly bool `json:"isStateOnly,omitempty"`
// BudgetHealth provides a stack-level budget health summary for JSON output.
BudgetHealth *BudgetHealthSummary `json:"budgetHealth,omitempty"`
}
StackContext provides metadata about the Pulumi stack being analysed in the overview, including change detection information.
func (*StackContext) Validate ¶ added in v0.3.0
func (s *StackContext) Validate() error
Validate checks that the StackContext fields are well-formed.
type StateCostInput ¶
type StateCostInput struct {
Resource ResourceDescriptor
HourlyRate float64
CreatedAt time.Time
IsExternal bool
}
StateCostInput contains input for state-based cost calculation.
func (*StateCostInput) Validate ¶
func (s *StateCostInput) Validate() error
Validate checks that the StateCostInput has valid fields.
type StateCostResult ¶
StateCostResult contains the result of state-based cost calculation.
func CalculateStateCost ¶
func CalculateStateCost(input StateCostInput, referenceTime time.Time) StateCostResult
CalculateStateCost calculates the estimated cost of a resource based on its runtime. The cost is calculated as: hourly_rate × runtime.Hours() where runtime = referenceTime - CreatedAt.
For imported resources (IsExternal=true), a warning note is added since the Created timestamp reflects import time, not actual creation.
Parameters:
- input: StateCostInput with resource details, hourly rate, and creation time
- referenceTime: The time to calculate runtime against (usually time.Now())
Returns StateCostResult with TotalCost, RuntimeHours, and any warning Notes.
type StateResource ¶ added in v0.3.0
type StateResource struct {
URN string `json:"urn,omitempty"`
Type string `json:"type,omitempty"`
ID string `json:"id,omitempty"`
Custom bool `json:"custom,omitempty"`
Properties map[string]interface{} `json:"properties,omitempty"`
// CreatedAt tracks when the resource was first added to Pulumi state.
// Available since Pulumi v3.60.0; nil for older state files.
// JSON key is "createdAt" (not "created" as in the Pulumi state format);
// conversion happens in cli.convertStateResources.
CreatedAt *time.Time `json:"createdAt,omitempty"`
}
StateResource represents a resource from Pulumi state for overview merging. This is a lightweight projection of ingest.StackExportResource to avoid an import cycle (ingest already imports engine).
type StructuredError ¶ added in v0.3.0
type StructuredError struct {
Code string `json:"code"`
Message string `json:"message"`
ResourceType string `json:"resourceType"`
}
StructuredError is a machine-readable error representation included in JSON/NDJSON output so AI agents can programmatically categorize errors without parsing the human-readable Notes field.
type SustainabilityMetric ¶
SustainabilityMetric represents a single sustainability impact measurement.
type ThresholdEvaluationResult ¶ added in v0.2.5
type ThresholdEvaluationResult struct {
Threshold *pbc.BudgetThreshold // Original threshold
Triggered bool // Whether threshold was crossed
TriggeredAt time.Time // When triggered (zero if not)
SpendType string // "actual" or "forecasted"
}
ThresholdEvaluationResult contains evaluated threshold state.
func EvaluateThresholds ¶ added in v0.2.5
func EvaluateThresholds( ctx context.Context, budget *pbc.Budget, currentSpend float64, forecastedSpend float64, ) []ThresholdEvaluationResult
EvaluateThresholds checks which thresholds have been triggered.
Behavior:
- Evaluates against current spend for ACTUAL thresholds
- Evaluates against forecasted spend for FORECASTED thresholds
- Sets Triggered=true and TriggeredAt for crossed thresholds
- Returns all thresholds with updated triggered status
- Preserves existing TriggeredAt timestamp if already triggered
type ThresholdStatus ¶ added in v0.2.4
type ThresholdStatus struct {
// Threshold is the configured threshold percentage (e.g., 80.0 for 80%).
Threshold float64
// Type is the alert type ("actual" or "forecasted").
Type config.AlertType
// Status is the evaluation result: OK, APPROACHING, or EXCEEDED.
Status ThresholdStatusValue
}
ThresholdStatus represents the status of an individual alert threshold.
type ThresholdStatusValue ¶ added in v0.2.4
type ThresholdStatusValue string
ThresholdStatusValue represents the status of a threshold evaluation.
const ( // ThresholdStatusOK indicates the threshold has not been reached. ThresholdStatusOK ThresholdStatusValue = "OK" // ThresholdStatusApproaching indicates spending is approaching the threshold (within 5%). ThresholdStatusApproaching ThresholdStatusValue = "APPROACHING" // ThresholdStatusExceeded indicates the threshold has been exceeded. ThresholdStatusExceeded ThresholdStatusValue = "EXCEEDED" )
Threshold status values for budget alert evaluation.
type UndismissResult ¶ added in v0.3.0
type UndismissResult struct {
// RecommendationID is the ID of the undismissed recommendation.
RecommendationID string `json:"recommendationId"`
// WasDismissed is false if the recommendation wasn't dismissed.
WasDismissed bool `json:"wasDismissed"`
// Message provides information about the undismiss operation.
Message string `json:"message,omitempty"`
}
UndismissResult contains the outcome of an undismiss operation.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package batch provides utilities for processing large datasets in fixed-size batches.
|
Package batch provides utilities for processing large datasets in fixed-size batches. |
|
Package cache provides BoltDB-backed caching with TTL expiration for cost query results.
|
Package cache provides BoltDB-backed caching with TTL expiration for cost query results. |