Merge pull request #1045 from timstclair/v2-info-simple

Add V2 ContainerInfo API
This commit is contained in:
Vish Kannan 2016-01-13 12:04:26 -08:00
commit b47498c16e
6 changed files with 465 additions and 304 deletions

View File

@ -19,7 +19,6 @@ import (
"net/http" "net/http"
"path" "path"
"strconv" "strconv"
"time"
info "github.com/google/cadvisor/info/v1" info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/info/v2" "github.com/google/cadvisor/info/v2"
@ -358,27 +357,26 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
case statsApi: case statsApi:
name := getContainerName(request) name := getContainerName(request)
glog.V(4).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt) 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 { if err != nil {
return err return err
} }
contStats := make(map[string][]v2.ContainerStats, 0) contStats := make(map[string][]*v2.ContainerStats, 0)
for name, cont := range conts { for name, cinfo := range infos {
contStats[name] = convertStats(cont) contStats[name] = cinfo.Stats
} }
return writeResult(contStats, w) return writeResult(contStats, w)
case customMetricsApi: case customMetricsApi:
containerName := getContainerName(request) containerName := getContainerName(request)
glog.V(4).Infof("Api - Custom Metrics: Looking for metrics for container %q, options %+v", containerName, opt) 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 { if err != nil {
return err return err
} }
contMetrics := make(map[string]map[string]map[string][]info.MetricValBasic, 0) 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) metrics := make(map[string]map[string][]info.MetricValBasic, 0)
contStats := convertStats(cont) for _, contStat := range cinfo.Stats {
for _, contStat := range contStats {
if contStat.HasCustomMetrics { if contStat.HasCustomMetrics {
for name, allLabels := range contStat.CustomMetrics { for name, allLabels := range contStat.CustomMetrics {
metricLabels := make(map[string][]info.MetricValBasic, 0) 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) { func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
supportedTypes := map[string]bool{ supportedTypes := map[string]bool{
v2.TypeName: true, v2.TypeName: true,

View File

@ -19,11 +19,9 @@ import (
"net/http" "net/http"
"reflect" "reflect"
"testing" "testing"
"time"
"github.com/google/cadvisor/events" "github.com/google/cadvisor/events"
info "github.com/google/cadvisor/info/v1" info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/info/v2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -81,170 +79,3 @@ func TestGetEventRequestDoubleArgument(t *testing.T) {
assert.True(t, stream) assert.True(t, stream)
assert.Nil(t, err) 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)
}
}

View File

@ -52,6 +52,14 @@ type MemorySpec struct {
SwapLimit uint64 `json:"swap_limit,omitempty"` 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 { type ContainerSpec struct {
// Time at which the container was created. // Time at which the container was created.
CreationTime time.Time `json:"creation_time,omitempty"` CreationTime time.Time `json:"creation_time,omitempty"`

153
info/v2/conversion.go Normal file
View 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
View 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)
}
}

View File

@ -62,6 +62,9 @@ type Manager interface {
// Get information about a container. // Get information about a container.
GetContainerInfo(containerName string, query *info.ContainerInfoRequest) (*info.ContainerInfo, error) 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). // Get information about all subcontainers of the specified container (includes self).
SubcontainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error) 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. // Get V2 container spec from v1 container info.
func (self *manager) getV2Spec(cinfo *containerInfo) v2.ContainerSpec { func (self *manager) getV2Spec(cinfo *containerInfo) v2.ContainerSpec {
specV1 := self.getAdjustedSpec(cinfo) spec := self.getAdjustedSpec(cinfo)
specV2 := v2.ContainerSpec{ return v2.ContainerSpecFromV1(&spec, cinfo.Aliases, cinfo.Namespace)
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
} }
func (self *manager) getAdjustedSpec(cinfo *containerInfo) info.ContainerSpec { func (self *manager) getAdjustedSpec(cinfo *containerInfo) info.ContainerSpec {
@ -417,7 +395,6 @@ func (self *manager) getAdjustedSpec(cinfo *containerInfo) info.ContainerSpec {
return spec return spec
} }
// Get a container by name.
func (self *manager) GetContainerInfo(containerName string, query *info.ContainerInfoRequest) (*info.ContainerInfo, error) { func (self *manager) GetContainerInfo(containerName string, query *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
cont, err := self.getContainerData(containerName) cont, err := self.getContainerData(containerName)
if err != nil { if err != nil {
@ -426,6 +403,34 @@ func (self *manager) GetContainerInfo(containerName string, query *info.Containe
return self.containerDataToContainerInfo(cont, query) 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) { func (self *manager) containerDataToContainerInfo(cont *containerData, query *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
// Get the info from the container. // Get the info from the container.
cinfo, err := cont.GetInfo() cinfo, err := cont.GetInfo()