mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
parent
1ec6f3b032
commit
76ac0c02db
67 changed files with 2009 additions and 27 deletions
|
@ -23,6 +23,7 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
|
|||
| [http](./kibana-plugin-core-server.coresetup.http.md) | <code>HttpServiceSetup</code> | [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) |
|
||||
| [metrics](./kibana-plugin-core-server.coresetup.metrics.md) | <code>MetricsServiceSetup</code> | [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) |
|
||||
| [savedObjects](./kibana-plugin-core-server.coresetup.savedobjects.md) | <code>SavedObjectsServiceSetup</code> | [SavedObjectsServiceSetup](./kibana-plugin-core-server.savedobjectsservicesetup.md) |
|
||||
| [status](./kibana-plugin-core-server.coresetup.status.md) | <code>StatusServiceSetup</code> | [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) |
|
||||
| [uiSettings](./kibana-plugin-core-server.coresetup.uisettings.md) | <code>UiSettingsServiceSetup</code> | [UiSettingsServiceSetup](./kibana-plugin-core-server.uisettingsservicesetup.md) |
|
||||
| [uuid](./kibana-plugin-core-server.coresetup.uuid.md) | <code>UuidServiceSetup</code> | [UuidServiceSetup](./kibana-plugin-core-server.uuidservicesetup.md) |
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreSetup](./kibana-plugin-core-server.coresetup.md) > [status](./kibana-plugin-core-server.coresetup.status.md)
|
||||
|
||||
## CoreSetup.status property
|
||||
|
||||
[StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md)
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
status: StatusServiceSetup;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreStatus](./kibana-plugin-core-server.corestatus.md) > [elasticsearch](./kibana-plugin-core-server.corestatus.elasticsearch.md)
|
||||
|
||||
## CoreStatus.elasticsearch property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
elasticsearch: ServiceStatus;
|
||||
```
|
|
@ -0,0 +1,21 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreStatus](./kibana-plugin-core-server.corestatus.md)
|
||||
|
||||
## CoreStatus interface
|
||||
|
||||
Status of core services.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface CoreStatus
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [elasticsearch](./kibana-plugin-core-server.corestatus.elasticsearch.md) | <code>ServiceStatus</code> | |
|
||||
| [savedObjects](./kibana-plugin-core-server.corestatus.savedobjects.md) | <code>ServiceStatus</code> | |
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreStatus](./kibana-plugin-core-server.corestatus.md) > [savedObjects](./kibana-plugin-core-server.corestatus.savedobjects.md)
|
||||
|
||||
## CoreStatus.savedObjects property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
savedObjects: ServiceStatus;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) > [incompatibleNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.incompatiblenodes.md)
|
||||
|
||||
## ElasticsearchStatusMeta.incompatibleNodes property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
incompatibleNodes: NodesVersionCompatibility['incompatibleNodes'];
|
||||
```
|
|
@ -0,0 +1,20 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md)
|
||||
|
||||
## ElasticsearchStatusMeta interface
|
||||
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface ElasticsearchStatusMeta
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [incompatibleNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.incompatiblenodes.md) | <code>NodesVersionCompatibility['incompatibleNodes']</code> | |
|
||||
| [warningNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.warningnodes.md) | <code>NodesVersionCompatibility['warningNodes']</code> | |
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) > [warningNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.warningnodes.md)
|
||||
|
||||
## ElasticsearchStatusMeta.warningNodes property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
warningNodes: NodesVersionCompatibility['warningNodes'];
|
||||
```
|
|
@ -66,6 +66,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| [ContextSetup](./kibana-plugin-core-server.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. |
|
||||
| [CoreSetup](./kibana-plugin-core-server.coresetup.md) | Context passed to the plugins <code>setup</code> method. |
|
||||
| [CoreStart](./kibana-plugin-core-server.corestart.md) | Context passed to the plugins <code>start</code> method. |
|
||||
| [CoreStatus](./kibana-plugin-core-server.corestatus.md) | Status of core services. |
|
||||
| [CustomHttpResponseOptions](./kibana-plugin-core-server.customhttpresponseoptions.md) | HTTP response parameters for a response with adjustable status code. |
|
||||
| [DeprecationAPIClientParams](./kibana-plugin-core-server.deprecationapiclientparams.md) | |
|
||||
| [DeprecationAPIResponse](./kibana-plugin-core-server.deprecationapiresponse.md) | |
|
||||
|
@ -75,6 +76,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| [ElasticsearchError](./kibana-plugin-core-server.elasticsearcherror.md) | |
|
||||
| [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) | |
|
||||
| [ElasticsearchServiceStart](./kibana-plugin-core-server.elasticsearchservicestart.md) | |
|
||||
| [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) | |
|
||||
| [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) | |
|
||||
| [ErrorHttpResponseOptions](./kibana-plugin-core-server.errorhttpresponseoptions.md) | HTTP response parameters |
|
||||
| [FakeRequest](./kibana-plugin-core-server.fakerequest.md) | Fake request object created manually by Kibana plugins. |
|
||||
|
@ -101,6 +103,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| [LoggerFactory](./kibana-plugin-core-server.loggerfactory.md) | The single purpose of <code>LoggerFactory</code> interface is to define a way to retrieve a context-based logger instance. |
|
||||
| [LogMeta](./kibana-plugin-core-server.logmeta.md) | Contextual metadata |
|
||||
| [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) | APIs to retrieves metrics gathered and exposed by the core platform. |
|
||||
| [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) | |
|
||||
| [OnPostAuthToolkit](./kibana-plugin-core-server.onpostauthtoolkit.md) | A tool set defining an outcome of OnPostAuth interceptor for incoming request. |
|
||||
| [OnPreAuthToolkit](./kibana-plugin-core-server.onpreauthtoolkit.md) | A tool set defining an outcome of OnPreAuth interceptor for incoming request. |
|
||||
| [OnPreResponseExtensions](./kibana-plugin-core-server.onpreresponseextensions.md) | Additional data to extend a response. |
|
||||
|
@ -162,15 +165,18 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-core-server.savedobjectsresolveimporterrorsoptions.md) | Options to control the "resolve import" operation. |
|
||||
| [SavedObjectsServiceSetup](./kibana-plugin-core-server.savedobjectsservicesetup.md) | Saved Objects is Kibana's data persistence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceSetup API exposes methods for registering Saved Object types, creating and registering Saved Object client wrappers and factories. |
|
||||
| [SavedObjectsServiceStart](./kibana-plugin-core-server.savedobjectsservicestart.md) | Saved Objects is Kibana's data persisentence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceStart API provides a scoped Saved Objects client for interacting with Saved Objects. |
|
||||
| [SavedObjectStatusMeta](./kibana-plugin-core-server.savedobjectstatusmeta.md) | Meta information about the SavedObjectService's status. Available to plugins via [CoreSetup.status](./kibana-plugin-core-server.coresetup.status.md)<!-- -->. |
|
||||
| [SavedObjectsType](./kibana-plugin-core-server.savedobjectstype.md) | |
|
||||
| [SavedObjectsTypeManagementDefinition](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.md) | Configuration options for the [type](./kibana-plugin-core-server.savedobjectstype.md)<!-- -->'s management section. |
|
||||
| [SavedObjectsTypeMappingDefinition](./kibana-plugin-core-server.savedobjectstypemappingdefinition.md) | Describe a saved object type mapping. |
|
||||
| [SavedObjectsUpdateOptions](./kibana-plugin-core-server.savedobjectsupdateoptions.md) | |
|
||||
| [SavedObjectsUpdateResponse](./kibana-plugin-core-server.savedobjectsupdateresponse.md) | |
|
||||
| [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) | The current status of a service at a point in time. |
|
||||
| [SessionCookieValidationResult](./kibana-plugin-core-server.sessioncookievalidationresult.md) | Return type from a function to validate cookie contents. |
|
||||
| [SessionStorage](./kibana-plugin-core-server.sessionstorage.md) | Provides an interface to store and retrieve data across requests. |
|
||||
| [SessionStorageCookieOptions](./kibana-plugin-core-server.sessionstoragecookieoptions.md) | Configuration used to create HTTP session storage based on top of cookie mechanism. |
|
||||
| [SessionStorageFactory](./kibana-plugin-core-server.sessionstoragefactory.md) | SessionStorage factory to bind one to an incoming request |
|
||||
| [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) | API for accessing status of Core and this plugin's dependencies as well as for customizing this plugin's status. |
|
||||
| [StringValidationRegex](./kibana-plugin-core-server.stringvalidationregex.md) | StringValidation with regex object |
|
||||
| [StringValidationRegexString](./kibana-plugin-core-server.stringvalidationregexstring.md) | StringValidation as regex string |
|
||||
| [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) | UiSettings parameters defined by the plugins. |
|
||||
|
@ -184,6 +190,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| Variable | Description |
|
||||
| --- | --- |
|
||||
| [kibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) | Set of helpers used to create <code>KibanaResponse</code> to form HTTP response on an incoming request. Should be returned as a result of [RequestHandler](./kibana-plugin-core-server.requesthandler.md) execution. |
|
||||
| [ServiceStatusLevels](./kibana-plugin-core-server.servicestatuslevels.md) | The current "level" of availability of a service. |
|
||||
| [validBodyOutput](./kibana-plugin-core-server.validbodyoutput.md) | The set of valid body.output |
|
||||
|
||||
## Type Aliases
|
||||
|
@ -256,6 +263,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| [SavedObjectsClientWrapperFactory](./kibana-plugin-core-server.savedobjectsclientwrapperfactory.md) | Describes the factory used to create instances of Saved Objects Client Wrappers. |
|
||||
| [SavedObjectsFieldMapping](./kibana-plugin-core-server.savedobjectsfieldmapping.md) | Describe a [saved object type mapping](./kibana-plugin-core-server.savedobjectstypemappingdefinition.md) field.<!-- -->Please refer to [elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html) For the mapping documentation |
|
||||
| [ScopeableRequest](./kibana-plugin-core-server.scopeablerequest.md) | A user credentials container. It accommodates the necessary auth credentials to impersonate the current user.<!-- -->See [KibanaRequest](./kibana-plugin-core-server.kibanarequest.md)<!-- -->. |
|
||||
| [ServiceStatusLevel](./kibana-plugin-core-server.servicestatuslevel.md) | A convenience type that represents the union of each value in [ServiceStatusLevels](./kibana-plugin-core-server.servicestatuslevels.md)<!-- -->. |
|
||||
| [SharedGlobalConfig](./kibana-plugin-core-server.sharedglobalconfig.md) | |
|
||||
| [StartServicesAccessor](./kibana-plugin-core-server.startservicesaccessor.md) | Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed <code>start</code>. This should only be used inside handlers registered during <code>setup</code> that will only be executed after <code>start</code> lifecycle. |
|
||||
| [StringValidation](./kibana-plugin-core-server.stringvalidation.md) | Allows regex objects or a regex string |
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [incompatibleNodes](./kibana-plugin-core-server.nodesversioncompatibility.incompatiblenodes.md)
|
||||
|
||||
## NodesVersionCompatibility.incompatibleNodes property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
incompatibleNodes: NodeInfo[];
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [isCompatible](./kibana-plugin-core-server.nodesversioncompatibility.iscompatible.md)
|
||||
|
||||
## NodesVersionCompatibility.isCompatible property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
isCompatible: boolean;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [kibanaVersion](./kibana-plugin-core-server.nodesversioncompatibility.kibanaversion.md)
|
||||
|
||||
## NodesVersionCompatibility.kibanaVersion property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
kibanaVersion: string;
|
||||
```
|
|
@ -0,0 +1,22 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md)
|
||||
|
||||
## NodesVersionCompatibility interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface NodesVersionCompatibility
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [incompatibleNodes](./kibana-plugin-core-server.nodesversioncompatibility.incompatiblenodes.md) | <code>NodeInfo[]</code> | |
|
||||
| [isCompatible](./kibana-plugin-core-server.nodesversioncompatibility.iscompatible.md) | <code>boolean</code> | |
|
||||
| [kibanaVersion](./kibana-plugin-core-server.nodesversioncompatibility.kibanaversion.md) | <code>string</code> | |
|
||||
| [message](./kibana-plugin-core-server.nodesversioncompatibility.message.md) | <code>string</code> | |
|
||||
| [warningNodes](./kibana-plugin-core-server.nodesversioncompatibility.warningnodes.md) | <code>NodeInfo[]</code> | |
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [message](./kibana-plugin-core-server.nodesversioncompatibility.message.md)
|
||||
|
||||
## NodesVersionCompatibility.message property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
message?: string;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [warningNodes](./kibana-plugin-core-server.nodesversioncompatibility.warningnodes.md)
|
||||
|
||||
## NodesVersionCompatibility.warningNodes property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
warningNodes: NodeInfo[];
|
||||
```
|
|
@ -0,0 +1,20 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectStatusMeta](./kibana-plugin-core-server.savedobjectstatusmeta.md)
|
||||
|
||||
## SavedObjectStatusMeta interface
|
||||
|
||||
Meta information about the SavedObjectService's status. Available to plugins via [CoreSetup.status](./kibana-plugin-core-server.coresetup.status.md)<!-- -->.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface SavedObjectStatusMeta
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [migratedIndices](./kibana-plugin-core-server.savedobjectstatusmeta.migratedindices.md) | <code>{</code><br/><code> [status: string]: number;</code><br/><code> skipped: number;</code><br/><code> migrated: number;</code><br/><code> }</code> | |
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectStatusMeta](./kibana-plugin-core-server.savedobjectstatusmeta.md) > [migratedIndices](./kibana-plugin-core-server.savedobjectstatusmeta.migratedindices.md)
|
||||
|
||||
## SavedObjectStatusMeta.migratedIndices property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
migratedIndices: {
|
||||
[status: string]: number;
|
||||
skipped: number;
|
||||
migrated: number;
|
||||
};
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) > [detail](./kibana-plugin-core-server.servicestatus.detail.md)
|
||||
|
||||
## ServiceStatus.detail property
|
||||
|
||||
A more detailed description of the service status.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
detail?: string;
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) > [documentationUrl](./kibana-plugin-core-server.servicestatus.documentationurl.md)
|
||||
|
||||
## ServiceStatus.documentationUrl property
|
||||
|
||||
A URL to open in a new tab about how to resolve or troubleshoot the problem.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
documentationUrl?: string;
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) > [level](./kibana-plugin-core-server.servicestatus.level.md)
|
||||
|
||||
## ServiceStatus.level property
|
||||
|
||||
The current availability level of the service.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
level: ServiceStatusLevel;
|
||||
```
|
|
@ -0,0 +1,24 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md)
|
||||
|
||||
## ServiceStatus interface
|
||||
|
||||
The current status of a service at a point in time.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface ServiceStatus<Meta extends Record<string, any> | unknown = unknown>
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [detail](./kibana-plugin-core-server.servicestatus.detail.md) | <code>string</code> | A more detailed description of the service status. |
|
||||
| [documentationUrl](./kibana-plugin-core-server.servicestatus.documentationurl.md) | <code>string</code> | A URL to open in a new tab about how to resolve or troubleshoot the problem. |
|
||||
| [level](./kibana-plugin-core-server.servicestatus.level.md) | <code>ServiceStatusLevel</code> | The current availability level of the service. |
|
||||
| [meta](./kibana-plugin-core-server.servicestatus.meta.md) | <code>Meta</code> | Any JSON-serializable data to be included in the HTTP API response. Useful for providing more fine-grained, machine-readable information about the service status. May include status information for underlying features. |
|
||||
| [summary](./kibana-plugin-core-server.servicestatus.summary.md) | <code>string</code> | A high-level summary of the service status. |
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) > [meta](./kibana-plugin-core-server.servicestatus.meta.md)
|
||||
|
||||
## ServiceStatus.meta property
|
||||
|
||||
Any JSON-serializable data to be included in the HTTP API response. Useful for providing more fine-grained, machine-readable information about the service status. May include status information for underlying features.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
meta?: Meta;
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) > [summary](./kibana-plugin-core-server.servicestatus.summary.md)
|
||||
|
||||
## ServiceStatus.summary property
|
||||
|
||||
A high-level summary of the service status.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
summary: string;
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatusLevel](./kibana-plugin-core-server.servicestatuslevel.md)
|
||||
|
||||
## ServiceStatusLevel type
|
||||
|
||||
A convenience type that represents the union of each value in [ServiceStatusLevels](./kibana-plugin-core-server.servicestatuslevels.md)<!-- -->.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare type ServiceStatusLevel = typeof ServiceStatusLevels[keyof typeof ServiceStatusLevels];
|
||||
```
|
|
@ -0,0 +1,37 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatusLevels](./kibana-plugin-core-server.servicestatuslevels.md)
|
||||
|
||||
## ServiceStatusLevels variable
|
||||
|
||||
The current "level" of availability of a service.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
ServiceStatusLevels: Readonly<{
|
||||
available: Readonly<{
|
||||
toString: () => "available";
|
||||
valueOf: () => 0;
|
||||
}>;
|
||||
degraded: Readonly<{
|
||||
toString: () => "degraded";
|
||||
valueOf: () => 1;
|
||||
}>;
|
||||
unavailable: Readonly<{
|
||||
toString: () => "unavailable";
|
||||
valueOf: () => 2;
|
||||
}>;
|
||||
critical: Readonly<{
|
||||
toString: () => "critical";
|
||||
valueOf: () => 3;
|
||||
}>;
|
||||
}>
|
||||
```
|
||||
|
||||
## Remarks
|
||||
|
||||
The values implement `valueOf` to allow for easy comparisons between status levels with <<!-- -->, ><!-- -->, etc. Higher values represent higher severities. Note that the default `Array.prototype.sort` implementation does not correctly sort these values.
|
||||
|
||||
A snapshot serializer is available in `src/core/server/test_utils` to ease testing of these values with Jest.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) > [core$](./kibana-plugin-core-server.statusservicesetup.core_.md)
|
||||
|
||||
## StatusServiceSetup.core$ property
|
||||
|
||||
Current status for all Core services.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
core$: Observable<CoreStatus>;
|
||||
```
|
|
@ -0,0 +1,20 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md)
|
||||
|
||||
## StatusServiceSetup interface
|
||||
|
||||
API for accessing status of Core and this plugin's dependencies as well as for customizing this plugin's status.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface StatusServiceSetup
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [core$](./kibana-plugin-core-server.statusservicesetup.core_.md) | <code>Observable<CoreStatus></code> | Current status for all Core services. |
|
||||
|
|
@ -23,7 +23,6 @@
|
|||
| Function | Description |
|
||||
| --- | --- |
|
||||
| [getDefaultSearchParams(config)](./kibana-plugin-plugins-data-server.getdefaultsearchparams.md) | |
|
||||
| [getTotalLoaded({ total, failed, successful })](./kibana-plugin-plugins-data-server.gettotalloaded.md) | |
|
||||
| [parseInterval(interval)](./kibana-plugin-plugins-data-server.parseinterval.md) | |
|
||||
| [plugin(initializerContext)](./kibana-plugin-plugins-data-server.plugin.md) | Static code to be shared externally |
|
||||
| [shouldReadFieldFromDocValues(aggregatable, esType)](./kibana-plugin-plugins-data-server.shouldreadfieldfromdocvalues.md) | |
|
||||
|
|
|
@ -26,8 +26,10 @@ import {
|
|||
InternalElasticsearchServiceSetup,
|
||||
ElasticsearchServiceSetup,
|
||||
ElasticsearchServiceStart,
|
||||
ElasticsearchStatusMeta,
|
||||
} from './types';
|
||||
import { NodesVersionCompatibility } from './version_check/ensure_es_version';
|
||||
import { ServiceStatus, ServiceStatusLevels } from '../status';
|
||||
|
||||
const createScopedClusterClientMock = (): jest.Mocked<IScopedClusterClient> => ({
|
||||
callAsInternalUser: jest.fn(),
|
||||
|
@ -102,6 +104,10 @@ const createInternalSetupContractMock = () => {
|
|||
warningNodes: [],
|
||||
kibanaVersion: '8.0.0',
|
||||
}),
|
||||
status$: new BehaviorSubject<ServiceStatus<ElasticsearchStatusMeta>>({
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: 'Elasticsearch is available',
|
||||
}),
|
||||
legacy: {
|
||||
config$: new BehaviorSubject({} as ElasticsearchConfig),
|
||||
},
|
||||
|
|
|
@ -40,6 +40,7 @@ import { InternalHttpServiceSetup, GetAuthHeaders } from '../http/';
|
|||
import { InternalElasticsearchServiceSetup, ElasticsearchServiceStart } from './types';
|
||||
import { CallAPIOptions } from './api_types';
|
||||
import { pollEsNodesVersion } from './version_check/ensure_es_version';
|
||||
import { calculateStatus$ } from './status';
|
||||
|
||||
/** @internal */
|
||||
interface CoreClusterClients {
|
||||
|
@ -186,6 +187,7 @@ export class ElasticsearchService
|
|||
adminClient: this.adminClient,
|
||||
dataClient,
|
||||
createClient: this.createClient,
|
||||
status$: calculateStatus$(esNodesCompatibility$),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -31,3 +31,4 @@ export { config, configSchema, ElasticsearchConfig } from './elasticsearch_confi
|
|||
export { ElasticsearchError, ElasticsearchErrorHelpers } from './errors';
|
||||
export * from './api_types';
|
||||
export * from './types';
|
||||
export { NodesVersionCompatibility } from './version_check/ensure_es_version';
|
||||
|
|
222
src/core/server/elasticsearch/status.test.ts
Normal file
222
src/core/server/elasticsearch/status.test.ts
Normal file
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { take } from 'rxjs/operators';
|
||||
import { Subject, of } from 'rxjs';
|
||||
|
||||
import { calculateStatus$ } from './status';
|
||||
import { ServiceStatusLevels, ServiceStatus } from '../status';
|
||||
import { ServiceStatusLevelSnapshotSerializer } from '../status/test_utils';
|
||||
import { NodesVersionCompatibility } from './version_check/ensure_es_version';
|
||||
|
||||
expect.addSnapshotSerializer(ServiceStatusLevelSnapshotSerializer);
|
||||
|
||||
const nodeInfo = {
|
||||
version: '1.1.1',
|
||||
ip: '1.1.1.1',
|
||||
http: {
|
||||
publish_address: 'https://1.1.1.1:9200',
|
||||
},
|
||||
name: 'node1',
|
||||
};
|
||||
|
||||
describe('calculateStatus', () => {
|
||||
it('starts in unavailable', async () => {
|
||||
expect(
|
||||
await calculateStatus$(new Subject())
|
||||
.pipe(take(1))
|
||||
.toPromise()
|
||||
).toEqual({
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: 'Waiting for Elasticsearch',
|
||||
meta: {
|
||||
warningNodes: [],
|
||||
incompatibleNodes: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('changes to available when isCompatible and no warningNodes', async () => {
|
||||
expect(
|
||||
await calculateStatus$(
|
||||
of({ isCompatible: true, kibanaVersion: '1.1.1', warningNodes: [], incompatibleNodes: [] })
|
||||
)
|
||||
.pipe(take(2))
|
||||
.toPromise()
|
||||
).toEqual({
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: 'Elasticsearch is available',
|
||||
meta: {
|
||||
warningNodes: [],
|
||||
incompatibleNodes: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('changes to degraded when isCompatible and warningNodes present', async () => {
|
||||
expect(
|
||||
await calculateStatus$(
|
||||
of({
|
||||
isCompatible: true,
|
||||
kibanaVersion: '1.1.1',
|
||||
warningNodes: [nodeInfo],
|
||||
incompatibleNodes: [],
|
||||
// this isn't the real message, just used to test that the message
|
||||
// is forwarded to the status
|
||||
message: 'Some nodes are a different version',
|
||||
})
|
||||
)
|
||||
.pipe(take(2))
|
||||
.toPromise()
|
||||
).toEqual({
|
||||
level: ServiceStatusLevels.degraded,
|
||||
summary: 'Some nodes are a different version',
|
||||
meta: {
|
||||
incompatibleNodes: [],
|
||||
warningNodes: [nodeInfo],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('changes to critical when isCompatible is false', async () => {
|
||||
expect(
|
||||
await calculateStatus$(
|
||||
of({
|
||||
isCompatible: false,
|
||||
kibanaVersion: '2.1.1',
|
||||
warningNodes: [nodeInfo],
|
||||
incompatibleNodes: [nodeInfo],
|
||||
// this isn't the real message, just used to test that the message
|
||||
// is forwarded to the status
|
||||
message: 'Incompatible with Elasticsearch',
|
||||
})
|
||||
)
|
||||
.pipe(take(2))
|
||||
.toPromise()
|
||||
).toEqual({
|
||||
level: ServiceStatusLevels.critical,
|
||||
summary: 'Incompatible with Elasticsearch',
|
||||
meta: {
|
||||
incompatibleNodes: [nodeInfo],
|
||||
warningNodes: [nodeInfo],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('emits status updates when node compatibility changes', () => {
|
||||
const nodeCompat$ = new Subject<NodesVersionCompatibility>();
|
||||
|
||||
const statusUpdates: ServiceStatus[] = [];
|
||||
const subscription = calculateStatus$(nodeCompat$).subscribe(status =>
|
||||
statusUpdates.push(status)
|
||||
);
|
||||
|
||||
nodeCompat$.next({
|
||||
isCompatible: false,
|
||||
kibanaVersion: '2.1.1',
|
||||
incompatibleNodes: [],
|
||||
warningNodes: [],
|
||||
message: 'Unable to retrieve version info',
|
||||
});
|
||||
nodeCompat$.next({
|
||||
isCompatible: false,
|
||||
kibanaVersion: '2.1.1',
|
||||
incompatibleNodes: [nodeInfo],
|
||||
warningNodes: [],
|
||||
message: 'Incompatible with Elasticsearch',
|
||||
});
|
||||
nodeCompat$.next({
|
||||
isCompatible: true,
|
||||
kibanaVersion: '1.1.1',
|
||||
warningNodes: [nodeInfo],
|
||||
incompatibleNodes: [],
|
||||
message: 'Some nodes are incompatible',
|
||||
});
|
||||
nodeCompat$.next({
|
||||
isCompatible: true,
|
||||
kibanaVersion: '1.1.1',
|
||||
warningNodes: [],
|
||||
incompatibleNodes: [],
|
||||
});
|
||||
|
||||
subscription.unsubscribe();
|
||||
expect(statusUpdates).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"level": unavailable,
|
||||
"meta": Object {
|
||||
"incompatibleNodes": Array [],
|
||||
"warningNodes": Array [],
|
||||
},
|
||||
"summary": "Waiting for Elasticsearch",
|
||||
},
|
||||
Object {
|
||||
"level": critical,
|
||||
"meta": Object {
|
||||
"incompatibleNodes": Array [],
|
||||
"warningNodes": Array [],
|
||||
},
|
||||
"summary": "Unable to retrieve version info",
|
||||
},
|
||||
Object {
|
||||
"level": critical,
|
||||
"meta": Object {
|
||||
"incompatibleNodes": Array [
|
||||
Object {
|
||||
"http": Object {
|
||||
"publish_address": "https://1.1.1.1:9200",
|
||||
},
|
||||
"ip": "1.1.1.1",
|
||||
"name": "node1",
|
||||
"version": "1.1.1",
|
||||
},
|
||||
],
|
||||
"warningNodes": Array [],
|
||||
},
|
||||
"summary": "Incompatible with Elasticsearch",
|
||||
},
|
||||
Object {
|
||||
"level": degraded,
|
||||
"meta": Object {
|
||||
"incompatibleNodes": Array [],
|
||||
"warningNodes": Array [
|
||||
Object {
|
||||
"http": Object {
|
||||
"publish_address": "https://1.1.1.1:9200",
|
||||
},
|
||||
"ip": "1.1.1.1",
|
||||
"name": "node1",
|
||||
"version": "1.1.1",
|
||||
},
|
||||
],
|
||||
},
|
||||
"summary": "Some nodes are incompatible",
|
||||
},
|
||||
Object {
|
||||
"level": available,
|
||||
"meta": Object {
|
||||
"incompatibleNodes": Array [],
|
||||
"warningNodes": Array [],
|
||||
},
|
||||
"summary": "Elasticsearch is available",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
78
src/core/server/elasticsearch/status.ts
Normal file
78
src/core/server/elasticsearch/status.ts
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable, merge, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { ServiceStatus, ServiceStatusLevels } from '../status';
|
||||
import { ElasticsearchStatusMeta } from './types';
|
||||
import { NodesVersionCompatibility } from './version_check/ensure_es_version';
|
||||
|
||||
export const calculateStatus$ = (
|
||||
esNodesCompatibility$: Observable<NodesVersionCompatibility>
|
||||
): Observable<ServiceStatus<ElasticsearchStatusMeta>> =>
|
||||
merge(
|
||||
of({
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: `Waiting for Elasticsearch`,
|
||||
meta: {
|
||||
warningNodes: [],
|
||||
incompatibleNodes: [],
|
||||
},
|
||||
}),
|
||||
esNodesCompatibility$.pipe(
|
||||
map(
|
||||
({
|
||||
isCompatible,
|
||||
message,
|
||||
incompatibleNodes,
|
||||
warningNodes,
|
||||
}): ServiceStatus<ElasticsearchStatusMeta> => {
|
||||
if (!isCompatible) {
|
||||
return {
|
||||
level: ServiceStatusLevels.critical,
|
||||
summary:
|
||||
// Message should always be present, but this is a safe fallback
|
||||
message ??
|
||||
`Some Elasticsearch nodes are not compatible with this version of Kibana`,
|
||||
meta: { warningNodes, incompatibleNodes },
|
||||
};
|
||||
} else if (warningNodes.length > 0) {
|
||||
return {
|
||||
level: ServiceStatusLevels.degraded,
|
||||
summary:
|
||||
// Message should always be present, but this is a safe fallback
|
||||
message ??
|
||||
`Some Elasticsearch nodes are running different versions than this version of Kibana`,
|
||||
meta: { warningNodes, incompatibleNodes },
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: `Elasticsearch is available`,
|
||||
meta: {
|
||||
warningNodes: [],
|
||||
incompatibleNodes: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
|
@ -22,6 +22,7 @@ import { ElasticsearchConfig } from './elasticsearch_config';
|
|||
import { ElasticsearchClientConfig } from './elasticsearch_client_config';
|
||||
import { IClusterClient, ICustomClusterClient } from './cluster_client';
|
||||
import { NodesVersionCompatibility } from './version_check/ensure_es_version';
|
||||
import { ServiceStatus } from '../status';
|
||||
|
||||
/**
|
||||
* @public
|
||||
|
@ -128,4 +129,11 @@ export interface InternalElasticsearchServiceSetup extends ElasticsearchServiceS
|
|||
readonly config$: Observable<ElasticsearchConfig>;
|
||||
};
|
||||
esNodesCompatibility$: Observable<NodesVersionCompatibility>;
|
||||
status$: Observable<ServiceStatus<ElasticsearchStatusMeta>>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ElasticsearchStatusMeta {
|
||||
warningNodes: NodesVersionCompatibility['warningNodes'];
|
||||
incompatibleNodes: NodesVersionCompatibility['incompatibleNodes'];
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ export const pollEsNodesVersion = ({
|
|||
kibanaVersion,
|
||||
ignoreVersionMismatch,
|
||||
esVersionCheckInterval: healthCheckInterval,
|
||||
}: PollEsNodesVersionOptions): Observable<any> => {
|
||||
}: PollEsNodesVersionOptions): Observable<NodesVersionCompatibility> => {
|
||||
log.debug('Checking Elasticsearch version');
|
||||
return timer(0, healthCheckInterval).pipe(
|
||||
exhaustMap(() => {
|
||||
|
|
|
@ -60,6 +60,7 @@ import {
|
|||
import { CapabilitiesSetup, CapabilitiesStart } from './capabilities';
|
||||
import { UuidServiceSetup } from './uuid';
|
||||
import { MetricsServiceSetup } from './metrics';
|
||||
import { StatusServiceSetup } from './status';
|
||||
|
||||
export { bootstrap } from './bootstrap';
|
||||
export { Capabilities, CapabilitiesProvider, CapabilitiesSwitcher } from './capabilities';
|
||||
|
@ -95,6 +96,8 @@ export {
|
|||
ElasticsearchErrorHelpers,
|
||||
ElasticsearchServiceSetup,
|
||||
ElasticsearchServiceStart,
|
||||
ElasticsearchStatusMeta,
|
||||
NodesVersionCompatibility,
|
||||
APICaller,
|
||||
FakeRequest,
|
||||
ScopeableRequest,
|
||||
|
@ -226,6 +229,7 @@ export {
|
|||
SavedObjectsUpdateResponse,
|
||||
SavedObjectsServiceStart,
|
||||
SavedObjectsServiceSetup,
|
||||
SavedObjectStatusMeta,
|
||||
SavedObjectsDeleteOptions,
|
||||
ISavedObjectsRepository,
|
||||
SavedObjectsRepository,
|
||||
|
@ -294,6 +298,14 @@ export {
|
|||
LegacyInternals,
|
||||
} from './legacy';
|
||||
|
||||
export {
|
||||
CoreStatus,
|
||||
ServiceStatus,
|
||||
ServiceStatusLevel,
|
||||
ServiceStatusLevels,
|
||||
StatusServiceSetup,
|
||||
} from './status';
|
||||
|
||||
/**
|
||||
* Plugin specific context passed to a route handler.
|
||||
*
|
||||
|
@ -348,14 +360,16 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
|
|||
elasticsearch: ElasticsearchServiceSetup;
|
||||
/** {@link HttpServiceSetup} */
|
||||
http: HttpServiceSetup;
|
||||
/** {@link MetricsServiceSetup} */
|
||||
metrics: MetricsServiceSetup;
|
||||
/** {@link SavedObjectsServiceSetup} */
|
||||
savedObjects: SavedObjectsServiceSetup;
|
||||
/** {@link StatusServiceSetup} */
|
||||
status: StatusServiceSetup;
|
||||
/** {@link UiSettingsServiceSetup} */
|
||||
uiSettings: UiSettingsServiceSetup;
|
||||
/** {@link UuidServiceSetup} */
|
||||
uuid: UuidServiceSetup;
|
||||
/** {@link MetricsServiceSetup} */
|
||||
metrics: MetricsServiceSetup;
|
||||
/** {@link StartServicesAccessor} */
|
||||
getStartServices: StartServicesAccessor<TPluginsStart, TStart>;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import {
|
|||
import { InternalUiSettingsServiceSetup, InternalUiSettingsServiceStart } from './ui_settings';
|
||||
import { UuidServiceSetup } from './uuid';
|
||||
import { InternalMetricsServiceSetup } from './metrics';
|
||||
import { InternalStatusServiceSetup } from './status';
|
||||
|
||||
/** @internal */
|
||||
export interface InternalCoreSetup {
|
||||
|
@ -38,10 +39,11 @@ export interface InternalCoreSetup {
|
|||
context: ContextSetup;
|
||||
http: InternalHttpServiceSetup;
|
||||
elasticsearch: InternalElasticsearchServiceSetup;
|
||||
uiSettings: InternalUiSettingsServiceSetup;
|
||||
savedObjects: InternalSavedObjectsServiceSetup;
|
||||
uuid: UuidServiceSetup;
|
||||
metrics: InternalMetricsServiceSetup;
|
||||
savedObjects: InternalSavedObjectsServiceSetup;
|
||||
status: InternalStatusServiceSetup;
|
||||
uiSettings: InternalUiSettingsServiceSetup;
|
||||
uuid: UuidServiceSetup;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,6 +48,7 @@ import { findLegacyPluginSpecs } from './plugins';
|
|||
import { LegacyVars, LegacyServiceSetupDeps, LegacyServiceStartDeps } from './types';
|
||||
import { LegacyService } from './legacy_service';
|
||||
import { coreMock } from '../mocks';
|
||||
import { statusServiceMock } from '../status/status_service.mock';
|
||||
|
||||
const MockKbnServer: jest.Mock<KbnServer> = KbnServer as any;
|
||||
|
||||
|
@ -106,6 +107,7 @@ beforeEach(() => {
|
|||
rendering: renderingServiceMock,
|
||||
metrics: metricsServiceMock.createInternalSetupContract(),
|
||||
uuid: uuidSetup,
|
||||
status: statusServiceMock.createInternalSetupContract(),
|
||||
},
|
||||
plugins: { 'plugin-id': 'plugin-value' },
|
||||
};
|
||||
|
|
|
@ -306,6 +306,9 @@ export class LegacyService implements CoreService {
|
|||
registerType: setupDeps.core.savedObjects.registerType,
|
||||
getImportExportObjectLimit: setupDeps.core.savedObjects.getImportExportObjectLimit,
|
||||
},
|
||||
status: {
|
||||
core$: setupDeps.core.status.core$,
|
||||
},
|
||||
uiSettings: {
|
||||
register: setupDeps.core.uiSettings.register,
|
||||
},
|
||||
|
|
|
@ -33,6 +33,7 @@ import { InternalCoreSetup, InternalCoreStart } from './internal_types';
|
|||
import { capabilitiesServiceMock } from './capabilities/capabilities_service.mock';
|
||||
import { metricsServiceMock } from './metrics/metrics_service.mock';
|
||||
import { uuidServiceMock } from './uuid/uuid_service.mock';
|
||||
import { statusServiceMock } from './status/status_service.mock';
|
||||
|
||||
export { httpServerMock } from './http/http_server.mocks';
|
||||
export { sessionStorageMock } from './http/cookie_session_storage.mocks';
|
||||
|
@ -133,9 +134,10 @@ function createCoreSetupMock({
|
|||
elasticsearch: elasticsearchServiceMock.createSetup(),
|
||||
http: httpMock,
|
||||
savedObjects: savedObjectsServiceMock.createInternalSetupContract(),
|
||||
status: statusServiceMock.createSetupContract(),
|
||||
metrics: metricsServiceMock.createSetupContract(),
|
||||
uiSettings: uiSettingsMock,
|
||||
uuid: uuidServiceMock.createSetupContract(),
|
||||
metrics: metricsServiceMock.createSetupContract(),
|
||||
getStartServices: jest
|
||||
.fn<Promise<[ReturnType<typeof createCoreStartMock>, object, any]>, []>()
|
||||
.mockResolvedValue([createCoreStartMock(), pluginStartDeps, pluginStartContract]),
|
||||
|
@ -161,10 +163,11 @@ function createInternalCoreSetupMock() {
|
|||
context: contextServiceMock.createSetupContract(),
|
||||
elasticsearch: elasticsearchServiceMock.createInternalSetup(),
|
||||
http: httpServiceMock.createSetupContract(),
|
||||
uiSettings: uiSettingsServiceMock.createSetupContract(),
|
||||
savedObjects: savedObjectsServiceMock.createInternalSetupContract(),
|
||||
uuid: uuidServiceMock.createSetupContract(),
|
||||
metrics: metricsServiceMock.createInternalSetupContract(),
|
||||
savedObjects: savedObjectsServiceMock.createInternalSetupContract(),
|
||||
status: statusServiceMock.createInternalSetupContract(),
|
||||
uuid: uuidServiceMock.createSetupContract(),
|
||||
uiSettings: uiSettingsServiceMock.createSetupContract(),
|
||||
};
|
||||
return setupDeps;
|
||||
}
|
||||
|
|
|
@ -175,6 +175,9 @@ export function createPluginSetupContext<TPlugin, TPluginDependencies>(
|
|||
registerType: deps.savedObjects.registerType,
|
||||
getImportExportObjectLimit: deps.savedObjects.getImportExportObjectLimit,
|
||||
},
|
||||
status: {
|
||||
core$: deps.status.core$,
|
||||
},
|
||||
uiSettings: {
|
||||
register: deps.uiSettings.register,
|
||||
},
|
||||
|
|
|
@ -68,7 +68,11 @@ export {
|
|||
SavedObjectMigrationContext,
|
||||
} from './migrations';
|
||||
|
||||
export { SavedObjectsType, SavedObjectsTypeManagementDefinition } from './types';
|
||||
export {
|
||||
SavedObjectStatusMeta,
|
||||
SavedObjectsType,
|
||||
SavedObjectsTypeManagementDefinition,
|
||||
} from './types';
|
||||
|
||||
export { savedObjectsConfig, savedObjectsMigrationConfig } from './saved_objects_config';
|
||||
export { SavedObjectTypeRegistry, ISavedObjectTypeRegistry } from './saved_objects_type_registry';
|
||||
|
|
|
@ -22,4 +22,4 @@ export { IndexMigrator } from './index_migrator';
|
|||
export { buildActiveMappings } from './build_active_mappings';
|
||||
export { CallCluster } from './call_cluster';
|
||||
export { LogFn } from './migration_logger';
|
||||
export { MigrationResult } from './migration_coordinator';
|
||||
export { MigrationResult, MigrationStatus } from './migration_coordinator';
|
||||
|
|
|
@ -39,6 +39,8 @@ import { SavedObjectsMigrationLogger } from './migration_logger';
|
|||
|
||||
const DEFAULT_POLL_INTERVAL = 15000;
|
||||
|
||||
export type MigrationStatus = 'waiting' | 'running' | 'completed';
|
||||
|
||||
export type MigrationResult =
|
||||
| { status: 'skipped' }
|
||||
| { status: 'patched' }
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { MigrationResult } from './core';
|
||||
export { KibanaMigrator, IKibanaMigrator } from './kibana';
|
||||
export {
|
||||
SavedObjectMigrationFn,
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { KibanaMigrator, IKibanaMigrator } from './kibana_migrator';
|
||||
export { KibanaMigrator, IKibanaMigrator, KibanaMigratorStatus } from './kibana_migrator';
|
||||
|
|
|
@ -17,10 +17,11 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { KibanaMigrator } from './kibana_migrator';
|
||||
import { KibanaMigrator, KibanaMigratorStatus } from './kibana_migrator';
|
||||
import { buildActiveMappings } from '../core';
|
||||
const { mergeTypes } = jest.requireActual('./kibana_migrator');
|
||||
import { SavedObjectsType } from '../../types';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
const defaultSavedObjectTypes: SavedObjectsType[] = [
|
||||
{
|
||||
|
@ -47,6 +48,20 @@ const createMigrator = (
|
|||
runMigrations: jest.fn(),
|
||||
getActiveMappings: jest.fn(),
|
||||
migrateDocument: jest.fn(),
|
||||
getStatus$: jest.fn(
|
||||
() =>
|
||||
new BehaviorSubject<KibanaMigratorStatus>({
|
||||
status: 'completed',
|
||||
result: [
|
||||
{
|
||||
status: 'migrated',
|
||||
destIndex: '.test-kibana_2',
|
||||
sourceIndex: '.test-kibana_1',
|
||||
elapsedMs: 10,
|
||||
},
|
||||
],
|
||||
})
|
||||
),
|
||||
};
|
||||
|
||||
mockMigrator.getActiveMappings.mockReturnValue(buildActiveMappings(mergeTypes(types)));
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
import { KibanaMigratorOptions, KibanaMigrator } from './kibana_migrator';
|
||||
import { loggingServiceMock } from '../../../logging/logging_service.mock';
|
||||
|
@ -79,6 +80,33 @@ describe('KibanaMigrator', () => {
|
|||
.filter(callClusterPath => callClusterPath === 'cat.templates');
|
||||
expect(callClusterCommands.length).toBe(1);
|
||||
});
|
||||
|
||||
it('emits results on getMigratorResult$()', async () => {
|
||||
const options = mockOptions();
|
||||
const clusterStub = jest.fn<any, any>(() => ({ status: 404 }));
|
||||
|
||||
options.callCluster = clusterStub;
|
||||
const migrator = new KibanaMigrator(options);
|
||||
const migratorStatus = migrator
|
||||
.getStatus$()
|
||||
.pipe(take(3))
|
||||
.toPromise();
|
||||
await migrator.runMigrations();
|
||||
const { status, result } = await migratorStatus;
|
||||
expect(status).toEqual('completed');
|
||||
expect(result![0]).toMatchObject({
|
||||
destIndex: '.my-index_1',
|
||||
elapsedMs: expect.any(Number),
|
||||
sourceIndex: '.my-index',
|
||||
status: 'migrated',
|
||||
});
|
||||
expect(result![1]).toMatchObject({
|
||||
destIndex: 'other-index_1',
|
||||
elapsedMs: expect.any(Number),
|
||||
sourceIndex: 'other-index',
|
||||
status: 'migrated',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -24,10 +24,17 @@
|
|||
|
||||
import { Logger } from 'src/core/server/logging';
|
||||
import { KibanaConfigType } from 'src/core/server/kibana_config';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { IndexMapping, SavedObjectsTypeMappingDefinitions } from '../../mappings';
|
||||
import { SavedObjectUnsanitizedDoc, SavedObjectsSerializer } from '../../serialization';
|
||||
import { docValidator, PropertyValidators } from '../../validation';
|
||||
import { buildActiveMappings, CallCluster, IndexMigrator } from '../core';
|
||||
import {
|
||||
buildActiveMappings,
|
||||
CallCluster,
|
||||
IndexMigrator,
|
||||
MigrationResult,
|
||||
MigrationStatus,
|
||||
} from '../core';
|
||||
import { DocumentMigrator, VersionedTransformer } from '../core/document_migrator';
|
||||
import { createIndexMap } from '../core/build_index_map';
|
||||
import { SavedObjectsMigrationConfigType } from '../../saved_objects_config';
|
||||
|
@ -46,6 +53,11 @@ export interface KibanaMigratorOptions {
|
|||
|
||||
export type IKibanaMigrator = Pick<KibanaMigrator, keyof KibanaMigrator>;
|
||||
|
||||
export interface KibanaMigratorStatus {
|
||||
status: MigrationStatus;
|
||||
result?: MigrationResult[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the shape of mappings and documents in the Kibana index.
|
||||
*/
|
||||
|
@ -58,7 +70,10 @@ export class KibanaMigrator {
|
|||
private readonly mappingProperties: SavedObjectsTypeMappingDefinitions;
|
||||
private readonly typeRegistry: ISavedObjectTypeRegistry;
|
||||
private readonly serializer: SavedObjectsSerializer;
|
||||
private migrationResult?: Promise<Array<{ status: string }>>;
|
||||
private migrationResult?: Promise<MigrationResult[]>;
|
||||
private readonly status$ = new BehaviorSubject<KibanaMigratorStatus>({
|
||||
status: 'waiting',
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates an instance of KibanaMigrator.
|
||||
|
@ -109,12 +124,20 @@ export class KibanaMigrator {
|
|||
Array<{ status: string }>
|
||||
> {
|
||||
if (this.migrationResult === undefined || rerun) {
|
||||
this.migrationResult = this.runMigrationsInternal();
|
||||
this.status$.next({ status: 'running' });
|
||||
this.migrationResult = this.runMigrationsInternal().then(result => {
|
||||
this.status$.next({ status: 'completed', result });
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
return this.migrationResult;
|
||||
}
|
||||
|
||||
public getStatus$() {
|
||||
return this.status$.asObservable();
|
||||
}
|
||||
|
||||
private runMigrationsInternal() {
|
||||
const kibanaIndexName = this.kibanaConfig.index;
|
||||
const indexMap = createIndexMap({
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import {
|
||||
SavedObjectsService,
|
||||
InternalSavedObjectsServiceSetup,
|
||||
|
@ -29,6 +31,7 @@ import { savedObjectsClientProviderMock } from './service/lib/scoped_client_prov
|
|||
import { savedObjectsRepositoryMock } from './service/lib/repository.mock';
|
||||
import { savedObjectsClientMock } from './service/saved_objects_client.mock';
|
||||
import { typeRegistryMock } from './saved_objects_type_registry.mock';
|
||||
import { ServiceStatusLevels } from '../status';
|
||||
|
||||
type SavedObjectsServiceContract = PublicMethodsOf<SavedObjectsService>;
|
||||
|
||||
|
@ -75,6 +78,10 @@ const createSetupContractMock = () => {
|
|||
const createInternalSetupContractMock = () => {
|
||||
const internalSetupContract: jest.Mocked<InternalSavedObjectsServiceSetup> = {
|
||||
...createSetupContractMock(),
|
||||
status$: new BehaviorSubject({
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: `SavedObjects is available`,
|
||||
}),
|
||||
};
|
||||
return internalSetupContract;
|
||||
};
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Subject } from 'rxjs';
|
||||
import { first, filter, take } from 'rxjs/operators';
|
||||
import { Subject, Observable } from 'rxjs';
|
||||
import { first, filter, take, switchMap } from 'rxjs/operators';
|
||||
import { CoreService } from '../../types';
|
||||
import {
|
||||
SavedObjectsClient,
|
||||
|
@ -38,7 +38,7 @@ import {
|
|||
SavedObjectConfig,
|
||||
} from './saved_objects_config';
|
||||
import { KibanaRequest, InternalHttpServiceSetup } from '../http';
|
||||
import { SavedObjectsClientContract, SavedObjectsType } from './types';
|
||||
import { SavedObjectsClientContract, SavedObjectsType, SavedObjectStatusMeta } from './types';
|
||||
import { ISavedObjectsRepository, SavedObjectsRepository } from './service/lib/repository';
|
||||
import {
|
||||
SavedObjectsClientFactoryProvider,
|
||||
|
@ -50,6 +50,8 @@ import { SavedObjectTypeRegistry, ISavedObjectTypeRegistry } from './saved_objec
|
|||
import { PropertyValidators } from './validation';
|
||||
import { SavedObjectsSerializer } from './serialization';
|
||||
import { registerRoutes } from './routes';
|
||||
import { ServiceStatus } from '../status';
|
||||
import { calculateStatus$ } from './status';
|
||||
|
||||
/**
|
||||
* Saved Objects is Kibana's data persistence mechanism allowing plugins to
|
||||
|
@ -164,7 +166,9 @@ export interface SavedObjectsServiceSetup {
|
|||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type InternalSavedObjectsServiceSetup = SavedObjectsServiceSetup;
|
||||
export interface InternalSavedObjectsServiceSetup extends SavedObjectsServiceSetup {
|
||||
status$: Observable<ServiceStatus<SavedObjectStatusMeta>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saved Objects is Kibana's data persisentence mechanism allowing plugins to
|
||||
|
@ -321,6 +325,10 @@ export class SavedObjectsService
|
|||
});
|
||||
|
||||
return {
|
||||
status$: calculateStatus$(
|
||||
this.migrator$.pipe(switchMap(migrator => migrator.getStatus$())),
|
||||
setupDeps.elasticsearch.status$
|
||||
),
|
||||
setClientFactoryProvider: provider => {
|
||||
if (this.started) {
|
||||
throw new Error('cannot call `setClientFactoryProvider` after service startup.');
|
||||
|
|
134
src/core/server/saved_objects/status.test.ts
Normal file
134
src/core/server/saved_objects/status.test.ts
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { of, Observable } from 'rxjs';
|
||||
import { ServiceStatus, ServiceStatusLevels } from '../status';
|
||||
import { calculateStatus$ } from './status';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
describe('calculateStatus$', () => {
|
||||
const expectUnavailableDueToEs = (status$: Observable<ServiceStatus>) =>
|
||||
expect(status$.pipe(take(1)).toPromise()).resolves.toEqual({
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: `SavedObjects service is not available without a healthy Elasticearch connection`,
|
||||
});
|
||||
|
||||
const expectUnavailableDueToMigrations = (status$: Observable<ServiceStatus>) =>
|
||||
expect(status$.pipe(take(1)).toPromise()).resolves.toEqual({
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: `SavedObjects service is waiting to start migrations`,
|
||||
});
|
||||
|
||||
describe('when elasticsearch is unavailable', () => {
|
||||
const esStatus$ = of<ServiceStatus>({
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: 'xxx',
|
||||
});
|
||||
|
||||
it('is unavailable before migrations have ran', async () => {
|
||||
await expectUnavailableDueToEs(calculateStatus$(of<any>(), esStatus$));
|
||||
});
|
||||
it('is unavailable after migrations have ran', async () => {
|
||||
await expectUnavailableDueToEs(
|
||||
calculateStatus$(of({ status: 'completed', result: [] }), esStatus$)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when elasticsearch is critical', () => {
|
||||
const esStatus$ = of<ServiceStatus>({
|
||||
level: ServiceStatusLevels.critical,
|
||||
summary: 'xxx',
|
||||
});
|
||||
|
||||
it('is unavailable before migrations have ran', async () => {
|
||||
await expectUnavailableDueToEs(calculateStatus$(of<any>(), esStatus$));
|
||||
});
|
||||
it('is unavailable after migrations have ran', async () => {
|
||||
await expectUnavailableDueToEs(
|
||||
calculateStatus$(
|
||||
of({ status: 'completed', result: [{ status: 'migrated' } as any] }),
|
||||
esStatus$
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when elasticsearch is available', () => {
|
||||
const esStatus$ = of<ServiceStatus>({
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: 'Available',
|
||||
});
|
||||
|
||||
it('is unavailable before migrations have ran', async () => {
|
||||
await expectUnavailableDueToMigrations(calculateStatus$(of<any>(), esStatus$));
|
||||
});
|
||||
it('is unavailable while migrations are running', async () => {
|
||||
await expect(
|
||||
calculateStatus$(of({ status: 'running' }), esStatus$)
|
||||
.pipe(take(2))
|
||||
.toPromise()
|
||||
).resolves.toEqual({
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: `SavedObjects service is running migrations`,
|
||||
});
|
||||
});
|
||||
it('is available after migrations have ran', async () => {
|
||||
await expect(
|
||||
calculateStatus$(
|
||||
of({ status: 'completed', result: [{ status: 'skipped' }, { status: 'patched' }] }),
|
||||
esStatus$
|
||||
)
|
||||
.pipe(take(2))
|
||||
.toPromise()
|
||||
).resolves.toEqual({
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: `SavedObjects service has completed migrations and is available`,
|
||||
meta: {
|
||||
migratedIndices: {
|
||||
migrated: 0,
|
||||
patched: 1,
|
||||
skipped: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when elasticsearch is degraded', () => {
|
||||
const esStatus$ = of<ServiceStatus>({ level: ServiceStatusLevels.degraded, summary: 'xxx' });
|
||||
|
||||
it('is unavailable before migrations have ran', async () => {
|
||||
await expectUnavailableDueToMigrations(calculateStatus$(of<any>(), esStatus$));
|
||||
});
|
||||
it('is degraded after migrations have ran', async () => {
|
||||
await expect(
|
||||
calculateStatus$(
|
||||
of<any>([{ status: 'skipped' }]),
|
||||
esStatus$
|
||||
)
|
||||
.pipe(take(2))
|
||||
.toPromise()
|
||||
).resolves.toEqual({
|
||||
level: ServiceStatusLevels.degraded,
|
||||
summary: 'SavedObjects service is degraded due to Elasticsearch: [xxx]',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
84
src/core/server/saved_objects/status.ts
Normal file
84
src/core/server/saved_objects/status.ts
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { startWith, map } from 'rxjs/operators';
|
||||
import { ServiceStatus, ServiceStatusLevels } from '../status';
|
||||
import { SavedObjectStatusMeta } from './types';
|
||||
import { KibanaMigratorStatus } from './migrations/kibana';
|
||||
|
||||
export const calculateStatus$ = (
|
||||
rawMigratorStatus$: Observable<KibanaMigratorStatus>,
|
||||
elasticsearchStatus$: Observable<ServiceStatus>
|
||||
): Observable<ServiceStatus<SavedObjectStatusMeta>> => {
|
||||
const migratorStatus$: Observable<ServiceStatus<SavedObjectStatusMeta>> = rawMigratorStatus$.pipe(
|
||||
map(migrationStatus => {
|
||||
if (migrationStatus.status === 'waiting') {
|
||||
return {
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: `SavedObjects service is waiting to start migrations`,
|
||||
};
|
||||
} else if (migrationStatus.status === 'running') {
|
||||
return {
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: `SavedObjects service is running migrations`,
|
||||
};
|
||||
}
|
||||
|
||||
const statusCounts: SavedObjectStatusMeta['migratedIndices'] = { migrated: 0, skipped: 0 };
|
||||
if (migrationStatus.result) {
|
||||
migrationStatus.result.forEach(({ status }) => {
|
||||
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: `SavedObjects service has completed migrations and is available`,
|
||||
meta: {
|
||||
migratedIndices: statusCounts,
|
||||
},
|
||||
};
|
||||
}),
|
||||
startWith({
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: `SavedObjects service is waiting to start migrations`,
|
||||
})
|
||||
);
|
||||
|
||||
return combineLatest([elasticsearchStatus$, migratorStatus$]).pipe(
|
||||
map(([esStatus, migratorStatus]) => {
|
||||
if (esStatus.level >= ServiceStatusLevels.unavailable) {
|
||||
return {
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: `SavedObjects service is not available without a healthy Elasticearch connection`,
|
||||
};
|
||||
} else if (migratorStatus.level === ServiceStatusLevels.unavailable) {
|
||||
return migratorStatus;
|
||||
} else if (esStatus.level === ServiceStatusLevels.degraded) {
|
||||
return {
|
||||
level: esStatus.level,
|
||||
summary: `SavedObjects service is degraded due to Elasticsearch: [${esStatus.summary}]`,
|
||||
};
|
||||
} else {
|
||||
return migratorStatus;
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
|
@ -46,6 +46,19 @@ export {
|
|||
SavedObjectsMigrationVersion,
|
||||
} from '../../types';
|
||||
|
||||
/**
|
||||
* Meta information about the SavedObjectService's status. Available to plugins via {@link CoreSetup.status}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface SavedObjectStatusMeta {
|
||||
migratedIndices: {
|
||||
[status: string]: number;
|
||||
skipped: number;
|
||||
migrated: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @public
|
||||
|
|
|
@ -638,6 +638,8 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
|
|||
// (undocumented)
|
||||
savedObjects: SavedObjectsServiceSetup;
|
||||
// (undocumented)
|
||||
status: StatusServiceSetup;
|
||||
// (undocumented)
|
||||
uiSettings: UiSettingsServiceSetup;
|
||||
// (undocumented)
|
||||
uuid: UuidServiceSetup;
|
||||
|
@ -655,6 +657,14 @@ export interface CoreStart {
|
|||
uiSettings: UiSettingsServiceStart;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface CoreStatus {
|
||||
// (undocumented)
|
||||
elasticsearch: ServiceStatus;
|
||||
// (undocumented)
|
||||
savedObjects: ServiceStatus;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class CspConfig implements ICspConfig {
|
||||
// @internal
|
||||
|
@ -794,6 +804,14 @@ export interface ElasticsearchServiceStart {
|
|||
};
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ElasticsearchStatusMeta {
|
||||
// (undocumented)
|
||||
incompatibleNodes: NodesVersionCompatibility['incompatibleNodes'];
|
||||
// (undocumented)
|
||||
warningNodes: NodesVersionCompatibility['warningNodes'];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface EnvironmentMode {
|
||||
// (undocumented)
|
||||
|
@ -1249,6 +1267,24 @@ export type MIGRATION_DEPRECATION_LEVEL = 'none' | 'info' | 'warning' | 'critica
|
|||
// @public
|
||||
export type MutatingOperationRefreshSetting = boolean | 'wait_for';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "NodesVersionCompatibility" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface NodesVersionCompatibility {
|
||||
// Warning: (ae-forgotten-export) The symbol "NodeInfo" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
incompatibleNodes: NodeInfo[];
|
||||
// (undocumented)
|
||||
isCompatible: boolean;
|
||||
// (undocumented)
|
||||
kibanaVersion: string;
|
||||
// (undocumented)
|
||||
message?: string;
|
||||
// (undocumented)
|
||||
warningNodes: NodeInfo[];
|
||||
}
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "OnPostAuthResult" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// @public
|
||||
|
@ -2169,6 +2205,16 @@ export interface SavedObjectsServiceStart {
|
|||
getTypeRegistry: () => ISavedObjectTypeRegistry;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface SavedObjectStatusMeta {
|
||||
// (undocumented)
|
||||
migratedIndices: {
|
||||
[status: string]: number;
|
||||
skipped: number;
|
||||
migrated: number;
|
||||
};
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface SavedObjectsType {
|
||||
convertToAliasScript?: string;
|
||||
|
@ -2237,6 +2283,38 @@ export class ScopedClusterClient implements IScopedClusterClient {
|
|||
callAsInternalUser(endpoint: string, clientParams?: Record<string, any>, options?: CallAPIOptions): Promise<any>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface ServiceStatus<Meta extends Record<string, any> | unknown = unknown> {
|
||||
detail?: string;
|
||||
documentationUrl?: string;
|
||||
level: ServiceStatusLevel;
|
||||
meta?: Meta;
|
||||
summary: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type ServiceStatusLevel = typeof ServiceStatusLevels[keyof typeof ServiceStatusLevels];
|
||||
|
||||
// @public
|
||||
export const ServiceStatusLevels: Readonly<{
|
||||
available: Readonly<{
|
||||
toString: () => "available";
|
||||
valueOf: () => 0;
|
||||
}>;
|
||||
degraded: Readonly<{
|
||||
toString: () => "degraded";
|
||||
valueOf: () => 1;
|
||||
}>;
|
||||
unavailable: Readonly<{
|
||||
toString: () => "unavailable";
|
||||
valueOf: () => 2;
|
||||
}>;
|
||||
critical: Readonly<{
|
||||
toString: () => "critical";
|
||||
valueOf: () => 3;
|
||||
}>;
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export interface SessionCookieValidationResult {
|
||||
isValid: boolean;
|
||||
|
@ -2274,6 +2352,11 @@ export type SharedGlobalConfig = RecursiveReadonly_2<{
|
|||
// @public
|
||||
export type StartServicesAccessor<TPluginsStart extends object = object, TStart = unknown> = () => Promise<[CoreStart, TPluginsStart, TStart]>;
|
||||
|
||||
// @public
|
||||
export interface StatusServiceSetup {
|
||||
core$: Observable<CoreStatus>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type StringValidation = StringValidationRegex | StringValidationRegexString;
|
||||
|
||||
|
|
|
@ -85,3 +85,9 @@ export const mockMetricsService = metricsServiceMock.create();
|
|||
jest.doMock('./metrics/metrics_service', () => ({
|
||||
MetricsService: jest.fn(() => mockMetricsService),
|
||||
}));
|
||||
|
||||
import { statusServiceMock } from './status/status_service.mock';
|
||||
export const mockStatusService = statusServiceMock.create();
|
||||
jest.doMock('./status/status_service', () => ({
|
||||
StatusService: jest.fn(() => mockStatusService),
|
||||
}));
|
||||
|
|
|
@ -29,6 +29,7 @@ import {
|
|||
mockUiSettingsService,
|
||||
mockRenderingService,
|
||||
mockMetricsService,
|
||||
mockStatusService,
|
||||
} from './server.test.mocks';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
@ -63,6 +64,7 @@ test('sets up services on "setup"', async () => {
|
|||
expect(mockUiSettingsService.setup).not.toHaveBeenCalled();
|
||||
expect(mockRenderingService.setup).not.toHaveBeenCalled();
|
||||
expect(mockMetricsService.setup).not.toHaveBeenCalled();
|
||||
expect(mockStatusService.setup).not.toHaveBeenCalled();
|
||||
|
||||
await server.setup();
|
||||
|
||||
|
@ -74,6 +76,7 @@ test('sets up services on "setup"', async () => {
|
|||
expect(mockUiSettingsService.setup).toHaveBeenCalledTimes(1);
|
||||
expect(mockRenderingService.setup).toHaveBeenCalledTimes(1);
|
||||
expect(mockMetricsService.setup).toHaveBeenCalledTimes(1);
|
||||
expect(mockStatusService.setup).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('injects legacy dependency to context#setup()', async () => {
|
||||
|
@ -141,6 +144,7 @@ test('stops services on "stop"', async () => {
|
|||
expect(mockSavedObjectsService.stop).not.toHaveBeenCalled();
|
||||
expect(mockUiSettingsService.stop).not.toHaveBeenCalled();
|
||||
expect(mockMetricsService.stop).not.toHaveBeenCalled();
|
||||
expect(mockStatusService.stop).not.toHaveBeenCalled();
|
||||
|
||||
await server.stop();
|
||||
|
||||
|
@ -151,6 +155,7 @@ test('stops services on "stop"', async () => {
|
|||
expect(mockSavedObjectsService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockUiSettingsService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockMetricsService.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockStatusService.stop).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test(`doesn't setup core services if config validation fails`, async () => {
|
||||
|
@ -167,6 +172,7 @@ test(`doesn't setup core services if config validation fails`, async () => {
|
|||
expect(mockUiSettingsService.setup).not.toHaveBeenCalled();
|
||||
expect(mockRenderingService.setup).not.toHaveBeenCalled();
|
||||
expect(mockMetricsService.setup).not.toHaveBeenCalled();
|
||||
expect(mockStatusService.setup).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test(`doesn't setup core services if legacy config validation fails`, async () => {
|
||||
|
@ -187,4 +193,5 @@ test(`doesn't setup core services if legacy config validation fails`, async () =
|
|||
expect(mockSavedObjectsService.stop).not.toHaveBeenCalled();
|
||||
expect(mockUiSettingsService.setup).not.toHaveBeenCalled();
|
||||
expect(mockMetricsService.setup).not.toHaveBeenCalled();
|
||||
expect(mockStatusService.setup).not.toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
@ -36,6 +36,9 @@ import { UiSettingsService } from './ui_settings';
|
|||
import { PluginsService, config as pluginsConfig } from './plugins';
|
||||
import { SavedObjectsService } from '../server/saved_objects';
|
||||
import { MetricsService, opsConfig } from './metrics';
|
||||
import { CapabilitiesService } from './capabilities';
|
||||
import { UuidService } from './uuid';
|
||||
import { StatusService } from './status/status_service';
|
||||
|
||||
import { config as cspConfig } from './csp';
|
||||
import { config as elasticsearchConfig } from './elasticsearch';
|
||||
|
@ -50,8 +53,6 @@ import { mapToObject } from '../utils';
|
|||
import { ContextService } from './context';
|
||||
import { RequestHandlerContext } from '.';
|
||||
import { InternalCoreSetup, InternalCoreStart } from './internal_types';
|
||||
import { CapabilitiesService } from './capabilities';
|
||||
import { UuidService } from './uuid';
|
||||
|
||||
const coreId = Symbol('core');
|
||||
const rootConfigPath = '';
|
||||
|
@ -70,6 +71,7 @@ export class Server {
|
|||
private readonly uiSettings: UiSettingsService;
|
||||
private readonly uuid: UuidService;
|
||||
private readonly metrics: MetricsService;
|
||||
private readonly status: StatusService;
|
||||
private readonly coreApp: CoreApp;
|
||||
|
||||
private pluginsInitialized?: boolean;
|
||||
|
@ -95,6 +97,7 @@ export class Server {
|
|||
this.capabilities = new CapabilitiesService(core);
|
||||
this.uuid = new UuidService(core);
|
||||
this.metrics = new MetricsService(core);
|
||||
this.status = new StatusService(core);
|
||||
this.coreApp = new CoreApp(core);
|
||||
}
|
||||
|
||||
|
@ -145,15 +148,21 @@ export class Server {
|
|||
|
||||
const metricsSetup = await this.metrics.setup({ http: httpSetup });
|
||||
|
||||
const statusSetup = this.status.setup({
|
||||
elasticsearch: elasticsearchServiceSetup,
|
||||
savedObjects: savedObjectsSetup,
|
||||
});
|
||||
|
||||
const coreSetup: InternalCoreSetup = {
|
||||
capabilities: capabilitiesSetup,
|
||||
context: contextServiceSetup,
|
||||
elasticsearch: elasticsearchServiceSetup,
|
||||
http: httpSetup,
|
||||
uiSettings: uiSettingsSetup,
|
||||
savedObjects: savedObjectsSetup,
|
||||
uuid: uuidSetup,
|
||||
metrics: metricsSetup,
|
||||
savedObjects: savedObjectsSetup,
|
||||
status: statusSetup,
|
||||
uiSettings: uiSettingsSetup,
|
||||
uuid: uuidSetup,
|
||||
};
|
||||
|
||||
const pluginsSetup = await this.plugins.setup(coreSetup);
|
||||
|
@ -220,6 +229,7 @@ export class Server {
|
|||
await this.uiSettings.stop();
|
||||
await this.rendering.stop();
|
||||
await this.metrics.stop();
|
||||
await this.status.stop();
|
||||
}
|
||||
|
||||
private registerCoreContext(coreSetup: InternalCoreSetup, rendering: RenderingServiceSetup) {
|
||||
|
|
180
src/core/server/status/get_summary_status.test.ts
Normal file
180
src/core/server/status/get_summary_status.test.ts
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { ServiceStatus, ServiceStatusLevels } from './types';
|
||||
import { getSummaryStatus } from './get_summary_status';
|
||||
|
||||
describe('getSummaryStatus', () => {
|
||||
const available: ServiceStatus = { level: ServiceStatusLevels.available, summary: 'Available' };
|
||||
const degraded: ServiceStatus = {
|
||||
level: ServiceStatusLevels.degraded,
|
||||
summary: 'This is degraded!',
|
||||
};
|
||||
const unavailable: ServiceStatus = {
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: 'This is unavailable!',
|
||||
};
|
||||
const critical: ServiceStatus = {
|
||||
level: ServiceStatusLevels.critical,
|
||||
summary: 'This is critical!',
|
||||
};
|
||||
|
||||
it('returns available when all status are available', () => {
|
||||
expect(
|
||||
getSummaryStatus(
|
||||
Object.entries({
|
||||
s1: available,
|
||||
s2: available,
|
||||
s3: available,
|
||||
})
|
||||
)
|
||||
).toMatchObject({
|
||||
level: ServiceStatusLevels.available,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns degraded when the worst status is degraded', () => {
|
||||
expect(
|
||||
getSummaryStatus(
|
||||
Object.entries({
|
||||
s1: available,
|
||||
s2: degraded,
|
||||
s3: available,
|
||||
})
|
||||
)
|
||||
).toMatchObject({
|
||||
level: ServiceStatusLevels.degraded,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns unavailable when the worst status is unavailable', () => {
|
||||
expect(
|
||||
getSummaryStatus(
|
||||
Object.entries({
|
||||
s1: available,
|
||||
s2: degraded,
|
||||
s3: unavailable,
|
||||
})
|
||||
)
|
||||
).toMatchObject({
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns critical when the worst status is critical', () => {
|
||||
expect(
|
||||
getSummaryStatus(
|
||||
Object.entries({
|
||||
s1: critical,
|
||||
s2: degraded,
|
||||
s3: unavailable,
|
||||
})
|
||||
)
|
||||
).toMatchObject({
|
||||
level: ServiceStatusLevels.critical,
|
||||
});
|
||||
});
|
||||
|
||||
describe('summary', () => {
|
||||
describe('when a single service is at highest level', () => {
|
||||
it('returns all information about that single service', () => {
|
||||
expect(
|
||||
getSummaryStatus(
|
||||
Object.entries({
|
||||
s1: degraded,
|
||||
s2: {
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: 'Lorem ipsum',
|
||||
detail: 'Vivamus pulvinar sem ac luctus ultrices.',
|
||||
documentationUrl: 'http://helpmenow.com/problem1',
|
||||
meta: {
|
||||
custom: { data: 'here' },
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
).toEqual({
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: '[s2]: Lorem ipsum',
|
||||
detail: 'Vivamus pulvinar sem ac luctus ultrices.',
|
||||
documentationUrl: 'http://helpmenow.com/problem1',
|
||||
meta: {
|
||||
custom: { data: 'here' },
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when multiple services is at highest level', () => {
|
||||
it('returns aggregated information about the affected services', () => {
|
||||
expect(
|
||||
getSummaryStatus(
|
||||
Object.entries({
|
||||
s1: degraded,
|
||||
s2: {
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: 'Lorem ipsum',
|
||||
detail: 'Vivamus pulvinar sem ac luctus ultrices.',
|
||||
documentationUrl: 'http://helpmenow.com/problem1',
|
||||
meta: {
|
||||
custom: { data: 'here' },
|
||||
},
|
||||
},
|
||||
s3: {
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: 'Proin mattis',
|
||||
detail: 'Nunc quis nulla at mi lobortis pretium.',
|
||||
documentationUrl: 'http://helpmenow.com/problem2',
|
||||
meta: {
|
||||
other: { data: 'over there' },
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
).toEqual({
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: '[2] services are unavailable',
|
||||
detail: 'See the status page for more information',
|
||||
meta: {
|
||||
affectedServices: {
|
||||
s2: {
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: 'Lorem ipsum',
|
||||
detail: 'Vivamus pulvinar sem ac luctus ultrices.',
|
||||
documentationUrl: 'http://helpmenow.com/problem1',
|
||||
meta: {
|
||||
custom: { data: 'here' },
|
||||
},
|
||||
},
|
||||
s3: {
|
||||
level: ServiceStatusLevels.unavailable,
|
||||
summary: 'Proin mattis',
|
||||
detail: 'Nunc quis nulla at mi lobortis pretium.',
|
||||
documentationUrl: 'http://helpmenow.com/problem2',
|
||||
meta: {
|
||||
other: { data: 'over there' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
84
src/core/server/status/get_summary_status.ts
Normal file
84
src/core/server/status/get_summary_status.ts
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { ServiceStatus, ServiceStatusLevels, ServiceStatusLevel } from './types';
|
||||
|
||||
/**
|
||||
* Returns a single {@link ServiceStatus} that summarizes the most severe status level from a group of statuses.
|
||||
* @param statuses
|
||||
*/
|
||||
export const getSummaryStatus = (statuses: Array<[string, ServiceStatus]>): ServiceStatus => {
|
||||
const grouped = groupByLevel(statuses);
|
||||
const highestSeverityLevel = getHighestSeverityLevel(grouped.keys());
|
||||
const highestSeverityGroup = grouped.get(highestSeverityLevel)!;
|
||||
|
||||
if (highestSeverityLevel === ServiceStatusLevels.available) {
|
||||
return {
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: `All services are available`,
|
||||
};
|
||||
} else if (highestSeverityGroup.size === 1) {
|
||||
const [serviceName, status] = [...highestSeverityGroup.entries()][0];
|
||||
return {
|
||||
...status,
|
||||
summary: `[${serviceName}]: ${status.summary!}`,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
level: highestSeverityLevel,
|
||||
summary: `[${highestSeverityGroup.size}] services are ${highestSeverityLevel.toString()}`,
|
||||
// TODO: include URL to status page
|
||||
detail: `See the status page for more information`,
|
||||
meta: {
|
||||
affectedServices: Object.fromEntries([...highestSeverityGroup]),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const groupByLevel = (
|
||||
statuses: Array<[string, ServiceStatus]>
|
||||
): Map<ServiceStatusLevel, Map<string, ServiceStatus>> => {
|
||||
const byLevel = new Map<ServiceStatusLevel, Map<string, ServiceStatus>>();
|
||||
|
||||
for (const [serviceName, status] of statuses) {
|
||||
let levelMap = byLevel.get(status.level);
|
||||
if (!levelMap) {
|
||||
levelMap = new Map<string, ServiceStatus>();
|
||||
byLevel.set(status.level, levelMap);
|
||||
}
|
||||
|
||||
levelMap.set(serviceName, status);
|
||||
}
|
||||
|
||||
return byLevel;
|
||||
};
|
||||
|
||||
const getHighestSeverityLevel = (levels: Iterable<ServiceStatusLevel>): ServiceStatusLevel => {
|
||||
const sorted = [...levels].sort((a, b) => {
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a > b) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
return sorted[sorted.length - 1] ?? ServiceStatusLevels.available;
|
||||
};
|
21
src/core/server/status/index.ts
Normal file
21
src/core/server/status/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export { StatusService } from './status_service';
|
||||
export * from './types';
|
71
src/core/server/status/status_service.mock.ts
Normal file
71
src/core/server/status/status_service.mock.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { StatusService } from './status_service';
|
||||
import {
|
||||
InternalStatusServiceSetup,
|
||||
StatusServiceSetup,
|
||||
ServiceStatusLevels,
|
||||
ServiceStatus,
|
||||
CoreStatus,
|
||||
} from './types';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
const available: ServiceStatus = {
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: 'Service is working',
|
||||
};
|
||||
const availableCoreStatus: CoreStatus = {
|
||||
elasticsearch: available,
|
||||
savedObjects: available,
|
||||
};
|
||||
|
||||
const createSetupContractMock = () => {
|
||||
const setupContract: jest.Mocked<StatusServiceSetup> = {
|
||||
core$: new BehaviorSubject(availableCoreStatus),
|
||||
};
|
||||
|
||||
return setupContract;
|
||||
};
|
||||
|
||||
const createInternalSetupContractMock = () => {
|
||||
const setupContract: jest.Mocked<InternalStatusServiceSetup> = {
|
||||
core$: new BehaviorSubject(availableCoreStatus),
|
||||
overall$: new BehaviorSubject(available),
|
||||
};
|
||||
|
||||
return setupContract;
|
||||
};
|
||||
|
||||
type StatusServiceContract = PublicMethodsOf<StatusService>;
|
||||
|
||||
const createMock = () => {
|
||||
const mocked: jest.Mocked<StatusServiceContract> = {
|
||||
setup: jest.fn().mockReturnValue(createInternalSetupContractMock()),
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
};
|
||||
return mocked;
|
||||
};
|
||||
|
||||
export const statusServiceMock = {
|
||||
create: createMock,
|
||||
createSetupContract: createSetupContractMock,
|
||||
createInternalSetupContract: createInternalSetupContractMock,
|
||||
};
|
229
src/core/server/status/status_service.test.ts
Normal file
229
src/core/server/status/status_service.test.ts
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { of, BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { ServiceStatus, ServiceStatusLevels, CoreStatus } from './types';
|
||||
import { StatusService } from './status_service';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { mockCoreContext } from '../core_context.mock';
|
||||
import { ServiceStatusLevelSnapshotSerializer } from './test_utils';
|
||||
|
||||
expect.addSnapshotSerializer(ServiceStatusLevelSnapshotSerializer);
|
||||
|
||||
describe('StatusService', () => {
|
||||
const available: ServiceStatus<any> = {
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: 'Available',
|
||||
};
|
||||
const degraded: ServiceStatus<any> = {
|
||||
level: ServiceStatusLevels.degraded,
|
||||
summary: 'This is degraded!',
|
||||
};
|
||||
|
||||
describe('setup', () => {
|
||||
describe('core$', () => {
|
||||
it('rolls up core status observables into single observable', async () => {
|
||||
const setup = new StatusService(mockCoreContext.create()).setup({
|
||||
elasticsearch: {
|
||||
status$: of(available),
|
||||
},
|
||||
savedObjects: {
|
||||
status$: of(degraded),
|
||||
},
|
||||
});
|
||||
expect(await setup.core$.pipe(first()).toPromise()).toEqual({
|
||||
elasticsearch: available,
|
||||
savedObjects: degraded,
|
||||
});
|
||||
});
|
||||
|
||||
it('replays last event', async () => {
|
||||
const setup = new StatusService(mockCoreContext.create()).setup({
|
||||
elasticsearch: {
|
||||
status$: of(available),
|
||||
},
|
||||
savedObjects: {
|
||||
status$: of(degraded),
|
||||
},
|
||||
});
|
||||
const subResult1 = await setup.core$.pipe(first()).toPromise();
|
||||
const subResult2 = await setup.core$.pipe(first()).toPromise();
|
||||
const subResult3 = await setup.core$.pipe(first()).toPromise();
|
||||
expect(subResult1).toEqual({
|
||||
elasticsearch: available,
|
||||
savedObjects: degraded,
|
||||
});
|
||||
expect(subResult2).toEqual({
|
||||
elasticsearch: available,
|
||||
savedObjects: degraded,
|
||||
});
|
||||
expect(subResult3).toEqual({
|
||||
elasticsearch: available,
|
||||
savedObjects: degraded,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not emit duplicate events', () => {
|
||||
const elasticsearch$ = new BehaviorSubject(available);
|
||||
const savedObjects$ = new BehaviorSubject(degraded);
|
||||
const setup = new StatusService(mockCoreContext.create()).setup({
|
||||
elasticsearch: {
|
||||
status$: elasticsearch$,
|
||||
},
|
||||
savedObjects: {
|
||||
status$: savedObjects$,
|
||||
},
|
||||
});
|
||||
|
||||
const statusUpdates: CoreStatus[] = [];
|
||||
const subscription = setup.core$.subscribe(status => statusUpdates.push(status));
|
||||
|
||||
elasticsearch$.next(available);
|
||||
elasticsearch$.next(available);
|
||||
elasticsearch$.next({
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: `Wow another summary`,
|
||||
});
|
||||
savedObjects$.next(degraded);
|
||||
savedObjects$.next(available);
|
||||
savedObjects$.next(available);
|
||||
subscription.unsubscribe();
|
||||
|
||||
expect(statusUpdates).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"elasticsearch": Object {
|
||||
"level": available,
|
||||
"summary": "Available",
|
||||
},
|
||||
"savedObjects": Object {
|
||||
"level": degraded,
|
||||
"summary": "This is degraded!",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"elasticsearch": Object {
|
||||
"level": available,
|
||||
"summary": "Wow another summary",
|
||||
},
|
||||
"savedObjects": Object {
|
||||
"level": degraded,
|
||||
"summary": "This is degraded!",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"elasticsearch": Object {
|
||||
"level": available,
|
||||
"summary": "Wow another summary",
|
||||
},
|
||||
"savedObjects": Object {
|
||||
"level": available,
|
||||
"summary": "Available",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('overall$', () => {
|
||||
it('exposes an overall summary', async () => {
|
||||
const setup = new StatusService(mockCoreContext.create()).setup({
|
||||
elasticsearch: {
|
||||
status$: of(degraded),
|
||||
},
|
||||
savedObjects: {
|
||||
status$: of(degraded),
|
||||
},
|
||||
});
|
||||
expect(await setup.overall$.pipe(first()).toPromise()).toMatchObject({
|
||||
level: ServiceStatusLevels.degraded,
|
||||
summary: '[2] services are degraded',
|
||||
});
|
||||
});
|
||||
|
||||
it('replays last event', async () => {
|
||||
const setup = new StatusService(mockCoreContext.create()).setup({
|
||||
elasticsearch: {
|
||||
status$: of(degraded),
|
||||
},
|
||||
savedObjects: {
|
||||
status$: of(degraded),
|
||||
},
|
||||
});
|
||||
const subResult1 = await setup.overall$.pipe(first()).toPromise();
|
||||
const subResult2 = await setup.overall$.pipe(first()).toPromise();
|
||||
const subResult3 = await setup.overall$.pipe(first()).toPromise();
|
||||
expect(subResult1).toMatchObject({
|
||||
level: ServiceStatusLevels.degraded,
|
||||
summary: '[2] services are degraded',
|
||||
});
|
||||
expect(subResult2).toMatchObject({
|
||||
level: ServiceStatusLevels.degraded,
|
||||
summary: '[2] services are degraded',
|
||||
});
|
||||
expect(subResult3).toMatchObject({
|
||||
level: ServiceStatusLevels.degraded,
|
||||
summary: '[2] services are degraded',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not emit duplicate events', () => {
|
||||
const elasticsearch$ = new BehaviorSubject(available);
|
||||
const savedObjects$ = new BehaviorSubject(degraded);
|
||||
const setup = new StatusService(mockCoreContext.create()).setup({
|
||||
elasticsearch: {
|
||||
status$: elasticsearch$,
|
||||
},
|
||||
savedObjects: {
|
||||
status$: savedObjects$,
|
||||
},
|
||||
});
|
||||
|
||||
const statusUpdates: ServiceStatus[] = [];
|
||||
const subscription = setup.overall$.subscribe(status => statusUpdates.push(status));
|
||||
|
||||
elasticsearch$.next(available);
|
||||
elasticsearch$.next(available);
|
||||
elasticsearch$.next({
|
||||
level: ServiceStatusLevels.available,
|
||||
summary: `Wow another summary`,
|
||||
});
|
||||
savedObjects$.next(degraded);
|
||||
savedObjects$.next(available);
|
||||
savedObjects$.next(available);
|
||||
subscription.unsubscribe();
|
||||
|
||||
expect(statusUpdates).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"level": degraded,
|
||||
"summary": "[savedObjects]: This is degraded!",
|
||||
},
|
||||
Object {
|
||||
"level": available,
|
||||
"summary": "All services are available",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
78
src/core/server/status/status_service.ts
Normal file
78
src/core/server/status/status_service.ts
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { map, distinctUntilChanged, shareReplay } from 'rxjs/operators';
|
||||
import { isDeepStrictEqual } from 'util';
|
||||
|
||||
import { CoreService } from '../../types';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { Logger } from '../logging';
|
||||
import { InternalElasticsearchServiceSetup } from '../elasticsearch';
|
||||
import { InternalSavedObjectsServiceSetup } from '../saved_objects';
|
||||
|
||||
import { ServiceStatus, CoreStatus, InternalStatusServiceSetup } from './types';
|
||||
import { getSummaryStatus } from './get_summary_status';
|
||||
|
||||
interface SetupDeps {
|
||||
elasticsearch: Pick<InternalElasticsearchServiceSetup, 'status$'>;
|
||||
savedObjects: Pick<InternalSavedObjectsServiceSetup, 'status$'>;
|
||||
}
|
||||
|
||||
export class StatusService implements CoreService<InternalStatusServiceSetup> {
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(coreContext: CoreContext) {
|
||||
this.logger = coreContext.logger.get('status');
|
||||
}
|
||||
|
||||
public setup(core: SetupDeps) {
|
||||
const core$ = this.setupCoreStatus(core);
|
||||
const overall$: Observable<ServiceStatus> = core$.pipe(
|
||||
map(coreStatus => {
|
||||
const summary = getSummaryStatus(Object.entries(coreStatus));
|
||||
this.logger.debug(`Recalculated overall status`, { status: summary });
|
||||
return summary;
|
||||
}),
|
||||
distinctUntilChanged(isDeepStrictEqual)
|
||||
);
|
||||
|
||||
return {
|
||||
core$,
|
||||
overall$,
|
||||
};
|
||||
}
|
||||
|
||||
public start() {}
|
||||
|
||||
public stop() {}
|
||||
|
||||
private setupCoreStatus({ elasticsearch, savedObjects }: SetupDeps): Observable<CoreStatus> {
|
||||
return combineLatest([elasticsearch.status$, savedObjects.status$]).pipe(
|
||||
map(([elasticsearchStatus, savedObjectsStatus]) => ({
|
||||
elasticsearch: elasticsearchStatus,
|
||||
savedObjects: savedObjectsStatus,
|
||||
})),
|
||||
distinctUntilChanged(isDeepStrictEqual),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
}
|
25
src/core/server/status/test_utils.ts
Normal file
25
src/core/server/status/test_utils.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { ServiceStatusLevels, ServiceStatusLevel } from './types';
|
||||
|
||||
export const ServiceStatusLevelSnapshotSerializer: jest.SnapshotSerializerPlugin = {
|
||||
test: (val: any) => Object.values(ServiceStatusLevels).includes(val),
|
||||
print: (val: ServiceStatusLevel) => val.toString(),
|
||||
};
|
134
src/core/server/status/types.ts
Normal file
134
src/core/server/status/types.ts
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { deepFreeze } from '../../utils';
|
||||
|
||||
/**
|
||||
* The current status of a service at a point in time.
|
||||
*
|
||||
* @typeParam Meta - JSON-serializable object. Plugins should export this type to allow other plugins to read the `meta`
|
||||
* field in a type-safe way.
|
||||
* @public
|
||||
*/
|
||||
export interface ServiceStatus<Meta extends Record<string, any> | unknown = unknown> {
|
||||
/**
|
||||
* The current availability level of the service.
|
||||
*/
|
||||
level: ServiceStatusLevel;
|
||||
/**
|
||||
* A high-level summary of the service status.
|
||||
*/
|
||||
summary: string;
|
||||
/**
|
||||
* A more detailed description of the service status.
|
||||
*/
|
||||
detail?: string;
|
||||
/**
|
||||
* A URL to open in a new tab about how to resolve or troubleshoot the problem.
|
||||
*/
|
||||
documentationUrl?: string;
|
||||
/**
|
||||
* Any JSON-serializable data to be included in the HTTP API response. Useful for providing more fine-grained,
|
||||
* machine-readable information about the service status. May include status information for underlying features.
|
||||
*/
|
||||
meta?: Meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current "level" of availability of a service.
|
||||
*
|
||||
* @remarks
|
||||
* The values implement `valueOf` to allow for easy comparisons between status levels with <, >, etc. Higher values
|
||||
* represent higher severities. Note that the default `Array.prototype.sort` implementation does not correctly sort
|
||||
* these values.
|
||||
*
|
||||
* A snapshot serializer is available in `src/core/server/test_utils` to ease testing of these values with Jest.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const ServiceStatusLevels = deepFreeze({
|
||||
/**
|
||||
* Everything is working!
|
||||
*/
|
||||
available: {
|
||||
toString: () => 'available',
|
||||
valueOf: () => 0,
|
||||
},
|
||||
/**
|
||||
* Some features may not be working.
|
||||
*/
|
||||
degraded: {
|
||||
toString: () => 'degraded',
|
||||
valueOf: () => 1,
|
||||
},
|
||||
/**
|
||||
* The service is unavailable, but other functions that do not depend on this service should work.
|
||||
*/
|
||||
unavailable: {
|
||||
toString: () => 'unavailable',
|
||||
valueOf: () => 2,
|
||||
},
|
||||
/**
|
||||
* Block all user functions and display the status page, reserved for Core services only.
|
||||
*/
|
||||
critical: {
|
||||
toString: () => 'critical',
|
||||
valueOf: () => 3,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* A convenience type that represents the union of each value in {@link ServiceStatusLevels}.
|
||||
* @public
|
||||
*/
|
||||
export type ServiceStatusLevel = typeof ServiceStatusLevels[keyof typeof ServiceStatusLevels];
|
||||
|
||||
/**
|
||||
* Status of core services.
|
||||
*
|
||||
* @internalRemarks
|
||||
* Only contains entries for backend services that could have a non-available `status`.
|
||||
* For example, `context` cannot possibly be broken, so it is not included.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface CoreStatus {
|
||||
elasticsearch: ServiceStatus;
|
||||
savedObjects: ServiceStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* API for accessing status of Core and this plugin's dependencies as well as for customizing this plugin's status.
|
||||
* @public
|
||||
*/
|
||||
export interface StatusServiceSetup {
|
||||
/**
|
||||
* Current status for all Core services.
|
||||
*/
|
||||
core$: Observable<CoreStatus>;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface InternalStatusServiceSetup extends StatusServiceSetup {
|
||||
/**
|
||||
* Overall system status used for HTTP API
|
||||
*/
|
||||
overall$: Observable<ServiceStatus>;
|
||||
}
|
|
@ -18,3 +18,4 @@
|
|||
*/
|
||||
|
||||
export { createHttpServer } from './http/test_utils';
|
||||
export { ServiceStatusLevelSnapshotSerializer } from './status/test_utils';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue