@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,88 @@
|
|
|
1
|
+
import { ServiceDeploymentStatus, } from '@causa/workspace-core';
|
|
2
|
+
import { googleResources } from '../resources.js';
|
|
3
|
+
/**
|
|
4
|
+
* The deployment status for each state of the `Ready` terminal condition of a Cloud Run service.
|
|
5
|
+
* States are listed both by name and by number, as clients may return either.
|
|
6
|
+
*/
|
|
7
|
+
const DEPLOYMENT_STATUSES = {
|
|
8
|
+
CONDITION_PENDING: ServiceDeploymentStatus.Deploying,
|
|
9
|
+
1: ServiceDeploymentStatus.Deploying,
|
|
10
|
+
CONDITION_RECONCILING: ServiceDeploymentStatus.Deploying,
|
|
11
|
+
2: ServiceDeploymentStatus.Deploying,
|
|
12
|
+
CONDITION_FAILED: ServiceDeploymentStatus.Failed,
|
|
13
|
+
3: ServiceDeploymentStatus.Failed,
|
|
14
|
+
CONDITION_SUCCEEDED: ServiceDeploymentStatus.Deployed,
|
|
15
|
+
4: ServiceDeploymentStatus.Deployed,
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Returns what is deployed for a Cloud Run service.
|
|
19
|
+
*
|
|
20
|
+
* @param service The Cloud Run service.
|
|
21
|
+
* @returns The deployment.
|
|
22
|
+
*/
|
|
23
|
+
function toDeployment(service) {
|
|
24
|
+
const image = service.template?.containers?.[0]?.image ?? undefined;
|
|
25
|
+
const updateSeconds = service.updateTime?.seconds;
|
|
26
|
+
const updatedAt = updateSeconds
|
|
27
|
+
? new Date(Number(updateSeconds.toString()) * 1000)
|
|
28
|
+
: undefined;
|
|
29
|
+
const { state, message } = service.terminalCondition ?? {};
|
|
30
|
+
const status = state ? DEPLOYMENT_STATUSES[state] : undefined;
|
|
31
|
+
return {
|
|
32
|
+
...(image ? { image } : {}),
|
|
33
|
+
...(updatedAt ? { updatedAt } : {}),
|
|
34
|
+
...(status ? { status } : {}),
|
|
35
|
+
...(status && message ? { message } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Reads the deployments of Cloud Run services.
|
|
40
|
+
*/
|
|
41
|
+
export const googleCloudRun = {
|
|
42
|
+
name: 'googleCloudRun',
|
|
43
|
+
description: 'The `deployment` of services deployed on Cloud Run: the container image, the last update time, and the status of the latest deployment from the `Ready` condition. The deployment is the current one, with a warning when the service was updated after the end of the evaluation window.',
|
|
44
|
+
fetch: async (environment) => {
|
|
45
|
+
const services = googleResources(environment, 'run.googleapis.com/Service', 'service');
|
|
46
|
+
if (services.length === 0) {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
const { CloudRunService } = await import('../../../services/cloud-run.js');
|
|
50
|
+
const { servicesClient } = environment.context.service(CloudRunService);
|
|
51
|
+
// Calls on a gax client crash the process when credentials cannot be loaded, while `initialize` rejects.
|
|
52
|
+
await servicesClient.initialize();
|
|
53
|
+
const byLocation = Map.groupBy(services, ({ segments }) => `projects/${segments.projects}/locations/${segments.locations}`);
|
|
54
|
+
const nodes = {};
|
|
55
|
+
const warnings = [];
|
|
56
|
+
await Promise.all([...byLocation].map(async ([parent, serviceNodes]) => {
|
|
57
|
+
let deployed;
|
|
58
|
+
try {
|
|
59
|
+
[deployed] = await servicesClient.listServices({ parent });
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
warnings.push({
|
|
63
|
+
message: `Failed to list the Cloud Run services in '${parent}': ${error.message ?? error}`,
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const byName = new Map(deployed.map((s) => [s.name, s]));
|
|
68
|
+
for (const { node, name } of serviceNodes) {
|
|
69
|
+
const service = byName.get(name);
|
|
70
|
+
if (!service) {
|
|
71
|
+
warnings.push({
|
|
72
|
+
message: `The Cloud Run service '${name}' of '${node.id}' does not exist.`,
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const deployment = toDeployment(service);
|
|
77
|
+
const { updatedAt } = deployment;
|
|
78
|
+
if (updatedAt && updatedAt > environment.at) {
|
|
79
|
+
warnings.push({
|
|
80
|
+
message: `The Cloud Run service '${name}' of '${node.id}' was updated at ${updatedAt.toISOString()}, after the end of the evaluation window. Its deployment is the current one.`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
nodes[node.id] = { data: { deployment } };
|
|
84
|
+
}
|
|
85
|
+
}));
|
|
86
|
+
return { nodes, warnings };
|
|
87
|
+
},
|
|
88
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { ScheduledJobAttemptStatus, } from '@causa/workspace-core';
|
|
2
|
+
import { googleResources } from '../resources.js';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the data of a scheduled job.
|
|
5
|
+
* The attempt succeeded if its status code is `OK`. The status message is kept, or the name of the code if the attempt
|
|
6
|
+
* failed without a message.
|
|
7
|
+
*
|
|
8
|
+
* @param job The Cloud Scheduler job.
|
|
9
|
+
* @param statusCodes The gRPC status codes, which are passed as `google-gax` is imported dynamically.
|
|
10
|
+
* @returns The data of the node.
|
|
11
|
+
*/
|
|
12
|
+
function toData(job, statusCodes) {
|
|
13
|
+
const code = job.status?.code ?? statusCodes.OK;
|
|
14
|
+
const succeeded = code === statusCodes.OK;
|
|
15
|
+
const message = job.status?.message ||
|
|
16
|
+
(succeeded ? undefined : (statusCodes[code] ?? `${code}`));
|
|
17
|
+
return {
|
|
18
|
+
...(job.lastAttemptTime
|
|
19
|
+
? {
|
|
20
|
+
lastAttempt: {
|
|
21
|
+
time: new Date(job.lastAttemptTime),
|
|
22
|
+
status: succeeded
|
|
23
|
+
? ScheduledJobAttemptStatus.Succeeded
|
|
24
|
+
: ScheduledJobAttemptStatus.Failed,
|
|
25
|
+
...(message ? { message } : {}),
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
: {}),
|
|
29
|
+
...(job.scheduleTime && job.state === 'ENABLED'
|
|
30
|
+
? { nextRun: new Date(job.scheduleTime) }
|
|
31
|
+
: {}),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Reads the last attempt and next run of Cloud Scheduler jobs.
|
|
36
|
+
*/
|
|
37
|
+
export const googleCloudScheduler = {
|
|
38
|
+
name: 'googleCloudScheduler',
|
|
39
|
+
description: 'The `lastAttempt` and `nextRun` of Cloud Scheduler jobs. Paused jobs have no next run. They are the current ones, with a warning when the job was attempted after the end of the evaluation window.',
|
|
40
|
+
fetch: async (environment) => {
|
|
41
|
+
const jobs = googleResources(environment, 'cloudscheduler.googleapis.com/Job', 'scheduledJob');
|
|
42
|
+
const byLocation = Map.groupBy(jobs, ({ segments }) => `projects/${segments.projects}/locations/${segments.locations}`);
|
|
43
|
+
if (byLocation.size === 0) {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
const [{ GoogleApisService }, { Status }] = await Promise.all([
|
|
47
|
+
import('../../../services/google-apis.js'),
|
|
48
|
+
import('google-gax'),
|
|
49
|
+
]);
|
|
50
|
+
const client = await environment.context
|
|
51
|
+
.service(GoogleApisService)
|
|
52
|
+
.getClient('cloudscheduler', 'v1', {});
|
|
53
|
+
const nodes = {};
|
|
54
|
+
const warnings = [];
|
|
55
|
+
await Promise.all([...byLocation].map(async ([parent, jobNodes]) => {
|
|
56
|
+
const deployed = new Map();
|
|
57
|
+
try {
|
|
58
|
+
let pageToken;
|
|
59
|
+
do {
|
|
60
|
+
const { data } = await client.projects.locations.jobs.list({
|
|
61
|
+
parent,
|
|
62
|
+
pageSize: 500,
|
|
63
|
+
pageToken,
|
|
64
|
+
});
|
|
65
|
+
(data.jobs ?? []).forEach((j) => j.name && deployed.set(j.name, j));
|
|
66
|
+
pageToken = data.nextPageToken ?? undefined;
|
|
67
|
+
} while (pageToken);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
warnings.push({
|
|
71
|
+
message: `Failed to list the Cloud Scheduler jobs in '${parent}': ${error.message ?? error}`,
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
for (const { node, name } of jobNodes) {
|
|
76
|
+
const job = deployed.get(name);
|
|
77
|
+
if (!job) {
|
|
78
|
+
warnings.push({
|
|
79
|
+
message: `The Cloud Scheduler job '${name}' of '${node.id}' does not exist.`,
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const data = toData(job, Status);
|
|
84
|
+
const time = data.lastAttempt?.time;
|
|
85
|
+
if (time && time > environment.at) {
|
|
86
|
+
warnings.push({
|
|
87
|
+
message: `The Cloud Scheduler job '${name}' of '${node.id}' was last attempted at ${time.toISOString()}, after the end of the evaluation window. Its last attempt and next run are the current ones.`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
nodes[node.id] = { data };
|
|
91
|
+
}
|
|
92
|
+
}));
|
|
93
|
+
return { nodes, warnings };
|
|
94
|
+
},
|
|
95
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GraphEnvironmentFetcher } from '@causa/workspace-core';
|
|
2
|
+
/**
|
|
3
|
+
* Builds the URL of a page of the Google Cloud console.
|
|
4
|
+
*
|
|
5
|
+
* @param segments The segments of the path of the page, which are encoded.
|
|
6
|
+
* @param project The GCP project.
|
|
7
|
+
* @param query Additional query parameters.
|
|
8
|
+
* @returns The URL.
|
|
9
|
+
*/
|
|
10
|
+
export declare function consoleUrl(segments: string[], project: string, query?: Record<string, string>): string;
|
|
11
|
+
/**
|
|
12
|
+
* Builds links to the Google Cloud console for the nodes' resources.
|
|
13
|
+
*/
|
|
14
|
+
export declare const googleConsoleLinks: GraphEnvironmentFetcher;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { googleResources } from '../resources.js';
|
|
2
|
+
/**
|
|
3
|
+
* The base URL of the Google Cloud console.
|
|
4
|
+
*/
|
|
5
|
+
const CONSOLE_URL = 'https://console.cloud.google.com';
|
|
6
|
+
/**
|
|
7
|
+
* Builds the URL of a page of the Google Cloud console.
|
|
8
|
+
*
|
|
9
|
+
* @param segments The segments of the path of the page, which are encoded.
|
|
10
|
+
* @param project The GCP project.
|
|
11
|
+
* @param query Additional query parameters.
|
|
12
|
+
* @returns The URL.
|
|
13
|
+
*/
|
|
14
|
+
export function consoleUrl(segments, project, query = {}) {
|
|
15
|
+
const path = segments.map(encodeURIComponent).join('/');
|
|
16
|
+
const parameters = new URLSearchParams({ project, ...query });
|
|
17
|
+
return `${CONSOLE_URL}/${path}?${parameters}`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The builders of links, keyed by resource type.
|
|
21
|
+
*/
|
|
22
|
+
const LINK_BUILDERS = {
|
|
23
|
+
'run.googleapis.com/Service': ({ projects, locations, services }) => [
|
|
24
|
+
{
|
|
25
|
+
label: 'Cloud Run service',
|
|
26
|
+
url: consoleUrl(['run', 'detail', locations, services, 'metrics'], projects),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: 'Cloud Run service logs',
|
|
30
|
+
url: consoleUrl(['run', 'detail', locations, services, 'logs'], projects),
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
'pubsub.googleapis.com/Topic': ({ projects, topics }) => [
|
|
34
|
+
{
|
|
35
|
+
label: 'Pub/Sub topic',
|
|
36
|
+
url: consoleUrl(['cloudpubsub', 'topic', 'detail', topics], projects),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
'pubsub.googleapis.com/Subscription': ({ projects, subscriptions }) => [
|
|
40
|
+
{
|
|
41
|
+
label: 'Pub/Sub subscription',
|
|
42
|
+
url: consoleUrl(['cloudpubsub', 'subscription', 'detail', subscriptions], projects),
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
'cloudtasks.googleapis.com/Queue': ({ projects, locations, queues }) => [
|
|
46
|
+
{
|
|
47
|
+
label: 'Cloud Tasks queue',
|
|
48
|
+
url: consoleUrl(['cloudtasks', 'queue', locations, queues, 'tasks'], projects),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
'cloudscheduler.googleapis.com/Job': ({ projects }) => [
|
|
52
|
+
{
|
|
53
|
+
label: 'Cloud Scheduler jobs',
|
|
54
|
+
url: consoleUrl(['cloudscheduler'], projects),
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
'spanner.googleapis.com/Instance': ({ projects, instances }) => [
|
|
58
|
+
{
|
|
59
|
+
label: 'Spanner instance',
|
|
60
|
+
url: consoleUrl(['spanner', 'instances', instances, 'details', 'databases'], projects),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
'spanner.googleapis.com/Database': ({ projects, instances, databases }) => [
|
|
64
|
+
{
|
|
65
|
+
label: 'Spanner database',
|
|
66
|
+
url: consoleUrl([
|
|
67
|
+
'spanner',
|
|
68
|
+
'instances',
|
|
69
|
+
instances,
|
|
70
|
+
'databases',
|
|
71
|
+
databases,
|
|
72
|
+
'details',
|
|
73
|
+
'tables',
|
|
74
|
+
], projects),
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
'firestore.googleapis.com/Database': ({ projects, databases }) => [
|
|
78
|
+
{
|
|
79
|
+
label: 'Firestore database',
|
|
80
|
+
url: consoleUrl([
|
|
81
|
+
'firestore',
|
|
82
|
+
'databases',
|
|
83
|
+
databases === '(default)' ? '-default-' : databases,
|
|
84
|
+
'data',
|
|
85
|
+
], projects),
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
'bigquery.googleapis.com/Dataset': ({ projects, datasets }) => [
|
|
89
|
+
{
|
|
90
|
+
label: 'BigQuery dataset',
|
|
91
|
+
url: consoleUrl(['bigquery'], projects, {
|
|
92
|
+
p: projects,
|
|
93
|
+
d: datasets,
|
|
94
|
+
page: 'dataset',
|
|
95
|
+
}),
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
'bigquery.googleapis.com/Table': ({ projects, datasets, tables }) => [
|
|
99
|
+
{
|
|
100
|
+
label: 'BigQuery table',
|
|
101
|
+
url: consoleUrl(['bigquery'], projects, {
|
|
102
|
+
p: projects,
|
|
103
|
+
d: datasets,
|
|
104
|
+
t: tables,
|
|
105
|
+
page: 'table',
|
|
106
|
+
}),
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
'compute.googleapis.com/UrlMap': ({ projects }) => [
|
|
110
|
+
{
|
|
111
|
+
label: 'Load balancers',
|
|
112
|
+
url: consoleUrl(['net-services', 'loadbalancing', 'list', 'loadBalancers'], projects),
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Builds links to the Google Cloud console for the nodes' resources.
|
|
118
|
+
*/
|
|
119
|
+
export const googleConsoleLinks = {
|
|
120
|
+
name: 'googleConsoleLinks',
|
|
121
|
+
description: 'The `links` of nodes whose resource is a Google Cloud resource, to the Google Cloud console.',
|
|
122
|
+
fetch: async (environment) => {
|
|
123
|
+
const nodes = {};
|
|
124
|
+
for (const { node, type, segments } of googleResources(environment)) {
|
|
125
|
+
const builder = LINK_BUILDERS[type];
|
|
126
|
+
if (builder && segments.projects) {
|
|
127
|
+
nodes[node.id] = { data: { links: builder(segments) } };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return { nodes };
|
|
131
|
+
},
|
|
132
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { GraphAlertSeverity, } from '@causa/workspace-core';
|
|
2
|
+
import { googleResources } from '../resources.js';
|
|
3
|
+
import { consoleUrl } from './console-links.js';
|
|
4
|
+
/**
|
|
5
|
+
* The longest period over which Error Reporting can list groups. Periods always end now.
|
|
6
|
+
*/
|
|
7
|
+
const PERIOD = 'PERIOD_30_DAYS';
|
|
8
|
+
/**
|
|
9
|
+
* The length of {@link PERIOD}, in milliseconds.
|
|
10
|
+
*/
|
|
11
|
+
const PERIOD_LENGTH = 30 * 24 * 3600 * 1000;
|
|
12
|
+
/**
|
|
13
|
+
* Converts the statistics of an open error group to an alert, titled with the first line of the message of the group's
|
|
14
|
+
* representative error.
|
|
15
|
+
*
|
|
16
|
+
* @param stats The statistics of the group.
|
|
17
|
+
* @param project The GCP project of the group.
|
|
18
|
+
* @returns The alert, or `undefined` if the group has never been seen.
|
|
19
|
+
*/
|
|
20
|
+
function toGraphAlert(stats, project) {
|
|
21
|
+
if (!stats.firstSeenTime) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const groupId = stats.group?.groupId ?? undefined;
|
|
25
|
+
const message = stats.representative?.message?.split('\n', 1)[0].trim();
|
|
26
|
+
return {
|
|
27
|
+
title: message || `Error group ${groupId}`,
|
|
28
|
+
severity: GraphAlertSeverity.Error,
|
|
29
|
+
openedAt: new Date(stats.firstSeenTime),
|
|
30
|
+
...(groupId
|
|
31
|
+
? { url: consoleUrl(['errors', 'detail', groupId], project) }
|
|
32
|
+
: {}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Reads the open groups of errors of Cloud Run services from Error Reporting, as alerts.
|
|
37
|
+
*/
|
|
38
|
+
export const googleErrorReporting = {
|
|
39
|
+
name: 'googleErrorReporting',
|
|
40
|
+
description: 'The `alerts` of Cloud Run services for their open Error Reporting groups, i.e. groups with occurrences during the last 30 days, first seen before the end of the evaluation window. Acknowledged, resolved, and muted groups are excluded.',
|
|
41
|
+
fetch: async (environment) => {
|
|
42
|
+
const services = googleResources(environment, 'run.googleapis.com/Service', 'service');
|
|
43
|
+
if (services.length === 0) {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
const { at } = environment;
|
|
47
|
+
if (Date.now() - at.getTime() > PERIOD_LENGTH) {
|
|
48
|
+
return {
|
|
49
|
+
warnings: [
|
|
50
|
+
{
|
|
51
|
+
message: 'Error groups cannot be listed for an evaluation window ending more than 30 days ago.',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const { GoogleApisService } = await import('../../../services/google-apis.js');
|
|
57
|
+
const client = await environment.context
|
|
58
|
+
.service(GoogleApisService)
|
|
59
|
+
.getClient('clouderrorreporting', 'v1beta1', {});
|
|
60
|
+
const nodes = {};
|
|
61
|
+
const warnings = [];
|
|
62
|
+
await Promise.all(services.map(async ({ node, segments }) => {
|
|
63
|
+
const stats = [];
|
|
64
|
+
try {
|
|
65
|
+
let pageToken;
|
|
66
|
+
do {
|
|
67
|
+
const { data } = await client.projects.groupStats.list({
|
|
68
|
+
projectName: `projects/${segments.projects}`,
|
|
69
|
+
'serviceFilter.service': segments.services,
|
|
70
|
+
'timeRange.period': PERIOD,
|
|
71
|
+
pageSize: 100,
|
|
72
|
+
pageToken,
|
|
73
|
+
});
|
|
74
|
+
stats.push(...(data.errorGroupStats ?? []));
|
|
75
|
+
pageToken = data.nextPageToken ?? undefined;
|
|
76
|
+
} while (pageToken);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
warnings.push({
|
|
80
|
+
message: `Failed to list the error groups of '${node.id}': ${error.message ?? error}`,
|
|
81
|
+
});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const alerts = stats.flatMap((groupStats) => {
|
|
85
|
+
// An unspecified resolution status is interpreted as open.
|
|
86
|
+
const status = groupStats.group?.resolutionStatus ?? 'OPEN';
|
|
87
|
+
const alert = status === 'OPEN'
|
|
88
|
+
? toGraphAlert(groupStats, segments.projects)
|
|
89
|
+
: undefined;
|
|
90
|
+
return alert && alert.openedAt < at ? [alert] : [];
|
|
91
|
+
});
|
|
92
|
+
if (alerts.length > 0) {
|
|
93
|
+
nodes[node.id] = { alerts };
|
|
94
|
+
}
|
|
95
|
+
}));
|
|
96
|
+
return { nodes, warnings };
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { googleCloudMonitoringAlerts } from './cloud-monitoring-alerts.js';
|
|
2
|
+
import { googleCloudMonitoring } from './cloud-monitoring.js';
|
|
3
|
+
import { googleCloudRun } from './cloud-run.js';
|
|
4
|
+
import { googleCloudScheduler } from './cloud-scheduler.js';
|
|
5
|
+
import { googleConsoleLinks } from './console-links.js';
|
|
6
|
+
import { googleErrorReporting } from './error-reporting.js';
|
|
7
|
+
/**
|
|
8
|
+
* The fetchers of environment data and metrics for Google Cloud.
|
|
9
|
+
*/
|
|
10
|
+
export const GOOGLE_GRAPH_ENVIRONMENT_FETCHERS = [
|
|
11
|
+
googleCloudMonitoring,
|
|
12
|
+
googleCloudMonitoringAlerts,
|
|
13
|
+
googleCloudRun,
|
|
14
|
+
googleCloudScheduler,
|
|
15
|
+
googleConsoleLinks,
|
|
16
|
+
googleErrorReporting,
|
|
17
|
+
];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './fetchers/index.js';
|
|
2
|
+
export * from './load-balancer-backends.js';
|
|
3
|
+
export * from './monitoring-alerts.js';
|
|
4
|
+
export * from './monitoring-catalog.js';
|
|
5
|
+
export * from './monitoring-mappings.js';
|
|
6
|
+
export * from './monitoring-query.js';
|
|
7
|
+
export * from './monitoring-values.js';
|
|
8
|
+
export * from './resource-resolvers.js';
|
|
9
|
+
export * from './resources.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './fetchers/index.js';
|
|
2
|
+
export * from './load-balancer-backends.js';
|
|
3
|
+
export * from './monitoring-alerts.js';
|
|
4
|
+
export * from './monitoring-catalog.js';
|
|
5
|
+
export * from './monitoring-mappings.js';
|
|
6
|
+
export * from './monitoring-query.js';
|
|
7
|
+
export * from './monitoring-values.js';
|
|
8
|
+
export * from './resource-resolvers.js';
|
|
9
|
+
export * from './resources.js';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { WorkspaceContext } from '@causa/workspace';
|
|
2
|
+
import { GraphEnvironmentFact, type GraphEnvironmentContext, type GraphFactOutput } from '@causa/workspace-core/graph';
|
|
3
|
+
import { type MonitoringLabel, type MonitoringResourceMapping } from './monitoring-mappings.js';
|
|
4
|
+
import { type GoogleResource } from './resources.js';
|
|
5
|
+
/**
|
|
6
|
+
* Maps load balancer labels to the URL maps of the graph, i.e. API routers.
|
|
7
|
+
*/
|
|
8
|
+
export declare const URL_MAP_MAPPING: MonitoringResourceMapping;
|
|
9
|
+
/**
|
|
10
|
+
* A backend of load balancers, which routes requests to a resource.
|
|
11
|
+
*/
|
|
12
|
+
export type LoadBalancerBackend = {
|
|
13
|
+
/**
|
|
14
|
+
* The kind of the backend.
|
|
15
|
+
*/
|
|
16
|
+
readonly kind: LoadBalancerBackendKind;
|
|
17
|
+
/**
|
|
18
|
+
* The GCP project of the backend.
|
|
19
|
+
*/
|
|
20
|
+
readonly project: string;
|
|
21
|
+
/**
|
|
22
|
+
* The values of the key labels of the kind identifying the backend.
|
|
23
|
+
*/
|
|
24
|
+
readonly labels: Readonly<Record<MonitoringLabel, string>>;
|
|
25
|
+
/**
|
|
26
|
+
* The full resource name of the resource the backend routes requests to, e.g. a Cloud Run service.
|
|
27
|
+
*/
|
|
28
|
+
readonly resource: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* A kind of load balancer backend, routing requests to the resources of nodes of a type.
|
|
32
|
+
*/
|
|
33
|
+
export type LoadBalancerBackendKind = {
|
|
34
|
+
/**
|
|
35
|
+
* The type of the nodes requests are routed to.
|
|
36
|
+
*/
|
|
37
|
+
readonly nodeType: string;
|
|
38
|
+
/**
|
|
39
|
+
* The type of the nodes' resource, in their `data.resource.type`.
|
|
40
|
+
*/
|
|
41
|
+
readonly resourceType: string;
|
|
42
|
+
/**
|
|
43
|
+
* The labels of load balancer time series and alerts identifying a backend of this kind within a project.
|
|
44
|
+
*/
|
|
45
|
+
readonly keyLabels: readonly MonitoringLabel[];
|
|
46
|
+
/**
|
|
47
|
+
* Lists the backends of this kind. It throws if some backends cannot be listed.
|
|
48
|
+
*
|
|
49
|
+
* @param context The context, from which the `GoogleApisService` is obtained.
|
|
50
|
+
* @param projects The projects of the graph's URL maps, in which backends are listed.
|
|
51
|
+
* @param resources The resources of the kind's types, e.g. to only list regional backends in their regions.
|
|
52
|
+
* @returns The backends.
|
|
53
|
+
*/
|
|
54
|
+
list(context: WorkspaceContext, projects: readonly string[], resources: readonly GoogleResource[]): Promise<LoadBalancerBackend[]>;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Serverless network endpoint groups, routing requests to Cloud Run services.
|
|
58
|
+
* They are listed in the regions of the services, in the projects of the URL maps.
|
|
59
|
+
*/
|
|
60
|
+
export declare const SERVERLESS_NEG_BACKENDS: LoadBalancerBackendKind;
|
|
61
|
+
/**
|
|
62
|
+
* The kinds of backends through which requests can be routed to nodes.
|
|
63
|
+
*/
|
|
64
|
+
export declare const LOAD_BALANCER_BACKEND_KINDS: readonly LoadBalancerBackendKind[];
|
|
65
|
+
/**
|
|
66
|
+
* Lists the backends of all kinds, in the projects of the URL maps of the graph. No API is called if the graph has no
|
|
67
|
+
* URL map. The fact fails if some backends cannot be listed.
|
|
68
|
+
*/
|
|
69
|
+
export declare class LoadBalancerBackendsFact extends GraphEnvironmentFact<readonly LoadBalancerBackend[]> {
|
|
70
|
+
compute(environment: GraphEnvironmentContext): Promise<GraphFactOutput<readonly LoadBalancerBackend[]>>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Builds the mapping of the backends of a kind, from load balancer labels to the resources they route requests to.
|
|
74
|
+
*
|
|
75
|
+
* @param kind The kind of backends.
|
|
76
|
+
* @returns The mapping.
|
|
77
|
+
*/
|
|
78
|
+
export declare function loadBalancerBackendMapping(kind: LoadBalancerBackendKind): MonitoringResourceMapping;
|
|
79
|
+
/**
|
|
80
|
+
* The mappings of load balancer labels to the resources backends route requests to, one per kind of backend.
|
|
81
|
+
*/
|
|
82
|
+
export declare const LOAD_BALANCER_BACKEND_MAPPINGS: readonly MonitoringResourceMapping[];
|