flag

package
v0.0.0-...-12a2a41 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package flag holds the flag domain types and the storage contract.

Index

Constants

View Source
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"
)
View Source
const (
	ActionRequestCreated   = "change_request.created"
	ActionRequestApproved  = "change_request.approved"
	ActionRequestRejected  = "change_request.rejected"
	ActionRequestCancelled = "change_request.cancelled"
	ActionRequestExpired   = "change_request.expired"
)
View Source
const RequestTTL = 7 * 24 * time.Hour

RequestTTL is how long a change request waits for review.

Variables

View Source
var (
	ErrNotFound = errs.ErrNotFound
	ErrConflict = errs.ErrConflict
	ErrInvalid  = errs.ErrInvalid
)

Aliases so callers can match flag errors without importing errs.

Functions

func NewEnvChange

func NewEnvChange(cfg EnvConfig, reason string) any

NewEnvChange returns the audit record for setting cfg: the config itself, or an EmergencyChange when there's a reason.

func ValidKey

func ValidKey(key string) bool

func ValidateMeta

func ValidateMeta(key, name string) error

func ValidatePermanentReason

func ValidatePermanentReason(reason string) (string, error)

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

type EmergencyChange struct {
	EnvConfig
	Reason string `json:"emergency_reason"`
}

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.

func (EnvConfig) Equal

func (c EnvConfig) Equal(o EnvConfig) bool

Equal reports whether two configs behave the same. Nil and empty rules are equal.

func (EnvConfig) Validate

func (c EnvConfig) Validate() error

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

type Evaluation struct {
	Flag, Environment string
	At                time.Time
}

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.

func Assess

func Assess(f Flag, envs []Environment, now time.Time, after time.Duration, pending bool) Staleness

Assess reports whether a flag is stale: not permanent or archived, no change request pending, and either unused or settled for at least after.

func (Staleness) Stale

func (s Staleness) Stale() bool

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.

Directories

Path Synopsis
Package flagtest provides an in-memory flag.Store and a contract test suite that every flag.Store implementation must pass.
Package flagtest provides an in-memory flag.Store and a contract test suite that every flag.Store implementation must pass.

Jump to

Keyboard shortcuts

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