diff --git a/container/docker/factory.go b/container/docker/factory.go index 05ae89d9..84d50712 100644 --- a/container/docker/factory.go +++ b/container/docker/factory.go @@ -25,6 +25,7 @@ import ( "github.com/docker/libcontainer/cgroups/systemd" "github.com/fsouza/go-dockerclient" "github.com/google/cadvisor/container" + "github.com/google/cadvisor/container/libcontainer" "github.com/google/cadvisor/info" ) @@ -74,7 +75,7 @@ func (self *dockerFactory) CanHandle(name string) bool { return false } // Check if the container is known to docker and it is active. - _, id, err := splitName(name) + _, id, err := libcontainer.SplitName(name) if err != nil { return false } diff --git a/container/docker/handler.go b/container/docker/handler.go index 76dbfc04..f2f383b4 100644 --- a/container/docker/handler.go +++ b/container/docker/handler.go @@ -15,14 +15,12 @@ package docker import ( - "bufio" "encoding/json" "errors" "fmt" "math" "os" "path" - "path/filepath" "strings" "github.com/docker/libcontainer" @@ -42,7 +40,7 @@ type dockerContainerHandler struct { client *docker.Client name string parent string - ID string + id string aliases []string machineInfoFactory info.MachineInfoFactory useSystemd bool @@ -63,12 +61,12 @@ func newDockerContainerHandler( if handler.isDockerRoot() { return handler, nil } - parent, id, err := splitName(name) + parent, id, err := containerLibcontainer.SplitName(name) if err != nil { return nil, fmt.Errorf("invalid docker container %v: %v", name, err) } handler.parent = parent - handler.ID = id + handler.id = id ctnr, err := client.InspectContainer(id) // We assume that if Inspect fails then the container is not known to docker. if err != nil { @@ -89,45 +87,9 @@ func (self *dockerContainerHandler) isDockerRoot() bool { return self.name == "/docker" } -func splitName(containerName string) (string, string, error) { - parent, id := path.Split(containerName) - cgroupSelf, err := os.Open("/proc/self/cgroup") - if err != nil { - return "", "", err - } - scanner := bufio.NewScanner(cgroupSelf) - - subsys := []string{"memory", "cpu"} - nestedLevels := 0 - for scanner.Scan() { - line := scanner.Text() - elems := strings.Split(line, ":") - if len(elems) < 3 { - continue - } - for _, s := range subsys { - if elems[1] == s { - // count how many nested docker containers are there. - nestedLevels = strings.Count(elems[2], "/docker") - break - } - } - } - if nestedLevels > 0 { - // we are running inside a docker container - upperLevel := strings.Repeat("../../", nestedLevels) - parent = filepath.Join(upperLevel, parent) - } - // Strip the last "/" - if parent[len(parent)-1] == '/' { - parent = parent[:len(parent)-1] - } - return parent, id, nil -} - // TODO(vmarmol): Switch to getting this from libcontainer once we have a solid API. func (self *dockerContainerHandler) readLibcontainerConfig() (config *libcontainer.Config, err error) { - configPath := path.Join(dockerRootDir, self.ID, "container.json") + configPath := path.Join(dockerRootDir, self.id, "container.json") if !utils.FileExists(configPath) { // TODO(vishh): Return file name as well once we have a better error interface. err = fileNotFound @@ -146,11 +108,15 @@ func (self *dockerContainerHandler) readLibcontainerConfig() (config *libcontain } config = retConfig + // Replace cgroup parent and name with our own since we may be running in a different context. + config.Cgroups.Parent = self.parent + config.Cgroups.Name = self.id + return } func (self *dockerContainerHandler) readLibcontainerState() (state *libcontainer.State, err error) { - statePath := path.Join(dockerRootDir, self.ID, "state.json") + statePath := path.Join(dockerRootDir, self.id, "state.json") if !utils.FileExists(statePath) { // TODO(vishh): Return file name as well once we have a better error interface. err = fileNotFound diff --git a/container/libcontainer/helpers.go b/container/libcontainer/helpers.go index e5ace212..315bdf67 100644 --- a/container/libcontainer/helpers.go +++ b/container/libcontainer/helpers.go @@ -1,6 +1,11 @@ package libcontainer import ( + "bufio" + "os" + "path" + "path/filepath" + "strings" "time" "github.com/docker/libcontainer" @@ -64,3 +69,48 @@ func toContainerStats(libcontainerStats *libcontainer.ContainerStats) *info.Cont return ret } + +// Given a container name, returns the parent and name of the container to be fed to libcontainer. +func SplitName(containerName string) (string, string, error) { + parent, id := path.Split(containerName) + cgroupSelf, err := os.Open("/proc/self/cgroup") + if err != nil { + return "", "", err + } + scanner := bufio.NewScanner(cgroupSelf) + + // Find how nested we are. Libcontainer takes container names relative to the current process. + subsys := []string{"memory", "cpu"} + nestedLevels := 0 + for scanner.Scan() { + line := scanner.Text() + elems := strings.Split(line, ":") + if len(elems) < 3 { + continue + } + for _, s := range subsys { + if elems[1] == s { + if elems[2] == "/" { + // We're running at root, no nesting. + nestedLevels = 0 + } else { + // Count how deeply nested we are. + nestedLevels = strings.Count(elems[2], "/") + } + break + } + } + } + if nestedLevels > 0 { + // we are running inside a docker container + upperLevel := strings.Repeat("../", nestedLevels) + parent = filepath.Join(upperLevel, parent) + } + + // Strip the last "/" + if parent[len(parent)-1] == '/' { + parent = parent[:len(parent)-1] + } + + return parent, id, nil +} diff --git a/container/raw/handler.go b/container/raw/handler.go index c1424a6b..4345f0bf 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -31,13 +31,22 @@ import ( type rawContainerHandler struct { name string + cgroup *cgroups.Cgroup cgroupSubsystems *cgroupSubsystems machineInfoFactory info.MachineInfoFactory } func newRawContainerHandler(name string, cgroupSubsystems *cgroupSubsystems, machineInfoFactory info.MachineInfoFactory) (container.ContainerHandler, error) { + parent, id, err := libcontainer.SplitName(name) + if err != nil { + return nil, err + } return &rawContainerHandler{ - name: name, + name: name, + cgroup: &cgroups.Cgroup{ + Parent: parent, + Name: id, + }, cgroupSubsystems: cgroupSubsystems, machineInfoFactory: machineInfoFactory, }, nil @@ -116,12 +125,7 @@ func (self *rawContainerHandler) GetSpec() (*info.ContainerSpec, error) { } func (self *rawContainerHandler) GetStats() (stats *info.ContainerStats, err error) { - cgroup := &cgroups.Cgroup{ - Parent: "/", - Name: self.name, - } - - return libcontainer.GetStatsCgroupOnly(cgroup) + return libcontainer.GetStatsCgroupOnly(self.cgroup) } // Lists all directories under "path" and outputs the results as children of "parent".