-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Kibana preboot
lifecycle stage.
#99318
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,261 @@ | ||
- Start Date: 2020-06-04 | ||
- RFC PR: (leave this empty) | ||
- Kibana Issue: https://github.com/elastic/kibana/issues/89287 | ||
|
||
--- | ||
- [1. Summary](#1-summary) | ||
- [2. Motivation](#2-motivation) | ||
- [3. Detailed design](#3-detailed-design) | ||
- [3.1 Core client-side changes](#31-core-client-side-changes) | ||
- [3.2 Core server-side changes](#32-core-server-side-changes) | ||
- [3.2.1 Plugins service](#321-plugins-service) | ||
- [3.2.2 HTTP service](#322-http-service) | ||
- [3.2.3 Elasticsearch service](#323-elasticsearch-service) | ||
- [3.2.4 UI Settings service](#324-ui-settings-service) | ||
- [3.2.5 Rendering service](#325-rendering-service) | ||
- [3.2.6 I18n service](#326-i18n-service) | ||
- [3.2.7 Environment service](#327-environment-service) | ||
- [3.2.8 Core app service](#328-core-app-service) | ||
- [3.2.9 Preboot service](#329-preboot-service) | ||
- [3.2.10 Bootstrap](#3210-bootstrap) | ||
- [4. Drawbacks](#4-drawbacks) | ||
- [5. Alternatives](#5-alternatives) | ||
- [6. Adoption strategy](#6-adoption-strategy) | ||
- [7. How we teach this](#7-how-we-teach-this) | ||
- [8. Unresolved questions](#8-unresolved-questions) | ||
- [8.1 Lifecycle stage name](#81-lifecycle-stage-name) | ||
- [8.2 Development mode and basepath proxy](#82-development-mode-and-basepath-proxy) | ||
- [9. Resolved questions](#9-resolved-questions) | ||
- [9.1 Core client-side changes](#91-core-client-side-changes) | ||
|
||
# 1. Summary | ||
|
||
The `preboot` (see [unresolved question 1](#81-lifecycle-stage-name)) is the Kibana initial lifecycle stage at which it only initializes a bare minimum of the core services and a limited set of special-purpose plugins. It's assumed that Kibana can change and reload its own configuration at this stage and may require administrator involvement before it can proceed to the `setup` and `start` stages. | ||
|
||
# 2. Motivation | ||
|
||
The `preboot` lifecycle stage is a prerequisite for the Kibana interactive setup mode. This is the mode Kibana enters to on the first launch if it detects that user hasn't explicitly configured their own connection to Elasticsearch. In this mode, Kibana will present an interface to the user that would allow them to provide Elasticsearch connection information and potentially any other configuration information. Once the information is verified, Kibana will write it to the disk and allow the rest of Kibana to start. | ||
|
||
The interactive setup mode will be provided through a dedicated `userSetup` plugin that will be initialized at the `preboot` stage. | ||
|
||
# 3. Detailed design | ||
|
||
The central part of the `preboot` stage is a dedicated HTTP server instance formerly known as `Not Ready` server. Kibana starts this server at the `preboot` stage and shuts it down as soon as the main HTTP server is ready to start, as illustrated at the following diagram: | ||
|
||
![Preboot plugins lifetime](../images/0019_lifecycle_preboot.png) | ||
|
||
Currently, preboot HTTP server only exposes a status endpoint and renders a static `Kibana server is not ready yet` string whenever users try to access Kibana before it's completely initialized. The changes proposed in this RFC should allow special-purpose plugins to define custom HTTP endpoints, and serve interactive client-side applications on this server, and hence make Kibana interactive setup mode possible. | ||
|
||
## 3.1 Core client-side changes | ||
|
||
The RFC aims to limit the changes to only those that are absolutely required and doesn't assume any modifications in the client-side part of the Kibana Core at the moment. This may introduce a certain level of inconsistency in the client-side codebase, but we consider it insignificant. See [resolved question 1](#91-core-client-side-changes) for more details. | ||
|
||
## 3.2 Core server-side changes | ||
|
||
We'll update only several Core server-side services to support the new `preboot` lifecycle stage and preboot plugins. | ||
|
||
Once none of the `preboot` plugins holds the `setup` anymore, Kibana might need to reload the configuration before it can finally proceed to `setup`. This doesn't require any special care from the existing plugin developers since Kibana would instantiate plugins only after it reloads the config. We'll also make sure that neither of the Core services relies on the stale configuration it may have acquired during the `preboot` stage. | ||
|
||
### 3.2.1 Plugins service | ||
|
||
First of all, we'll introduce a new type of special-purpose plugins: preboot plugins, in contrast to standard plugins. Kibana will initialize preboot plugins at the `preboot` stage, before even instantiating standard plugins. | ||
|
||
Preboot plugins have only `setup` and `stop` methods, and can only depend on other preboot plugins. Standard plugins cannot depend on the preboot plugins since Kibana will stop them before starting the standard plugins: | ||
|
||
```ts | ||
export interface PrebootPlugin<TSetup = void, TPluginsSetup extends object = object> { | ||
setup(core: CorePrebootSetup, plugins: TPluginsSetup): TSetup; | ||
stop?(): void; | ||
} | ||
``` | ||
|
||
To differentiate preboot and standard plugins we'll introduce a new _optional_ `type` property in the plugin manifest. The property can have only two possible values: `preboot` for `preboot` plugins and `standard` for the standard ones. If `type` is omitted, the `standard` value will be assumed. | ||
|
||
```json5 | ||
// NOTE(azasypkin): all other existing properties have been omitted for brevity. | ||
{ | ||
"type": "preboot", // 'preboot' | 'standard' | undefined | ||
} | ||
``` | ||
|
||
The Plugins service will split plugins into two separate groups during discovery to use them separately at the `preboot`, `setup`, and `start` stages. The Core contract that preboot plugins will receive during their `setup` will be different from the one standard plugins receive, and will only include the functionality that is currently required for the interactive setup mode. We'll discuss this functionality in details in the following sections: | ||
|
||
```ts | ||
export interface CorePrebootSetup { | ||
elasticsearch: ElasticsearchServicePrebootSetup; | ||
http: HttpServicePrebootSetup; | ||
preboot: PrebootServiceSetup; | ||
} | ||
``` | ||
|
||
### 3.2.2 HTTP service | ||
|
||
We'll change HTTP service to initialize and start preboot HTTP server (formerly known as `Not Ready` server) in the new `preboot` method instead of `setup`. The returned `InternalHttpServicePrebootSetup` contract will presumably be very similar to the existing `InternalHttpServiceSetup` contract, but will only include APIs we currently need to support interactive setup mode: | ||
|
||
```ts | ||
// NOTE(azasypkin): some existing properties have been omitted for brevity. | ||
export interface InternalHttpServicePrebootSetup | ||
extends Pick<HttpServiceSetup, 'auth' | 'csp' | 'basePath' | 'getServerInfo'> { | ||
server: HttpServerSetup['server']; | ||
externalUrl: ExternalUrlConfig; | ||
registerRoutes(path: string, callback: (router: IRouter) => void): void; | ||
} | ||
``` | ||
|
||
The only part of this contract that will be available to the preboot plugins via `CorePrebootSetup` is the API to register HTTP routes on the already running preboot HTTP server: | ||
|
||
```ts | ||
export interface HttpServicePrebootSetup { | ||
registerRoutes(path: string, callback: (router: IRouter) => void): void; | ||
} | ||
``` | ||
|
||
The Core HTTP context available to handlers of the routes registered on the preboot HTTP server will only expose the `uiSettings` service. As explained in the [UI Settings service section](#324-ui-settings-service), this service will only give access to the **default Core** UI settings and their overrides set through Kibana configuration, if any. | ||
```ts | ||
// NOTE(azasypkin): the fact that the client is lazily initialized has been omitted for brevity. | ||
export interface PrebootCoreRouteHandlerContext { | ||
readonly uiSettings: { client: IUiSettingsClient }; | ||
} | ||
``` | ||
|
||
The authentication and authorization components are not available at the `preboot` stage, and hence all preboot HTTP server routes can be freely accessed by anyone with access to the network Kibana is exposed to. | ||
|
||
Just as today, Kibana will shut the preboot HTTP server down as soon as it's ready to start the main HTTP server. | ||
|
||
### 3.2.3 Elasticsearch service | ||
|
||
As mentioned in the [Motivation section](#2-motivation), the main goal of the interactive setup mode is to give the user a hassle-free way to configure Kibana connection to an Elasticsearch cluster. That means that users might provide certain connection information, and Kibana preboot plugins should be able to construct a new Elasticsearch client using this information to verify it and potentially call Elasticsearch APIs. | ||
|
||
To support this use case we'll add a new `preboot` method to the Elasticsearch service that will return the following contract, and make it available to the preboot plugins via `CorePrebootSetup`: | ||
|
||
```ts | ||
export interface ElasticsearchServicePrebootSetup { | ||
readonly createClient: ( | ||
type: string, | ||
clientConfig?: Partial<ElasticsearchClientConfig> | ||
) => ICustomClusterClient; | ||
} | ||
``` | ||
|
||
The Elasticsearch clients created with `createClient` rely on the default Kibana Elasticsearch configuration and any configuration overrides specified by the consumer. | ||
|
||
__NOTE:__ We may need to expose a full or portion of Elasticsearch config to the preboot plugins for them to check if the user has already configured Elasticsearch connection. There are other ways to check that without direct access to the configuration though. | ||
|
||
### 3.2.4 UI Settings service | ||
|
||
We'll introduce a new `preboot` method in the UI Settings service that will produce a UI Settings client instance. Since during the `preboot` stage Kibana can access neither user information nor Saved Objects, this client will only give access to the **default Core** UI settings and their overrides set through Kibana configuration, if any: | ||
|
||
```ts | ||
export interface InternalUiSettingsServicePrebootSetup { | ||
defaultsClient(): IUiSettingsClient; | ||
} | ||
``` | ||
|
||
UI Settings service isn't strictly necessary during the `preboot` stage, but many Kibana Core components rely on it explicitly and implicitly, which justifies this simple change. | ||
|
||
### 3.2.5 Rendering service | ||
|
||
We'll introduce a new `preboot` method in the Rendering service that will register Kibana main UI bootstrap template route on the preboot HTTP server as it does for the main HTTP server today. The main difference is that bootstrap UI will only reference bundles of the preboot plugins and will rely on the default UI settings. | ||
|
||
### 3.2.6 I18n service | ||
|
||
We'll introduce a new `preboot` method in the I18n service to only include translations for the Core itself and preboot plugins in the translations bundle loaded with the preboot UI bootstrap template. This would potentially allow us to switch locale during interactive setup mode if there is such a need in the future. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Agree, that we shouldn't implement it from the very beginning. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We don't translate log messages, but we still localize the interactive setup mode plugin (I can see us or partners distributing Kibana binaries with the predefined non-English locale). But every byte counts, so preboot HTTP server will only be serving translations required by Core and preboot plugins. |
||
|
||
### 3.2.7 Environment service | ||
|
||
There are no changes required in the Environment service itself, but we'll expose one additional property from its `setup` contract to the plugins: the paths to the known configuration files. The interactive setup mode should be able to figure out to which configuration file Kibana should save any changes users might need to make. | ||
|
||
### 3.2.8 Core app service | ||
|
||
We'll introduce a new `preboot` method in the Core app service to register routes on the preboot HTTP server necessary for the rendering of the Kibana preboot applications. Most of the routes will be the same as for the main HTTP server, but there are three notable exceptions: | ||
|
||
1. JS bundles routes will only include those exposed by the preboot plugins | ||
|
||
2. Default route for the preboot HTTP server will be hardcoded to the root path (`/`) since we cannot rely on the default value of the `defaultRoute` UI setting (`/app/home`) | ||
|
||
3. Main application route (`/app/{id}/{any*}`) will be replaced with the catch-all route (`/{path*}`). The reason is that if the user tries to access Kibana with a legit standard application URL (e.g. `/app/discover/?parameters`) while Kibana is still at the `preboot` stage, they will end up with `Application is not found` error. Instead, with the catch-all route, Kibana will capture the original URL in the `next` query string parameter and redirect the user to the root (e.g. `/?next=%2Fapp%2Fdiscover%2F%3Fparameters`). This will allow us to automatically redirect the user back to the original URL as soon as Kibana is ready. The main drawback and limitation of this approach are that there can be only one root-level preboot application. We can lift this limitation in the future if we have to though, for example, to support post-preboot Saved Objects migration UI or something similar. | ||
|
||
Serving a proper Kibana application on the root route of the preboot HTTP server implies that we'll also have a chance to replace the static `Kibana server is not ready yet` string with a more helpful and user-friendly application. Such application may potentially display a certain set of Kibana status information. | ||
|
||
### 3.2.9 Preboot service | ||
|
||
To support interactive applications at the `preboot` stage we should allow preboot plugins to pause Kibana startup sequence. This functionality will be exposed by the new Preboot service, and will be available to the preboot plugins via `CorePrebootSetup`. Preboot plugins will be able to provide a promise to hold `setup` and/or `start` for as long as needed, and also let Kibana know if it has to reload configuration before it enters the `setup` stage. | ||
|
||
```ts | ||
export interface PrebootServiceSetup { | ||
readonly isSetupOnHold: () => boolean; | ||
readonly holdSetupUntilResolved: ( | ||
reason: string, | ||
promise: Promise<{ shouldReloadConfig: boolean } | void> | ||
) => void; | ||
readonly isStartOnHold: () => boolean; | ||
readonly holdStartUntilResolved: ( | ||
reason: string, | ||
promise: Promise<void> | ||
) => void | ||
} | ||
``` | ||
|
||
Preboot service will provide a pair of helper `isSetupOnHold` and `isStartOnHold` methods that would allow consumers to check if `setup` or `start` are on hold before they are blocked on waiting. | ||
|
||
Internal Preboot service contract will also expose `waitUntilCanSetup` and `waitUntilCanStart` methods that bootstrap process can use to know when it can proceed to `setup` and `start` stages. If any of these methods returns a `Promise` that is rejected, Kibana will shut down. | ||
|
||
```ts | ||
// NOTE(azasypkin): some existing properties have been omitted for brevity. | ||
export interface InternalPrebootServiceSetup { | ||
readonly waitUntilCanSetup: () => Promise<{ shouldReloadConfig: boolean } | void>; | ||
readonly waitUntilCanStart: () => Promise<void>; | ||
} | ||
``` | ||
|
||
### 3.2.10 Bootstrap | ||
|
||
We'll update Kibana bootstrap sequence to include `preboot` stage and to conditionally reload configuration before proceeding to `setup` and `start` stages: | ||
|
||
```ts | ||
// NOTE(azasypkin): some functionality and checks have been omitted for brevity. | ||
const { preboot } = await root.preboot(); | ||
|
||
const { shouldReloadConfig } = await preboot.waitUntilCanSetup(); | ||
if (shouldReloadConfig) { | ||
await reloadConfiguration('pre-boot request'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm trying to decide if we want to run through the On the other hand, a bug in the I'm torn on which way to go, so I'll leave this here as a discussion topic for now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We can, but I see only two reasons when this can happen:
These look like unrecoverable issues to me, and I'd prefer Kibana to crash as it does today when config validation fails. For example, if HTTP config fails validation we won't be able to even start preboot HTTP server.
That would be a great feature for Kibana for sure, but I feel like it's a bit out of scope for now. The near future plan is to make very few config changes and every change should be extensively validated. Having said that, you're raising a very important point that seems to be outside of this RFC, but we'll need to take care of it as soon as we start working on the |
||
} | ||
await root.setup(); | ||
|
||
await preboot.waitUntilCanStart(); | ||
await root.start(); | ||
``` | ||
|
||
It's not yet clear if we need to adjust the base path proxy to account for this new lifecycle stage (see [unresolved question 2](#82-development-mode-and-basepath-proxy)). | ||
|
||
# 4. Drawbacks | ||
|
||
The main drawback is that proposed changes affect quite a few Kibana Core services that may impose a risk of breaking something in the critical parts of Kibana. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if we have a requirement to support configuration changes from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please elaborate on this a bit? I'm not sure I'm following. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant that if we had a requirement to support configuration changes from Stack Management UI, Kibana wouldn't leverage the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see now, thanks. Yeah, to be able to handle configuration changes from the Stack Management UI "at run-time" we'll likely need to widely adopt that reactive approach Josh mentioned in #99318 (review). Hopefully we can get there at some point. |
||
|
||
# 5. Alternatives | ||
|
||
The most viable alternative to support interactive setup mode for Kibana was a standalone application that would be completely separated from Kibana. We ruled out this option since we won't be able to leverage existing and battle-tested Core services, UI components, and development tools. This would make the long-term maintenance burden unreasonably high. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I initially though the main reason to have an UI for this was for Cloud, as we can't provide a CLI tool for cloud customers. However, AFAIK, cloud instances are not really impacted by this interactive setup, as instances should already be properly configured. Given that, isn't the possibility to provide a cli tool another potential alternative? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Answered in #99318 (comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another alternative could be to avoid blocking Kibana start until the connection to Elasticsearch is established. @pgayvallet would it be covered by #96626? Of course, it's not something we'll add tomorrow, but if we think it's the right approach, we can temporarily limit ourselves to any quick hack-ish solution. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No, for multiple reasons
|
||
|
||
# 6. Adoption strategy | ||
|
||
The new `preboot` stage doesn't need an adoption strategy since it's intended for internal platform use only. | ||
|
||
# 7. How we teach this | ||
|
||
The new `preboot` stage shouldn't need much knowledge sharing since it's intended for internal platform use only and doesn't affect the standard plugins. All new services, methods, and contracts will be sufficiently documented in the code. | ||
|
||
# 8. Unresolved questions | ||
|
||
## 8.1 Lifecycle stage name | ||
|
||
Is `preboot` the right name for this new lifecycle stage? Do we have a better alternative? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I was thinking of |
||
|
||
## 8.2 Development mode and basepath proxy | ||
|
||
Currently, the base path proxy blocks any requests to Kibana until it receives `SERVER_LISTENING` message. Kibana's main process sends this message only after `start`, but we should change that to support interactive preboot applications. It's not yet clear how big the impact of this change will be. | ||
|
||
# 9. Resolved questions | ||
|
||
## 9.1 Core client-side changes | ||
|
||
The server-side part of the `preboot` plugins will follow a new `PrebootPlugin` interface that doesn't have a `start` method, but the client-side part will stay the same as for standard plugins. This significantly simplifies implementation and doesn't introduce any known technical issues, but, unfortunately, brings some inconsistency to the codebase. We agreed that it's tolerable assuming we define a dedicated client-side `PrebootPlugin` interface that would hide from `CoreStart` all services that are unavailable to the preboot plugins (e.g., Saved Objects service). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Motivation
says we runpreboot
phase only ifit detects that user hasn't explicitly configured their own connection to Elasticsearch.
Does it mean that we runpreboot
whenever ES configuration is present, but Kibana cannot establish a network connection?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good question.
The
preboot
will still run, but if interactive setup mode plugin detects that user has already configured connection to ES it 1) will not hold the boot, and 2) will instead render a "replacement" for the staticKibana server is not ready yet
string (Design Team will help us to display something user-friendlier and that will automatically redirect user to the original URL when the primary server is up).Yes, if a user has already configured ES connection, we assume they know what they are doing no matter if ES is available or not. But it'd easy to improve experience in the future if we feel we need it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I strongly feel we should be showing some limited status information on this UI to indicate what's happening inside Kibana. Historically, this information has required authentication to access (
/api/status
), but I think we could safely expose just the overall status level and summary text without requiring auth.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. We may not have enough time to cover this in the initial implementation though, but I'll make sure we have everything in place to do this in the follow-up, if we want to.