@saptools/cf-events 0.1.4 → 0.2.1
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 +31 -29
- package/dist/cli.js +253 -37
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +63 -15
- package/dist/index.js +244 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -39,8 +39,11 @@ function parseDuration(raw) {
|
|
|
39
39
|
}
|
|
40
40
|
return amount * unitMs;
|
|
41
41
|
}
|
|
42
|
+
function toCfAuditTimestamp(date) {
|
|
43
|
+
return date.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
44
|
+
}
|
|
42
45
|
function durationToCreatedAfter(raw, now) {
|
|
43
|
-
return new Date(now.getTime() - parseDuration(raw))
|
|
46
|
+
return toCfAuditTimestamp(new Date(now.getTime() - parseDuration(raw)));
|
|
44
47
|
}
|
|
45
48
|
function parseTypeFilter(raw) {
|
|
46
49
|
if (raw === void 0) {
|
|
@@ -88,6 +91,39 @@ function summarizeCrashes(appName, events) {
|
|
|
88
91
|
crashes
|
|
89
92
|
};
|
|
90
93
|
}
|
|
94
|
+
function crashAppName(event) {
|
|
95
|
+
if (event.target.name.length > 0) {
|
|
96
|
+
return event.target.name;
|
|
97
|
+
}
|
|
98
|
+
if (event.target.guid.length > 0) {
|
|
99
|
+
return event.target.guid;
|
|
100
|
+
}
|
|
101
|
+
return "(unknown app)";
|
|
102
|
+
}
|
|
103
|
+
function summarizeSpaceCrashes(selector, events) {
|
|
104
|
+
const byApp = /* @__PURE__ */ new Map();
|
|
105
|
+
for (const event of events.filter(isCrashEvent)) {
|
|
106
|
+
const appName = crashAppName(event);
|
|
107
|
+
byApp.set(appName, [...byApp.get(appName) ?? [], event]);
|
|
108
|
+
}
|
|
109
|
+
const apps = [...byApp.entries()].map(([appName, appEvents]) => summarizeCrashes(appName, appEvents)).map(({ appName, crashCount, lastCrashAt, lastCrashReason, crashes }) => ({
|
|
110
|
+
appName,
|
|
111
|
+
crashCount,
|
|
112
|
+
lastCrashAt,
|
|
113
|
+
lastCrashReason,
|
|
114
|
+
crashes
|
|
115
|
+
})).sort((left, right) => {
|
|
116
|
+
const byTime = (right.lastCrashAt ?? "").localeCompare(left.lastCrashAt ?? "");
|
|
117
|
+
return byTime === 0 ? left.appName.localeCompare(right.appName) : byTime;
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
scope: "space",
|
|
121
|
+
selector,
|
|
122
|
+
crashCount: apps.reduce((sum, app) => sum + app.crashCount, 0),
|
|
123
|
+
lastCrashAt: apps[0]?.lastCrashAt,
|
|
124
|
+
apps
|
|
125
|
+
};
|
|
126
|
+
}
|
|
91
127
|
|
|
92
128
|
// src/format.ts
|
|
93
129
|
var EVENT_LABELS = {
|
|
@@ -188,6 +224,34 @@ function formatEventsReport(appName, events, now) {
|
|
|
188
224
|
...events.map((event) => formatEventLine(event, now))
|
|
189
225
|
].join("\n");
|
|
190
226
|
}
|
|
227
|
+
function eventTargetLabel(event) {
|
|
228
|
+
if (event.target.name.length > 0) {
|
|
229
|
+
return event.target.name;
|
|
230
|
+
}
|
|
231
|
+
if (event.target.type.length > 0) {
|
|
232
|
+
return event.target.type;
|
|
233
|
+
}
|
|
234
|
+
return event.target.guid.length > 0 ? event.target.guid : "(unknown target)";
|
|
235
|
+
}
|
|
236
|
+
function formatSpaceEventLine(event, now) {
|
|
237
|
+
const actor = event.actor.name.length > 0 ? event.actor.name : event.actor.type;
|
|
238
|
+
return [
|
|
239
|
+
` ${event.createdAt.padEnd(26)}`,
|
|
240
|
+
formatRelativeTime(event.createdAt, now).padEnd(11),
|
|
241
|
+
describeEventType(event.type).padEnd(24),
|
|
242
|
+
eventTargetLabel(event).padEnd(18),
|
|
243
|
+
actor.length > 0 ? actor : "(unknown actor)"
|
|
244
|
+
].join(" ");
|
|
245
|
+
}
|
|
246
|
+
function formatSpaceEventsReport(selector, events, now) {
|
|
247
|
+
if (events.length === 0) {
|
|
248
|
+
return `No audit events found for space ${selector}.`;
|
|
249
|
+
}
|
|
250
|
+
return [
|
|
251
|
+
`Audit events for space ${selector} (${events.length.toString()}):`,
|
|
252
|
+
...events.map((event) => formatSpaceEventLine(event, now))
|
|
253
|
+
].join("\n");
|
|
254
|
+
}
|
|
191
255
|
function formatSshStatusReport(status, now) {
|
|
192
256
|
const sessionLines = status.sessions.map((session) => {
|
|
193
257
|
const tag = session.likelyActive ? "[active]" : "[past] ";
|
|
@@ -212,7 +276,7 @@ function formatSshStatusReport(status, now) {
|
|
|
212
276
|
" are inferred from recent ssh-authorized audit events."
|
|
213
277
|
].join("\n");
|
|
214
278
|
}
|
|
215
|
-
function
|
|
279
|
+
function formatAppCrashReport(summary, now) {
|
|
216
280
|
if (summary.crashCount === 0) {
|
|
217
281
|
return `No crashes found for ${summary.appName}.`;
|
|
218
282
|
}
|
|
@@ -233,6 +297,35 @@ function formatCrashReport(summary, now) {
|
|
|
233
297
|
...crashLines
|
|
234
298
|
].join("\n");
|
|
235
299
|
}
|
|
300
|
+
function formatSpaceCrashReport(summary, now) {
|
|
301
|
+
if (summary.crashCount === 0) {
|
|
302
|
+
return `No crashes found for space ${summary.selector}.`;
|
|
303
|
+
}
|
|
304
|
+
const appLines = summary.apps.flatMap((app) => [
|
|
305
|
+
` ${app.appName} (${app.crashCount.toString()})`,
|
|
306
|
+
...app.crashes.map((crash) => {
|
|
307
|
+
const index = crash.index === void 0 ? "-" : `#${crash.index.toString()}`;
|
|
308
|
+
const reason = crash.reason ?? "unknown";
|
|
309
|
+
const exit = crash.exitStatus === void 0 ? "" : ` exit ${crash.exitStatus.toString()}`;
|
|
310
|
+
return ` ${crash.at} instance ${index} ${reason}${exit}`;
|
|
311
|
+
})
|
|
312
|
+
]);
|
|
313
|
+
return [
|
|
314
|
+
`Crash report for space ${summary.selector}`,
|
|
315
|
+
"",
|
|
316
|
+
` Crashes: ${summary.crashCount.toString()}`,
|
|
317
|
+
` Apps affected: ${summary.apps.length.toString()}`,
|
|
318
|
+
...summary.lastCrashAt === void 0 ? [] : [` Last crash: ${summary.lastCrashAt} (${formatRelativeTime(summary.lastCrashAt, now)})`],
|
|
319
|
+
"",
|
|
320
|
+
...appLines
|
|
321
|
+
].join("\n");
|
|
322
|
+
}
|
|
323
|
+
function formatCrashReport(summary, now) {
|
|
324
|
+
if ("scope" in summary) {
|
|
325
|
+
return formatSpaceCrashReport(summary, now);
|
|
326
|
+
}
|
|
327
|
+
return formatAppCrashReport(summary, now);
|
|
328
|
+
}
|
|
236
329
|
function formatInstanceLine(instance) {
|
|
237
330
|
return [
|
|
238
331
|
` #${instance.index.toString()}`.padEnd(6),
|
|
@@ -278,6 +371,12 @@ function asRecord(value) {
|
|
|
278
371
|
function asArray(value) {
|
|
279
372
|
return Array.isArray(value) ? value : [];
|
|
280
373
|
}
|
|
374
|
+
function requireArray(value, message) {
|
|
375
|
+
if (!Array.isArray(value)) {
|
|
376
|
+
throw new Error(message);
|
|
377
|
+
}
|
|
378
|
+
return value;
|
|
379
|
+
}
|
|
281
380
|
function asString(value) {
|
|
282
381
|
return typeof value === "string" ? value : "";
|
|
283
382
|
}
|
|
@@ -327,7 +426,7 @@ function mapProcessStat(value) {
|
|
|
327
426
|
}
|
|
328
427
|
function buildAuditEventsPath(input, perPage) {
|
|
329
428
|
const params = new URLSearchParams();
|
|
330
|
-
params
|
|
429
|
+
applyAuditEventScope(params, input.scope);
|
|
331
430
|
params.set("order_by", "-created_at");
|
|
332
431
|
params.set("per_page", perPage.toString());
|
|
333
432
|
if (input.types !== void 0 && input.types.length > 0) {
|
|
@@ -338,6 +437,60 @@ function buildAuditEventsPath(input, perPage) {
|
|
|
338
437
|
}
|
|
339
438
|
return `/v3/audit_events?${params.toString()}`;
|
|
340
439
|
}
|
|
440
|
+
function applyAuditEventScope(params, scope) {
|
|
441
|
+
if (scope.kind === "app") {
|
|
442
|
+
params.set("target_guids", scope.appGuid);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
params.set("space_guids", scope.spaceGuid);
|
|
446
|
+
}
|
|
447
|
+
async function resolveOrganizationGuid(orgName, curl) {
|
|
448
|
+
const params = new URLSearchParams();
|
|
449
|
+
params.set("names", orgName);
|
|
450
|
+
const body = asRecord(parseJson(await curl(`/v3/organizations?${params.toString()}`)));
|
|
451
|
+
const first = asRecord(asArray(body["resources"])[0]);
|
|
452
|
+
const guid = asString(first["guid"]);
|
|
453
|
+
if (guid.length === 0) {
|
|
454
|
+
throw new Error(`Could not resolve the GUID for organization "${orgName}".`);
|
|
455
|
+
}
|
|
456
|
+
return guid;
|
|
457
|
+
}
|
|
458
|
+
async function resolveSpaceGuid(spaceName, orgGuid, curl) {
|
|
459
|
+
const params = new URLSearchParams();
|
|
460
|
+
params.set("names", spaceName);
|
|
461
|
+
params.set("organization_guids", orgGuid);
|
|
462
|
+
const body = asRecord(parseJson(await curl(`/v3/spaces?${params.toString()}`)));
|
|
463
|
+
const first = asRecord(asArray(body["resources"])[0]);
|
|
464
|
+
const guid = asString(first["guid"]);
|
|
465
|
+
if (guid.length === 0) {
|
|
466
|
+
throw new Error(`Could not resolve the GUID for space "${spaceName}".`);
|
|
467
|
+
}
|
|
468
|
+
return guid;
|
|
469
|
+
}
|
|
470
|
+
function formatCfApiError(error) {
|
|
471
|
+
const record = asRecord(error);
|
|
472
|
+
const title = asString(record["title"]);
|
|
473
|
+
const detail = asString(record["detail"]);
|
|
474
|
+
if (title.length > 0 && detail.length > 0) {
|
|
475
|
+
return `${title}: ${detail}`;
|
|
476
|
+
}
|
|
477
|
+
if (title.length > 0) {
|
|
478
|
+
return title;
|
|
479
|
+
}
|
|
480
|
+
if (detail.length > 0) {
|
|
481
|
+
return detail;
|
|
482
|
+
}
|
|
483
|
+
const code = asNumber(record["code"]);
|
|
484
|
+
return code === void 0 ? "unknown CF API error" : `code ${code.toString()}`;
|
|
485
|
+
}
|
|
486
|
+
function throwIfCfApiErrors(body) {
|
|
487
|
+
const errors = asRecord(body)["errors"];
|
|
488
|
+
if (errors === void 0) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const details = requireArray(errors, "The CF API returned an invalid errors response.").map(formatCfApiError).filter((detail) => detail.length > 0);
|
|
492
|
+
throw new Error(`CF API error ${details.length > 0 ? details.join("; ") : "unknown CF API error"}`);
|
|
493
|
+
}
|
|
341
494
|
function nextPagePath(body) {
|
|
342
495
|
const next = asRecord(asRecord(asRecord(body)["pagination"])["next"]);
|
|
343
496
|
const href = next["href"];
|
|
@@ -358,7 +511,11 @@ async function fetchAuditEvents(input, curl) {
|
|
|
358
511
|
let path = buildAuditEventsPath(input, perPage);
|
|
359
512
|
while (path !== void 0 && events.length < limit) {
|
|
360
513
|
const body = parseJson(await curl(path));
|
|
361
|
-
|
|
514
|
+
throwIfCfApiErrors(body);
|
|
515
|
+
for (const resource of requireArray(
|
|
516
|
+
asRecord(body)["resources"],
|
|
517
|
+
"The CF API returned an invalid audit events response: missing resources array."
|
|
518
|
+
)) {
|
|
362
519
|
events.push(mapAuditEvent(resource));
|
|
363
520
|
if (events.length >= limit) {
|
|
364
521
|
break;
|
|
@@ -626,7 +783,12 @@ function extractSafeCliDetail(error) {
|
|
|
626
783
|
}
|
|
627
784
|
|
|
628
785
|
// src/selector.ts
|
|
629
|
-
var SELECTOR_USAGE = 'use "region/org/space/app" or a bare app name';
|
|
786
|
+
var SELECTOR_USAGE = 'use "region/org/space", "region/org/space/app", or a bare app name';
|
|
787
|
+
function assertNonEmpty(raw, parts, shape) {
|
|
788
|
+
if (parts.some((part) => part.length === 0)) {
|
|
789
|
+
throw new Error(`Invalid selector "${raw}": every ${shape} segment must be non-empty.`);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
630
792
|
function parseSelector(raw) {
|
|
631
793
|
const trimmed = raw.trim();
|
|
632
794
|
if (trimmed.length === 0) {
|
|
@@ -640,12 +802,21 @@ function parseSelector(raw) {
|
|
|
640
802
|
}
|
|
641
803
|
return { kind: "appName", appName };
|
|
642
804
|
}
|
|
805
|
+
if (parts.length === 3) {
|
|
806
|
+
assertNonEmpty(raw, parts, "region/org/space");
|
|
807
|
+
const [regionKey, orgName, spaceName] = parts;
|
|
808
|
+
return { kind: "spacePath", regionKey: regionKey ?? "", orgName: orgName ?? "", spaceName: spaceName ?? "" };
|
|
809
|
+
}
|
|
643
810
|
if (parts.length === 4) {
|
|
811
|
+
assertNonEmpty(raw, parts, "region/org/space/app");
|
|
644
812
|
const [regionKey, orgName, spaceName, appName] = parts;
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
813
|
+
return {
|
|
814
|
+
kind: "appPath",
|
|
815
|
+
regionKey: regionKey ?? "",
|
|
816
|
+
orgName: orgName ?? "",
|
|
817
|
+
spaceName: spaceName ?? "",
|
|
818
|
+
appName: appName ?? ""
|
|
819
|
+
};
|
|
649
820
|
}
|
|
650
821
|
throw new Error(`Invalid selector "${raw}": ${SELECTOR_USAGE}.`);
|
|
651
822
|
}
|
|
@@ -659,6 +830,7 @@ async function resolveSelector(raw) {
|
|
|
659
830
|
);
|
|
660
831
|
}
|
|
661
832
|
return {
|
|
833
|
+
kind: "app",
|
|
662
834
|
raw,
|
|
663
835
|
regionKey: current.regionKey ?? "",
|
|
664
836
|
apiEndpoint: current.apiEndpoint,
|
|
@@ -673,7 +845,18 @@ async function resolveSelector(raw) {
|
|
|
673
845
|
`Unknown region "${parsed.regionKey}". Use a bare app name (requires current CF target) or a known region key.`
|
|
674
846
|
);
|
|
675
847
|
}
|
|
848
|
+
if (parsed.kind === "spacePath") {
|
|
849
|
+
return {
|
|
850
|
+
kind: "space",
|
|
851
|
+
raw,
|
|
852
|
+
regionKey: parsed.regionKey,
|
|
853
|
+
apiEndpoint,
|
|
854
|
+
orgName: parsed.orgName,
|
|
855
|
+
spaceName: parsed.spaceName
|
|
856
|
+
};
|
|
857
|
+
}
|
|
676
858
|
return {
|
|
859
|
+
kind: "app",
|
|
677
860
|
raw,
|
|
678
861
|
regionKey: parsed.regionKey,
|
|
679
862
|
apiEndpoint,
|
|
@@ -724,10 +907,12 @@ function createCfClient(curl) {
|
|
|
724
907
|
fetchAuditEvents: (input) => fetchAuditEvents(input, curl),
|
|
725
908
|
fetchApp: (appGuid) => fetchApp(appGuid, curl),
|
|
726
909
|
fetchSshEnabled: (appGuid) => fetchSshEnabled(appGuid, curl),
|
|
727
|
-
fetchWebProcessStats: (appGuid) => fetchWebProcessStats(appGuid, curl)
|
|
910
|
+
fetchWebProcessStats: (appGuid) => fetchWebProcessStats(appGuid, curl),
|
|
911
|
+
resolveOrganizationGuid: (orgName) => resolveOrganizationGuid(orgName, curl),
|
|
912
|
+
resolveSpaceGuid: (spaceName, orgGuid) => resolveSpaceGuid(spaceName, orgGuid, curl)
|
|
728
913
|
};
|
|
729
914
|
}
|
|
730
|
-
async function
|
|
915
|
+
async function defaultWithCfTarget(selector, credentials, work) {
|
|
731
916
|
return await withCfSession(async (ctx) => {
|
|
732
917
|
await prepareCfCliSession(
|
|
733
918
|
{
|
|
@@ -738,15 +923,14 @@ async function defaultWithCfApp(selector, credentials, work) {
|
|
|
738
923
|
},
|
|
739
924
|
ctx
|
|
740
925
|
);
|
|
741
|
-
const appGuid = await cfAppGuid(selector.appName, ctx);
|
|
742
926
|
const client = createCfClient((path) => cfCurl(path, ctx));
|
|
743
|
-
return await work({
|
|
927
|
+
return await work({ selector, client, resolveAppGuid: (appName) => cfAppGuid(appName, ctx) });
|
|
744
928
|
});
|
|
745
929
|
}
|
|
746
930
|
function createDefaultDependencies() {
|
|
747
931
|
return {
|
|
748
932
|
resolveSelector,
|
|
749
|
-
|
|
933
|
+
withCfTarget: defaultWithCfTarget,
|
|
750
934
|
now: () => /* @__PURE__ */ new Date()
|
|
751
935
|
};
|
|
752
936
|
}
|
|
@@ -756,6 +940,24 @@ async function delay(ms, signal) {
|
|
|
756
940
|
} catch {
|
|
757
941
|
}
|
|
758
942
|
}
|
|
943
|
+
function requireAppSelector(selector, command) {
|
|
944
|
+
if (selector.kind === "app") {
|
|
945
|
+
return selector;
|
|
946
|
+
}
|
|
947
|
+
throw new Error(
|
|
948
|
+
`The ${command} command requires an app selector. Use region/org/space/app or a bare app name.`
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
async function resolveAuditScope(session) {
|
|
952
|
+
if (session.selector.kind === "app") {
|
|
953
|
+
return { kind: "app", appGuid: await session.resolveAppGuid(session.selector.appName) };
|
|
954
|
+
}
|
|
955
|
+
const orgGuid = await session.client.resolveOrganizationGuid(session.selector.orgName);
|
|
956
|
+
return {
|
|
957
|
+
kind: "space",
|
|
958
|
+
spaceGuid: await session.client.resolveSpaceGuid(session.selector.spaceName, orgGuid)
|
|
959
|
+
};
|
|
960
|
+
}
|
|
759
961
|
var CfEventsRuntime = class {
|
|
760
962
|
deps;
|
|
761
963
|
constructor(deps = createDefaultDependencies()) {
|
|
@@ -763,9 +965,9 @@ var CfEventsRuntime = class {
|
|
|
763
965
|
}
|
|
764
966
|
async fetchEvents(raw, credentials, options) {
|
|
765
967
|
const selector = await this.deps.resolveSelector(raw);
|
|
766
|
-
return await this.deps.
|
|
767
|
-
return await client.fetchAuditEvents({
|
|
768
|
-
|
|
968
|
+
return await this.deps.withCfTarget(selector, credentials, async (session) => {
|
|
969
|
+
return await session.client.fetchAuditEvents({
|
|
970
|
+
scope: await resolveAuditScope(session),
|
|
769
971
|
types: options.types.length > 0 ? options.types : void 0,
|
|
770
972
|
createdAfter: this.resolveCreatedAfter(options.since),
|
|
771
973
|
limit: options.limit
|
|
@@ -773,12 +975,13 @@ var CfEventsRuntime = class {
|
|
|
773
975
|
});
|
|
774
976
|
}
|
|
775
977
|
async getSshStatus(raw, credentials, since) {
|
|
776
|
-
const selector = await this.deps.resolveSelector(raw);
|
|
777
|
-
return await this.deps.
|
|
978
|
+
const selector = requireAppSelector(await this.deps.resolveSelector(raw), "ssh-status");
|
|
979
|
+
return await this.deps.withCfTarget(selector, credentials, async ({ client, resolveAppGuid }) => {
|
|
980
|
+
const appGuid = await resolveAppGuid(selector.appName);
|
|
778
981
|
const [sshEnabled, events] = await Promise.all([
|
|
779
982
|
client.fetchSshEnabled(appGuid),
|
|
780
983
|
client.fetchAuditEvents({
|
|
781
|
-
appGuid,
|
|
984
|
+
scope: { kind: "app", appGuid },
|
|
782
985
|
types: [...SSH_EVENT_TYPES],
|
|
783
986
|
createdAfter: durationToCreatedAfter(since, this.deps.now()),
|
|
784
987
|
limit: SSH_STATUS_EVENT_LIMIT
|
|
@@ -795,24 +998,28 @@ var CfEventsRuntime = class {
|
|
|
795
998
|
}
|
|
796
999
|
async getCrashes(raw, credentials, options) {
|
|
797
1000
|
const selector = await this.deps.resolveSelector(raw);
|
|
798
|
-
return await this.deps.
|
|
799
|
-
const events = await client.fetchAuditEvents({
|
|
800
|
-
|
|
1001
|
+
return await this.deps.withCfTarget(selector, credentials, async (session) => {
|
|
1002
|
+
const events = await session.client.fetchAuditEvents({
|
|
1003
|
+
scope: await resolveAuditScope(session),
|
|
801
1004
|
types: [...CRASH_EVENT_TYPES],
|
|
802
1005
|
createdAfter: this.resolveCreatedAfter(options.since),
|
|
803
1006
|
limit: options.limit
|
|
804
1007
|
});
|
|
805
|
-
|
|
1008
|
+
if (selector.kind === "app") {
|
|
1009
|
+
return summarizeCrashes(selector.appName, events);
|
|
1010
|
+
}
|
|
1011
|
+
return summarizeSpaceCrashes(`${selector.regionKey}/${selector.orgName}/${selector.spaceName}`, events);
|
|
806
1012
|
});
|
|
807
1013
|
}
|
|
808
1014
|
async getStatus(raw, credentials) {
|
|
809
|
-
const selector = await this.deps.resolveSelector(raw);
|
|
810
|
-
return await this.deps.
|
|
1015
|
+
const selector = requireAppSelector(await this.deps.resolveSelector(raw), "status");
|
|
1016
|
+
return await this.deps.withCfTarget(selector, credentials, async ({ client, resolveAppGuid }) => {
|
|
1017
|
+
const appGuid = await resolveAppGuid(selector.appName);
|
|
811
1018
|
const [app, instances, sshEnabled, recent] = await Promise.all([
|
|
812
1019
|
client.fetchApp(appGuid),
|
|
813
1020
|
client.fetchWebProcessStats(appGuid),
|
|
814
1021
|
client.fetchSshEnabled(appGuid),
|
|
815
|
-
client.fetchAuditEvents({ appGuid, types: void 0, createdAfter: void 0, limit: 1 })
|
|
1022
|
+
client.fetchAuditEvents({ scope: { kind: "app", appGuid }, types: void 0, createdAfter: void 0, limit: 1 })
|
|
816
1023
|
]);
|
|
817
1024
|
return {
|
|
818
1025
|
appName: selector.appName,
|
|
@@ -826,12 +1033,13 @@ var CfEventsRuntime = class {
|
|
|
826
1033
|
}
|
|
827
1034
|
async watchEvents(raw, credentials, options, onEvent, signal) {
|
|
828
1035
|
const selector = await this.deps.resolveSelector(raw);
|
|
829
|
-
await this.deps.
|
|
1036
|
+
await this.deps.withCfTarget(selector, credentials, async (session) => {
|
|
1037
|
+
const scope = await resolveAuditScope(session);
|
|
830
1038
|
const seen = /* @__PURE__ */ new Set();
|
|
831
1039
|
let cursor = durationToCreatedAfter(options.lookback, this.deps.now());
|
|
832
1040
|
while (!signal.aborted) {
|
|
833
|
-
const events = await client.fetchAuditEvents({
|
|
834
|
-
|
|
1041
|
+
const events = await session.client.fetchAuditEvents({
|
|
1042
|
+
scope,
|
|
835
1043
|
types: options.types.length > 0 ? options.types : void 0,
|
|
836
1044
|
createdAfter: cursor,
|
|
837
1045
|
limit: WATCH_FETCH_LIMIT
|
|
@@ -893,6 +1101,13 @@ function parsePositiveInteger(value, optionName) {
|
|
|
893
1101
|
function appLabel(selector) {
|
|
894
1102
|
return (selector.split("/").at(-1) ?? selector).trim();
|
|
895
1103
|
}
|
|
1104
|
+
function isSpaceSelector(selector) {
|
|
1105
|
+
return parseSelector(selector).kind === "spacePath";
|
|
1106
|
+
}
|
|
1107
|
+
function spaceLabel(selector) {
|
|
1108
|
+
const parsed = parseSelector(selector);
|
|
1109
|
+
return parsed.kind === "spacePath" ? `${parsed.regionKey}/${parsed.orgName}/${parsed.spaceName}` : selector;
|
|
1110
|
+
}
|
|
896
1111
|
function resolveSelectorArgument(selector) {
|
|
897
1112
|
return selector;
|
|
898
1113
|
}
|
|
@@ -920,7 +1135,7 @@ async function runEvents(selector, flags) {
|
|
|
920
1135
|
writeJson(events);
|
|
921
1136
|
return;
|
|
922
1137
|
}
|
|
923
|
-
writeOut(formatEventsReport(appLabel(resolvedSelector), events, /* @__PURE__ */ new Date()));
|
|
1138
|
+
writeOut(isSpaceSelector(resolvedSelector) ? formatSpaceEventsReport(spaceLabel(resolvedSelector), events, /* @__PURE__ */ new Date()) : formatEventsReport(appLabel(resolvedSelector), events, /* @__PURE__ */ new Date()));
|
|
924
1139
|
}
|
|
925
1140
|
async function runSshStatus(selector, flags) {
|
|
926
1141
|
const resolvedSelector = resolveSelectorArgument(selector);
|
|
@@ -959,12 +1174,12 @@ async function runStatus(selector, flags) {
|
|
|
959
1174
|
}
|
|
960
1175
|
writeOut(formatStatusReport(health, /* @__PURE__ */ new Date()));
|
|
961
1176
|
}
|
|
962
|
-
function emitWatchEvent(event, asJson) {
|
|
1177
|
+
function emitWatchEvent(event, asJson, spaceScope) {
|
|
963
1178
|
if (asJson) {
|
|
964
1179
|
writeJsonLine(event);
|
|
965
1180
|
return;
|
|
966
1181
|
}
|
|
967
|
-
writeOut(formatEventLine(event, /* @__PURE__ */ new Date()));
|
|
1182
|
+
writeOut(spaceScope ? formatSpaceEventLine(event, /* @__PURE__ */ new Date()) : formatEventLine(event, /* @__PURE__ */ new Date()));
|
|
968
1183
|
}
|
|
969
1184
|
async function runWatch(selector, flags) {
|
|
970
1185
|
const resolvedSelector = resolveSelectorArgument(selector);
|
|
@@ -978,6 +1193,7 @@ async function runWatch(selector, flags) {
|
|
|
978
1193
|
controller.abort();
|
|
979
1194
|
});
|
|
980
1195
|
const asJson = flags.json === true;
|
|
1196
|
+
const spaceScope = isSpaceSelector(resolvedSelector);
|
|
981
1197
|
try {
|
|
982
1198
|
process2.stderr.write(`Watching ${resolvedSelector} for new audit events. Press Ctrl+C to stop.
|
|
983
1199
|
`);
|
|
@@ -990,7 +1206,7 @@ async function runWatch(selector, flags) {
|
|
|
990
1206
|
types: parseTypeFilter(flags.type)
|
|
991
1207
|
},
|
|
992
1208
|
(event) => {
|
|
993
|
-
emitWatchEvent(event, asJson);
|
|
1209
|
+
emitWatchEvent(event, asJson, spaceScope);
|
|
994
1210
|
},
|
|
995
1211
|
controller.signal
|
|
996
1212
|
);
|
|
@@ -1036,7 +1252,7 @@ function buildProgram() {
|
|
|
1036
1252
|
const program = new Command();
|
|
1037
1253
|
program.name("cf-events").description("Inspect SAP BTP Cloud Foundry application audit events and SSH/debug sessions").version(readPackageVersion(), "-V, --version", "Print the cf-events package version");
|
|
1038
1254
|
addCommonOptions(
|
|
1039
|
-
program.command("events").description("List recent audit events for an app").argument("<selector>", "region/org/space/app or a bare app name")
|
|
1255
|
+
program.command("events").description("List recent audit events for an app or all apps in a space").argument("<selector>", "region/org/space, region/org/space/app, or a bare app name")
|
|
1040
1256
|
).option(
|
|
1041
1257
|
"--limit <count>",
|
|
1042
1258
|
"Maximum number of events to return",
|
|
@@ -1050,7 +1266,7 @@ function buildProgram() {
|
|
|
1050
1266
|
await runSshStatus(selector, flags);
|
|
1051
1267
|
});
|
|
1052
1268
|
addCommonOptions(
|
|
1053
|
-
program.command("crashes").description("Summarize recent crash events for an app").argument("<selector>", "region/org/space/app or a bare app name")
|
|
1269
|
+
program.command("crashes").description("Summarize recent crash events for an app or all apps in a space").argument("<selector>", "region/org/space, region/org/space/app, or a bare app name")
|
|
1054
1270
|
).option(
|
|
1055
1271
|
"--limit <count>",
|
|
1056
1272
|
"Maximum number of crash events to inspect",
|
|
@@ -1064,7 +1280,7 @@ function buildProgram() {
|
|
|
1064
1280
|
await runStatus(selector, flags);
|
|
1065
1281
|
});
|
|
1066
1282
|
addCommonOptions(
|
|
1067
|
-
program.command("watch").description("Poll for new audit events
|
|
1283
|
+
program.command("watch").description("Poll for new audit events for an app or all apps in a space").argument("<selector>", "region/org/space, region/org/space/app, or a bare app name")
|
|
1068
1284
|
).option(
|
|
1069
1285
|
"--interval <ms>",
|
|
1070
1286
|
"Polling interval in milliseconds",
|