usagecost

package
v0.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 22, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package usagecost allocates a real or configured infrastructure price to the apps and databases that consume it, then rolls the result up per project, environment and team. It is pure arithmetic: collection and persistence live with the callers.

Index

Constants

View Source
const (
	ScopeProject = "project"
	ScopeTeam    = "team"
)

Budget scope kinds.

View Source
const (
	LevelWarn     = "warn"
	LevelOver     = "over"
	LevelForecast = "forecast"
)

Budget breach levels, also the alert dedupe key suffix.

View Source
const (
	GroupApp     = "app"
	GroupProject = "project"
	GroupTag     = "tag"
	GroupNode    = "node"
	GroupKind    = "kind"
)

Explorer group dimensions.

View Source
const (
	ConfidenceLow    = "low"
	ConfidenceMedium = "medium"
	ConfidenceHigh   = "high"
)

Forecast confidence labels.

View Source
const (
	DimResource    = "resource"
	DimProject     = "project"
	DimEnvironment = "environment"
	DimTeam        = "team"
)

Group dimensions a report rolls cost up by.

View Source
const (
	ModeUnit = "unit"
	ModeNode = "node"
)

Allocation modes.

View Source
const (
	BasisAvg = "avg"
	BasisP50 = "p50"
)

Usage bases.

View Source
const (
	KindApp      = "app"
	KindDatabase = "database"
	KindIdle     = "idle"
	KindDay      = "day"
)

Row kinds a daily row can describe.

View Source
const DefaultWarnPct = 80.0

DefaultWarnPct is the first alert level when a budget sets none.

View Source
const OtherKey = "other"

OtherKey collapses the groups beyond the top series of an exploration.

View Source
const UnassignedKey = "unassigned"

UnassignedKey groups resources with no project, environment or team.

View Source
const UntaggedKey = "untagged"

UntaggedKey groups resources that carry no tag.

Variables

This section is empty.

Functions

func Buckets

func Buckets(p Period, monthly bool) []string

Buckets lists the day or month keys covered by p, oldest first.

func DayKey

func DayKey(t time.Time) string

DayKey formats a time as the UTC day key stored in snapshots.

func DaysInMonth

func DaysInMonth(t time.Time) int

DaysInMonth returns how many days the month containing t has.

func ElapsedDays

func ElapsedDays(now time.Time) float64

ElapsedDays is how many days of the month containing now have passed.

func ForecastMonthEnd

func ForecastMonthEnd(spent, elapsedDays float64, daysInMonth int, recentDaily []float64) float64

ForecastMonthEnd projects the month's total from spend so far. It uses the mean of the most recent full days when available, so a spike at the start of the month does not dominate, and falls back to the overall average.

func ParseDay

func ParseDay(s string) (time.Time, error)

ParseDay parses a day key.

func StartOfDay

func StartOfDay(t time.Time) time.Time

StartOfDay truncates to 00:00 UTC.

func StartOfMonth

func StartOfMonth(t time.Time) time.Time

StartOfMonth truncates to the first day of the UTC month.

func SumSamples

func SumSamples(samples []Sample, from, to time.Time) float64

SumSamples adds every value inside [from, to), for counter delta metrics.

func UnitPricesFromNode

func UnitPricesFromNode(n NodePrice, cpuWeightPct float64) (cpuCoreHour, memGiBHour float64)

UnitPricesFromNode derives per-core-hour and per-GiB-hour prices from one node's monthly price and capacity, splitting the price by cpuWeightPct.

func ValidGroup

func ValidGroup(g string) bool

ValidGroup reports whether g is a dimension Explore understands.

func WriteCSV

func WriteCSV(w io.Writer, currency string, r Report, labels map[string]string) error

WriteCSV writes the resource breakdown of a report as CSV.

Types

type Band

type Band struct {
	Spent      float64 `json:"spent"`
	Mid        float64 `json:"mid"`
	Low        float64 `json:"low"`
	High       float64 `json:"high"`
	Confidence string  `json:"confidence"`
	Samples    int     `json:"samples"`
}

Band is a month end forecast with an 80 percent range.

func ForecastBand

func ForecastBand(spent, elapsedDays float64, daysInMonth int, recentDaily []float64) Band

ForecastBand projects the month end like ForecastMonthEnd and adds a range from the day to day spread: the remaining days are treated as independent draws, so the band widens with the square root of the days left. With fewer than three full days it falls back to a fixed relative band and reports low confidence, because a spread over one or two samples means nothing.

type Breach

type Breach struct {
	Key   string
	Level string
	Text  string
}

Breach is one crossed budget threshold.

type Budget

type Budget struct {
	ID      string  `json:"id"`
	Scope   string  `json:"scope"`
	ScopeID string  `json:"scope_id"`
	Monthly float64 `json:"monthly"`
	WarnPct float64 `json:"warn_pct"`
}

