Documentation
¶
Overview ¶
Package spendguard refuses new deploys of an app whose spend cap or cost budget is in block_deploys mode and has reached 100% this month. It reads the state the cap controllers already keep, so it is deterministic and needs no usage query on the deploy path.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrDeploysBlocked = errors.New("deploys are blocked by a spend cap")
ErrDeploysBlocked is wrapped by every refusal.
Functions ¶
This section is empty.
Types ¶
type BlockedError ¶
BlockedError is returned when a deploy is refused.
func (*BlockedError) Error ¶
func (e *BlockedError) Error() string
func (*BlockedError) Unwrap ¶
func (e *BlockedError) Unwrap() error
Unwrap lets callers match ErrDeploysBlocked.
type Guard ¶
type Guard struct {
Store Store
// ProjectOf maps an app to its project id; nil or "" means no project.
ProjectOf func(ctx context.Context, app string) (string, error)
Logger *slog.Logger
Now func() time.Time
}
Guard decides whether a deploy may proceed.
func (*Guard) BeforeDeploy ¶
BeforeDeploy refuses a new deploy of a blocked app. A failure to read the guard state lets the deploy through, because a cost guard must never stop an app from shipping a fix on a storage hiccup.
type Status ¶
type Status struct {
Blocked bool `json:"blocked"`
Allowed bool `json:"allowed"`
Reasons []Reason `json:"reasons"`
Override *spendcap.Override `json:"override,omitempty"`
}
Status is the guard's verdict for one app.
type Store ¶
type Store interface {
GetSpendCap(ctx context.Context, app string) (spendcap.Cap, error)
GetSpendCapState(ctx context.Context, app string) (spendcap.State, bool, error)
ListCostBudgets(ctx context.Context) ([]costbudget.Budget, error)
GetCostBudgetState(ctx context.Context, id string) (costbudget.State, bool, error)
ActiveSpendOverride(ctx context.Context, app string, now time.Time) (spendcap.Override, bool, error)
}
Store is the persistence the guard reads; *store.DB satisfies it.