Allow partial success of GetStats().

This should make us more robust in the face of failure (at the cost of
making the failures less prominent). We allow GetStats() to return an
error and a partial result. We will process the result and report the
error.

Fixes #306.
This commit is contained in:
Victor Marmol 2015-02-03 15:26:31 -08:00
parent 01b456c129
commit 86238d0179
4 changed files with 16 additions and 11 deletions

View File

@ -82,6 +82,7 @@ func newDockerContainerHandler(
usesAufsDriver bool, usesAufsDriver bool,
cgroupSubsystems *containerLibcontainer.CgroupSubsystems, cgroupSubsystems *containerLibcontainer.CgroupSubsystems,
) (container.ContainerHandler, error) { ) (container.ContainerHandler, error) {
// TODO(vmarmol): Get from factory.
fsInfo, err := fs.NewFsInfo() fsInfo, err := fs.NewFsInfo()
if err != nil { if err != nil {
return nil, err return nil, err
@ -299,16 +300,16 @@ func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error
func (self *dockerContainerHandler) GetStats() (stats *info.ContainerStats, err error) { func (self *dockerContainerHandler) GetStats() (stats *info.ContainerStats, err error) {
state, err := self.readLibcontainerState() state, err := self.readLibcontainerState()
if err != nil { if err != nil {
return return nil, err
} }
stats, err = containerLibcontainer.GetStats(self.cgroupPaths, state) stats, err = containerLibcontainer.GetStats(self.cgroupPaths, state)
if err != nil { if err != nil {
return return stats, err
} }
err = self.getFsStats(stats) err = self.getFsStats(stats)
if err != nil { if err != nil {
return return stats, err
} }
return stats, nil return stats, nil

View File

@ -76,6 +76,7 @@ func newRawContainerHandler(name string, cgroupSubsystems *libcontainer.CgroupSu
cgroupPaths[key] = path.Join(val, name) cgroupPaths[key] = path.Join(val, name)
} }
// TODO(vmarmol): Get from factory.
fsInfo, err := fs.NewFsInfo() fsInfo, err := fs.NewFsInfo()
if err != nil { if err != nil {
return nil, err return nil, err
@ -303,12 +304,12 @@ func (self *rawContainerHandler) getFsStats(stats *info.ContainerStats) error {
func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) {
stats, err := libcontainer.GetStats(self.cgroupPaths, &self.libcontainerState) stats, err := libcontainer.GetStats(self.cgroupPaths, &self.libcontainerState)
if err != nil { if err != nil {
return nil, err return stats, err
} }
err = self.getFsStats(stats) err = self.getFsStats(stats)
if err != nil { if err != nil {
return nil, err return stats, err
} }
// Fill in network stats for root. // Fill in network stats for root.

View File

@ -67,6 +67,7 @@ func NewFsInfo() (FsInfo, error) {
} }
partitions[mount.Source] = partition{mount.Mountpoint, uint(mount.Major), uint(mount.Minor)} partitions[mount.Source] = partition{mount.Mountpoint, uint(mount.Major), uint(mount.Minor)}
} }
glog.Infof("Filesystem partitions: %+v", partitions)
return &RealFsInfo{partitions}, nil return &RealFsInfo{partitions}, nil
} }
@ -80,7 +81,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er
for device, partition := range self.partitions { for device, partition := range self.partitions {
_, hasMount := mountSet[partition.mountpoint] _, hasMount := mountSet[partition.mountpoint]
_, hasDevice := deviceSet[device] _, hasDevice := deviceSet[device]
if mountSet == nil || hasMount && !hasDevice { if mountSet == nil || (hasMount && !hasDevice) {
total, free, err := getVfsStats(partition.mountpoint) total, free, err := getVfsStats(partition.mountpoint)
if err != nil { if err != nil {
glog.Errorf("Statvfs failed. Error: %v", err) glog.Errorf("Statvfs failed. Error: %v", err)

View File

@ -244,18 +244,20 @@ func (c *containerData) updateLoad(newLoad uint64) {
} }
func (c *containerData) updateStats() error { func (c *containerData) updateStats() error {
stats, err := c.handler.GetStats() stats, statsErr := c.handler.GetStats()
if err != nil { if statsErr != nil {
// Ignore errors if the container is dead. // Ignore errors if the container is dead.
if !c.handler.Exists() { if !c.handler.Exists() {
return nil return nil
} }
return err
// Stats may be partially populated, push those before we return an error.
} }
if stats == nil { if stats == nil {
return nil return statsErr
} }
if c.loadReader != nil { if c.loadReader != nil {
// TODO(vmarmol): Cache this path.
path, err := c.handler.GetCgroupPath("cpu") path, err := c.handler.GetCgroupPath("cpu")
if err == nil { if err == nil {
loadStats, err := c.loadReader.GetCpuLoad(c.info.Name, path) loadStats, err := c.loadReader.GetCpuLoad(c.info.Name, path)
@ -280,7 +282,7 @@ func (c *containerData) updateStats() error {
if err != nil { if err != nil {
return err return err
} }
return nil return statsErr
} }
func (c *containerData) updateSubcontainers() error { func (c *containerData) updateSubcontainers() error {