Merge pull request #1045 from timstclair/v2-info-simple
Add V2 ContainerInfo API
This commit is contained in:
commit
b47498c16e
114
api/versions.go
114
api/versions.go
@ -19,7 +19,6 @@ import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/info/v2"
|
||||
@ -358,27 +357,26 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
|
||||
case statsApi:
|
||||
name := getContainerName(request)
|
||||
glog.V(4).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt)
|
||||
conts, err := m.GetRequestedContainersInfo(name, opt)
|
||||
infos, err := m.GetContainerInfoV2(name, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contStats := make(map[string][]v2.ContainerStats, 0)
|
||||
for name, cont := range conts {
|
||||
contStats[name] = convertStats(cont)
|
||||
contStats := make(map[string][]*v2.ContainerStats, 0)
|
||||
for name, cinfo := range infos {
|
||||
contStats[name] = cinfo.Stats
|
||||
}
|
||||
return writeResult(contStats, w)
|
||||
case customMetricsApi:
|
||||
containerName := getContainerName(request)
|
||||
glog.V(4).Infof("Api - Custom Metrics: Looking for metrics for container %q, options %+v", containerName, opt)
|
||||
conts, err := m.GetRequestedContainersInfo(containerName, opt)
|
||||
infos, err := m.GetContainerInfoV2(containerName, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contMetrics := make(map[string]map[string]map[string][]info.MetricValBasic, 0)
|
||||
for _, cont := range conts {
|
||||
for _, cinfo := range infos {
|
||||
metrics := make(map[string]map[string][]info.MetricValBasic, 0)
|
||||
contStats := convertStats(cont)
|
||||
for _, contStat := range contStats {
|
||||
for _, contStat := range cinfo.Stats {
|
||||
if contStat.HasCustomMetrics {
|
||||
for name, allLabels := range contStat.CustomMetrics {
|
||||
metricLabels := make(map[string][]info.MetricValBasic, 0)
|
||||
@ -451,104 +449,6 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
|
||||
}
|
||||
}
|
||||
|
||||
func instCpuStats(last, cur *info.ContainerStats) (*v2.CpuInstStats, error) {
|
||||
if last == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !cur.Timestamp.After(last.Timestamp) {
|
||||
return nil, fmt.Errorf("container stats move backwards in time")
|
||||
}
|
||||
if len(last.Cpu.Usage.PerCpu) != len(cur.Cpu.Usage.PerCpu) {
|
||||
return nil, fmt.Errorf("different number of cpus")
|
||||
}
|
||||
timeDelta := cur.Timestamp.Sub(last.Timestamp)
|
||||
if timeDelta <= 100*time.Millisecond {
|
||||
return nil, fmt.Errorf("time delta unexpectedly small")
|
||||
}
|
||||
// Nanoseconds to gain precision and avoid having zero seconds if the
|
||||
// difference between the timestamps is just under a second
|
||||
timeDeltaNs := uint64(timeDelta.Nanoseconds())
|
||||
convertToRate := func(lastValue, curValue uint64) (uint64, error) {
|
||||
if curValue < lastValue {
|
||||
return 0, fmt.Errorf("cumulative stats decrease")
|
||||
}
|
||||
valueDelta := curValue - lastValue
|
||||
return (valueDelta * 1e9) / timeDeltaNs, nil
|
||||
}
|
||||
total, err := convertToRate(last.Cpu.Usage.Total, cur.Cpu.Usage.Total)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
percpu := make([]uint64, len(last.Cpu.Usage.PerCpu))
|
||||
for i := range percpu {
|
||||
var err error
|
||||
percpu[i], err = convertToRate(last.Cpu.Usage.PerCpu[i], cur.Cpu.Usage.PerCpu[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
user, err := convertToRate(last.Cpu.Usage.User, cur.Cpu.Usage.User)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
system, err := convertToRate(last.Cpu.Usage.System, cur.Cpu.Usage.System)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v2.CpuInstStats{
|
||||
Usage: v2.CpuInstUsage{
|
||||
Total: total,
|
||||
PerCpu: percpu,
|
||||
User: user,
|
||||
System: system,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertStats(cont *info.ContainerInfo) []v2.ContainerStats {
|
||||
stats := make([]v2.ContainerStats, 0, len(cont.Stats))
|
||||
var last *info.ContainerStats
|
||||
for _, val := range cont.Stats {
|
||||
stat := v2.ContainerStats{
|
||||
Timestamp: val.Timestamp,
|
||||
HasCpu: cont.Spec.HasCpu,
|
||||
HasMemory: cont.Spec.HasMemory,
|
||||
HasNetwork: cont.Spec.HasNetwork,
|
||||
HasFilesystem: cont.Spec.HasFilesystem,
|
||||
HasDiskIo: cont.Spec.HasDiskIo,
|
||||
HasCustomMetrics: cont.Spec.HasCustomMetrics,
|
||||
}
|
||||
if stat.HasCpu {
|
||||
stat.Cpu = val.Cpu
|
||||
cpuInst, err := instCpuStats(last, val)
|
||||
if err != nil {
|
||||
glog.Warningf("Could not get instant cpu stats: %v", err)
|
||||
} else {
|
||||
stat.CpuInst = cpuInst
|
||||
}
|
||||
last = val
|
||||
}
|
||||
if stat.HasMemory {
|
||||
stat.Memory = val.Memory
|
||||
}
|
||||
if stat.HasNetwork {
|
||||
stat.Network.Interfaces = val.Network.Interfaces
|
||||
}
|
||||
if stat.HasFilesystem {
|
||||
stat.Filesystem = val.Filesystem
|
||||
}
|
||||
if stat.HasDiskIo {
|
||||
stat.DiskIo = val.DiskIo
|
||||
}
|
||||
if stat.HasCustomMetrics {
|
||||
stat.CustomMetrics = val.CustomMetrics
|
||||
}
|
||||
// TODO(rjnagal): Handle load stats.
|
||||
stats = append(stats, stat)
|
||||
}
|
||||
return stats
|
||||
}
|
||||
|
||||
func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
|
||||
supportedTypes := map[string]bool{
|
||||
v2.TypeName: true,
|
||||
|
@ -19,11 +19,9 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/cadvisor/events"
|
||||
info "github.com/google/cadvisor/info/v1"
|
||||
"github.com/google/cadvisor/info/v2"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@ -81,170 +79,3 @@ func TestGetEventRequestDoubleArgument(t *testing.T) {
|
||||
assert.True(t, stream)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestInstCpuStats(t *testing.T) {
|
||||
tests := []struct {
|
||||
last *info.ContainerStats
|
||||
cur *info.ContainerStats
|
||||
want *v2.CpuInstStats
|
||||
}{
|
||||
// Last is missing
|
||||
{
|
||||
nil,
|
||||
&info.ContainerStats{},
|
||||
nil,
|
||||
},
|
||||
// Goes back in time
|
||||
{
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(time.Second),
|
||||
},
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
// Zero time delta
|
||||
{
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
},
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
// Unexpectedly small time delta
|
||||
{
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
},
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(30 * time.Millisecond),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
// Different number of cpus
|
||||
{
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
Cpu: info.CpuStats{
|
||||
Usage: info.CpuUsage{
|
||||
PerCpu: []uint64{100, 200},
|
||||
},
|
||||
},
|
||||
},
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(time.Second),
|
||||
Cpu: info.CpuStats{
|
||||
Usage: info.CpuUsage{
|
||||
PerCpu: []uint64{100, 200, 300},
|
||||
},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
// Stat numbers decrease
|
||||
{
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
Cpu: info.CpuStats{
|
||||
Usage: info.CpuUsage{
|
||||
Total: 300,
|
||||
PerCpu: []uint64{100, 200},
|
||||
User: 250,
|
||||
System: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(time.Second),
|
||||
Cpu: info.CpuStats{
|
||||
Usage: info.CpuUsage{
|
||||
Total: 200,
|
||||
PerCpu: []uint64{100, 100},
|
||||
User: 150,
|
||||
System: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
// One second elapsed
|
||||
{
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
Cpu: info.CpuStats{
|
||||
Usage: info.CpuUsage{
|
||||
Total: 300,
|
||||
PerCpu: []uint64{100, 200},
|
||||
User: 250,
|
||||
System: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(time.Second),
|
||||
Cpu: info.CpuStats{
|
||||
Usage: info.CpuUsage{
|
||||
Total: 500,
|
||||
PerCpu: []uint64{200, 300},
|
||||
User: 400,
|
||||
System: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
&v2.CpuInstStats{
|
||||
Usage: v2.CpuInstUsage{
|
||||
Total: 200,
|
||||
PerCpu: []uint64{100, 100},
|
||||
User: 150,
|
||||
System: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Two seconds elapsed
|
||||
{
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
Cpu: info.CpuStats{
|
||||
Usage: info.CpuUsage{
|
||||
Total: 300,
|
||||
PerCpu: []uint64{100, 200},
|
||||
User: 250,
|
||||
System: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
&info.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(2 * time.Second),
|
||||
Cpu: info.CpuStats{
|
||||
Usage: info.CpuUsage{
|
||||
Total: 500,
|
||||
PerCpu: []uint64{200, 300},
|
||||
User: 400,
|
||||
System: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
&v2.CpuInstStats{
|
||||
Usage: v2.CpuInstUsage{
|
||||
Total: 100,
|
||||
PerCpu: []uint64{50, 50},
|
||||
User: 75,
|
||||
System: 25,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range tests {
|
||||
got, err := instCpuStats(c.last, c.cur)
|
||||
if err != nil {
|
||||
if c.want == nil {
|
||||
continue
|
||||
}
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
assert.Equal(t, c.want, got)
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,14 @@ type MemorySpec struct {
|
||||
SwapLimit uint64 `json:"swap_limit,omitempty"`
|
||||
}
|
||||
|
||||
type ContainerInfo struct {
|
||||
// Describes the container.
|
||||
Spec ContainerSpec `json:"spec,omitempty"`
|
||||
|
||||
// Historical statistics gathered from the container.
|
||||
Stats []*ContainerStats `json:"stats,omitempty"`
|
||||
}
|
||||
|
||||
type ContainerSpec struct {
|
||||
// Time at which the container was created.
|
||||
CreationTime time.Time `json:"creation_time,omitempty"`
|
||||
|
153
info/v2/conversion.go
Normal file
153
info/v2/conversion.go
Normal file
@ -0,0 +1,153 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// Utilities for converting v1 structs to v2 structs.
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/google/cadvisor/info/v1"
|
||||
)
|
||||
|
||||
// Get V2 container spec from v1 container info.
|
||||
func ContainerSpecFromV1(specV1 *v1.ContainerSpec, aliases []string, namespace string) ContainerSpec {
|
||||
specV2 := ContainerSpec{
|
||||
CreationTime: specV1.CreationTime,
|
||||
HasCpu: specV1.HasCpu,
|
||||
HasMemory: specV1.HasMemory,
|
||||
HasFilesystem: specV1.HasFilesystem,
|
||||
HasNetwork: specV1.HasNetwork,
|
||||
HasDiskIo: specV1.HasDiskIo,
|
||||
HasCustomMetrics: specV1.HasCustomMetrics,
|
||||
Image: specV1.Image,
|
||||
Labels: specV1.Labels,
|
||||
}
|
||||
if specV1.HasCpu {
|
||||
specV2.Cpu.Limit = specV1.Cpu.Limit
|
||||
specV2.Cpu.MaxLimit = specV1.Cpu.MaxLimit
|
||||
specV2.Cpu.Mask = specV1.Cpu.Mask
|
||||
}
|
||||
if specV1.HasMemory {
|
||||
specV2.Memory.Limit = specV1.Memory.Limit
|
||||
specV2.Memory.Reservation = specV1.Memory.Reservation
|
||||
specV2.Memory.SwapLimit = specV1.Memory.SwapLimit
|
||||
}
|
||||
if specV1.HasCustomMetrics {
|
||||
specV2.CustomMetrics = specV1.CustomMetrics
|
||||
}
|
||||
specV2.Aliases = aliases
|
||||
specV2.Namespace = namespace
|
||||
return specV2
|
||||
}
|
||||
|
||||
func ContainerStatsFromV1(statsV1 []*v1.ContainerStats, specV1 *v1.ContainerSpec) []*ContainerStats {
|
||||
stats := make([]*ContainerStats, 0, len(statsV1))
|
||||
var last *v1.ContainerStats
|
||||
for _, val := range statsV1 {
|
||||
stat := ContainerStats{
|
||||
Timestamp: val.Timestamp,
|
||||
HasCpu: specV1.HasCpu,
|
||||
HasMemory: specV1.HasMemory,
|
||||
HasNetwork: specV1.HasNetwork,
|
||||
HasFilesystem: specV1.HasFilesystem,
|
||||
HasDiskIo: specV1.HasDiskIo,
|
||||
HasCustomMetrics: specV1.HasCustomMetrics,
|
||||
}
|
||||
if stat.HasCpu {
|
||||
stat.Cpu = val.Cpu
|
||||
cpuInst, err := instCpuStats(last, val)
|
||||
if err != nil {
|
||||
glog.Warningf("Could not get instant cpu stats: %v", err)
|
||||
} else {
|
||||
stat.CpuInst = cpuInst
|
||||
}
|
||||
last = val
|
||||
}
|
||||
if stat.HasMemory {
|
||||
stat.Memory = val.Memory
|
||||
}
|
||||
if stat.HasNetwork {
|
||||
stat.Network.Interfaces = val.Network.Interfaces
|
||||
}
|
||||
if stat.HasFilesystem {
|
||||
stat.Filesystem = val.Filesystem
|
||||
}
|
||||
if stat.HasDiskIo {
|
||||
stat.DiskIo = val.DiskIo
|
||||
}
|
||||
if stat.HasCustomMetrics {
|
||||
stat.CustomMetrics = val.CustomMetrics
|
||||
}
|
||||
// TODO(rjnagal): Handle load stats.
|
||||
stats = append(stats, &stat)
|
||||
}
|
||||
return stats
|
||||
}
|
||||
|
||||
func instCpuStats(last, cur *v1.ContainerStats) (*CpuInstStats, error) {
|
||||
if last == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !cur.Timestamp.After(last.Timestamp) {
|
||||
return nil, fmt.Errorf("container stats move backwards in time")
|
||||
}
|
||||
if len(last.Cpu.Usage.PerCpu) != len(cur.Cpu.Usage.PerCpu) {
|
||||
return nil, fmt.Errorf("different number of cpus")
|
||||
}
|
||||
timeDelta := cur.Timestamp.Sub(last.Timestamp)
|
||||
if timeDelta <= 100*time.Millisecond {
|
||||
return nil, fmt.Errorf("time delta unexpectedly small")
|
||||
}
|
||||
// Nanoseconds to gain precision and avoid having zero seconds if the
|
||||
// difference between the timestamps is just under a second
|
||||
timeDeltaNs := uint64(timeDelta.Nanoseconds())
|
||||
convertToRate := func(lastValue, curValue uint64) (uint64, error) {
|
||||
if curValue < lastValue {
|
||||
return 0, fmt.Errorf("cumulative stats decrease")
|
||||
}
|
||||
valueDelta := curValue - lastValue
|
||||
return (valueDelta * 1e9) / timeDeltaNs, nil
|
||||
}
|
||||
total, err := convertToRate(last.Cpu.Usage.Total, cur.Cpu.Usage.Total)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
percpu := make([]uint64, len(last.Cpu.Usage.PerCpu))
|
||||
for i := range percpu {
|
||||
var err error
|
||||
percpu[i], err = convertToRate(last.Cpu.Usage.PerCpu[i], cur.Cpu.Usage.PerCpu[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
user, err := convertToRate(last.Cpu.Usage.User, cur.Cpu.Usage.User)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
system, err := convertToRate(last.Cpu.Usage.System, cur.Cpu.Usage.System)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CpuInstStats{
|
||||
Usage: CpuInstUsage{
|
||||
Total: total,
|
||||
PerCpu: percpu,
|
||||
User: user,
|
||||
System: system,
|
||||
},
|
||||
}, nil
|
||||
}
|
264
info/v2/conversion_test.go
Normal file
264
info/v2/conversion_test.go
Normal file
@ -0,0 +1,264 @@
|
||||
// Copyright 2016 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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/cadvisor/info/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
timestamp = time.Date(1987, time.August, 10, 0, 0, 0, 0, time.UTC)
|
||||
labels = map[string]string{"foo": "bar"}
|
||||
)
|
||||
|
||||
func TestConvertSpec(t *testing.T) {
|
||||
v1Spec := v1.ContainerSpec{
|
||||
CreationTime: timestamp,
|
||||
Labels: labels,
|
||||
HasCpu: true,
|
||||
Cpu: v1.CpuSpec{
|
||||
Limit: 2048,
|
||||
MaxLimit: 4096,
|
||||
Mask: "cpu_mask",
|
||||
},
|
||||
HasMemory: true,
|
||||
Memory: v1.MemorySpec{
|
||||
Limit: 2048,
|
||||
Reservation: 1024,
|
||||
SwapLimit: 8192,
|
||||
},
|
||||
HasNetwork: true,
|
||||
HasFilesystem: true,
|
||||
HasDiskIo: true,
|
||||
HasCustomMetrics: true,
|
||||
CustomMetrics: []v1.MetricSpec{{
|
||||
Name: "foo",
|
||||
Type: v1.MetricGauge,
|
||||
Format: v1.IntType,
|
||||
Units: "bars",
|
||||
}},
|
||||
Image: "gcr.io/kubernetes/kubernetes:v1",
|
||||
}
|
||||
|
||||
aliases := []string{"baz", "oof"}
|
||||
namespace := "foo_bar_baz"
|
||||
|
||||
expectedV2Spec := ContainerSpec{
|
||||
CreationTime: timestamp,
|
||||
Labels: labels,
|
||||
HasCpu: true,
|
||||
Cpu: CpuSpec{
|
||||
Limit: 2048,
|
||||
MaxLimit: 4096,
|
||||
Mask: "cpu_mask",
|
||||
},
|
||||
HasMemory: true,
|
||||
Memory: MemorySpec{
|
||||
Limit: 2048,
|
||||
Reservation: 1024,
|
||||
SwapLimit: 8192,
|
||||
},
|
||||
HasNetwork: true,
|
||||
HasFilesystem: true,
|
||||
HasDiskIo: true,
|
||||
HasCustomMetrics: true,
|
||||
CustomMetrics: []v1.MetricSpec{{
|
||||
Name: "foo",
|
||||
Type: v1.MetricGauge,
|
||||
Format: v1.IntType,
|
||||
Units: "bars",
|
||||
}},
|
||||
Image: "gcr.io/kubernetes/kubernetes:v1",
|
||||
Aliases: aliases,
|
||||
Namespace: namespace,
|
||||
}
|
||||
|
||||
v2Spec := ContainerSpecFromV1(&v1Spec, aliases, namespace)
|
||||
if !reflect.DeepEqual(v2Spec, expectedV2Spec) {
|
||||
t.Errorf("Converted spec differs from expectation!\nExpected: %+v\n Got: %+v\n", expectedV2Spec, v2Spec)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstCpuStats(t *testing.T) {
|
||||
tests := []struct {
|
||||
last *v1.ContainerStats
|
||||
cur *v1.ContainerStats
|
||||
want *CpuInstStats
|
||||
}{
|
||||
// Last is missing
|
||||
{
|
||||
nil,
|
||||
&v1.ContainerStats{},
|
||||
nil,
|
||||
},
|
||||
// Goes back in time
|
||||
{
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(time.Second),
|
||||
},
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
// Zero time delta
|
||||
{
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
},
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
// Unexpectedly small time delta
|
||||
{
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
},
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(30 * time.Millisecond),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
// Different number of cpus
|
||||
{
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
Cpu: v1.CpuStats{
|
||||
Usage: v1.CpuUsage{
|
||||
PerCpu: []uint64{100, 200},
|
||||
},
|
||||
},
|
||||
},
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(time.Second),
|
||||
Cpu: v1.CpuStats{
|
||||
Usage: v1.CpuUsage{
|
||||
PerCpu: []uint64{100, 200, 300},
|
||||
},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
// Stat numbers decrease
|
||||
{
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
Cpu: v1.CpuStats{
|
||||
Usage: v1.CpuUsage{
|
||||
Total: 300,
|
||||
PerCpu: []uint64{100, 200},
|
||||
User: 250,
|
||||
System: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(time.Second),
|
||||
Cpu: v1.CpuStats{
|
||||
Usage: v1.CpuUsage{
|
||||
Total: 200,
|
||||
PerCpu: []uint64{100, 100},
|
||||
User: 150,
|
||||
System: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
// One second elapsed
|
||||
{
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
Cpu: v1.CpuStats{
|
||||
Usage: v1.CpuUsage{
|
||||
Total: 300,
|
||||
PerCpu: []uint64{100, 200},
|
||||
User: 250,
|
||||
System: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(time.Second),
|
||||
Cpu: v1.CpuStats{
|
||||
Usage: v1.CpuUsage{
|
||||
Total: 500,
|
||||
PerCpu: []uint64{200, 300},
|
||||
User: 400,
|
||||
System: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
&CpuInstStats{
|
||||
Usage: CpuInstUsage{
|
||||
Total: 200,
|
||||
PerCpu: []uint64{100, 100},
|
||||
User: 150,
|
||||
System: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Two seconds elapsed
|
||||
{
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0),
|
||||
Cpu: v1.CpuStats{
|
||||
Usage: v1.CpuUsage{
|
||||
Total: 300,
|
||||
PerCpu: []uint64{100, 200},
|
||||
User: 250,
|
||||
System: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
&v1.ContainerStats{
|
||||
Timestamp: time.Unix(100, 0).Add(2 * time.Second),
|
||||
Cpu: v1.CpuStats{
|
||||
Usage: v1.CpuUsage{
|
||||
Total: 500,
|
||||
PerCpu: []uint64{200, 300},
|
||||
User: 400,
|
||||
System: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
&CpuInstStats{
|
||||
Usage: CpuInstUsage{
|
||||
Total: 100,
|
||||
PerCpu: []uint64{50, 50},
|
||||
User: 75,
|
||||
System: 25,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range tests {
|
||||
got, err := instCpuStats(c.last, c.cur)
|
||||
if err != nil {
|
||||
if c.want == nil {
|
||||
continue
|
||||
}
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
assert.Equal(t, c.want, got)
|
||||
}
|
||||
}
|
@ -62,6 +62,9 @@ type Manager interface {
|
||||
// Get information about a container.
|
||||
GetContainerInfo(containerName string, query *info.ContainerInfoRequest) (*info.ContainerInfo, error)
|
||||
|
||||
// Get V2 information about a container.
|
||||
GetContainerInfoV2(containerName string, options v2.RequestOptions) (map[string]v2.ContainerInfo, error)
|
||||
|
||||
// Get information about all subcontainers of the specified container (includes self).
|
||||
SubcontainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error)
|
||||
|
||||
@ -375,33 +378,8 @@ func (self *manager) GetContainerSpec(containerName string, options v2.RequestOp
|
||||
|
||||
// Get V2 container spec from v1 container info.
|
||||
func (self *manager) getV2Spec(cinfo *containerInfo) v2.ContainerSpec {
|
||||
specV1 := self.getAdjustedSpec(cinfo)
|
||||
specV2 := v2.ContainerSpec{
|
||||
CreationTime: specV1.CreationTime,
|
||||
HasCpu: specV1.HasCpu,
|
||||
HasMemory: specV1.HasMemory,
|
||||
HasFilesystem: specV1.HasFilesystem,
|
||||
HasNetwork: specV1.HasNetwork,
|
||||
HasDiskIo: specV1.HasDiskIo,
|
||||
HasCustomMetrics: specV1.HasCustomMetrics,
|
||||
Image: specV1.Image,
|
||||
}
|
||||
if specV1.HasCpu {
|
||||
specV2.Cpu.Limit = specV1.Cpu.Limit
|
||||
specV2.Cpu.MaxLimit = specV1.Cpu.MaxLimit
|
||||
specV2.Cpu.Mask = specV1.Cpu.Mask
|
||||
}
|
||||
if specV1.HasMemory {
|
||||
specV2.Memory.Limit = specV1.Memory.Limit
|
||||
specV2.Memory.Reservation = specV1.Memory.Reservation
|
||||
specV2.Memory.SwapLimit = specV1.Memory.SwapLimit
|
||||
}
|
||||
if specV1.HasCustomMetrics {
|
||||
specV2.CustomMetrics = specV1.CustomMetrics
|
||||
}
|
||||
specV2.Aliases = cinfo.Aliases
|
||||
specV2.Namespace = cinfo.Namespace
|
||||
return specV2
|
||||
spec := self.getAdjustedSpec(cinfo)
|
||||
return v2.ContainerSpecFromV1(&spec, cinfo.Aliases, cinfo.Namespace)
|
||||
}
|
||||
|
||||
func (self *manager) getAdjustedSpec(cinfo *containerInfo) info.ContainerSpec {
|
||||
@ -417,7 +395,6 @@ func (self *manager) getAdjustedSpec(cinfo *containerInfo) info.ContainerSpec {
|
||||
return spec
|
||||
}
|
||||
|
||||
// Get a container by name.
|
||||
func (self *manager) GetContainerInfo(containerName string, query *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
|
||||
cont, err := self.getContainerData(containerName)
|
||||
if err != nil {
|
||||
@ -426,6 +403,34 @@ func (self *manager) GetContainerInfo(containerName string, query *info.Containe
|
||||
return self.containerDataToContainerInfo(cont, query)
|
||||
}
|
||||
|
||||
func (self *manager) GetContainerInfoV2(containerName string, options v2.RequestOptions) (map[string]v2.ContainerInfo, error) {
|
||||
containers, err := self.getRequestedContainers(containerName, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
infos := make(map[string]v2.ContainerInfo, len(containers))
|
||||
for name, container := range containers {
|
||||
cinfo, err := container.GetInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nilTime time.Time // Ignored.
|
||||
stats, err := self.memoryCache.RecentStats(name, nilTime, nilTime, options.Count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
infos[name] = v2.ContainerInfo{
|
||||
Spec: self.getV2Spec(cinfo),
|
||||
Stats: v2.ContainerStatsFromV1(stats, &cinfo.Spec),
|
||||
}
|
||||
}
|
||||
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
func (self *manager) containerDataToContainerInfo(cont *containerData, query *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
|
||||
// Get the info from the container.
|
||||
cinfo, err := cont.GetInfo()
|
||||
|
Loading…
Reference in New Issue
Block a user