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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add method of overriding the /sys/fs/cgroup hierarchy, for reading cgroup metrics inside Docker [#148](https://github.com/elastic/gosigar/pull/148)

### Fixed

### Changed
Expand Down
56 changes: 45 additions & 11 deletions cgroup/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,63 @@ type mount struct {
type Reader struct {
// Mountpoint of the root filesystem. Defaults to / if not set. This can be
// useful for example if you mount / as /rootfs inside of a container.
rootfsMountpoint string
ignoreRootCgroups bool // Ignore a cgroup when its path is "/".
cgroupMountpoints map[string]string // Mountpoints for each subsystem (e.g. cpu, cpuacct, memory, blkio).
rootfsMountpoint string
ignoreRootCgroups bool // Ignore a cgroup when its path is "/".
cgroupsHierarchyOverride string
cgroupMountpoints map[string]string // Mountpoints for each subsystem (e.g. cpu, cpuacct, memory, blkio).
}

// ReaderOptions holds options for NewReaderOptions.
type ReaderOptions struct {
// RootfsMountpoint holds the mountpoint of the root filesystem.
//
// If unspecified, "/" is assumed.
RootfsMountpoint string

// IgnoreRootCgroups ignores cgroup subsystem with the path "/".
IgnoreRootCgroups bool

// CgroupsHierarchyOverride is an optional path override for cgroup
// subsystem paths. If non-empty, this will be used instead of the
// paths specified in /proc/<pid>/cgroup.
//
// This should be set to "/" when running within a Docker container,
// where the paths in /proc/<pid>/cgroup do not correspond to any
// paths under /sys/fs/cgroup.
CgroupsHierarchyOverride string
}

// NewReader creates and returns a new Reader.
func NewReader(rootfsMountpoint string, ignoreRootCgroups bool) (*Reader, error) {
if rootfsMountpoint == "" {
rootfsMountpoint = "/"
return NewReaderOptions(ReaderOptions{
RootfsMountpoint: rootfsMountpoint,
IgnoreRootCgroups: ignoreRootCgroups,
})
}

// NewReaderOptions creates and returns a new Reader with the given options.
func NewReaderOptions(opts ReaderOptions) (*Reader, error) {
if opts.RootfsMountpoint == "" {
opts.RootfsMountpoint = "/"
}

// Determine what subsystems are supported by the kernel.
subsystems, err := SupportedSubsystems(rootfsMountpoint)
subsystems, err := SupportedSubsystems(opts.RootfsMountpoint)
if err != nil {
return nil, err
}

// Locate the mountpoints of those subsystems.
mountpoints, err := SubsystemMountpoints(rootfsMountpoint, subsystems)
mountpoints, err := SubsystemMountpoints(opts.RootfsMountpoint, subsystems)
if err != nil {
return nil, err
}

return &Reader{
rootfsMountpoint: rootfsMountpoint,
ignoreRootCgroups: ignoreRootCgroups,
cgroupMountpoints: mountpoints,
rootfsMountpoint: opts.RootfsMountpoint,
ignoreRootCgroups: opts.IgnoreRootCgroups,
cgroupsHierarchyOverride: opts.CgroupsHierarchyOverride,
cgroupMountpoints: mountpoints,
}, nil
}

Expand Down Expand Up @@ -86,11 +116,15 @@ func (r *Reader) GetStatsForProcess(pid int) (*Stats, error) {
continue
}

id := filepath.Base(path)
if r.cgroupsHierarchyOverride != "" {
path = r.cgroupsHierarchyOverride
}
mounts[interestedSubsystem] = mount{
subsystem: interestedSubsystem,
mountpoint: subsystemMount,
id: id,
path: path,
id: filepath.Base(path),
fullPath: filepath.Join(subsystemMount, path),
}
}
Expand Down
31 changes: 31 additions & 0 deletions cgroup/reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const (
Expand Down Expand Up @@ -45,3 +46,33 @@ func TestReaderGetStats(t *testing.T) {

t.Log(string(json))
}

func TestReaderGetStatsHierarchyOverride(t *testing.T) {
// In testdata/docker, process 1's cgroup paths have
// no corresponding paths under /sys/fs/cgroup/<subsystem>.
//
// Setting CgroupsHierarchyOverride means that we use
// the root cgroup path instead. This is intended to test
// the scenario where we're reading cgroup metrics from
// within a Docker container.

reader, err := NewReaderOptions(ReaderOptions{
RootfsMountpoint: "testdata/docker",
IgnoreRootCgroups: true,
CgroupsHierarchyOverride: "/",
})
if err != nil {
t.Fatal(err)
}

stats, err := reader.GetStatsForProcess(1)
if err != nil {
t.Fatal(err)
}
if stats == nil {
t.Fatal("no cgroup stats found")
}

require.NotNil(t, stats.CPU)
assert.NotZero(t, stats.CPU.CFS.Shares)
}
10 changes: 10 additions & 0 deletions cgroup/testdata/docker/proc/1/cgroup
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
10:net_prio:/docker/123
9:perf_event:/docker/123
8:net_cls:/docker/123
7:freezer:/docker/123
6:devices:/docker/123
5:memory:/docker/123
4:blkio:/docker/123
3:cpuacct:/docker/123
2:cpu:/docker/123
1:cpuset:/docker/123