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 eventBus out.EventPublisher
33 logWriter out.ContainerLogWriter
34 config Config
35 mu sync.RWMutex
36}
37
38// NewService creates a new container service.
39func NewService(
40 runtime out.ContainerRuntime,
41 eventBus out.EventPublisher,
42 logWriter out.ContainerLogWriter,
43 config Config,
44) *Service {
45 return &Service{
46 runtime: runtime,
47 eventBus: eventBus,
48 logWriter: logWriter,
49 config: config,
50 }
51}
52
53// ListNetworks returns Gordon-managed networks (prefix-filtered,
54// manager-labeled).
55func (s *Service) ListNetworks(ctx context.Context) ([]*domain.NetworkInfo, error) {
56 s.mu.RLock()
57 cfg := s.config
58 s.mu.RUnlock()
59
60 networks, err := s.runtime.ListNetworks(ctx)
61 if err != nil {
62 return nil, err
63 }
64
65 var filtered []*domain.NetworkInfo
66 for _, network := range networks {
67 if strings.HasPrefix(network.Name, cfg.NetworkPrefix+"-") && network.Labels[domain.LabelManaged] == "true" {
68 filtered = append(filtered, network)
69 }
70 }
71
72 return filtered, nil
73}
74
75// Shutdown gracefully shuts down the container manager.
76// Containers are left running across Gordon restarts by design;
77// boot reconciliation picks them back up from ACTIVE state.
78func (s *Service) Shutdown(ctx context.Context) error {
79 ctx = zerowrap.CtxWithFields(ctx, map[string]any{
80 zerowrap.FieldLayer: "usecase",
81 zerowrap.FieldUseCase: "Shutdown",
82 })
83 log := zerowrap.FromCtx(ctx)
84 log.Info().Msg("shutting down container manager...")
85
86 // Close log writer to stop all log collection
87 if s.logWriter != nil {
88 if err := s.logWriter.Close(); err != nil {
89 log.Warn().Err(err).Msg("failed to close container log writer")
90 }
91 }
92
93 log.Info().Msg("container manager shutdown complete")
94 return nil
95}
96
97// UpdateConfig updates the service configuration.
98func (s *Service) UpdateConfig(config Config) {
99 s.mu.Lock()
100 s.config = config
101 s.mu.Unlock()
102}