1package app
2
3import (
4 "context"
5 "fmt"
6
7 "github.com/bnema/zerowrap"
8
9 "github.com/bnema/gordon/internal/boundaries/in"
10 configusecase "github.com/bnema/gordon/internal/usecase/config"
11 secretsusecase "github.com/bnema/gordon/internal/usecase/secrets"
12)
13
14// Kernel provides in-process service access for local CLI execution.
15//
16// It intentionally does not start HTTP servers or register signal handlers.
17type Kernel struct {
18 authEnabled bool
19 configSvc in.ConfigService
20 secretSvc in.SecretService
21 containerSvc in.ContainerService
22 backupSvc in.BackupService
23 registrySvc in.RegistryService
24 healthSvc in.HealthService
25 logSvc in.LogService
26 cleanup func()
27}
28
29// NewKernel initializes local services without starting server listeners.
30func NewKernel(configPath string) (*Kernel, error) {
31 ctx := context.Background()
32 v, cfg, err := initConfig(configPath)
33 if err != nil {
34 return nil, err
35 }
36
37 log, cleanup, err := initLogger(cfg)
38 if err != nil {
39 return nil, err
40 }
41 if cleanup == nil {
42 cleanup = func() {}
43 }
44
45 ctx = zerowrap.WithCtx(ctx, log)
46
47 // Prefer full service wiring so local CLI can execute the same operations
48 // as remote mode without going through HTTP admin endpoints.
49 if svc, fullErr := createServices(ctx, v, cfg, log); fullErr == nil {
50 return &Kernel{
51 authEnabled: cfg.Auth.Enabled,
52 configSvc: svc.configSvc,
53 secretSvc: svc.secretSvc,
54 containerSvc: svc.containerSvc,
55 backupSvc: svc.backupSvc,
56 registrySvc: svc.registrySvc,
57 healthSvc: svc.healthSvc,
58 logSvc: svc.logSvc,
59 cleanup: cleanup,
60 }, nil
61 } else {
62 log.Warn().Err(fullErr).Msg("local kernel running in minimal mode")
63 }
64
65 configSvc := configusecase.NewService(v, nil)
66 if err := configSvc.Load(ctx); err != nil {
67 cleanup()
68 return nil, fmt.Errorf("failed to load configuration: %w", err)
69 }
70
71 _, _, _, domainSecretStore, err := createDomainSecretStore(cfg, log)
72 if err != nil {
73 cleanup()
74 return nil, fmt.Errorf("failed to create local secret store: %w", err)
75 }
76
77 secretSvc := secretsusecase.NewService(domainSecretStore, log)
78
79 return &Kernel{
80 authEnabled: cfg.Auth.Enabled,
81 configSvc: configSvc,
82 secretSvc: secretSvc,
83 cleanup: cleanup,
84 }, nil
85}
86
87func (k *Kernel) Close() error {
88 if k == nil || k.cleanup == nil {
89 return nil
90 }
91 k.cleanup()
92 return nil
93}
94
95func (k *Kernel) Config() in.ConfigService { return k.configSvc }
96
97func (k *Kernel) Secrets() in.SecretService { return k.secretSvc }
98
99func (k *Kernel) Container() in.ContainerService { return k.containerSvc }
100
101func (k *Kernel) Backup() in.BackupService { return k.backupSvc }
102
103func (k *Kernel) Registry() in.RegistryService { return k.registrySvc }
104
105func (k *Kernel) Health() in.HealthService { return k.healthSvc }
106
107func (k *Kernel) Logs() in.LogService { return k.logSvc }
108
109func (k *Kernel) AuthEnabled() bool { return k != nil && k.authEnabled }