Skip to content

Performance: Developer Docs #14309

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

As the development of an extension progresses over time, through different versions of Rancher and the Shell package, some users may wonder why a specific extension version is available for installation in Rancher versions when it is known that it shouldn't work.

This is due to the fact that when the first releases were done, no-one could foresee if future versions would have breaking changes, therefore [annotations](../extensions-configuration#configurable-annotations) most commonly used like `catalog.cattle.io/rancher-version` or `catalog.cattle.io/kube-version` would either lack an upper limit boundary or just have too low of an upper limit.
This is due to the fact that when the first releases were done, no-one could foresee if future versions would have breaking changes, therefore [annotations](../configuration#configurable-annotations) most commonly used like `catalog.cattle.io/rancher-version` or `catalog.cattle.io/kube-version` would either lack an upper limit boundary or just have too low of an upper limit.

Since that extension was already built and served to users, fixing annotations and repackaging an extension my be a pain point that would often get postponed.

Expand Down
44 changes: 44 additions & 0 deletions docusaurus/docs/extensions/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Configuration

## 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.

### 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/display-name` | `String` | Specifies the Display Name for an Extension package's card on the "Extensions" page. |

### Other properties
| Property | Value | Description |
| ------ | :------: | --------------|
| `noAuth` | `Boolean` | If `noAuth` is set to `true` then the extension will be loaded even when the user is logged out. (Rancher 2.9 - Extensions API 2.0) |

### Example

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.29.0-0",
"catalog.cattle.io/rancher-version": ">= 2.7.7-0 < 2.9.0-0",
"catalog.cattle.io/ui-extensions-version": ">= 1.1.0",
"catalog.cattle.io/display-name": "My Super Great Extension"
},
"noAuth": true
},
...
}
```

Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
# Extensions configuration
# Folder Structure

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.

## Folders Structure

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`.


### chart
## chart
Components in the chart folder are used to add custom UI to a helm install flow. The dashboard will look up a custom chart component for a given helm chart by checking two annotations: `'catalog.cattle.io/ui-component'` if set, otherwise `'catalog.cattle.io/release-name'`.

### cloud-credential
## cloud-credential
Cloud credentials are components that add provider-specific UI to create cloud credentials, needed to provision clusters.

### dialog
## dialog
Components in the dialog folder are used within the `PromptModal` component. Dispatch the `promptModal` action from the dashboard store to open a modal. This action takes a few props:

|Name |Type |Description|
Expand All @@ -24,17 +22,17 @@ Components in the dialog folder are used within the `PromptModal` component. Dis
|modalWidth| String CSS Property | Desired width of the modal (default 600px)|
|modalSticky| Boolean | Whether or not to apply sticky positioning (default false)|

### formatters
## formatters
This is not a top-level folder in the shell, which uses `/components/formatter`, but a top-level `formatters` directory works the same way in an extension as the shell `formatter` directory does. Formatters are used to format data within tables.

### machine-config
## machine-config
Machine configs are used to add provider-specific UI to the rke2/k3s provisioning page.

### detail, edit, and list
## detail, edit, and list
`detail`, `edit`, and `list` folders are used to create custom CRUD views for kubernetes resources, and components in each should be given the same name as the targeted resource.

