diff --git a/cypress/e2e/tests/pages/extensions.spec.ts b/cypress/e2e/tests/pages/extensions.spec.ts index 2b5f7d382a..5b4a4ff8e4 100644 --- a/cypress/e2e/tests/pages/extensions.spec.ts +++ b/cypress/e2e/tests/pages/extensions.spec.ts @@ -183,6 +183,20 @@ describe('Extensions page', { tags: '@adminUser' }, () => { extensionsPo.extensionDetailsCloseClick(); }); + it('Should not display installed extensions within the available tab', () => { + const extensionsPo = new ExtensionsPagePo(); + + extensionsPo.goTo(); + + // check for installed extension in "installed" tab + extensionsPo.extensionTabInstalledClick(); + extensionsPo.extensionCard(EXTENSION_NAME).should('be.visible'); + + // check for installed extension in "available" tab + extensionsPo.extensionTabAvailableClick(); + extensionsPo.extensionCard(EXTENSION_NAME).should('not.exist'); + }); + it('Should update an extension version', () => { const extensionsPo = new ExtensionsPagePo(); diff --git a/docusaurus/docs/extensions/extensions-configuration.md b/docusaurus/docs/extensions/extensions-configuration.md index 7f8be41471..723be95671 100644 --- a/docusaurus/docs/extensions/extensions-configuration.md +++ b/docusaurus/docs/extensions/extensions-configuration.md @@ -2,4 +2,44 @@ Follow instructions [here](./extensions-getting-started.md) to scaffold your extension. This will assist you in the creation of an extension as a top-level product inside Rancher Dashboard. -Once you've done so, there are some initialization steps specific to extensions. Beyond that, extensions largely work the same as the rest of the dashboard. There are a set of top-level folders that can be defined and used as they are in the dashboard: `chart`, `cloud-credential`, `content`, `detail`, `edit`, `list`, `machine-config`, `models`, `promptRemove`, `l10n`, `windowComponents`, `dialog`, and `formatters`. You can read about what each of these folders does [here](../code-base-works/directory-structure.md). \ No newline at end of file +Once you've done so, there are some initialization steps specific to extensions. Beyond that, extensions largely work the same as the rest of the dashboard. There are a set of top-level folders that can be defined and used as they are in the dashboard: `chart`, `cloud-credential`, `content`, `detail`, `edit`, `list`, `machine-config`, `models`, `promptRemove`, `l10n`, `windowComponents`, `dialog`, and `formatters`. You can read about what each of these folders does [here](../code-base-works/directory-structure.md). + +## Extension Package Metadata + +Each extension package has the ability to customize certain aspects when it comes to compatibility with Rancher Manager/Kubernetes or displaying extension names. These are determined by the `rancher.annotations` object applied to the `package.json` of an extension package. + +These annotations allow you to specify compatibility with Kubernetes, Rancher Manager, the Extensions API, and the Rancher UI version by relying on [semver ranges](https://www.npmjs.com/package/semver/v/6.3.0#ranges). As well as version compatibility, you can also specify a Display Name for the Extension package as it appears on the "Extensions" page within the UI. + +### Configurable Annotations + +| Annotation | Value | Description | +| ------ | :------: | --------------| +| `catalog.cattle.io/kube-version` | `Range` | Determines if the Kubernetes version that Rancher Manager is utilizing is compatible with the Extension package. | +| `catalog.cattle.io/rancher-version` | `Range` | Determines the compatibility of the installed Rancher Manager version with the Extension package. | +| `catalog.cattle.io/ui-extensions-version` | `Range` | Determines the Extensions API version that is compatible with the Extension package. | +| `catalog.cattle.io/ui-version` | `Range` | Determines the Rancher UI version that is compatible with the Extension package. | +| `catalog.cattle.io/display-name` | `String` | Specifies the Display Name for an Extension package's card on the "Extensions" page. | + +### Example Configuration + +Here's an example configuration of an extensions `package.json`: + +___`./pkg/my-package/package.json`___ +```json +{ + "name": "my-package", + "description": "my-package plugin description", + "version": "0.1.0", + "rancher": { + "annotations": { + "catalog.cattle.io/kube-version": ">= v1.26.0-0 < v1.28.0-0", + "catalog.cattle.io/rancher-version": ">= 2.7.7-0 < 2.8.0-0", + "catalog.cattle.io/ui-extensions-version": ">= 1.1.0", + "catalog.cattle.io/ui-version": ">= 2.7.7-0 < 2.8.0-0", + "catalog.cattle.io/display-name": "My Super Great Extension" + } + }, + ... +} +``` + diff --git a/pkg/harvester-manager/package.json b/pkg/harvester-manager/package.json index 791c60b162..b633a570b5 100644 --- a/pkg/harvester-manager/package.json +++ b/pkg/harvester-manager/package.json @@ -4,7 +4,9 @@ "version": "0.1.0", "private": false, "rancher": { - "catalog.cattle.io/display-name": "Virtualization Manager" + "annotations": { + "catalog.cattle.io/display-name": "Virtualization Manager" + } }, "scripts": { "dev": "./node_modules/.bin/nuxt dev", diff --git a/shell/pages/c/_cluster/uiplugins/index.vue b/shell/pages/c/_cluster/uiplugins/index.vue index 852be1853d..4ea42a84f9 100644 --- a/shell/pages/c/_cluster/uiplugins/index.vue +++ b/shell/pages/c/_cluster/uiplugins/index.vue @@ -243,9 +243,9 @@ export default { all = all.map((chart) => { // Label can be overridden by chart annotation - const label = uiPluginAnnotation(UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME) || chart.chartNameDisplay; + const label = uiPluginAnnotation(chart, UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME) || chart.chartNameDisplay; const item = { - name: chart.chartNameDisplay, + name: chart.chartName, label, description: chart.chartDescription, id: chart.id, @@ -305,7 +305,7 @@ export default { if (!chart) { // A plugin is loaded, but there is no chart, so add an item so that it shows up const rancher = typeof p.metadata?.rancher === 'object' ? p.metadata.rancher : {}; - const label = rancher[UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME] || p.name; + const label = rancher.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME] || p.name; const item = { name: p.name, label,