Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion cgroup/cgroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ type Cgroup struct {
ContainerId string

subsystems map[string]string

// initScope: the process is in systemd's /init.scope, where systemd
// forks a unit's process before moving it to the unit's cgroup.
initScope bool
}

// InitScope reports whether the process is in systemd's /init.scope. Its Id
// is empty then, as for the root cgroup.
func (cg *Cgroup) InitScope() bool {
return cg.initScope
}

func (cg *Cgroup) getId() string {
Expand Down Expand Up @@ -139,7 +149,10 @@ func NewFromProcessCgroupFile(filePath string) (*Cgroup, error) {
}
p := path.Join(baseCgroupPath, cgPath)
switch p {
case "/", "/init.scope":
case "/init.scope":
cg.initScope = true
continue
case "/":
continue
}
cg.subsystems[cgType] = p
Expand Down
22 changes: 15 additions & 7 deletions cgroup/cgroup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,16 +238,24 @@ func TestContainerByCgroup(t *testing.T) {
as.Nil(err)
}

// The registry relies on a process in systemd's /init.scope, or in the root
// cgroup, having an empty Id: it is not cached as ignored, so its exec after
// systemd moves it to a unit's cgroup is seen.
// The registry does not cache a process in systemd's /init.scope as ignored,
// so its exec after systemd moves it to a unit's cgroup is seen. A process in
// the root cgroup (hosts without systemd) is cached as usual.
func TestInitScopeAndRootHaveEmptyId(t *testing.T) {
for _, content := range []string{"0::/init.scope\n", "0::/\n"} {
for _, c := range []struct {
content string
initScope bool
}{
{"0::/init.scope\n", true},
{"1:name=systemd:/init.scope\n2:cpu,cpuacct:/\n", true},
{"0::/\n", false},
} {
f := path.Join(t.TempDir(), "cgroup")
require.NoError(t, os.WriteFile(f, []byte(content), 0644))
require.NoError(t, os.WriteFile(f, []byte(c.content), 0644))
cg, err := NewFromProcessCgroupFile(f)
require.NoError(t, err)
assert.Equal(t, "", cg.Id, content)
assert.Equal(t, ContainerTypeStandaloneProcess, cg.ContainerType, content)
assert.Equal(t, "", cg.Id, c.content)
assert.Equal(t, ContainerTypeStandaloneProcess, cg.ContainerType, c.content)
assert.Equal(t, c.initScope, cg.InitScope(), c.content)
}
}
10 changes: 3 additions & 7 deletions containers/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,19 +643,15 @@ func (r *Registry) getOrCreateContainer(pid uint32) *Container {
// systemd forks a unit's process inside its own /init.scope and
// moves it to the unit's cgroup before exec. Caching the pid as
// ignored here would drop that exec, and a unit that does nothing
// else would never be detected. /init.scope and the root cgroup
// both parse to an empty Id (cgroup.go skips them).
if cg.Id == "" && pid != 1 {
// else would never be detected. Processes in the root cgroup (hosts
// without systemd) are cached as before.
if cg.InitScope() && pid != 1 {
klog.V(5).InfoS("ignoring without persisting", "cg", cg.Id, "pid", pid)
} else {
klog.V(5).InfoS("ignoring", "cg", cg.Id, "pid", pid)
r.containerLock.Lock()
t := time.Now()
r.containersByPidIgnored[pid] = &t
// Clean up stale ignored PIDs while we have the lock
if oldT := r.containersByPidIgnored[pid]; oldT != nil && time.Since(*oldT) >= IgnoredContainersCacheTTL {
delete(r.containersByPidIgnored, pid)
}
r.containerLock.Unlock()
}
return nil
Expand Down
Loading