Skip to content

Commit a210aca

Browse files
committed
Merge pull request #535 from kateknister/apiBranch
Added a basic api interface for events
2 parents e7a3518 + 6e14267 commit a210aca

File tree

5 files changed

+235
-9
lines changed

5 files changed

+235
-9
lines changed

api/handler.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ import (
2323
"path"
2424
"regexp"
2525
"sort"
26+
"strconv"
2627
"strings"
2728
"time"
2829

2930
"github.com/golang/glog"
31+
"github.com/google/cadvisor/events"
3032
"github.com/google/cadvisor/info"
3133
"github.com/google/cadvisor/manager"
3234
)
@@ -125,6 +127,7 @@ func writeResult(res interface{}, w http.ResponseWriter) error {
125127
w.Header().Set("Content-Type", "application/json")
126128
w.Write(out)
127129
return nil
130+
128131
}
129132

130133
func getContainerInfoRequest(body io.ReadCloser) (*info.ContainerInfoRequest, error) {
@@ -142,6 +145,74 @@ func getContainerInfoRequest(body io.ReadCloser) (*info.ContainerInfoRequest, er
142145
return &query, nil
143146
}
144147

148+
// The user can set any or none of the following arguments in any order
149+
// with any twice defined arguments being assigned the first value.
150+
// If the value type for the argument is wrong the field will be assumed to be
151+
// unassigned
152+
// bools: historical, subcontainers, oom_events, creation_events, deletion_events
153+
// ints: max_events, start_time (unix timestamp), end_time (unix timestamp)
154+
// example r.URL: http://localhost:8080/api/v1.3/events?oom_events=true&historical=true&max_events=10
155+
func getEventRequest(r *http.Request) (*events.Request, bool, error) {
156+
query := events.NewRequest()
157+
getHistoricalEvents := false
158+
159+
urlMap := r.URL.Query()
160+
161+
if val, ok := urlMap["historical"]; ok {
162+
newBool, err := strconv.ParseBool(val[0])
163+
if err == nil {
164+
getHistoricalEvents = newBool
165+
}
166+
}
167+
if val, ok := urlMap["subcontainers"]; ok {
168+
newBool, err := strconv.ParseBool(val[0])
169+
if err == nil {
170+
query.IncludeSubcontainers = newBool
171+
}
172+
}
173+
if val, ok := urlMap["oom_events"]; ok {
174+
newBool, err := strconv.ParseBool(val[0])
175+
if err == nil {
176+
query.EventType[events.TypeOom] = newBool
177+
}
178+
}
179+
if val, ok := urlMap["creation_events"]; ok {
180+
newBool, err := strconv.ParseBool(val[0])
181+
if err == nil {
182+
query.EventType[events.TypeContainerCreation] = newBool
183+
}
184+
}
185+
if val, ok := urlMap["deletion_events"]; ok {
186+
newBool, err := strconv.ParseBool(val[0])
187+
if err == nil {
188+
query.EventType[events.TypeContainerDeletion] = newBool
189+
}
190+
}
191+
if val, ok := urlMap["max_events"]; ok {
192+
newInt, err := strconv.Atoi(val[0])
193+
if err == nil {
194+
query.MaxEventsReturned = int(newInt)
195+
}
196+
}
197+
if val, ok := urlMap["start_time"]; ok {
198+
newTime, err := time.Parse(time.RFC3339, val[0])
199+
if err == nil {
200+
query.StartTime = newTime
201+
}
202+
}
203+
if val, ok := urlMap["end_time"]; ok {
204+
newTime, err := time.Parse(time.RFC3339, val[0])
205+
if err == nil {
206+
query.EndTime = newTime
207+
}
208+
}
209+
210+
glog.V(2).Infof(
211+
"%v was returned in api/handler.go:getEventRequest from the url rawQuery %v",
212+
query, r.URL.RawQuery)
213+
return query, getHistoricalEvents, nil
214+
}
215+
145216
func getContainerName(request []string) string {
146217
return path.Join("/", strings.Join(request, "/"))
147218
}

api/versions.go

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"net/http"
2020

2121
"github.com/golang/glog"
22+
"github.com/google/cadvisor/events"
2223
"github.com/google/cadvisor/info"
2324
"github.com/google/cadvisor/manager"
2425
)
@@ -30,6 +31,7 @@ const (
3031
dockerApi = "docker"
3132
summaryApi = "summary"
3233
specApi = "spec"
34+
eventsApi = "events"
3335
)
3436

