diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 237d9308dcf..b680328303e 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -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", diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/client/client_test.go b/Godeps/_workspace/src/github.com/google/cadvisor/client/client_test.go index f8016f178f8..eb6b7a45c87 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/client/client_test.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/client/client_test.go @@ -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) diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/container.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/container.go index 58931c8353e..7858fa5392d 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/container.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/info/container.go @@ -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 } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/container_test.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/container_test.go index 4b3c84a2423..bd730c19c01 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/container_test.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/info/container_test.go @@ -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) diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/test/datagen.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/test/datagen.go index d52a156a82c..5d43ae578a0 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/test/datagen.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/info/test/datagen.go @@ -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 -} diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/version.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/version.go index c5a91de01a5..86757344d4d 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/version.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/info/version.go @@ -15,4 +15,4 @@ package info // Version of cAdvisor. -const VERSION = "0.2.1" +const VERSION = "0.4.0"