Skip to content

Commit 63b6f90

Browse files
DennisAhausSAPyuriadam-saprkoster
authored
Add ProcessLength to heartbeat event (#390)
* Add ProcessLength to heartbeat event This change extends the heartbeat event payload to include ProcessLength, which represents the current number of running processes on the VM. By including process count in heartbeats, the bosh-monitor can now align its behavior with the bosh-cli 'vms' command output. This allows the monitor to react to process state changes in real-time without requiring separate get_state RPC calls * changed ProcessLength to NumberOfProcesses * changed NumberOfProcesses to pointer * changed some variable names * changed error handling when fetching processes * adjusted agent test * Added the error log if fetching processes fails Co-authored-by: Ruben Koster <hi@rkoster.dev> * fixed lint test --------- Co-authored-by: I766702 <yuri.adam@sap.com> Co-authored-by: Ruben Koster <hi@rkoster.dev>
1 parent c262c69 commit 63b6f90

File tree

4 files changed

+114
-22
lines changed

4 files changed

+114
-22
lines changed

agent/agent.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,23 @@ func (a Agent) getHeartbeat(status string) (Heartbeat, error) {
183183
return Heartbeat{}, bosherr.WrapError(err, "Getting job spec")
184184
}
185185

186+
var numberOfProcesses *int
187+
processes, err := a.jobSupervisor.Processes()
188+
if err != nil {
189+
a.logger.Debug(agentLogTag, "Failed to get processes for heartbeat: %s", err.Error())
190+
} else {
191+
n := len(processes)
192+
numberOfProcesses = &n
193+
}
186194
hb := Heartbeat{
187-
Deployment: spec.Deployment,
188-
Job: spec.JobSpec.Name,
189-
Index: spec.Index,
190-
JobState: status,
191-
Vitals: vitals,
192-
NodeID: spec.NodeID,
195+
Deployment: spec.Deployment,
196+
Job: spec.JobSpec.Name,
197+
Index: spec.Index,
198+
JobState: status,
199+
Vitals: vitals,
200+
NodeID: spec.NodeID,
201+
NumberOfProcesses: numberOfProcesses,
193202
}
194-
195203
return hb, nil
196204
}
197205

agent/agent_test.go

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
fakeas "github.com/cloudfoundry/bosh-agent/v2/agent/applier/applyspec/fakes"
1919
fakeagent "github.com/cloudfoundry/bosh-agent/v2/agent/fakes"
2020
boshhandler "github.com/cloudfoundry/bosh-agent/v2/handler"
21+
boshjobsuper "github.com/cloudfoundry/bosh-agent/v2/jobsupervisor"
2122
fakejobsuper "github.com/cloudfoundry/bosh-agent/v2/jobsupervisor/fakes"
2223
fakembus "github.com/cloudfoundry/bosh-agent/v2/mbus/fakes"
2324
"github.com/cloudfoundry/bosh-agent/v2/platform/platformfakes"
@@ -125,6 +126,11 @@ func init() { //nolint:funlen,gochecknoinits
125126
}
126127

127128
jobSupervisor.StatusStatus = "fake-state"
129+
jobSupervisor.ProcessesStatus = []boshjobsuper.Process{
130+
{Name: "process1", State: "running"},
131+
{Name: "process2", State: "running"},
132+
{Name: "process3", State: "stopped"},
133+
}
128134