### models
Kubernetes resources loaded through the dashboard store are, by default, instances of the resource class found here: `plugins/dashboard-store/resource-class.js`. Add a file with the name of the resource to the `models` directory to expand on that functionality. Generally, models should be an extension of the Steve class (Norman resources should not, but they are primarly used around auth functionality):
## models
Kubernetes resources loaded through the dashboard store are, by default, instances of the resource class found here: `plugins/dashboard-store/resource-class.js`. Add a file with the name of the resource to the `models` directory to expand on that functionality. Generally, models should be an extension of the Steve class (Norman resources should not, but they are primarily used around auth functionality):
```
import SteveModel from '@shell/plugins/steve/steve-class';

Expand All @@ -47,56 +45,19 @@ Some common model properties to overwrite are:
* `canDelete`: whether or not the current user should be able to delete a resource
* `detailLocation`: route for the detail view of one instance of the resource

### promptRemove
### Overlapping Model Names
It's possible that different products will use the same kubernetes resource, but need to add different model functionality (eg Harvester has a 'node' model). Files in a extension's `models` folder will overwrite any files in the `shell/models` directory across the application. To extend or overwrite model functionality for a given store, nest models within a subfolder with the same name as the vuex module's `namespace`.

## promptRemove
Components in the PromptRemove folder are used to customize the removal prompt for specific resource types. Components added to this folder should have the same file name as the resource they're intended for. These components do not control the actual removal action - they are intended to allow the developer to supply additional information about consequences of removing a given resource, eg the Global Role removal prompt warns how many users are bound to that role.

### l10n
## l10n
Extension translation strings are merged with those already present in `shell/assets/translations`. Translation strings with duplicate keys of those present in the relevant shell translation file will overwrite those shell translation strings _across the app_: be mindful if adding translation strings that are not explicitly scoped to your extension. Read more about translations [here](./advanced/localization)

### Extension Package Metadata
## 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.


## Overlapping Model Names
It's possible that different products will use the same kubernetes resource, but need to add different model functionality (eg Harvester has a 'node' model). Files in a extension's `models` folder will overwrite any files in the `shell/models` directory across the application. To extend or overwrite model functionality for a given store, nest models within a subfolder with the same name as the vuex module's `namespace`.

## 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/display-name` | `String` | Specifies the Display Name for an Extension package's card on the "Extensions" page. |

## Other configuration properties
| Property | Value | Description |
| ------ | :------: | --------------|
| `noAuth` | `Boolean` | If `noAuth` is set to `true` then the extension will be loaded even when the user is logged out. (Rancher 2.9 - Extensions API 2.0) |