Budget is a monthly spending limit for a project or a team.

type BudgetStatus

type BudgetStatus struct {
	Budget
	Name        string  `json:"name"`
	Currency    string  `json:"currency"`
	Spent       float64 `json:"spent"`
	Forecast    float64 `json:"forecast"`
	SpentPct    float64 `json:"spent_pct"`
	ForecastPct float64 `json:"forecast_pct"`
	Level       string  `json:"level"`
}

BudgetStatus is a budget with its month to date position.

func NewStatus

func NewStatus(b Budget, name, currency string, spent, forecast float64) BudgetStatus

NewStatus positions a budget against spend and a month end forecast.

func (BudgetStatus) Breaches

func (s BudgetStatus) Breaches() []Breach

Breaches lists which thresholds the status has crossed. Over implies warn, and a forecast overrun is reported only while spend has not already passed the limit, so one budget never produces redundant lines.

type DailyCost

type DailyCost struct {
	DailyUsage
	ComputeCost float64 `json:"compute_cost"`
	StorageCost float64 `json:"storage_cost"`
	EgressCost  float64 `json:"egress_cost"`
	IdleCost    float64 `json:"idle_cost"`
}

DailyCost is a DailyUsage with its allocated cost components.

func (DailyCost) Total

func (c DailyCost) Total() float64

Total is the full cost attributed to the row.

type DailyUsage

type DailyUsage struct {
	Day           string  `json:"day"`
	Kind          string  `json:"kind"`
	Name          string  `json:"name"`
	TeamID        string  `json:"team_id"`
	ProjectID     string  `json:"project_id"`
	EnvironmentID string  `json:"environment_id"`
	NodeID        string  `json:"node_id"`
	UpSeconds     float64 `json:"up_seconds"`
	CPUAvgCores   float64 `json:"cpu_avg_cores"`
	CPUP50Cores   float64 `json:"cpu_p50_cores"`
	MemAvgBytes   float64 `json:"mem_avg_bytes"`
	MemP50Bytes   float64 `json:"mem_p50_bytes"`
	StorageBytes  float64 `json:"storage_bytes"`
	EgressBytes   float64 `json:"egress_bytes"`
}

DailyUsage is one resource's measured usage for one UTC day.

func UsageFromSamples

func UsageFromSamples(base DailyUsage, cpuPercent, memBytes []Sample, from, to time.Time) DailyUsage

UsageFromSamples builds a resource's daily usage from its CPU percent and memory byte series. CPU percent is of one core, so 100 is one core.

type DayInput

type DayInput struct {
	Day      time.Time
	Settings Settings
	Nodes    map[string]NodePrice
	Usage    []DailyUsage
	// Fraction is the share of the day that has elapsed, 1 for a complete
	// day. A partial day scales the node's daily price the same way.
	Fraction float64
}

DayInput is everything allocation needs for one UTC day.

type DayResult

type DayResult struct {
	Rows []DailyCost
	// Unpriced lists node ids whose resources could not be priced.
	Unpriced []string
}

DayResult is the allocation of one day.

func AllocateDay

func AllocateDay(in DayInput) DayResult

AllocateDay prices one day of usage. Storage and egress unit prices apply in both modes; compute is priced per unit or taken from node bills.

type Exploration

type Exploration struct {
	GroupBy  string    `json:"group_by"`
	Buckets  []string  `json:"buckets"`
	Trend    []float64 `json:"trend"`
	Total    float64   `json:"total"`
	Previous float64   `json:"previous"`
	DeltaPct *float64  `json:"delta_pct,omitempty"`
	Series   []Series  `json:"series"`
	// Movers are the groups whose cost grew most against the previous period.
	Movers []Series `json:"movers"`
}

Exploration is the result of Explore.

func Explore

func Explore(in ExploreInput) Exploration

Explore aggregates cost per bucket and group, ranks the groups, and finds the largest increases against the previous period.

type ExploreInput

type ExploreInput struct {
	Period   Period
	Previous Period
	Rows     []DailyCost
	Prev     []DailyCost
	GroupBy  string
	// Monthly buckets by calendar month instead of by day.
	Monthly bool
	// TagsOf maps "kind:name" to tag names, used by the tag dimension.
	TagsOf map[string][]string
}

ExploreInput selects what Explore aggregates.

type Group

