Merge pull request #2683 from jimmidyson/cadvisor-0.6.2

Fixes #2681: update to cadvisor 0.6.2
This commit is contained in:
Victor Marmol 2014-12-04 03:48:40 +08:00
commit b1c7893916
10 changed files with 160 additions and 131 deletions

8
Godeps/Godeps.json generated
View File

@ -72,13 +72,13 @@
}, },
{ {
"ImportPath": "github.com/google/cadvisor/client", "ImportPath": "github.com/google/cadvisor/client",
"Comment": "0.5.0", "Comment": "0.6.2",
"Rev": "8c4f650e62f096710da794e536de86e34447fca9" "Rev": "89088df70eca64cf9d6b9a23a3d2bc21a30916d6"
}, },
{ {
"ImportPath": "github.com/google/cadvisor/info", "ImportPath": "github.com/google/cadvisor/info",
"Comment": "0.5.0", "Comment": "0.6.2",
"Rev": "8c4f650e62f096710da794e536de86e34447fca9" "Rev": "89088df70eca64cf9d6b9a23a3d2bc21a30916d6"
}, },
{ {
"ImportPath": "github.com/google/gofuzz", "ImportPath": "github.com/google/gofuzz",

View File

@ -39,7 +39,7 @@ func NewClient(url string) (*Client, error) {
} }
return &Client{ return &Client{
baseUrl: fmt.Sprintf("%sapi/v1.1/", url), baseUrl: fmt.Sprintf("%sapi/v1.2/", url),
}, nil }, nil
} }
@ -80,6 +80,38 @@ func (self *Client) SubcontainersInfo(name string, query *info.ContainerInfoRequ
return response, nil return response, nil
} }
// Returns the JSON container information for the specified
// Docker container and request.
func (self *Client) DockerContainer(name string, query *info.ContainerInfoRequest) (cinfo info.ContainerInfo, err error) {
u := self.dockerInfoUrl(name)
ret := make(map[string]info.ContainerInfo)
if err = self.httpGetJsonData(&ret, query, u, fmt.Sprintf("Docker container info for %q", name)); err != nil {
return
}
if len(ret) != 1 {
err = fmt.Errorf("expected to only receive 1 Docker container: %+v", ret)
return
}
for _, cont := range ret {
cinfo = cont
}
return
}
// Returns the JSON container information for all Docker containers.
func (self *Client) AllDockerContainers(query *info.ContainerInfoRequest) (cinfo []info.ContainerInfo, err error) {
u := self.dockerInfoUrl("/")
ret := make(map[string]info.ContainerInfo)
if err = self.httpGetJsonData(&ret, query, u, "all Docker containers info"); err != nil {
return
}
cinfo = make([]info.ContainerInfo, 0, len(ret))
for _, cont := range ret {
cinfo = append(cinfo, cont)
}
return
}
func (self *Client) machineInfoUrl() string { func (self *Client) machineInfoUrl() string {
return self.baseUrl + path.Join("machine") return self.baseUrl + path.Join("machine")
} }
@ -92,6 +124,10 @@ func (self *Client) subcontainersInfoUrl(name string) string {
return self.baseUrl + path.Join("subcontainers", name) return self.baseUrl + path.Join("subcontainers", name)
} }
func (self *Client) dockerInfoUrl(name string) string {
return self.baseUrl + path.Join("docker", name)
}
func (self *Client) httpGetJsonData(data, postData interface{}, url, infoName string) error { func (self *Client) httpGetJsonData(data, postData interface{}, url, infoName string) error {
var resp *http.Response var resp *http.Response
var err error var err error
@ -106,17 +142,19 @@ func (self *Client) httpGetJsonData(data, postData interface{}, url, infoName st
resp, err = http.Get(url) resp, err = http.Get(url)
} }
if err != nil { if err != nil {
err = fmt.Errorf("unable to get %v: %v", infoName, err) return fmt.Errorf("unable to get %q: %v", infoName, err)
return err }
if resp == nil {
return fmt.Errorf("received empty response from %q", infoName)
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
err = fmt.Errorf("unable to read all %v: %v", infoName, err) err = fmt.Errorf("unable to read all %q: %v", infoName, err)
return err return err
} }
if err = json.Unmarshal(body, data); err != nil { if err = json.Unmarshal(body, data); err != nil {
err = fmt.Errorf("unable to unmarshal %v (%v): %v", infoName, string(body), err) err = fmt.Errorf("unable to unmarshal %q (%v): %v", infoName, string(body), err)
return err return err
} }
return nil return nil

View File

@ -57,7 +57,7 @@ func cadvisorTestClient(path string, expectedPostObj, expectedPostObjEmpty, repl
} }
encoder := json.NewEncoder(w) encoder := json.NewEncoder(w)
encoder.Encode(replyObj) encoder.Encode(replyObj)
} else if r.URL.Path == "/api/v1.1/machine" { } else if r.URL.Path == "/api/v1.2/machine" {
fmt.Fprint(w, `{"num_cores":8,"memory_capacity":31625871360}`) fmt.Fprint(w, `{"num_cores":8,"memory_capacity":31625871360}`)
} else { } else {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
@ -79,7 +79,7 @@ func TestGetMachineinfo(t *testing.T) {
NumCores: 8, NumCores: 8,
MemoryCapacity: 31625871360, MemoryCapacity: 31625871360,
} }
client, server, err := cadvisorTestClient("/api/v1.1/machine", nil, nil, minfo, t) client, server, err := cadvisorTestClient("/api/v1.2/machine", nil, nil, minfo, t)
if err != nil { if err != nil {
t.Fatalf("unable to get a client %v", err) t.Fatalf("unable to get a client %v", err)
} }
@ -101,7 +101,7 @@ func TestGetContainerInfo(t *testing.T) {
} }
containerName := "/some/container" containerName := "/some/container"
cinfo := itest.GenerateRandomContainerInfo(containerName, 4, query, 1*time.Second) cinfo := itest.GenerateRandomContainerInfo(containerName, 4, query, 1*time.Second)
client, server, err := cadvisorTestClient(fmt.Sprintf("/api/v1.1/containers%v", containerName), query, &info.ContainerInfoRequest{}, cinfo, t) client, server, err := cadvisorTestClient(fmt.Sprintf("/api/v1.2/containers%v", containerName), query, &info.ContainerInfoRequest{}, cinfo, t)
if err != nil { if err != nil {
t.Fatalf("unable to get a client %v", err) t.Fatalf("unable to get a client %v", err)
} }
@ -129,7 +129,7 @@ func TestGetSubcontainersInfo(t *testing.T) {
*cinfo1, *cinfo1,
*cinfo2, *cinfo2,
} }
client, server, err := cadvisorTestClient(fmt.Sprintf("/api/v1.1/subcontainers%v", containerName), query, &info.ContainerInfoRequest{}, response, t) client, server, err := cadvisorTestClient(fmt.Sprintf("/api/v1.2/subcontainers%v", containerName), query, &info.ContainerInfoRequest{}, response, t)
if err != nil { if err != nil {
t.Fatalf("unable to get a client %v", err) t.Fatalf("unable to get a client %v", err)
} }

