Skip to content

Commit 70a927c

Browse files
Merge pull request #13188 from vojtechszocs/adopt-openshift-plugin-sdk
CONSOLE-3705: Phase 1 of using OpenShift Dynamic Plugin SDK
2 parents b5d67e3 + dd6de65 commit 70a927c

34 files changed

Lines changed: 880 additions & 694 deletions

dynamic-demo-plugin/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
"ts-loader": "9.x",
3939
"ts-node": "5.0.1",
4040
"typescript": "4.x",
41-
"webpack": "^5.73.0",
42-
"webpack-cli": "4.9.x"
41+
"webpack": "5.75.0",
42+
"webpack-cli": "5.0.x"
4343
},
4444
"consolePlugin": {
4545
"name": "console-demo-plugin",

dynamic-demo-plugin/yarn.lock

Lines changed: 123 additions & 122 deletions
Large diffs are not rendered by default.

frontend/packages/console-app/src/components/console-operator/ConsoleOperatorConfig.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ const ConsolePluginsList: React.FC<ConsolePluginsListType> = ({ obj }) => {
107107
return {
108108
name: plugin.metadata.name,
109109
version: plugin.metadata.version,
110-
description: plugin.metadata?.description || placeholder,
110+
description: plugin.metadata?.customProperties?.console?.description || placeholder,
111111
enabled: !!obj?.spec?.plugins?.includes(plugin.metadata.name),
112112
status: plugin.status,
113113
};
@@ -156,7 +156,7 @@ const ConsolePluginsList: React.FC<ConsolePluginsListType> = ({ obj }) => {
156156
return {
157157
name: plugin?.metadata?.name,
158158
version: loadedPluginInfo?.metadata?.version,
159-
description: loadedPluginInfo?.metadata?.description,
159+
description: loadedPluginInfo?.metadata?.customProperties?.console?.description,
160160
enabled,
161161
status: loadedPluginInfo?.status,
162162
};

frontend/packages/console-dynamic-plugin-sdk/README.md

Lines changed: 97 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,6 @@ released, installed and upgraded independently from each other. To ensure compat
99
other plugins, each plugin must declare its dependencies using [semantic version](https://semver.org/)
1010
ranges.
1111

12-
## Related Documentation
13-
14-
_[Extension Documentation](./docs/console-extensions.md)_ - Detailed documentation of every available console extension point.
15-
16-
_[API Documentation](./docs/api.md)_ - Detailed documentation of hooks, components, and other APIs provided by this package.
17-
18-
_[OpenShift Console Dynamic Plugins feature page](https://github.com/openshift/enhancements/blob/master/enhancements/console/dynamic-plugins.md)_ - A high level overview of dynamic plugins in relation to OLM operators and cluster administration.
19-
2012
Example project structure:
2113

2214
```
@@ -28,18 +20,30 @@ dynamic-demo-plugin/
2820
└── webpack.config.ts
2921
```
3022

31-
## SDK packages
23+
## Related Documentation
24+
25+
_[Extension Documentation][console-doc-extensions]_ - Detailed documentation of all available Console
26+
extension points.
3227

33-
| Package Name | Description |
34-
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
35-
| `@openshift-console/dynamic-plugin-sdk` | Provides core APIs, types and utilities used by dynamic plugins at runtime. |
36-
| `@openshift-console/dynamic-plugin-sdk-webpack` | Provides webpack plugin `ConsoleRemotePlugin` used to build all dynamic plugin assets. |
37-
| `@openshift-console/dynamic-plugin-sdk-internal` | Internal package exposing additional code. |
28+
_[API Documentation][console-doc-api]_ - Detailed documentation of React components, hooks and other APIs
29+
provided by Console to its dynamic plugins.
30+
31+
_[OpenShift Console Dynamic Plugins feature page][console-doc-feature-page]_ - A high-level overview of
32+
dynamic plugins in relation to OLM operators and cluster administration.
33+
34+
## Distributable SDK package overview
35+
36+
| Package Name | Description |
37+
| ------------ | ----------- |
38+
| `@openshift-console/dynamic-plugin-sdk` | Provides core APIs, types and utilities used by dynamic plugins at runtime. |
39+
| `@openshift-console/dynamic-plugin-sdk-webpack` | Provides webpack `ConsoleRemotePlugin` used to build all dynamic plugin assets. |
40+
| `@openshift-console/dynamic-plugin-sdk-internal` | Internal package exposing additional code. |
3841
| `@openshift-console/plugin-shared` | Provides reusable components and utility functions to build OCP dynamic plugins. Compatible with multiple versions of OpenShift Console. |
3942

4043
## OpenShift Console Versions vs SDK Versions
4144

42-
Not all NPM packages are fully compatible with all versions of the Console. This table will help align compatible versions of the SDK Packages to versions of the OpenShift Console.
45+
Not all NPM packages are fully compatible with all versions of the Console. This table will help align
46+
compatible versions of distributable SDK packages to versions of the OpenShift Console.
4347

4448
| Console Version | SDK Package | Last Package Version |
4549
| ----------------- | ----------------------------------------------- | -------------------- |
@@ -51,20 +55,20 @@ Not all NPM packages are fully compatible with all versions of the Console. This
5155
| | `@openshift-console/dynamic-plugin-sdk-webpack` | 0.0.6 |
5256
| 4.9.x **[Dev]** | `@openshift-console/dynamic-plugin-sdk` | 0.0.0-alpha18 |
5357

54-
Notes
58+
Notes:
5559

5660
- **[Tech]** - Release 4.10 was Tech Preview for the SDK packages
5761
- **[Dev]** - Release 4.9 was Dev Preview for the SDK packages
5862

59-
## `package.json`
63+
## Plugin metadata
6064

61-
Plugin metadata is declared via the `consolePlugin` object.
65+
Older versions of webpack `ConsoleRemotePlugin` assumed that the plugin metadata is specified via
66+
`consolePlugin` object within the `package.json` file, for example:
6267

6368
```jsonc
6469
{
6570
"name": "dynamic-demo-plugin",
6671
"version": "0.0.0",
67-
"private": true,
6872
// scripts, dependencies, devDependencies, ...
6973
"consolePlugin": {
7074
"name": "console-demo-plugin",
@@ -81,32 +85,40 @@ Plugin metadata is declared via the `consolePlugin` object.
8185
}
8286
```
8387

84-
`consolePlugin.name` is the plugin's unique identifier. It should be the same as `metadata.name`
85-
of the corresponding `ConsolePlugin` resource used to represent the plugin on the cluster.
86-
Therefore, it must be a valid
88+
Newer versions of webpack `ConsoleRemotePlugin` allow passing the plugin metadata directly as an
89+
object, for example:
90+
91+
```ts
92+
new ConsoleRemotePlugin({
93+
pluginMetadata: { /* same metadata like above */ },
94+
})
95+
```
96+
97+
`name` serves as the plugin's unique identifier. Its value should be the same as `metadata.name`
98+
of the corresponding `ConsolePlugin` resource on the cluster. Therefore, it must be a valid
8799
[DNS subdomain name](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names).
88100

89-
`consolePlugin.version` must be [semver](https://semver.org/) compliant.
101+
`version` must be [semver](https://semver.org/) compliant version string.
90102

91-
Dynamic plugins can expose modules representing additional code to be referenced, loaded and executed
103+
Dynamic plugins can expose modules representing plugin code that can be referenced, loaded and executed
92104
at runtime. A separate [webpack chunk](https://webpack.js.org/guides/code-splitting/) is generated for
93-
each entry in `consolePlugin.exposedModules` object. Exposed modules are resolved relative to plugin's
94-
webpack `context` option.
105+
each entry in the `exposedModules` object. Exposed modules are resolved relative to the plugin's webpack
106+
`context` option.
95107

96108
The `@console/pluginAPI` dependency is optional and refers to Console versions this dynamic plugin is
97-
compatible with. The `consolePlugin.dependencies` object may also refer to other dynamic plugins that
98-
are required for this dynamic plugin to work correctly. For dependencies whose versions may include
99-
a [semver pre-release](https://semver.org/#spec-item-9) identifier, adapt your semver range constraint
100-
to include the relevant pre-release prefix, e.g. use `~4.11.0-0.ci` when targeting pre-release versions
101-
like `4.11.0-0.ci-1234`.
102-
103-
See `ConsolePluginMetadata` type for details on the `consolePlugin` object and its schema.
109+
compatible with. The `dependencies` object may also refer to other dynamic plugins that are required for
110+
this plugin to work correctly. For dependencies where the version string may include a
111+
[semver pre-release](https://semver.org/#spec-item-9) identifier, adapt your semver range constraint
112+
(dependency value) to include the relevant pre-release prefix, e.g. use `~4.11.0-0.ci` when targeting
113+
pre-release versions like `4.11.0-0.ci-1234`.
104114

105-
## `console-extensions.json`
115+
## Extensions contributed by the plugin
106116

107-
Declares all extensions contributed by the plugin.
117+
Older versions of webpack `ConsoleRemotePlugin` assumed that the list of extensions contributed by the
118+
plugin is specified via the `console-extensions.json` file, for example:
108119

109120
```jsonc
121+
// This file is parsed as JSONC (JSON with Comments)
110122
[
111123
{
112124
"type": "console.flag",
@@ -128,67 +140,83 @@ Declares all extensions contributed by the plugin.
128140
]
129141
```
130142

131-
Depending on extension `type`, the `properties` object may contain code references, encoded as object
132-
literals `{ $codeRef: string }`. When loading dynamic plugins, encoded code references are transformed
133-
into functions `() => Promise<T>` used to load the referenced objects.
143+
Newer versions of webpack `ConsoleRemotePlugin` allow passing the extension list directly as an array
144+
of objects, for example:
145+
146+
```ts
147+
new ConsoleRemotePlugin({
148+
extensions: [ /* same extensions like above */ ],
149+
})
150+
```
151+
152+
Each extension a single instance of extending the Console application's functionality. Extensions are
153+
declarative and expressed as plain static objects.
154+
155+
Extension `type` determines the kind of extension to perform, while any data and/or code necessary to
156+
interpret such extensions are declared through their `properties`.
134157

135-
The `$codeRef` value should be formatted as either `moduleName.exportName` (referring to a named export)
136-
or `moduleName` (referring to the `default` export). Only the plugin's exposed modules (i.e. the keys of
137-
`consolePlugin.exposedModules` object) may be used in code references.
158+
Extensions may contain code references pointing to specific modules exposed by the plugin. For example:
159+
160+
- `{ $codeRef: 'barUtils' }` - refers to `default` export of `barUtils` module
161+
- `{ $codeRef: 'barUtils.testHandler' }` - refers to `testHandler` export of `barUtils` module
162+
163+
164+
When loading dynamic plugins, all encoded code references `{ $codeRef: string }` are transformed into
165+
functions `() => Promise<T>` used to load the referenced objects on demand. Only the plugin's exposed
166+
modules (i.e. the keys of `exposedModules` object) may be used in code references.
138167

139168
## Webpack config
140169

141170
Dynamic plugins _must_ be built with [webpack](https://webpack.js.org/) in order for their modules to
142171
seamlessly integrate with Console application at runtime. Use webpack version 5+ which includes native
143172
support for module federation.
144173

145-
All dynamic plugin assets are managed via webpack plugin `ConsoleRemotePlugin`.
174+
All dynamic plugin assets are generated via webpack `ConsoleRemotePlugin`.
146175

147176
```ts
148-
const { ConsoleRemotePlugin } = require('@openshift-console/dynamic-plugin-sdk-webpack');
177+
import { ConsoleRemotePlugin } from '@openshift-console/dynamic-plugin-sdk-webpack';
178+
import { Configuration } from 'webpack';
149179

150-
const config = {
151-
// 'entry' is optional, but unrelated to plugin assets
180+
const config: Configuration = {
181+
entry: {}, // Plugin container entry is generated by DynamicRemotePlugin
152182
plugins: [new ConsoleRemotePlugin()],
153183
// ... rest of webpack configuration
154184
};
155185

156186
export default config;
157187
```
158188

159-
`ConsoleRemotePlugin` automatically detects your plugin's metadata and extension declarations and
160-
generates the corresponding assets.
161-
162-
`ConsoleRemotePlugin` constructor supports an options object used to tweak its behavior. Refer to
163-
`ConsoleRemotePluginOptions` type for details on supported options.
189+
Refer to `ConsoleRemotePluginOptions` type for details on supported Console plugin build options.
164190

165191
## Generated assets
166192

167193
Building the above example plugin produces the following assets:
168194

169195
```
170196
dynamic-demo-plugin/dist/
197+
├── exposed-barUtils-chunk.js
171198
├── plugin-entry.js
172-
├── plugin-manifest.json
173-
└── utils_bar_ts-chunk.js
199+
└── plugin-manifest.json
174200
```
175201

176-
`plugin-manifest.json`: dynamic plugin manifest. Contains both metadata and extension declarations to
177-
be parsed and interpreted by Console at runtime. This is the first plugin asset loaded by Console.
202+
`plugin-manifest.json` is the dynamic plugin manifest. It contains both plugin metadata and extension
203+
declarations to be loaded and interpreted by Console at runtime. This is the first plugin asset loaded
204+
by Console.
178205

179-
`plugin-entry.js`: [webpack container entry chunk](https://webpack.js.org/concepts/module-federation/#low-level-concepts).
180-
Provides asynchronous access to specific modules exposed by the plugin. Loaded right after the plugin
181-
manifest.
206+
`plugin-entry.js` is the
207+
[webpack container entry chunk](https://webpack.js.org/concepts/module-federation/#low-level-concepts).
208+
It provides access to specific modules exposed by the plugin. It's loaded right after the plugin manifest.
182209

183-
`utils_bar_ts-chunk.js`: webpack chunk for the exposed `barUtils` module. Loaded via the plugin entry
184-
chunk when needed.
210+
`exposed-barUtils-chunk.js` is the generated webpack chunk for `barUtils` exposed module. It's loaded
211+
via the plugin entry chunk (`plugin-entry.js`) when needed.
185212

186213
## Plugin development
187214

188215
Run Bridge locally and instruct it to proxy e.g. `/api/plugins/console-demo-plugin` requests directly
189216
to your local plugin asset server (web server hosting the plugin's generated assets):
190217

191218
```sh
219+
# Note that the plugin's base URL should have a trailing slash
192220
./bin/bridge -plugins console-demo-plugin=http://localhost:9001/
193221
```
194222

@@ -220,8 +248,6 @@ list of plugin names (disable specific plugins) or an empty string (disable all
220248
to ensure a single version of React etc. is loaded and used by the application.
221249
- Enabling a plugin makes all of its extensions available for consumption. Individual extensions cannot
222250
be enabled or disabled separately.
223-
- Failure to resolve a code reference (unable to load module, missing module export etc.) will disable
224-
the plugin.
225251

226252
## Publishing SDK packages
227253

@@ -253,6 +279,14 @@ If the given package doesn't exist in npm registry, add `--access public` to `ya
253279

254280
## Future Deprecations in Shared Plugin Dependencies
255281

256-
Certain packages are currently in the shared plugin dependencies that will be removed in the future. Plugin authors will need to manually add these items to their configurations or chose other options:
282+
Console provides certain packages as shared modules to all of its dynamic plugins. Some of these shared
283+
modules may be removed in the future. Plugin authors will need to manually add these items to their webpack
284+
configs or choose other options.
285+
286+
The list of shared modules planned for deprecation:
287+
288+
- `react-helmet`
257289

258-
_- react-helmet_
290+
[console-doc-extensions]: ./docs/console-extensions.md
291+
[console-doc-api]: ./docs/api.md
292+
[console-doc-feature-page]: https://github.com/openshift/enhancements/blob/master/enhancements/console/dynamic-plugins.md

frontend/packages/console-dynamic-plugin-sdk/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
},
1818
"devDependencies": {
1919
"@microsoft/tsdoc": "0.14.2",
20+
"@openshift/dynamic-plugin-sdk-webpack": "^4.0.0",
2021
"@types/ejs": "3.x",
2122
"@types/fs-extra": "9.x",
2223
"ejs": "3.x",
2324
"fs-extra": "9.x",
2425
"ts-json-schema-generator": "0.98.0",
2526
"tsutils": "3.21.0",
26-
"webpack": "^5.73.0"
27+
"webpack": "5.75.0"
2728
}
2829
}

frontend/packages/console-dynamic-plugin-sdk/scripts/generate-schema.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ type SchemaTypeConfig = {
1717
};
1818

1919
const typeConfigs: SchemaTypeConfig[] = [
20-
{
21-
srcFile: 'src/schema/plugin-package.ts',
22-
typeName: 'ConsolePluginMetadata',
23-
},
2420
{
2521
srcFile: 'src/schema/console-extensions.ts',
2622
typeName: 'ConsoleExtensionsJSON',
@@ -29,7 +25,6 @@ const typeConfigs: SchemaTypeConfig[] = [
2925
{
3026
srcFile: 'src/schema/plugin-manifest.ts',
3127
typeName: 'ConsolePluginManifestJSON',
32-
handleConsoleExtensions: true,
3328
},
3429
];
3530

frontend/packages/console-dynamic-plugin-sdk/scripts/package-definitions.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,11 @@ export const getWebpackPackage: GetPackageDefinition = (
151151
main: 'lib/lib-webpack.js',
152152
...commonManifestFields,
153153
dependencies: {
154-
...parseDeps(sdkPackage, ['webpack'], missingDepCallback),
154+
...parseDeps(
155+
sdkPackage,
156+
['@openshift/dynamic-plugin-sdk-webpack', 'webpack'],
157+
missingDepCallback,
158+
),
155159
...parseDeps(
156160
rootPackage,
157161
['ajv', 'chalk', 'comment-json', 'find-up', 'read-pkg', 'semver'],
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { PluginBuildMetadata, PluginManifest } from '@openshift/dynamic-plugin-sdk-webpack';
2+
3+
/**
4+
* Additional plugin metadata supported by the Console application.
5+
*/
6+
export type ConsoleSupportedCustomProperties = Partial<{
7+
/** User-friendly plugin name. */
8+
displayName: string;
9+
10+
/** User-friendly plugin description. */
11+
description: string;
12+
13+
/** Disable the given static plugins when this plugin gets loaded. */
14+
disableStaticPlugins: string[];
15+
}>;
16+
17+
/**
18+
* Build-time Console dynamic plugin metadata.
19+
*/
20+
export type ConsolePluginBuildMetadata = PluginBuildMetadata & ConsoleSupportedCustomProperties;
21+
22+
/**
23+
* Standard Console dynamic plugin manifest format.
24+
*/
25+
export type StandardConsolePluginManifest = {
26+
customProperties?: {
27+
console?: ConsoleSupportedCustomProperties;
28+
[customNamespace: string]: unknown;
29+
};
30+
} & PluginManifest;
31+
32+
/**
33+
* Legacy Console dynamic plugin manifest format.
34+
*/
35+
export type LegacyConsolePluginManifest = Pick<
36+
PluginManifest,
37+
'name' | 'version' | 'dependencies' | 'extensions'
38+
> &
39+
ConsoleSupportedCustomProperties;
40+
41+
/**
42+
* This type supports both standard and legacy Console dynamic plugin manifest formats.
43+
*
44+
* Console application automatically adapts the manifest to standard format when loading
45+
* the given plugin.
46+
*/
47+
export type AnyConsolePluginManifest = StandardConsolePluginManifest | LegacyConsolePluginManifest;
48+
49+
export const isStandardPluginManifest = (
50+
m: AnyConsolePluginManifest,
51+
): m is StandardConsolePluginManifest =>
52+
// Standard plugin manifests must have a string valued baseURL property
53+
// eslint-disable-next-line dot-notation
54+
m['baseURL'] && typeof m['baseURL'] === 'string';

0 commit comments

Comments
 (0)