@causa/workspace-google 1.4.0-beta.1 → 1.4.0-beta.2
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/README.md +14 -0
- package/dist/functions/google-app-check/generate-token.js +3 -1
- package/dist/functions/google-firebase-storage/merge-rules.js +3 -1
- package/dist/functions/google-firestore/merge-rules.js +3 -1
- package/dist/functions/google-identity-platform/generate-custom-token.js +7 -2
- package/dist/functions/google-pubsub/write-topics.js +7 -2
- package/dist/functions/google-services/enable.js +3 -1
- package/dist/functions/google-spanner/write-databases.js +7 -2
- package/dist/functions/graph/fetch-environment-metric-series.call.d.ts +3 -0
- package/dist/functions/graph/fetch-environment-metric-series.call.js +107 -0
- package/dist/functions/graph/fetch-environment-metric-series.d.ts +23 -0
- package/dist/functions/graph/fetch-environment-metric-series.js +40 -0
- package/dist/functions/graph/get-environment-provider.d.ts +11 -0
- package/dist/functions/graph/get-environment-provider.js +20 -0
- package/dist/functions/graph/index.d.ts +2 -0
- package/dist/functions/graph/index.js +2 -0
- package/dist/functions/index.js +2 -2
- package/dist/graph/cloud-run.d.ts +2 -2
- package/dist/graph/cloud-run.js +2 -2
- package/dist/graph/environment/fetchers/cloud-monitoring-alerts.d.ts +6 -0
- package/dist/graph/environment/fetchers/cloud-monitoring-alerts.js +106 -0
- package/dist/graph/environment/fetchers/cloud-monitoring.d.ts +5 -0
- package/dist/graph/environment/fetchers/cloud-monitoring.js +13 -0
- package/dist/graph/environment/fetchers/cloud-run.d.ts +5 -0
- package/dist/graph/environment/fetchers/cloud-run.js +88 -0
- package/dist/graph/environment/fetchers/cloud-scheduler.d.ts +5 -0
- package/dist/graph/environment/fetchers/cloud-scheduler.js +95 -0
- package/dist/graph/environment/fetchers/console-links.d.ts +14 -0
- package/dist/graph/environment/fetchers/console-links.js +132 -0
- package/dist/graph/environment/fetchers/error-reporting.d.ts +5 -0
- package/dist/graph/environment/fetchers/error-reporting.js +98 -0
- package/dist/graph/environment/fetchers/index.d.ts +5 -0
- package/dist/graph/environment/fetchers/index.js +17 -0
- package/dist/graph/environment/index.d.ts +9 -0
- package/dist/graph/environment/index.js +9 -0
- package/dist/graph/environment/load-balancer-backends.d.ts +82 -0
- package/dist/graph/environment/load-balancer-backends.js +126 -0
- package/dist/graph/environment/monitoring-alerts.d.ts +60 -0
- package/dist/graph/environment/monitoring-alerts.js +67 -0
- package/dist/graph/environment/monitoring-catalog.d.ts +95 -0
- package/dist/graph/environment/monitoring-catalog.js +356 -0
- package/dist/graph/environment/monitoring-mappings.d.ts +133 -0
- package/dist/graph/environment/monitoring-mappings.js +97 -0
- package/dist/graph/environment/monitoring-query.d.ts +116 -0
- package/dist/graph/environment/monitoring-query.js +314 -0
- package/dist/graph/environment/monitoring-values.d.ts +41 -0
- package/dist/graph/environment/monitoring-values.js +119 -0
- package/dist/graph/environment/resource-resolvers.d.ts +7 -0
- package/dist/graph/environment/resource-resolvers.js +57 -0
- package/dist/graph/environment/resources.d.ts +45 -0
- package/dist/graph/environment/resources.js +49 -0
- package/dist/graph/index.d.ts +1 -0
- package/dist/graph/index.js +1 -0
- package/dist/graph/spanner.d.ts +2 -2
- package/dist/graph/spanner.js +2 -2
- package/package.json +19 -19
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { GraphMetricKind, } from '@causa/workspace-core';
|
|
2
|
+
import { CORE_GRAPH_METRIC_DEFINITIONS, } from '@causa/workspace-core/graph';
|
|
3
|
+
import { LOAD_BALANCER_BACKEND_MAPPINGS, URL_MAP_MAPPING, } from './load-balancer-backends.js';
|
|
4
|
+
import { templateMapping, } from './monitoring-mappings.js';
|
|
5
|
+
export const CLOUD_RUN_SERVICE_MAPPING = templateMapping({
|
|
6
|
+
nodeType: 'service',
|
|
7
|
+
resourceType: 'run.googleapis.com/Service',
|
|
8
|
+
monitoredResource: 'cloud_run_revision',
|
|
9
|
+
name: 'projects/{project}/locations/{resource.label.location}/services/{resource.label.service_name}',
|
|
10
|
+
});
|
|
11
|
+
export const PUBSUB_TOPIC_MAPPING = templateMapping({
|
|
12
|
+
nodeType: 'brokerTopic',
|
|
13
|
+
resourceType: 'pubsub.googleapis.com/Topic',
|
|
14
|
+
monitoredResource: 'pubsub_topic',
|
|
15
|
+
name: 'projects/{project}/topics/{resource.label.topic_id}',
|
|
16
|
+
});
|
|
17
|
+
export const PUBSUB_SUBSCRIPTION_MAPPING = templateMapping({
|
|
18
|
+
nodeType: 'brokerSubscription',
|
|
19
|
+
resourceType: 'pubsub.googleapis.com/Subscription',
|
|
20
|
+
monitoredResource: 'pubsub_subscription',
|
|
21
|
+
name: 'projects/{project}/subscriptions/{resource.label.subscription_id}',
|
|
22
|
+
});
|
|
23
|
+
export const CLOUD_TASKS_QUEUE_MAPPING = templateMapping({
|
|
24
|
+
nodeType: 'queue',
|
|
25
|
+
resourceType: 'cloudtasks.googleapis.com/Queue',
|
|
26
|
+
monitoredResource: 'cloud_tasks_queue',
|
|
27
|
+
name: 'projects/{project}/locations/{resource.label.location}/queues/{resource.label.queue_id}',
|
|
28
|
+
});
|
|
29
|
+
export const SPANNER_INSTANCE_MAPPING = templateMapping({
|
|
30
|
+
nodeType: 'google.spanner.instance',
|
|
31
|
+
resourceType: 'spanner.googleapis.com/Instance',
|
|
32
|
+
monitoredResource: 'spanner_instance',
|
|
33
|
+
name: 'projects/{project}/instances/{resource.label.instance_id}',
|
|
34
|
+
});
|
|
35
|
+
export const SPANNER_DATABASE_MAPPING = templateMapping({
|
|
36
|
+
nodeType: 'database',
|
|
37
|
+
resourceType: 'spanner.googleapis.com/Database',
|
|
38
|
+
monitoredResource: 'spanner_instance',
|
|
39
|
+
name: 'projects/{project}/instances/{resource.label.instance_id}/databases/{metric.label.database}',
|
|
40
|
+
});
|
|
41
|
+
export const FIRESTORE_DATABASE_MAPPING = templateMapping({
|
|
42
|
+
nodeType: 'database',
|
|
43
|
+
resourceType: 'firestore.googleapis.com/Database',
|
|
44
|
+
monitoredResource: 'firestore.googleapis.com/Database',
|
|
45
|
+
name: 'projects/{project}/databases/{resource.label.database_id}',
|
|
46
|
+
});
|
|
47
|
+
export const BIGQUERY_DATASET_MAPPING = templateMapping({
|
|
48
|
+
nodeType: 'database',
|
|
49
|
+
resourceType: 'bigquery.googleapis.com/Dataset',
|
|
50
|
+
monitoredResource: 'bigquery_dataset',
|
|
51
|
+
name: 'projects/{project}/datasets/{resource.label.dataset_id}',
|
|
52
|
+
});
|
|
53
|
+
export const CLOUD_SCHEDULER_JOB_MAPPING = templateMapping({
|
|
54
|
+
nodeType: 'scheduledJob',
|
|
55
|
+
resourceType: 'cloudscheduler.googleapis.com/Job',
|
|
56
|
+
monitoredResource: 'cloud_scheduler_job',
|
|
57
|
+
name: 'projects/{project}/locations/{resource.label.location}/jobs/{resource.label.job_id}',
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* All the mappings of Cloud Monitoring monitored resources to the resources of nodes.
|
|
61
|
+
*/
|
|
62
|
+
export const MONITORING_RESOURCE_MAPPINGS = [
|
|
63
|
+
CLOUD_RUN_SERVICE_MAPPING,
|
|
64
|
+
PUBSUB_TOPIC_MAPPING,
|
|
65
|
+
PUBSUB_SUBSCRIPTION_MAPPING,
|
|
66
|
+
CLOUD_TASKS_QUEUE_MAPPING,
|
|
67
|
+
SPANNER_INSTANCE_MAPPING,
|
|
68
|
+
SPANNER_DATABASE_MAPPING,
|
|
69
|
+
FIRESTORE_DATABASE_MAPPING,
|
|
70
|
+
BIGQUERY_DATASET_MAPPING,
|
|
71
|
+
...LOAD_BALANCER_BACKEND_MAPPINGS,
|
|
72
|
+
URL_MAP_MAPPING,
|
|
73
|
+
CLOUD_SCHEDULER_JOB_MAPPING,
|
|
74
|
+
];
|
|
75
|
+
/**
|
|
76
|
+
* Returns whether a subscription pushes messages to a service, rather than e.g. writing them to a table.
|
|
77
|
+
*
|
|
78
|
+
* @param node The node of the subscription.
|
|
79
|
+
* @param graph The graph containing the node.
|
|
80
|
+
* @returns `true` if the subscription targets a service.
|
|
81
|
+
*/
|
|
82
|
+
export function isPushSubscription(node, graph) {
|
|
83
|
+
return (graph.edges?.targets ?? []).some((edge) => edge.from === node.id && !!edge.to?.startsWith('service:'));
|
|
84
|
+
}
|
|
85
|
+
const CODE = 'code';
|
|
86
|
+
const METHOD = 'method';
|
|
87
|
+
/**
|
|
88
|
+
* The metrics read from Cloud Monitoring.
|
|
89
|
+
*/
|
|
90
|
+
export const MONITORING_METRICS = [
|
|
91
|
+
{
|
|
92
|
+
metric: 'requests',
|
|
93
|
+
mapping: CLOUD_RUN_SERVICE_MAPPING,
|
|
94
|
+
metricType: 'run.googleapis.com/request_count',
|
|
95
|
+
groupBy: { [CODE]: 'metric.label.response_code_class' },
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
metric: 'latency',
|
|
99
|
+
mapping: CLOUD_RUN_SERVICE_MAPPING,
|
|
100
|
+
metricType: 'run.googleapis.com/request_latencies',
|
|
101
|
+
groupBy: { [CODE]: 'metric.label.response_code_class' },
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
metric: 'instances',
|
|
105
|
+
mapping: CLOUD_RUN_SERVICE_MAPPING,
|
|
106
|
+
metricType: 'run.googleapis.com/container/instance_count',
|
|
107
|
+
filter: 'metric.label.state = "active"',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
metric: 'cpuUtilization',
|
|
111
|
+
mapping: CLOUD_RUN_SERVICE_MAPPING,
|
|
112
|
+
metricType: 'run.googleapis.com/container/cpu/utilizations',
|
|
113
|
+
scale: 100,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
metric: 'memoryUtilization',
|
|
117
|
+
mapping: CLOUD_RUN_SERVICE_MAPPING,
|
|
118
|
+
metricType: 'run.googleapis.com/container/memory/utilizations',
|
|
119
|
+
scale: 100,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
metric: 'concurrency',
|
|
123
|
+
mapping: CLOUD_RUN_SERVICE_MAPPING,
|
|
124
|
+
metricType: 'run.googleapis.com/container/max_request_concurrencies',
|
|
125
|
+
// Idle instances serve no request, and would pull the distribution down.
|
|
126
|
+
filter: 'metric.label.state = "active"',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
metric: 'errorLogs',
|
|
130
|
+
mapping: CLOUD_RUN_SERVICE_MAPPING,
|
|
131
|
+
metricType: 'logging.googleapis.com/log_entry_count',
|
|
132
|
+
filter: 'metric.label.severity = one_of("ERROR", "CRITICAL", "ALERT", "EMERGENCY")',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
metric: 'requests',
|
|
136
|
+
mapping: URL_MAP_MAPPING,
|
|
137
|
+
metricType: 'loadbalancing.googleapis.com/https/request_count',
|
|
138
|
+
groupBy: { [CODE]: 'metric.label.response_code_class' },
|
|
139
|
+
groupByNode: LOAD_BALANCER_BACKEND_MAPPINGS,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
metric: 'latency',
|
|
143
|
+
mapping: URL_MAP_MAPPING,
|
|
144
|
+
metricType: 'loadbalancing.googleapis.com/https/total_latencies',
|
|
145
|
+
groupBy: { [CODE]: 'metric.label.response_code_class' },
|
|
146
|
+
groupByNode: LOAD_BALANCER_BACKEND_MAPPINGS,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
metric: 'backendLatency',
|
|
150
|
+
mapping: URL_MAP_MAPPING,
|
|
151
|
+
metricType: 'loadbalancing.googleapis.com/https/backend_latencies',
|
|
152
|
+
groupBy: { [CODE]: 'metric.label.response_code_class' },
|
|
153
|
+
groupByNode: LOAD_BALANCER_BACKEND_MAPPINGS,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
metric: 'published',
|
|
157
|
+
mapping: PUBSUB_TOPIC_MAPPING,
|
|
158
|
+
metricType: 'pubsub.googleapis.com/topic/message_sizes',
|
|
159
|
+
countRate: true,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
metric: 'publishRequests',
|
|
163
|
+
mapping: PUBSUB_TOPIC_MAPPING,
|
|
164
|
+
metricType: 'pubsub.googleapis.com/topic/send_request_count',
|
|
165
|
+
groupBy: { [CODE]: 'metric.label.response_class' },
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
metric: 'publishLatency',
|
|
169
|
+
mapping: PUBSUB_TOPIC_MAPPING,
|
|
170
|
+
metricType: 'pubsub.googleapis.com/topic/send_request_latencies',
|
|
171
|
+
scale: 0.001,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
metric: 'messageSize',
|
|
175
|
+
mapping: PUBSUB_TOPIC_MAPPING,
|
|
176
|
+
metricType: 'pubsub.googleapis.com/topic/message_sizes',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
metric: 'backlog',
|
|
180
|
+
mapping: PUBSUB_SUBSCRIPTION_MAPPING,
|
|
181
|
+
metricType: 'pubsub.googleapis.com/subscription/num_undelivered_messages',
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
metric: 'backlogAge',
|
|
185
|
+
mapping: PUBSUB_SUBSCRIPTION_MAPPING,
|
|
186
|
+
metricType: 'pubsub.googleapis.com/subscription/oldest_unacked_message_age',
|
|
187
|
+
reducer: 'REDUCE_MAX',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
metric: 'delivered',
|
|
191
|
+
mapping: PUBSUB_SUBSCRIPTION_MAPPING,
|
|
192
|
+
metricType: 'pubsub.googleapis.com/subscription/sent_message_count',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
metric: 'acked',
|
|
196
|
+
mapping: PUBSUB_SUBSCRIPTION_MAPPING,
|
|
197
|
+
metricType: 'pubsub.googleapis.com/subscription/ack_message_count',
|
|
198
|
+
// Messages not matching the subscription's filter are acknowledged by Pub/Sub, without being delivered.
|
|
199
|
+
filter: 'metric.label.delivery_type != "filter"',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
metric: 'pushRequests',
|
|
203
|
+
mapping: PUBSUB_SUBSCRIPTION_MAPPING,
|
|
204
|
+
metricType: 'pubsub.googleapis.com/subscription/push_request_count',
|
|
205
|
+
groupBy: { [CODE]: 'metric.label.response_class' },
|
|
206
|
+
measures: isPushSubscription,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
metric: 'pushLatency',
|
|
210
|
+
mapping: PUBSUB_SUBSCRIPTION_MAPPING,
|
|
211
|
+
metricType: 'pubsub.googleapis.com/subscription/push_request_latencies',
|
|
212
|
+
groupBy: { [CODE]: 'metric.label.response_code' },
|
|
213
|
+
scale: 0.001,
|
|
214
|
+
measures: isPushSubscription,
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
metric: 'backlog',
|
|
218
|
+
mapping: CLOUD_TASKS_QUEUE_MAPPING,
|
|
219
|
+
metricType: 'cloudtasks.googleapis.com/queue/depth',
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
metric: 'published',
|
|
223
|
+
mapping: CLOUD_TASKS_QUEUE_MAPPING,
|
|
224
|
+
metricType: 'cloudtasks.googleapis.com/api/request_count',
|
|
225
|
+
filter: 'metric.label.api_method = "CreateTask"',
|
|
226
|
+
groupBy: { [CODE]: 'metric.label.response_code' },
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
metric: 'pushRequests',
|
|
230
|
+
mapping: CLOUD_TASKS_QUEUE_MAPPING,
|
|
231
|
+
metricType: 'cloudtasks.googleapis.com/queue/task_attempt_count',
|
|
232
|
+
groupBy: { [CODE]: 'metric.label.response_code' },
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
metric: 'dispatchDelay',
|
|
236
|
+
mapping: CLOUD_TASKS_QUEUE_MAPPING,
|
|
237
|
+
metricType: 'cloudtasks.googleapis.com/queue/task_attempt_delays',
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
metric: 'cpuUtilization',
|
|
241
|
+
mapping: SPANNER_INSTANCE_MAPPING,
|
|
242
|
+
metricType: 'spanner.googleapis.com/instance/cpu/utilization',
|
|
243
|
+
scale: 100,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
metric: 'storageUtilization',
|
|
247
|
+
mapping: SPANNER_INSTANCE_MAPPING,
|
|
248
|
+
metricType: 'spanner.googleapis.com/instance/storage/utilization',
|
|
249
|
+
reducer: 'REDUCE_MAX',
|
|
250
|
+
scale: 100,
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
metric: 'nodes',
|
|
254
|
+
mapping: SPANNER_INSTANCE_MAPPING,
|
|
255
|
+
metricType: 'spanner.googleapis.com/instance/processing_units',
|
|
256
|
+
reducer: 'REDUCE_MAX',
|
|
257
|
+
scale: 0.001,
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
metric: 'requests',
|
|
261
|
+
mapping: SPANNER_DATABASE_MAPPING,
|
|
262
|
+
metricType: 'spanner.googleapis.com/api/api_request_count',
|
|
263
|
+
groupBy: {
|
|
264
|
+
[METHOD]: 'metric.label.method',
|
|
265
|
+
[CODE]: 'metric.label.status',
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
metric: 'latency',
|
|
270
|
+
mapping: SPANNER_DATABASE_MAPPING,
|
|
271
|
+
metricType: 'spanner.googleapis.com/api/request_latencies',
|
|
272
|
+
groupBy: { [METHOD]: 'metric.label.method' },
|
|
273
|
+
scale: 1000,
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
metric: 'storage',
|
|
277
|
+
mapping: SPANNER_DATABASE_MAPPING,
|
|
278
|
+
metricType: 'spanner.googleapis.com/instance/storage/used_bytes',
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
metric: 'cpuUtilization',
|
|
282
|
+
mapping: SPANNER_DATABASE_MAPPING,
|
|
283
|
+
metricType: 'spanner.googleapis.com/instance/cpu/utilization',
|
|
284
|
+
scale: 100,
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
metric: 'requests',
|
|
288
|
+
mapping: FIRESTORE_DATABASE_MAPPING,
|
|
289
|
+
metricType: 'firestore.googleapis.com/api/request_count',
|
|
290
|
+
groupBy: {
|
|
291
|
+
[METHOD]: 'metric.label.api_method',
|
|
292
|
+
[CODE]: 'metric.label.response_code',
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
metric: 'latency',
|
|
297
|
+
mapping: FIRESTORE_DATABASE_MAPPING,
|
|
298
|
+
metricType: 'firestore.googleapis.com/api/request_latencies',
|
|
299
|
+
groupBy: { [METHOD]: 'metric.label.api_method' },
|
|
300
|
+
scale: 1000,
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
metric: 'storage',
|
|
304
|
+
mapping: FIRESTORE_DATABASE_MAPPING,
|
|
305
|
+
metricType: 'firestore.googleapis.com/storage/data_and_index_storage_bytes',
|
|
306
|
+
// Sampled every minute, but written hours late, and in stretches separated by gaps of up to 13 hours.
|
|
307
|
+
minPeriod: 24 * 3600,
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
metric: 'storage',
|
|
311
|
+
mapping: BIGQUERY_DATASET_MAPPING,
|
|
312
|
+
metricType: 'bigquery.googleapis.com/storage/stored_bytes',
|
|
313
|
+
// Sampled every 30 minutes, and visible up to 3 hours later.
|
|
314
|
+
minPeriod: 4 * 3600,
|
|
315
|
+
},
|
|
316
|
+
];
|
|
317
|
+
/**
|
|
318
|
+
* The definitions of the metrics of the node types defined by the Google module.
|
|
319
|
+
*/
|
|
320
|
+
export const GOOGLE_GRAPH_METRIC_DEFINITIONS = {
|
|
321
|
+
'google.spanner.instance': {
|
|
322
|
+
cpuUtilization: {
|
|
323
|
+
name: 'CPU utilization',
|
|
324
|
+
description: 'CPU utilization of the instance, across all databases.',
|
|
325
|
+
kind: GraphMetricKind.Gauge,
|
|
326
|
+
unit: '%',
|
|
327
|
+
},
|
|
328
|
+
storageUtilization: {
|
|
329
|
+
name: 'Storage utilization',
|
|
330
|
+
description: 'Storage used, as a percentage of the limit of the provisioned capacity.',
|
|
331
|
+
kind: GraphMetricKind.Gauge,
|
|
332
|
+
unit: '%',
|
|
333
|
+
},
|
|
334
|
+
nodes: {
|
|
335
|
+
name: 'Nodes',
|
|
336
|
+
description: 'Provisioned compute capacity, where 1,000 processing units make a node.',
|
|
337
|
+
kind: GraphMetricKind.Gauge,
|
|
338
|
+
unit: '{node}',
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* Returns the kind of a metric read from Cloud Monitoring, from its definition for the type of its nodes.
|
|
344
|
+
*
|
|
345
|
+
* @param metric The metric.
|
|
346
|
+
* @returns The kind of the metric.
|
|
347
|
+
*/
|
|
348
|
+
export function monitoringMetricKind(metric) {
|
|
349
|
+
const { nodeType } = metric.mapping;
|
|
350
|
+
const definition = CORE_GRAPH_METRIC_DEFINITIONS[nodeType]?.[metric.metric] ??
|
|
351
|
+
GOOGLE_GRAPH_METRIC_DEFINITIONS[nodeType]?.[metric.metric];
|
|
352
|
+
if (!definition) {
|
|
353
|
+
throw new Error(`The metric '${metric.metric}' of node type '${nodeType}' has no definition.`);
|
|
354
|
+
}
|
|
355
|
+
return definition.kind;
|
|
356
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { type GraphEnvironmentContext, type GraphNodeEntry } from '@causa/workspace-core/graph';
|
|
2
|
+
/**
|
|
3
|
+
* A label of Cloud Monitoring time series or alerts, written as in `groupByFields`.
|
|
4
|
+
*/
|
|
5
|
+
export type MonitoringLabel = `resource.label.${string}` | `metric.label.${string}`;
|
|
6
|
+
/**
|
|
7
|
+
* What identifies a resource in a time series or an alert.
|
|
8
|
+
*/
|
|
9
|
+
export type MonitoringResourceKey = {
|
|
10
|
+
/**
|
|
11
|
+
* The GCP project of the resource.
|
|
12
|
+
*/
|
|
13
|
+
readonly project: string;
|
|
14
|
+
/**
|
|
15
|
+
* The values of all the key labels of the mapping, keyed by label.
|
|
16
|
+
*/
|
|
17
|
+
readonly labels: Readonly<Record<MonitoringLabel, string>>;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Returns the full name of the resource identified by a key, or `undefined` if the key identifies no resource.
|
|
21
|
+
*/
|
|
22
|
+
export type MonitoringResourceResolver = (key: MonitoringResourceKey) => string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* How time series and alerts on a Cloud Monitoring monitored resource identify the resources of the nodes of a type.
|
|
25
|
+
*/
|
|
26
|
+
export type MonitoringResourceMapping = {
|
|
27
|
+
/**
|
|
28
|
+
* The type of the nodes.
|
|
29
|
+
*/
|
|
30
|
+
readonly nodeType: string;
|
|
31
|
+
/**
|
|
32
|
+
* The type of the nodes' resource, in their `data.resource.type`.
|
|
33
|
+
*/
|
|
34
|
+
readonly resourceType: string;
|
|
35
|
+
/**
|
|
36
|
+
* The type of the monitored resource, e.g. `cloud_run_revision`.
|
|
37
|
+
*/
|
|
38
|
+
readonly monitoredResource: string;
|
|
39
|
+
/**
|
|
40
|
+
* The labels identifying a resource within a project. A time series or alert missing one of them, or with an empty
|
|
41
|
+
* value, is not mapped.
|
|
42
|
+
*/
|
|
43
|
+
readonly keyLabels: readonly MonitoringLabel[];
|
|
44
|
+
/**
|
|
45
|
+
* Prepares the resolution of keys to resource names in the environment.
|
|
46
|
+
* It is called through {@link prepareMonitoringMapping}, once per read or listing of alerts, and only when the mapping
|
|
47
|
+
* may apply. It throws a `GraphFactError` when a fact it depends on cannot be computed.
|
|
48
|
+
*
|
|
49
|
+
* @param environment The context of the environment.
|
|
50
|
+
* @returns The resolver.
|
|
51
|
+
*/
|
|
52
|
+
resolver(environment: GraphEnvironmentContext): MonitoringResourceResolver | Promise<MonitoringResourceResolver>;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* A mapping whose resolver is prepared.
|
|
56
|
+
*/
|
|
57
|
+
export type PreparedMonitoringMapping = {
|
|
58
|
+
/**
|
|
59
|
+
* The mapping.
|
|
60
|
+
*/
|
|
61
|
+
readonly mapping: MonitoringResourceMapping;
|
|
62
|
+
/**
|
|
63
|
+
* The resolver of the mapping.
|
|
64
|
+
*/
|
|
65
|
+
readonly resolve: MonitoringResourceResolver;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Returns the name of a label, without its source, e.g. `service_name` for `resource.label.service_name`.
|
|
69
|
+
*
|
|
70
|
+
* @param label The label.
|
|
71
|
+
* @returns The name of the label.
|
|
72
|
+
*/
|
|
73
|
+
export declare function monitoringLabelName(label: MonitoringLabel): string;
|
|
74
|
+
/**
|
|
75
|
+
* Builds a mapping whose resource names are made of the values of its key labels.
|
|
76
|
+
*
|
|
77
|
+
* @param mapping The mapping, with the template of the name. `{project}` is the project of the key. Other placeholders
|
|
78
|
+
* are labels, e.g. `projects/{project}/locations/{resource.label.location}/services/{resource.label.service_name}`,
|
|
79
|
+
* and make up the key labels, in order.
|
|
80
|
+
* @returns The mapping.
|
|
81
|
+
*/
|
|
82
|
+
export declare function templateMapping(mapping: Pick<MonitoringResourceMapping, 'nodeType' | 'resourceType' | 'monitoredResource'> & {
|
|
83
|
+
readonly name: string;
|
|
84
|
+
}): MonitoringResourceMapping;
|
|
85
|
+
/**
|
|
86
|
+
* Reads the key of a mapping.
|
|
87
|
+
*
|
|
88
|
+
* @param mapping The mapping.
|
|
89
|
+
* @param project The project of the time series or alert.
|
|
90
|
+
* @param read Returns the value of a label of the time series or alert.
|
|
91
|
+
* @returns The key, or `undefined` if a key label is missing or empty.
|
|
92
|
+
*/
|
|
93
|
+
export declare function readMonitoringResourceKey(mapping: MonitoringResourceMapping, project: string, read: (label: MonitoringLabel) => string | undefined): MonitoringResourceKey | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* Prepares a mapping.
|
|
96
|
+
*
|
|
97
|
+
* @param environment The context of the environment.
|
|
98
|
+
* @param mapping The mapping.
|
|
99
|
+
* @returns The prepared mapping, or `undefined` if a fact its resolver depends on could not be computed. The failure is
|
|
100
|
+
* reported by the fact.
|
|
101
|
+
*/
|
|
102
|
+
export declare function prepareMonitoringMapping(environment: GraphEnvironmentContext, mapping: MonitoringResourceMapping): Promise<PreparedMonitoringMapping | undefined>;
|
|
103
|
+
/**
|
|
104
|
+
* A resource name resolved by a mapping, which no node of the graph holds.
|
|
105
|
+
*/
|
|
106
|
+
export type MissingMonitoredResource = {
|
|
107
|
+
/**
|
|
108
|
+
* The mapping that resolved the name.
|
|
109
|
+
*/
|
|
110
|
+
readonly mapping: MonitoringResourceMapping;
|
|
111
|
+
/**
|
|
112
|
+
* The key from which the name was resolved.
|
|
113
|
+
*/
|
|
114
|
+
readonly key: MonitoringResourceKey;
|
|
115
|
+
/**
|
|
116
|
+
* The resolved resource name.
|
|
117
|
+
*/
|
|
118
|
+
readonly name: string;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Finds the node of the resource identified by a time series or an alert, trying mappings from the one with the most
|
|
122
|
+
* key labels. A mapping applies when its key can be read, and the name it resolves is held by a node of its node type.
|
|
123
|
+
*
|
|
124
|
+
* @param environment The context of the environment.
|
|
125
|
+
* @param mappings The prepared mappings that may apply.
|
|
126
|
+
* @param project The project of the time series or alert.
|
|
127
|
+
* @param read Returns the value of a label of the time series or alert.
|
|
128
|
+
* @returns The node, if a mapping applies, and the names resolved by mappings that are not held by a node of the graph.
|
|
129
|
+
*/
|
|
130
|
+
export declare function findMonitoredNode(environment: GraphEnvironmentContext, mappings: readonly PreparedMonitoringMapping[], project: string, read: (label: MonitoringLabel) => string | undefined): {
|
|
131
|
+
readonly node?: GraphNodeEntry;
|
|
132
|
+
readonly missing: MissingMonitoredResource[];
|
|
133
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { GraphFactError, } from '@causa/workspace-core/graph';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the name of a label, without its source, e.g. `service_name` for `resource.label.service_name`.
|
|
4
|
+
*
|
|
5
|
+
* @param label The label.
|
|
6
|
+
* @returns The name of the label.
|
|
7
|
+
*/
|
|
8
|
+
export function monitoringLabelName(label) {
|
|
9
|
+
return label.slice(label.lastIndexOf('.') + 1);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Builds a mapping whose resource names are made of the values of its key labels.
|
|
13
|
+
*
|
|
14
|
+
* @param mapping The mapping, with the template of the name. `{project}` is the project of the key. Other placeholders
|
|
15
|
+
* are labels, e.g. `projects/{project}/locations/{resource.label.location}/services/{resource.label.service_name}`,
|
|
16
|
+
* and make up the key labels, in order.
|
|
17
|
+
* @returns The mapping.
|
|
18
|
+
*/
|
|
19
|
+
export function templateMapping(mapping) {
|
|
20
|
+
const { name, ...rest } = mapping;
|
|
21
|
+
const placeholder = /\{([\w.]+)\}/g;
|
|
22
|
+
const keyLabels = [...name.matchAll(placeholder)]
|
|
23
|
+
.map(([, label]) => label)
|
|
24
|
+
.filter((label) => label !== 'project');
|
|
25
|
+
return {
|
|
26
|
+
...rest,
|
|
27
|
+
keyLabels,
|
|
28
|
+
resolver: () => ({ project, labels }) => name.replace(placeholder, (_, label) => label === 'project' ? project : labels[label]),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Reads the key of a mapping.
|
|
33
|
+
*
|
|
34
|
+
* @param mapping The mapping.
|
|
35
|
+
* @param project The project of the time series or alert.
|
|
36
|
+
* @param read Returns the value of a label of the time series or alert.
|
|
37
|
+
* @returns The key, or `undefined` if a key label is missing or empty.
|
|
38
|
+
*/
|
|
39
|
+
export function readMonitoringResourceKey(mapping, project, read) {
|
|
40
|
+
const labels = {};
|
|
41
|
+
for (const label of mapping.keyLabels) {
|
|
42
|
+
const value = read(label);
|
|
43
|
+
if (!value) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
labels[label] = value;
|
|
47
|
+
}
|
|
48
|
+
return { project, labels: labels };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Prepares a mapping.
|
|
52
|
+
*
|
|
53
|
+
* @param environment The context of the environment.
|
|
54
|
+
* @param mapping The mapping.
|
|
55
|
+
* @returns The prepared mapping, or `undefined` if a fact its resolver depends on could not be computed. The failure is
|
|
56
|
+
* reported by the fact.
|
|
57
|
+
*/
|
|
58
|
+
export async function prepareMonitoringMapping(environment, mapping) {
|
|
59
|
+
try {
|
|
60
|
+
return { mapping, resolve: await mapping.resolver(environment) };
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (error instanceof GraphFactError) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Finds the node of the resource identified by a time series or an alert, trying mappings from the one with the most
|
|
71
|
+
* key labels. A mapping applies when its key can be read, and the name it resolves is held by a node of its node type.
|
|
72
|
+
*
|
|
73
|
+
* @param environment The context of the environment.
|
|
74
|
+
* @param mappings The prepared mappings that may apply.
|
|
75
|
+
* @param project The project of the time series or alert.
|
|
76
|
+
* @param read Returns the value of a label of the time series or alert.
|
|
77
|
+
* @returns The node, if a mapping applies, and the names resolved by mappings that are not held by a node of the graph.
|
|
78
|
+
*/
|
|
79
|
+
export function findMonitoredNode(environment, mappings, project, read) {
|
|
80
|
+
const missing = [];
|
|
81
|
+
const sorted = [...mappings].sort((a, b) => b.mapping.keyLabels.length - a.mapping.keyLabels.length);
|
|
82
|
+
for (const { mapping, resolve } of sorted) {
|
|
83
|
+
const key = readMonitoringResourceKey(mapping, project, read);
|
|
84
|
+
const name = key && resolve(key);
|
|
85
|
+
if (!key || !name) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const node = environment.resourceNode(mapping.resourceType, name);
|
|
89
|
+
if (!node) {
|
|
90
|
+
missing.push({ mapping, key, name });
|
|
91
|
+
}
|
|
92
|
+
else if (node.type === mapping.nodeType) {
|
|
93
|
+
return { node, missing };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { missing };
|
|
97
|
+
}
|