diff --git a/container/container.go b/container/container.go index 30383560..ba28854b 100644 --- a/container/container.go +++ b/container/container.go @@ -41,7 +41,7 @@ type SubcontainerEvent struct { // Interface for container operation handlers. type ContainerHandler interface { ContainerReference() (info.ContainerReference, error) - GetSpec() (*info.ContainerSpec, error) + GetSpec() (info.ContainerSpec, error) GetStats() (*info.ContainerStats, error) ListContainers(listType ListType) ([]info.ContainerReference, error) ListThreads(listType ListType) ([]int, error) diff --git a/container/docker/handler.go b/container/docker/handler.go index 296cb7ad..d3dff07d 100644 --- a/container/docker/handler.go +++ b/container/docker/handler.go @@ -149,9 +149,9 @@ func (self *dockerContainerHandler) readLibcontainerState() (state *libcontainer return } -func libcontainerConfigToContainerSpec(config *libcontainer.Config, mi *info.MachineInfo) *info.ContainerSpec { - spec := new(info.ContainerSpec) - spec.Memory = new(info.MemorySpec) +func libcontainerConfigToContainerSpec(config *libcontainer.Config, mi *info.MachineInfo) info.ContainerSpec { + var spec info.ContainerSpec + spec.HasMemory = true spec.Memory.Limit = math.MaxUint64 spec.Memory.SwapLimit = math.MaxUint64 if config.Cgroups.Memory > 0 { @@ -162,7 +162,7 @@ func libcontainerConfigToContainerSpec(config *libcontainer.Config, mi *info.Mac } // Get CPU info - spec.Cpu = new(info.CpuSpec) + spec.HasCpu = true spec.Cpu.Limit = 1024 if config.Cgroups.CpuShares != 0 { spec.Cpu.Limit = uint64(config.Cgroups.CpuShares) @@ -173,12 +173,14 @@ func libcontainerConfigToContainerSpec(config *libcontainer.Config, mi *info.Mac } else { spec.Cpu.Mask = config.Cgroups.CpusetCpus } + + spec.HasNetwork = true return spec } -func (self *dockerContainerHandler) GetSpec() (spec *info.ContainerSpec, err error) { +func (self *dockerContainerHandler) GetSpec() (spec info.ContainerSpec, err error) { if self.isDockerRoot() { - return &info.ContainerSpec{}, nil + return info.ContainerSpec{}, nil } mi, err := self.machineInfoFactory.GetMachineInfo() if err != nil { diff --git a/container/mock.go b/container/mock.go index 28b7fcea..e2804d15 100644 --- a/container/mock.go +++ b/container/mock.go @@ -44,9 +44,9 @@ func (self *MockContainerHandler) ContainerReference() (info.ContainerReference, return args.Get(0).(info.ContainerReference), args.Error(1) } -func (self *MockContainerHandler) GetSpec() (*info.ContainerSpec, error) { +func (self *MockContainerHandler) GetSpec() (info.ContainerSpec, error) { args := self.Called() - return args.Get(0).(*info.ContainerSpec), args.Error(1) + return args.Get(0).(info.ContainerSpec), args.Error(1) } func (self *MockContainerHandler) GetStats() (*info.ContainerStats, error) { diff --git a/container/raw/handler.go b/container/raw/handler.go index 3848174c..b777d9a4 100644 --- a/container/raw/handler.go +++ b/container/raw/handler.go @@ -92,15 +92,15 @@ func readInt64(dirpath string, file string) uint64 { return val } -func (self *rawContainerHandler) GetSpec() (*info.ContainerSpec, error) { - spec := new(info.ContainerSpec) +func (self *rawContainerHandler) GetSpec() (info.ContainerSpec, error) { + var spec info.ContainerSpec // The raw driver assumes unified hierarchy containers. // Get machine info. mi, err := self.machineInfoFactory.GetMachineInfo() if err != nil { - return nil, err + return spec, err } // CPU. @@ -108,7 +108,7 @@ func (self *rawContainerHandler) GetSpec() (*info.ContainerSpec, error) { if ok { cpuRoot = path.Join(cpuRoot, self.name) if utils.FileExists(cpuRoot) { - spec.Cpu = new(info.CpuSpec) + spec.HasCpu = true spec.Cpu.Limit = readInt64(cpuRoot, "cpu.shares") } } @@ -117,11 +117,9 @@ func (self *rawContainerHandler) GetSpec() (*info.ContainerSpec, error) { // This will fail for non-unified hierarchies. We'll return the whole machine mask in that case. cpusetRoot, ok := self.cgroupSubsystems.mountPoints["cpuset"] if ok { - if spec.Cpu == nil { - spec.Cpu = new(info.CpuSpec) - } cpusetRoot = path.Join(cpusetRoot, self.name) if utils.FileExists(cpusetRoot) { + spec.HasCpu = true spec.Cpu.Mask = readString(cpusetRoot, "cpuset.cpus") if spec.Cpu.Mask == "" { spec.Cpu.Mask = fmt.Sprintf("0-%d", mi.NumCores-1) @@ -134,7 +132,7 @@ func (self *rawContainerHandler) GetSpec() (*info.ContainerSpec, error) { if ok { memoryRoot = path.Join(memoryRoot, self.name) if utils.FileExists(memoryRoot) { - spec.Memory = new(info.MemorySpec) + spec.HasMemory = true spec.Memory.Limit = readInt64(memoryRoot, "memory.limit_in_bytes") spec.Memory.SwapLimit = readInt64(memoryRoot, "memory.memsw.limit_in_bytes") } diff --git a/info/container.go b/info/container.go index 7858fa53..20e3e7e1 100644 --- a/info/container.go +++ b/info/container.go @@ -40,8 +40,13 @@ type MemorySpec struct { } type ContainerSpec struct { - Cpu *CpuSpec `json:"cpu,omitempty"` - Memory *MemorySpec `json:"memory,omitempty"` + HasCpu bool `json:"has_cpu"` + Cpu CpuSpec `json:"cpu,omitempty"` + + HasMemory bool `json:"has_memory"` + Memory MemorySpec `json:"memory,omitempty"` + + HasNetwork bool `json:"has_network"` } // Container reference contains enough information to uniquely identify a container @@ -66,7 +71,7 @@ type ContainerInfo struct { Subcontainers []ContainerReference `json:"subcontainers,omitempty"` // The isolation used in the container. - Spec *ContainerSpec `json:"spec,omitempty"` + Spec ContainerSpec `json:"spec,omitempty"` // Historical statistics gathered from the container. Stats []*ContainerStats `json:"stats,omitempty"` diff --git a/info/test/datagen.go b/info/test/datagen.go index 5d43ae57..bb0b2ff4 100644 --- a/info/test/datagen.go +++ b/info/test/datagen.go @@ -51,10 +51,10 @@ func GenerateRandomStats(numStats, numCores int, duration time.Duration) []*info return ret } -func GenerateRandomContainerSpec(numCores int) *info.ContainerSpec { - ret := &info.ContainerSpec{ - Cpu: &info.CpuSpec{}, - Memory: &info.MemorySpec{}, +func GenerateRandomContainerSpec(numCores int) info.ContainerSpec { + ret := info.ContainerSpec{ + Cpu: info.CpuSpec{}, + Memory: info.MemorySpec{}, } ret.Cpu.Limit = uint64(1000 + rand.Int63n(2000)) ret.Cpu.MaxLimit = uint64(1000 + rand.Int63n(2000)) diff --git a/manager/container.go b/manager/container.go index d6e7e97d..5ccc1550 100644 --- a/manager/container.go +++ b/manager/container.go @@ -41,7 +41,7 @@ type containerStat struct { type containerInfo struct { info.ContainerReference Subcontainers []info.ContainerReference - Spec *info.ContainerSpec + Spec info.ContainerSpec } type containerData struct { @@ -166,7 +166,6 @@ func (c *containerData) housekeeping() { time.Sleep(nextHousekeeping.Sub(time.Now())) } lastHousekeeping = nextHousekeeping - } } diff --git a/manager/manager.go b/manager/manager.go index 3817a770..55be8f5a 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -166,7 +166,7 @@ func (self *manager) containerDataToContainerInfo(cont *containerData, query *in } // Set default value to an actual value - if ret.Spec.Memory != nil { + if ret.Spec.HasMemory { // Memory.Limit is 0 means there's no limit if ret.Spec.Memory.Limit == 0 { ret.Spec.Memory.Limit = uint64(self.machineInfo.MemoryCapacity) diff --git a/pages/containers.go b/pages/containers.go index e8f98068..eca961aa 100644 --- a/pages/containers.go +++ b/pages/containers.go @@ -108,7 +108,7 @@ type pageData struct { ContainerName string ParentContainers []info.ContainerReference Subcontainers []info.ContainerReference - Spec *info.ContainerSpec + Spec info.ContainerSpec Stats []*info.ContainerStats MachineInfo *info.MachineInfo ResourcesAvailable bool @@ -300,14 +300,6 @@ func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) } } - networkStatsAvailable := false - for _, stat := range cont.Stats { - if stat.Network != nil { - networkStatsAvailable = true - break - } - } - data := &pageData{ ContainerName: displayName, // TODO(vmarmol): Only use strings for this. @@ -316,10 +308,10 @@ func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) Spec: cont.Spec, Stats: cont.Stats, MachineInfo: machineInfo, - ResourcesAvailable: cont.Spec.Cpu != nil || cont.Spec.Memory != nil, - CpuAvailable: cont.Spec.Cpu != nil, - MemoryAvailable: cont.Spec.Memory != nil, - NetworkAvailable: networkStatsAvailable, + ResourcesAvailable: cont.Spec.HasCpu || cont.Spec.HasMemory || cont.Spec.HasNetwork, + CpuAvailable: cont.Spec.HasCpu, + MemoryAvailable: cont.Spec.HasMemory, + NetworkAvailable: cont.Spec.HasNetwork, } err = pageTemplate.Execute(w, data) if err != nil { diff --git a/pages/static/containers_js.go b/pages/static/containers_js.go index db49c9fe..60abd640 100644 --- a/pages/static/containers_js.go +++ b/pages/static/containers_js.go @@ -142,7 +142,7 @@ function getStats(containerName, callback) { // Draw the graph for CPU usage. function drawCpuTotalUsage(elementId, machineInfo, stats) { - if (!hasResource(stats, "cpu")) { + if (stats.spec.has_cpu && !hasResource(stats, "cpu")) { return; } @@ -163,7 +163,7 @@ function drawCpuTotalUsage(elementId, machineInfo, stats) { // Draw the graph for per-core CPU usage. function drawCpuPerCoreUsage(elementId, machineInfo, stats) { - if (!hasResource(stats, "cpu")) { + if (stats.spec.has_cpu && !hasResource(stats, "cpu")) { return; } @@ -190,7 +190,7 @@ function drawCpuPerCoreUsage(elementId, machineInfo, stats) { // Draw the graph for CPU usage breakdown. function drawCpuUsageBreakdown(elementId, containerInfo) { - if (!hasResource(containerInfo, "cpu")) { + if (containerInfo.spec.has_cpu && !hasResource(containerInfo, "cpu")) { return; } @@ -215,7 +215,7 @@ function drawOverallUsage(elementId, machineInfo, containerInfo) { var cur = containerInfo.stats[containerInfo.stats.length - 1]; var cpuUsage = 0; - if (containerInfo.spec.cpu && containerInfo.stats.length >= 2) { + if (containerInfo.spec.has_cpu && containerInfo.stats.length >= 2) { var prev = containerInfo.stats[containerInfo.stats.length - 2]; var rawUsage = cur.cpu.usage.total - prev.cpu.usage.total; var intervalInNs = getInterval(cur.timestamp, prev.timestamp); @@ -228,7 +228,7 @@ function drawOverallUsage(elementId, machineInfo, containerInfo) { } var memoryUsage = 0; - if (containerInfo.spec.memory) { + if (containerInfo.spec.has_memory) { // Saturate to the machine size. var limit = containerInfo.spec.memory.limit; if (limit > machineInfo.memory_capacity) { @@ -244,7 +244,7 @@ function drawOverallUsage(elementId, machineInfo, containerInfo) { var oneMegabyte = 1024 * 1024; function drawMemoryUsage(elementId, containerInfo) { - if (!hasResource(containerInfo, "memory")) { + if (containerInfo.spec.has_memory && !hasResource(containerInfo, "memory")) { return; } @@ -264,7 +264,7 @@ function drawMemoryUsage(elementId, containerInfo) { // Draw the graph for network tx/rx bytes. function drawNetworkBytes(elementId, machineInfo, stats) { - if (!hasResource(stats, "network")) { + if (stats.spec.has_network && !hasResource(stats, "network")) { return; } @@ -286,7 +286,7 @@ function drawNetworkBytes(elementId, machineInfo, stats) { // Draw the graph for network errors function drawNetworkErrors(elementId, machineInfo, stats) { - if (!hasResource(stats, "network")) { + if (stats.spec.has_network && !hasResource(stats, "network")) { return; } @@ -328,33 +328,41 @@ function stepExecute(steps) { function drawCharts(machineInfo, containerInfo) { var steps = []; - steps.push(function() { - drawOverallUsage("usage-gauge", machineInfo, containerInfo) - }); + if (containerInfo.spec.has_cpu || containerInfo.spec.has_memory) { + steps.push(function() { + drawOverallUsage("usage-gauge", machineInfo, containerInfo) + }); + } // CPU. - steps.push(function() { - drawCpuTotalUsage("cpu-total-usage-chart", machineInfo, containerInfo); - }); - steps.push(function() { - drawCpuPerCoreUsage("cpu-per-core-usage-chart", machineInfo, containerInfo); - }); - steps.push(function() { - drawCpuUsageBreakdown("cpu-usage-breakdown-chart", containerInfo); - }); + if (containerInfo.spec.has_cpu) { + steps.push(function() { + drawCpuTotalUsage("cpu-total-usage-chart", machineInfo, containerInfo); + }); + steps.push(function() { + drawCpuPerCoreUsage("cpu-per-core-usage-chart", machineInfo, containerInfo); + }); + steps.push(function() { + drawCpuUsageBreakdown("cpu-usage-breakdown-chart", containerInfo); + }); + } // Memory. - steps.push(function() { - drawMemoryUsage("memory-usage-chart", containerInfo); - }); + if (containerInfo.spec.has_memory) { + steps.push(function() { + drawMemoryUsage("memory-usage-chart", containerInfo); + }); + } // Network. - steps.push(function() { - drawNetworkBytes("network-bytes-chart", machineInfo, containerInfo); - }); - steps.push(function() { - drawNetworkErrors("network-errors-chart", machineInfo, containerInfo); - }); + if (containerInfo.spec.has_network) { + steps.push(function() { + drawNetworkBytes("network-bytes-chart", machineInfo, containerInfo); + }); + steps.push(function() { + drawNetworkErrors("network-errors-chart", machineInfo, containerInfo); + }); + } stepExecute(steps); }