Documentation
¶
Overview ¶
Package flag holds the flag domain types and the storage contract.
Index ¶
- Constants
- Variables
- func NewEnvChange(cfg EnvConfig, reason string) any
- func ValidKey(key string) bool
- func ValidateMeta(key, name string) error
- func ValidatePermanentReason(reason string) (string, error)
- type Activity
- type ChangeRequest
- type EmergencyChange
- type EnvConfig
- type Environment
- type Evaluation
- type Flag
- type Meta
- type PermanentSnapshot
- type RequestFilter
- type RequestSnapshot
- type RequestStatus
- type StaleReason
- type Staleness
- type StewardSnapshot
- type Store
Constants ¶
const ( ActionCreated = "flag.created" ActionUpdated = "flag.updated" ActionEnvUpdated = "flag.environment_updated" ActionArchived = "flag.archived" ActionSteward = "flag.steward_changed" ActionPermanent = "flag.permanent_changed" ActionEnvironmentCreated = "environment.created" ActionEnvironmentUpdated = "environment.updated" )
const ( ActionRequestCreated = "change_request.created" ActionRequestApproved = "change_request.approved" ActionRequestRejected = "change_request.rejected" ActionRequestCancelled = "change_request.cancelled" ActionRequestExpired = "change_request.expired" )
const RequestTTL = 7 * 24 * time.Hour
RequestTTL is how long a change request waits for review.
Variables ¶
var ( ErrNotFound = errs.ErrNotFound ErrConflict = errs.ErrConflict ErrInvalid = errs.ErrInvalid )
Aliases so callers can match flag errors without importing errs.
Functions ¶
func NewEnvChange ¶
NewEnvChange returns the audit record for setting cfg: the config itself, or an EmergencyChange when there's a reason.
func ValidateMeta ¶
func ValidatePermanentReason ¶
ValidatePermanentReason trims a reason and checks its length.
Types ¶
type Activity ¶
type Activity struct {
ChangedAt time.Time
// EvaluatedAt is the last evaluation, or when tracking began if the
// flag hasn't been evaluated since.
EvaluatedAt time.Time
}
Activity is a flag's recent history in one environment.
type ChangeRequest ¶
type ChangeRequest struct {
ID int64
FlagKey string
Environment string
RequestedBy string
Reason string
// Base is the environment's config when the request was made.
// Approval applies Proposed only if the environment still has Base.
Base EnvConfig
Proposed EnvConfig
Status RequestStatus
ReviewedBy string
ReviewComment string
CreatedAt time.Time
ExpiresAt time.Time
ResolvedAt *time.Time
}
ChangeRequest proposes a new config for a flag in one environment, for a second person to approve.
type EmergencyChange ¶
EmergencyChange is the audit record of an environment change made with a reason instead of an approved request.
type EnvConfig ¶
type EnvConfig struct {
Enabled bool `json:"enabled"`
RolloutPercentage int `json:"rollout_percentage"`
Rules []eval.Rule `json:"rules"`
}
EnvConfig is a flag's state in one environment. Rules is never nil when returned by a Store.
type Environment ¶
type Environment struct {
Key string `json:"key"`
Name string `json:"name"`
// Changes to flags in protected environments (prod by default) go
// through change requests; see ChangeRequest.
Protected bool `json:"protected"`
}
func (Environment) Validate ¶
func (e Environment) Validate() error
type Evaluation ¶
Evaluation records that a flag was evaluated in an environment.
type Flag ¶
type Flag struct {
Key string
Name string
Description string
// Steward is the accountable user's handle; "" means unassigned.
Steward string
CreatedAt time.Time
UpdatedAt time.Time
ArchivedAt *time.Time
Environments map[string]EnvConfig
// Activity says when the flag last changed and was last evaluated in
// each environment.
Activity map[string]Activity
// PermanentReason marks a flag meant to last, such as an operations
// kill switch; it's never reported stale. "" means not permanent.
PermanentReason string
// StaleNotifiedAt is when the steward was last told it's stale.
StaleNotifiedAt *time.Time
}
type Meta ¶
type Meta struct {
Key string `json:"key"`
Name string `json:"name"`
Description string `json:"description"`
Steward string `json:"steward,omitempty"`
}
Meta is the audit snapshot of a flag's descriptive fields.
type PermanentSnapshot ¶
type PermanentSnapshot struct {
Reason *string `json:"permanent_reason"` // null when not permanent
}
PermanentSnapshot is the audit record of a permanent mark.
func NewPermanentSnapshot ¶
func NewPermanentSnapshot(reason string) PermanentSnapshot
type RequestFilter ¶
type RequestFilter struct {
Status RequestStatus
FlagKey string
}
RequestFilter selects change requests; empty fields match everything.
type RequestSnapshot ¶
type RequestSnapshot struct {
ID int64 `json:"id"`
Status RequestStatus `json:"status"`
RequestedBy string `json:"requested_by"`
Proposed EnvConfig `json:"proposed"`
Reason string `json:"reason,omitempty"`
Comment string `json:"comment,omitempty"`
}
RequestSnapshot is the audit record of a change request.
func NewRequestSnapshot ¶
func NewRequestSnapshot(r ChangeRequest) RequestSnapshot
type RequestStatus ¶
type RequestStatus string
RequestStatus is where a change request is in its life.
const ( RequestPending RequestStatus = "pending" RequestApproved RequestStatus = "approved" RequestRejected RequestStatus = "rejected" RequestCancelled RequestStatus = "cancelled" RequestExpired RequestStatus = "expired" )
type StaleReason ¶
type StaleReason string
StaleReason says why a flag is stale; "" means it isn't.
const ( // StaleUnused: nothing has evaluated the flag anywhere for a while. StaleUnused StaleReason = "unused" // StaleOn and StaleOff: every environment has served the same value // to everyone, unchanged, for a while. StaleOn StaleReason = "always_on" StaleOff StaleReason = "always_off" // StaleMixed: settled, but on in some environments and off in others. StaleMixed StaleReason = "settled_mixed" )
type Staleness ¶
type Staleness struct {
Reason StaleReason
// Since is when the flag became stale.
Since time.Time
Suggestion string
}
Staleness is a flag's stale assessment.
type StewardSnapshot ¶
type StewardSnapshot struct {
Steward *string `json:"steward"` // null when unassigned
}
StewardSnapshot is the audit snapshot for a steward change.
func NewStewardSnapshot ¶
func NewStewardSnapshot(handle string) StewardSnapshot
type Store ¶
type Store interface {
// CreateFlag creates the flag, disabled, in every environment.
// steward may be "" for none; callers validate the handle.
CreateFlag(ctx context.Context, actor, key, name, description, steward string) (Flag, error)
GetFlag(ctx context.Context, key string) (Flag, error)
ListFlags(ctx context.Context) ([]Flag, error)
UpdateFlag(ctx context.Context, actor, key, name, description string) (Flag, error)
// UpdateEnvironment sets a flag's config in env. A non-empty reason
// marks an emergency change, recorded in the audit event.
UpdateEnvironment(ctx context.Context, actor, key, env string, cfg EnvConfig, reason string) (Flag, error)
// SetSteward assigns a non-empty steward; callers validate the handle.
SetSteward(ctx context.Context, actor, key, steward string) (Flag, error)
// SetPermanent marks a flag as meant to last, with a reason, or clears
// the mark when reason is "".
SetPermanent(ctx context.Context, actor, key, reason string) (Flag, error)
// RecordEvaluations notes when flags were evaluated. A time earlier
// than the stored one, or an unknown flag or environment, is ignored.
RecordEvaluations(ctx context.Context, seen []Evaluation) error
// MarkStaleNotified records that the flags' stewards were told they're
// stale. Unknown keys are ignored.
MarkStaleNotified(ctx context.Context, keys []string, at time.Time) error
ArchiveFlag(ctx context.Context, actor, key string) error
// EvalConfig returns what eval.Evaluate needs for one flag in one environment.
EvalConfig(ctx context.Context, key, env string) (eval.Flag, error)
// ListEnvironments returns environments sorted by key.
ListEnvironments(ctx context.Context) ([]Environment, error)
GetEnvironment(ctx context.Context, key string) (Environment, error)
// CreateEnvironment also gives every existing flag default settings
// (disabled, 100%) in the new environment.
CreateEnvironment(ctx context.Context, actor string, env Environment) (Environment, error)
UpdateEnvironmentSettings(ctx context.Context, actor string, env Environment) (Environment, error)
// ListEnvironmentAuditEvents returns environment-level events (not
// flag changes), oldest first.
ListEnvironmentAuditEvents(ctx context.Context, env string) ([]audit.Event, error)
// ListAuditEvents returns a flag's events, oldest first.
ListAuditEvents(ctx context.Context, flagKey string) ([]audit.Event, error)
// CreateChangeRequest proposes cfg for a flag in env, based on its
// current config there. Only one request can be pending per flag and
// environment (ErrConflict), and cfg must differ from the current
// config (ErrInvalid).
CreateChangeRequest(ctx context.Context, actor, key, env string, cfg EnvConfig, reason string, expiresAt time.Time) (ChangeRequest, error)
GetChangeRequest(ctx context.Context, id int64) (ChangeRequest, error)
// ListChangeRequests returns matching requests, newest first.
ListChangeRequests(ctx context.Context, filter RequestFilter) ([]ChangeRequest, error)
// ApproveChangeRequest applies a pending request's config and marks it
// approved, in one transaction. The requester can't approve it
// (ErrForbidden). It fails with ErrConflict if the request isn't
// pending, has expired, or the environment changed since it was made.
ApproveChangeRequest(ctx context.Context, actor string, id int64, comment string) (ChangeRequest, error)
// RejectChangeRequest closes a pending request without applying it.
// The requester cancels instead (ErrForbidden).
RejectChangeRequest(ctx context.Context, actor string, id int64, comment string) (ChangeRequest, error)
// CancelChangeRequest withdraws a pending request. Only its requester
// can (ErrForbidden).
CancelChangeRequest(ctx context.Context, actor string, id int64) (ChangeRequest, error)
// ExpireChangeRequests marks pending requests past their expiry as
// expired and returns how many it marked.
ExpireChangeRequests(ctx context.Context) (int, error)
}
Store persists flags. Every mutation writes its audit event in the same transaction, so a change is never recorded without its history.
Archived flags are still returned by GetFlag but excluded from ListFlags and EvalConfig, and cannot be modified.