Skip to content

Commit 3cda7fe

Browse files
committed
RHIDP-4807: Third-party plugin installation in RHDH
1 parent 2e353e8 commit 3cda7fe

File tree

7 files changed

+464
-13
lines changed

7 files changed

+464
-13
lines changed

assemblies/dynamic-plugins/assembly-installing-rhdh-plugins.adoc

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,17 @@ The dynamic plugin support is based on the backend plugin manager package, which
66
You can use the dynamic plugins that come preinstalled with {product} or install external dynamic plugins from a public NPM registry.
77

88
// Operator installation
9-
include::../modules/dynamic-plugins/proc-config-dynamic-plugins-rhdh-operator.adoc[leveloffset=+1]
9+
include::../modules/dynamic-plugins/proc-config-dynamic-plugins-rhdh-operator.adoc[leveloffset=+2]
1010

1111
// Helm installation
12-
include::../modules/dynamic-plugins/con-install-dynamic-plugin-helm.adoc[leveloffset=+1]
13-
include::../modules/dynamic-plugins/proc-obtaining-integrity-checksum.adoc[leveloffset=+2]
14-
include::../modules/dynamic-plugins/ref-example-dynamic-plugin-helm-installations.adoc[leveloffset=+2]
15-
include::../modules/dynamic-plugins/proc-rhdh-example-external-dynamic-plugins.adoc[leveloffset=+2]
12+
include::../modules/dynamic-plugins/con-install-dynamic-plugin-helm.adoc[leveloffset=+2]
13+
include::../modules/dynamic-plugins/proc-obtaining-integrity-checksum.adoc[leveloffset=+3]
14+
include::../modules/dynamic-plugins/ref-example-dynamic-plugin-helm-installations.adoc[leveloffset=+3]
15+
include::../modules/dynamic-plugins/proc-rhdh-example-external-dynamic-plugins.adoc[leveloffset=+3]
1616

1717
// Air gapped environment
1818
//include::../modules/dynamic-plugins/proc-rhdh-installing-external-dynamic-plugins-airgapped.adoc[leveloffset=+1]
19-
include::../modules/dynamic-plugins/proc-using-custom-npm-registry.adoc[leveloffset=+1]
20-
21-
// Viewing installed plugins
22-
include::../modules/dynamic-plugins/proc-viewing-installed-plugins.adoc[leveloffset=+1]
19+
include::../modules/dynamic-plugins/proc-install-plugins-using-custom-npm-registry.adoc[leveloffset=+2]
2320

