Commit ec19d41b authored by Rohit Jnagal's avatar Rohit Jnagal

Merge pull request #8141 from vmarmol/cadvisor

Update cAdvisor godep.
parents 60ebcf61 4664b395
......@@ -206,88 +206,88 @@
},
{
"ImportPath": "github.com/google/cadvisor/api",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/collector",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/container",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/events",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/fs",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/healthz",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/http",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/info/v1",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/info/v2",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/manager",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/metrics",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/pages",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/storage",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/summary",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/utils",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/validate",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/cadvisor/version",
"Comment": "0.13.0-32-g832c6e9",
"Rev": "832c6e94c325ecdf05da884880c503be45594818"
"Comment": "0.13.0-48-g9535dd2",
"Rev": "9535dd21e2aca94f024528679d22e3637178cda0"
},
{
"ImportPath": "github.com/google/go-github/github",
......
......@@ -38,6 +38,7 @@ const (
storageApi = "storage"
attributesApi = "attributes"
versionApi = "version"
psApi = "ps"
)
// Interface for a cAdvisor API version
......@@ -304,7 +305,7 @@ func (self *version2_0) Version() string {
}
func (self *version2_0) SupportedRequestTypes() []string {
return []string{versionApi, attributesApi, eventsApi, machineApi, summaryApi, statsApi, specApi, storageApi}
return []string{versionApi, attributesApi, eventsApi, machineApi, summaryApi, statsApi, specApi, storageApi, psApi}
}
func (self *version2_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
......@@ -391,6 +392,17 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
return writeResult(fi, w)
case eventsApi:
return handleEventRequest(request, m, w, r)
case psApi:
// reuse container type from request.
// ignore recursive.
// TODO(rjnagal): consider count to limit ps output.
name := getContainerName(request)
glog.V(4).Infof("Api - Spec for container %q, options %+v", name, opt)
ps, err := m.GetProcessList(name, opt)
if err != nil {
return fmt.Errorf("process listing failed: %v", err)
}
return writeResult(ps, w)
default:
return fmt.Errorf("unknown request type %q", requestType)
}
......
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package base
import (
"time"
"github.com/google/cadvisor/collector"
"github.com/google/cadvisor/info/v2"
)
type Collector struct {
}
var _ collector.Collector = &Collector{}
func (c *Collector) Collect() (time.Time, []v2.Metric, error) {
return time.Now(), []v2.Metrics{}, nil
}
func (c *Collector) Name() string {
return "default"
}
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package base
import ()
......@@ -18,6 +18,8 @@ import (
"fmt"
"strings"
"time"
"github.com/google/cadvisor/info/v2"
)
type collectorManager struct {
......@@ -46,17 +48,19 @@ func (cm *collectorManager) RegisterCollector(collector Collector) error {
return nil
}
func (cm *collectorManager) Collect() (time.Time, error) {
func (cm *collectorManager) Collect() (time.Time, []v2.Metric, error) {
var errors []error
// Collect from all collectors that are ready.
var next time.Time
var metrics []v2.Metric
for _, c := range cm.collectors {
if c.nextCollectionTime.Before(time.Now()) {
nextCollection, err := c.collector.Collect()
nextCollection, newMetrics, err := c.collector.Collect()
if err != nil {
errors = append(errors, err)
}
metrics = append(metrics, newMetrics...)
c.nextCollectionTime = nextCollection
}
......@@ -66,7 +70,7 @@ func (cm *collectorManager) Collect() (time.Time, error) {
}
}
return next, compileErrors(errors)
return next, metrics, compileErrors(errors)
}
// Make an error slice into a single error.
......
......@@ -18,6 +18,7 @@ import (
"testing"
"time"
"github.com/google/cadvisor/info/v2"
"github.com/stretchr/testify/assert"
)
......@@ -27,9 +28,9 @@ type fakeCollector struct {
collectedFrom int
}
func (fc *fakeCollector) Collect() (time.Time, error) {
func (fc *fakeCollector) Collect() (time.Time, []v2.Metric, error) {
fc.collectedFrom++
return fc.nextCollectionTime, fc.err
return fc.nextCollectionTime, []v2.Metric{}, fc.err
}
func (fc *fakeCollector) Name() string {
......@@ -53,7 +54,7 @@ func TestCollect(t *testing.T) {
assert.NoError(cm.RegisterCollector(f2))
// First collection, everyone gets collected from.
nextTime, err := cm.Collect()
nextTime, _, err := cm.Collect()
assert.Equal(firstTime, nextTime)
assert.NoError(err)
assert.Equal(1, f1.collectedFrom)
......@@ -62,7 +63,7 @@ func TestCollect(t *testing.T) {
f1.nextCollectionTime = time.Now().Add(2 * time.Hour)
// Second collection, only the one that is ready gets collected from.
nextTime, err = cm.Collect()
nextTime, _, err = cm.Collect()
assert.Equal(secondTime, nextTime)
assert.NoError(err)
assert.Equal(2, f1.collectedFrom)
......
......@@ -16,6 +16,8 @@ package collector
import (
"time"
"github.com/google/cadvisor/info/v2"
)
type FakeCollectorManager struct {
......@@ -25,7 +27,7 @@ func (fkm *FakeCollectorManager) RegisterCollector(collector Collector) error {
return nil
}
func (fkm *FakeCollectorManager) Collect() (time.Time, error) {
func (fkm *FakeCollectorManager) Collect() (time.Time, []v2.Metric, error) {
var zero time.Time
return zero, nil
return zero, []v2.Metric{}, nil
}
......@@ -15,6 +15,7 @@
package collector
import (
"github.com/google/cadvisor/info/v2"
"time"
)
......@@ -26,7 +27,7 @@ type Collector interface {
// Returns the next time this collector should be collected from.
// Next collection time is always returned, even when an error occurs.
// A collection time of zero means no more collection.
Collect() (time.Time, error)
Collect() (time.Time, []v2.Metric, error)
// Name of this collector.
Name() string
......@@ -41,5 +42,5 @@ type CollectorManager interface {
// at which a collector will be ready to collect from.
// Next collection time is always returned, even when an error occurs.
// A collection time of zero means no more collection.
Collect() (time.Time, error)
Collect() (time.Time, []v2.Metric, error)
}
......@@ -325,8 +325,7 @@ func (self *dockerContainerHandler) ListThreads(listType container.ListType) ([]
}
func (self *dockerContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
// TODO(vmarmol): Implement.
return nil, nil
return containerLibcontainer.GetProcesses(self.cgroupManager)
}
func (self *dockerContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error {
......
......@@ -96,6 +96,14 @@ func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string) (*info.
return stats, nil
}
func GetProcesses(cgroupManager cgroups.Manager) ([]int, error) {
pids, err := cgroupManager.GetPids()
if err != nil {
return nil, err
}
return pids, nil
}
func DockerStateDir(dockerRoot string) string {
return path.Join(dockerRoot, "containers")
}
......@@ -186,10 +194,21 @@ func toContainerStats2(s *cgroups.Stats, ret *info.ContainerStats) {
ret.Memory.HierarchicalData.Pgmajfault = v
}
if v, ok := s.MemoryStats.Stats["total_inactive_anon"]; ok {
ret.Memory.WorkingSet = ret.Memory.Usage - v
if v, ok := s.MemoryStats.Stats["total_active_file"]; ok {
ret.Memory.WorkingSet -= v
workingSet := ret.Memory.Usage
if workingSet < v {
workingSet = 0
} else {
workingSet -= v
}
if v, ok := s.MemoryStats.Stats["total_inactive_file"]; ok {
if workingSet < v {
workingSet = 0
} else {
workingSet -= v
}
}
ret.Memory.WorkingSet = workingSet
}
}
......
......@@ -264,6 +264,7 @@ func (self *rawContainerHandler) getFsStats(stats *info.ContainerStats) error {
Device: fs.Device,
Limit: fs.Capacity,
Usage: fs.Capacity - fs.Free,
Available: fs.Available,
ReadsCompleted: fs.DiskStats.ReadsCompleted,
ReadsMerged: fs.DiskStats.ReadsMerged,
SectorsRead: fs.DiskStats.SectorsRead,
......@@ -398,8 +399,7 @@ func (self *rawContainerHandler) ListThreads(listType container.ListType) ([]int
}
func (self *rawContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
// TODO(vmarmol): Implement
return nil, nil
return libcontainer.GetProcesses(self.cgroupManager)
}
func (self *rawContainerHandler) watchDirectory(dir string, containerName string) error {
......
......@@ -20,6 +20,7 @@ package fs
/*
extern int getBytesFree(const char *path, unsigned long long *bytes);
extern int getBytesTotal(const char *path, unsigned long long *bytes);
extern int getBytesAvail(const char *path, unsigned long long *bytes);
*/
import "C"
......@@ -176,7 +177,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er
_, hasMount := mountSet[partition.mountpoint]
_, hasDevice := deviceSet[device]
if mountSet == nil || (hasMount && !hasDevice) {
total, free, err := getVfsStats(partition.mountpoint)
total, free, avail, err := getVfsStats(partition.mountpoint)
if err != nil {
glog.Errorf("Statvfs failed. Error: %v", err)
} else {
......@@ -186,7 +187,7 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er
Major: uint(partition.major),
Minor: uint(partition.minor),
}
fs := Fs{deviceInfo, total, free, diskStatsMap[device]}
fs := Fs{deviceInfo, total, free, avail, diskStatsMap[device]}
filesystems = append(filesystems, fs)
}
}
......@@ -287,18 +288,22 @@ func (self *RealFsInfo) GetDirUsage(dir string) (uint64, error) {
return usageInKb * 1024, nil
}
func getVfsStats(path string) (total uint64, free uint64, err error) {
func getVfsStats(path string) (total uint64, free uint64, avail uint64, err error) {
_p0, err := syscall.BytePtrFromString(path)
if err != nil {
return 0, 0, err
return 0, 0, 0, err
}
res, err := C.getBytesFree((*C.char)(unsafe.Pointer(_p0)), (*_Ctype_ulonglong)(unsafe.Pointer(&free)))
if res != 0 {
return 0, 0, err
return 0, 0, 0, err
}
res, err = C.getBytesTotal((*C.char)(unsafe.Pointer(_p0)), (*_Ctype_ulonglong)(unsafe.Pointer(&total)))
if res != 0 {
return 0, 0, err
return 0, 0, 0, err
}
return total, free, nil
res, err = C.getBytesAvail((*C.char)(unsafe.Pointer(_p0)), (*_Ctype_ulonglong)(unsafe.Pointer(&avail)))
if res != 0 {
return 0, 0, 0, err
}
return total, free, avail, nil
}
......@@ -21,3 +21,14 @@ int getBytesTotal(const char *path, unsigned long long *bytes) {
*bytes = buf.f_frsize * buf.f_blocks;
return 0;
}
// Bytes available to non-root.
int getBytesAvail(const char *path, unsigned long long *bytes) {
struct statvfs buf;
int res;
if ((res = statvfs(path, &buf)) && res != 0) {
return -1;
}
*bytes = buf.f_frsize * buf.f_bavail;
return 0;
}
......@@ -24,6 +24,7 @@ type Fs struct {
DeviceInfo
Capacity uint64
Free uint64
Available uint64
DiskStats DiskStats
}
......
......@@ -341,6 +341,9 @@ type FsStats struct {
// Number of bytes that is consumed by the container on this filesystem.
Usage uint64 `json:"usage"`
// Number of bytes available for non-root user.
Available uint64 `json:"available"`
// Number of reads completed
// This is the total number of reads completed successfully.
ReadsCompleted uint64 `json:"reads_completed"`
......
......@@ -151,6 +151,9 @@ type FsInfo struct {
// Filesystem usage in bytes.
Capacity uint64 `json:"capacity"`
// Bytes available for non-root use.
Available uint64 `json:"available"`
// Number of bytes used on this filesystem.
Usage uint64 `json:"usage"`
......@@ -166,3 +169,16 @@ type RequestOptions struct {
// Whether to include stats for child subcontainers.
Recursive bool `json:"recursive"`
}
type ProcessInfo struct {
User string `json:"user"`
Pid int `json:"pid"`
Ppid int `json:"parent_pid"`
StartTime string `json:"start_time"`
PercentCpu string `json:"percent_cpu"`
RSS string `json:"rss"`
VirtualSize string `json:"virtual_size"`
Status string `json:"status"`
RunningTime string `json:"running_time"`
Cmd string `json:"cmd"`
}
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v2
import (
"time"
)
// Type of metric being exported.
type MetricType string
const (
// Instantaneous value. May increase or decrease.
MetricGauge MetricType = "gauge"
// A counter-like value that is only expected to increase.
MetricCumulative = "cumulative"
// Rate over a time period.
MetricDelta = "delta"
)
// An exported metric.
type Metric struct {
// The name of the metric.
Name string `json:"name"`
// Type of the metric.
Type MetricType `json:"type"`
// Metadata associated with this metric.
Labels map[string]string
// Value of the metric. Only one of these values will be
// available according to the output type of the metric.
// If no values are available, there are no data points.
IntPoints []IntPoint `json:"int_points,omitempty"`
FloatPoints []FloatPoint `json:"float_points,omitempty"`
}
// An integer metric data point.
type IntPoint struct {
// Time at which the metric was queried
Timestamp time.Time `json:"timestamp"`
// The value of the metric at this point.
Value int64 `json:"value"`
}
// A float metric data point.
type FloatPoint struct {
// Time at which the metric was queried
Timestamp time.Time `json:"timestamp"`
// The value of the metric at this point.
Value float64 `json:"value"`
}
......@@ -18,7 +18,10 @@ import (
"flag"
"fmt"
"math"
"os/exec"
"sort"
"strconv"
"strings"
"sync"
"time"
......@@ -113,6 +116,63 @@ func (c *containerData) DerivedStats() (v2.DerivedStats, error) {
return c.summaryReader.DerivedStats()
}
func (c *containerData) GetProcessList() ([]v2.ProcessInfo, error) {
// report all processes for root.
isRoot := c.info.Name == "/"
pidMap := map[int]bool{}
if !isRoot {
pids, err := c.handler.ListProcesses(container.ListSelf)
if err != nil {
return nil, err
}
for _, pid := range pids {
pidMap[pid] = true
}
}
// TODO(rjnagal): Take format as an option?
format := "user,pid,ppid,stime,pcpu,rss,vsz,stat,time,comm"
args := []string{"-e", "-o", format}
expectedFields := 10
out, err := exec.Command("ps", args...).Output()
if err != nil {
return nil, fmt.Errorf("failed to execute ps command: %v", err)
}
processes := []v2.ProcessInfo{}
lines := strings.Split(string(out), "\n")
for _, line := range lines[1:] {
if len(line) == 0 {
continue
}
fields := strings.Fields(line)
if len(fields) < expectedFields {
return nil, fmt.Errorf("expected at least %d fields, found %d: output: %q", expectedFields, len(fields), line)
}
pid, err := strconv.Atoi(fields[1])
if err != nil {
return nil, fmt.Errorf("invalid pid %q: %v", fields[1], err)
}
ppid, err := strconv.Atoi(fields[2])
if err != nil {
return nil, fmt.Errorf("invalid ppid %q: %v", fields[2], err)
}
if isRoot || pidMap[pid] == true {
processes = append(processes, v2.ProcessInfo{
User: fields[0],
Pid: pid,
Ppid: ppid,
StartTime: fields[3],
PercentCpu: fields[4],
RSS: fields[5],
VirtualSize: fields[6],
Status: fields[7],
RunningTime: fields[8],
Cmd: strings.Join(fields[9:], " "),
})
}
}
return processes, nil
}
func newContainerData(containerName string, memoryStorage *memory.InMemoryStorage, handler container.ContainerHandler, loadReader cpuload.CpuLoadReader, logUsage bool, collectorManager collector.CollectorManager) (*containerData, error) {
if memoryStorage == nil {
return nil, fmt.Errorf("nil memory storage")
......@@ -232,8 +292,9 @@ func (c *containerData) housekeeping() {
}
}
// TODO(vmarmol): Export metrics.
// Run custom collectors.
nextCollectionTime, err := c.collectorManager.Collect()
nextCollectionTime, _, err := c.collectorManager.Collect()
if err != nil && c.allowErrorLogging() {
glog.Warningf("[%s] Collection failed: %v", c.info.Name, err)
}
......
......@@ -90,6 +90,9 @@ type Manager interface {
// Returns information for all global filesystems if label is empty.
GetFsInfo(label string) ([]v2.FsInfo, error)
// Get ps output for a container.
GetProcessList(containerName string, options v2.RequestOptions) ([]v2.ProcessInfo, error)
// Get events streamed through passedChannel that fit the request.
WatchForEvents(request *events.Request) (*events.EventChannel, error)
......@@ -609,6 +612,7 @@ func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) {
Mountpoint: mountpoint,
Capacity: fs.Limit,
Usage: fs.Usage,
Available: fs.Available,
Labels: labels,
}
fsInfo = append(fsInfo, fi)
......@@ -640,6 +644,27 @@ func (m *manager) Exists(containerName string) bool {
return false
}
func (m *manager) GetProcessList(containerName string, options v2.RequestOptions) ([]v2.ProcessInfo, error) {
// override recursive. Only support single container listing.
options.Recursive = false
conts, err := m.getRequestedContainers(containerName, options)
if err != nil {
return nil, err
}
if len(conts) != 1 {
return nil, fmt.Errorf("Expected the request to match only one container")
}
// TODO(rjnagal): handle count? Only if we can do count by type (eg. top 5 cpu users)
ps := []v2.ProcessInfo{}
for _, cont := range conts {
ps, err = cont.GetProcessList()
if err != nil {
return nil, err
}
}
return ps, nil
}
// Create a container.
func (m *manager) createContainer(containerName string) error {
handler, accept, err := container.NewContainerHandler(containerName)
......
......@@ -99,3 +99,8 @@ func (c *ManagerMock) GetFsInfo() ([]v2.FsInfo, error) {
args := c.Called()
return args.Get(0).([]v2.FsInfo), args.Error(1)
}
func (c *ManagerMock) GetProcessList(name string, options v2.RequestOptions) ([]v2.ProcessInfo, error) {
args := c.Called()
return args.Get(0).([]v2.ProcessInfo), args.Error(1)
}
......@@ -108,6 +108,12 @@ const containersHtmlTemplate = `
</div>
<div id="usage-gauge" class="panel-body"></div>
</div>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Processes</h3>
</div>
<div id="processes-top" class="panel-body"></div>
</div>
{{if .CpuAvailable}}
<div class="panel panel-primary">
<div class="panel-heading">
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment