v3.3.2 9697f9d

app_state.go RawHistoryFolder

1package out
2
3import (
4	"context"
5
6	"github.com/bnema/gordon/internal/domain"
7)
8
9// AppState persists desired/active app state, reservations, intents,
10// operation journals, and ownership records. Implemented by the bbolt
11// appstate adapter; consumed whole by the deployment engine. Other use
12// cases depend on the narrower AppStateReader, AppCatalogReader, or
13// AppApplyStore ports.
14// All methods are safe for concurrent use within one process;
15// cross-process coordination is owned by the implementation
16// (store.lock flock). No secret values pass through this boundary.
17type AppState interface {
18	// Close releases the durable store and its process lock.
19	Close() error
20
21	// Recover completes committed-but-unmaterialized apply intents
22	// before any new mutation is accepted (recovery-before-mutation).
23	Recover(ctx context.Context) error
24
25	// LoadRecoveryInhibitions returns every durable recovery-inhibition
26	// record of one app. Boot and periodic recovery must refuse to
27	// start or restart an inhibited container ID.
28	LoadRecoveryInhibitions(ctx context.Context, app string) ([]domain.AppRecoveryInhibition, error)
29
30	// SaveRecoveryInhibition durably records one generation-scoped
31	// recovery inhibition. Repeated calls for the same
32	// (app, service, container ID) replace the existing record.
33	SaveRecoveryInhibition(ctx context.Context, inhibition domain.AppRecoveryInhibition) error
34
35	// ClearRecoveryInhibition drops the inhibition of one exact
36	// generation. Clearing an absent record is not an error.
37	ClearRecoveryInhibition(ctx context.Context, app, service, containerID string) error
38
39	// LoadCheckpoint returns the global reservation checkpoint.
40	LoadCheckpoint(ctx context.Context) (domain.AppStoreCheckpoint, error)
41
42	// AppExists reports whether the app has a live identity: desired
43	// state, active state, or an ownership incarnation. It never creates
44	// state. A retired app whose only remaining record is an operation
45	// journal does not exist (its own key still replays through
46	// ClaimOperation).
47	AppExists(ctx context.Context, app string) (bool, error)
48
49	// RegisterBackendBinds records Gordon-generated loopback backend
50	// binds (owner gordon-backend) in the global checkpoint. It fails
51	// closed on conflict with another app's desired/active/in-flight
52	// claim. Old binds stay claimed until ReleaseBackendBinds after
53	// verified withdrawal; same-app claims never self-conflict.
54	RegisterBackendBinds(ctx context.Context, binds []domain.AppListenerReservation) error
55
56	// ReleaseBackendBinds drops Gordon-generated loopback claims for one
57	// retired container. Unknown claims are ignored.
58	ReleaseBackendBinds(ctx context.Context, app, containerID string) error
59
60	// ListApps returns normalized names of apps with any state.
61	ListApps(ctx context.Context) ([]string, error)
62
63	// LoadDesired returns the latest accepted desired revision.
64	// ok is false when the app has no desired state.
65	LoadDesired(ctx context.Context, app string) (domain.AppDesiredRevision, bool, error)
66
67	// LoadRevision returns one immutable revision by id.
68	LoadRevision(ctx context.Context, app, revision string) (domain.AppDesiredRevision, error)
69
70	// LoadActive returns the per-service effective state.
71	// ok is false when the app was never deployed.
72	LoadActive(ctx context.Context, app string) (domain.AppActive, bool, error)
73
74	// LoadIntent returns the durable stopped/running intent.
75	LoadIntent(ctx context.Context, app string) (domain.AppStopIntent, error)
76
77	// SaveIntent persists running/stopped intent.
78	SaveIntent(ctx context.Context, intent domain.AppStopIntent) error
79
80	// LoadOwnership returns the ownership record (zero value when absent).
81	LoadOwnership(ctx context.Context, app string) (domain.AppOwnership, error)
82
83	// SaveOwnership persists the ownership record.
84	SaveOwnership(ctx context.Context, ownership domain.AppOwnership) error
85
86	// AcceptApply durably accepts one apply intent: it stages the full
87	// candidate, commits it (the single atomic commit point), and
88	// materializes the revision, desired pointer, and reservation
89	// checkpoint. Each step is its own transaction, so a crash after the
90	// commit is finished by Recover and a staged orphan is swept by GC.
91	// A garbage-collection failure after materialization is logged and
92	// does not fail the accepted apply.
93	AcceptApply(ctx context.Context, intent domain.AppApplyIntent) error
94
95	// SaveOperation persists an operation journal record atomically.
96	SaveOperation(ctx context.Context, op domain.AppOperation) error
97
98	// ClaimOperation is the single atomic check-and-write point for one
99	// mutation request key. Absent key: the candidate is persisted as an
100	// in-flight journal and claimed is true. Same key with the same
101	// request identity: the stored journal is returned and claimed is
102	// false, meaning the caller must not execute the request's effects
103	// again. Same key with a different request identity:
104	// ErrAppStateConflict. Absent key for an app with no desired, active,
105	// or ownership incarnation: ErrAppNotFound, writing nothing. A
106	// concurrent claim of the same key never creates a second journal.
107	ClaimOperation(ctx context.Context, candidate domain.AppOperation) (existing domain.AppOperation, claimed bool, err error)
108
109	// LoadOperation returns one operation journal record.
110	LoadOperation(ctx context.Context, app, opID string) (domain.AppOperation, error)
111
112	// LoadLatestOperation returns the most recently started operation.
113	LoadLatestOperation(ctx context.Context, app string) (domain.AppOperation, bool, error)
114
115	// SaveActive persists the per-service effective state.
116	SaveActive(ctx context.Context, active domain.AppActive) error
117
118	// RetireApp atomically ends an app's incarnation: it archives the
119	// live ownership record with every resource marked retained, drops
120	// the live ownership record, resets the app UUID so a reapply
121	// allocates a new incarnation, and clears desired, active, intent,
122	// staged intents, and revisions. Retained resources stay protected
123	// by the archived record and can never be adopted by a name reuse.
124	RetireApp(ctx context.Context, app string) error
125}
126
127// AppStateReader is the ACTIVE/intent subset read paths need.
128// Narrower than AppState so read paths never gain write access.
129type AppStateReader interface {
130	ListApps(ctx context.Context) ([]string, error)
131	LoadActive(ctx context.Context, app string) (domain.AppActive, bool, error)
132	LoadIntent(ctx context.Context, app string) (domain.AppStopIntent, error)
133}
134
135// AppCatalogReader is the read-only view the app administration read
136// model needs: identity, desired/active state, and operation journals.
137type AppCatalogReader interface {
138	AppStateReader
139	AppExists(ctx context.Context, app string) (bool, error)
140	LoadDesired(ctx context.Context, app string) (domain.AppDesiredRevision, bool, error)
141	LoadRevision(ctx context.Context, app, revision string) (domain.AppDesiredRevision, error)
142	LoadOwnership(ctx context.Context, app string) (domain.AppOwnership, error)
143	LoadOperation(ctx context.Context, app, opID string) (domain.AppOperation, error)
144	LoadLatestOperation(ctx context.Context, app string) (domain.AppOperation, bool, error)
145}
146
147// AppApplyStore is what accepting desired configuration needs: recovery
148// before mutation, conflict inputs, and one durable accept.
149type AppApplyStore interface {
150	Recover(ctx context.Context) error
151	LoadCheckpoint(ctx context.Context) (domain.AppStoreCheckpoint, error)
152	LoadDesired(ctx context.Context, app string) (domain.AppDesiredRevision, bool, error)
153	LoadActive(ctx context.Context, app string) (domain.AppActive, bool, error)
154	// LoadRevision reads the app env of the revisions ACTIVE services were
155	// activated from, so diffs compare against what actually runs.
156	LoadRevision(ctx context.Context, app, revision string) (domain.AppDesiredRevision, error)
157	AcceptApply(ctx context.Context, intent domain.AppApplyIntent) error
158}
159
160// AppTrafficRefresher is the app-facing traffic contract: one serialized
161// HTTP/L4 publish boundary that supports both fail-closed withdrawal of
162// one service and full publication of current verified state. Both
163// operations read the latest validated config at call time and are
164// serialized against reload and other rebuilds so a stale config can
165// never win. WithdrawService must leave no forwarding for the named
166// service once it returns nil; failing loudly is preferred to leaving
167// stale L4 forwarding behind.
168type AppTrafficRefresher interface {
169	// RebuildTraffic re-projects verified ACTIVE state and applies the
170	// full HTTP/L4 graph.
171	RebuildTraffic(ctx context.Context) error
172	// WithdrawService publishes the current graph without the named
173	// app service, so a dead or unverified backend stops receiving
174	// traffic. A nil return means stale forwarding is proven disabled.
175	WithdrawService(ctx context.Context, app, service string) error
176	// WithdrawServiceState drops the named service's recorded backend
177	// binds in ACTIVE without applying the graph. It is the canonical
178	// state-only withdrawal: ready-path failures use it when the
179	// caller already owns publication (or none is due), so no second
180	// routing-state write races the serialized WithdrawService path.
181	WithdrawServiceState(ctx context.Context, app, service string) error
182}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close