Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Start/stop/restart actions and main integration.

This change adds the ability to use start/stop/restart actions in
event rules. It also integrates the eventmonitor into the main code,
so it runs when gonit is running.

Change-Id: I617024ccbc83b02e0fb80c6eb34a7c4d0eac46c5
  • Loading branch information...
commit cb0d39f504a83e641b53571138d721c873bb62b6 1 parent 8d7c5a5
Ben Lisbakken authored September 04, 2012
50  api.go
@@ -11,7 +11,7 @@ import (
11 11
 var notimpl = errors.New("Method not implemented")
12 12
 
13 13
 type API struct {
14  
-	control *Control
  14
+	Control *Control
15 15
 }
16 16
 
17 17
 type ProcessSummary struct {
@@ -64,7 +64,7 @@ func (e *ActionError) Error() string {
64 64
 
65 65
 func NewAPI(config *ConfigManager) *API {
66 66
 	return &API{
67  
-		control: &Control{configManager: config},
  67
+		Control: &Control{configManager: config},
68 68
 	}
69 69
 }
70 70
 
@@ -83,23 +83,23 @@ func (c *Control) callAction(name string, r *ActionResult, action int) error {
83 83
 }
84 84
 
85 85
 func (a *API) StartProcess(name string, r *ActionResult) error {
86  
-	return a.control.callAction(name, r, ACTION_START)
  86
+	return a.Control.callAction(name, r, ACTION_START)
87 87
 }
88 88
 
89 89
 func (a *API) StopProcess(name string, r *ActionResult) error {
90  
-	return a.control.callAction(name, r, ACTION_STOP)
  90
+	return a.Control.callAction(name, r, ACTION_STOP)
91 91
 }
92 92
 
93 93
 func (a *API) RestartProcess(name string, r *ActionResult) error {
94  
-	return a.control.callAction(name, r, ACTION_RESTART)
  94
+	return a.Control.callAction(name, r, ACTION_RESTART)
95 95
 }
96 96
 
97 97
 func (a *API) MonitorProcess(name string, r *ActionResult) error {
98  
-	return a.control.callAction(name, r, ACTION_MONITOR)
  98
+	return a.Control.callAction(name, r, ACTION_MONITOR)
99 99
 }
100 100
 
101 101
 func (a *API) UnmonitorProcess(name string, r *ActionResult) error {
102  
-	return a.control.callAction(name, r, ACTION_UNMONITOR)
  102
+	return a.Control.callAction(name, r, ACTION_UNMONITOR)
103 103
 }
104 104
 
105 105
 func (c *Control) processSummary(process *Process, summary *ProcessSummary) {
@@ -129,13 +129,13 @@ func (c *Control) processStatus(process *Process, status *ProcessStatus) error {
129 129
 }
130 130
 
131 131
 func (a *API) StatusProcess(name string, r *ProcessStatus) error {
132  
-	process, err := a.control.Config().FindProcess(name)
  132
+	process, err := a.Control.Config().FindProcess(name)
133 133
 
134 134
 	if err != nil {
135 135
 		return err
136 136
 	}
137 137
 
138  
-	return a.control.processStatus(process, r)
  138
+	return a.Control.processStatus(process, r)
139 139
 }
140 140
 
141 141
 // *Group methods apply to a service group
@@ -155,23 +155,23 @@ func (c *Control) groupAction(name string, r *ActionResult, action int) error {
155 155
 }
156 156
 
157 157
 func (a *API) StartGroup(name string, r *ActionResult) error {
158  
-	return a.control.groupAction(name, r, ACTION_START)
  158
+	return a.Control.groupAction(name, r, ACTION_START)
159 159
 }
160 160
 
161 161
 func (a *API) StopGroup(name string, r *ActionResult) error {
162  
-	return a.control.groupAction(name, r, ACTION_STOP)
  162
+	return a.Control.groupAction(name, r, ACTION_STOP)
163 163
 }
164 164
 
165 165
 func (a *API) RestartGroup(name string, r *ActionResult) error {
166  
-	return a.control.groupAction(name, r, ACTION_RESTART)
  166
+	return a.Control.groupAction(name, r, ACTION_RESTART)
167 167
 }
168 168
 
169 169
 func (a *API) MonitorGroup(name string, r *ActionResult) error {
170  
-	return a.control.groupAction(name, r, ACTION_MONITOR)
  170
+	return a.Control.groupAction(name, r, ACTION_MONITOR)
171 171
 }
172 172
 
173 173
 func (a *API) UnmonitorGroup(name string, r *ActionResult) error {
174  
-	return a.control.groupAction(name, r, ACTION_UNMONITOR)
  174
+	return a.Control.groupAction(name, r, ACTION_UNMONITOR)
175 175
 }
176 176
 
177 177
 func (c *Control) groupStatus(group *ProcessGroup,
@@ -187,14 +187,14 @@ func (c *Control) groupStatus(group *ProcessGroup,
187 187
 }
188 188
 
189 189
 func (a *API) StatusGroup(name string, r *ProcessGroupStatus) error {
190  
-	group, err := a.control.Config().FindGroup(name)
  190
+	group, err := a.Control.Config().FindGroup(name)
191 191
 
192 192
 	if err != nil {
193 193
 		return err
194 194
 	}
195 195
 
196 196
 	r.Name = name
197  
-	a.control.groupStatus(group, r)
  197
+	a.Control.groupStatus(group, r)
198 198
 
199 199
 	return nil
200 200
 }
@@ -211,40 +211,40 @@ func (c *Control) allAction(r *ActionResult, action int) error {
211 211
 }
212 212
 
213 213
 func (a *API) StartAll(unused interface{}, r *ActionResult) error {
214  
-	return a.control.allAction(r, ACTION_START)
  214
+	return a.Control.allAction(r, ACTION_START)
215 215
 }
216 216
 
217 217
 func (a *API) StopAll(unused interface{}, r *ActionResult) error {
218  
-	return a.control.allAction(r, ACTION_STOP)
  218
+	return a.Control.allAction(r, ACTION_STOP)
219 219
 }
220 220
 
221 221
 func (a *API) RestartAll(unused interface{}, r *ActionResult) error {
222  
-	return a.control.allAction(r, ACTION_RESTART)
  222
+	return a.Control.allAction(r, ACTION_RESTART)
223 223
 }
224 224
 
225 225
 func (a *API) MonitorAll(unused interface{}, r *ActionResult) error {
226  
-	return a.control.allAction(r, ACTION_MONITOR)
  226
+	return a.Control.allAction(r, ACTION_MONITOR)
227 227
 }
228 228
 
229 229
 func (a *API) UnmonitorAll(unused interface{}, r *ActionResult) error {
230  
-	return a.control.allAction(r, ACTION_UNMONITOR)
  230
+	return a.Control.allAction(r, ACTION_UNMONITOR)
231 231
 }
232 232
 
233 233
 func (a *API) StatusAll(name string, r *ProcessGroupStatus) error {
234 234
 	r.Name = name
235 235
 
236  
-	for _, processGroup := range a.control.Config().ProcessGroups {
237  
-		a.control.groupStatus(processGroup, r)
  236
+	for _, processGroup := range a.Control.Config().ProcessGroups {
  237
+		a.Control.groupStatus(processGroup, r)
238 238
 	}
239 239
 
240 240
 	return nil
241 241
 }
242 242
 
243 243
 func (a *API) Summary(unused interface{}, s *Summary) error {
244  
-	for _, group := range a.control.Config().ProcessGroups {
  244
+	for _, group := range a.Control.Config().ProcessGroups {
245 245
 		for _, process := range group.Processes {
246 246
 			summary := ProcessSummary{}
247  
-			a.control.processSummary(process, &summary)
  247
+			a.Control.processSummary(process, &summary)
248 248
 			s.Processes = append(s.Processes, summary)
249 249
 		}
250 250
 	}
34  configmanager.go
@@ -63,15 +63,16 @@ type Process struct {
63 63
 	Description string
64 64
 	DependsOn   []string
65 65
 	Actions     map[string][]string
66  
-	// TODO How do we make it so Monitor is true by default and only false when
67  
-	// explicitly set in yaml?
68  
-	Monitor bool
  66
+	MonitorMode string
69 67
 }
70 68
 
71 69
 const (
72 70
 	CONFIG_FILE_POSTFIX   = "-gonit.yml"
73 71
 	SETTINGS_FILENAME     = "gonit.yml"
74 72
 	UNIX_SOCKET_TRANSPORT = "unix_socket"
  73
+	MONITOR_MODE_ACTIVE   = "active"
  74
+	MONITOR_MODE_PASSIVE  = "passive"
  75
+	MONITOR_MODE_MANUAL   = "manual"
75 76
 )
76 77
 
77 78
 const (
@@ -217,12 +218,27 @@ func (c *ConfigManager) Parse(paths ...string) error {
217 218
 		log.Printf("No settings found, using defaults.")
218 219
 	}
219 220
 	c.applyDefaultSettings()
  221
+	c.applyDefaultConfigOpts()
220 222
 	if err := c.validate(); err != nil {
221 223
 		return err
222 224
 	}
223 225
 	return nil
224 226
 }
225 227
 
  228
+func (c *ConfigManager) applyDefaultMonitorMode() {
  229
+	for _, pg := range c.ProcessGroups {
  230
+		for _, process := range pg.Processes {
  231
+			if process.MonitorMode == "" {
  232
+				process.MonitorMode = MONITOR_MODE_ACTIVE
  233
+			}
  234
+		}
  235
+	}
  236
+}
  237
+
  238
+func (c *ConfigManager) applyDefaultConfigOpts() {
  239
+	c.applyDefaultMonitorMode()
  240
+}
  241
+
226 242
 // Validates that certain fields exist in the config file.
227 243
 func (pg ProcessGroup) validateRequiredFieldsExist() error {
228 244
 	for name, process := range pg.Processes {
@@ -283,3 +299,15 @@ func (c *ConfigManager) validate() error {
283 299
 	}
284 300
 	return nil
285 301
 }
  302
+
  303
+func (p *Process) IsMonitoringModeActive() bool {
  304
+	return p.MonitorMode == MONITOR_MODE_ACTIVE
  305
+}
  306
+
  307
+func (p *Process) IsMonitoringModePassive() bool {
  308
+	return p.MonitorMode == MONITOR_MODE_PASSIVE
  309
+}
  310
+
  311
+func (p *Process) IsMonitoringModeManual() bool {
  312
+	return p.MonitorMode == MONITOR_MODE_MANUAL
  313
+}
62  control.go
@@ -5,6 +5,7 @@ package gonit
5 5
 import (
6 6
 	"fmt"
7 7
 	"log"
  8
+	"sync"
8 9
 	"time"
9 10
 )
10 11
 
@@ -28,8 +29,14 @@ const (
28 29
 	processStarted
29 30
 )
30 31
 
  32
+// So we can mock it in tests.
  33
+type EventMonitorInterface interface {
  34
+	StartMonitoringProcess(process *Process)
  35
+}
  36
+
31 37
 type Control struct {
32 38
 	configManager *ConfigManager
  39
+	EventMonitor  EventMonitorInterface
33 40
 	visits        map[string]*visitor
34 41
 	states        map[string]*processState
35 42
 }
@@ -43,8 +50,9 @@ type visitor struct {
43 50
 
44 51
 // XXX TODO should state be attached to Process type?
45 52
 type processState struct {
46  
-	Monitor int
47  
-	Starts  int
  53
+	Monitor     int
  54
+	MonitorLock sync.Mutex
  55
+	Starts      int
48 56
 }
49 57
 
50 58
 // XXX TODO needed for tests, a form of this should probably be in ConfigManager
@@ -69,6 +77,7 @@ func (c *ConfigManager) AddProcess(groupName string, process *Process) error {
69 77
 	return nil
70 78
 }
71 79
 
  80
+// BUG(lisbakke): If there are two processes named the same thing in different process groups, this could return the wrong process. ConfigManager should enforce unique group/process names.
72 81
 // XXX TODO should probably be in configmanager.go
73 82
 // Helper methods to find a Process by name
74 83
 func (c *ConfigManager) FindProcess(name string) (*Process, error) {
@@ -137,6 +146,12 @@ func (c *Control) State(process *Process) *processState {
137 146
 	return c.states[process.Name]
138 147
 }
139 148
 
  149
+// Registers the event monitor with Control so that it can turn event monitoring
  150
+// on/off when processes are started/stopped.
  151
+func (c *Control) RegisterEventMonitor(eventMonitor *EventMonitor) {
  152
+	c.EventMonitor = eventMonitor
  153
+}
  154
+
140 155
 // Invoke given action for the given process and its
141 156
 // dependents and/or dependencies
142 157
 func (c *Control) DoAction(name string, action int) error {
@@ -155,19 +170,19 @@ func (c *Control) DoAction(name string, action int) error {
155 170
 			c.monitorSet(process)
156 171
 			return nil
157 172
 		}
158  
-		c.doDepend(process, ACTION_STOP, false)
  173
+		c.doDepend(process, ACTION_STOP)
159 174
 		c.doStart(process)
160  
-		c.doDepend(process, ACTION_START, false)
  175
+		c.doDepend(process, ACTION_START)
161 176
 
162 177
 	case ACTION_STOP:
163  
-		c.doDepend(process, ACTION_STOP, true)
164  
-		c.doStop(process, true)
  178
+		c.doDepend(process, ACTION_STOP)
  179
+		c.doStop(process)
165 180
 
166 181
 	case ACTION_RESTART:
167  
-		c.doDepend(process, ACTION_STOP, false)
168  
-		if c.doStop(process, false) {
  182
+		c.doDepend(process, ACTION_STOP)
  183
+		if c.doStop(process) {
169 184
 			c.doStart(process)
170  
-			c.doDepend(process, ACTION_START, false)
  185
+			c.doDepend(process, ACTION_START)
171 186
 		} else {
172 187
 			c.monitorSet(process)
173 188
 		}
@@ -176,7 +191,7 @@ func (c *Control) DoAction(name string, action int) error {
176 191
 		c.doMonitor(process)
177 192
 
178 193
 	case ACTION_UNMONITOR:
179  
-		c.doDepend(process, ACTION_UNMONITOR, false)
  194
+		c.doDepend(process, ACTION_UNMONITOR)
180 195
 		c.doUnmonitor(process)
181 196
 
182 197
 	default:
@@ -215,9 +230,8 @@ func (c *Control) doStart(process *Process) {
215 230
 }
216 231
 
217 232
 // Stop the given Process.
218  
-// Monitoring is disabled when unmonitor flag is true.
219 233
 // Waits for process to stop or until Process.Timeout is reached.
220  
-func (c *Control) doStop(process *Process, unmonitor bool) bool {
  234
+func (c *Control) doStop(process *Process) bool {
221 235
 	visitor := c.visitorOf(process)
222 236
 	var rv = true
223 237
 	if visitor.stopped {
@@ -232,9 +246,7 @@ func (c *Control) doStop(process *Process, unmonitor bool) bool {
232 246
 		}
233 247
 	}
234 248
 
235  
-	if unmonitor {
236  
-		c.monitorUnset(process)
237  
-	}
  249
+	c.monitorUnset(process)
238 250
 
239 251
 	return rv
240 252
 }
@@ -268,7 +280,7 @@ func (c *Control) doUnmonitor(process *Process) {
268 280
 }
269 281
 
270 282
 // Apply actions to processes that depend on the given Process
271  
-func (c *Control) doDepend(process *Process, action int, unmonitor bool) {
  283
+func (c *Control) doDepend(process *Process, action int) {
272 284
 	c.configManager.VisitProcesses(func(child *Process) bool {
273 285
 		for _, dep := range child.DependsOn {
274 286
 			if dep == process.Name {
@@ -279,11 +291,11 @@ func (c *Control) doDepend(process *Process, action int, unmonitor bool) {
279 291
 					c.doMonitor(child)
280 292
 				}
281 293
 
282  
-				c.doDepend(child, action, unmonitor)
  294
+				c.doDepend(child, action)
283 295
 
284 296
 				switch action {
285 297
 				case ACTION_STOP:
286  
-					c.doStop(child, unmonitor)
  298
+					c.doStop(child)
287 299
 				case ACTION_UNMONITOR:
288 300
 					c.doUnmonitor(child)
289 301
 				}
@@ -296,22 +308,32 @@ func (c *Control) doDepend(process *Process, action int, unmonitor bool) {
296 308
 
297 309
 func (c *Control) monitorSet(process *Process) {
298 310
 	state := c.State(process)
299  
-
  311
+	state.MonitorLock.Lock()
  312
+	defer state.MonitorLock.Unlock()
300 313
 	if state.Monitor == MONITOR_NOT {
301 314
 		state.Monitor = MONITOR_INIT
  315
+		c.EventMonitor.StartMonitoringProcess(process)
302 316
 		log.Printf("%q monitoring enabled", process.Name)
303 317
 	}
304 318
 }
305 319
 
306 320
 func (c *Control) monitorUnset(process *Process) {
307 321
 	state := c.State(process)
308  
-
  322
+	state.MonitorLock.Lock()
  323
+	defer state.MonitorLock.Unlock()
309 324
 	if state.Monitor != MONITOR_NOT {
310 325
 		state.Monitor = MONITOR_NOT
311 326
 		log.Printf("%q monitoring disabled", process.Name)
312 327
 	}
313 328
 }
314 329
 
  330
+func (c *Control) IsMonitoring(process *Process) bool {
  331
+	state := c.State(process)
  332
+	state.MonitorLock.Lock()
  333
+	defer state.MonitorLock.Unlock()
  334
+	return state.Monitor == MONITOR_INIT || state.Monitor == MONITOR_YES
  335
+}
  336
+
315 337
 // Poll process for expected state change
316 338
 func (p *Process) pollState(timeout time.Duration, expect int) bool {
317 339
 	isRunning := false
16  control_test.go
@@ -12,8 +12,17 @@ import (
12 12
 
13 13
 var groupName = "controlTest"
14 14
 
  15
+type FakeEventMonitor struct {
  16
+	numStartMonitoringCalled int
  17
+}
  18
+
  19
+func (fem *FakeEventMonitor) StartMonitoringProcess(process *Process) {
  20
+	fem.numStartMonitoringCalled++
  21
+}
  22
+
15 23
 func TestActions(t *testing.T) {
16  
-	c := &Control{}
  24
+	fem := &FakeEventMonitor{}
  25
+	c := &Control{EventMonitor: fem}
17 26
 
18 27
 	name := "simple"
19 28
 	process := helper.NewTestProcess(name, nil, false)
@@ -26,6 +35,7 @@ func TestActions(t *testing.T) {
26 35
 	assert.Equal(t, 0, c.State(process).Starts)
27 36
 
28 37
 	rv := c.DoAction(name, ACTION_START)
  38
+	assert.Equal(t, 1, fem.numStartMonitoringCalled)
29 39
 	assert.Equal(t, nil, rv)
30 40
 
31 41
 	assert.Equal(t, MONITOR_INIT, c.State(process).Monitor)
@@ -34,6 +44,7 @@ func TestActions(t *testing.T) {
34 44
 	assert.Equal(t, true, process.IsRunning())
35 45
 
36 46
 	rv = c.DoAction(name, ACTION_RESTART)
  47
+	assert.Equal(t, 2, fem.numStartMonitoringCalled)
37 48
 	assert.Equal(t, nil, rv)
38 49
 
39 50
 	assert.Equal(t, 2, c.State(process).Starts)
@@ -44,13 +55,14 @@ func TestActions(t *testing.T) {
44 55
 	assert.Equal(t, MONITOR_NOT, c.State(process).Monitor)
45 56
 
46 57
 	rv = c.DoAction(name, ACTION_MONITOR)
  58
+	assert.Equal(t, 3, fem.numStartMonitoringCalled)
47 59
 	assert.Equal(t, nil, rv)
48 60
 
49 61
 	assert.Equal(t, MONITOR_INIT, c.State(process).Monitor)
50 62
 }
51 63
 
52 64
 func TestDepends(t *testing.T) {
53  
-	c := &Control{}
  65
+	c := &Control{EventMonitor: &FakeEventMonitor{}}
54 66
 
55 67
 	name := "depsimple"
56 68
 	process := helper.NewTestProcess(name, nil, false)
208  eventmonitor.go
@@ -8,15 +8,17 @@ import (
8 8
 	"log"
9 9
 	"math"
10 10
 	"net"
  11
+	"strings"
11 12
 	"time"
12 13
 )
13 14
 
14 15
 // TODO:
15  
-// - Support more actions than alert.
16 16
 // - Maybe consider changing the way rule checking works so that it timestamps
17 17
 //       the last time each rule was checked instead of the way it is?
18 18
 // - Debug messages?
19 19
 // - Support more than just unix socket for alerts.
  20
+// - Move the parsing/validation logic from here to configmanager, since that's
  21
+//   a better fit.
20 22
 
21 23
 // After configmanager gets the rules to be monitored, eventmonitor parses the
22 24
 // rules and stores their data as ParsedEvent.
@@ -30,6 +32,7 @@ type ParsedEvent struct {
30 32
 	processName  string
31 33
 	description  string
32 34
 	interval     time.Duration
  35
+	action       string
33 36
 }
34 37
 
35 38
 // The JSON message that is sent in alerts.
@@ -48,6 +51,8 @@ const (
48 51
 	DEFAULT_INTERVAL = "2s"
49 52
 )
50 53
 
  54
+var validActions = []string{"stop", "start", "restart", "alert"}
  55
+
51 56
 const (
52 57
 	EQ_OPERATOR  = 0x1
53 58
 	NEQ_OPERATOR = 0x2
@@ -55,6 +60,16 @@ const (
55 60
 	LT_OPERATOR  = 0x4
56 61
 )
57 62
 
  63
+// Returns whether or not the actionName is a valid action.
  64
+func isValidAction(actionName string) bool {
  65
+	for _, action := range validActions {
  66
+		if actionName == action {
  67
+			return true
  68
+		}
  69
+	}
  70
+	return false
  71
+}
  72
+
58 73
 // Returns whether a character is an operator character in an event rule.
59 74
 func isAnOperatorChar(operatorChar string) bool {
60 75
 	return operatorChar == "<" || operatorChar == ">" || operatorChar == "=" ||
@@ -89,25 +104,39 @@ func checkRule(parsedEvent *ParsedEvent, resourceVal uint64) bool {
89 104
 // resource values from resourcemanager.  If any events trigger, it will take
90 105
 // appropriate action.
91 106
 type EventMonitor struct {
92  
-	alertEvents     []*ParsedEvent
  107
+	events          []*ParsedEvent
93 108
 	resourceManager ResourceManager
94 109
 	configManager   *ConfigManager
  110
+	control         ControlInterface
95 111
 	startTime       int64
96 112
 	quitChan        chan bool
97 113
 }
98 114
 
  115
+type ControlInterface interface {
  116
+	DoAction(name string, action int) error
  117
+	IsMonitoring(process *Process) bool
  118
+}
  119
+
  120
+// Simple helper to make testing easier.
  121
+func (e *EventMonitor) registerControl(control ControlInterface) {
  122
+	e.control = control
  123
+}
  124
+
99 125
 // Initializes the eventmonitor by parsing event rules and initializing data
100 126
 // structures.  The configmanager is where the events come from.
101  
-func (e *EventMonitor) setup(configManager *ConfigManager) error {
  127
+func (e *EventMonitor) setup(configManager *ConfigManager,
  128
+	control *Control) error {
102 129
 	e.resourceManager = resourceManager
103 130
 	e.configManager = configManager
104  
-	e.alertEvents = []*ParsedEvent{}
  131
+	e.registerControl(control)
  132
+	e.events = []*ParsedEvent{}
105 133
 	for _, group := range e.configManager.ProcessGroups {
106 134
 		for _, process := range group.Processes {
107 135
 			for actionName, actions := range process.Actions {
108 136
 				for _, eventName := range actions {
109 137
 					event := group.EventByName(eventName)
110  
-					if err := e.loadEvent(event, group.Name, process); err != nil {
  138
+					if err := e.loadEvent(event, group.Name, process,
  139
+						actionName); err != nil {
111 140
 						return fmt.Errorf("Did not load rule '%v' on action '%v' because "+
112 141
 							"of error: '%v'.", eventName, actionName, err.Error())
113 142
 					}
@@ -120,13 +149,55 @@ func (e *EventMonitor) setup(configManager *ConfigManager) error {
120 149
 	return nil
121 150
 }
122 151
 
  152
+func (e *EventMonitor) printTriggeredMessage(event *ParsedEvent,
  153
+	resourceVal uint64) {
  154
+	log.Printf("'%v' triggered '%v' for '%v' (at '%v'). Executing '%v'.\n",
  155
+		event.processName, event.ruleString, event.duration, resourceVal,
  156
+		event.action)
  157
+}
  158
+
  159
+func (e *EventMonitor) triggerAction(process *Process, event *ParsedEvent,
  160
+	resourceVal uint64) error {
  161
+	switch event.action {
  162
+	case "stop":
  163
+		if e.TriggerProcessActions(process) {
  164
+			e.printTriggeredMessage(event, resourceVal)
  165
+			return e.control.DoAction(event.processName, ACTION_STOP)
  166
+		} else {
  167
+			return nil
  168
+		}
  169
+	case "start":
  170
+		if e.TriggerProcessActions(process) {
  171
+			e.printTriggeredMessage(event, resourceVal)
  172
+			return e.control.DoAction(event.processName, ACTION_START)
  173
+		} else {
  174
+			return nil
  175
+		}
  176
+	case "restart":
  177
+		if e.TriggerProcessActions(process) {
  178
+			e.printTriggeredMessage(event, resourceVal)
  179
+			return e.control.DoAction(event.processName, ACTION_RESTART)
  180
+		} else {
  181
+			return nil
  182
+		}
  183
+	case "alert":
  184
+		if e.TriggerAlerts(process) {
  185
+			e.printTriggeredMessage(event, resourceVal)
  186
+			return e.sendAlert(event)
  187
+		} else {
  188
+			return nil
  189
+		}
  190
+	}
  191
+	return fmt.Errorf("No event action '%v' exists.", event.action)
  192
+}
  193
+
123 194
 // Given a configmanager config, this function starts the eventmonitor on
124 195
 // monitoring events and dispatching them.
125  
-func (e *EventMonitor) Start(configManager *ConfigManager) error {
126  
-	if err := e.setup(configManager); err != nil {
  196
+func (e *EventMonitor) Start(configManager *ConfigManager,
  197
+	control *Control) error {
  198
+	if err := e.setup(configManager, control); err != nil {
127 199
 		return err
128 200
 	}
129  
-
130 201
 	go func() {
131 202
 		timeToWait := 1 * time.Second
132 203
 		ticker := time.NewTicker(timeToWait)
@@ -140,16 +211,16 @@ func (e *EventMonitor) Start(configManager *ConfigManager) error {
140 211
 			case <-ticker.C:
141 212
 				for _, group := range e.configManager.ProcessGroups {
142 213
 					for _, process := range group.Processes {
143  
-						// TODO change the GetPid to be a go routine that happens every X
144  
-						// seconds with a lock on it so we don't have to keep opening the
145  
-						// file.
146  
-						pid, err := process.Pid()
147  
-						if err != nil {
148  
-							log.Println("Could not get pid file for process '%v'. Error: %+v",
149  
-								process.Name, err)
150  
-						}
151  
-						if process.Monitor != false {
152  
-							e.checkRules(process.Name, pid)
  214
+						if e.IsMonitoring(process) {
  215
+							// TODO change the GetPid to be a go routine that happens every X
  216
+							// seconds with a lock on it so we don't have to keep opening the
  217
+							// file.
  218
+							pid, err := process.Pid()
  219
+							if err != nil {
  220
+								log.Printf("Could not get pid file for process '%v'. Error: "+
  221
+									"%+v\n", process.Name, err)
  222
+							}
  223
+							e.checkRules(process, pid)
153 224
 						}
154 225
 					}
155 226
 				}
@@ -167,50 +238,44 @@ func (e *EventMonitor) Stop() {
167 238
 
168 239
 // Given a process name and a pid, this will check all the rules associated with
169 240
 // it for this time period.
170  
-func (e *EventMonitor) checkRules(processName string, pid int) {
  241
+func (e *EventMonitor) checkRules(process *Process, pid int) {
  242
+	processName := process.Name
171 243
 	diffTime := time.Now().Unix() - e.startTime
172  
-	// So we don't check the same thing more than once in a row.
173  
-	cachedResources := map[string]uint64{}
174  
-	for _, alertEvent := range e.alertEvents {
175  
-		interval := int64(alertEvent.interval.Seconds())
176  
-		if alertEvent.processName == processName &&
  244
+	for _, event := range e.events {
  245
+		interval := int64(event.interval.Seconds())
  246
+		if event.processName == processName &&
177 247
 			(interval == 0 || diffTime%interval == 0) {
178 248
 			var resourceVal uint64
179  
-			// Use cache unless we must pull the resource.
180  
-			resourceVal, has_key := cachedResources[alertEvent.resourceName]
181  
-			if !has_key {
182  
-				var err error
183  
-				resourceVal, err = e.resourceManager.GetResource(alertEvent, pid)
184  
-				if err != nil {
185  
-					log.Print(err)
186  
-					continue
187  
-				}
188  
-				cachedResources[alertEvent.resourceName] = resourceVal
  249
+			var err error
  250
+			resourceVal, err = e.resourceManager.GetResource(event, pid)
  251
+			if err != nil {
  252
+				log.Print(err)
  253
+				continue
189 254
 			}
190  
-			ruleTriggered := checkRule(alertEvent, resourceVal)
  255
+			ruleTriggered := checkRule(event, resourceVal)
191 256
 			if ruleTriggered {
192 257
 				// TODO right now this can block the monitoring loop.
193  
-				if err := e.sendAlert(alertEvent); err != nil {
  258
+				if err := e.triggerAction(process, event, resourceVal); err != nil {
194 259
 					log.Print(err)
195 260
 				}
196 261
 			}
197 262
 		}
198 263
 	}
  264
+	e.resourceManager.ClearCachedResources()
199 265
 }
200 266
 
201 267
 // Given Events from ConfigManager, parses them and adds them to internal data
202 268
 // so they can be monitored.
203 269
 func (e *EventMonitor) loadEvent(event *Event, groupName string,
204  
-	process *Process) error {
205  
-	parsedEvent, err := e.parseEvent(event, groupName, process.Name)
  270
+	process *Process, actionName string) error {
  271
+	parsedEvent, err := e.parseEvent(event, groupName, process.Name, actionName)
206 272
 	if err != nil {
207 273
 		return err
208 274
 	}
209  
-
210 275
 	if err = e.validateInterval(parsedEvent); err != nil {
211 276
 		return err
212 277
 	}
213  
-	e.alertEvents = append(e.alertEvents, parsedEvent)
  278
+	e.events = append(e.events, parsedEvent)
214 279
 	return nil
215 280
 }
216 281
 
@@ -282,7 +347,7 @@ func (e *EventMonitor) parseRule(rule string) (uint64, int, string, error) {
282 347
 // Given an Event, parses the rule into amount, operator and resourceName, does
283 348
 // a few other things, then returns a ParsedEvent ready to be monitored.
284 349
 func (e *EventMonitor) parseEvent(event *Event, groupName string,
285  
-	processName string) (*ParsedEvent, error) {
  350
+	processName string, actionName string) (*ParsedEvent, error) {
286 351
 	rule := event.Rule
287 352
 	ruleAmount, operator, resourceName, err := e.parseRule(rule)
288 353
 	if err != nil {
@@ -307,7 +372,13 @@ func (e *EventMonitor) parseEvent(event *Event, groupName string,
307 372
 		return nil, err
308 373
 	}
309 374
 
  375
+	if !isValidAction(actionName) {
  376
+		return nil, fmt.Errorf("No event action '%v' exists. Valid actions "+
  377
+			"are [%+v].", actionName, strings.Join(validActions, ", "))
  378
+	}
  379
+
310 380
 	parsedEvent := &ParsedEvent{
  381
+		action:       actionName,
311 382
 		operator:     operator,
312 383
 		ruleAmount:   ruleAmount,
313 384
 		resourceName: resourceName,
@@ -321,22 +392,20 @@ func (e *EventMonitor) parseEvent(event *Event, groupName string,
321 392
 	return parsedEvent, nil
322 393
 }
323 394
 
324  
-// Sends an alerts.
  395
+// Sends an alert.
325 396
 func (e *EventMonitor) sendAlert(parsedEvent *ParsedEvent) error {
326 397
 	settings := e.configManager.Settings
327 398
 	if settings.AlertTransport == UNIX_SOCKET_TRANSPORT {
328  
-		if err := e.sendUnixSocketAlert(parsedEvent, settings.SocketFile); err != nil {
  399
+		if err := e.sendUnixSocketAlert(parsedEvent,
  400
+			settings.SocketFile); err != nil {
329 401
 			return err
330 402
 		}
331  
-	} else {
332  
-		log.Printf("Rule '%v' for process '%v' has triggered for > %v seconds.\n",
333  
-			parsedEvent.ruleString, parsedEvent.processName, parsedEvent.duration)
334 403
 	}
335 404
 	return nil
336 405
 }
337 406
 
338 407
 func (e *EventMonitor) validateInterval(parsedEvent *ParsedEvent) error {
339  
-	for _, event := range e.alertEvents {
  408
+	for _, event := range e.events {
340 409
 		if event.processName == parsedEvent.processName &&
341 410
 			event.resourceName == parsedEvent.resourceName {
342 411
 			if event.interval != parsedEvent.interval {
@@ -344,20 +413,21 @@ func (e *EventMonitor) validateInterval(parsedEvent *ParsedEvent) error {
344 413
 					"poll intervals for the same resource '%v'.", event.ruleString,
345 414
 					parsedEvent.ruleString, event.processName, event.resourceName)
346 415
 			}
347  
-			durationRatio := event.duration.Seconds() / event.interval.Seconds()
348  
-			if event.resourceName == CPU_PERCENT_NAME &&
349  
-				(event.duration.Seconds()/event.interval.Seconds()) <= 1 {
350  
-				return fmt.Errorf("Rule '%v' duration / interval must be greater "+
351  
-					"than 1.  It is '%+v / %+v'.", event.ruleString,
352  
-					event.duration.Seconds(), event.interval.Seconds())
353  
-			}
354  
-
355  
-			if math.Mod(durationRatio, 1.0) != 0.0 {
356  
-				return fmt.Errorf("Rule '%v' duration / interval must be an integer.",
357  
-					event.ruleString)
358  
-			}
359 416
 		}
360 417
 	}
  418
+	durationRatio := parsedEvent.duration.Seconds() /
  419
+		parsedEvent.interval.Seconds()
  420
+	if parsedEvent.resourceName == CPU_PERCENT_NAME &&
  421
+		(parsedEvent.duration.Seconds()/parsedEvent.interval.Seconds()) <= 1 {
  422
+		return fmt.Errorf("Rule '%v' duration / interval must be greater "+
  423
+			"than 1.  It is '%+v / %+v'.", parsedEvent.ruleString,
  424
+			parsedEvent.duration.Seconds(), parsedEvent.interval.Seconds())
  425
+	}
  426
+
  427
+	if math.Mod(durationRatio, 1.0) != 0.0 {
  428
+		return fmt.Errorf("Rule '%v' duration / interval must be an integer.",
  429
+			parsedEvent.ruleString)
  430
+	}
361 431
 	return nil
362 432
 }
363 433
 
@@ -388,3 +458,23 @@ func (e *EventMonitor) sendUnixSocketAlert(parsedEvent *ParsedEvent,
388 458
 	}
389 459
 	return nil
390 460
 }
  461
+
  462
+func (e *EventMonitor) CleanDataForProcess(p *Process) {
  463
+	e.resourceManager.CleanDataForProcess(p)
  464
+}
  465
+
  466
+func (e *EventMonitor) IsMonitoring(p *Process) bool {
  467
+	return e.control.IsMonitoring(p) && !p.IsMonitoringModeManual()
  468
+}
  469
+
  470
+func (e *EventMonitor) StartMonitoringProcess(p *Process) {
  471
+	e.CleanDataForProcess(p)
  472
+}
  473
+
  474
+func (e *EventMonitor) TriggerAlerts(p *Process) bool {
  475
+	return p.IsMonitoringModeActive() || p.IsMonitoringModePassive()
  476
+}
  477
+
  478
+func (e *EventMonitor) TriggerProcessActions(p *Process) bool {
  479
+	return p.IsMonitoringModeActive()
  480
+}
139  eventmonitor_test.go
@@ -15,6 +15,12 @@ func init() {
15 15
 	eventMonitor = EventMonitor{}
16 16
 }
17 17
 
  18
+func RegisterNewFakeControl() *FakeControl {
  19
+	fc := &FakeControl{}
  20
+	eventMonitor.registerControl(fc)
  21
+	return fc
  22
+}
  23
+
18 24
 func TestIsAnOperatorChar(t *testing.T) {
19 25
 	assert.Equal(t, true, isAnOperatorChar("="))
20 26
 	assert.Equal(t, true, isAnOperatorChar(">"))
@@ -132,7 +138,7 @@ func TestParseEvent(t *testing.T) {
132 138
 		Description: "The best rule ever!",
133 139
 	}
134 140
 	parsedEvent, err :=
135  
-		eventMonitor.parseEvent(&event, "GroupName", "ProcessName")
  141
+		eventMonitor.parseEvent(&event, "GroupName", "ProcessName", "alert")
136 142
 	if err != nil {
137 143
 		t.Fatal(err)
138 144
 	}
@@ -154,16 +160,143 @@ func TestParseBadIntervalEvents(t *testing.T) {
154 160
 		Interval:    "10s",
155 161
 		Description: "The best rule ever!",
156 162
 	}
157  
-	_, err := eventMonitor.parseEvent(&event1, "GroupName", "ProcessName")
  163
+	_, err := eventMonitor.parseEvent(&event1, "GroupName", "ProcessName",
  164
+		"alert")
158 165
 	if err != nil {
159 166
 		assert.Equal(t,
160 167
 			"Rule 'memory_used>2gb' duration / interval must be an integer.",
161 168
 			err.Error())
162 169
 	}
163  
-	_, err = eventMonitor.parseEvent(&event2, "GroupName", "ProcessName")
  170
+	_, err = eventMonitor.parseEvent(&event2, "GroupName", "ProcessName", "alert")
164 171
 	if err != nil {
165 172
 		assert.Equal(t,
166 173
 			"Rule 'cpu_percent>60' duration / interval must be greater than 1.  It "+
167 174
 				"is '10 / 10'.", err.Error())
168 175
 	}
169 176
 }
  177
+
  178
+type FakeControl struct {
  179
+	numDoActionCalled int
  180
+	lastActionCalled  int
  181
+	isMonitoring      bool
  182
+}
  183
+
  184
+func (fc *FakeControl) DoAction(name string, action int) error {
  185
+	fc.numDoActionCalled++
  186
+	fc.lastActionCalled = action
  187
+	return nil
  188
+}
  189
+
  190
+func (fc *FakeControl) IsMonitoring(process *Process) bool {
  191
+	return fc.isMonitoring
  192
+}
  193
+
  194
+func TestActionTriggers(t *testing.T) {
  195
+	fc := RegisterNewFakeControl()
  196
+	fc.isMonitoring = true
  197
+	eventMonitor.configManager = &ConfigManager{}
  198
+	eventMonitor.configManager.Settings = &Settings{}
  199
+	process := &Process{
  200
+		Name:        "ProcessName",
  201
+		MonitorMode: "active",
  202
+	}
  203
+	event := Event{
  204
+		Rule:        "memory_used>2mb",
  205
+		Duration:    "10s",
  206
+		Interval:    "10s",
  207
+		Description: "The best rule ever!",
  208
+	}
  209
+	parsedEvent, _ :=
  210
+		eventMonitor.parseEvent(&event, "GroupName", "ProcessName", "stop")
  211
+	err := eventMonitor.triggerAction(process, parsedEvent, 0)
  212
+	if err != nil {
  213
+		t.Fatal(err)
  214
+	}
  215
+	assert.Equal(t, 1, fc.numDoActionCalled)
  216
+	assert.Equal(t, ACTION_STOP, fc.lastActionCalled)
  217
+
  218
+	parsedEvent, _ =
  219
+		eventMonitor.parseEvent(&event, "GroupName", "ProcessName", "start")
  220
+	err = eventMonitor.triggerAction(process, parsedEvent, 0)
  221
+	if err != nil {
  222
+		t.Fatal(err)
  223
+	}
  224
+	assert.Equal(t, 2, fc.numDoActionCalled)
  225
+	assert.Equal(t, ACTION_START, fc.lastActionCalled)
  226
+
  227
+	parsedEvent, _ =
  228
+		eventMonitor.parseEvent(&event, "GroupName", "ProcessName", "restart")
  229
+	err = eventMonitor.triggerAction(process, parsedEvent, 0)
  230
+	if err != nil {
  231
+		t.Fatal(err)
  232
+	}
  233
+	assert.Equal(t, 3, fc.numDoActionCalled)
  234
+	assert.Equal(t, ACTION_RESTART, fc.lastActionCalled)
  235
+
  236
+	parsedEvent, _ =
  237
+		eventMonitor.parseEvent(&event, "GroupName", "ProcessName", "alert")
  238
+	err = eventMonitor.triggerAction(process, parsedEvent, 0)
  239
+	if err != nil {
  240
+		t.Fatal(err)
  241
+	}
  242
+	assert.Equal(t, 3, fc.numDoActionCalled)
  243
+
  244
+	_, err =
  245
+		eventMonitor.parseEvent(&event, "GroupName", "ProcessName", "doesntexist")
  246
+	assert.Equal(t, "No event action 'doesntexist' exists. Valid actions are "+
  247
+		"[stop, start, restart, alert].", err.Error())
  248
+	parsedEvent.action = "doesntexist"
  249
+	err = eventMonitor.triggerAction(process, parsedEvent, 0)
  250
+	assert.Equal(t, "No event action 'doesntexist' exists.", err.Error())
  251
+
  252
+	eventMonitor = EventMonitor{}
  253
+}
  254
+
  255
+func TestMonitoringModes(t *testing.T) {
  256
+	fc := RegisterNewFakeControl()
  257
+	fc.isMonitoring = true
  258
+	eventMonitor.configManager = &ConfigManager{}
  259
+	eventMonitor.configManager.Settings = &Settings{}
  260
+	process := &Process{
  261
+		Name:        "ProcessName",
  262
+		MonitorMode: "active",
  263
+	}
  264
+	event := Event{
  265
+		Rule:        "memory_used>2mb",
  266
+		Duration:    "10s",
  267
+		Interval:    "10s",
  268
+		Description: "The best rule ever!",
  269
+	}
  270
+	parsedEvent, _ :=
  271
+		eventMonitor.parseEvent(&event, "GroupName", "ProcessName", "stop")
  272
+	err := eventMonitor.triggerAction(process, parsedEvent, 0)
  273
+	if err != nil {
  274
+		t.Fatal(err)
  275
+	}
  276
+	assert.Equal(t, 1, fc.numDoActionCalled)
  277
+	assert.Equal(t, ACTION_STOP, fc.lastActionCalled)
  278
+
  279
+	// We shoudn't trigger the action in passive mode.
  280
+	process.MonitorMode = "passive"
  281
+	parsedEvent, _ =
  282
+		eventMonitor.parseEvent(&event, "GroupName", "ProcessName", "start")
  283
+	err = eventMonitor.triggerAction(process, parsedEvent, 0)
  284
+	if err != nil {
  285
+		t.Fatal(err)
  286
+	}
  287
+	assert.Equal(t, 1, fc.numDoActionCalled)
  288
+	assert.Equal(t, ACTION_STOP, fc.lastActionCalled)
  289
+
  290
+	// We shouldn't trigger the action in manual mode.
  291
+	process.MonitorMode = "manual"
  292
+	parsedEvent, _ =
  293
+		eventMonitor.parseEvent(&event, "GroupName", "ProcessName", "start")
  294
+	err = eventMonitor.triggerAction(process, parsedEvent, 0)
  295
+	if err != nil {
  296
+		t.Fatal(err)
  297
+	}
  298
+	assert.Equal(t, 1, fc.numDoActionCalled)
  299
+	assert.Equal(t, ACTION_STOP, fc.lastActionCalled)
  300
+
  301
+	eventMonitor = EventMonitor{}
  302
+}
24  gonit/main.go
@@ -34,8 +34,9 @@ var (
34 34
 	defaultRpcUrl  = filepath.Join(home, "."+name+".sock")
35 35
 
36 36
 	// internal
37  
-	api       *gonit.API
38  
-	rpcServer *gonit.RpcServer
  37
+	api          *gonit.API
  38
+	rpcServer    *gonit.RpcServer
  39
+	eventMonitor *gonit.EventMonitor
39 40
 )
40 41
 
41 42
 func main() {
@@ -56,11 +57,10 @@ func main() {
56 57
 	}
57 58
 
58 59
 	api = gonit.NewAPI(configManager)
59  
-
60 60
 	args := flag.Args()
61 61
 	if len(args) == 0 {
62 62
 		if polltime != 0 {
63  
-			runDaemon()
  63
+			runDaemon(api.Control, configManager)
64 64
 		} else {
65 65
 			log.Fatal("Nothing todo (yet)")
66 66
 		}
@@ -186,7 +186,7 @@ func wakeup() {
186 186
 
187 187
 func shutdown() {
188 188
 	log.Printf("Quit")
189  
-
  189
+	eventMonitor.Stop()
190 190
 	if rpcServer != nil {
191 191
 		rpcServer.Shutdown()
192 192
 	}
@@ -237,7 +237,7 @@ func isRunning() bool {
237 237
 	return err == nil && syscall.Kill(pid, 0) == nil
238 238
 }
239 239
 
240  
-func runDaemon() {
  240
+func runDaemon(control *gonit.Control, configManager *gonit.ConfigManager) {
241 241
 	if isRunning() {
242 242
 		log.Fatalf("%s daemon is already running", name)
243 243
 	}
@@ -252,11 +252,21 @@ func runDaemon() {
252 252
 		log.Fatal(err)
253 253
 	}
254 254
 	defer os.Remove(pidfile)
255  
-
  255
+	createEventMonitor(control, configManager)
256 256
 	start()
257 257
 	loop()
258 258
 }
259 259
 
  260
+func createEventMonitor(control *gonit.Control,
  261
+	configManager *gonit.ConfigManager) {
  262
+	eventMonitor = &gonit.EventMonitor{}
  263
+	err := eventMonitor.Start(configManager, control)
  264
+	if err != nil {
  265
+		log.Fatal(err)
  266
+	}
  267
+	control.RegisterEventMonitor(eventMonitor)
  268
+}
  269
+
260 270
 func showVersion() {
261 271
 	fmt.Printf("Gonit version %s\n", gonit.VERSION)
262 272
 }
45  resourcemanager.go
@@ -42,6 +42,9 @@ func (s *SigarGetter) getProcTime(pid int) (uint64, error) {
42 42
 type ResourceManager struct {
43 43
 	resourceHolders []*ResourceHolder
44 44
 	sigarInterface  SigarInterface
  45
+	// Used by eventmonitor to cache resources so they don't get pulled multiple
  46
+	// times when multiple rules are being checked for the same resource.
  47
+	cachedResources map[string]uint64
45 48
 }
46 49
 
47 50
 type ResourceHolder struct {
@@ -53,7 +56,8 @@ type ResourceHolder struct {
53 56
 }
54 57
 
55 58
 var resourceManager ResourceManager = ResourceManager{
56  
-	sigarInterface: &SigarGetter{},
  59
+	sigarInterface:  &SigarGetter{},
  60
+	cachedResources: map[string]uint64{},
57 61
 }
58 62
 
59 63
 type DataTimestamp struct {
@@ -79,6 +83,18 @@ var validResourceNames = map[string]bool{
79 83
 // Cleans data from ResourceManager.
80 84
 func (r *ResourceManager) CleanData() {
81 85
 	r.resourceHolders = []*ResourceHolder{}
  86
+	r.ClearCachedResources()
  87
+}
  88
+
  89
+// Cleans up the resource data used for a process's event monitors.
  90
+func (r *ResourceManager) CleanDataForProcess(p *Process) {
  91
+	for _, resourceHolder := range r.resourceHolders {
  92
+		if resourceHolder.processName == p.Name {
  93
+			resourceHolder.dataTimestamps = []*DataTimestamp{}
  94
+			resourceHolder.firstEntryIndex = 0
  95
+		}
  96
+	}
  97
+	r.ClearCachedResources()
82 98
 }
83 99
 
84 100
 // Get the nth entry in the data.  Accepts a negaitve number, as well, so that
@@ -140,9 +156,27 @@ func (r *ResourceManager) getResourceHolder(
140 156
 	}
141 157
 
142 158
 	resourceHolder.maxDataToStore = int64(math.Ceil(duration / interval))
  159
+	r.resourceHolders = append(r.resourceHolders, resourceHolder)
143 160
 	return resourceHolder
144 161
 }
145 162
 
  163
+// Sets an entry in the resources cache.
  164
+func (r *ResourceManager) setCachedResource(resourceName string, value uint64) {
  165
+	r.cachedResources[resourceName] = value
  166
+}
  167
+
  168
+// Gets an entry in the resources cache.
  169
+func (r *ResourceManager) getCachedResource(
  170
+	resourceName string) (uint64, bool) {
  171
+	value, has_key := r.cachedResources[resourceName]
  172
+	return value, has_key
  173
+}
  174
+
  175
+// Clears the resources cache.
  176
+func (r *ResourceManager) ClearCachedResources() {
  177
+	r.cachedResources = map[string]uint64{}
  178
+}
  179
+
146 180
 // Given a ParsedEvent, will populate the correct resourceHolder with the
147 181
 // resource value.
148 182
 func (r *ResourceManager) gatherResource(parsedEvent *ParsedEvent,
@@ -151,7 +185,6 @@ func (r *ResourceManager) gatherResource(parsedEvent *ParsedEvent,
151 185
 	if err := r.gather(pid, resourceHolder); err != nil {
152 186
 		return err
153 187
 	}
154  
-	r.resourceHolders = append(r.resourceHolders, resourceHolder)
155 188
 	return nil
156 189
 }
157 190
 
@@ -232,9 +265,16 @@ func (r *ResourceManager) GetResource(parsedEvent *ParsedEvent,
232 265
 	pid int) (uint64, error) {
233 266
 	resourceName := parsedEvent.resourceName
234 267
 	processName := parsedEvent.processName
  268
+
  269
+	data, has_key := r.getCachedResource(resourceName)
  270
+	if has_key {
  271
+		return data, nil
  272
+	}
  273
+
235 274
 	if err := r.gatherResource(parsedEvent, pid); err != nil {
236 275
 		return 0, err
237 276
 	}
  277
+
238 278
 	for _, resourceHolder := range r.resourceHolders {
239 279
 		lenResourceData := len(resourceHolder.dataTimestamps)
240 280
 		if resourceHolder.processName == processName &&
@@ -243,6 +283,7 @@ func (r *ResourceManager) GetResource(parsedEvent *ParsedEvent,
243 283
 			if err != nil {
244 284
 				return 0, err
245 285
 			}
  286
+			r.setCachedResource(resourceName, data)
246 287
 			return data, nil
247 288
 		}
248 289
 	}
31  resourcemanager_test.go
@@ -27,7 +27,10 @@ func (s *FakeSigarGetter) getProcTime(pid int) (uint64, error) {
27 27
 var r ResourceManager
28 28
 
29 29
 func Setup() {
30  
-	r = ResourceManager{sigarInterface: &SigarGetter{}}
  30
+	r = ResourceManager{
  31
+		sigarInterface:  &SigarGetter{},
  32
+		cachedResources: map[string]uint64{},
  33
+	}
31 34
 }
32 35
 
33 36
 func TestCalculateProcPercent(t *testing.T) {
@@ -93,6 +96,7 @@ func TestGatherProcPercent(t *testing.T) {
93 96
 	timeThen := r.resourceHolders[0].dataTimestamps[0].nanoTimestamp
94 97
 	// Set the time to be 1 second ago.
95 98
 	r.resourceHolders[0].dataTimestamps[0].nanoTimestamp = timeThen - 1000000000
  99
+	r.ClearCachedResources()
96 100
 	resourceVal, err = r.GetResource(pe, 1234)
97 101
 	if err != nil {
98 102
 		t.Fatal(err)
@@ -155,6 +159,7 @@ func TestCircularProcData(t *testing.T) {
155 159
 			procUsed: uint64(2886 + i),
156 160
 		}
157 161
 		r.SetSigarInterface(fsg)
  162
+		r.ClearCachedResources()
158 163
 		_, err := r.GetResource(pe, 1234)
159 164
 		if err != nil {
160 165
 			t.Fatal(err)
@@ -172,6 +177,7 @@ func TestCircularProcData(t *testing.T) {
172 177
 		procUsed: uint64(2897),
173 178
 	}
174 179