## 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.29.0-0",
"catalog.cattle.io/rancher-version": ">= 2.7.7-0 < 2.9.0-0",
"catalog.cattle.io/ui-extensions-version": ">= 1.1.0",
"catalog.cattle.io/display-name": "My Super Great Extension"
},
"noAuth": true
},
...
}
```

123 changes: 123 additions & 0 deletions docusaurus/docs/extensions/performance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Performance

To ensure UI performance is maintained please follow this guide.

## Handle resource scaling

Rancher may connect to clusters with a large number of resources. For example a downstream cluster may have 10,000 pods, or the upstream cluster may be aware of 20,000 nodes.

> The following applies to components and processes that result in requests to the internal Steve API (`/v1/<resource>` or `/k8s/clusters/<cluster id>/v1/...`).
>
> Those resulting in requests to the internal Norman API (`/v3/<resource>`) or directly to Kube API (`/apis/...` or `/k8s/clusters/<cluster id>/apis/...`) do not require updating.

### Approach

#### Legacy
Before 2.12.0 the pattern to access and monitor resources was to fetch them all and receive an update via WebSocket whenever one changed. However this does not scale when the resource count increases.

- Takes a long time to fetch them all
- Increases memory footprint, impacting browser performance
- Lots of churn when any change, there could be 100s of web socket messages a second


#### Current
From 2.12.0 a new process for improving performance at scale has been introduced.

- Specifically fetch only the required resources
- Filters applied service-side
- Paginate lists server-side
- Watch the resource and update when needed
- 1 debounced web socket update --> 1 http request

### Update Lists to use Server-Side Pagination

When showing a list of resources previously a `ResourceTable` component was used. This would receive a resource schema and some properties. With those it would handle the fetch of all resources and monitoring for updates. The headers / columns for the list would come from the resource's product configuration.

Now though we recommend using the `PaginatedResourceTable` component. This does the exact same as ResourceTable, however if enabled will paginate server-side thus avoiding fetching all resources.

*Secondary Resources*

The 'primary' resource of a list will be fetched, per page, by the `PaginatedResourceTable` component. However sometimes other 'secondary' resources are needed by a list column.

These can either be fetched
- once upfront when the component is first loaded
- Supply a method `:fetchSecondaryResources="..."` to the component
- whenever a new page is shown
- Supply a method `:fetchPageSecondaryResources="..."` to the component
- This could be triggered a lot so should itself be performant

*Header configuration*

Due to pagination happening server-side the UI can no-longer sort and filter on properties it manages locally. The header configuration that supports this new process is automatically created unless custom headers have been provided.

To override existing non-compatible server-side pagination headers
- if headers are supplied to the component, also set `:pagination-headers="..."`
- if headers are supplied by product configuration, supply a second array to `headers(<resourcetype>, <non server-side pagination compatible headers>)`

A compatible header will contain `value`, `sort` and `search` properties that reference values that exist natively in the resource server-side

*Examples*

- rancher/dashboard `shell/list/service.vue`
- for custom header configuration see `headers(SERVICE,` in `shell/config/product/explorer.js`
- for secondary resources see `fetchSecondaryResources`
- rancher/dashboard `shell/list/node.vue`
- for custom header configuration see `headers(NODE,` in `shell/config/product/explorer.js`
- for secondary resources see `fetchSecondaryResources`
- for page secondary resources see `fetchPageSecondaryResources`
- rancher/dashboard `shell/pages/c/_cluster/explorer/EventsTable.vue`
- for custom header configuration see `:pagination-headers="paginationHeaders"`
- rancher/dashboard `shell/pages/home.vue`
- for custom header configuration see `:pagination-headers="paginationHeaders"`
- for secondary resources see `fetchSecondaryResources`
- for page secondary resources see `fetchPageSecondaryResources`

*Checklist*

1. `ResourceTable` has been replaced with `PaginatedResourceTable`
1. Component containing `PaginatedResourceTable` should not dispatch any `findAll` actions for primary or secondary resources
- Primary will be fetched via the `PaginatedResourceTable` component (no extra changes required)
- Secondary resources should be fetched via functions `fetchSecondaryResources` and/or `fetchPageSecondaryResources` passed into `PaginatedResourceTable`
1. If headers have been provided or configured in product, specific server-side pagination compatible ones should be provided
- Header `value`, `sort` and `search` all reference properties on the native object stored server-side
1. List has been validated when Server-Side Pagination is enabled via the `ui-sql-cache` Feature Flag

### Update Selects to limit resources

In the UI there are places where the user is required to select a specific resource. A new select component `ResourceLabeledSelect` has been created that supports both the old method (fetch everything, display everything) and the new method (fetch only a page's worth of data, only show that page). This should replace usages of `LabeledSelect`.

Some additional configuration can be supplied, see the `paginatedResourceSettings` property / `ResourceLabeledSelectPaginateSettings` type for details.

*Examples*

- rancher/dashboard `shell/components/form/SecretSelector.vue`
- rancher/dashboard `shell/chart/rancher-backup/S3.vue`

*Checklist*

1. `LabeledSelect` component has been replaced with `ResourceLabeledSelect`
1. Configuration has been supplied, given the resource type and requirements
1. Change has been validated when Server-Side Pagination is enabled via the `ui-sql-cache` Feature Flag

### Update Standalone Requests

There may be other places and circumstances where an action `findAll` needs to be replaced with something that filters down the results to the desired subset

*General Filtering*

The `findAll` action can be replaced with `findPage` which unlocks a number of different filtering options. For details see `shell/types/store/pagination.types.ts` `FilterArgs` and inline comments. For details on what these represent see [Steve API docs](https://github.com/rancher/steve/) (NOTE - The steve api is internal and liable to change without notice)

*Uncached Resources*

The `findX` set of filters will populate the local cache with a result set and keep it updated. The cache is 1:1 with type. There are scenarios where you might want to keep the cached results but also make another request to fetch resources of the same type. To avoid overwriting the cache the helper `PaginationWrapper` can be used. This will handle making the request and hooks to act upon when that changes.

*labelSelectors Filtering*
// TODO: RC

*Examples*
- shell/list/persistentvolume.vue `fetchPageSecondaryResources`

*Checklists*
- Cache based resources are fetched and filtered by updating `findAll` to use `findPage`
- Non-cache based resources are managed by `PaginationWrapper`
- TODO: RC local usages of the labelSelector help functions are updated to use ??? - Filters are either basic or labelSelector
2 changes: 1 addition & 1 deletion docusaurus/docs/extensions/rancher-2.9-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ These changes bring Shell versions in line with standard versioning patterns. On

> With these annotations your extension will be restricted to Rancher version greater or equal to `2.9.0` and will also be restricted to a UI Extensions version (Shell version) greater or equal to `2.0.1`.
These are not mandatory but highly recommended to ensure your extension versions reference Rancher / Shell versions they're compatible with.
For more information about the annotation we allow for, check the documentation [here](./extensions-configuration#configurable-annotations).
For more information about the annotation we allow for, check the documentation [here](./configuration#configurable-annotations).

- After the above steps, just publish a new version of the extension. That published version should now be compliant with Rancher 2.9.0.

Expand Down
Loading
Loading