janitor

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: 14 Imported by: 0

Documentation

Overview

Package janitor runs bounded, level-triggered cleanup passes on the control plane's own node: unused Docker data, build cache over budget, orphaned volumes (quarantined first) and old failed deploy attempts.

Index

Constants

View Source
const (
	ActionRemoved         = "removed"
	ActionWouldRemove     = "would_remove"
	ActionQuarantined     = "quarantined"
	ActionWouldQuarantine = "would_quarantine"
	ActionKept            = "kept"
	StatusRan             = "ran"
	StatusSkipped         = "skipped"
	StatusFailed          = "failed"
	TriggerScheduled      = "scheduled"
	TriggerManual         = "manual"
	TriggerDiskFix        = "disk_fix"
	TriggerNodeGuardrail  = "node_guardrail"
)

Item actions a stage reports.

Variables

AllStages lists every stage in the order a pass runs them.

View Source
var ErrBusy = errors.New("janitor: a cleanup pass is already running")

ErrBusy means another pass is already running.

Functions

func SavePolicy

func SavePolicy(ctx context.Context, s PolicyStore, p Policy, actor string, now time.Time) error

SavePolicy validates and stores p.

Types

type Controller

type Controller struct {
	// contains filtered or unexported fields
}

Controller runs scheduled passes from the reconcile loop.

func NewController

func NewController(e *Engine) *Controller

NewController wraps e.

func (*Controller) Name

func (c *Controller) Name() string

Name implements reconcile.Controller.

func (*Controller) Reconcile

func (c *Controller) Reconcile(ctx context.Context) (reconcile.Result, error)

Reconcile implements reconcile.Controller.

type Deps

type Deps struct {
	Store  Store
	Docker Docker
	// DiskFree reports free and total bytes of the volume Docker data lives on.
	DiskFree func(ctx context.Context) (free, total int64, err error)
	// DesiredContainers lists container names the reconciler still wants, which a prune must keep.
	DesiredContainers func(ctx context.Context) ([]string, error)
	// OrphanVolumes lists named volumes no desired state or container references.
	OrphanVolumes func(ctx context.Context) ([]docker.NamedVolume, error)
	RemoveVolume  func(ctx context.Context, name string) error
	Logger        *slog.Logger
	Now           func() time.Time
}

Deps are the engine's collaborators.

type Docker

type Docker interface {
	DiskUsage(ctx context.Context) (docker.DiskUsage, error)
	ListAllImages(ctx context.Context) ([]docker.ImageRecord, error)
	PruneContainers(ctx context.Context, keep []string) (docker.PruneContainersResult, error)
	PruneDanglingImages(ctx context.Context) (docker.PruneImagesResult, error)
	PruneAnonymousVolumes(ctx context.Context) (docker.PruneVolumesResult, error)
	BuildCacheRecords(ctx context.Context) ([]docker.BuildCacheRecord, error)
	PruneBuildCacheOlderThan(ctx context.Context, olderThan time.Duration) (docker.PruneBuildCacheResult, error)
}

Docker is the daemon surface a pass uses. *docker.Client satisfies it.

type Engine

type Engine struct {
	Deps
	// contains filtered or unexported fields
}

Engine runs cleanup passes one at a time.

func New

func New(d Deps) *Engine

New builds an Engine.

func (*Engine) Due

func (e *Engine) Due(p Policy) bool

Due reports whether a scheduled pass should run now.

func (*Engine) History

func (e *Engine) History(ctx context.Context, limit int) ([]Report, error)

History returns the newest runs.

func (*Engine) Run

func (e *Engine) Run(ctx context.Context, opts Options) (Report, error)

Run executes one pass under the saved policy and records it in history.

type Item

type Item struct {
	Name      string `json:"name"`
	Detail    string `json:"detail,omitempty"`
	SizeBytes int64  `json:"size_bytes"`
	Action    string `json:"action"`
}

Item is one thing a stage removed, would remove, or deliberately kept.

type Options

