Documentation
¶
Overview ¶
Package deployurls keeps an immutable hostname for each recent successful deploy: a retained service that runs that attempt's pinned image and sleeps when idle, so a past deployment can be opened without rolling back.
Index ¶
- Constants
- func Clone(app store.DesiredService, w Wanted, host string) store.DesiredService
- func ExcludeReason(app store.DesiredService) string
- func Key(a store.DeployAttempt) string
- func MaxUnreadyFromEnv(getenv func(string) string) int
- func ParseMode(s string) (mode string, ok bool)
- func ServiceName(app, key string) string
- type Config
- type Guard
- type GuardStore
- type ParentSecrets
- type SecretReader
- type Store
- type Syncer
- type Wanted
Constants ¶
const ( // EnvKeep is how many recent successful deploys per app keep a URL; 0 turns the feature off. EnvKeep = "APP_DEPLOY_URL_KEEP" // EnvIdleMinutes is how long a retained deploy runs without traffic before it sleeps. EnvIdleMinutes = "APP_DEPLOY_URL_IDLE_MINUTES" // EnvInterval is how often the syncer looks for new deploys. EnvInterval = "APP_DEPLOY_URL_INTERVAL" // EnvMaxCopies caps the retained copies of older deploys per app, the live deploy excluded. EnvMaxCopies = "APP_DEPLOY_URL_MAX_COPIES" // EnvMinFreeMemoryMB is the host available memory below which no new copy is created; 0 turns the check off. EnvMinFreeMemoryMB = "APP_DEPLOY_URL_MIN_FREE_MEMORY_MB" // EnvMaxMemoryPercent skips an app whose memory limit exceeds this share of host RAM; 0 turns the check off. EnvMaxMemoryPercent = "APP_DEPLOY_URL_MAX_MEMORY_PERCENT" // EnvCopies seeds the copies mode (off, private, public) until a stored value overrides it. EnvCopies = "APP_DEPLOY_URL_COPIES" )
Environment variables that tune deploy URLs.
const ( ModeOff = "off" ModePrivate = "private" ModePublic = "public" // DefaultMode is what an install gets when neither a stored value nor the env seed is set. DefaultMode = ModePrivate )
Copies modes. Private keeps each copy asleep with no hostname, so no route or certificate exists for it until the mode is set to public.
const ( DefaultKeep = 3 DefaultIdleMinutes = 5 DefaultInterval = time.Minute DefaultMaxCopies = 1 DefaultMinFreeMB = 1024 DefaultMaxMemPct = 25 )
Defaults for the variables above.
const DefaultMaxUnready = 3
DefaultMaxUnready matches the few restarts the crash backoff allows before it plateaus.
const EnvMaxUnready = "APP_DEPLOY_URL_MAX_UNREADY"
EnvMaxUnready is how many readiness failures in a row a retained deploy service may have before it is suspended instead of retried.
Variables ¶
This section is empty.
Functions ¶
func Clone ¶
func Clone(app store.DesiredService, w Wanted, host string) store.DesiredService
Clone is the retained service for w: the app's config restored to that attempt, running the pinned image, with no volumes, host ports, hooks or automatic rollback, so an old deploy cannot touch live data or fight the app. It carries the host only in public mode (host is "" otherwise), so a private copy has no route and no certificate order.
func ExcludeReason ¶
func ExcludeReason(app store.DesiredService) string
ExcludeReason names why app gets no retained copies, or "" when it may. A copy runs without the app's volumes and host ports, so for an app that owns data or a host claim it would be an empty second instance, never a rollback target.
func Key ¶
func Key(a store.DeployAttempt) string
Key is the short, DNS-safe identity of an attempt: the commit when it has one, else its id.
func MaxUnreadyFromEnv ¶
MaxUnreadyFromEnv reads EnvMaxUnready, falling back to the default.
func ParseMode ¶
ParseMode normalizes s to a copies mode; ok is false for anything else, including "".
func ServiceName ¶
ServiceName is the retained service for app and key; it doubles as the host label.
Types ¶
type Config ¶
type Config struct {
Keep int
IdleMinutes int
Interval time.Duration
MaxCopies int
MinFreeMB int
MaxMemPct int
// Mode is the env-seeded copies mode; a stored setting wins over it (see Syncer.Mode).
Mode string
}
Config is the environment-driven tuning.
func ConfigFromEnv ¶
ConfigFromEnv reads the Env* variables, using defaults for anything missing or malformed.
func FromProcessEnv ¶
func FromProcessEnv() Config
FromProcessEnv is ConfigFromEnv over the process environment.
type Guard ¶
type Guard struct {
Store GuardStore
Max int
// contains filtered or unexported fields
}
Guard bounds the retries of retained deploy services and lets them resolve their app's secrets. It outlives the per-pass controllers.
func (*Guard) Asleep ¶
Asleep reports whether service is a retained deploy copy that is marked sleeping. Only a wake request may start it, never a bulk or project start.
func (*Guard) IsRetained ¶
IsRetained reports whether service is a retained deploy service.
func (*Guard) NoteUnready ¶
func (g *Guard) NoteUnready(ctx context.Context, service string) (retained, stopped bool, err error)
NoteUnready records a readiness failure of service. For a retained deploy service that reaches Max it suspends the service (containers are torn down by the normal suspend path) and reports stopped. Other services are untouched.
type GuardStore ¶
type GuardStore interface {
DeployURLApp(ctx context.Context, serviceName string) (string, bool, error)
UpdateServiceSuspended(ctx context.Context, name string, suspended bool) error
}
GuardStore is what Guard needs; *store.DB satisfies it.
type ParentSecrets ¶
type ParentSecrets struct {
Inner SecretReader
Apps interface {
DeployURLApp(ctx context.Context, serviceName string) (string, bool, error)
}
}
ParentSecrets resolves a retained deploy service's secrets under its app's name, so the clone sees the same secret env without a stored copy of the values.
type SecretReader ¶
type SecretReader interface {
Exists(ctx context.Context, serviceName, envKey string) (bool, error)
Resolve(ctx context.Context, serviceName, envKey string) (string, error)
}
SecretReader is the secrets surface the application controller uses.
type Store ¶
type Store interface {
ListDesiredServices(ctx context.Context) ([]store.DesiredService, error)
SaveDesiredService(ctx context.Context, svc store.DesiredService) error
SaveSuspendedDesiredService(ctx context.Context, svc store.DesiredService) error
ListAppSleep(ctx context.Context) ([]store.AppSleep, error)
DeleteAppSleep(ctx context.Context, serviceName string) error
// DeleteAppSleepIfOrphan deletes only while no desired service has the name.
DeleteAppSleepIfOrphan(ctx context.Context, serviceName string) error
ListDeployAttempts(ctx context.Context, serviceName string) ([]store.DeployAttempt, error)
ListRollbackPins(ctx context.Context, serviceName string) ([]store.RollbackPin, error)
SetAppSleepIdle(ctx context.Context, serviceName string, minutes int, now time.Time) error
SetAppSleepHold(ctx context.Context, serviceName string, hold bool, now time.Time) error
SetAppSleeping(ctx context.Context, serviceName string, sleeping bool, now time.Time) error
ListDeployURLs(ctx context.Context, appName string) ([]store.DeployURL, error)
SaveDeployURL(ctx context.Context, u store.DeployURL) error
DeleteDeployURL(ctx context.Context, serviceName string) error
}
Store is what the syncer needs; *store.DB satisfies it.
type Syncer ¶
type Syncer struct {
Store Store
Config Config
// Mode returns the stored copies mode, or "" to fall back to Config.Mode.
Mode func(ctx context.Context) string
// HostFor returns the hostname for a retained service, or "" when none can be formed.
HostFor func(ctx context.Context, serviceName string) string
// Remove deletes a retained service and tears its containers down.
Remove func(ctx context.Context, serviceName string) error
// Nudge asks the reconcilers to run now.
Nudge func()
// HostMemory reports host total and available bytes; nil reads this host.
HostMemory func() (total, avail int64, err error)
Now func() time.Time
Logger *slog.Logger
// contains filtered or unexported fields
}
Syncer reconciles retained deploy services against each app's recent deploys.
type Wanted ¶
type Wanted struct {
Attempt store.DeployAttempt
Key string
ServiceName string
Pin *store.RollbackPin
Ref string
// Live is true while the attempt is what the app runs now: the app's own URL serves it.
Live bool
}
Wanted is one deploy attempt that should have a URL.
func Plan ¶
func Plan(app store.DesiredService, attempts []store.DeployAttempt, pins []store.RollbackPin, keep int) []Wanted
Plan picks the newest keep successful attempts whose content can still be redeployed (a registry digest or a rollback pin). Two attempts of the same commit share one URL, the newest. Image GC is untouched: it only frees what no pin or running service holds.