From b049a3cccfa2e8bbd10312b83f7bcbf0437b7f73 Mon Sep 17 00:00:00 2001 From: Victor Marmol Date: Thu, 23 Apr 2015 19:20:16 -0700 Subject: [PATCH] Update cAdvisor dependency. Picks up fixes for events and OOM handling. --- Godeps/Godeps.json | 64 +++---- .../github.com/google/cadvisor/api/handler.go | 13 +- .../google/cadvisor/api/versions.go | 30 ++-- .../google/cadvisor/container/factory.go | 6 +- .../google/cadvisor/events/handler.go | 4 +- .../google/cadvisor/events/handler_test.go | 11 ++ .../google/cadvisor/info/v1/container.go | 15 +- .../google/cadvisor/manager/container.go | 3 - .../google/cadvisor/manager/manager.go | 26 ++- .../google/cadvisor/manager/manager_test.go | 2 +- .../google/cadvisor/pages/containers.go | 2 +- .../google/cadvisor/pages/docker.go | 2 +- .../google/cadvisor/storage/memory/memory.go | 28 +-- .../cadvisor/storage/memory/memory_test.go | 4 +- .../google/cadvisor/summary/percentiles.go | 3 - .../cadvisor/utils/cpuload/netlink/reader.go | 4 +- .../cadvisor/utils/oomparser/oomparser.go | 27 +-- .../utils/oomparser/oomparser_test.go | 26 +-- .../stats_buffer.go => utils/timed_store.go} | 112 +++++------- .../timed_store_test.go} | 169 ++++++++---------- .../google/cadvisor/version/version.go | 2 +- 21 files changed, 280 insertions(+), 273 deletions(-) rename Godeps/_workspace/src/github.com/google/cadvisor/{storage/memory/stats_buffer.go => utils/timed_store.go} (51%) rename Godeps/_workspace/src/github.com/google/cadvisor/{storage/memory/stats_buffer_test.go => utils/timed_store_test.go} (62%) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index c2b206a2a80..1df8da5ab34 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -197,83 +197,83 @@ }, { "ImportPath": "github.com/google/cadvisor/api", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/container", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/events", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/fs", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/healthz", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/http", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/info/v1", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/info/v2", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/manager", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/metrics", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/pages", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/storage", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/summary", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/utils", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/validate", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/cadvisor/version", - "Comment": "0.11.0-26-g417cd5e", - "Rev": "417cd5ead23d33eff717a41d3be80ea73bf307cd" + "Comment": "0.12.0-21-g3166552", + "Rev": "3166552cc4481e48dc1002e000d7e4ae8d5b9850" }, { "ImportPath": "github.com/google/gofuzz", diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/api/handler.go b/Godeps/_workspace/src/github.com/google/cadvisor/api/handler.go index 9c78c986fec..71b368060ca 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/api/handler.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/api/handler.go @@ -67,7 +67,7 @@ const ( func handleRequest(supportedApiVersions map[string]ApiVersion, m manager.Manager, w http.ResponseWriter, r *http.Request) error { start := time.Now() defer func() { - glog.V(2).Infof("Request took %s", time.Since(start)) + glog.V(4).Infof("Request took %s", time.Since(start)) }() request := r.URL.Path @@ -151,11 +151,9 @@ func streamResults(eventChannel *events.EventChannel, w http.ResponseWriter, r * for { select { case <-cn.CloseNotify(): - glog.V(3).Infof("Received CloseNotify event. About to return from api/handler:streamResults") m.CloseEventChannel(eventChannel.GetWatchId()) return nil case ev := <-eventChannel.GetChannel(): - glog.V(3).Infof("Received event from watch channel in api: %v", ev) err := enc.Encode(ev) if err != nil { glog.Errorf("error encoding message %+v for result stream: %v", ev, err) @@ -207,6 +205,12 @@ func getEventRequest(r *http.Request) (*events.Request, bool, error) { query.EventType[info.EventOom] = newBool } } + if val, ok := urlMap["oom_kill_events"]; ok { + newBool, err := strconv.ParseBool(val[0]) + if err == nil { + query.EventType[info.EventOomKill] = newBool + } + } if val, ok := urlMap["creation_events"]; ok { newBool, err := strconv.ParseBool(val[0]) if err == nil { @@ -238,9 +242,6 @@ func getEventRequest(r *http.Request) (*events.Request, bool, error) { } } - glog.V(2).Infof( - "%v was returned in api/handler.go:getEventRequest from the url rawQuery %v", - query, r.URL.RawQuery) return query, stream, nil } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/api/versions.go b/Godeps/_workspace/src/github.com/google/cadvisor/api/versions.go index 9f56ff6ccd4..3dbf43b94d0 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/api/versions.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/api/versions.go @@ -17,6 +17,7 @@ package api import ( "fmt" "net/http" + "path" "strconv" "github.com/golang/glog" @@ -79,7 +80,7 @@ func (self *version1_0) SupportedRequestTypes() []string { func (self *version1_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error { switch requestType { case machineApi: - glog.V(2).Infof("Api - Machine") + glog.V(4).Infof("Api - Machine") // Get the MachineInfo machineInfo, err := m.GetMachineInfo() @@ -93,7 +94,7 @@ func (self *version1_0) HandleRequest(requestType string, request []string, m ma } case containersApi: containerName := getContainerName(request) - glog.V(2).Infof("Api - Container(%s)", containerName) + glog.V(4).Infof("Api - Container(%s)", containerName) // Get the query request. query, err := getContainerInfoRequest(r.Body) @@ -143,7 +144,7 @@ func (self *version1_1) HandleRequest(requestType string, request []string, m ma switch requestType { case subcontainersApi: containerName := getContainerName(request) - glog.V(2).Infof("Api - Subcontainers(%s)", containerName) + glog.V(4).Infof("Api - Subcontainers(%s)", containerName) // Get the query request. query, err := getContainerInfoRequest(r.Body) @@ -192,7 +193,7 @@ func (self *version1_2) SupportedRequestTypes() []string { func (self *version1_2) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error { switch requestType { case dockerApi: - glog.V(2).Infof("Api - Docker(%v)", request) + glog.V(4).Infof("Api - Docker(%v)", request) // Get the query request. query, err := getContainerInfoRequest(r.Body) @@ -261,18 +262,19 @@ func (self *version1_3) SupportedRequestTypes() []string { func (self *version1_3) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error { switch requestType { case eventsApi: - return handleEventRequest(m, w, r) + return handleEventRequest(request, m, w, r) default: return self.baseVersion.HandleRequest(requestType, request, m, w, r) } } -func handleEventRequest(m manager.Manager, w http.ResponseWriter, r *http.Request) error { +func handleEventRequest(request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error { query, stream, err := getEventRequest(r) if err != nil { return err } - glog.V(2).Infof("Api - Events(%v)", query) + query.ContainerName = path.Join("/", getContainerName(request)) + glog.V(4).Infof("Api - Events(%v)", query) if !stream { pastEvents, err := m.GetPastEvents(query) if err != nil { @@ -312,14 +314,14 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma } switch requestType { case versionApi: - glog.V(2).Infof("Api - Version") + glog.V(4).Infof("Api - Version") versionInfo, err := m.GetVersionInfo() if err != nil { return err } return writeResult(versionInfo.CadvisorVersion, w) case attributesApi: - glog.V(2).Info("Api - Attributes") + glog.V(4).Info("Api - Attributes") machineInfo, err := m.GetMachineInfo() if err != nil { @@ -332,7 +334,7 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma info := v2.GetAttributes(machineInfo, versionInfo) return writeResult(info, w) case machineApi: - glog.V(2).Info("Api - Machine") + glog.V(4).Info("Api - Machine") // TODO(rjnagal): Move machineInfo from v1. machineInfo, err := m.GetMachineInfo() @@ -342,7 +344,7 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma return writeResult(machineInfo, w) case summaryApi: containerName := getContainerName(request) - glog.V(2).Infof("Api - Summary for container %q, options %+v", containerName, opt) + glog.V(4).Infof("Api - Summary for container %q, options %+v", containerName, opt) stats, err := m.GetDerivedStats(containerName, opt) if err != nil { @@ -351,7 +353,7 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma return writeResult(stats, w) case statsApi: name := getContainerName(request) - glog.V(2).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt) + glog.V(4).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt) conts, err := m.GetRequestedContainersInfo(name, opt) if err != nil { return err @@ -363,7 +365,7 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma return writeResult(contStats, w) case specApi: containerName := getContainerName(request) - glog.V(2).Infof("Api - Spec for container %q, options %+v", containerName, opt) + glog.V(4).Infof("Api - Spec for container %q, options %+v", containerName, opt) specs, err := m.GetContainerSpec(containerName, opt) if err != nil { return err @@ -388,7 +390,7 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma } return writeResult(fi, w) case eventsApi: - return handleEventRequest(m, w, r) + return handleEventRequest(request, m, w, r) default: return fmt.Errorf("unknown request type %q", requestType) } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/container/factory.go b/Godeps/_workspace/src/github.com/google/cadvisor/container/factory.go index 9007ea1cfef..3b9cea1a835 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/container/factory.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/container/factory.go @@ -65,13 +65,13 @@ func NewContainerHandler(name string) (ContainerHandler, error) { for _, factory := range factories { canHandle, err := factory.CanHandle(name) if err != nil { - glog.V(1).Infof("Error trying to work out if we can hande %s: %v", name, err) + glog.V(4).Infof("Error trying to work out if we can hande %s: %v", name, err) } if canHandle { - glog.V(1).Infof("Using factory %q for container %q", factory, name) + glog.V(3).Infof("Using factory %q for container %q", factory, name) return factory.NewContainerHandler(name) } else { - glog.V(1).Infof("Factory %q was unable to handle container %q", factory, name) + glog.V(4).Infof("Factory %q was unable to handle container %q", factory, name) } } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/events/handler.go b/Godeps/_workspace/src/github.com/google/cadvisor/events/handler.go index 24fa7c1db9e..9d0265038df 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/events/handler.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/events/handler.go @@ -174,7 +174,7 @@ func getMaxEventsReturned(request *Request, eSlice EventSlice) EventSlice { // equivalent func checkIfIsSubcontainer(request *Request, event *info.Event) bool { if request.IncludeSubcontainers == true { - return strings.HasPrefix(event.ContainerName+"/", request.ContainerName+"/") + return request.ContainerName == "/" || strings.HasPrefix(event.ContainerName+"/", request.ContainerName+"/") } return event.ContainerName == request.ContainerName } @@ -270,7 +270,7 @@ func (self *events) AddEvent(e *info.Event) error { for _, watchObject := range watchesToSend { watchObject.eventChannel.GetChannel() <- e } - glog.V(1).Infof("Added event %v", e) + glog.V(4).Infof("Added event %v", e) return nil } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/events/handler_test.go b/Godeps/_workspace/src/github.com/google/cadvisor/events/handler_test.go index e1336e913f3..f9f074b4fac 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/events/handler_test.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/events/handler_test.go @@ -67,6 +67,8 @@ func ensureProperEventReturned(t *testing.T, expectedEvent *info.Event, eventObj func TestCheckIfIsSubcontainer(t *testing.T) { myRequest := NewRequest() myRequest.ContainerName = "/root" + rootRequest := NewRequest() + rootRequest.ContainerName = "/" sameContainerEvent := &info.Event{ ContainerName: "/root", @@ -78,6 +80,10 @@ func TestCheckIfIsSubcontainer(t *testing.T) { ContainerName: "/root-completely-different-container", } + if checkIfIsSubcontainer(rootRequest, sameContainerEvent) { + t.Errorf("should not have found %v to be a subcontainer of %v", + sameContainerEvent, rootRequest) + } if !checkIfIsSubcontainer(myRequest, sameContainerEvent) { t.Errorf("should have found %v and %v had the same container name", myRequest, sameContainerEvent) @@ -87,8 +93,13 @@ func TestCheckIfIsSubcontainer(t *testing.T) { myRequest, subContainerEvent) } + rootRequest.IncludeSubcontainers = true myRequest.IncludeSubcontainers = true + if !checkIfIsSubcontainer(rootRequest, sameContainerEvent) { + t.Errorf("should have found %v to be a subcontainer of %v", + sameContainerEvent.ContainerName, rootRequest.ContainerName) + } if !checkIfIsSubcontainer(myRequest, sameContainerEvent) { t.Errorf("should have found %v and %v had the same container", myRequest.ContainerName, sameContainerEvent.ContainerName) diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go index c4a8d981a49..b8591815fb5 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go @@ -494,12 +494,13 @@ type Event struct { // EventType is an enumerated type which lists the categories under which // events may fall. The Event field EventType is populated by this enum. -type EventType int +type EventType string const ( - EventOom EventType = iota - EventContainerCreation - EventContainerDeletion + EventOom EventType = "oom" + EventOomKill = "oomKill" + EventContainerCreation = "containerCreation" + EventContainerDeletion = "containerDeletion" ) // Extra information about an event. Only one type will be set. @@ -507,8 +508,8 @@ type EventData struct { // Information about a container creation event. Created *CreatedEventData `json:"created,omitempty"` - // Information about an OOM event. - Oom *OomEventData `json:"oom,omitempty"` + // Information about an OOM kill event. + OomKill *OomKillEventData `json:"oom,omitempty"` } // Information related to a container creation event. @@ -518,7 +519,7 @@ type CreatedEventData struct { } // Information related to an OOM kill instance -type OomEventData struct { +type OomKillEventData struct { // process id of the killed process Pid int `json:"pid"` diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/manager/container.go b/Godeps/_workspace/src/github.com/google/cadvisor/manager/container.go index 657e32ae9be..1d77adcfa4d 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/manager/container.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/manager/container.go @@ -162,11 +162,9 @@ func (self *containerData) nextHousekeeping(lastHousekeeping time.Time) time.Tim if self.housekeepingInterval > *maxHousekeepingInterval { self.housekeepingInterval = *maxHousekeepingInterval } - glog.V(3).Infof("Raising housekeeping interval for %q to %v", self.info.Name, self.housekeepingInterval) } else if self.housekeepingInterval != *HousekeepingInterval { // Lower interval back to the baseline. self.housekeepingInterval = *HousekeepingInterval - glog.V(3).Infof("Lowering housekeeping interval for %q to %v", self.info.Name, self.housekeepingInterval) } } } @@ -270,7 +268,6 @@ func (c *containerData) updateLoad(newLoad uint64) { } else { c.loadAvg = c.loadAvg*loadDecay + float64(newLoad)*(1.0-loadDecay) } - glog.V(3).Infof("New load for %q: %v. latest sample: %d", c.info.Name, c.loadAvg, newLoad) } func (c *containerData) updateStats() error { diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go b/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go index b3fe8be16e9..378686534f0 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager.go @@ -41,6 +41,7 @@ import ( var globalHousekeepingInterval = flag.Duration("global_housekeeping_interval", 1*time.Minute, "Interval between global housekeepings") var logCadvisorUsage = flag.Bool("log_cadvisor_usage", false, "Whether to log the usage of the cAdvisor container") +var enableLoadReader = flag.Bool("enable_load_reader", false, "Whether to enable cpu load reader") // The Manager interface defines operations for starting a manager and getting // container and machine information. @@ -175,8 +176,8 @@ type manager struct { // Start the container manager. func (self *manager) Start() error { - // TODO(rjnagal): Skip creating cpu load reader while we improve resource usage and accuracy. - if false { + + if *enableLoadReader { // Create cpu load reader. cpuLoadReader, err := cpuload.New() if err != nil { @@ -273,7 +274,7 @@ func (self *manager) globalHousekeeping(quit chan error) { // Log if housekeeping took too long. duration := time.Since(start) if duration >= longHousekeeping { - glog.V(1).Infof("Global Housekeeping(%d) took %s", t.Unix(), duration) + glog.V(3).Infof("Global Housekeeping(%d) took %s", t.Unix(), duration) } case <-quit: // Quit if asked to do so. @@ -878,21 +879,32 @@ func (self *manager) watchForNewOoms() error { go func() { for oomInstance := range outStream { + // Surface OOM and OOM kill events. newEvent := &info.Event{ ContainerName: oomInstance.ContainerName, Timestamp: oomInstance.TimeOfDeath, EventType: info.EventOom, + } + err := self.eventHandler.AddEvent(newEvent) + if err != nil { + glog.Errorf("failed to add OOM event for %q: %v", oomInstance.ContainerName, err) + } + glog.V(3).Infof("Created an OOM event in container %q at %v", oomInstance.ContainerName, oomInstance.TimeOfDeath) + + newEvent = &info.Event{ + ContainerName: oomInstance.VictimContainerName, + Timestamp: oomInstance.TimeOfDeath, + EventType: info.EventOomKill, EventData: info.EventData{ - Oom: &info.OomEventData{ + OomKill: &info.OomKillEventData{ Pid: oomInstance.Pid, ProcessName: oomInstance.ProcessName, }, }, } - glog.V(2).Infof("Created an oom event: %v", newEvent) - err := self.eventHandler.AddEvent(newEvent) + err = self.eventHandler.AddEvent(newEvent) if err != nil { - glog.Errorf("failed to add event %v, got error: %v", newEvent, err) + glog.Errorf("failed to add OOM kill event for %q: %v", oomInstance.ContainerName, err) } } }() diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager_test.go b/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager_test.go index d24ae611b20..382f71cac80 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager_test.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/manager/manager_test.go @@ -81,7 +81,7 @@ func expectManagerWithContainers(containers []string, query *info.ContainerInfoR infosMap[container] = itest.GenerateRandomContainerInfo(container, 4, query, 1*time.Second) } - memoryStorage := memory.New(query.NumStats, nil) + memoryStorage := memory.New(time.Duration(query.NumStats)*time.Second, nil) sysfs := &fakesysfs.FakeSysFs{} m := createManagerAndAddContainers( memoryStorage, diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/pages/containers.go b/Godeps/_workspace/src/github.com/google/cadvisor/pages/containers.go index 6f7efaaafb1..d504d56f84b 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/pages/containers.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/pages/containers.go @@ -242,6 +242,6 @@ func serveContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL) e glog.Errorf("Failed to apply template: %s", err) } - glog.V(1).Infof("Request took %s", time.Since(start)) + glog.V(5).Infof("Request took %s", time.Since(start)) return nil } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/pages/docker.go b/Godeps/_workspace/src/github.com/google/cadvisor/pages/docker.go index 3fc23ecd6d0..ee0f900eed8 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/pages/docker.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/pages/docker.go @@ -111,6 +111,6 @@ func serveDockerPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error glog.Errorf("Failed to apply template: %s", err) } - glog.V(1).Infof("Request took %s", time.Since(start)) + glog.V(5).Infof("Request took %s", time.Since(start)) return nil } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/memory.go b/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/memory.go index 2d5b14a309a..5a53c17d0ae 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/memory.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/memory.go @@ -22,14 +22,15 @@ import ( "github.com/golang/glog" info "github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/storage" + "github.com/google/cadvisor/utils" ) // TODO(vmarmol): See about refactoring this class, we have an unecessary redirection of containerStorage and InMemoryStorage. // containerStorage is used to store per-container information type containerStorage struct { ref info.ContainerReference - recentStats *StatsBuffer - maxNumStats int + recentStats *utils.TimedStore + maxAge time.Duration lock sync.RWMutex } @@ -38,28 +39,33 @@ func (self *containerStorage) AddStats(stats *info.ContainerStats) error { defer self.lock.Unlock() // Add the stat to storage. - self.recentStats.Add(stats) + self.recentStats.Add(stats.Timestamp, stats) return nil } func (self *containerStorage) RecentStats(start, end time.Time, maxStats int) ([]*info.ContainerStats, error) { self.lock.RLock() defer self.lock.RUnlock() - return self.recentStats.InTimeRange(start, end, maxStats), nil + result := self.recentStats.InTimeRange(start, end, maxStats) + converted := make([]*info.ContainerStats, len(result)) + for i, el := range result { + converted[i] = el.(*info.ContainerStats) + } + return converted, nil } -func newContainerStore(ref info.ContainerReference, maxNumStats int) *containerStorage { +func newContainerStore(ref info.ContainerReference, maxAge time.Duration) *containerStorage { return &containerStorage{ ref: ref, - recentStats: NewStatsBuffer(maxNumStats), - maxNumStats: maxNumStats, + recentStats: utils.NewTimedStore(maxAge), + maxAge: maxAge, } } type InMemoryStorage struct { lock sync.RWMutex containerStorageMap map[string]*containerStorage - maxNumStats int + maxAge time.Duration backend storage.StorageDriver } @@ -71,7 +77,7 @@ func (self *InMemoryStorage) AddStats(ref info.ContainerReference, stats *info.C self.lock.Lock() defer self.lock.Unlock() if cstore, ok = self.containerStorageMap[ref.Name]; !ok { - cstore = newContainerStore(ref, self.maxNumStats) + cstore = newContainerStore(ref, self.maxAge) self.containerStorageMap[ref.Name] = cstore } }() @@ -113,12 +119,12 @@ func (self *InMemoryStorage) Close() error { } func New( - maxNumStats int, + maxAge time.Duration, backend storage.StorageDriver, ) *InMemoryStorage { ret := &InMemoryStorage{ containerStorageMap: make(map[string]*containerStorage, 32), - maxNumStats: maxNumStats, + maxAge: maxAge, backend: backend, } return ret diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/memory_test.go b/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/memory_test.go index 2b2059f85f8..d95f4c11c20 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/memory_test.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/memory_test.go @@ -47,7 +47,7 @@ func getRecentStats(t *testing.T, memoryStorage *InMemoryStorage, numStats int) } func TestAddStats(t *testing.T) { - memoryStorage := New(60, nil) + memoryStorage := New(60*time.Second, nil) assert := assert.New(t) assert.Nil(memoryStorage.AddStats(containerRef, makeStat(0))) @@ -70,7 +70,7 @@ func TestRecentStatsNoRecentStats(t *testing.T) { // Make an instance of InMemoryStorage with n stats. func makeWithStats(n int) *InMemoryStorage { - memoryStorage := New(60, nil) + memoryStorage := New(60*time.Second, nil) for i := 0; i < n; i++ { memoryStorage.AddStats(containerRef, makeStat(i)) diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/summary/percentiles.go b/Godeps/_workspace/src/github.com/google/cadvisor/summary/percentiles.go index 1c7faa0bd4c..1893a65c0c0 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/summary/percentiles.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/summary/percentiles.go @@ -21,7 +21,6 @@ import ( "math" "sort" - "github.com/golang/glog" info "github.com/google/cadvisor/info/v2" ) @@ -172,10 +171,8 @@ func GetMinutePercentiles(stats []*secondSample) info.Usage { if !lastSample.Timestamp.IsZero() { cpuRate, err := getCpuRate(*stat, lastSample) if err != nil { - glog.V(3).Infof("Skipping sample, %v", err) continue } - glog.V(3).Infof("Adding cpu rate sample : %d", cpuRate) cpu.AddSample(cpuRate) memory.AddSample(stat.Memory) } else { diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/utils/cpuload/netlink/reader.go b/Godeps/_workspace/src/github.com/google/cadvisor/utils/cpuload/netlink/reader.go index 89f1d8455a0..68337652a91 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/utils/cpuload/netlink/reader.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/utils/cpuload/netlink/reader.go @@ -37,7 +37,7 @@ func New() (*NetlinkReader, error) { if err != nil { return nil, fmt.Errorf("failed to get netlink family id for task stats: %s", err) } - glog.V(2).Infof("Family id for taskstats: %d", id) + glog.V(4).Infof("Family id for taskstats: %d", id) return &NetlinkReader{ familyId: id, conn: conn, @@ -73,6 +73,6 @@ func (self *NetlinkReader) GetCpuLoad(name string, path string) (info.LoadStats, if err != nil { return info.LoadStats{}, err } - glog.V(3).Infof("Task stats for %q: %+v", path, stats) + glog.V(4).Infof("Task stats for %q: %+v", path, stats) return stats, nil } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/utils/oomparser/oomparser.go b/Godeps/_workspace/src/github.com/google/cadvisor/utils/oomparser/oomparser.go index 3a4b32f6fd3..deafc273725 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/utils/oomparser/oomparser.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/utils/oomparser/oomparser.go @@ -16,7 +16,7 @@ package oomparser import ( "bufio" - "errors" + "fmt" "io" "os" "os/exec" @@ -30,7 +30,7 @@ import ( ) var containerRegexp *regexp.Regexp = regexp.MustCompile( - `Task in (.*) killed as a result of limit of `) + `Task in (.*) killed as a result of limit of (.*)`) var lastLineRegexp *regexp.Regexp = regexp.MustCompile( `(^[A-Z]{1}[a-z]{2} .*[0-9]{1,2} [0-9]{1,2}:[0-9]{2}:[0-9]{2}) .* Killed process ([0-9]+) \(([0-9A-Za-z_]+)\)`) var firstLineRegexp *regexp.Regexp = regexp.MustCompile( @@ -52,6 +52,9 @@ type OomInstance struct { TimeOfDeath time.Time // the absolute name of the container that OOMed ContainerName string + // the absolute name of the container that was killed + // due to the OOM. + VictimContainerName string } // gets the container name from a line and adds it to the oomInstance. @@ -61,6 +64,7 @@ func getContainerName(line string, currentOomInstance *OomInstance) error { return nil } currentOomInstance.ContainerName = path.Join("/", parsedLine[1]) + currentOomInstance.VictimContainerName = path.Join("/", parsedLine[2]) return nil } @@ -183,17 +187,20 @@ func trySystemd() (*OomParser, error) { } +// List of possible kernel log files. These are prioritized in order so that +// we will use the first one that is available. +var kernelLogFiles = []string{"/var/log/kern.log", "/var/log/messages", "/var/log/syslog"} + // looks for system files that contain kernel messages and if one is found, sets // the systemFile attribute of the OomParser object func getSystemFile() (string, error) { - const varLogMessages = "/var/log/messages" - const varLogSyslog = "/var/log/syslog" - if utils.FileExists(varLogMessages) { - return varLogMessages, nil - } else if utils.FileExists(varLogSyslog) { - return varLogSyslog, nil + for _, logFile := range kernelLogFiles { + if utils.FileExists(logFile) { + glog.Infof("OOM parser using kernel log file: %q", logFile) + return logFile, nil + } } - return "", errors.New("neither " + varLogSyslog + " nor " + varLogMessages + " exists from which to read kernel errors") + return "", fmt.Errorf("unable to find any kernel log file available from our set: %v", kernelLogFiles) } // initializes an OomParser object and calls getSystemFile to set the systemFile @@ -201,12 +208,10 @@ func getSystemFile() (string, error) { func New() (*OomParser, error) { systemFile, err := getSystemFile() if err != nil { - glog.V(1).Infof("received error %v when calling getSystemFile", err) return trySystemd() } file, err := os.Open(systemFile) if err != nil { - glog.V(1).Infof("received error %v when opening file", err) return trySystemd() } return &OomParser{ diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/utils/oomparser/oomparser_test.go b/Godeps/_workspace/src/github.com/google/cadvisor/utils/oomparser/oomparser_test.go index 4cb6a6d82e1..7da55e61fe8 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/utils/oomparser/oomparser_test.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/utils/oomparser/oomparser_test.go @@ -17,13 +17,14 @@ package oomparser import ( "bufio" "os" + "reflect" "testing" "time" ) const startLine = "Jan 21 22:01:49 localhost kernel: [62278.816267] ruby invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0" const endLine = "Jan 21 22:01:49 localhost kernel: [62279.421192] Killed process 19667 (evilprogram2) total-vm:1460016kB, anon-rss:1414008kB, file-rss:4kB" -const containerLine = "Jan 26 14:10:07 kateknister0.mtv.corp.google.com kernel: [1814368.465205] Task in /mem2 killed as a result of limit of /mem2" +const containerLine = "Jan 26 14:10:07 kateknister0.mtv.corp.google.com kernel: [1814368.465205] Task in /mem2 killed as a result of limit of /mem3" const containerLogFile = "containerOomExampleLog.txt" const systemLogFile = "systemOomExampleLog.txt" @@ -35,10 +36,11 @@ func createExpectedContainerOomInstance(t *testing.T) *OomInstance { return nil } return &OomInstance{ - Pid: 13536, - ProcessName: "memorymonster", - TimeOfDeath: deathTime, - ContainerName: "/mem2", + Pid: 13536, + ProcessName: "memorymonster", + TimeOfDeath: deathTime, + ContainerName: "/mem2", + VictimContainerName: "/mem3", } } @@ -50,10 +52,11 @@ func createExpectedSystemOomInstance(t *testing.T) *OomInstance { return nil } return &OomInstance{ - Pid: 1532, - ProcessName: "badsysprogram", - TimeOfDeath: deathTime, - ContainerName: "/", + Pid: 1532, + ProcessName: "badsysprogram", + TimeOfDeath: deathTime, + ContainerName: "/", + VictimContainerName: "/", } } @@ -73,6 +76,9 @@ func TestGetContainerName(t *testing.T) { if currentOomInstance.ContainerName != "/mem2" { t.Errorf("getContainerName should have set containerName to /mem2, not %s", currentOomInstance.ContainerName) } + if currentOomInstance.VictimContainerName != "/mem3" { + t.Errorf("getContainerName should have set victimContainerName to /mem3, not %s", currentOomInstance.VictimContainerName) + } } func TestGetProcessNamePid(t *testing.T) { @@ -139,7 +145,7 @@ func helpTestStreamOoms(oomCheckInstance *OomInstance, sysFile string, t *testin select { case oomInstance := <-outStream: - if *oomCheckInstance != *oomInstance { + if reflect.DeepEqual(*oomCheckInstance, *oomInstance) { t.Errorf("wrong instance returned. Expected %v and got %v", oomCheckInstance, oomInstance) } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/stats_buffer.go b/Godeps/_workspace/src/github.com/google/cadvisor/utils/timed_store.go similarity index 51% rename from Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/stats_buffer.go rename to Godeps/_workspace/src/github.com/google/cadvisor/utils/timed_store.go index b71bfe2acf8..153d8e3280e 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/stats_buffer.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/utils/timed_store.go @@ -12,48 +12,57 @@ // See the License for the specific language governing permissions and // limitations under the License. -package memory +package utils import ( "sort" "time" - - info "github.com/google/cadvisor/info/v1" ) -// A circular buffer for ContainerStats. -type StatsBuffer struct { - buffer []*info.ContainerStats - size int - index int +// A time-based buffer for ContainerStats. Holds information for a specific time period. +type TimedStore struct { + buffer []timedStoreData + age time.Duration } -// Returns a new thread-compatible StatsBuffer. -func NewStatsBuffer(size int) *StatsBuffer { - return &StatsBuffer{ - buffer: make([]*info.ContainerStats, size), - size: 0, - index: size - 1, +type timedStoreData struct { + timestamp time.Time + data interface{} +} + +// Returns a new thread-compatible TimedStore. +func NewTimedStore(age time.Duration) *TimedStore { + return &TimedStore{ + buffer: make([]timedStoreData, 0), + age: age, } } // Adds an element to the start of the buffer (removing one from the end if necessary). -func (self *StatsBuffer) Add(item *info.ContainerStats) { - if self.size < len(self.buffer) { - self.size++ +func (self *TimedStore) Add(timestamp time.Time, item interface{}) { + // Remove any elements before the eviction time. + evictTime := timestamp.Add(-self.age) + index := sort.Search(len(self.buffer), func(index int) bool { + return self.buffer[index].timestamp.After(evictTime) + }) + if index < len(self.buffer) { + self.buffer = self.buffer[index:] } - self.index = (self.index + 1) % len(self.buffer) - copied := *item - self.buffer[self.index] = &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 *StatsBuffer) 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 self.size == 0 { - return []*info.ContainerStats{} + if len(self.buffer) == 0 { + return []interface{}{} } // Return all results in a time range if specified. @@ -67,18 +76,18 @@ func (self *StatsBuffer) InTimeRange(start, end time.Time, maxResults int) []*in var startIndex int if start.IsZero() { // None specified, start at the beginning. - startIndex = self.size - 1 + startIndex = len(self.buffer) - 1 } else { // Start is the index before the elements smaller than it. We do this by // finding the first element smaller than start and taking the index // before that element - startIndex = sort.Search(self.size, func(index int) bool { + 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{}{} } } @@ -88,13 +97,13 @@ func (self *StatsBuffer) InTimeRange(start, end time.Time, maxResults int) []*in endIndex = 0 } else { // End is the first index smaller than or equal to it (so, not larger). - endIndex = sort.Search(self.size, func(index int) bool { + 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 == self.size { - return []*info.ContainerStats{} + if endIndex == len(self.buffer) { + return []interface{}{} } } @@ -106,46 +115,23 @@ func (self *StatsBuffer) InTimeRange(start, end time.Time, maxResults int) []*in } // 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) } return result } -// TODO(vmarmol): Remove this function as it will no longer be neededt. -// Returns the first N elements in the buffer. If N > size of buffer, size of buffer elements are returned. -// Returns the elements in ascending timestamp order. -func (self *StatsBuffer) FirstN(n int) []*info.ContainerStats { - // Cap n at the number of elements we have. - if n > self.size { - n = self.size - } - - // index points to the latest element, get n before that one (keeping in mind we may have gone through 0). - start := self.index - (n - 1) - if start < 0 { - start += len(self.buffer) - } - - // Copy the elements. - res := make([]*info.ContainerStats, n) - for i := 0; i < n; i++ { - index := (start + i) % len(self.buffer) - res[i] = self.buffer[index] - } - return res +// Gets the element at the specified index. Note that elements are output in LIFO order. +func (self *TimedStore) Get(index int) interface{} { + return self.getData(index).data } -// Gets the element at the specified index. Note that elements are stored in LIFO order. -func (self *StatsBuffer) Get(index int) *info.ContainerStats { - calculatedIndex := self.index - index - if calculatedIndex < 0 { - calculatedIndex += len(self.buffer) - } - return self.buffer[calculatedIndex] +// 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] } -func (self *StatsBuffer) Size() int { - return self.size +func (self *TimedStore) Size() int { + return len(self.buffer) } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/stats_buffer_test.go b/Godeps/_workspace/src/github.com/google/cadvisor/utils/timed_store_test.go similarity index 62% rename from Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/stats_buffer_test.go rename to Godeps/_workspace/src/github.com/google/cadvisor/utils/timed_store_test.go index f142e931624..4ac114a7fec 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/storage/memory/stats_buffer_test.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/utils/timed_store_test.go @@ -12,15 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -package memory +package utils import ( - "strconv" - "strings" "testing" "time" - info "github.com/google/cadvisor/info/v1" "github.com/stretchr/testify/assert" ) @@ -29,92 +26,78 @@ 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 *StatsBuffer, expectedSize int) { +func expectSize(t *testing.T, sb *TimedStore, expectedSize int) { if sb.Size() != expectedSize { t.Errorf("Expected size %v, got %v", expectedSize, sb.Size()) } } -func expectFirstN(t *testing.T, sb *StatsBuffer, expected []int32) { - expectElements(t, sb.FirstN(sb.Size()), expected) +func expectAllElements(t *testing.T, sb *TimedStore, expected []int) { + size := sb.Size() + els := make([]interface{}, size) + for i := 0; i < size; i++ { + els[i] = sb.Get(size - i - 1) + } + expectElements(t, []interface{}(els), expected) } -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, actual) return } for i, el := range actual { - if el.Cpu.LoadAverage != expected[i] { - actualElements := make([]string, len(actual)) - for i, element := range actual { - actualElements[i] = strconv.Itoa(int(element.Cpu.LoadAverage)) - } - t.Errorf("Expected elements %v, got %v", expected, strings.Join(actualElements, ",")) + 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 TestAddAndFirstN(t *testing.T) { - sb := NewStatsBuffer(5) +func TestAdd(t *testing.T) { + sb := NewTimedStore(5 * time.Second) // Add 1. - sb.Add(createStats(1)) + sb.Add(createTime(0), 0) expectSize(t, sb, 1) - expectFirstN(t, sb, []int32{1}) + 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) - expectFirstN(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) - expectFirstN(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) - expectFirstN(t, sb, []int32{6, 7, 8, 9, 10}) + expectAllElements(t, sb, []int{6, 7, 8, 9, 10}) } func TestGet(t *testing.T) { - sb := NewStatsBuffer(5) - sb.Add(createStats(1)) - sb.Add(createStats(2)) - sb.Add(createStats(3)) + sb := NewTimedStore(5 * time.Second) + sb.Add(createTime(1), 1) + sb.Add(createTime(2), 2) + sb.Add(createTime(3), 3) expectSize(t, sb, 3) - expectFirstN(t, sb, []int32{1, 2, 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) { - sb := NewStatsBuffer(5) + sb := NewTimedStore(5 * time.Second) assert := assert.New(t) var empty time.Time @@ -126,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)) @@ -191,19 +174,19 @@ func TestInTimeRange(t *testing.T) { } func TestInTimeRangeWithLimit(t *testing.T) { - sb := NewStatsBuffer(5) - sb.Add(createStats(1)) - sb.Add(createStats(2)) - sb.Add(createStats(3)) - sb.Add(createStats(4)) + sb := NewTimedStore(5 * time.Second) + 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)) } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/version/version.go b/Godeps/_workspace/src/github.com/google/cadvisor/version/version.go index bed71eb132c..6967e24450e 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/version/version.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/version/version.go @@ -15,4 +15,4 @@ package version // Version of cAdvisor. -const VERSION = "0.11.0" +const VERSION = "0.12.0"