@checkstack/healthcheck-jenkins-backend 0.3.10 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/package.json +3 -3
- package/src/collectors/build-history.test.ts +2 -2
- package/src/collectors/build-history.ts +24 -0
- package/src/collectors/job-status.test.ts +2 -1
- package/src/collectors/job-status.ts +18 -0
- package/src/collectors/node-health.test.ts +4 -2
- package/src/collectors/node-health.ts +24 -0
- package/src/collectors/queue-info.test.ts +4 -4
- package/src/collectors/queue-info.ts +20 -0
- package/src/collectors/server-info.ts +13 -0
- package/src/index.ts +1 -1
- package/src/strategy.ts +14 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @checkstack/healthcheck-jenkins-backend
|
|
2
2
|
|
|
3
|
+
## 0.3.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8d1ef12: ## Downstream consumer bumps for the anomaly detection + cache system rollout
|
|
8
|
+
|
|
9
|
+
Packages on this branch were updated as part of the anomaly detection feature (schema annotations on result fields, plugin metadata for the modular cache system) but were not listed in the upstream changesets.
|
|
10
|
+
|
|
11
|
+
- **`@checkstack/healthcheck-common`** (minor) — new RPC contract additions and schema changes supporting per-field anomaly metadata.
|
|
12
|
+
- **`@checkstack/cache-memory-common`** (minor) — new package providing access rules + plugin metadata for the in-memory cache backend.
|
|
13
|
+
- **healthcheck plugins** (patch) — adopt the new `x-anomaly-*` schema annotations on their result fields so anomaly detection works automatically against their checks. No public API changes.
|
|
14
|
+
- **integration / notification / auth / queue / collector plugins** (patch) — minor internal updates as consumers of upstream API changes (cache plugin registry, schema additions). No public API changes.
|
|
15
|
+
|
|
16
|
+
- Updated dependencies [8d1ef12]
|
|
17
|
+
- Updated dependencies [8d1ef12]
|
|
18
|
+
- Updated dependencies [8d1ef12]
|
|
19
|
+
- @checkstack/healthcheck-common@0.12.0
|
|
20
|
+
- @checkstack/common@0.7.0
|
|
21
|
+
- @checkstack/backend-api@0.13.0
|
|
22
|
+
|
|
3
23
|
## 0.3.10
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkstack/healthcheck-jenkins-backend",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"checkstack": {
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"test": "bun test"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@checkstack/backend-api": "0.
|
|
16
|
+
"@checkstack/backend-api": "0.12.0",
|
|
17
17
|
"@checkstack/common": "0.6.5",
|
|
18
|
-
"@checkstack/healthcheck-common": "0.
|
|
18
|
+
"@checkstack/healthcheck-common": "0.11.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/bun": "^1.0.0",
|
|
@@ -9,7 +9,7 @@ describe("BuildHistoryCollector", () => {
|
|
|
9
9
|
const collector = new BuildHistoryCollector();
|
|
10
10
|
|
|
11
11
|
const createMockClient = (
|
|
12
|
-
response: JenkinsResponse
|
|
12
|
+
response: JenkinsResponse,
|
|
13
13
|
): JenkinsTransportClient => ({
|
|
14
14
|
exec: async () => response,
|
|
15
15
|
});
|
|
@@ -99,7 +99,7 @@ describe("BuildHistoryCollector", () => {
|
|
|
99
99
|
];
|
|
100
100
|
|
|
101
101
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
102
|
-
|
|
102
|
+
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
103
103
|
|
|
104
104
|
expect(aggregated.avgSuccessRate.avg).toBe(70);
|
|
105
105
|
expect(aggregated.avgBuildDuration.avg).toBe(70000);
|
|
@@ -41,50 +41,70 @@ const buildHistoryResultSchema = z.object({
|
|
|
41
41
|
totalBuilds: healthResultNumber({
|
|
42
42
|
"x-chart-type": "counter",
|
|
43
43
|
"x-chart-label": "Total Builds",
|
|
44
|
+
"x-anomaly-enabled": true,
|
|
45
|
+
"x-anomaly-direction": "deviation",
|
|
44
46
|
}),
|
|
45
47
|
successCount: healthResultNumber({
|
|
46
48
|
"x-chart-type": "counter",
|
|
47
49
|
"x-chart-label": "Successful",
|
|
50
|
+
"x-anomaly-enabled": true,
|
|
51
|
+
"x-anomaly-direction": "higher-is-better",
|
|
48
52
|
}),
|
|
49
53
|
failureCount: healthResultNumber({
|
|
50
54
|
"x-chart-type": "counter",
|
|
51
55
|
"x-chart-label": "Failed",
|
|
56
|
+
"x-anomaly-enabled": true,
|
|
57
|
+
"x-anomaly-direction": "lower-is-better",
|
|
52
58
|
}),
|
|
53
59
|
unstableCount: healthResultNumber({
|
|
54
60
|
"x-chart-type": "counter",
|
|
55
61
|
"x-chart-label": "Unstable",
|
|
62
|
+
"x-anomaly-enabled": true,
|
|
63
|
+
"x-anomaly-direction": "lower-is-better",
|
|
56
64
|
}),
|
|
57
65
|
abortedCount: healthResultNumber({
|
|
58
66
|
"x-chart-type": "counter",
|
|
59
67
|
"x-chart-label": "Aborted",
|
|
68
|
+
"x-anomaly-enabled": true,
|
|
69
|
+
"x-anomaly-direction": "lower-is-better",
|
|
60
70
|
}),
|
|
61
71
|
successRate: healthResultNumber({
|
|
62
72
|
"x-chart-type": "gauge",
|
|
63
73
|
"x-chart-label": "Success Rate",
|
|
64
74
|
"x-chart-unit": "%",
|
|
75
|
+
"x-anomaly-enabled": true,
|
|
76
|
+
"x-anomaly-direction": "higher-is-better",
|
|
65
77
|
}),
|
|
66
78
|
avgDurationMs: healthResultNumber({
|
|
67
79
|
"x-chart-type": "line",
|
|
68
80
|
"x-chart-label": "Avg Duration",
|
|
69
81
|
"x-chart-unit": "ms",
|
|
82
|
+
"x-anomaly-enabled": true,
|
|
83
|
+
"x-anomaly-direction": "lower-is-better",
|
|
70
84
|
}),
|
|
71
85
|
minDurationMs: healthResultNumber({
|
|
72
86
|
"x-chart-type": "line",
|
|
73
87
|
"x-chart-label": "Min Duration",
|
|
74
88
|
"x-chart-unit": "ms",
|
|
89
|
+
"x-anomaly-enabled": true,
|
|
90
|
+
"x-anomaly-direction": "lower-is-better",
|
|
75
91
|
}),
|
|
76
92
|
maxDurationMs: healthResultNumber({
|
|
77
93
|
"x-chart-type": "line",
|
|
78
94
|
"x-chart-label": "Max Duration",
|
|
79
95
|
"x-chart-unit": "ms",
|
|
96
|
+
"x-anomaly-enabled": true,
|
|
97
|
+
"x-anomaly-direction": "lower-is-better",
|
|
80
98
|
}),
|
|
81
99
|
lastSuccessBuildNumber: healthResultNumber({
|
|
82
100
|
"x-chart-type": "counter",
|
|
83
101
|
"x-chart-label": "Last Success #",
|
|
102
|
+
"x-anomaly-enabled": false,
|
|
84
103
|
}).optional(),
|
|
85
104
|
lastFailureBuildNumber: healthResultNumber({
|
|
86
105
|
"x-chart-type": "counter",
|
|
87
106
|
"x-chart-label": "Last Failure #",
|
|
107
|
+
"x-anomaly-enabled": false,
|
|
88
108
|
}).optional(),
|
|
89
109
|
});
|
|
90
110
|
|
|
@@ -96,11 +116,15 @@ const buildHistoryAggregatedFields = {
|
|
|
96
116
|
"x-chart-type": "gauge",
|
|
97
117
|
"x-chart-label": "Avg Success Rate",
|
|
98
118
|
"x-chart-unit": "%",
|
|
119
|
+
"x-anomaly-enabled": true,
|
|
120
|
+
"x-anomaly-direction": "higher-is-better",
|
|
99
121
|
}),
|
|
100
122
|
avgBuildDuration: aggregatedAverage({
|
|
101
123
|
"x-chart-type": "line",
|
|
102
124
|
"x-chart-label": "Avg Build Duration",
|
|
103
125
|
"x-chart-unit": "ms",
|
|
126
|
+
"x-anomaly-enabled": true,
|
|
127
|
+
"x-anomaly-direction": "lower-is-better",
|
|
104
128
|
}),
|
|
105
129
|
};
|
|
106
130
|
|
|
@@ -70,7 +70,8 @@ describe("JobStatusCollector", () => {
|
|
|
70
70
|
pluginId: "healthcheck-jenkins",
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
// Failed builds are reported in result metrics, not as collector errors
|
|
74
|
+
expect(result.error).toBeUndefined();
|
|
74
75
|
expect(result.result.lastBuildResult).toBe("FAILURE");
|
|
75
76
|
});
|
|
76
77
|
|
|
@@ -44,40 +44,52 @@ const jobStatusResultSchema = z.object({
|
|
|
44
44
|
jobName: healthResultString({
|
|
45
45
|
"x-chart-type": "text",
|
|
46
46
|
"x-chart-label": "Job Name",
|
|
47
|
+
"x-anomaly-enabled": false,
|
|
47
48
|
}),
|
|
48
49
|
buildable: healthResultBoolean({
|
|
49
50
|
"x-chart-type": "boolean",
|
|
50
51
|
"x-chart-label": "Buildable",
|
|
52
|
+
"x-anomaly-enabled": true,
|
|
53
|
+
"x-anomaly-direction": "dominance",
|
|
51
54
|
}),
|
|
52
55
|
lastBuildNumber: healthResultNumber({
|
|
53
56
|
"x-chart-type": "counter",
|
|
54
57
|
"x-chart-label": "Last Build #",
|
|
58
|
+
"x-anomaly-enabled": false,
|
|
55
59
|
}).optional(),
|
|
56
60
|
lastBuildResult: healthResultString({
|
|
57
61
|
"x-chart-type": "text",
|
|
58
62
|
"x-chart-label": "Last Build Result",
|
|
63
|
+
"x-anomaly-enabled": false,
|
|
59
64
|
}).optional(),
|
|
60
65
|
lastBuildDurationMs: healthResultNumber({
|
|
61
66
|
"x-chart-type": "line",
|
|
62
67
|
"x-chart-label": "Build Duration",
|
|
63
68
|
"x-chart-unit": "ms",
|
|
69
|
+
"x-anomaly-enabled": true,
|
|
70
|
+
"x-anomaly-direction": "lower-is-better",
|
|
64
71
|
}).optional(),
|
|
65
72
|
lastBuildTimestamp: healthResultNumber({
|
|
66
73
|
"x-chart-type": "counter",
|
|
67
74
|
"x-chart-label": "Last Build Time",
|
|
75
|
+
"x-anomaly-enabled": false,
|
|
68
76
|
}).optional(),
|
|
69
77
|
timeSinceLastBuildMs: healthResultNumber({
|
|
70
78
|
"x-chart-type": "line",
|
|
71
79
|
"x-chart-label": "Time Since Last Build",
|
|
72
80
|
"x-chart-unit": "ms",
|
|
81
|
+
"x-anomaly-enabled": false,
|
|
73
82
|
}).optional(),
|
|
74
83
|
inQueue: healthResultBoolean({
|
|
75
84
|
"x-chart-type": "boolean",
|
|
76
85
|
"x-chart-label": "In Queue",
|
|
86
|
+
"x-anomaly-enabled": true,
|
|
87
|
+
"x-anomaly-direction": "dominance",
|
|
77
88
|
}),
|
|
78
89
|
color: healthResultString({
|
|
79
90
|
"x-chart-type": "text",
|
|
80
91
|
"x-chart-label": "Status Color",
|
|
92
|
+
"x-anomaly-enabled": false,
|
|
81
93
|
}),
|
|
82
94
|
});
|
|
83
95
|
|
|
@@ -89,16 +101,22 @@ const jobStatusAggregatedFields = {
|
|
|
89
101
|
"x-chart-type": "line",
|
|
90
102
|
"x-chart-label": "Avg Build Duration",
|
|
91
103
|
"x-chart-unit": "ms",
|
|
104
|
+
"x-anomaly-enabled": true,
|
|
105
|
+
"x-anomaly-direction": "lower-is-better",
|
|
92
106
|
}),
|
|
93
107
|
successRate: aggregatedRate({
|
|
94
108
|
"x-chart-type": "gauge",
|
|
95
109
|
"x-chart-label": "Success Rate",
|
|
96
110
|
"x-chart-unit": "%",
|
|
111
|
+
"x-anomaly-enabled": true,
|
|
112
|
+
"x-anomaly-direction": "higher-is-better",
|
|
97
113
|
}),
|
|
98
114
|
buildableRate: aggregatedRate({
|
|
99
115
|
"x-chart-type": "gauge",
|
|
100
116
|
"x-chart-label": "Enabled Rate",
|
|
101
117
|
"x-chart-unit": "%",
|
|
118
|
+
"x-anomaly-enabled": true,
|
|
119
|
+
"x-anomaly-direction": "higher-is-better",
|
|
102
120
|
}),
|
|
103
121
|
};
|
|
104
122
|
|
|
@@ -107,7 +107,9 @@ describe("NodeHealthCollector", () => {
|
|
|
107
107
|
|
|
108
108
|
expect(result.result.offlineNodes).toBe(1);
|
|
109
109
|
expect(result.result.nodeOffline).toBe(true);
|
|
110
|
-
expect(result.
|
|
110
|
+
expect(result.result.nodeOfflineReason).toBe("Connection lost");
|
|
111
|
+
// Offline status is reported in result metrics, not as an error
|
|
112
|
+
expect(result.error).toBeUndefined();
|
|
111
113
|
});
|
|
112
114
|
|
|
113
115
|
it("should aggregate correctly", () => {
|
|
@@ -141,7 +143,7 @@ describe("NodeHealthCollector", () => {
|
|
|
141
143
|
];
|
|
142
144
|
|
|
143
145
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
144
|
-
|
|
146
|
+
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
145
147
|
|
|
146
148
|
expect(aggregated.avgOnlineNodes.avg).toBe(4);
|
|
147
149
|
expect(aggregated.avgUtilization.avg).toBe(60);
|
|
@@ -40,44 +40,62 @@ const nodeHealthResultSchema = z.object({
|
|
|
40
40
|
totalNodes: healthResultNumber({
|
|
41
41
|
"x-chart-type": "counter",
|
|
42
42
|
"x-chart-label": "Total Nodes",
|
|
43
|
+
"x-anomaly-enabled": true,
|
|
44
|
+
"x-anomaly-direction": "deviation",
|
|
43
45
|
}),
|
|
44
46
|
onlineNodes: healthResultNumber({
|
|
45
47
|
"x-chart-type": "counter",
|
|
46
48
|
"x-chart-label": "Online Nodes",
|
|
49
|
+
"x-anomaly-enabled": true,
|
|
50
|
+
"x-anomaly-direction": "higher-is-better",
|
|
47
51
|
}),
|
|
48
52
|
offlineNodes: healthResultNumber({
|
|
49
53
|
"x-chart-type": "counter",
|
|
50
54
|
"x-chart-label": "Offline Nodes",
|
|
55
|
+
"x-anomaly-enabled": true,
|
|
56
|
+
"x-anomaly-direction": "lower-is-better",
|
|
51
57
|
}),
|
|
52
58
|
busyExecutors: healthResultNumber({
|
|
53
59
|
"x-chart-type": "counter",
|
|
54
60
|
"x-chart-label": "Busy Executors",
|
|
61
|
+
"x-anomaly-enabled": true,
|
|
62
|
+
"x-anomaly-direction": "deviation",
|
|
55
63
|
}),
|
|
56
64
|
idleExecutors: healthResultNumber({
|
|
57
65
|
"x-chart-type": "counter",
|
|
58
66
|
"x-chart-label": "Idle Executors",
|
|
67
|
+
"x-anomaly-enabled": true,
|
|
68
|
+
"x-anomaly-direction": "deviation",
|
|
59
69
|
}),
|
|
60
70
|
totalExecutors: healthResultNumber({
|
|
61
71
|
"x-chart-type": "counter",
|
|
62
72
|
"x-chart-label": "Total Executors",
|
|
73
|
+
"x-anomaly-enabled": true,
|
|
74
|
+
"x-anomaly-direction": "deviation",
|
|
63
75
|
}),
|
|
64
76
|
executorUtilization: healthResultNumber({
|
|
65
77
|
"x-chart-type": "gauge",
|
|
66
78
|
"x-chart-label": "Executor Utilization",
|
|
67
79
|
"x-chart-unit": "%",
|
|
80
|
+
"x-anomaly-enabled": true,
|
|
81
|
+
"x-anomaly-direction": "lower-is-better",
|
|
68
82
|
}),
|
|
69
83
|
// For single node mode
|
|
70
84
|
nodeDisplayName: healthResultString({
|
|
71
85
|
"x-chart-type": "text",
|
|
72
86
|
"x-chart-label": "Node Name",
|
|
87
|
+
"x-anomaly-enabled": false,
|
|
73
88
|
}).optional(),
|
|
74
89
|
nodeOffline: healthResultBoolean({
|
|
75
90
|
"x-chart-type": "boolean",
|
|
76
91
|
"x-chart-label": "Node Offline",
|
|
92
|
+
"x-anomaly-enabled": true,
|
|
93
|
+
"x-anomaly-direction": "dominance",
|
|
77
94
|
}).optional(),
|
|
78
95
|
nodeOfflineReason: healthResultString({
|
|
79
96
|
"x-chart-type": "text",
|
|
80
97
|
"x-chart-label": "Offline Reason",
|
|
98
|
+
"x-anomaly-enabled": false,
|
|
81
99
|
}).optional(),
|
|
82
100
|
});
|
|
83
101
|
|
|
@@ -88,15 +106,21 @@ const nodeHealthAggregatedFields = {
|
|
|
88
106
|
avgOnlineNodes: aggregatedAverage({
|
|
89
107
|
"x-chart-type": "line",
|
|
90
108
|
"x-chart-label": "Avg Online Nodes",
|
|
109
|
+
"x-anomaly-enabled": true,
|
|
110
|
+
"x-anomaly-direction": "higher-is-better",
|
|
91
111
|
}),
|
|
92
112
|
avgUtilization: aggregatedAverage({
|
|
93
113
|
"x-chart-type": "gauge",
|
|
94
114
|
"x-chart-label": "Avg Utilization",
|
|
95
115
|
"x-chart-unit": "%",
|
|
116
|
+
"x-anomaly-enabled": true,
|
|
117
|
+
"x-anomaly-direction": "lower-is-better",
|
|
96
118
|
}),
|
|
97
119
|
minOnlineNodes: aggregatedMinMax({
|
|
98
120
|
"x-chart-type": "line",
|
|
99
121
|
"x-chart-label": "Min Online Nodes",
|
|
122
|
+
"x-anomaly-enabled": true,
|
|
123
|
+
"x-anomaly-direction": "higher-is-better",
|
|
100
124
|
}),
|
|
101
125
|
};
|
|
102
126
|
|
|
@@ -9,7 +9,7 @@ describe("QueueInfoCollector", () => {
|
|
|
9
9
|
const collector = new QueueInfoCollector();
|
|
10
10
|
|
|
11
11
|
const createMockClient = (
|
|
12
|
-
response: JenkinsResponse
|
|
12
|
+
response: JenkinsResponse,
|
|
13
13
|
): JenkinsTransportClient => ({
|
|
14
14
|
exec: async () => response,
|
|
15
15
|
});
|
|
@@ -56,8 +56,8 @@ describe("QueueInfoCollector", () => {
|
|
|
56
56
|
expect(result.result.buildableCount).toBe(2);
|
|
57
57
|
expect(result.result.stuckCount).toBe(1);
|
|
58
58
|
expect(result.result.oldestWaitingMs).toBeGreaterThanOrEqual(30000);
|
|
59
|
-
//
|
|
60
|
-
expect(result.error).
|
|
59
|
+
// Non-critical status (stuck items) is reported in result metrics, not as an error
|
|
60
|
+
expect(result.error).toBeUndefined();
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
it("should report no error for empty queue", async () => {
|
|
@@ -105,7 +105,7 @@ describe("QueueInfoCollector", () => {
|
|
|
105
105
|
];
|
|
106
106
|
|
|
107
107
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
108
|
-
|
|
108
|
+
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
109
109
|
|
|
110
110
|
expect(aggregated.avgQueueLength.avg).toBe(4);
|
|
111
111
|
expect(aggregated.maxQueueLength.max).toBe(5);
|
|
@@ -31,28 +31,42 @@ const queueInfoResultSchema = z.object({
|
|
|
31
31
|
queueLength: healthResultNumber({
|
|
32
32
|
"x-chart-type": "counter",
|
|
33
33
|
"x-chart-label": "Queue Length",
|
|
34
|
+
"x-anomaly-enabled": true,
|
|
35
|
+
"x-anomaly-direction": "lower-is-better",
|
|
36
|
+
"x-anomaly-sensitivity": 2,
|
|
37
|
+
"x-anomaly-confirmation-window": 4,
|
|
34
38
|
}),
|
|
35
39
|
blockedCount: healthResultNumber({
|
|
36
40
|
"x-chart-type": "counter",
|
|
37
41
|
"x-chart-label": "Blocked Items",
|
|
42
|
+
"x-anomaly-enabled": true,
|
|
43
|
+
"x-anomaly-direction": "lower-is-better",
|
|
38
44
|
}),
|
|
39
45
|
buildableCount: healthResultNumber({
|
|
40
46
|
"x-chart-type": "counter",
|
|
41
47
|
"x-chart-label": "Buildable Items",
|
|
48
|
+
"x-anomaly-enabled": true,
|
|
49
|
+
"x-anomaly-direction": "deviation",
|
|
42
50
|
}),
|
|
43
51
|
stuckCount: healthResultNumber({
|
|
44
52
|
"x-chart-type": "counter",
|
|
45
53
|
"x-chart-label": "Stuck Items",
|
|
54
|
+
"x-anomaly-enabled": true,
|
|
55
|
+
"x-anomaly-direction": "lower-is-better",
|
|
46
56
|
}),
|
|
47
57
|
oldestWaitingMs: healthResultNumber({
|
|
48
58
|
"x-chart-type": "line",
|
|
49
59
|
"x-chart-label": "Oldest Wait Time",
|
|
50
60
|
"x-chart-unit": "ms",
|
|
61
|
+
"x-anomaly-enabled": true,
|
|
62
|
+
"x-anomaly-direction": "lower-is-better",
|
|
51
63
|
}),
|
|
52
64
|
avgWaitingMs: healthResultNumber({
|
|
53
65
|
"x-chart-type": "line",
|
|
54
66
|
"x-chart-label": "Avg Wait Time",
|
|
55
67
|
"x-chart-unit": "ms",
|
|
68
|
+
"x-anomaly-enabled": true,
|
|
69
|
+
"x-anomaly-direction": "lower-is-better",
|
|
56
70
|
}),
|
|
57
71
|
});
|
|
58
72
|
|
|
@@ -63,15 +77,21 @@ const queueInfoAggregatedFields = {
|
|
|
63
77
|
avgQueueLength: aggregatedAverage({
|
|
64
78
|
"x-chart-type": "line",
|
|
65
79
|
"x-chart-label": "Avg Queue Length",
|
|
80
|
+
"x-anomaly-enabled": true,
|
|
81
|
+
"x-anomaly-direction": "lower-is-better",
|
|
66
82
|
}),
|
|
67
83
|
maxQueueLength: aggregatedMinMax({
|
|
68
84
|
"x-chart-type": "line",
|
|
69
85
|
"x-chart-label": "Max Queue Length",
|
|
86
|
+
"x-anomaly-enabled": true,
|
|
87
|
+
"x-anomaly-direction": "lower-is-better",
|
|
70
88
|
}),
|
|
71
89
|
avgWaitTime: aggregatedAverage({
|
|
72
90
|
"x-chart-type": "line",
|
|
73
91
|
"x-chart-label": "Avg Wait Time",
|
|
74
92
|
"x-chart-unit": "ms",
|
|
93
|
+
"x-anomaly-enabled": true,
|
|
94
|
+
"x-anomaly-direction": "lower-is-better",
|
|
75
95
|
}),
|
|
76
96
|
};
|
|
77
97
|
|
|
@@ -32,27 +32,36 @@ const serverInfoResultSchema = z.object({
|
|
|
32
32
|
jenkinsVersion: healthResultString({
|
|
33
33
|
"x-chart-type": "text",
|
|
34
34
|
"x-chart-label": "Jenkins Version",
|
|
35
|
+
"x-anomaly-enabled": false,
|
|
35
36
|
}),
|
|
36
37
|
mode: healthResultString({
|
|
37
38
|
"x-chart-type": "text",
|
|
38
39
|
"x-chart-label": "Server Mode",
|
|
40
|
+
"x-anomaly-enabled": false,
|
|
39
41
|
}),
|
|
40
42
|
numExecutors: healthResultNumber({
|
|
41
43
|
"x-chart-type": "counter",
|
|
42
44
|
"x-chart-label": "Executors",
|
|
45
|
+
"x-anomaly-enabled": true,
|
|
46
|
+
"x-anomaly-direction": "deviation",
|
|
43
47
|
}),
|
|
44
48
|
usableWorkers: healthResultNumber({
|
|
45
49
|
"x-chart-type": "counter",
|
|
46
50
|
"x-chart-label": "Usable Workers",
|
|
51
|
+
"x-anomaly-enabled": true,
|
|
52
|
+
"x-anomaly-direction": "deviation",
|
|
47
53
|
}),
|
|
48
54
|
totalJobs: healthResultNumber({
|
|
49
55
|
"x-chart-type": "counter",
|
|
50
56
|
"x-chart-label": "Total Jobs",
|
|
57
|
+
"x-anomaly-enabled": true,
|
|
58
|
+
"x-anomaly-direction": "deviation",
|
|
51
59
|
}),
|
|
52
60
|
uptime: healthResultNumber({
|
|
53
61
|
"x-chart-type": "line",
|
|
54
62
|
"x-chart-label": "Uptime",
|
|
55
63
|
"x-chart-unit": "hours",
|
|
64
|
+
"x-anomaly-enabled": false,
|
|
56
65
|
}).optional(),
|
|
57
66
|
});
|
|
58
67
|
|
|
@@ -63,10 +72,14 @@ const serverInfoAggregatedFields = {
|
|
|
63
72
|
avgExecutors: aggregatedAverage({
|
|
64
73
|
"x-chart-type": "line",
|
|
65
74
|
"x-chart-label": "Avg Executors",
|
|
75
|
+
"x-anomaly-enabled": true,
|
|
76
|
+
"x-anomaly-direction": "deviation",
|
|
66
77
|
}),
|
|
67
78
|
avgTotalJobs: aggregatedAverage({
|
|
68
79
|
"x-chart-type": "line",
|
|
69
80
|
"x-chart-label": "Avg Jobs",
|
|
81
|
+
"x-anomaly-enabled": true,
|
|
82
|
+
"x-anomaly-direction": "deviation",
|
|
70
83
|
}),
|
|
71
84
|
};
|
|
72
85
|
|
package/src/index.ts
CHANGED
|
@@ -33,7 +33,7 @@ export default createBackendPlugin({
|
|
|
33
33
|
collectorRegistry.register(new NodeHealthCollector());
|
|
34
34
|
|
|
35
35
|
logger.info(
|
|
36
|
-
"✅ Jenkins health check registered (strategy + 5 collectors)"
|
|
36
|
+
"✅ Jenkins health check registered (strategy + 5 collectors)",
|
|
37
37
|
);
|
|
38
38
|
},
|
|
39
39
|
});
|
package/src/strategy.ts
CHANGED
|
@@ -57,15 +57,22 @@ const jenkinsResultSchema = healthResultSchema({
|
|
|
57
57
|
connected: healthResultBoolean({
|
|
58
58
|
"x-chart-type": "boolean",
|
|
59
59
|
"x-chart-label": "Connected",
|
|
60
|
+
"x-anomaly-enabled": true,
|
|
61
|
+
"x-anomaly-direction": "dominance",
|
|
60
62
|
}),
|
|
61
63
|
responseTimeMs: healthResultNumber({
|
|
62
64
|
"x-chart-type": "line",
|
|
63
65
|
"x-chart-label": "Response Time",
|
|
64
66
|
"x-chart-unit": "ms",
|
|
67
|
+
"x-anomaly-enabled": true,
|
|
68
|
+
"x-anomaly-direction": "lower-is-better",
|
|
69
|
+
"x-anomaly-sensitivity": 2,
|
|
70
|
+
"x-anomaly-confirmation-window": 3,
|
|
65
71
|
}).optional(),
|
|
66
72
|
error: healthResultString({
|
|
67
73
|
"x-chart-type": "status",
|
|
68
74
|
"x-chart-label": "Error",
|
|
75
|
+
"x-anomaly-enabled": false,
|
|
69
76
|
}).optional(),
|
|
70
77
|
});
|
|
71
78
|
|
|
@@ -77,15 +84,21 @@ const jenkinsAggregatedFields = {
|
|
|
77
84
|
"x-chart-type": "gauge",
|
|
78
85
|
"x-chart-label": "Success Rate",
|
|
79
86
|
"x-chart-unit": "%",
|
|
87
|
+
"x-anomaly-enabled": true,
|
|
88
|
+
"x-anomaly-direction": "higher-is-better",
|
|
80
89
|
}),
|
|
81
90
|
avgResponseTimeMs: aggregatedAverage({
|
|
82
91
|
"x-chart-type": "line",
|
|
83
92
|
"x-chart-label": "Avg Response Time",
|
|
84
93
|
"x-chart-unit": "ms",
|
|
94
|
+
"x-anomaly-enabled": true,
|
|
95
|
+
"x-anomaly-direction": "lower-is-better",
|
|
85
96
|
}),
|
|
86
97
|
errorCount: aggregatedCounter({
|
|
87
98
|
"x-chart-type": "counter",
|
|
88
99
|
"x-chart-label": "Errors",
|
|
100
|
+
"x-anomaly-enabled": true,
|
|
101
|
+
"x-anomaly-direction": "lower-is-better",
|
|
89
102
|
}),
|
|
90
103
|
};
|
|
91
104
|
|
|
@@ -188,8 +201,7 @@ export class JenkinsHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
188
201
|
} catch (error) {
|
|
189
202
|
clearTimeout(timeoutId);
|
|
190
203
|
|
|
191
|
-
const errorMessage =
|
|
192
|
-
extractErrorMessage(error);
|
|
204
|
+
const errorMessage = extractErrorMessage(error);
|
|
193
205
|
return {
|
|
194
206
|
statusCode: 0,
|
|
195
207
|
data: undefined,
|