2421
//basic plugin configuration
2522
//include::../modules/dynamic-plugins/con-basic-config-dynamic-plugins.adoc[leveloffset=+1]
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
[id="assembly-third-party-plugins-installation"]
2+
= Third-party plugins in {product}
3+
:context: assembly-third-party-plugins-installation
4+
5+
In {product}, third-party dynamic plugins can be integrated seamlessly, enabling the use of external plugins alongside native ones. This flexibility allows you to enhance the platform’s functionality without modifying its core structure. To add third-party dynamic plugins, you can either export them as standalone packages or wrap them in custom wrappers, especially if you want to adjust the plugin’s dependencies or configurations for compatibility.
6+
7+
The third-party plugins are typically packaged as dynamic plugins, which means they can be loaded at runtime, enabling you to easily manage external modules without rebuilding the entire application. The process involves creating a wrapper that exports the plugin code, ensuring that dependencies are correctly bundled or marked as shared, depending on their relationship to the {product-short} environment.
8+
9+
To integrate a third-party plugin into {product-short}:
10+
11+
. First, obtain the plugin's source code.
12+
. Export the plugin as a dynamic plugin package. See xref:proc-export-third-party-plugins-rhdh_{context}[].
13+
. Package and publish the dynamic plugin. See xref:proc-package-third-party-dynamic-plugin_{context}[].
14+
. Install the plugin in the {product-short} environment. See xref:proc-install-third-party-plugins-rhdh_{context}[].
15+
16+
The third-party plugins include:
17+
18+
Backend plugins::
19+
+
20+
--
21+
To ensure compatibility with the dynamic plugin support and enable their use as dynamic plugins, existing backend plugins must be compatible with, the new backend system. Additionally, these plugins must be rebuilt using a dedicated CLI command.
22+
23+
The new backend system is created using `createBackendPlugin()` or `createBackendModule()`. You must export the new backend system as the default export from either the main package or an alpha package (if support exists in alpha). The dynamic export mechanism automatically identifies private dependencies, and adds them to the `bundleDependencies` field in the `package.json` file. This mechanism ensures that the dynamic plugin package is self-contained, with private dependencies bundled within a private `node_modules` folder.
24+
25+
The backend plugins contains two types of dependencies, including:
26+
27+
* *Shared dependencies* are provided by the Backstage application and listed as `peerDependencies` in `package.json` file, not bundled in the dynamic plugin package. By default, all `@backstage` packages are shared.
28+
+
29+
You can use the `--shared-package` flag to specify shared dependencies. To treat a `@backstage` package as private, use the negation prefix (`!`).
30+
31+
* *Embedded dependencies* are bundled into the dynamic plugin package with their dependencies hoisted to the top level. By default, packages with `-node` or `-common` suffixes are embedded.
32+
+
33+
You can use the `--embed-package` flag to specify additional embedded packages, including those in the same monorepo that do not follow the default naming convention.
34+
35+
The following is an example of exporting a dynamic plugin with shared and embedded packages:
36+
37+
.Example dynamic plugin export with shared and embedded packages
38+
[source,bash]
39+
----
40+
npx @janus-idp/cli@latest export-dynamic-plugin --shared-package '!/@backstage/plugin-notifications/'<1> --embed-package @backstage/plugin-notifications-backend <2>
41+
----
42+
43+
<1> `@backstage/plugin-notifications` package is treated as a private dependency and is bundled in the dynamic plugin package, despite being in the `@backstage` scope.
44+
<2> `@backstage/plugin-notifications-backend` package is marked as an embedded dependency and is bundled in the dynamic plugin package.
45+
--
46+
47+
Frontend plugins::
48+
+
49+
--
50+
Frontend plugins can leverage Scalprum for configuration, which the CLI can generate automatically during the export process. The generated default configuration is logged when running the following command:
51+
52+
.Example command to log the default configuration
53+
[source,bash]
54+
----
55+
npx @janus-idp/cli@latest export-dynamic
56+
----
57+
58+
The following is an example of default Scalprum configuration:
59+
60+
.Default Scalprum configuration
61+
[source,json]
62+
----
63+
"scalprum": {
64+
"name": "<package_name>", // The Webpack container name matches the NPM package name, with "@" replaced by "." and "/" removed.
65+
"exposedModules": {
66+
"PluginRoot": "./src/index.ts" // The default module name is "PluginRoot" and doesn't need explicit specification in the app-config.yaml file.
67+
}
68+
}
69+
----
70+
71+
To customize Scalprum, add a `scalprum` section to the `package.json` file. For example:
72+
73+
.Example Scalprum customization
74+
[source,json]
75+
----
76+
"scalprum": {
77+
"name": "custom-package-name",
78+
"exposedModules": {
79+
"FooModuleName": "./src/foo.ts",
80+
"BarModuleName": "./src/bar.ts"
81+
// Define multiple modules here, with each exposed as a separate entry point in the Webpack container.
82+
}
83+
}
84+
----
85+
86+
Dynamic plugins might need adjustments for {product-short} needs, such as static JSX for mountpoints or dynamic routes. These changes are optional but might be incompatible with static plugins.
87+
88+
To include static JSX, define an additional export and use it as the dynamic plugin's `importName`. For example:
89+
90+
.Example static and dynamic plugin export
91+
[source,tsx]
92+
----
93+
// For a static plugin
94+
export const EntityTechdocsContent = () => {...}
95+
96+
// For a dynamic plugin
97+
export const DynamicEntityTechdocsContent = {
98+
element: EntityTechdocsContent,
99+
staticJSXContent: (
100+
<TechDocsAddons>
101+
<ReportIssue />
102+
</TechDocsAddons>
103+
),
104+
};
105+
----
106+
--
107+
//Export third-party plugins
108+
include::../modules/dynamic-plugins/proc-export-third-party-plugins-rhdh.adoc[leveloffset=+2]
109+
110+
//package and publish third-party plugins
111+
include::../modules/dynamic-plugins/proc-package-third-party-dynamic-plugin.adoc[leveloffset=+2]
112+
113+
//install third-party plugins
114+
include::../modules/dynamic-plugins/proc-install-third-party-plugins-rhdh.adoc[leveloffset=+2]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[id="proc-export-third-party-plugins-rhdh_{context}"]
2+
= Exporting third-party plugins in {product}
3+
4+
To use dynamic plugins in {product}, the plugins must be exported as separate packages. These packages contain the plugin code and dependencies, ready for dynamic plugin integration into {product-short}.
5+
6+
.Prerequisites
7+
* The `@janus-idp/cli` package is installed. Use the latest version (`@latest` tag) for compatibility with the most recent features and fixes.
8+
* The third-party plugin must have a valid package.json file in its root directory, containing all required metadata and dependencies.
9+
* Node.js and NPM is installed and configured.
10+
* You have access to a private NPM registry for publishing and distributing the exported plugin package.
11+
* The third-party plugin is compatible with your {product} version. For more information, see link:https://github.com/janus-idp/backstage-showcase/blob/main/docs/dynamic-plugins/versions.md[Version compatibility matrix].
12+
13+
.Procedure
14+
. Use the `package export-dynamic-plugin` command from the `@janus-idp/cli` package to export the plugin:
15+
+
16+
--
17+
.Example command to export a third-party plugin
18+
[source,bash]
19+
----
20+
npx @janus-idp/cli@latest package export-dynamic-plugin
21+
----
22+
23+
Ensure that you execute the previous command in the root directory of the plugin's JavaScript package (containing `package.json` file).
24+
--
25+
26+
. Located the exported package in the `dist-dynamic` sub-folder.
27+
. Use `npm pack` or a private NPM registry to package or distribute the plugin. For more information, see xref:proc-package-third-party-dynamic-plugin_{context}[].
28+
+
29+
--
30+
[IMPORTANT]
31+
====
32+
Ensure that you do not push derived dynamic plugin packages to a public NPM registry. Use a private NPM registry only.
33+
====
34+
--
35+