View File

@ -1,34 +0,0 @@
// Copyright 2014 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 info
// This struct describes one type of relationship between containers: One
// container, antagonist, interferes the performance of other
// containers, victims.
type Interference struct {
// Absolute name of the antagonist container name. This field
// should not be empty.
Antagonist string `json:"antagonist"`
// The absolute path of the victims. This field should not be empty.
Victims []string `json:"victims"`
// The name of the detector used to detect this antagonism. This field
// should not be empty
Detector string `json:"detector"`
// Human readable description of this interference
Description string `json:"description,omitempty"`
}

View File

@ -53,10 +53,16 @@ type ContainerSpec struct {
// Container reference contains enough information to uniquely identify a container // Container reference contains enough information to uniquely identify a container
type ContainerReference struct { type ContainerReference struct {
// The absolute name of the container. // The absolute name of the container. This is unique on the machine.
Name string `json:"name"` Name string `json:"name"`
// Other names by which the container is known within a certain namespace.
// This is unique within that namespace.
Aliases []string `json:"aliases,omitempty"` Aliases []string `json:"aliases,omitempty"`
// Namespace under which the aliases of a container are unique.
// An example of a namespace is "docker" for Docker containers.
Namespace string `json:"namespace,omitempty"`
} }
// ContainerInfoQuery is used when users check a container info from the REST api. // ContainerInfoQuery is used when users check a container info from the REST api.
@ -184,32 +190,30 @@ type DiskIoStats struct {
IoServiced []PerDiskStats `json:"io_serviced,omitempty"` IoServiced []PerDiskStats `json:"io_serviced,omitempty"`
IoQueued []PerDiskStats `json:"io_queued,omitempty"` IoQueued []PerDiskStats `json:"io_queued,omitempty"`
Sectors []PerDiskStats `json:"sectors,omitempty"` Sectors []PerDiskStats `json:"sectors,omitempty"`
IoServiceTime []PerDiskStats `json:"io_service_time,omitempty"`
IoWaitTime []PerDiskStats `json:"io_wait_time,omitempty"`
IoMerged []PerDiskStats `json:"io_merged,omitempty"`
IoTime []PerDiskStats `json:"io_time,omitempty"`
} }
type MemoryStats struct { type MemoryStats struct {
// Memory limit, equivalent to "limit" in MemorySpec.
// Units: Bytes.
Limit uint64 `json:"limit,omitempty"`
// Usage statistics.
// Current memory usage, this includes all memory regardless of when it was // Current memory usage, this includes all memory regardless of when it was
// accessed. // accessed.
// Units: Bytes. // Units: Bytes.
Usage uint64 `json:"usage,omitempty"` Usage uint64 `json:"usage"`
// The amount of working set memory, this includes recently accessed memory, // The amount of working set memory, this includes recently accessed memory,
// dirty memory, and kernel memory. Working set is <= "usage". // dirty memory, and kernel memory. Working set is <= "usage".
// Units: Bytes. // Units: Bytes.
WorkingSet uint64 `json:"working_set,omitempty"` WorkingSet uint64 `json:"working_set"`
ContainerData MemoryStatsMemoryData `json:"container_data,omitempty"` ContainerData MemoryStatsMemoryData `json:"container_data,omitempty"`
HierarchicalData MemoryStatsMemoryData `json:"hierarchical_data,omitempty"` HierarchicalData MemoryStatsMemoryData `json:"hierarchical_data,omitempty"`
} }
type MemoryStatsMemoryData struct { type MemoryStatsMemoryData struct {
Pgfault uint64 `json:"pgfault,omitempty"` Pgfault uint64 `json:"pgfault"`
Pgmajfault uint64 `json:"pgmajfault,omitempty"` Pgmajfault uint64 `json:"pgmajfault"`
} }
type NetworkStats struct { type NetworkStats struct {
@ -240,55 +244,74 @@ type FsStats struct {
// Number of bytes that is consumed by the container on this filesystem. // Number of bytes that is consumed by the container on this filesystem.
Usage uint64 `json:"usage"` Usage uint64 `json:"usage"`
// Number of reads completed
// This is the total number of reads completed successfully.
ReadsCompleted uint64 `json:"reads_completed"`
// Number of reads merged
// Reads and writes which are adjacent to each other may be merged for
// efficiency. Thus two 4K reads may become one 8K read before it is
// ultimately handed to the disk, and so it will be counted (and queued)
// as only one I/O. This field lets you know how often this was done.
ReadsMerged uint64 `json:"reads_merged"`
// Number of sectors read
// This is the total number of sectors read successfully.
SectorsRead uint64 `json:"sectors_read"`
// Number of milliseconds spent reading
// This is the total number of milliseconds spent by all reads (as
// measured from __make_request() to end_that_request_last()).
ReadTime uint64 `json:"read_time"`
// Number of writes completed
// This is the total number of writes completed successfully.
WritesCompleted uint64 `json:"writes_completed"`
// Number of writes merged
// See the description of reads merged.
WritesMerged uint64 `json:"writes_merged"`
// Number of sectors written
// This is the total number of sectors written successfully.
SectorsWritten uint64 `json:"sectors_written"`
// Number of milliseconds spent writing
// This is the total number of milliseconds spent by all writes (as
// measured from __make_request() to end_that_request_last()).
WriteTime uint64 `json:"write_time"`
// Number of I/Os currently in progress
// The only field that should go to zero. Incremented as requests are
// given to appropriate struct request_queue and decremented as they finish.
IoInProgress uint64 `json:"io_in_progress"`
// Number of milliseconds spent doing I/Os
// This field increases so long as field 9 is nonzero.
IoTime uint64 `json:"io_time"`
// weighted number of milliseconds spent doing I/Os
// This field is incremented at each I/O start, I/O completion, I/O
// merge, or read of these stats by the number of I/Os in progress
// (field 9) times the number of milliseconds spent doing I/O since the
// last update of this field. This can provide an easy measure of both
// I/O completion time and the backlog that may be accumulating.
WeightedIoTime uint64 `json:"weighted_io_time"`
} }
type ContainerStats struct { type ContainerStats struct {
// The time of this stat point. // The time of this stat point.
Timestamp time.Time `json:"timestamp"` Timestamp time.Time `json:"timestamp"`
Cpu *CpuStats `json:"cpu,omitempty"` Cpu CpuStats `json:"cpu,omitempty"`
DiskIo DiskIoStats `json:"diskio,omitempty"` DiskIo DiskIoStats `json:"diskio,omitempty"`
Memory *MemoryStats `json:"memory,omitempty"` Memory MemoryStats `json:"memory,omitempty"`
Network *NetworkStats `json:"network,omitempty"` Network NetworkStats `json:"network,omitempty"`
// Filesystem statistics // Filesystem statistics
Filesystem []FsStats `json:"filesystem,omitempty"` Filesystem []FsStats `json:"filesystem,omitempty"`
} }
// Makes a deep copy of the ContainerStats and returns a pointer to the new
// copy. Copy() will allocate a new ContainerStats object if dst is nil.
func (self *ContainerStats) Copy(dst *ContainerStats) *ContainerStats {
if dst == nil {
dst = new(ContainerStats)
}
dst.Timestamp = self.Timestamp
if self.Cpu != nil {
if dst.Cpu == nil {
dst.Cpu = new(CpuStats)
}
// To make a deep copy of a slice, we need to copy every value
// in the slice. To make less memory allocation, we would like
// to reuse the slice in dst if possible.
percpu := dst.Cpu.Usage.PerCpu
if len(percpu) != len(self.Cpu.Usage.PerCpu) {
percpu = make([]uint64, len(self.Cpu.Usage.PerCpu))
}
dst.Cpu.Usage = self.Cpu.Usage
dst.Cpu.Load = self.Cpu.Load
copy(percpu, self.Cpu.Usage.PerCpu)
dst.Cpu.Usage.PerCpu = percpu
} else {
dst.Cpu = nil
}
if self.Memory != nil {
if dst.Memory == nil {
dst.Memory = new(MemoryStats)
}
*dst.Memory = *self.Memory
} else {
dst.Memory = nil
}
return dst
}
func timeEq(t1, t2 time.Time, tolerance time.Duration) bool { func timeEq(t1, t2 time.Time, tolerance time.Duration) bool {
// t1 should not be later than t2 // t1 should not be later than t2
if t1.After(t2) { if t1.After(t2) {
@ -328,12 +351,22 @@ func (a *ContainerStats) Eq(b *ContainerStats) bool {
// Checks equality of the stats values. // Checks equality of the stats values.
func (a *ContainerStats) StatsEq(b *ContainerStats) bool { func (a *ContainerStats) StatsEq(b *ContainerStats) bool {
// TODO(vmarmol): Consider using this through reflection.
if !reflect.DeepEqual(a.Cpu, b.Cpu) { if !reflect.DeepEqual(a.Cpu, b.Cpu) {
return false return false
} }
if !reflect.DeepEqual(a.Memory, b.Memory) { if !reflect.DeepEqual(a.Memory, b.Memory) {
return false return false
} }
if !reflect.DeepEqual(a.DiskIo, b.DiskIo) {
return false
}
if !reflect.DeepEqual(a.Network, b.Network) {
return false
}
if !reflect.DeepEqual(a.Filesystem, b.Filesystem) {
return false
}
return true return true
} }

View File

@ -15,7 +15,6 @@
package info package info
import ( import (
"reflect"
"testing" "testing"
"time" "time"
) )
@ -69,10 +68,7 @@ func TestStatsEndTime(t *testing.T) {
} }
func createStats(cpuUsage, memUsage uint64, timestamp time.Time) *ContainerStats { func createStats(cpuUsage, memUsage uint64, timestamp time.Time) *ContainerStats {
stats := &ContainerStats{ stats := &ContainerStats{}
Cpu: &CpuStats{},
Memory: &MemoryStats{},
}
stats.Cpu.Usage.PerCpu = []uint64{cpuUsage} stats.Cpu.Usage.PerCpu = []uint64{cpuUsage}
stats.Cpu.Usage.Total = cpuUsage stats.Cpu.Usage.Total = cpuUsage
stats.Cpu.Usage.System = 0 stats.Cpu.Usage.System = 0
@ -81,21 +77,3 @@ func createStats(cpuUsage, memUsage uint64, timestamp time.Time) *ContainerStats
stats.Timestamp = timestamp stats.Timestamp = timestamp
return stats return stats
} }
func TestContainerStatsCopy(t *testing.T) {
stats := createStats(100, 101, time.Now())
shadowStats := stats.Copy(nil)
if !reflect.DeepEqual(stats, shadowStats) {
t.Errorf("Copy() returned different object")
}
stats.Cpu.Usage.PerCpu[0] = shadowStats.Cpu.Usage.PerCpu[0] + 1
stats.Cpu.Load = shadowStats.Cpu.Load + 1
stats.Memory.Usage = shadowStats.Memory.Usage + 1
if reflect.DeepEqual(stats, shadowStats) {
t.Errorf("Copy() did not deeply copy the object")
}
stats = shadowStats.Copy(stats)
if !reflect.DeepEqual(stats, shadowStats) {
t.Errorf("Copy() returned different object")
}
}

View File

@ -31,8 +31,6 @@ func GenerateRandomStats(numStats, numCores int, duration time.Duration) []*info
} }
for i := 0; i < numStats; i++ { for i := 0; i < numStats; i++ {
stats := new(info.ContainerStats) stats := new(info.ContainerStats)
stats.Cpu = new(info.CpuStats)
stats.Memory = new(info.MemoryStats)
stats.Timestamp = currentTime stats.Timestamp = currentTime
currentTime = currentTime.Add(duration) currentTime = currentTime.Add(duration)

View File

@ -15,4 +15,4 @@
package info package info
// Version of cAdvisor. // Version of cAdvisor.
const VERSION = "0.5.0" const VERSION = "0.6.2"

View File

@ -25,6 +25,7 @@ import (
// cadvisorInterface is an abstract interface for testability. It abstracts the interface of "github.com/google/cadvisor/client".Client. // cadvisorInterface is an abstract interface for testability. It abstracts the interface of "github.com/google/cadvisor/client".Client.
type cadvisorInterface interface { type cadvisorInterface interface {
DockerContainer(name string, req *cadvisor.ContainerInfoRequest) (cadvisor.ContainerInfo, error)
ContainerInfo(name string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) ContainerInfo(name string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error)
MachineInfo() (*cadvisor.MachineInfo, error) MachineInfo() (*cadvisor.MachineInfo, error)
} }
@ -41,6 +42,16 @@ func (kl *Kubelet) statsFromContainerPath(cc cadvisorInterface, containerPath st
return cinfo, nil return cinfo, nil
} }
// This method takes a Docker container's ID and returns the stats for the
// container.
func (kl *Kubelet) statsFromDockerContainer(cc cadvisorInterface, containerId string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) {
cinfo, err := cc.DockerContainer(containerId, req)
if err != nil {
return nil, err
}
return &cinfo, nil
}
// GetContainerInfo returns stats (from Cadvisor) for a container. // GetContainerInfo returns stats (from Cadvisor) for a container.
func (kl *Kubelet) GetContainerInfo(podFullName, uuid, containerName string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) { func (kl *Kubelet) GetContainerInfo(podFullName, uuid, containerName string, req *cadvisor.ContainerInfoRequest) (*cadvisor.ContainerInfo, error) {
cc := kl.GetCadvisorClient() cc := kl.GetCadvisorClient()
@ -55,7 +66,7 @@ func (kl *Kubelet) GetContainerInfo(podFullName, uuid, containerName string, req
if !found { if !found {
return nil, fmt.Errorf("couldn't find container") return nil, fmt.Errorf("couldn't find container")
} }
return kl.statsFromContainerPath(cc, fmt.Sprintf("/docker/%s", dockerContainer.ID), req) return kl.statsFromDockerContainer(cc, dockerContainer.ID, req)
} }
// GetRootInfo returns stats (from Cadvisor) of current machine (root container). // GetRootInfo returns stats (from Cadvisor) of current machine (root container).

View File

@ -873,6 +873,12 @@ func (c *mockCadvisorClient) ContainerInfo(name string, req *info.ContainerInfoR
return args.Get(0).(*info.ContainerInfo), args.Error(1) return args.Get(0).(*info.ContainerInfo), args.Error(1)
} }
// DockerContainer is a mock implementation of CadvisorInterface.DockerContainer.
func (c *mockCadvisorClient) DockerContainer(name string, req *info.ContainerInfoRequest) (info.ContainerInfo, error) {
args := c.Called(name, req)
return args.Get(0).(info.ContainerInfo), args.Error(1)
}
// MachineInfo is a mock implementation of CadvisorInterface.MachineInfo. // MachineInfo is a mock implementation of CadvisorInterface.MachineInfo.
func (c *mockCadvisorClient) MachineInfo() (*info.MachineInfo, error) { func (c *mockCadvisorClient) MachineInfo() (*info.MachineInfo, error) {
args := c.Called() args := c.Called()
@ -882,7 +888,7 @@ func (c *mockCadvisorClient) MachineInfo() (*info.MachineInfo, error) {
func TestGetContainerInfo(t *testing.T) { func TestGetContainerInfo(t *testing.T) {
containerID := "ab2cdf" containerID := "ab2cdf"
containerPath := fmt.Sprintf("/docker/%v", containerID) containerPath := fmt.Sprintf("/docker/%v", containerID)
containerInfo := &info.ContainerInfo{ containerInfo := info.ContainerInfo{
ContainerReference: info.ContainerReference{ ContainerReference: info.ContainerReference{
Name: containerPath, Name: containerPath,
}, },
@ -890,7 +896,7 @@ func TestGetContainerInfo(t *testing.T) {
mockCadvisor := &mockCadvisorClient{} mockCadvisor := &mockCadvisorClient{}
cadvisorReq := &info.ContainerInfoRequest{} cadvisorReq := &info.ContainerInfoRequest{}
mockCadvisor.On("ContainerInfo", containerPath, cadvisorReq).Return(containerInfo, nil) mockCadvisor.On("DockerContainer", containerID, cadvisorReq).Return(containerInfo, nil)
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
kubelet.cadvisorClient = mockCadvisor kubelet.cadvisorClient = mockCadvisor
@ -961,13 +967,12 @@ func TestGetContainerInfoWithoutCadvisor(t *testing.T) {
func TestGetContainerInfoWhenCadvisorFailed(t *testing.T) { func TestGetContainerInfoWhenCadvisorFailed(t *testing.T) {
containerID := "ab2cdf" containerID := "ab2cdf"
containerPath := fmt.Sprintf("/docker/%v", containerID)
containerInfo := &info.ContainerInfo{} containerInfo := info.ContainerInfo{}
mockCadvisor := &mockCadvisorClient{} mockCadvisor := &mockCadvisorClient{}
cadvisorReq := &info.ContainerInfoRequest{} cadvisorReq := &info.ContainerInfoRequest{}
expectedErr := fmt.Errorf("some error") expectedErr := fmt.Errorf("some error")
mockCadvisor.On("ContainerInfo", containerPath, cadvisorReq).Return(containerInfo, expectedErr) mockCadvisor.On("DockerContainer", containerID, cadvisorReq).Return(containerInfo, expectedErr)
kubelet, _, fakeDocker := newTestKubelet(t) kubelet, _, fakeDocker := newTestKubelet(t)
kubelet.cadvisorClient = mockCadvisor kubelet.cadvisorClient = mockCadvisor