3537
// Interface for a cAdvisor API version
@@ -49,8 +51,11 @@ func getApiVersions() []ApiVersion {
4951
v1_0 := &version1_0{}
5052
v1_1 := newVersion1_1(v1_0)
5153
v1_2 := newVersion1_2(v1_1)
52-
v2_0 := newVersion2_0(v1_2)
53-
return []ApiVersion{v1_0, v1_1, v1_2, v2_0}
54+
v1_3 := newVersion1_3(v1_2)
55+
v2_0 := newVersion2_0(v1_3)
56+
57+
return []ApiVersion{v1_0, v1_1, v1_2, v1_3, v2_0}
58+
5459
}
5560

5661
// API v1.0
@@ -227,12 +232,72 @@ func (self *version1_2) HandleRequest(requestType string, request []string, m ma
227232
}
228233
}
229234

230-
// v2.0 builds on v1.2
231-
type version2_0 struct {
235+
// API v1.3
236+
237+
type version1_3 struct {
232238
baseVersion *version1_2
233239
}
234240

235-
func newVersion2_0(v *version1_2) *version2_0 {
241+
// v1.3 builds on v1.2.
242+
func newVersion1_3(v *version1_2) *version1_3 {
243+
return &version1_3{
244+
baseVersion: v,
245+
}
246+
}
247+
248+
func (self *version1_3) Version() string {
249+
return "v1.3"
250+
}
251+
252+
func (self *version1_3) SupportedRequestTypes() []string {
253+
return append(self.baseVersion.SupportedRequestTypes(), eventsApi)
254+
}
255+
256+
func (self *version1_3) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
257+
switch requestType {
258+
case eventsApi:
259+
query, eventsFromAllTime, err := getEventRequest(r)
260+
if err != nil {
261+
return err
262+
}
263+
glog.V(2).Infof("Api - Events(%v)", query)
264+
265+
if eventsFromAllTime {
266+
allEvents, err := m.GetPastEvents(query)
267+
if err != nil {
268+
return err
269+
}
270+
return writeResult(allEvents, w)
271+
} else {
272+
// every time URL is entered to watch, a channel is created here
273+
eventChannel := make(chan *events.Event, 10)
274+
err = m.WatchForEvents(query, eventChannel)
275+
276+
defer close(eventChannel)
277+
currentEventSet := make(events.EventSlice, 0)
278+
for ev := range eventChannel {
279+
// todo: implement write-as-received writeResult method
280+
currentEventSet = append(currentEventSet, ev)
281+
err = writeResult(currentEventSet, w)
282+
if err != nil {
283+
return err
284+
}
285+
}
286+
}
287+
return nil
288+
default:
289+
return self.baseVersion.HandleRequest(requestType, request, m, w, r)
290+
}
291+
}
292+
293+
// API v2.0
294+
295+
// v2.0 builds on v1.3
296+
type version2_0 struct {
297+
baseVersion *version1_3
298+
}
299+
300+
func newVersion2_0(v *version1_3) *version2_0 {
236301
return &version2_0{
237302
baseVersion: v,
238303
}

api/versions_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2015 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package api
16+
17+
import (
18+
"io"
19+
"net/http"
20+
"reflect"
21+
"testing"
22+
23+
"github.com/google/cadvisor/events"
24+
"github.com/stretchr/testify/assert"
25+
)
26+
27+
// returns an http.Request pointer for an input url test string
28+
func makeHTTPRequest(requestURL string, t *testing.T) *http.Request {
29+
dummyReader, _ := io.Pipe()
30+
r, err := http.NewRequest("GET", requestURL, dummyReader)
31+
assert.Nil(t, err)
32+
return r
33+
}
34+
35+
func TestGetEventRequestBasicRequest(t *testing.T) {
36+
r := makeHTTPRequest("http://localhost:8080/api/v1.3/events?oom_events=true&historical=true&max_events=10", t)
37+
expectedQuery := &events.Request{
38+
EventType: map[events.EventType]bool{
39+
events.TypeOom: true,
40+
},
41+
MaxEventsReturned: 10,
42+
}
43+
44+
receivedQuery, getHistoricalEvents, err := getEventRequest(r)
45+
46+
if !reflect.DeepEqual(expectedQuery, receivedQuery) {
47+
t.Errorf("expected %v but received %v", expectedQuery, receivedQuery)
48+
}
49+
assert.True(t, getHistoricalEvents)
50+
assert.Nil(t, err)
51+
}
52+
53+
func TestGetEventEmptyRequest(t *testing.T) {
54+
r := makeHTTPRequest("", t)
55+
expectedQuery := events.NewRequest()
56+
57+
receivedQuery, getHistoricalEvents, err := getEventRequest(r)
58+
59+
if !reflect.DeepEqual(expectedQuery, receivedQuery) {
60+
t.Errorf("expected %v but received %v", expectedQuery, receivedQuery)
61+
}
62+
assert.False(t, getHistoricalEvents)
63+
assert.Nil(t, err)
64+
}
65+
66+
func TestGetEventRequestDoubleArgument(t *testing.T) {
67+
r := makeHTTPRequest("http://localhost:8080/api/v1.3/events?historical=true&oom_events=true&oom_events=false", t)
68+
expectedQuery := &events.Request{
69+
EventType: map[events.EventType]bool{
70+
events.TypeOom: true,
71+
},
72+
}
73+
74+
receivedQuery, getHistoricalEvents, err := getEventRequest(r)
75+
76+
if !reflect.DeepEqual(expectedQuery, receivedQuery) {
77+
t.Errorf("expected %v but received %v", expectedQuery, receivedQuery)
78+
}
79+
assert.True(t, getHistoricalEvents)
80+
assert.Nil(t, err)
81+
}

events/handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ type Request struct {
103103
// allows the caller to put a limit on how many
104104
// events they receive. If there are more events than MaxEventsReturned
105105
// then the most chronologically recent events in the time period
106-
// specified are returned
106+
// specified are returned. Must be >= 1
107107
MaxEventsReturned int
108108
// the absolute container name for which the event occurred
109109
ContainerName string

manager/manager.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ type Manager interface {
7474

7575
// Get events streamed through passedChannel that fit the request.
7676
WatchForEvents(request *events.Request, passedChannel chan *events.Event) error
77+
78+
// Get past events that have been detected and that fit the request.
79+
GetPastEvents(request *events.Request) (events.EventSlice, error)
7780
}
7881

7982
// New takes a memory storage and returns a new manager.
@@ -474,6 +477,7 @@ func (m *manager) createContainer(containerName string) error {
474477

475478
// Start the container's housekeeping.
476479
cont.Start()
480+
477481
return nil
478482
}
479483

@@ -596,8 +600,8 @@ func (self *manager) watchForNewContainers(quit chan error) error {
596600
}
597601

598602
// Register for new subcontainers.
599-
events := make(chan container.SubcontainerEvent, 16)
600-
err := root.handler.WatchSubcontainers(events)
603+
eventsChannel := make(chan container.SubcontainerEvent, 16)
604+
err := root.handler.WatchSubcontainers(eventsChannel)
601605
if err != nil {
602606
return err
603607
}
@@ -612,7 +616,7 @@ func (self *manager) watchForNewContainers(quit chan error) error {
612616
go func() {
613617
for {
614618
select {
615-
case event := <-events:
619+
case event := <-eventsChannel:
616620
switch {
617621
case event.EventType == container.SubcontainerAdd:
618622
err = self.createContainer(event.Name)
@@ -668,3 +672,8 @@ func (self *manager) watchForNewOoms() error {
668672
func (self *manager) WatchForEvents(request *events.Request, passedChannel chan *events.Event) error {
669673
return self.eventHandler.WatchEvents(passedChannel, request)
670674
}
675+
676+
// can be called by the api which will return all events satisfying the request
677+
func (self *manager) GetPastEvents(request *events.Request) (events.EventSlice, error) {
678+
return self.eventHandler.GetEvents(request)
679+
}

0 commit comments

Comments
 (0)