type Group struct {
	Key           string    `json:"key"`
	Kind          string    `json:"kind,omitempty"`
	Name          string    `json:"name,omitempty"`
	Total         float64   `json:"total"`
	Compute       float64   `json:"compute"`
	Storage       float64   `json:"storage"`
	Egress        float64   `json:"egress"`
	Idle          float64   `json:"idle_share"`
	Previous      float64   `json:"previous"`
	Delta         float64   `json:"delta"`
	DeltaPct      *float64  `json:"delta_pct,omitempty"`
	ProjectID     string    `json:"project_id,omitempty"`
	TeamID        string    `json:"team_id,omitempty"`
	EnvironmentID string    `json:"environment_id,omitempty"`
	Daily         []float64 `json:"daily"`
}

Group is one row of a ranked breakdown.

func RollupTags

func RollupTags(p, prevP Period, rows, prev []DailyCost, tagsOf map[string][]string) []Group

RollupTags ranks cost by tag. tagsOf maps "kind:name" to tag names. A resource with several tags counts in each, so tag totals can exceed the report total.

type Integrated

type Integrated struct {
	Avg float64
	P50 float64
	// UpSeconds is how long the metric was actually reported, so downtime
	// counts as zero rather than as the average of the uptime.
	UpSeconds float64
}

Integrated is a metric reduced over one window.

func Integrate

func Integrate(samples []Sample, from, to time.Time) Integrated

Integrate reduces samples inside [from, to). Each sample covers the gap to the next one, capped at twice the typical interval so a stopped container does not keep accruing.

type NodePrice

type NodePrice struct {
	NodeID       string  `json:"node_id"`
	MonthlyPrice float64 `json:"monthly_price"`
	VCPUs        float64 `json:"vcpus"`
	MemoryGiB    float64 `json:"memory_gib"`
}

NodePrice is one node's monthly bill and capacity.

func (NodePrice) Priced

func (n NodePrice) Priced() bool

Priced reports whether the node can be used for allocation.

type Period

type Period struct {
	From time.Time `json:"from"`
	To   time.Time `json:"to"`
}

Period is a half open [From, To) range of UTC days.

func PreviousPeriod

func PreviousPeriod(p Period, monthToDate bool) Period

PreviousPeriod returns the comparison window: for month to date, the same number of days at the start of last month; otherwise the window of equal length immediately before.

func (Period) Days

func (p Period) Days() int

Days returns the day count of the period.

type Report

type Report struct {
	Period       Period   `json:"period"`
	Previous     Period   `json:"previous"`
	Days         []string `json:"days"`
	Totals       Totals   `json:"totals"`
	Resources    []Group  `json:"resources"`
	Projects     []Group  `json:"projects"`
	Environments []Group  `json:"environments"`
	Teams        []Group  `json:"teams"`
	Movers       []Group  `json:"movers"`
	Tags         []Group  `json:"tags"`
	// Forecast is the projected month end total, set for periods that
	// include the current month's elapsed days.
	Forecast *float64 `json:"forecast,omitempty"`
}

Report is the full showback result for one period.

func Rollup

func Rollup(p, prevP Period, rows, prev []DailyCost, includeIdle bool) Report

Rollup builds a report. rows and prev are allocated daily rows of the period and its comparison window; includeIdle controls whether the unallocated remainder is shown (callers hide it from team scoped users).

type Sample

type Sample struct {
	At    time.Time
	Value float64
}

Sample is one timestamped metric reading.

type Series

type Series struct {
	Key      string    `json:"key"`
	Total    float64   `json:"total"`
	Previous float64   `json:"previous"`
	Delta    float64   `json:"delta"`
	DeltaPct *float64  `json:"delta_pct,omitempty"`
	Values   []float64 `json:"values"`
}

Series is one group's cost over the buckets.

type Settings

type Settings struct {
	Mode            string  `json:"mode"`
	Currency        string  `json:"currency"`
	CPUCoreHour     float64 `json:"cpu_core_hour"`
	MemGiBHour      float64 `json:"mem_gib_hour"`
	StorageGiBMonth float64 `json:"storage_gib_month"`
	EgressGiB       float64 `json:"egress_gib"`
	// IdleSpreadPct is, in node mode, the share of unattributed node cost
	// spread over resources by usage; in unit mode a proportional overhead.
	IdleSpreadPct float64 `json:"idle_spread_pct"`
	Basis         string  `json:"basis"`
	// CPUWeightPct is the part of a node price attributed to CPU, the rest
	// to memory.
	CPUWeightPct float64 `json:"cpu_weight_pct"`
}

Settings is the operator's pricing configuration.

func DefaultSettings

func DefaultSettings() Settings

DefaultSettings is what an unconfigured instance reports: unit mode with zero prices, so nothing is invented until the operator sets prices.

type Totals

type Totals struct {
	Total       float64  `json:"total"`
	Allocated   float64  `json:"allocated"`
	Unallocated float64  `json:"unallocated"`
	Previous    float64  `json:"previous"`
	DeltaPct    *float64 `json:"delta_pct,omitempty"`
}

Totals summarises a report.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL