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
- Variables
- func Clear(ctx context.Context, st port.State) error
- func Lift(ctx context.Context, tables []Table, force bool) (lifted int, forced bool, err error)
- func ReadOnly(ctx context.Context) bool
- func Refuse(w http.ResponseWriter, r *http.Request, err error)
- func WithReadOnly(ctx context.Context) context.Context
- func Write(ctx context.Context, st port.State, f Flag) error
- type Error
- type Flag
- type Gate
- func (g *Gate) Active(ctx context.Context) (Flag, bool)
- func (g *Gate) Flag(ctx context.Context) (Flag, error)
- func (g *Gate) Middleware(serves func(*http.Request) bool, next http.Handler) http.Handler
- func (g *Gate) MiddlewareFrozen(serves, frozen func(*http.Request) bool, next http.Handler) http.Handler
- func (g *Gate) Writable(ctx context.Context) error
- type Option
- type Set
- type Status
- type Table
Constants ¶
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.
const Code = "maintenance"
Code is the name a refusal has where a caller answers it by name.
const DefaultTTL = 10 * time.Second
DefaultTTL is how long a gate trusts what it read.
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.
const RetryAfter = 30
RetryAfter is the Retry-After a refused HTTP request carries, in seconds.
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 ¶
var ErrMaintenance = errors.New("maintenance: the service is under maintenance")
ErrMaintenance is a module that is under maintenance.
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.
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 Lift ¶
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 ¶
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 ¶
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.
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.
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 ¶
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.
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 ¶
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 ¶
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 ¶
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.
type Set ¶
Set is the gates of the modules one process serves, by module.
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.