maintenance

package
v1.76.2 Latest Latest
Warning

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

Go to latest
Published: Oct 12, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package maintenance is the flag that says a module is being restored, and the gate every module reads before it writes (docs/decisions/0072, decision 9).

The flag is one record in each module's OWN table, Key, so a module needs no grant on another module's table to read it. Only the restore function and an operator (`sluis maintenance`, with the grant of the Pulumi library's MaintenanceOperatorStatements) write it: the library denies every function role a write of the `maintenance` partition of its own table. A migration freeze (StateMigration) also serves a read-only token path (Gate.MiddlewareFrozen). A module reads it through a Gate, which caches the answer for a short time (a Lambda function reads it at most every DefaultTTL however many requests it serves) and refuses writes while it is set. Reads of what was already issued keep working.

A flag that cannot be read fails CLOSED for a write and OPEN for a read: a module that cannot tell whether a restore is running must not write over it, and need not stop verifying tokens.

Index

Constants

View Source
const (
	// StateRestoring is a restore in progress, or one that failed and left the
	// module closed until an operator looks.
	StateRestoring = "restoring"
	// StateMigration is a migration freeze, set by `sluis maintenance on`: the
	// installation refuses every write of state, and still answers what deploy
	// tooling and verification read ([Gate.MiddlewareFrozen], [ReadOnly]).
	StateMigration = "migration"
)

The values of Flag.State.

View Source
const Code = "maintenance"

Code is the name a refusal has where a caller answers it by name.

View Source
const DefaultTTL = 10 * time.Second

DefaultTTL is how long a gate trusts what it read.

View Source
const Key = "rec.maintenance"

Key is the State key of the flag, in every module's table. The layout puts it in the kind `maintenance`, which the Pulumi library keys its deny on.

View Source
const RetryAfter = 30

RetryAfter is the Retry-After a refused HTTP request carries, in seconds.

View Source
const Settle = DefaultTTL + 5*time.Second

Settle is how long after a flag is written every reader has seen it: a gate trusts what it read for DefaultTTL, and this adds a margin.

Variables

View Source
var ErrMaintenance = errors.New("maintenance: the service is under maintenance")

ErrMaintenance is a module that is under maintenance.

View Source
var ErrRestoring = errors.New("maintenance: a restore holds the flag; lifting it mid-restore serves a half-written installation. " +
	"Run `sluis restore start --resume`, or pass --force when the restore is abandoned")

ErrRestoring is a flag a restore holds, which `off` lifts only when forced.

View Source
var ErrUnknown = errors.New("maintenance: the flag could not be read, so writes are refused")

ErrUnknown is a flag that could not be read, so a write is refused.

Functions

func Clear

func Clear(ctx context.Context, st port.State) error

Clear lifts the flag in st.

func Lift

func Lift(ctx context.Context, tables []Table, force bool) (lifted int, forced bool, err error)

Lift clears the flag of every table. A flag a restore holds is lifted only with force. It reports how many tables carried a flag and whether a restore's was among them.

func ReadOnly

func ReadOnly(ctx context.Context) bool

ReadOnly reports whether ctx was marked by WithReadOnly.

func Refuse

func Refuse(w http.ResponseWriter, r *http.Request, err error)

Refuse writes the answer to a refused HTTP request: 503 with Retry-After, in JSON or, to a browser, a short page.

func WithReadOnly

func WithReadOnly(ctx context.Context) context.Context

WithReadOnly marks ctx as the context of a request that must change no state: a store that would record a token, rotate a refresh token or note a client among a sign-in's clients leaves that out.

func Write

func Write(ctx context.Context, st port.State, f Flag) error

Write sets the flag in st. It is what the restore function calls, with the restore role's own credentials; no other role can write it.

Types

type Error

type Error struct {
	// Flag is what was read; zero when Unknown.
	Flag Flag
	// Err is why the flag could not be read, when it could not.
	Err error
}

Error is the refusal of a write.

func (*Error) Error

func (e *Error) Error() string

Error implements error.

func (*Error) Is

func (e *Error) Is(target error) bool

Is makes the refusal match its sentinel.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap exposes the read error.

type Flag

type Flag struct {
	State  string    `json:"state"`
	Since  time.Time `json:"since"`
	By     string    `json:"by,omitempty"`
	Reason string    `json:"reason,omitempty"`
}

Flag is the record.

func Read

func Read(ctx context.Context, st port.StateReader) (Flag, error)

Read is the flag in st as it is now, with no cache. A read that fails is an error, since an operator asking must not be told "off" by a failure.

func (Flag) Active

func (f Flag) Active() bool

Active reports whether the flag is set.

func (Flag) Frozen

func (f Flag) Frozen() bool

Frozen reports whether the flag is a migration freeze, the one state in which a read-only token path is served.

func (Flag) Quiet

func (f Flag) Quiet(now time.Time) time.Duration

Quiet reports how much longer a caller must wait before it can treat the writers of a module as stopped: zero once the flag has been set for Settle.

type Gate

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

Gate reads the flag of one module's table. The zero value is not usable; a nil *Gate is: it never refuses, which is a process with no shared state.

func New

func New(src port.StateReader, opts ...Option) *Gate

New makes a gate over the module's own State (or a read-only view of it).

func (*Gate) Active

func (g *Gate) Active(ctx context.Context) (Flag, bool)

Active is the flag if it is set. A read that fails is "not set": it is for what a module still serves in maintenance, and for showing the state.

func (*Gate) Flag

func (g *Gate) Flag(ctx context.Context) (Flag, error)

Flag is the flag as read, from the cache when it is younger than the TTL. An absent record is the zero Flag. A read that fails is not cached, so the next call reads again.

func (*Gate) Middleware

func (g *Gate) Middleware(serves func(*http.Request) bool, next http.Handler) http.Handler

Middleware refuses, while the module is under maintenance, every request that serves does not name. A request serves names passes through with no read of the flag.

func (*Gate) MiddlewareFrozen

func (g *Gate) MiddlewareFrozen(serves, frozen func(*http.Request) bool, next http.Handler) http.Handler

MiddlewareFrozen is Gate.Middleware with a second list. While the flag is a migration freeze, a request that frozen names is served too, with its context marked WithReadOnly and a Retry-After on any 5xx it answers: it is a request that reads (the token exchange, a refresh that is not rotated). While the flag is any other state, frozen is not asked.

func (*Gate) Writable

func (g *Gate) Writable(ctx context.Context) error

Writable is nil when the module may write, and an *Error when it is under maintenance or the flag cannot be read.

type Option

type Option func(*Gate)

Option adjusts a gate.

func WithClock

func WithClock(now func() time.Time) Option

WithClock sets the clock, for a test.

func WithTTL

func WithTTL(d time.Duration) Option

WithTTL sets how long a read is trusted. Zero or less is DefaultTTL.

type Set

type Set map[port.Module]*Gate

Set is the gates of the modules one process serves, by module.

func (Set) Any

func (s Set) Any(ctx context.Context) (port.Module, Flag, bool)

Any is the first module under maintenance, in the layout's module order.

func (Set) Of

func (s Set) Of(m port.Module) *Gate

Of is the module's gate; nil (which never refuses) when the process has none.

type Status

type Status struct {
	Table string `json:"table"`
	Flag  Flag   `json:"flag"`
	On    bool   `json:"on"`
	Err   string `json:"error,omitempty"`
}

Status is the flag of one table as read now. Err is set when it could not be read, in which case the table is neither on nor off.

func Freeze

func Freeze(ctx context.Context, tables []Table, by, reason string, now time.Time) ([]Status, error)

Freeze sets StateMigration on every table. A table that is already frozen keeps its own Since, By and Reason, so that setting it twice is not a second freeze. A table a restore holds is not touched, and nothing is written to any table before every one has been read.

func Statuses

func Statuses(ctx context.Context, tables []Table) []Status

Statuses reads the flag of every table, with no cache.

type Table

type Table struct {
	// Name is the module, or "all" for the one table of layout v4.
	Name  string
	State port.State
}

Table is one State that holds a flag, and the name it is shown by.

Jump to

Keyboard shortcuts

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