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). An empty service targets every service, which a
39 // multi-service app accepts only with all set (domain.ErrAppServiceScope).
40 Deploy(ctx context.Context, app, revision, service string, all bool, idempotencyKey string) (*domain.AppOperation, error)
41
42 // Stop persists the durable stopped intent and stops exact containers.
43 Stop(ctx context.Context, app, idempotencyKey string) (*domain.AppOperation, error)
44
45 // Start clears the stopped intent and ensures running from active.
46 Start(ctx context.Context, app, idempotencyKey string) (*domain.AppOperation, error)
47
48 // Restart restarts from pinned digests without re-resolution.
49 // An empty service targets every service, which a multi-service app
50 // accepts only with all set (domain.ErrAppServiceScope).
51 Restart(ctx context.Context, app, service string, all bool, idempotencyKey string) (*domain.AppOperation, error)
52
53 // Remove withdraws workloads; volumes and secrets are retained as
54 // owned orphans and the name stays reserved.
55 Remove(ctx context.Context, app, idempotencyKey string) (*domain.AppOperation, error)
56
57 // OperationByKey recovers an ambiguous mutation outcome by the
58 // client-supplied idempotency key.
59 OperationByKey(ctx context.Context, app, key string) (*domain.AppOperation, error)
60
61 // ListSecrets returns metadata-only desired and active registrations.
62 ListSecrets(ctx context.Context, app, service string) ([]AppSecretMetadata, error)
63
64 // SetSecrets writes secret values for pre-registered names in one
65 // service. service is required; every key must already exist in
66 // desired or active state.
67 SetSecrets(ctx context.Context, app, service string, values map[string]string) error
68
69 // DeleteSecret removes one secret value. Refused when referenced.
70 DeleteSecret(ctx context.Context, app, service, key string) error
71}
72
73// AppApplyResult describes one accepted (or no-op) apply.
74// AppSecretMetadata is a secret registration without its value.
75type AppSecretMetadata struct {
76 Service string
77 Key string
78 Name string
79 Source string
80 Presence string
81}
82
83type AppApplyResult struct {
84 App string
85 FormerRevision string
86 ResultingRevision string
87 Noop bool
88 Pending bool
89 Diff domain.AppDiff
90 IntentID string
91}
92
93// AppDryRunResult describes validation without persistence.
94// The resulting revision is a preview only and is never stored.
95type AppDryRunResult struct {
96 App string
97 Valid bool
98 Diff domain.AppDiff
99}
100
101// AppSummary is one row of the app list: revisions, acceptance status,
102// and the latest recorded outcome.
103type AppSummary struct {
104 App string
105 Desired string
106 DesiredStatus string
107 Active string
108 Converged bool
109 // Pending reports desired state that the active revision has not
110 // reached yet. It is derived from desired vs ACTIVE, never from a
111 // journal.
112 Pending bool
113 Stopped bool
114 LastOutcome string
115}
116
117// AppServiceView is one service's effective state for inspection.
118// Digest and container are ids, never secret values.
119type AppServiceView struct {
120 EffectiveRevision string
121 Digest string
122 Container string
123 RestartUnsafe bool
124}
125
126// AppRetainedView lists the resources the app owns and would retain on
127// removal: runtime volume names, secret paths, and pinned image
128// references. It never carries secret values.
129type AppRetainedView struct {
130 Volumes []string
131 Secrets []string
132 Images []string
133}
134
135// AppDetail inspects desired + active + intent + ownership + last op for
136// one app.
137type AppDetail struct {
138 App string
139 DesiredRevision string
140 DesiredStatus string
141 Pending bool
142 Converged bool
143 ConvergedRevision string
144 Services map[string]AppServiceView
145 Stopped bool
146 Retained AppRetainedView
147 LastOp string
148 LastOpKind string
149 LastOutcome string
150 LastOpStartedAt time.Time
151}