service.go RawHistoryFolder
1// Package container implements the retained container lifecycle use case.
2//
3// The v3 declarative-apps cutover removed the route-container engine
4// (deploy/restart/remove/reconcile/attachments/sync/autostart, image-label
5// inference, readiness cascade, monitor). Workload effects belong to the
6// deployment engine through out.ContainerRuntime directly. What remains:
7//
8// - ListNetworks: read-only inventory of Gordon-managed networks.
9// - Shutdown: graceful teardown (log writer close; containers are left
10// running across Gordon restarts by design).
11package container
12
13import (
14 "context"
15 "strings"
16 "sync"
17
18 "github.com/bnema/zerowrap"
19
20 "github.com/bnema/gordon/internal/boundaries/out"
21 "github.com/bnema/gordon/internal/domain"
22)
23
24// Config holds configuration needed by the container service.
25type Config struct {
26 NetworkPrefix string
27}
28
29// Service implements the ContainerService interface.
30type Service struct {
31 runtime out.ContainerRuntime
32 envLoader out.EnvLoader
33 eventBus out.EventPublisher
34 logWriter out.ContainerLogWriter
35 config Config
36 mu sync.RWMutex
37}
38
39// NewService creates a new container service.
40func NewService(
41 runtime out.ContainerRuntime,
42 envLoader out.EnvLoader,
43 eventBus out.EventPublisher,
44 logWriter out.ContainerLogWriter,
45 config Config,
46) *Service {
47 return &Service{
48 runtime: runtime,
49 envLoader: envLoader,
50 eventBus: eventBus,
51 logWriter: logWriter,
52 config: config,
53 }
54}
55
56// ListNetworks returns Gordon-managed networks (prefix-filtered,
57// manager-labeled).
58func (s *Service) ListNetworks(ctx context.Context) ([]*domain.NetworkInfo, error) {
59 s.mu.RLock()
60 cfg := s.config
61 s.mu.RUnlock()
62
63 networks, err := s.runtime.ListNetworks(ctx)
64 if err != nil {
65 return nil, err
66 }
67
68 var filtered []*domain.NetworkInfo
69 for _, network := range networks {
70 if strings.HasPrefix(network.Name, cfg.NetworkPrefix+"-") && network.Labels[domain.LabelManaged] == "true" {
71 filtered = append(filtered, network)
72 }
73 }
74
75 return filtered, nil
76}
77
78// Shutdown gracefully shuts down the container manager.
79// Containers are left running across Gordon restarts by design;
80// boot reconciliation picks them back up from ACTIVE state.
81func (s *Service) Shutdown(ctx context.Context) error {
82 ctx = zerowrap.CtxWithFields(ctx, map[string]any{
83 zerowrap.FieldLayer: "usecase",
84 zerowrap.FieldUseCase: "Shutdown",
85 })
86 log := zerowrap.FromCtx(ctx)
87 log.Info().Msg("shutting down container manager...")
88
89 // Close log writer to stop all log collection
90 if s.logWriter != nil {
91 if err := s.logWriter.Close(); err != nil {
92 log.Warn().Err(err).Msg("failed to close container log writer")
93 }
94 }
95
96 log.Info().Msg("container manager shutdown complete")
97 return nil
98}
99
100// UpdateConfig updates the service configuration.
101func (s *Service) UpdateConfig(config Config) {
102 s.mu.Lock()
103 s.config = config
104 s.mu.Unlock()
105}