type Options struct {
	Trigger string
	DryRun  bool
	// Force runs the size-driven stages even when free space is above budget.
	Force bool
	// Stages limits the pass to these stages; empty runs every stage the policy enables.
	Stages []Stage
}

Options say how one pass runs.

type Policy

type Policy struct {
	Enabled bool `json:"enabled"`
	// DryRun makes scheduled passes report without removing anything.
	DryRun          bool `json:"dry_run"`
	IntervalMinutes int  `json:"interval_minutes"`
	// DiskFreeBudgetPercent is the free space below which size-driven stages run on a schedule.
	DiskFreeBudgetPercent float64 `json:"disk_free_budget_percent"`
	BuildCacheBudgetMB    int64   `json:"build_cache_budget_mb"`
	BuildCacheMaxAgeHours int     `json:"build_cache_max_age_hours"`
	// VolumeQuarantineDays is how long an orphaned volume is held before it may be deleted.
	VolumeQuarantineDays int `json:"volume_quarantine_days"`
	// DeleteOrphanVolumes lets a pass delete volumes whose quarantine ended; off keeps them listed only.
	DeleteOrphanVolumes  bool    `json:"delete_orphan_volumes"`
	AttemptRetentionDays int     `json:"attempt_retention_days"`
	AttemptKeepPerApp    int     `json:"attempt_keep_per_app"`
	DisabledStages       []Stage `json:"disabled_stages"`
}

Policy is the operator-editable cleanup configuration.

func DefaultPolicy

func DefaultPolicy() Policy

DefaultPolicy reports only until an operator turns removal on.

func LoadPolicy

func LoadPolicy(ctx context.Context, s PolicyStore) (Policy, error)

LoadPolicy returns the saved policy, or the default when none was saved.

func (Policy) Validate

func (p Policy) Validate() error

Validate rejects values that would make a pass unsafe or meaningless.

type PolicyStore

type PolicyStore interface {
	GetJanitorPolicy(ctx context.Context) (string, bool, error)
	SetJanitorPolicy(ctx context.Context, body, actor string, now time.Time) error
}

PolicyStore persists the policy as JSON.

type Report

type Report struct {
	ID             string        `json:"id"`
	StartedAt      time.Time     `json:"started_at"`
	Trigger        string        `json:"trigger"`
	DryRun         bool          `json:"dry_run"`
	FreePercent    float64       `json:"free_percent"`
	Pressure       bool          `json:"pressure"`
	Stages         []StageResult `json:"stages"`
	ReclaimedBytes int64         `json:"reclaimed_bytes"`
	Error          string        `json:"error,omitempty"`
}

Report is one pass.

type Stage

type Stage string

Stage names one cleanup category.

const (
	StageContainers Stage = "containers"
	StageImages     Stage = "images"
	StageBuildCache Stage = "build_cache"
	StageVolumes    Stage = "volumes"
	StageAttempts   Stage = "deploy_attempts"
)

Cleanup stages.

type StageResult

type StageResult struct {
	Stage          Stage  `json:"stage"`
	Status         string `json:"status"`
	Reason         string `json:"reason,omitempty"`
	Items          []Item `json:"items"`
	ReclaimedBytes int64  `json:"reclaimed_bytes"`
	Error          string `json:"error,omitempty"`
}

StageResult is what one stage did in a pass.

type Store

type Store interface {
	PolicyStore
	InsertJanitorRun(ctx context.Context, r store.JanitorRun, keep int) error
	ListJanitorRuns(ctx context.Context, limit int) ([]store.JanitorRun, error)
	UpsertJanitorQuarantine(ctx context.Context, q store.JanitorQuarantine) error
	ListJanitorQuarantine(ctx context.Context, kind string) ([]store.JanitorQuarantine, error)
	DeleteJanitorQuarantine(ctx context.Context, kind, name string) error
	ListPrunableDeployAttempts(ctx context.Context, cutoff time.Time, keep, limit int) ([]store.PrunableAttempt, error)
	DeleteDeployAttempts(ctx context.Context, ids []string) error
}

Store is the persistence a pass needs. *store.DB satisfies it.

Jump to

Keyboard shortcuts

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