Skip to content

Commit

Permalink
Add support for dynamic application configurations (#5855)
Browse files Browse the repository at this point in the history
* Add application configuration service

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update API path name

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* implement two APIs/interfaces

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* expose get function for other plugins to use

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update interfaces

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* implement the APIs and interfaces

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add license and jsdoc

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update docs

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add more docs

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update variable name

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* remove unnecessary dependency

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* format readme

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* use osd version

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* remove debugging info

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update logging

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* remove lint js

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* remove logs

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update name style

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update function visibility and error function

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* fix unit test failures

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add unit test

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* remove lint file

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add more tests

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add unit tests for routes

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add remaining unit tests

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add enabled to this plugin

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update readme to mention experimental

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update change log

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* dummy commit to trigger workflow rerun

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* remove experimental

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add key to yml file

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* remove i18n

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* remove lint rc

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update comment style

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add input validation

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update unit tests

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* prevent multiple registration

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add return types

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* update readme wording

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add unit test to the plugin class about double register

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* move related ymls

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* move validation to a function

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* use trimmed versions

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* reword changelog entry

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* readability

Signed-off-by: Tianle Huang <tianleh@amazon.com>

* add back yml change

Signed-off-by: Tianle Huang <tianleh@amazon.com>

---------

Signed-off-by: Tianle Huang <tianleh@amazon.com>
  • Loading branch information
tianleh authored Mar 2, 2024
1 parent 0c394bd commit 2c8d9d3
Show file tree
Hide file tree
Showing 21 changed files with 1,464 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Multiple Datasource] Refactor client and legacy client to use authentication registry ([#5881](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5881))
- [Multiple Datasource] Improved error handling for the search API when a null value is passed for the dataSourceId ([#5882](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5882))
- [Multiple Datasource] Hide/Show authentication method in multi data source plugin based on configuration ([#5916](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5916))
- [[Dynamic Configurations] Add support for dynamic application configurations ([#5855](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5855))

### 🐛 Bug Fixes

Expand Down
7 changes: 7 additions & 0 deletions config/opensearch_dashboards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
# dashboards. OpenSearch Dashboards creates a new index if the index doesn't already exist.
#opensearchDashboards.index: ".opensearch_dashboards"

# OpenSearch Dashboards uses an index in OpenSearch to store dynamic configurations.
# This shall be a different index from opensearchDashboards.index.
# opensearchDashboards.configIndex: ".opensearch_dashboards_config"

# Set the value of this setting to true to enable plugin application config. By default it is disabled.
# application_config.enabled: false

# The default application to load.
#opensearchDashboards.defaultAppId: "home"

Expand Down
1 change: 1 addition & 0 deletions src/core/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export function pluginInitializerContextConfigMock<T>(config: T) {
const globalConfig: SharedGlobalConfig = {
opensearchDashboards: {
index: '.opensearch_dashboards_tests',
configIndex: '.opensearch_dashboards_config_tests',
autocompleteTerminateAfter: duration(100000),
autocompleteTimeout: duration(1000),
},
Expand Down
1 change: 1 addition & 0 deletions src/core/server/opensearch_dashboards_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const config = {
schema: schema.object({
enabled: schema.boolean({ defaultValue: true }),
index: schema.string({ defaultValue: '.kibana' }),
configIndex: schema.string({ defaultValue: '.opensearch_dashboards_config' }),
autocompleteTerminateAfter: schema.duration({ defaultValue: 100000 }),
autocompleteTimeout: schema.duration({ defaultValue: 1000 }),
branding: schema.object({
Expand Down
1 change: 1 addition & 0 deletions src/core/server/plugins/plugin_context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ describe('createPluginInitializerContext', () => {
expect(configObject).toStrictEqual({
opensearchDashboards: {
index: '.kibana',
configIndex: '.opensearch_dashboards_config',
autocompleteTerminateAfter: duration(100000),
autocompleteTimeout: duration(1000),
},
Expand Down
7 changes: 6 additions & 1 deletion src/core/server/plugins/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,12 @@ export interface Plugin<

export const SharedGlobalConfigKeys = {
// We can add more if really needed
opensearchDashboards: ['index', 'autocompleteTerminateAfter', 'autocompleteTimeout'] as const,
opensearchDashboards: [
'index',
'configIndex',
'autocompleteTerminateAfter',
'autocompleteTimeout',
] as const,
opensearch: ['shardTimeout', 'requestTimeout', 'pingTimeout'] as const,
path: ['data'] as const,
savedObjects: ['maxImportPayloadBytes'] as const,
Expand Down
1 change: 1 addition & 0 deletions src/legacy/server/config/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ export default () =>
opensearchDashboards: Joi.object({
enabled: Joi.boolean().default(true),
index: Joi.string().default('.kibana'),
configIndex: Joi.string().default('.opensearch_dashboards_config'),
autocompleteTerminateAfter: Joi.number().integer().min(1).default(100000),
// TODO Also allow units here like in opensearch config once this is moved to the new platform
autocompleteTimeout: Joi.number().integer().min(1).default(1000),
Expand Down
112 changes: 112 additions & 0 deletions src/plugins/application_config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# ApplicationConfig Plugin

An OpenSearch Dashboards plugin for application configuration and a default implementation based on OpenSearch as storage.

---

## Introduction

This plugin introduces the support of dynamic application configurations as opposed to the existing static configuration in OSD YAML file `opensearch_dashboards.yml`. It stores the configuration in an index whose default name is `.opensearch_dashboards_config` and could be customized through the key `opensearchDashboards.configIndex` in OSD YAML file. Initially the new index does not exist. Only OSD users who need dynamic configurations will create it.

It also provides an interface `ConfigurationClient` for future extensions of external configuration clients. A default implementation based on OpenSearch as database is used.

This plugin is disabled by default.

## Configuration

OSD users who want to set up application configurations will first need to enable this plugin by the following line in OSD YML.

```
application_config.enabled: true
```

Then they can perform configuration operations through CURL the OSD APIs.

(Note that the commands following could be first obtained from a copy as curl option from the network tab of a browser development tool and then replaced with the API names)

Below is the CURL command to view all configurations.

```
curl '{osd endpoint}/api/appconfig' -X GET
```

Below is the CURL command to view the configuration of an entity.

```
curl '{osd endpoint}/api/appconfig/{entity}' -X GET
```

Below is the CURL command to update the configuration of an entity.

```
curl '{osd endpoint}/api/appconfig/{entity}' -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' -H 'osd-xsrf: osd-fetch' -H 'Sec-Fetch-Dest: empty' --data-raw '{"newValue":"{new value}"}'
```

Below is the CURL command to delete the configuration of an entity.

```
curl '{osd endpoint}/api/appconfig/{entity}' -X DELETE -H 'osd-xsrf: osd-fetch' -H 'Sec-Fetch-Dest: empty'
```


## External Configuration Clients

While a default OpenSearch based client is implemented, OSD users can use external configuration clients through an OSD plugin (outside OSD).

Let's call this plugin `MyConfigurationClientPlugin`.

First, this plugin will need to implement a class `MyConfigurationClient` based on interface `ConfigurationClient` defined in the `types.ts` under directory `src/plugins/application_config/server/types.ts`. Below are the functions inside the interface.

```
getConfig(): Promise<Map<string, string>>;
getEntityConfig(entity: string): Promise<string>;
updateEntityConfig(entity: string, newValue: string): Promise<string>;
deleteEntityConfig(entity: string): Promise<string>;
```

Second, this plugin needs to declare `applicationConfig` as its dependency by adding it to `requiredPlugins` in its own `opensearch_dashboards.json`.

Third, the plugin will define a new type called `AppPluginSetupDependencies` as follows in its own `types.ts`.

```
export interface AppPluginSetupDependencies {
applicationConfig: ApplicationConfigPluginSetup;
}
```

Then the plugin will import the new type `AppPluginSetupDependencies` and add to its own setup input. Below is the skeleton of the class `MyConfigurationClientPlugin`.

```
// MyConfigurationClientPlugin
public setup(core: CoreSetup, { applicationConfig }: AppPluginSetupDependencies) {
...
// The function createClient provides an instance of ConfigurationClient which
// could have a underlying DynamoDB or Postgres implementation.
const myConfigurationClient: ConfigurationClient = this.createClient();
applicationConfig.registerConfigurationClient(myConfigurationClient);
...
return {};
}
```

## Onboarding Configurations

Since the APIs and interfaces can take an entity, a new use case to this plugin could just pass their entity into the parameters. There is no need to implement new APIs or interfaces. To programmatically call the functions in `ConfigurationClient` from a plugin (the caller plugin), below is the code example.

Similar to [section](#external-configuration-clients), a new type `AppPluginSetupDependencies` which encapsulates `ApplicationConfigPluginSetup` is needed. Then it can be imported into the `setup` function of the caller plugin. Then the caller plugin will have access to the `getConfigurationClient` and `registerConfigurationClient` exposed by `ApplicationConfigPluginSetup`.

## Development

See the [OpenSearch Dashboards contributing
guide](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/CONTRIBUTING.md) for instructions
setting up your development environment.
7 changes: 7 additions & 0 deletions src/plugins/application_config/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export const PLUGIN_ID = 'applicationConfig';
export const PLUGIN_NAME = 'application_config';
12 changes: 12 additions & 0 deletions src/plugins/application_config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { schema, TypeOf } from '@osd/config-schema';

export const configSchema = schema.object({
enabled: schema.boolean({ defaultValue: false }),
});

export type ApplicationConfigSchema = TypeOf<typeof configSchema>;
9 changes: 9 additions & 0 deletions src/plugins/application_config/opensearch_dashboards.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "applicationConfig",
"version": "opensearchDashboards",
"opensearchDashboardsVersion": "opensearchDashboards",
"server": true,
"ui": false,
"requiredPlugins": [],
"optionalPlugins": []
}
23 changes: 23 additions & 0 deletions src/plugins/application_config/server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { PluginConfigDescriptor, PluginInitializerContext } from '../../../core/server';
import { ApplicationConfigSchema, configSchema } from '../config';
import { ApplicationConfigPlugin } from './plugin';

/*
This exports static code and TypeScript types,
as well as, OpenSearch Dashboards Platform `plugin()` initializer.
*/

export const config: PluginConfigDescriptor<ApplicationConfigSchema> = {
schema: configSchema,
};

export function plugin(initializerContext: PluginInitializerContext) {
return new ApplicationConfigPlugin(initializerContext);
}

export { ApplicationConfigPluginSetup, ApplicationConfigPluginStart } from './types';
Loading

0 comments on commit 2c8d9d3

Please sign in to comment.