Skip to content

Commit f8577d3

Browse files
committed
feat(datastores): auto-enrich containers at startup with --enrichment flag
Containers discovered at startup are now automatically enriched with runtime metadata when --enrichment container is enabled, ensuring ListContainers() returns fully populated data immediately.
1 parent 0f549e0 commit f8577d3

2 files changed

Lines changed: 71 additions & 17 deletions

File tree

docs/docs/detectors/datastore-api.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,10 @@ func (d *MyDetector) Init(params detection.DetectorParams) error {
713713
- Returns only live, running containers (excludes expired/dead)
714714
- Cleaner alternative to buffering `existing_container` events
715715

716+
**Container Enrichment**:
717+
718+
When `--enrichment container` flag is used, containers discovered at startup are automatically enriched with runtime metadata (name, image, digest, pod information). Without enrichment, only container ID and runtime are available from cgroup paths.
719+
716720
---
717721

718722
## SystemStore

pkg/datastores/container/containers.go

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,15 @@ type Pod struct {
4141

4242
// Manager contains information about running containers in the host.
4343
type Manager struct {
44-
cgroups *cgroup.Cgroups
45-
cgroupsMap map[uint32]CgroupDir
46-
containerMap map[string]Container
47-
deleted []uint64
48-
lock sync.RWMutex // protecting both cgroups and deleted fields
49-
enricher runtime.Service
50-
bpfMapName string
51-
lastAccessNano atomic.Int64 // last datastore access time (Unix nano)
44+
cgroups *cgroup.Cgroups
45+
cgroupsMap map[uint32]CgroupDir
46+
containerMap map[string]Container
47+
deleted []uint64
48+
lock sync.RWMutex // protecting both cgroups and deleted fields
49+
enricher runtime.Service
50+
enrichmentEnabled bool
51+
bpfMapName string
52+
lastAccessNano atomic.Int64 // last datastore access time (Unix nano)
5253
}
5354

5455
// CgroupDir represents a cgroup dir (which may be a container cgroup dir).
@@ -73,11 +74,12 @@ func New(
7374
error,
7475
) {
7576
containers := &Manager{
76-
cgroups: cgroups,
77-
cgroupsMap: make(map[uint32]CgroupDir),
78-
containerMap: make(map[string]Container),
79-
lock: sync.RWMutex{},
80-
bpfMapName: mapName,
77+
cgroups: cgroups,
78+
cgroupsMap: make(map[uint32]CgroupDir),
79+
containerMap: make(map[string]Container),
80+
lock: sync.RWMutex{},
81+
enrichmentEnabled: enrichmentEnabled,
82+
bpfMapName: mapName,
8183
}
8284

8385
// Attempt to register enrichers for all supported runtimes.
@@ -111,9 +113,11 @@ func New(
111113

112114
// Close executes cleanup logic for Containers object.
113115
func (c *Manager) Close() error {
114-
err := c.enricher.Close()
115-
if err != nil {
116-
return errfmt.WrapError(err)
116+
if c.enrichmentEnabled {
117+
err := c.enricher.Close()
118+
if err != nil {
119+
return errfmt.WrapError(err)
120+
}
117121
}
118122
return nil
119123
}
@@ -129,7 +133,53 @@ func (c *Manager) GetCgroupVersion() cgroup.CgroupVersion {
129133
// Initialize initializes the container manager by populating existing containers.
130134
// This method is called automatically by the datastore registry during initialization.
131135
func (c *Manager) Initialize(ctx context.Context) error {
132-
return c.Populate()
136+
// First, populate containers from cgroup filesystem
137+
if err := c.Populate(); err != nil {
138+
return err
139+
}
140+
141+
// If enrichment is enabled, proactively enrich all discovered containers
142+
if c.enrichmentEnabled {
143+
c.enrichExistingContainers()
144+
}
145+
146+
return nil
147+
}
148+
149+
// enrichExistingContainers enriches all container root cgroups discovered during population.
150+
// This is called during initialization when enrichment is enabled to ensure containers
151+
// have full metadata (name, image, etc.) available immediately via ListContainers API.
152+
func (c *Manager) enrichExistingContainers() {
153+
c.lock.RLock()
154+
var cgroupsToEnrich []uint32
155+
for cgroupId, cgroupDir := range c.cgroupsMap {
156+
// Only enrich container root cgroups that are live
157+
if cgroupDir.ContainerRoot && cgroupDir.expiresAt.IsZero() {
158+
cgroupsToEnrich = append(cgroupsToEnrich, cgroupId)
159+
}
160+
}
161+
c.lock.RUnlock()
162+
163+
logger.Debugw("Enriching existing containers at startup",
164+
"count", len(cgroupsToEnrich))
165+
166+
// Enrich each container (don't hold read lock during enrichment)
167+
enrichedCount := 0
168+
for _, cgroupId := range cgroupsToEnrich {
169+
_, err := c.EnrichCgroupInfo(uint64(cgroupId))
170+
if err != nil {
171+
// Log but don't fail - some containers may not be enrichable
172+
logger.Debugw("Failed to enrich existing container",
173+
"cgroup_id", cgroupId,
174+
"error", err)
175+
} else {
176+
enrichedCount++
177+
}
178+
}
179+
180+
logger.Debugw("Container enrichment at startup completed",
181+
"enriched", enrichedCount,
182+
"total", len(cgroupsToEnrich))
133183
}
134184

135185
// Populate populates Containers struct by reading mounted proc and cgroups fs.

0 commit comments

Comments
 (0)