update to 0.4.0 for cadvisor

This commit is contained in:
Eric Paris 2014-09-22 13:49:45 -04:00
parent 71c1137213
commit b24f202dd4
6 changed files with 20 additions and 427 deletions

8
Godeps/Godeps.json generated
View File

@ -60,13 +60,13 @@
},
{
"ImportPath": "github.com/google/cadvisor/client",
"Comment": "0.2.0-27-g17b0ec5",
"Rev": "17b0ec576bcbeb321c133e4378dee1e500c9850d"
"Comment": "0.4.0",
"Rev": "5a6d06c02600b1e57e55a9d9f71dbac1bfc9fe6c"
},
{
"ImportPath": "github.com/google/cadvisor/info",
"Comment": "0.2.0-27-g17b0ec5",
"Rev": "17b0ec576bcbeb321c133e4378dee1e500c9850d"
"Comment": "0.4.0",
"Rev": "5a6d06c02600b1e57e55a9d9f71dbac1bfc9fe6c"
},
{
"ImportPath": "github.com/google/gofuzz",

View File

@ -49,10 +49,10 @@ func cadvisorTestClient(path string, expectedPostObj, expectedPostObjEmpty, repl
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(expectedPostObjEmpty)
if err != nil {
t.Errorf("Recieved invalid object: %v", err)
t.Errorf("Received invalid object: %v", err)
}
if !reflect.DeepEqual(expectedPostObj, expectedPostObjEmpty) {
t.Errorf("Recieved unexpected object: %+v", expectedPostObjEmpty)
t.Errorf("Received unexpected object: %+v", expectedPostObjEmpty)
}
}
encoder := json.NewEncoder(w)
@ -93,10 +93,7 @@ func TestGetMachineinfo(t *testing.T) {
func TestGetContainerInfo(t *testing.T) {
query := &info.ContainerInfoRequest{
NumStats: 3,
NumSamples: 2,
CpuUsagePercentiles: []int{10, 50, 90},
MemoryUsagePercentiles: []int{10, 80, 90},
NumStats: 3,
}
containerName := "/some/container"
cinfo := itest.GenerateRandomContainerInfo(containerName, 4, query, 1*time.Second)

View File

@ -15,9 +15,7 @@
package info
import (
"fmt"
"reflect"
"sort"
"time"
)
@ -59,13 +57,6 @@ type ContainerReference struct {
type ContainerInfoRequest struct {
// Max number of stats to return.
NumStats int `json:"num_stats,omitempty"`
// Max number of samples to return.
NumSamples int `json:"num_samples,omitempty"`
// Different percentiles of CPU usage within a period. The values must be within [0, 100]
CpuUsagePercentiles []int `json:"cpu_usage_percentiles,omitempty"`
// Different percentiles of memory usage within a period. The values must be within [0, 100]
MemoryUsagePercentiles []int `json:"memory_usage_percentiles,omitempty"`
}
type ContainerInfo struct {
@ -79,11 +70,6 @@ type ContainerInfo struct {
// Historical statistics gathered from the container.
Stats []*ContainerStats `json:"stats,omitempty"`
// Randomly sampled container states.
Samples []*ContainerStatsSample `json:"samples,omitempty"`
StatsPercentiles *ContainerStatsPercentiles `json:"stats_summary,omitempty"`
}
// ContainerInfo may be (un)marshaled by json or other en/decoder. In that
@ -112,9 +98,6 @@ func (self *ContainerInfo) Eq(b *ContainerInfo) bool {
if !reflect.DeepEqual(self.Spec, b.Spec) {
return false
}
if !reflect.DeepEqual(self.StatsPercentiles, b.StatsPercentiles) {
return false
}
for i, expectedStats := range b.Stats {
selfStats := self.Stats[i]
@ -123,12 +106,6 @@ func (self *ContainerInfo) Eq(b *ContainerInfo) bool {
}
}
for i, expectedSample := range b.Samples {
selfSample := self.Samples[i]
if !expectedSample.Eq(selfSample) {
return false
}
}
return true
}
@ -278,24 +255,6 @@ func (self *ContainerStats) Copy(dst *ContainerStats) *ContainerStats {
return dst
}
type ContainerStatsSample struct {
// Timetamp of the end of the sample period
Timestamp time.Time `json:"timestamp"`
// Duration of the sample period
Duration time.Duration `json:"duration"`
Cpu struct {
// number of nanoseconds of CPU time used by the container
Usage uint64 `json:"usage"`
// Per-core usage of the container. (unit: nanoseconds)
PerCpuUsage []uint64 `json:"per_cpu_usage,omitempty"`
} `json:"cpu"`
Memory struct {
// Units: Bytes.
Usage uint64 `json:"usage"`
} `json:"memory"`
}
func timeEq(t1, t2 time.Time, tolerance time.Duration) bool {
// t1 should not be later than t2
if t1.After(t2) {
@ -330,6 +289,11 @@ func (a *ContainerStats) Eq(b *ContainerStats) bool {
if !timeEq(a.Timestamp, b.Timestamp, timePrecision) {
return false
}
return a.StatsEq(b)
}
// Checks equality of the stats values.
func (a *ContainerStats) StatsEq(b *ContainerStats) bool {
if !reflect.DeepEqual(a.Cpu, b.Cpu) {
return false
}
@ -339,138 +303,10 @@ func (a *ContainerStats) Eq(b *ContainerStats) bool {
return true
}
// This function is useful because we do not require precise time
// representation.
func (a *ContainerStatsSample) Eq(b *ContainerStatsSample) bool {
if !timeEq(a.Timestamp, b.Timestamp, timePrecision) {
return false
// Saturate CPU usage to 0.
func calculateCpuUsage(prev, cur uint64) uint64 {
if prev > cur {
return 0
}
if !durationEq(a.Duration, b.Duration, timePrecision) {
return false
}
if !reflect.DeepEqual(a.Cpu, b.Cpu) {
return false
}
if !reflect.DeepEqual(a.Memory, b.Memory) {
return false
}
return true
}
type Percentile struct {
Percentage int `json:"percentage"`
Value uint64 `json:"value"`
}
type ContainerStatsPercentiles struct {
MaxMemoryUsage uint64 `json:"max_memory_usage,omitempty"`
MemoryUsagePercentiles []Percentile `json:"memory_usage_percentiles,omitempty"`
CpuUsagePercentiles []Percentile `json:"cpu_usage_percentiles,omitempty"`
}
// Each sample needs two stats because the cpu usage in ContainerStats is
// cumulative.
// prev should be an earlier observation than current.
// This method is not thread/goroutine safe.
func NewSample(prev, current *ContainerStats) (*ContainerStatsSample, error) {
if prev == nil || current == nil {
return nil, fmt.Errorf("empty stats")
}
// Ignore this sample if it is incomplete
if prev.Cpu == nil || prev.Memory == nil || current.Cpu == nil || current.Memory == nil {
return nil, fmt.Errorf("incomplete stats")
}
// prev must be an early observation
if !current.Timestamp.After(prev.Timestamp) {
return nil, fmt.Errorf("wrong stats order")
}
// This data is invalid.
if current.Cpu.Usage.Total < prev.Cpu.Usage.Total {
return nil, fmt.Errorf("current CPU usage is less than prev CPU usage (cumulative).")
}
var percpu []uint64
if len(current.Cpu.Usage.PerCpu) > 0 {
curNumCpus := len(current.Cpu.Usage.PerCpu)
percpu = make([]uint64, curNumCpus)
for i, currUsage := range current.Cpu.Usage.PerCpu {
var prevUsage uint64 = 0
if i < len(prev.Cpu.Usage.PerCpu) {
prevUsage = prev.Cpu.Usage.PerCpu[i]
}
if currUsage < prevUsage {
return nil, fmt.Errorf("current per-core CPU usage is less than prev per-core CPU usage (cumulative).")
}
percpu[i] = currUsage - prevUsage
}
}
sample := new(ContainerStatsSample)
// Calculate the diff to get the CPU usage within the time interval.
sample.Cpu.Usage = current.Cpu.Usage.Total - prev.Cpu.Usage.Total
sample.Cpu.PerCpuUsage = percpu
// Memory usage is current memory usage
sample.Memory.Usage = current.Memory.Usage
sample.Timestamp = current.Timestamp
sample.Duration = current.Timestamp.Sub(prev.Timestamp)
return sample, nil
}
type uint64Slice []uint64
func (self uint64Slice) Len() int {
return len(self)
}
func (self uint64Slice) Less(i, j int) bool {
return self[i] < self[j]
}
func (self uint64Slice) Swap(i, j int) {
self[i], self[j] = self[j], self[i]
}
func (self uint64Slice) Percentiles(requestedPercentiles ...int) []Percentile {
if len(self) == 0 {
return nil
}
ret := make([]Percentile, 0, len(requestedPercentiles))
sort.Sort(self)
for _, p := range requestedPercentiles {
idx := (len(self) * p / 100) - 1
if idx < 0 {
idx = 0
}
ret = append(
ret,
Percentile{
Percentage: p,
Value: self[idx],
},
)
}
return ret
}
func NewPercentiles(samples []*ContainerStatsSample, cpuPercentages, memoryPercentages []int) *ContainerStatsPercentiles {
if len(samples) == 0 {
return nil
}
cpuUsages := make([]uint64, 0, len(samples))
memUsages := make([]uint64, 0, len(samples))
for _, sample := range samples {
if sample == nil {
continue
}
cpuUsages = append(cpuUsages, sample.Cpu.Usage)
memUsages = append(memUsages, sample.Memory.Usage)
}
ret := new(ContainerStatsPercentiles)
ret.CpuUsagePercentiles = uint64Slice(cpuUsages).Percentiles(cpuPercentages...)
ret.MemoryUsagePercentiles = uint64Slice(memUsages).Percentiles(memoryPercentages...)
return ret
return cur - prev
}

View File

@ -68,65 +68,6 @@ func TestStatsEndTime(t *testing.T) {
}
}
func TestPercentiles(t *testing.T) {
N := 100
data := make([]uint64, N)
for i := 1; i < N+1; i++ {
data[i-1] = uint64(i)
}
percentages := []int{
80,
90,
50,
}
percentiles := uint64Slice(data).Percentiles(percentages...)
for _, s := range percentiles {
if s.Value != uint64(s.Percentage) {
t.Errorf("%v percentile data should be %v, but got %v", s.Percentage, s.Percentage, s.Value)
}
}
}
func TestPercentilesSmallDataSet(t *testing.T) {
var value uint64 = 11
data := []uint64{value}
percentages := []int{
80,
90,
50,
}
percentiles := uint64Slice(data).Percentiles(percentages...)
for _, s := range percentiles {
if s.Value != value {
t.Errorf("%v percentile data should be %v, but got %v", s.Percentage, value, s.Value)
}
}
}
func TestNewSampleNilStats(t *testing.T) {
stats := &ContainerStats{
Cpu: &CpuStats{},
Memory: &MemoryStats{},
}
stats.Cpu.Usage.PerCpu = []uint64{uint64(10)}
stats.Cpu.Usage.Total = uint64(10)
stats.Cpu.Usage.System = uint64(2)
stats.Cpu.Usage.User = uint64(8)
stats.Memory.Usage = uint64(200)
sample, err := NewSample(nil, stats)
if err == nil {
t.Errorf("generated an unexpected sample: %+v", sample)
}
sample, err = NewSample(stats, nil)
if err == nil {
t.Errorf("generated an unexpected sample: %+v", sample)
}
}
func createStats(cpuUsage, memUsage uint64, timestamp time.Time) *ContainerStats {
stats := &ContainerStats{
Cpu: &CpuStats{},
@ -141,144 +82,6 @@ func createStats(cpuUsage, memUsage uint64, timestamp time.Time) *ContainerStats
return stats
}
func TestAddSample(t *testing.T) {
cpuPrevUsage := uint64(10)
cpuCurrentUsage := uint64(15)
memCurrentUsage := uint64(200)
prevTime := time.Now()
prev := createStats(cpuPrevUsage, memCurrentUsage, prevTime)
current := createStats(cpuCurrentUsage, memCurrentUsage, prevTime.Add(1*time.Second))
sample, err := NewSample(prev, current)
if err != nil {
t.Errorf("should be able to generate a sample. but received error: %v", err)
}
if sample == nil {
t.Fatalf("nil sample and nil error. unexpected result!")
}
if sample.Memory.Usage != memCurrentUsage {
t.Errorf("wrong memory usage: %v. should be %v", sample.Memory.Usage, memCurrentUsage)
}
if sample.Cpu.Usage != cpuCurrentUsage-cpuPrevUsage {
t.Errorf("wrong CPU usage: %v. should be %v", sample.Cpu.Usage, cpuCurrentUsage-cpuPrevUsage)
}
}
func TestAddSampleIncompleteStats(t *testing.T) {
cpuPrevUsage := uint64(10)
cpuCurrentUsage := uint64(15)
memCurrentUsage := uint64(200)
prevTime := time.Now()
prev := createStats(cpuPrevUsage, memCurrentUsage, prevTime)
current := createStats(cpuCurrentUsage, memCurrentUsage, prevTime.Add(1*time.Second))
stats := &ContainerStats{
Cpu: prev.Cpu,
Memory: nil,
}
sample, err := NewSample(stats, current)
if err == nil {
t.Errorf("generated an unexpected sample: %+v", sample)
}
sample, err = NewSample(prev, stats)
if err == nil {
t.Errorf("generated an unexpected sample: %+v", sample)
}
stats = &ContainerStats{
Cpu: nil,
Memory: prev.Memory,
}
sample, err = NewSample(stats, current)
if err == nil {
t.Errorf("generated an unexpected sample: %+v", sample)
}
sample, err = NewSample(prev, stats)
if err == nil {
t.Errorf("generated an unexpected sample: %+v", sample)
}
}
func TestAddSampleWrongOrder(t *testing.T) {
cpuPrevUsage := uint64(10)
cpuCurrentUsage := uint64(15)
memCurrentUsage := uint64(200)
prevTime := time.Now()
prev := createStats(cpuPrevUsage, memCurrentUsage, prevTime)
current := createStats(cpuCurrentUsage, memCurrentUsage, prevTime.Add(1*time.Second))
sample, err := NewSample(current, prev)
if err == nil {
t.Errorf("generated an unexpected sample: %+v", sample)
}
}
func TestAddSampleWrongCpuUsage(t *testing.T) {
cpuPrevUsage := uint64(15)
cpuCurrentUsage := uint64(10)
memCurrentUsage := uint64(200)
prevTime := time.Now()
prev := createStats(cpuPrevUsage, memCurrentUsage, prevTime)
current := createStats(cpuCurrentUsage, memCurrentUsage, prevTime.Add(1*time.Second))
sample, err := NewSample(prev, current)
if err == nil {
t.Errorf("generated an unexpected sample: %+v", sample)
}
}
func TestAddSampleHotPluggingCpu(t *testing.T) {
cpuPrevUsage := uint64(10)
cpuCurrentUsage := uint64(15)
memCurrentUsage := uint64(200)
prevTime := time.Now()
prev := createStats(cpuPrevUsage, memCurrentUsage, prevTime)
current := createStats(cpuCurrentUsage, memCurrentUsage, prevTime.Add(1*time.Second))
current.Cpu.Usage.PerCpu = append(current.Cpu.Usage.PerCpu, 10)
sample, err := NewSample(prev, current)
if err != nil {
t.Errorf("should be able to generate a sample. but received error: %v", err)
}
if len(sample.Cpu.PerCpuUsage) != 2 {
t.Fatalf("Should have 2 cores.")
}
if sample.Cpu.PerCpuUsage[0] != cpuCurrentUsage-cpuPrevUsage {
t.Errorf("First cpu usage is %v. should be %v", sample.Cpu.PerCpuUsage[0], cpuCurrentUsage-cpuPrevUsage)
}
if sample.Cpu.PerCpuUsage[1] != 10 {
t.Errorf("Second cpu usage is %v. should be 10", sample.Cpu.PerCpuUsage[1])
}
}
func TestAddSampleHotUnpluggingCpu(t *testing.T) {
cpuPrevUsage := uint64(10)
cpuCurrentUsage := uint64(15)
memCurrentUsage := uint64(200)
prevTime := time.Now()
prev := createStats(cpuPrevUsage, memCurrentUsage, prevTime)
current := createStats(cpuCurrentUsage, memCurrentUsage, prevTime.Add(1*time.Second))
prev.Cpu.Usage.PerCpu = append(prev.Cpu.Usage.PerCpu, 10)
sample, err := NewSample(prev, current)
if err != nil {
t.Errorf("should be able to generate a sample. but received error: %v", err)
}
if len(sample.Cpu.PerCpuUsage) != 1 {
t.Fatalf("Should have 1 cores.")
}
if sample.Cpu.PerCpuUsage[0] != cpuCurrentUsage-cpuPrevUsage {
t.Errorf("First cpu usage is %v. should be %v", sample.Cpu.PerCpuUsage[0], cpuCurrentUsage-cpuPrevUsage)
}
}
func TestContainerStatsCopy(t *testing.T) {
stats := createStats(100, 101, time.Now())
shadowStats := stats.Copy(nil)

View File

@ -65,57 +65,14 @@ func GenerateRandomContainerSpec(numCores int) *info.ContainerSpec {
func GenerateRandomContainerInfo(containerName string, numCores int, query *info.ContainerInfoRequest, duration time.Duration) *info.ContainerInfo {
stats := GenerateRandomStats(query.NumStats, numCores, duration)
samples, _ := NewSamplesFromStats(stats...)
if len(samples) > query.NumSamples {
samples = samples[:query.NumSamples]
}
cpuPercentiles := make([]info.Percentile, 0, len(query.CpuUsagePercentiles))
// TODO(monnand): This will generate percentiles where 50%tile data may
// be larger than 90%tile data.
for _, p := range query.CpuUsagePercentiles {
percentile := info.Percentile{p, uint64(rand.Int63n(1000))}
cpuPercentiles = append(cpuPercentiles, percentile)
}
memPercentiles := make([]info.Percentile, 0, len(query.MemoryUsagePercentiles))
for _, p := range query.MemoryUsagePercentiles {
percentile := info.Percentile{p, uint64(rand.Int63n(1000))}
memPercentiles = append(memPercentiles, percentile)
}
percentiles := &info.ContainerStatsPercentiles{
MaxMemoryUsage: uint64(rand.Int63n(4096)),
MemoryUsagePercentiles: memPercentiles,
CpuUsagePercentiles: cpuPercentiles,
}
spec := GenerateRandomContainerSpec(numCores)
ret := &info.ContainerInfo{
ContainerReference: info.ContainerReference{
Name: containerName,
},
Spec: spec,
StatsPercentiles: percentiles,
Samples: samples,
Stats: stats,
Spec: spec,
Stats: stats,
}
return ret
}
func NewSamplesFromStats(stats ...*info.ContainerStats) ([]*info.ContainerStatsSample, error) {
if len(stats) < 2 {
return nil, nil
}
samples := make([]*info.ContainerStatsSample, 0, len(stats)-1)
for i, s := range stats[1:] {
prev := stats[i]
sample, err := info.NewSample(prev, s)
if err != nil {
return nil, fmt.Errorf("Unable to generate sample from %+v and %+v: %v",
prev, s, err)
}
samples = append(samples, sample)
}
return samples, nil
}

View File

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