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 volumeSvc in.VolumeService
27 cleanup func()
28}
29
30// NewKernel initializes local services without starting server listeners.
31func NewKernel(configPath string) (*Kernel, error) {
32 ctx := context.Background()
33 v, cfg, err := initConfig(configPath)
34 if err != nil {
35 return nil, err
36 }
37
38 log, cleanup, err := initLogger(cfg)
39 if err != nil {
40 return nil, err
41 }
42 if cleanup == nil {
43 cleanup = func() {}
44 }
45
46 ctx = zerowrap.WithCtx(ctx, log)
47
48 // Prefer full service wiring so local CLI can execute the same operations
49 // as remote mode without going through HTTP admin endpoints.
50 if svc, fullErr := createServices(ctx, v, cfg, log); fullErr == nil {
51 return &Kernel{
52 authEnabled: cfg.Auth.Enabled,
53 configSvc: svc.configSvc,
54 secretSvc: svc.secretSvc,
55 containerSvc: svc.containerSvc,
56 backupSvc: svc.backupSvc,
57 registrySvc: svc.registrySvc,
58 healthSvc: svc.healthSvc,
59 logSvc: svc.logSvc,
60 volumeSvc: svc.volumeSvc,
61 cleanup: cleanup,
62 }, nil
63 } else {
64 log.Warn().Err(fullErr).Msg("local kernel running in minimal mode")
65 }
66
67 configSvc := configusecase.NewService(v, nil)
68 if err := configSvc.Load(ctx); err != nil {
69 cleanup()
70 return nil, fmt.Errorf("failed to load configuration: %w", err)
71 }
72
73 _, _, _, domainSecretStore, err := createDomainSecretStore(cfg, log)
74 if err != nil {
75 cleanup()
76 return nil, fmt.Errorf("failed to create local secret store: %w", err)
77 }
78
79 secretSvc := secretsusecase.NewService(domainSecretStore, log, nil)
80
81 return &Kernel{
82 authEnabled: cfg.Auth.Enabled,
83 configSvc: configSvc,
84 secretSvc: secretSvc,
85 cleanup: cleanup,
86 }, nil
87}
88
89func (k *Kernel) Close() error {
90 if k == nil || k.cleanup == nil {
91 return nil
92 }
93 k.cleanup()
94 return nil
95}
96
97func (k *Kernel) Config() in.ConfigService { return k.configSvc }
98
99func (k *Kernel) Secrets() in.SecretService { return k.secretSvc }
100
101func (k *Kernel) Container() in.ContainerService { return k.containerSvc }
102
103func (k *Kernel) Backup() in.BackupService { return k.backupSvc }
104
105func (k *Kernel) Registry() in.RegistryService { return k.registrySvc }
106
107func (k *Kernel) Health() in.HealthService { return k.healthSvc }
108
109func (k *Kernel) Logs() in.LogService { return k.logSvc }
110
111func (k *Kernel) Volumes() in.VolumeService { return k.volumeSvc }
112
113func (k *Kernel) AuthEnabled() bool { return k != nil && k.authEnabled }