diff --git a/utils/timed_store.go b/utils/timed_store.go index 8582a3e2..153d8e32 100644 --- a/utils/timed_store.go +++ b/utils/timed_store.go @@ -17,46 +17,52 @@ package utils import ( "sort" "time" - - info "github.com/google/cadvisor/info/v1" ) // A time-based buffer for ContainerStats. Holds information for a specific time period. type TimedStore struct { - buffer []*info.ContainerStats + buffer []timedStoreData age time.Duration } +type timedStoreData struct { + timestamp time.Time + data interface{} +} + // Returns a new thread-compatible TimedStore. func NewTimedStore(age time.Duration) *TimedStore { return &TimedStore{ - buffer: make([]*info.ContainerStats, 0), + buffer: make([]timedStoreData, 0), age: age, } } // Adds an element to the start of the buffer (removing one from the end if necessary). -func (self *TimedStore) Add(item *info.ContainerStats) { +func (self *TimedStore) Add(timestamp time.Time, item interface{}) { // Remove any elements before the eviction time. - evictTime := item.Timestamp.Add(-self.age) + evictTime := timestamp.Add(-self.age) index := sort.Search(len(self.buffer), func(index int) bool { - return self.buffer[index].Timestamp.After(evictTime) + return self.buffer[index].timestamp.After(evictTime) }) if index < len(self.buffer) { self.buffer = self.buffer[index:] } - copied := *item - self.buffer = append(self.buffer, &copied) + copied := item + self.buffer = append(self.buffer, timedStoreData{ + timestamp: timestamp, + data: copied, + }) } // Returns up to maxResult elements in the specified time period (inclusive). // Results are from first to last. maxResults of -1 means no limit. When first // and last are specified, maxResults is ignored. -func (self *TimedStore) InTimeRange(start, end time.Time, maxResults int) []*info.ContainerStats { +func (self *TimedStore) InTimeRange(start, end time.Time, maxResults int) []interface{} { // No stats, return empty. if len(self.buffer) == 0 { - return []*info.ContainerStats{} + return []interface{}{} } // Return all results in a time range if specified. @@ -77,11 +83,11 @@ func (self *TimedStore) InTimeRange(start, end time.Time, maxResults int) []*inf // before that element startIndex = sort.Search(len(self.buffer), func(index int) bool { // buffer[index] < start - return self.Get(index).Timestamp.Before(start) + return self.getData(index).timestamp.Before(start) }) - 1 // Check if start is after all the data we have. if startIndex < 0 { - return []*info.ContainerStats{} + return []interface{}{} } } @@ -93,11 +99,11 @@ func (self *TimedStore) InTimeRange(start, end time.Time, maxResults int) []*inf // End is the first index smaller than or equal to it (so, not larger). endIndex = sort.Search(len(self.buffer), func(index int) bool { // buffer[index] <= t -> !(buffer[index] > t) - return !self.Get(index).Timestamp.After(end) + return !self.getData(index).timestamp.After(end) }) // Check if end is before all the data we have. if endIndex == len(self.buffer) { - return []*info.ContainerStats{} + return []interface{}{} } } @@ -109,7 +115,7 @@ func (self *TimedStore) InTimeRange(start, end time.Time, maxResults int) []*inf } // Return in sorted timestamp order so from the "back" to "front". - result := make([]*info.ContainerStats, numResults) + result := make([]interface{}, numResults) for i := 0; i < numResults; i++ { result[i] = self.Get(startIndex - i) } @@ -117,7 +123,12 @@ func (self *TimedStore) InTimeRange(start, end time.Time, maxResults int) []*inf } // Gets the element at the specified index. Note that elements are output in LIFO order. -func (self *TimedStore) Get(index int) *info.ContainerStats { +func (self *TimedStore) Get(index int) interface{} { + return self.getData(index).data +} + +// Gets the data at the specified index. Note that elements are output in LIFO order. +func (self *TimedStore) getData(index int) timedStoreData { return self.buffer[len(self.buffer)-index-1] } diff --git a/utils/timed_store_test.go b/utils/timed_store_test.go index 2a7aa6fc..4ac114a7 100644 --- a/utils/timed_store_test.go +++ b/utils/timed_store_test.go @@ -15,12 +15,9 @@ package utils import ( - "strconv" - "strings" "testing" "time" - info "github.com/google/cadvisor/info/v1" "github.com/stretchr/testify/assert" ) @@ -29,96 +26,74 @@ func createTime(id int) time.Time { return zero.Add(time.Duration(id+1) * time.Second) } -func createStats(id int32) *info.ContainerStats { - return &info.ContainerStats{ - Timestamp: createTime(int(id)), - Cpu: info.CpuStats{ - LoadAverage: id, - }, - } -} - func expectSize(t *testing.T, sb *TimedStore, expectedSize int) { if sb.Size() != expectedSize { t.Errorf("Expected size %v, got %v", expectedSize, sb.Size()) } } -func expectAllElements(t *testing.T, sb *TimedStore, expected []int32) { +func expectAllElements(t *testing.T, sb *TimedStore, expected []int) { size := sb.Size() - els := make([]*info.ContainerStats, size) + els := make([]interface{}, size) for i := 0; i < size; i++ { els[i] = sb.Get(size - i - 1) } - expectElements(t, els, expected) + expectElements(t, []interface{}(els), expected) } -func getActualElements(actual []*info.ContainerStats) string { - actualElements := make([]string, len(actual)) - for i, element := range actual { - actualElements[i] = strconv.Itoa(int(element.Cpu.LoadAverage)) - } - return strings.Join(actualElements, ",") -} - -func expectElements(t *testing.T, actual []*info.ContainerStats, expected []int32) { +func expectElements(t *testing.T, actual []interface{}, expected []int) { if len(actual) != len(expected) { - t.Errorf("Expected elements %v, got %v", expected, getActualElements(actual)) + t.Errorf("Expected elements %v, got %v", expected, actual) return } for i, el := range actual { - if el.Cpu.LoadAverage != expected[i] { - t.Errorf("Expected elements %v, got %v", expected, getActualElements(actual)) + if el.(int) != expected[i] { + t.Errorf("Expected elements %v, got %v", expected, actual) return } } } -func expectElement(t *testing.T, stat *info.ContainerStats, expected int32) { - if stat.Cpu.LoadAverage != expected { - t.Errorf("Expected %d, but received %d", expected, stat.Cpu.LoadAverage) - } -} - func TestAdd(t *testing.T) { sb := NewTimedStore(5 * time.Second) // Add 1. - sb.Add(createStats(0)) + sb.Add(createTime(0), 0) expectSize(t, sb, 1) - expectAllElements(t, sb, []int32{0}) + expectAllElements(t, sb, []int{0}) // Fill the buffer. for i := 1; i <= 5; i++ { expectSize(t, sb, i) - sb.Add(createStats(int32(i))) + sb.Add(createTime(i), i) } expectSize(t, sb, 5) - expectAllElements(t, sb, []int32{1, 2, 3, 4, 5}) + expectAllElements(t, sb, []int{1, 2, 3, 4, 5}) // Add more than is available in the buffer - sb.Add(createStats(6)) + sb.Add(createTime(6), 6) expectSize(t, sb, 5) - expectAllElements(t, sb, []int32{2, 3, 4, 5, 6}) + expectAllElements(t, sb, []int{2, 3, 4, 5, 6}) // Replace all elements. for i := 7; i <= 10; i++ { - sb.Add(createStats(int32(i))) + sb.Add(createTime(i), i) } expectSize(t, sb, 5) - expectAllElements(t, sb, []int32{6, 7, 8, 9, 10}) + expectAllElements(t, sb, []int{6, 7, 8, 9, 10}) } func TestGet(t *testing.T) { sb := NewTimedStore(5 * time.Second) - sb.Add(createStats(1)) - sb.Add(createStats(2)) - sb.Add(createStats(3)) + sb.Add(createTime(1), 1) + sb.Add(createTime(2), 2) + sb.Add(createTime(3), 3) expectSize(t, sb, 3) - expectElement(t, sb.Get(0), 3) - expectElement(t, sb.Get(1), 2) - expectElement(t, sb.Get(2), 1) + assert := assert.New(t) + assert.Equal(sb.Get(0).(int), 3) + assert.Equal(sb.Get(1).(int), 2) + assert.Equal(sb.Get(2).(int), 1) } func TestInTimeRange(t *testing.T) { @@ -134,60 +109,60 @@ func TestInTimeRange(t *testing.T) { assert.Empty(sb.InTimeRange(empty, empty, 10)) // One element. - sb.Add(createStats(1)) + sb.Add(createTime(1), 1) expectSize(t, sb, 1) - expectElements(t, sb.InTimeRange(createTime(0), createTime(5), 10), []int32{1}) - expectElements(t, sb.InTimeRange(createTime(1), createTime(5), 10), []int32{1}) - expectElements(t, sb.InTimeRange(createTime(0), createTime(1), 10), []int32{1}) - expectElements(t, sb.InTimeRange(createTime(1), createTime(1), 10), []int32{1}) + expectElements(t, sb.InTimeRange(createTime(0), createTime(5), 10), []int{1}) + expectElements(t, sb.InTimeRange(createTime(1), createTime(5), 10), []int{1}) + expectElements(t, sb.InTimeRange(createTime(0), createTime(1), 10), []int{1}) + expectElements(t, sb.InTimeRange(createTime(1), createTime(1), 10), []int{1}) assert.Empty(sb.InTimeRange(createTime(2), createTime(5), 10)) // Two element. - sb.Add(createStats(2)) + sb.Add(createTime(2), 2) expectSize(t, sb, 2) - expectElements(t, sb.InTimeRange(createTime(0), createTime(5), 10), []int32{1, 2}) - expectElements(t, sb.InTimeRange(createTime(1), createTime(5), 10), []int32{1, 2}) - expectElements(t, sb.InTimeRange(createTime(0), createTime(2), 10), []int32{1, 2}) - expectElements(t, sb.InTimeRange(createTime(1), createTime(2), 10), []int32{1, 2}) - expectElements(t, sb.InTimeRange(createTime(1), createTime(1), 10), []int32{1}) - expectElements(t, sb.InTimeRange(createTime(2), createTime(2), 10), []int32{2}) + expectElements(t, sb.InTimeRange(createTime(0), createTime(5), 10), []int{1, 2}) + expectElements(t, sb.InTimeRange(createTime(1), createTime(5), 10), []int{1, 2}) + expectElements(t, sb.InTimeRange(createTime(0), createTime(2), 10), []int{1, 2}) + expectElements(t, sb.InTimeRange(createTime(1), createTime(2), 10), []int{1, 2}) + expectElements(t, sb.InTimeRange(createTime(1), createTime(1), 10), []int{1}) + expectElements(t, sb.InTimeRange(createTime(2), createTime(2), 10), []int{2}) assert.Empty(sb.InTimeRange(createTime(3), createTime(5), 10)) // Many elements. - sb.Add(createStats(3)) - sb.Add(createStats(4)) + sb.Add(createTime(3), 3) + sb.Add(createTime(4), 4) expectSize(t, sb, 4) - expectElements(t, sb.InTimeRange(createTime(0), createTime(5), 10), []int32{1, 2, 3, 4}) - expectElements(t, sb.InTimeRange(createTime(0), createTime(5), 10), []int32{1, 2, 3, 4}) - expectElements(t, sb.InTimeRange(createTime(1), createTime(5), 10), []int32{1, 2, 3, 4}) - expectElements(t, sb.InTimeRange(createTime(0), createTime(4), 10), []int32{1, 2, 3, 4}) - expectElements(t, sb.InTimeRange(createTime(1), createTime(4), 10), []int32{1, 2, 3, 4}) - expectElements(t, sb.InTimeRange(createTime(0), createTime(2), 10), []int32{1, 2}) - expectElements(t, sb.InTimeRange(createTime(1), createTime(2), 10), []int32{1, 2}) - expectElements(t, sb.InTimeRange(createTime(2), createTime(3), 10), []int32{2, 3}) - expectElements(t, sb.InTimeRange(createTime(3), createTime(4), 10), []int32{3, 4}) - expectElements(t, sb.InTimeRange(createTime(3), createTime(5), 10), []int32{3, 4}) + expectElements(t, sb.InTimeRange(createTime(0), createTime(5), 10), []int{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(createTime(0), createTime(5), 10), []int{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(createTime(1), createTime(5), 10), []int{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(createTime(0), createTime(4), 10), []int{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(createTime(1), createTime(4), 10), []int{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(createTime(0), createTime(2), 10), []int{1, 2}) + expectElements(t, sb.InTimeRange(createTime(1), createTime(2), 10), []int{1, 2}) + expectElements(t, sb.InTimeRange(createTime(2), createTime(3), 10), []int{2, 3}) + expectElements(t, sb.InTimeRange(createTime(3), createTime(4), 10), []int{3, 4}) + expectElements(t, sb.InTimeRange(createTime(3), createTime(5), 10), []int{3, 4}) assert.Empty(sb.InTimeRange(createTime(5), createTime(5), 10)) // Start and end time ignores maxResults. - expectElements(t, sb.InTimeRange(createTime(1), createTime(5), 1), []int32{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(createTime(1), createTime(5), 1), []int{1, 2, 3, 4}) // No start time. - expectElements(t, sb.InTimeRange(empty, createTime(5), 10), []int32{1, 2, 3, 4}) - expectElements(t, sb.InTimeRange(empty, createTime(4), 10), []int32{1, 2, 3, 4}) - expectElements(t, sb.InTimeRange(empty, createTime(3), 10), []int32{1, 2, 3}) - expectElements(t, sb.InTimeRange(empty, createTime(2), 10), []int32{1, 2}) - expectElements(t, sb.InTimeRange(empty, createTime(1), 10), []int32{1}) + expectElements(t, sb.InTimeRange(empty, createTime(5), 10), []int{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(empty, createTime(4), 10), []int{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(empty, createTime(3), 10), []int{1, 2, 3}) + expectElements(t, sb.InTimeRange(empty, createTime(2), 10), []int{1, 2}) + expectElements(t, sb.InTimeRange(empty, createTime(1), 10), []int{1}) // No end time. - expectElements(t, sb.InTimeRange(createTime(0), empty, 10), []int32{1, 2, 3, 4}) - expectElements(t, sb.InTimeRange(createTime(1), empty, 10), []int32{1, 2, 3, 4}) - expectElements(t, sb.InTimeRange(createTime(2), empty, 10), []int32{2, 3, 4}) - expectElements(t, sb.InTimeRange(createTime(3), empty, 10), []int32{3, 4}) - expectElements(t, sb.InTimeRange(createTime(4), empty, 10), []int32{4}) + expectElements(t, sb.InTimeRange(createTime(0), empty, 10), []int{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(createTime(1), empty, 10), []int{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(createTime(2), empty, 10), []int{2, 3, 4}) + expectElements(t, sb.InTimeRange(createTime(3), empty, 10), []int{3, 4}) + expectElements(t, sb.InTimeRange(createTime(4), empty, 10), []int{4}) // No start or end time. - expectElements(t, sb.InTimeRange(empty, empty, 10), []int32{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(empty, empty, 10), []int{1, 2, 3, 4}) // Start after data. assert.Empty(sb.InTimeRange(createTime(5), createTime(5), 10)) @@ -200,18 +175,18 @@ func TestInTimeRange(t *testing.T) { func TestInTimeRangeWithLimit(t *testing.T) { sb := NewTimedStore(5 * time.Second) - sb.Add(createStats(1)) - sb.Add(createStats(2)) - sb.Add(createStats(3)) - sb.Add(createStats(4)) + sb.Add(createTime(1), 1) + sb.Add(createTime(2), 2) + sb.Add(createTime(3), 3) + sb.Add(createTime(4), 4) expectSize(t, sb, 4) var empty time.Time // Limit cuts off from latest timestamp. - expectElements(t, sb.InTimeRange(empty, empty, 4), []int32{1, 2, 3, 4}) - expectElements(t, sb.InTimeRange(empty, empty, 3), []int32{2, 3, 4}) - expectElements(t, sb.InTimeRange(empty, empty, 2), []int32{3, 4}) - expectElements(t, sb.InTimeRange(empty, empty, 1), []int32{4}) + expectElements(t, sb.InTimeRange(empty, empty, 4), []int{1, 2, 3, 4}) + expectElements(t, sb.InTimeRange(empty, empty, 3), []int{2, 3, 4}) + expectElements(t, sb.InTimeRange(empty, empty, 2), []int{3, 4}) + expectElements(t, sb.InTimeRange(empty, empty, 1), []int{4}) assert.Empty(t, sb.InTimeRange(empty, empty, 0)) }