storage

package
v1.3.9 Latest Latest
Warning

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

Go to latest
Published: May 8, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package storage is the persistence layer used by the agent (pattern catalog, shadow log, service registry) and by the incident service (incident history). One Provider is constructed at boot from `storage:` in config.yaml and passed to every consumer that needs to read or write durable state.

The interface is split into two concerns:

  • Blob: opaque byte slices keyed by short name. Used by the agent catalog and shadow log, both of which already serialize themselves to JSON. Backends translate the name into a file path / Redis key / row.
  • Incident: first-class CRUD-ish operations because the UI lists, filters, and acks incidents.

Backends today:

  • file (pkg/storage/file.go) — production
  • memory (pkg/storage/memory.go) — tests only
  • redis (pkg/storage/redis.go) — config stub, returns ErrUnsupported
  • database (pkg/storage/database.go) — config stub

Index

Constants

View Source
const MaxIncidentsDefault = 1000

MaxIncidentsDefault is the default rolling cap for the file backend. Older records are dropped on SaveIncident when the cap is exceeded.

Variables

View Source
var ErrNotFound = errors.New("storage: not found")

ErrNotFound is returned by Get* methods when the key/id is missing. File and memory backends translate os.ErrNotExist into ErrNotFound so callers can rely on errors.Is(err, storage.ErrNotFound).

View Source
var ErrUnsupported = errors.New("storage: backend not implemented")

ErrUnsupported is returned by the redis/database stub backends.

Functions

This section is empty.

Types

type Config

type Config struct {
	Type     string // file | redis | database (default: file)
	File     FileOptions
	Redis    RedisOptions
	Database DatabaseOptions
}

Config mirrors the root-level `storage:` block in config.yaml. It is kept here (rather than in pkg/config) so tests in this package don't pull in viper.

type DatabaseOptions

type DatabaseOptions struct {
	Driver       string // postgres | mysql | sqlite
	DSN          string
	MaxIncidents int
}

DatabaseOptions mirrors the database sub-block of `storage:`. The database backend is config-only today — instantiating it returns ErrUnsupported. The struct exists so the config layer can validate its shape.

type FileOptions

type FileOptions struct {
	DataDir      string // default "data"
	MaxIncidents int    // default MaxIncidentsDefault
}

FileOptions configures the file backend. Empty fields fall back to sensible defaults.

type IncidentRecord

type IncidentRecord struct {
	ID               string   `json:"id"`
	TeamID           string   `json:"team_id,omitempty"`
	Title            string   `json:"title,omitempty"`
	Source           string   `json:"source,omitempty"`  // "http" | "sns" | "sqs" | ...
	Service          string   `json:"service,omitempty"` // best-effort from payload
	Resolved         bool     `json:"resolved"`
	ChannelsNotified []string `json:"channels_notified,omitempty"`
	OnCallTriggered  bool     `json:"oncall_triggered,omitempty"`
	// NotifyStatus reflects the outcome of the alert fan-out:
	// "pending" — record persisted, fan-out not yet attempted
	// "sent"    — every enabled channel returned success
	// "failed"  — at least one channel returned an error (see NotifyError)
	NotifyStatus string                 `json:"notify_status,omitempty"`
	NotifyError  string                 `json:"notify_error,omitempty"`
	CreatedAt    time.Time              `json:"created_at"`
	AckedAt      *time.Time             `json:"acked_at,omitempty"`
	Content      map[string]interface{} `json:"content,omitempty"`
}

IncidentRecord is the durable shape of an incident. It mirrors the runtime models.Incident plus the audit fields the UI needs (when it happened, who got notified, was it acked, raw payload for debugging).

type Provider

type Provider interface {
	// ReadBlob returns the contents previously written under name.
	// Missing blobs MUST return (nil, nil) — not ErrNotFound — so the
	// agent's "fresh start" path stays a single line.
	ReadBlob(name string) ([]byte, error)
	// WriteBlob atomically replaces the blob stored under name.
	WriteBlob(name string, data []byte) error

	// SaveIncident appends a new incident to the store. Subsequent
	// SaveIncident calls with the same ID overwrite the existing record
	// (used by the ack path). Implementations are responsible for
	// trimming the history to a sane upper bound — the file backend
	// caps at MaxIncidents.
	SaveIncident(rec *IncidentRecord) error
	// UpdateIncidentAck stamps an existing incident as acknowledged.
	// Returns ErrNotFound when the id is unknown.
	UpdateIncidentAck(id string, ackedAt time.Time) error
	// GetIncident returns one incident or ErrNotFound.
	GetIncident(id string) (*IncidentRecord, error)
	// ListIncidents returns the most recent incidents, newest first.
	// limit <= 0 returns the full window.
	ListIncidents(limit int) ([]*IncidentRecord, error)

	// Close releases any underlying resources (file handles, redis
	// connections, db pools). Calling Close on a closed provider is a
	// no-op.
	Close() error
}

Provider is the storage interface used by the agent and incident service.

func New

func New(c Config) (Provider, error)

New constructs the configured backend.

func NewDatabase

func NewDatabase(_ DatabaseOptions) (Provider, error)

NewDatabase is a placeholder that returns ErrUnsupported.

func NewFile

func NewFile(opts FileOptions) (Provider, error)

NewFile returns a Provider backed by the local filesystem.

func NewMemory

func NewMemory() Provider

NewMemory returns a Provider that keeps all state in memory. Intended for tests; never select via the config factory.

func NewRedis

func NewRedis(_ RedisOptions) (Provider, error)

NewRedis is a placeholder that returns ErrUnsupported. The redis backend will be implemented alongside its first dependency.

type RedisOptions

type RedisOptions struct {
	Host               string
	Port               int
	Password           string
	DB                 int
	InsecureSkipVerify bool
	KeyPrefix          string
	MaxIncidents       int
}

RedisOptions mirrors the redis sub-block of `storage:`. The redis backend is config-only today — instantiating it returns ErrUnsupported. The struct exists so the config layer can validate its shape.

Jump to

Keyboard shortcuts

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