129135
vitalService.GetReturns(boshvitals.Vitals{
130136
Load: []string{"a", "b", "c"},
@@ -134,13 +140,15 @@ func init() { //nolint:funlen,gochecknoinits
134140
expectedJobName := "fake-job"
135141
expectedJobIndex := 1
136142
expectedNodeID := "node-id"
143+
expectedNumberOfProcesses := 3
137144
expectedHb := agent.Heartbeat{
138-
Deployment: "FakeDeployment",
139-
Job: &expectedJobName,
140-
Index: &expectedJobIndex,
141-
JobState: "fake-state",
142-
NodeID: expectedNodeID,
143-
Vitals: boshvitals.Vitals{Load: []string{"a", "b", "c"}},
145+
Deployment: "FakeDeployment",
146+
Job: &expectedJobName,
147+
Index: &expectedJobIndex,
148+
JobState: "fake-state",
149+
NodeID: expectedNodeID,
150+
Vitals: boshvitals.Vitals{Load: []string{"a", "b", "c"}},
151+
NumberOfProcesses: &expectedNumberOfProcesses,
144152
}
145153

146154
It("sends initial heartbeat", func() {
@@ -247,6 +255,55 @@ func init() { //nolint:funlen,gochecknoinits
247255
})
248256
})
249257

258+
Context("when the boshAgent fails to get processes for a heartbeat", func() {
259+
BeforeEach(func() {
260+
jobName := "fake-job"
261+
nodeID := "node-id"
262+
jobIndex := 1
263+
specService.Spec = boshas.V1ApplySpec{
264+
Deployment: "FakeDeployment",
265+
JobSpec: boshas.JobSpec{Name: &jobName},
266+
Index: &jobIndex,
267+
NodeID: nodeID,
268+
}
269+
270+
jobSupervisor.StatusStatus = "fake-state"
271+
jobSupervisor.ProcessesError = errors.New("fake-processes-error")
272+
273+
vitalService.GetReturns(boshvitals.Vitals{
274+
Load: []string{"a", "b", "c"},
275+
}, nil)
276+
277+
handler.KeepOnRunning()
278+
handler.SendErr = errors.New("stop")
279+
})
280+
281+
It("sends heartbeat with NumberOfProcesses as nil", func() {
282+
err := boshAgent.Run()
283+
Expect(err).To(HaveOccurred())
284+
Expect(err.Error()).To(ContainSubstring("stop"))
285+
Expect(err.Error()).ToNot(ContainSubstring("fake-processes-error"))
286+
287+
expectedJobName := "fake-job"
288+
expectedJobIndex := 1
289+
expectedHb := agent.Heartbeat{
290+
Deployment: "FakeDeployment",
291+
Job: &expectedJobName,
292+
Index: &expectedJobIndex,
293+
JobState: "fake-state",
294+
NodeID: "node-id",
295+
Vitals: boshvitals.Vitals{Load: []string{"a", "b", "c"}},
296+
NumberOfProcesses: nil,
297+
}
298+
299+
Expect(handler.SendInputs()).To(ContainElement(fakembus.SendInput{
300+
Target: boshhandler.HealthMonitor,
301+
Topic: boshhandler.Heartbeat,
302+
Message: expectedHb,
303+
}))
304+
})
305+
})
306+
250307
It("sends job monitoring alerts to health manager", func() {
251308
handler.KeepOnRunning()
252309

agent/heartbeat.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import (
88
// https://www.pivotaltracker.com/story/show/132265151
99

1010
type Heartbeat struct {
11-
Deployment string `json:"deployment"`
12-
Job *string `json:"job"`
13-
Index *int `json:"index"`
14-
JobState string `json:"job_state"`
15-
Vitals boshvitals.Vitals `json:"vitals"`
16-
NodeID string `json:"node_id"`
11+
Deployment string `json:"deployment"`
12+
Job *string `json:"job"`
13+
Index *int `json:"index"`
14+
JobState string `json:"job_state"`
15+
Vitals boshvitals.Vitals `json:"vitals"`
16+
NodeID string `json:"node_id"`
17+
NumberOfProcesses *int `json:"number_of_processes"`
1718
}
1819

1920
// Heartbeat payload example:

agent/heartbeat_test.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func init() { //nolint:gochecknoinits
1616
It("serializes heartbeat with all fields", func() {
1717
name := "foo"
1818
index := 0
19+
numberOfProcesses := 3
1920

2021
hb := Heartbeat{
2122
Deployment: "FakeDeployment",
@@ -29,10 +30,11 @@ func init() { //nolint:gochecknoinits
2930
"persistent": boshvitals.SpecificDiskVitals{},
3031
},
3132
},
32-
NodeID: "node-id",
33+
NodeID: "node-id",
34+
NumberOfProcesses: &numberOfProcesses,
3335
}
3436

35-
expectedJSON := `{"deployment":"FakeDeployment","job":"foo","index":0,"job_state":"running","vitals":{"cpu":{},"disk":{"ephemeral":{},"persistent":{},"system":{}},"mem":{},"swap":{},"uptime":{}},"node_id":"node-id"}`
37+
expectedJSON := `{"deployment":"FakeDeployment","job":"foo","index":0,"job_state":"running","vitals":{"cpu":{},"disk":{"ephemeral":{},"persistent":{},"system":{}},"mem":{},"swap":{},"uptime":{}},"node_id":"node-id","number_of_processes":3}`
3638

3739
hbBytes, err := json.Marshal(hb)
3840
Expect(err).ToNot(HaveOccurred())
@@ -42,6 +44,30 @@ func init() { //nolint:gochecknoinits
4244

4345
Context("when job name, index are not available", func() {
4446
It("serializes job name and index as nulls to indicate that there is no job assigned to this agent", func() {
47+
numberOfProcesses := 0
48+
49+
hb := Heartbeat{
50+
Deployment: "FakeDeployment",
51+
JobState: "running",
52+
Vitals: boshvitals.Vitals{
53+
Disk: boshvitals.DiskVitals{
54+
"system": boshvitals.SpecificDiskVitals{},
55+
"ephemeral": boshvitals.SpecificDiskVitals{},
56+
"persistent": boshvitals.SpecificDiskVitals{},
57+
},
58+
},
59+
NodeID: "node-id",
60+
NumberOfProcesses: &numberOfProcesses,
61+
}
62+
63+
expectedJSON := `{"deployment":"FakeDeployment","job":null,"index":null,"job_state":"running","vitals":{"cpu":{},"disk":{"ephemeral":{},"persistent":{},"system":{}},"mem":{},"swap":{},"uptime":{}},"node_id":"node-id","number_of_processes":0}`
64+
65+
hbBytes, err := json.Marshal(hb)
66+
Expect(err).ToNot(HaveOccurred())
67+
Expect(string(hbBytes)).To(Equal(expectedJSON))
68+
})
69+
70+
It("serializes NumberOfProcesses as null when not available", func() {
4571
hb := Heartbeat{
4672
Deployment: "FakeDeployment",
4773
JobState: "running",
@@ -55,7 +81,7 @@ func init() { //nolint:gochecknoinits
5581
NodeID: "node-id",
5682
}
5783

58-
expectedJSON := `{"deployment":"FakeDeployment","job":null,"index":null,"job_state":"running","vitals":{"cpu":{},"disk":{"ephemeral":{},"persistent":{},"system":{}},"mem":{},"swap":{},"uptime":{}},"node_id":"node-id"}`
84+
expectedJSON := `{"deployment":"FakeDeployment","job":null,"index":null,"job_state":"running","vitals":{"cpu":{},"disk":{"ephemeral":{},"persistent":{},"system":{}},"mem":{},"swap":{},"uptime":{}},"node_id":"node-id","number_of_processes":null}`
5985

6086
hbBytes, err := json.Marshal(hb)
6187
Expect(err).ToNot(HaveOccurred())

0 commit comments

Comments
 (0)