modules/dynamic-plugins/proc-using-custom-npm-registry.adoc renamed to modules/dynamic-plugins/proc-install-plugins-using-custom-npm-registry.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[id="proc-using-custom-npm-registry"]
1+
[id="proc-install-plugins-using-custom-npm-registry"]
22

33
//= Using a custom NPM registry for dynamic plugin packages
44
= Installing dynamic plugins in an air-gapped environment
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
[id="proc-install-third-party-plugins-rhdh_{context}"]
2+
= Installing third-party plugins in {product}
3+
4+
You can install a third-party plugins in {product} without rebuilding the {product-very-short} application.
5+
6+
.Prerequisites
7+
* The third-party plugin is packaged as a dynamic plugin in one of the following formats:
8+
** OCI image
9+
** TGZ file
10+
** NPM or JavaScript package
11+
12+
For more information about packaging a third-party plugin, see xref:proc-package-third-party-dynamic-plugin_{context}[].
13+
14+
[NOTE]
15+
====
16+
You can also load dynamic plugins from a plain directory, though this is intended for development or testing purposes and is not recommended for production, except for plugins included in the {product-very-short} container image.
17+
====
18+
19+
.Procedure
20+
Load a plugin packaged as an OCI image::
21+
+
22+
--
23+
. Define the plugin with the `oci://` prefix in the following format in `dynamic-plugins.default.yaml` file:
24+
+
25+
`oci://<image-name>:<tag>!<plugin-name>`
26+
+
27+
.Example configuration in `dynamic-plugins.default.yaml` file
28+
[source,yaml]
29+
----
30+
plugins:
31+
- disabled: false
32+
package: oci://quay.io/example/image:v0.0.1!backstage-plugin-myplugin
33+
----
34+
35+
. Configure authentication for private registries by setting the `REGISTRY_AUTH_FILE` environment variable to the path of the registry configuration file. For example, `~/.config/containers/auth.json` or `~/.docker/config.json`.
36+
37+
. To perform an integrity check, use the image digest in place of the tag in `dynamic-plugins.default.yaml` file as follows:
38+
+
39+
.Example configuration in `dynamic-plugins.default.yaml` file
40+
[source,yaml]
41+
----
42+
plugins:
43+
- disabled: false
44+
package: oci://quay.io/example/image@sha256:<digest>!<plugin-name>
45+
----
46+
47+
. To apply the changes, restart the {product-very-short} application.
48+
--
49+
50+
Load a plugin packaged as a TGZ file::
51+
+
52+
--
53+
. Specify the archive URL and its integrity hash in the `dynamic-plugins.default.yaml` file using the following format:
54+
+
55+
.Configuration format in `dynamic-plugins.default.yaml` file
56+
[source,yaml]
57+
----
58+
plugins:
59+
- disabled: false
60+
package: https://example.com/backstage-plugin-myplugin-1.0.0.tgz
61+
integrity: <hash>
62+
----
63+
+
64+
.Example configuration in `dynamic-plugins.default.yaml` file
65+
[source,yaml]
66+
----
67+
plugins:
68+
- disabled: false
69+
package: https://example.com/backstage-plugin-myplugin-1.0.0.tgz
70+
integrity: sha512-9WlbgEdadJNeQxdn1973r5E4kNFvnT9GjLD627GWgrhCaxjCmxqdNW08cj+Bf47mwAtZMt1Ttyo+ZhDRDj9PoA==
71+
----
72+
73+
. To apply the changes, restart the {product-very-short} application.
74+
--
75+
76+
Load a plugin packaged as a JavaScript or NPM package::
77+
+
78+
--
79+
. Run the following command to obtain the integrity hash from the NPM registry:
80+
+
81+
[source,bash]
82+
----
83+
npm view --registry <registry-url> @backstage-community/plugin-todo-dynamic@<version> dist.integrity
84+
----
85+
86+
. Specify the package name, version, and its integrity hash in the `dynamic-plugins.default.yaml` file as follows:
87+
+
88+
.Configuration format in `dynamic-plugins.default.yaml` file
89+
[source,yaml]
90+
----
91+
plugins:
92+
- disabled: false
93+
package: @example/[email protected]
94+
integrity: <hash>
95+
----
96+
+
97+
.Example configuration in `dynamic-plugins.default.yaml` file
98+
[source,yaml]
99+
----
100+
plugins:
101+
- disabled: false
102+
package: @example/[email protected]
103+
integrity: sha512-9WlbgEdadJNeQxdn1973r5E4kNFvnT9GjLD627GWgrhCaxjCmxqdNW08cj+Bf47mwAtZMt1Ttyo+ZhDRDj9PoA==
104+
----
105+
106+
. If you are using a custom NPM registry, create a `.npmrc` file with the registry URL and authentication details:
107+
+
108+
.Example code for `.npmrc` file
109+
[source,text]
110+
----
111+
registry=<registry-url>
112+
//<registry-url>:_authToken=<auth-token>
113+
----
114+
115+
. When using {ocp-short} or Kubernetes:
116+
+
117+
* Create a secret with the `.npmrc` content as follows:
118+
+
119+
.Example secret configuration
120+
[source,yaml]
121+
----
122+
apiVersion: v1
123+
kind: Secret
124+
metadata:
125+
name: dynamic-plugins-npmrc
126+
type: Opaque
127+
stringData:
128+
.npmrc: |
129+
registry=<registry-url>
130+
//<registry-url>:_authToken=<auth-token>
131+
----
132+
133+
* For {product-very-short} Helm chart, name the secret using the following format for automatic mounting:
134+
+
135+
`<release-name>-dynamic-plugins-npmrc`
136+
137+
. To apply the changes, restart the {product-very-short} application.
138+
--

0 commit comments

Comments
 (0)