Skip to content

Commit 2006eca

Browse files
authored
Add plugin status API (#75819)
1 parent 4bd9cce commit 2006eca

21 files changed

+916
-32
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) &gt; [dependencies$](./kibana-plugin-core-server.statusservicesetup.dependencies_.md)
4+
5+
## StatusServiceSetup.dependencies$ property
6+
7+
Current status for all plugins this plugin depends on. Each key of the `Record` is a plugin id.
8+
9+
<b>Signature:</b>
10+
11+
```typescript
12+
dependencies$: Observable<Record<string, ServiceStatus>>;
13+
```
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) &gt; [derivedStatus$](./kibana-plugin-core-server.statusservicesetup.derivedstatus_.md)
4+
5+
## StatusServiceSetup.derivedStatus$ property
6+
7+
The status of this plugin as derived from its dependencies.
8+
9+
<b>Signature:</b>
10+
11+
```typescript
12+
derivedStatus$: Observable<ServiceStatus>;
13+
```
14+
15+
## Remarks
16+
17+
By default, plugins inherit this derived status from their dependencies. Calling overrides this default status.
18+
19+
This may emit multliple times for a single status change event as propagates through the dependency tree
20+

docs/development/core/server/kibana-plugin-core-server.statusservicesetup.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,73 @@ API for accessing status of Core and this plugin's dependencies as well as for c
1212
export interface StatusServiceSetup
1313
```
1414

15+
## Remarks
16+
17+
By default, a plugin inherits it's current status from the most severe status level of any Core services and any plugins that it depends on. This default status is available on the API.
18+
19+
Plugins may customize their status calculation by calling the API with an Observable. Within this Observable, a plugin may choose to only depend on the status of some of its dependencies, to ignore severe status levels of particular Core services they are not concerned with, or to make its status dependent on other external services.
20+
21+
## Example 1
22+
23+
Customize a plugin's status to only depend on the status of SavedObjects:
24+
25+
```ts
26+
core.status.set(
27+
core.status.core$.pipe(
28+
. map((coreStatus) => {
29+
return coreStatus.savedObjects;
30+
}) ;
31+
);
32+
);
33+
34+
```
35+
36+
## Example 2
37+
38+
Customize a plugin's status to include an external service:
39+
40+
```ts
41+
const externalStatus$ = interval(1000).pipe(
42+
switchMap(async () => {
43+
const resp = await fetch(`https://myexternaldep.com/_healthz`);
44+
const body = await resp.json();
45+
if (body.ok) {
46+
return of({ level: ServiceStatusLevels.available, summary: 'External Service is up'});
47+
} else {
48+
return of({ level: ServiceStatusLevels.available, summary: 'External Service is unavailable'});
49+
}
50+
}),
51+
catchError((error) => {
52+
of({ level: ServiceStatusLevels.unavailable, summary: `External Service is down`, meta: { error }})
53+
})
54+
);
55+
56+
core.status.set(
57+
combineLatest([core.status.derivedStatus$, externalStatus$]).pipe(
58+
map(([derivedStatus, externalStatus]) => {
59+
if (externalStatus.level > derivedStatus) {
60+
return externalStatus;
61+
} else {
62+
return derivedStatus;
63+
}
64+
})
65+
)
66+
);
67+
68+
```
69+
1570
## Properties
1671

1772
| Property | Type | Description |
1873
| --- | --- | --- |
1974
| [core$](./kibana-plugin-core-server.statusservicesetup.core_.md) | <code>Observable&lt;CoreStatus&gt;</code> | Current status for all Core services. |
75+
| [dependencies$](./kibana-plugin-core-server.statusservicesetup.dependencies_.md) | <code>Observable&lt;Record&lt;string, ServiceStatus&gt;&gt;</code> | Current status for all plugins this plugin depends on. Each key of the <code>Record</code> is a plugin id. |
76+
| [derivedStatus$](./kibana-plugin-core-server.statusservicesetup.derivedstatus_.md) | <code>Observable&lt;ServiceStatus&gt;</code> | The status of this plugin as derived from its dependencies. |
2077
| [overall$](./kibana-plugin-core-server.statusservicesetup.overall_.md) | <code>Observable&lt;ServiceStatus&gt;</code> | Overall system status for all of Kibana. |
2178

79+
## Methods
80+
81+
| Method | Description |
82+
| --- | --- |
83+
| [set(status$)](./kibana-plugin-core-server.statusservicesetup.set.md) | Allows a plugin to specify a custom status dependent on its own criteria. Completely overrides the default inherited status. |
84+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) &gt; [set](./kibana-plugin-core-server.statusservicesetup.set.md)
4+
5+
## StatusServiceSetup.set() method
6+
7+
Allows a plugin to specify a custom status dependent on its own criteria. Completely overrides the default inherited status.
8+
9+
<b>Signature:</b>
10+
11+
```typescript
12+
set(status$: Observable<ServiceStatus>): void;
13+
```
14+
15+
## Parameters
16+
17+
| Parameter | Type | Description |
18+
| --- | --- | --- |
19+
| status$ | <code>Observable&lt;ServiceStatus&gt;</code> | |
20+
21+
<b>Returns:</b>
22+
23+
`void`
24+
25+
## Remarks
26+
27+
See the [StatusServiceSetup.derivedStatus$](./kibana-plugin-core-server.statusservicesetup.derivedstatus_.md) API for leveraging the default status calculation that is provided by Core.
28+

rfcs/text/0010_service_status.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ interface StatusSetup {
137137
* Current status for all dependencies of the current plugin.
138138
* Each key of the `Record` is a plugin id.
139139
*/
140-
plugins$: Observable<Record<string, ServiceStatus>>;
140+
dependencies$: Observable<Record<string, ServiceStatus>>;
141141

142142
/**
143143
* The status of this plugin as derived from its dependencies.

src/core/server/legacy/legacy_service.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,9 @@ export class LegacyService implements CoreService {
323323
status: {
324324
core$: setupDeps.core.status.core$,
325325
overall$: setupDeps.core.status.overall$,
326+
set: setupDeps.core.status.plugins.set.bind(null, 'legacy'),
327+
dependencies$: setupDeps.core.status.plugins.getDependenciesStatus$('legacy'),
328+
derivedStatus$: setupDeps.core.status.plugins.getDerivedStatus$('legacy'),
326329
},
327330
uiSettings: {
328331
register: setupDeps.core.uiSettings.register,

src/core/server/plugins/plugin_context.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ export function createPluginSetupContext<TPlugin, TPluginDependencies>(
185185
status: {
186186
core$: deps.status.core$,
187187
overall$: deps.status.overall$,
188+
set: deps.status.plugins.set.bind(null, plugin.name),
189+
dependencies$: deps.status.plugins.getDependenciesStatus$(plugin.name),
190+
derivedStatus$: deps.status.plugins.getDerivedStatus$(plugin.name),
188191
},
189192
uiSettings: {
190193
register: deps.uiSettings.register,

src/core/server/plugins/plugins_system.test.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,27 @@ test('getPluginDependencies returns dependency tree of symbols', () => {
100100
pluginsSystem.addPlugin(createPlugin('no-dep'));
101101

102102
expect(pluginsSystem.getPluginDependencies()).toMatchInlineSnapshot(`
103-
Map {
104-
Symbol(plugin-a) => Array [
105-
Symbol(no-dep),
106-
],
107-
Symbol(plugin-b) => Array [
108-
Symbol(plugin-a),
109-
Symbol(no-dep),
110-
],
111-
Symbol(no-dep) => Array [],
103+
Object {
104+
"asNames": Map {
105+
"plugin-a" => Array [
106+
"no-dep",
107+
],
108+
"plugin-b" => Array [
109+
"plugin-a",
110+
"no-dep",
111+
],
112+
"no-dep" => Array [],
113+
},
114+
"asOpaqueIds": Map {
115+
Symbol(plugin-a) => Array [
116+
Symbol(no-dep),
117+
],
118+
Symbol(plugin-b) => Array [
119+
Symbol(plugin-a),
120+
Symbol(no-dep),
121+
],
122+
Symbol(no-dep) => Array [],
123+
},
112124
}
113125
`);
114126
});

src/core/server/plugins/plugins_system.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@
2020
import { CoreContext } from '../core_context';
2121
import { Logger } from '../logging';
2222
import { PluginWrapper } from './plugin';
23-
import { DiscoveredPlugin, PluginName, PluginOpaqueId } from './types';
23+
import { DiscoveredPlugin, PluginName } from './types';
2424
import { createPluginSetupContext, createPluginStartContext } from './plugin_context';
2525
import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service';
2626
import { withTimeout } from '../../utils';
27+
import { PluginDependencies } from '.';
2728

2829
const Sec = 1000;
2930
/** @internal */
@@ -45,9 +46,19 @@ export class PluginsSystem {
4546
* @returns a ReadonlyMap of each plugin and an Array of its available dependencies
4647
* @internal
4748
*/
48-
public getPluginDependencies(): ReadonlyMap<PluginOpaqueId, PluginOpaqueId[]> {
49-
// Return dependency map of opaque ids
50-
return new Map(
49+
public getPluginDependencies(): PluginDependencies {
50+
const asNames = new Map(
51+
[...this.plugins].map(([name, plugin]) => [
52+
plugin.name,
53+
[
54+
...new Set([
55+
...plugin.requiredPlugins,
56+
...plugin.optionalPlugins.filter((optPlugin) => this.plugins.has(optPlugin)),
57+
]),
58+
].map((depId) => this.plugins.get(depId)!.name),
59+
])
60+
);
61+
const asOpaqueIds = new Map(
5162
[...this.plugins].map(([name, plugin]) => [
5263
plugin.opaqueId,
5364
[
@@ -58,6 +69,8 @@ export class PluginsSystem {
5869
].map((depId) => this.plugins.get(depId)!.opaqueId),
5970
])
6071
);
72+
73+
return { asNames, asOpaqueIds };
6174
}
6275

6376
public async setupPlugins(deps: PluginsServiceSetupDeps) {

src/core/server/plugins/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ export type PluginName = string;
9393
/** @public */
9494
export type PluginOpaqueId = symbol;
9595

96+
/** @internal */
97+
export interface PluginDependencies {
98+
asNames: ReadonlyMap<PluginName, PluginName[]>;
99+
asOpaqueIds: ReadonlyMap<PluginOpaqueId, PluginOpaqueId[]>;
100+
}
101+
96102
/**
97103
* Describes the set of required and optional properties plugin can define in its
98104
* mandatory JSON manifest file.

0 commit comments

Comments
 (0)