1package in
2
3import (
4 "context"
5 "time"
6
7 "github.com/bnema/gordon/internal/domain"
8)
9
10// AppService is the driving port for the v3 app lifecycle:
11// apply + reads (list/show/diff), lifecycle verbs
12// (deploy/stop/start/restart/remove), op recovery by idempotency key,
13// and service-scoped secret value writes.
14//
15// The port owns its view structs and speaks domain types only — never
16// adapter DTOs. Secret VALUES cross SetSecrets by necessity (that is the
17// write path); every other method carries names, digests, and ids only.
18//
19// This is preparation: the interface is frozen here, unreachable.
20// The admin handler and the service implementation wire in at cutover;
21// until then no caller exists.
22type AppService interface {
23 // Apply validates a manifest spec and persists it as desired state,
24 // or validates without persistence when dryRun is set. source is the
25 // raw manifest bytes (hashed for audit, never re-read).
26 Apply(ctx context.Context, spec domain.AppSpec, source []byte, dryRun bool) (*AppApplyResult, *AppDryRunResult, error)
27
28 // List returns one summary per known app.
29 List(ctx context.Context) ([]AppSummary, error)
30
31 // Show inspects desired + active + intent + last op for one app.
32 Show(ctx context.Context, app string) (*AppDetail, error)
33
34 // Diff returns the normalized desired-vs-active diff for one app.
35 Diff(ctx context.Context, app string) (domain.AppDiff, error)
36
37 // Deploy activates a captured revision (empty revision means the
38 // desired head; empty service means all services).
39 Deploy(ctx context.Context, app, revision, service, idempotencyKey string) (*domain.AppOperation, error)
40
41 // Stop persists the durable stopped intent and stops exact containers.
42 Stop(ctx context.Context, app, idempotencyKey string) (*domain.AppOperation, error)
43
44 // Start clears the stopped intent and ensures running from active.
45 Start(ctx context.Context, app, idempotencyKey string) (*domain.AppOperation, error)
46
47 // Restart restarts from pinned digests without re-resolution.
48 // Empty service means all services.
49 Restart(ctx context.Context, app, service, idempotencyKey string) (*domain.AppOperation, error)
50
51 // Remove withdraws workloads; volumes and secrets are retained as
52 // owned orphans and the name stays reserved.
53 Remove(ctx context.Context, app, idempotencyKey string) (*domain.AppOperation, error)
54
55 // OperationByKey recovers an ambiguous mutation outcome by the
56 // client-supplied idempotency key.
57 OperationByKey(ctx context.Context, app, key string) (*domain.AppOperation, error)
58
59 // ListSecrets returns metadata-only desired and active registrations.
60 ListSecrets(ctx context.Context, app, service string) ([]AppSecretMetadata, error)
61
62 // SetSecrets writes secret values for pre-registered names in one
63 // service. service is required; every key must already exist in
64 // desired or active state.
65 SetSecrets(ctx context.Context, app, service string, values map[string]string) error
66
67 // DeleteSecret removes one secret value. Refused when referenced.
68 DeleteSecret(ctx context.Context, app, service, key string) error
69}
70
71// AppApplyResult describes one accepted (or no-op) apply.
72// AppSecretMetadata is a secret registration without its value.
73type AppSecretMetadata struct {
74 Service string
75 Key string
76 Name string
77 Source string
78 Presence string
79}
80
81type AppApplyResult struct {
82 App string
83 FormerRevision string
84 ResultingRevision string
85 Noop bool
86 Pending bool
87 Diff domain.AppDiff
88 IntentID string
89}
90
91// AppDryRunResult describes validation without persistence.
92// The resulting revision is a preview only and is never stored.
93type AppDryRunResult struct {
94 App string
95 Valid bool
96 Diff domain.AppDiff
97}
98
99// AppSummary is one row of the app list: revisions, acceptance status,
100// and the latest recorded outcome.
101type AppSummary struct {
102 App string
103 Desired string
104 DesiredStatus string
105 Active string
106 Converged bool
107 // Pending reports desired state that the active revision has not
108 // reached yet. It is derived from desired vs ACTIVE, never from a
109 // journal.
110 Pending bool
111 Stopped bool
112 LastOutcome string
113}
114
115// AppServiceView is one service's effective state for inspection.
116// Digest and container are ids, never secret values.
117type AppServiceView struct {
118 EffectiveRevision string
119 Digest string
120 Container string
121 RestartUnsafe bool
122}
123
124// AppRetainedView lists the resources the app owns and would retain on
125// removal: runtime volume names, secret paths, and pinned image
126// references. It never carries secret values.
127type AppRetainedView struct {
128 Volumes []string
129 Secrets []string
130 Images []string
131}
132
133// AppDetail inspects desired + active + intent + ownership + last op for
134// one app.
135type AppDetail struct {
136 App string
137 DesiredRevision string
138 DesiredStatus string
139 Pending bool
140 Converged bool
141 ConvergedRevision string
142 Services map[string]AppServiceView
143 Stopped bool
144 Retained AppRetainedView
145 LastOp string
146 LastOpKind string
147 LastOutcome string
148 LastOpStartedAt time.Time
149}