Added integration tests for the new api/v2.1/machinestats API.

Updated the v2 client along the way.

Signed-off-by: Vishnu kannan <vishnuk@google.com>
This commit is contained in:
Vishnu kannan 2016-01-11 15:15:43 -08:00
parent 944201c86b
commit c2a30dba3e
7 changed files with 127 additions and 48 deletions

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Copyright 2015 Google Inc. All rights reserved.
# 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.
@ -12,6 +12,7 @@
# 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.
echo ">> starting cAdvisor locally"
sudo ./cadvisor &

View File

@ -25,7 +25,7 @@ import (
"path"
"strings"
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/info/v1"
"github.com/golang/glog"
)
@ -35,7 +35,7 @@ type Client struct {
baseUrl string
}
// NewClient returns a new client with the specified base URL.
// NewClient returns a new v1.3 client with the specified base URL.
func NewClient(url string) (*Client, error) {
if !strings.HasSuffix(url, "/") {
url += "/"
@ -47,9 +47,9 @@ func NewClient(url string) (*Client, error) {
}
// Returns all past events that satisfy the request
func (self *Client) EventStaticInfo(name string) (einfo []*info.Event, err error) {
func (self *Client) EventStaticInfo(name string) (einfo []*v1.Event, err error) {
u := self.eventsInfoUrl(name)
ret := new([]*info.Event)
ret := new([]*v1.Event)
if err = self.httpGetJsonData(ret, nil, u, "event info"); err != nil {
return
}
@ -59,7 +59,7 @@ func (self *Client) EventStaticInfo(name string) (einfo []*info.Event, err error
// Streams all events that occur that satisfy the request into the channel
// that is passed
func (self *Client) EventStreamingInfo(name string, einfo chan *info.Event) (err error) {
func (self *Client) EventStreamingInfo(name string, einfo chan *v1.Event) (err error) {
u := self.eventsInfoUrl(name)
if err = self.getEventStreamingData(u, einfo); err != nil {
return
@ -70,9 +70,9 @@ func (self *Client) EventStreamingInfo(name string, einfo chan *info.Event) (err
// MachineInfo returns the JSON machine information for this client.
// A non-nil error result indicates a problem with obtaining
// the JSON machine information data.
func (self *Client) MachineInfo() (minfo *info.MachineInfo, err error) {
func (self *Client) MachineInfo() (minfo *v1.MachineInfo, err error) {
u := self.machineInfoUrl()
ret := new(info.MachineInfo)
ret := new(v1.MachineInfo)
if err = self.httpGetJsonData(ret, nil, u, "machine info"); err != nil {
return
}
@ -82,9 +82,9 @@ func (self *Client) MachineInfo() (minfo *info.MachineInfo, err error) {
// ContainerInfo returns the JSON container information for the specified
// container and request.
func (self *Client) ContainerInfo(name string, query *info.ContainerInfoRequest) (cinfo *info.ContainerInfo, err error) {
func (self *Client) ContainerInfo(name string, query *v1.ContainerInfoRequest) (cinfo *v1.ContainerInfo, err error) {
u := self.containerInfoUrl(name)
ret := new(info.ContainerInfo)
ret := new(v1.ContainerInfo)
if err = self.httpGetJsonData(ret, query, u, fmt.Sprintf("container info for %q", name)); err != nil {
return
}
@ -93,12 +93,12 @@ func (self *Client) ContainerInfo(name string, query *info.ContainerInfoRequest)
}
// Returns the information about all subcontainers (recursive) of the specified container (including itself).
func (self *Client) SubcontainersInfo(name string, query *info.ContainerInfoRequest) ([]info.ContainerInfo, error) {
var response []info.ContainerInfo
func (self *Client) SubcontainersInfo(name string, query *v1.ContainerInfoRequest) ([]v1.ContainerInfo, error) {
var response []v1.ContainerInfo
url := self.subcontainersInfoUrl(name)
err := self.httpGetJsonData(&response, query, url, fmt.Sprintf("subcontainers container info for %q", name))
if err != nil {
return []info.ContainerInfo{}, err
return []v1.ContainerInfo{}, err
}
return response, nil
@ -106,9 +106,9 @@ func (self *Client) SubcontainersInfo(name string, query *info.ContainerInfoRequ
// 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) {
func (self *Client) DockerContainer(name string, query *v1.ContainerInfoRequest) (cinfo v1.ContainerInfo, err error) {
u := self.dockerInfoUrl(name)
ret := make(map[string]info.ContainerInfo)
ret := make(map[string]v1.ContainerInfo)
if err = self.httpGetJsonData(&ret, query, u, fmt.Sprintf("Docker container info for %q", name)); err != nil {
return
}
@ -123,13 +123,13 @@ func (self *Client) DockerContainer(name string, query *info.ContainerInfoReques
}
// Returns the JSON container information for all Docker containers.
func (self *Client) AllDockerContainers(query *info.ContainerInfoRequest) (cinfo []info.ContainerInfo, err error) {
func (self *Client) AllDockerContainers(query *v1.ContainerInfoRequest) (cinfo []v1.ContainerInfo, err error) {
u := self.dockerInfoUrl("/")
ret := make(map[string]info.ContainerInfo)
ret := make(map[string]v1.ContainerInfo)
if err = self.httpGetJsonData(&ret, query, u, "all Docker containers info"); err != nil {
return
}
cinfo = make([]info.ContainerInfo, 0, len(ret))
cinfo = make([]v1.ContainerInfo, 0, len(ret))
for _, cont := range ret {
cinfo = append(cinfo, cont)
}
@ -191,7 +191,7 @@ func (self *Client) httpGetJsonData(data, postData interface{}, url, infoName st
return nil
}
func (self *Client) getEventStreamingData(url string, einfo chan *info.Event) error {
func (self *Client) getEventStreamingData(url string, einfo chan *v1.Event) error {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
@ -205,7 +205,7 @@ func (self *Client) getEventStreamingData(url string, einfo chan *info.Event) er
}
dec := json.NewDecoder(resp.Body)
var m *info.Event = &info.Event{}
var m *v1.Event = &v1.Event{}
for {
err := dec.Decode(m)
if err != nil {

View File

@ -25,7 +25,7 @@ import (
"strings"
v1 "github.com/google/cadvisor/info/v1"
info "github.com/google/cadvisor/info/v2"
"github.com/google/cadvisor/info/v2"
)
// Client represents the base URL for a cAdvisor client.
@ -40,7 +40,7 @@ func NewClient(url string) (*Client, error) {
}
return &Client{
baseUrl: fmt.Sprintf("%sapi/v2.0/", url),
baseUrl: fmt.Sprintf("%sapi/v2.1/", url),
}, nil
}
@ -57,6 +57,16 @@ func (self *Client) MachineInfo() (minfo *v1.MachineInfo, err error) {
return
}
// MachineStats returns the JSON machine statistics for this client.
// A non-nil error result indicates a problem with obtaining
// the JSON machine information data.
func (self *Client) MachineStats() ([]v2.MachineStats, error) {
var ret []v2.MachineStats
u := self.machineStatsUrl()
err := self.httpGetJsonData(&ret, nil, u, "machine stats")
return ret, err
}
// VersionInfo returns the version info for cAdvisor.
func (self *Client) VersionInfo() (version string, err error) {
u := self.versionInfoUrl()
@ -65,9 +75,9 @@ func (self *Client) VersionInfo() (version string, err error) {
}
// Attributes returns hardware and software attributes of the machine.
func (self *Client) Attributes() (attr *info.Attributes, err error) {
func (self *Client) Attributes() (attr *v2.Attributes, err error) {
u := self.attributesUrl()
ret := new(info.Attributes)
ret := new(v2.Attributes)
if err = self.httpGetJsonData(ret, nil, u, "attributes"); err != nil {
return
}
@ -79,6 +89,10 @@ func (self *Client) machineInfoUrl() string {
return self.baseUrl + path.Join("machine")
}
func (self *Client) machineStatsUrl() string {
return self.baseUrl + path.Join("machinestats")
}
func (self *Client) versionInfoUrl() string {
return self.baseUrl + path.Join("version")
}

View File

@ -24,8 +24,9 @@ import (
"testing"
"time"
info "github.com/google/cadvisor/info/v1"
infoV2 "github.com/google/cadvisor/info/v2"
"github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/info/v2"
"github.com/stretchr/testify/assert"
"github.com/kr/pretty"
)
@ -44,11 +45,11 @@ func testGetJsonData(
return nil
}
func cadvisorTestClient(path string, expectedPostObj *info.ContainerInfoRequest, replyObj interface{}, t *testing.T) (*Client, *httptest.Server, error) {
func cadvisorTestClient(path string, expectedPostObj *v1.ContainerInfoRequest, replyObj interface{}, t *testing.T) (*Client, *httptest.Server, error) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == path {
if expectedPostObj != nil {
expectedPostObjEmpty := new(info.ContainerInfoRequest)
expectedPostObjEmpty := new(v1.ContainerInfoRequest)
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(expectedPostObjEmpty); err != nil {
t.Errorf("Received invalid object: %v", err)
@ -61,7 +62,7 @@ func cadvisorTestClient(path string, expectedPostObj *info.ContainerInfoRequest,
}
encoder := json.NewEncoder(w)
encoder.Encode(replyObj)
} else if r.URL.Path == "/api/v2.0/version" {
} else if r.URL.Path == "/api/v2.1/version" {
fmt.Fprintf(w, "0.1.2")
} else {
w.WriteHeader(http.StatusNotFound)
@ -78,11 +79,11 @@ func cadvisorTestClient(path string, expectedPostObj *info.ContainerInfoRequest,
// TestGetMachineInfo performs one test to check if MachineInfo()
// in a cAdvisor client returns the correct result.
func TestGetMachineinfo(t *testing.T) {
minfo := &info.MachineInfo{
func TestGetMachineInfo(t *testing.T) {
mv1 := &v1.MachineInfo{
NumCores: 8,
MemoryCapacity: 31625871360,
DiskMap: map[string]info.DiskInfo{
DiskMap: map[string]v1.DiskInfo{
"8:0": {
Name: "sda",
Major: 8,
@ -91,7 +92,7 @@ func TestGetMachineinfo(t *testing.T) {
},
},
}
client, server, err := cadvisorTestClient("/api/v2.0/machine", nil, minfo, t)
client, server, err := cadvisorTestClient("/api/v2.1/machine", nil, mv1, t)
if err != nil {
t.Fatalf("unable to get a client %v", err)
}
@ -100,14 +101,14 @@ func TestGetMachineinfo(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(returned, minfo) {
t.Fatalf("received unexpected machine info")
if !reflect.DeepEqual(returned, mv1) {
t.Fatalf("received unexpected machine v1")
}
}
// TestGetVersionInfo performs one test to check if VersionInfo()
// TestGetVersionV1 performs one test to check if VersionV1()
// in a cAdvisor client returns the correct result.
func TestGetVersioninfo(t *testing.T) {
func TestGetVersionv1(t *testing.T) {
version := "0.1.2"
client, server, err := cadvisorTestClient("", nil, version, t)
if err != nil {
@ -119,21 +120,21 @@ func TestGetVersioninfo(t *testing.T) {
t.Fatal(err)
}
if returned != version {
t.Fatalf("received unexpected version info")
t.Fatalf("received unexpected version v1")
}
}
// TestAttributes performs one test to check if Attributes()
// in a cAdvisor client returns the correct result.
func TestGetAttributes(t *testing.T) {
attr := &infoV2.Attributes{
attr := &v2.Attributes{
KernelVersion: "3.3.0",
ContainerOsVersion: "Ubuntu 14.4",
DockerVersion: "Docker 1.5",
CadvisorVersion: "0.1.2",
NumCores: 8,
MemoryCapacity: 31625871360,
DiskMap: map[string]info.DiskInfo{
DiskMap: map[string]v1.DiskInfo{
"8:0": {
Name: "sda",
Major: 8,
@ -142,7 +143,7 @@ func TestGetAttributes(t *testing.T) {
},
},
}
client, server, err := cadvisorTestClient("/api/v2.0/attributes", nil, attr, t)
client, server, err := cadvisorTestClient("/api/v2.1/attributes", nil, attr, t)
if err != nil {
t.Fatalf("unable to get a client %v", err)
}
@ -184,8 +185,12 @@ func TestMachineStats(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(returned, machineStats) {
t.Fatalf("received unexpected machine stats\nExp: %v\nAct: %v", machineStats, returned)
assert.Len(t, returned, len(machineStats))
if !reflect.DeepEqual(returned[0].Cpu, machineStats[0].Cpu) {
t.Fatalf("received unexpected machine stats\nExp: %+v\nAct: %+v", machineStats, returned)
}
if !reflect.DeepEqual(returned[0].Filesystem, machineStats[0].Filesystem) {
t.Fatalf("received unexpected machine stats\nExp: %+v\nAct: %+v", machineStats, returned)
}
}

View File

@ -73,7 +73,12 @@ func MachineStatsFromV1(cont *v1.ContainerInfo) []MachineStats {
stat.Memory = &val.Memory
}
if cont.Spec.HasNetwork {
stat.Network.Interfaces = val.Network.Interfaces
stat.Network = &NetworkStats{
// FIXME: Use reflection instead.
Tcp: TcpStat(val.Network.Tcp),
Tcp6: TcpStat(val.Network.Tcp6),
Interfaces: val.Network.Interfaces,
}
}
if cont.Spec.HasFilesystem {
stat.Filesystem = machineFsStatsFromV1(val.Filesystem)

View File

@ -143,7 +143,7 @@ type DiskStats struct {
// This is the total number of sectors read successfully.
SectorsRead *uint64 `json:"sectors_read,omitempty"`
// Number of milliseconds spent reading
// Time spent reading
// This is the total number of milliseconds spent by all reads (as
// measured from __make_request() to end_that_request_last()).
ReadDuration *time.Duration `json:"read_duration,omitempty"`
@ -160,7 +160,7 @@ type DiskStats struct {
// This is the total number of sectors written successfully.
SectorsWritten *uint64 `json:"sectors_written,omitempty"`
// Number of milliseconds spent writing
// Time spent writing
// This is the total number of milliseconds spent by all writes (as
// measured from __make_request() to end_that_request_last()).
WriteDuration *time.Duration `json:"write_duration,omitempty"`
@ -170,11 +170,11 @@ type DiskStats struct {
// given to appropriate struct request_queue and decremented as they finish.
IoInProgress *uint64 `json:"io_in_progress,omitempty"`
// Number of milliseconds spent doing I/Os
// Time spent doing I/Os
// This field increases so long as field 9 is nonzero.
IoDuration *time.Duration `json:"io_duration,omitempty"`
// weighted number of milliseconds spent doing I/Os
// weighted time 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

View File

@ -0,0 +1,54 @@
// 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 api
import (
"testing"
"time"
"github.com/google/cadvisor/integration/framework"
"github.com/stretchr/testify/assert"
)
func TestMachineStatsIsReturned(t *testing.T) {
fm := framework.New(t)
defer fm.Cleanup()
machineStats, err := fm.Cadvisor().ClientV2().MachineStats()
if err != nil {
t.Fatal(err)
}
as := assert.New(t)
for _, stat := range machineStats {
as.NotEqual(stat.Timestamp, time.Time{})
as.True(stat.Cpu.Usage.Total > 0)
as.True(len(stat.Cpu.Usage.PerCpu) > 0)
if stat.CpuInst != nil {
as.True(stat.CpuInst.Usage.Total > 0)
}
as.True(stat.Memory.Usage > 0)
for _, nStat := range stat.Network.Interfaces {
as.NotEqual(nStat.Name, "")
as.NotEqual(nStat.RxBytes, 0)
}
for _, fsStat := range stat.Filesystem {
as.NotEqual(fsStat.Device, "")
as.NotNil(fsStat.Capacity)
as.NotNil(fsStat.Usage)
as.NotNil(fsStat.ReadsCompleted)
}
}
}