@rancher/shell 3.0.8-rc.1 → 3.0.8-rc.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/brand/suse/banner.svg +1 -0
- package/assets/brand/suse/dark/banner.svg +1 -0
- package/assets/brand/suse/dark/login-landscape.svg +1 -0
- package/assets/brand/suse/dark/rancher-logo.svg +1 -1
- package/assets/brand/suse/favicon.png +0 -0
- package/assets/brand/suse/login-landscape.svg +1 -0
- package/assets/brand/suse/metadata.json +11 -1
- package/assets/brand/suse/rancher-logo.svg +1 -1
- package/assets/fonts/suse/suse-v2-latin-300.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-300.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-600.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-600.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-700.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-700.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-800.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-800.woff2 +0 -0
- package/assets/fonts/suse/suse-v2-latin-regular.woff +0 -0
- package/assets/fonts/suse/suse-v2-latin-regular.woff2 +0 -0
- package/assets/images/content/README.md +5 -0
- package/assets/images/content/cloud-native.svg +84 -0
- package/assets/images/content/dark/cloud-native.svg +21 -0
- package/assets/images/content/dark/shield.svg +59 -0
- package/assets/images/content/dark/suse.svg +10 -0
- package/assets/images/content/shield.svg +59 -0
- package/assets/images/content/suse.svg +10 -0
- package/assets/styles/base/_typography.scss +1 -0
- package/assets/styles/fonts/_fontstack.scss +53 -1
- package/assets/styles/global/_cards.scss +0 -3
- package/assets/styles/global/_layout.scss +21 -35
- package/assets/styles/themes/_dark.scss +1 -1
- package/assets/styles/themes/_light.scss +1 -1
- package/assets/styles/themes/_modern.scss +11 -3
- package/assets/styles/themes/_suse.scss +116 -24
- package/assets/translations/en-us.yaml +94 -10
- package/components/AutoscalerCard.vue +113 -0
- package/components/AutoscalerTab.vue +94 -0
- package/components/BackLink.vue +8 -0
- package/components/BannerGraphic.vue +36 -21
- package/components/BrandImage.vue +17 -6
- package/components/ClusterIconMenu.vue +1 -1
- package/components/ClusterProviderIcon.vue +1 -1
- package/components/Cron/CronExpressionEditor.vue +1 -1
- package/components/Cron/CronExpressionEditorModal.vue +1 -1
- package/components/Drawer/Chrome.vue +2 -6
- package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +4 -9
- package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
- package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
- package/components/Drawer/ResourceDetailDrawer/index.vue +4 -9
- package/components/Drawer/ResourceDetailDrawer/types.ts +17 -0
- package/components/Drawer/types.ts +3 -0
- package/components/DynamicContent/DynamicContentBanner.vue +102 -0
- package/components/DynamicContent/DynamicContentCloseButton.vue +42 -0
- package/components/DynamicContent/DynamicContentIcon.vue +132 -0
- package/components/DynamicContent/DynamicContentPanel.vue +112 -0
- package/components/DynamicContent/content.ts +78 -0
- package/components/EmberPage.vue +1 -1
- package/components/IconOrSvg.vue +2 -2
- package/components/PaginatedResourceTable.vue +2 -6
- package/components/PopoverCard.vue +192 -0
- package/components/Questions/__tests__/index.test.ts +159 -0
- package/components/Resource/Detail/CopyToClipboard.vue +4 -1
- package/components/Resource/Detail/FetchLoader/composables.ts +18 -4
- package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +1 -1
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +4 -0
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +1 -1
- package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
- package/components/Resource/Detail/Metadata/composables.ts +9 -9
- package/components/Resource/Detail/Metadata/index.vue +3 -3
- package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -19
- package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +0 -29
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +132 -150
- package/components/Resource/Detail/ResourcePopover/index.vue +54 -159
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
- package/components/Resource/Detail/TitleBar/composables.ts +2 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -6
- package/components/Resource/Detail/composables.ts +12 -0
- package/components/ResourceDetail/Masthead/latest.vue +29 -0
- package/components/ResourceDetail/index.vue +4 -1
- package/components/ResourceList/Masthead.vue +1 -1
- package/components/ResourceTable.vue +1 -1
- package/components/SortableTable/index.vue +2 -1
- package/components/Tabbed/__tests__/index.test.ts +86 -0
- package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
- package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
- package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
- package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
- package/components/__tests__/AutoscalerCard.test.ts +154 -0
- package/components/__tests__/AutoscalerTab.test.ts +125 -0
- package/components/__tests__/PopoverCard.test.ts +204 -0
- package/components/auth/SelectPrincipal.vue +24 -6
- package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
- package/components/auth/login/ldap.vue +3 -3
- package/components/form/NodeScheduling.vue +2 -2
- package/components/formatter/Autoscaler.vue +97 -0
- package/components/formatter/InternalExternalIP.vue +198 -24
- package/components/formatter/__tests__/Autoscaler.test.ts +156 -0
- package/components/formatter/__tests__/InternalExternalIP.test.ts +133 -0
- package/components/google/util/__tests__/formatter.test.ts +47 -0
- package/components/google/util/formatter.ts +5 -2
- package/components/nav/Group.vue +21 -5
- package/components/nav/Header.vue +37 -17
- package/components/nav/NamespaceFilter.vue +13 -1
- package/components/nav/NotificationCenter/index.vue +2 -1
- package/components/nav/TopLevelMenu.helper.ts +16 -6
- package/components/nav/TopLevelMenu.vue +4 -2
- package/components/nav/Type.vue +8 -3
- package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
- package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
- package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
- package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
- package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
- package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
- package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
- package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
- package/components/nav/WindowManager/constants.ts +23 -0
- package/components/nav/WindowManager/index.vue +61 -575
- package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
- package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
- package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
- package/components/nav/__tests__/Type.test.ts +59 -0
- package/components/templates/default.vue +4 -40
- package/components/templates/home.vue +31 -5
- package/components/templates/plain.vue +30 -4
- package/components/templates/standalone.vue +1 -1
- package/composables/useI18n.ts +10 -1
- package/composables/useInterval.ts +15 -0
- package/config/__test__/uiplugins.test.ts +309 -0
- package/config/labels-annotations.js +9 -1
- package/config/product/explorer.js +3 -1
- package/config/product/manager.js +20 -9
- package/config/router/navigation-guards/clusters.js +3 -3
- package/config/router/navigation-guards/products.js +1 -1
- package/config/router/routes.js +10 -2
- package/config/settings.ts +2 -1
- package/config/store.js +4 -2
- package/config/table-headers.js +8 -0
- package/config/types.js +9 -0
- package/config/uiplugins.js +46 -2
- package/config/version.js +1 -1
- package/core/__test__/extension-manager-impl.test.js +236 -0
- package/core/extension-manager-impl.js +21 -4
- package/core/plugin-helpers.ts +4 -2
- package/core/plugins-loader.js +2 -2
- package/core/types-provisioning.ts +8 -1
- package/detail/pod.vue +1 -0
- package/detail/provisioning.cattle.io.cluster.vue +19 -7
- package/dialog/DeveloperLoadExtensionDialog.vue +13 -4
- package/dialog/RollbackWorkloadDialog.vue +2 -5
- package/dialog/SearchDialog.vue +1 -0
- package/directives/ui-context.ts +103 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +2 -2
- package/edit/auth/__tests__/oidc.test.ts +26 -0
- package/edit/auth/github.vue +5 -0
- package/edit/auth/oidc.vue +5 -1
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
- package/edit/cloudcredential.vue +1 -1
- package/edit/configmap.vue +1 -0
- package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
- package/edit/fleet.cattle.io.gitrepo.vue +0 -10
- package/edit/fleet.cattle.io.helmop.vue +6 -6
- package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
- package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
- package/edit/logging-flow/index.vue +1 -0
- package/edit/logging.banzaicloud.io.output/index.vue +1 -0
- package/edit/management.cattle.io.fleetworkspace.vue +1 -1
- package/edit/management.cattle.io.project.vue +1 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
- package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
- package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
- package/edit/monitoring.coreos.com.route.vue +1 -1
- package/edit/namespace.vue +1 -0
- package/edit/networking.istio.io.destinationrule/index.vue +1 -0
- package/edit/networking.k8s.io.ingress/index.vue +1 -0
- package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
- package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
- package/edit/node.vue +1 -0
- package/edit/persistentvolume/index.vue +27 -22
- package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
- package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
- package/edit/persistentvolume/plugins/azureFile.vue +15 -14
- package/edit/persistentvolume/plugins/cephfs.vue +15 -14
- package/edit/persistentvolume/plugins/cinder.vue +15 -14
- package/edit/persistentvolume/plugins/csi.vue +18 -16
- package/edit/persistentvolume/plugins/fc.vue +13 -14
- package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
- package/edit/persistentvolume/plugins/flocker.vue +1 -3
- package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
- package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
- package/edit/persistentvolume/plugins/hostPath.vue +40 -39
- package/edit/persistentvolume/plugins/iscsi.vue +13 -14
- package/edit/persistentvolume/plugins/local.vue +1 -3
- package/edit/persistentvolume/plugins/longhorn.vue +23 -22
- package/edit/persistentvolume/plugins/nfs.vue +15 -14
- package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
- package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
- package/edit/persistentvolume/plugins/quobyte.vue +15 -14
- package/edit/persistentvolume/plugins/rbd.vue +15 -14
- package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
- package/edit/persistentvolume/plugins/storageos.vue +15 -14
- package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +32 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +35 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +155 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +21 -21
- package/edit/provisioning.cattle.io.cluster/index.vue +28 -18
- package/edit/provisioning.cattle.io.cluster/rke2.vue +50 -16
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +107 -5
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +92 -4
- package/edit/secret/index.vue +1 -1
- package/edit/service.vue +9 -4
- package/edit/serviceaccount.vue +1 -0
- package/edit/storage.k8s.io.storageclass/index.vue +1 -0
- package/edit/workload/index.vue +2 -1
- package/edit/workload/mixins/workload.js +1 -1
- package/initialize/App.vue +4 -4
- package/initialize/install-directives.js +2 -0
- package/initialize/install-plugins.js +19 -2
- package/list/provisioning.cattle.io.cluster.vue +15 -2
- package/machine-config/amazonec2.vue +42 -135
- package/machine-config/components/EC2Networking.vue +490 -0
- package/machine-config/components/__tests__/EC2Networking.test.ts +148 -0
- package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +294 -0
- package/machine-config/digitalocean.vue +11 -0
- package/machine-config/google.vue +1 -1
- package/mixins/__tests__/brand.spec.ts +2 -2
- package/mixins/__tests__/chart.test.ts +21 -0
- package/mixins/brand.js +1 -7
- package/mixins/chart.js +7 -1
- package/mixins/create-edit-view/index.js +5 -0
- package/models/__tests__/chart.test.ts +33 -4
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +112 -5
- package/models/chart.js +25 -13
- package/models/cluster/node.js +13 -6
- package/models/cluster.x-k8s.io.machine.js +10 -20
- package/models/cluster.x-k8s.io.machinedeployment.js +5 -1
- package/models/management.cattle.io.cluster.js +21 -3
- package/models/management.cattle.io.kontainerdriver.js +1 -0
- package/models/provisioning.cattle.io.cluster.js +249 -33
- package/package.json +8 -7
- package/pages/auth/login.vue +41 -5
- package/pages/auth/setup.vue +1 -1
- package/pages/auth/verify.vue +3 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
- package/pages/c/_cluster/apps/charts/chart.vue +33 -15
- package/pages/c/_cluster/apps/charts/index.vue +11 -13
- package/pages/c/_cluster/apps/charts/install.vue +1 -1
- package/pages/c/_cluster/explorer/index.vue +8 -6
- package/pages/c/_cluster/manager/hostedprovider/index.vue +220 -0
- package/pages/c/_cluster/settings/brand.vue +1 -1
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
- package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
- package/pages/c/_cluster/uiplugins/index.vue +126 -184
- package/pages/home.vue +14 -4
- package/pkg/auto-import.js +3 -3
- package/pkg/dynamic-importer.lib.js +5 -1
- package/pkg/import.js +1 -1
- package/plugins/dashboard-client-init.js +3 -0
- package/plugins/dashboard-store/getters.js +19 -2
- package/plugins/dashboard-store/model-loader.js +1 -1
- package/plugins/dashboard-store/resource-class.js +10 -6
- package/plugins/dynamic-content.js +13 -0
- package/plugins/i18n.js +8 -0
- package/plugins/plugin.js +2 -2
- package/plugins/steve/__tests__/steve-pagination-utils.test.ts +333 -0
- package/plugins/steve/steve-class.js +1 -1
- package/plugins/steve/steve-pagination-utils.ts +39 -20
- package/plugins/steve/subscribe.js +17 -9
- package/plugins/subscribe-events.ts +4 -2
- package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
- package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -34
- package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
- package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
- package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
- package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
- package/rancher-components/Pill/types.ts +0 -1
- package/rancher-components/RcDropdown/RcDropdownItem.vue +1 -0
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
- package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
- package/rancher-components/RcIcon/RcIcon.vue +46 -0
- package/rancher-components/RcIcon/index.ts +1 -0
- package/rancher-components/RcIcon/types.ts +160 -0
- package/rancher-components/utils/status.test.ts +67 -0
- package/rancher-components/utils/status.ts +77 -0
- package/scripts/typegen.sh +1 -0
- package/store/__tests__/catalog.test.ts +1 -1
- package/store/action-menu.js +8 -0
- package/store/auth.js +4 -4
- package/store/catalog.js +6 -0
- package/store/features.js +1 -0
- package/store/i18n.js +3 -3
- package/store/index.js +40 -19
- package/store/notifications.ts +51 -4
- package/store/plugins.js +7 -3
- package/store/prefs.js +6 -6
- package/store/type-map.js +7 -7
- package/store/ui-context.ts +86 -0
- package/store/wm.ts +244 -0
- package/types/notifications/index.ts +27 -3
- package/types/shell/index.d.ts +80 -4
- package/types/store/__tests__/pagination.types.spec.ts +137 -0
- package/types/store/pagination.types.ts +157 -9
- package/types/store/subscribe-events.types.ts +8 -1
- package/types/store/subscribe.types.ts +1 -0
- package/types/window-manager.ts +24 -0
- package/utils/__tests__/object.test.ts +19 -0
- package/utils/__tests__/provider.test.ts +98 -0
- package/utils/__tests__/selector-typed.test.ts +263 -0
- package/utils/__tests__/version.test.ts +19 -1
- package/utils/autoscaler-utils.ts +7 -0
- package/utils/back-off.ts +3 -3
- package/utils/brand.ts +29 -0
- package/utils/chart.js +18 -0
- package/utils/color.js +1 -1
- package/utils/dynamic-content/__tests__/announcement.test.ts +498 -0
- package/utils/dynamic-content/__tests__/info.test.ts +21 -9
- package/utils/dynamic-content/announcement.ts +142 -0
- package/utils/dynamic-content/example.json +40 -0
- package/utils/dynamic-content/index.ts +6 -2
- package/utils/dynamic-content/info.ts +44 -2
- package/utils/dynamic-content/new-release.ts +1 -1
- package/utils/dynamic-content/notification-handler.ts +48 -0
- package/utils/dynamic-content/types.d.ts +53 -1
- package/utils/dynamic-importer.js +2 -2
- package/utils/favicon.js +4 -4
- package/utils/object.js +20 -2
- package/utils/pagination-utils.ts +2 -2
- package/utils/pagination-wrapper.ts +13 -9
- package/utils/provider.ts +14 -0
- package/utils/scroll.js +7 -0
- package/utils/selector-typed.ts +6 -2
- package/utils/settings.ts +15 -0
- package/utils/unit-tests/pagination-utils.spec.ts +8 -8
- package/utils/validators/machine-pool.ts +13 -3
- package/utils/version.js +15 -0
- package/vue.config.js +3 -3
- package/assets/images/icons/document.svg +0 -3
- package/plugins/nuxt-client-init.js +0 -3
- package/store/wm.js +0 -95
- /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
- /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
- /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
|
@@ -6,12 +6,12 @@ import {
|
|
|
6
6
|
HCI,
|
|
7
7
|
MANAGEMENT,
|
|
8
8
|
SNAPSHOT,
|
|
9
|
-
VIRTUAL_TYPES
|
|
9
|
+
VIRTUAL_TYPES,
|
|
10
|
+
HOSTED_PROVIDER
|
|
10
11
|
} from '@shell/config/types';
|
|
11
12
|
import { MULTI_CLUSTER } from '@shell/store/features';
|
|
12
13
|
import { DSL } from '@shell/store/type-map';
|
|
13
14
|
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
14
|
-
|
|
15
15
|
export const NAME = 'manager';
|
|
16
16
|
|
|
17
17
|
export function init(store) {
|
|
@@ -63,7 +63,7 @@ export function init(store) {
|
|
|
63
63
|
basicType([
|
|
64
64
|
CAPI.RANCHER_CLUSTER,
|
|
65
65
|
'cloud-credentials',
|
|
66
|
-
'
|
|
66
|
+
'providers',
|
|
67
67
|
]);
|
|
68
68
|
|
|
69
69
|
configureType(SNAPSHOT, { depaginate: true });
|
|
@@ -73,12 +73,22 @@ export function init(store) {
|
|
|
73
73
|
});
|
|
74
74
|
weightType(CAPI.RANCHER_CLUSTER, 100, true);
|
|
75
75
|
weightType('cloud-credentials', 99, true);
|
|
76
|
-
weightType('
|
|
76
|
+
weightType('providers', 98, true);
|
|
77
77
|
weightType(CATALOG.CLUSTER_REPO, 97, true);
|
|
78
|
+
virtualType({
|
|
79
|
+
labelKey: 'providers.hosted.title',
|
|
80
|
+
name: HOSTED_PROVIDER,
|
|
81
|
+
group: 'Root',
|
|
82
|
+
weight: 1,
|
|
83
|
+
namespaced: false,
|
|
84
|
+
icon: 'globe',
|
|
85
|
+
route: { name: 'c-cluster-manager-hostedprovider' },
|
|
86
|
+
exact: true
|
|
87
|
+
});
|
|
78
88
|
|
|
79
89
|
virtualType({
|
|
80
90
|
labelKey: 'drivers.kontainer.title',
|
|
81
|
-
name: 'rke-kontainer-
|
|
91
|
+
name: 'rke-kontainer-providers',
|
|
82
92
|
group: 'Root',
|
|
83
93
|
namespaced: false,
|
|
84
94
|
icon: 'globe',
|
|
@@ -87,7 +97,7 @@ export function init(store) {
|
|
|
87
97
|
});
|
|
88
98
|
virtualType({
|
|
89
99
|
labelKey: 'drivers.node.title',
|
|
90
|
-
name: 'rke-node-
|
|
100
|
+
name: 'rke-node-providers',
|
|
91
101
|
group: 'Root',
|
|
92
102
|
namespaced: false,
|
|
93
103
|
icon: 'globe',
|
|
@@ -107,9 +117,10 @@ export function init(store) {
|
|
|
107
117
|
});
|
|
108
118
|
|
|
109
119
|
basicType([
|
|
110
|
-
|
|
111
|
-
'rke-
|
|
112
|
-
|
|
120
|
+
HOSTED_PROVIDER,
|
|
121
|
+
'rke-kontainer-providers',
|
|
122
|
+
'rke-node-providers',
|
|
123
|
+
], 'providers');
|
|
113
124
|
|
|
114
125
|
weightType(CAPI.MACHINE_DEPLOYMENT, 4, true);
|
|
115
126
|
weightType(CAPI.MACHINE_SET, 3, true);
|
|
@@ -30,12 +30,12 @@ export async function loadClusters(to, from, next, { store }) {
|
|
|
30
30
|
const oldPkg = getPackageFromRoute(from);
|
|
31
31
|
const oldProduct = getProductFromRoute(from);
|
|
32
32
|
|
|
33
|
-
// TODO: Replace all references to store.$
|
|
33
|
+
// TODO: Replace all references to store.$extension.
|
|
34
34
|
// Unfortunately the initialization code has circular dependencies between creating
|
|
35
35
|
// the router and creating the store that will need to be untangled before this can be tackled.
|
|
36
36
|
|
|
37
37
|
// Leave an old pkg where we weren't before?
|
|
38
|
-
const oldPkgPlugin = oldPkg ? Object.values(store.$
|
|
38
|
+
const oldPkgPlugin = oldPkg ? Object.values(store.$extension.getPlugins()).find((p) => p.name === oldPkg) : null;
|
|
39
39
|
|
|
40
40
|
if (oldPkg && oldPkg !== pkg ) {
|
|
41
41
|
// Execute anything optional the plugin wants to. For example resetting it's store to remove data
|
|
@@ -53,7 +53,7 @@ export async function loadClusters(to, from, next, { store }) {
|
|
|
53
53
|
];
|
|
54
54
|
|
|
55
55
|
// Entering a new package where we weren't before?
|
|
56
|
-
const newPkgPlugin = pkg ? Object.values(store.$
|
|
56
|
+
const newPkgPlugin = pkg ? Object.values(store.$extension.getPlugins()).find((p) => p.name === pkg) : null;
|
|
57
57
|
|
|
58
58
|
// Note - We can't block on oldPkg !== newPkg because on a fresh load the `from` route equals the `to` route
|
|
59
59
|
if (pkg && (oldPkg !== pkg || from.fullPath === to.fullPath)) {
|
|
@@ -9,7 +9,7 @@ export async function loadProducts(to, from, next, { store }) {
|
|
|
9
9
|
// GC should be notified of route change before any find/get request is made that might be used for that page
|
|
10
10
|
store.dispatch('gcRouteChanged', to);
|
|
11
11
|
|
|
12
|
-
await applyProducts(store, store.$
|
|
12
|
+
await applyProducts(store, store.$extension);
|
|
13
13
|
setProduct(store, to);
|
|
14
14
|
next();
|
|
15
15
|
}
|
package/config/router/routes.js
CHANGED
|
@@ -81,9 +81,13 @@ export default [
|
|
|
81
81
|
{
|
|
82
82
|
path: '/c/:cluster/uiplugins',
|
|
83
83
|
name: 'c-cluster-uiplugins',
|
|
84
|
-
component: () => interopDefault(import('@shell/pages/c/_cluster/uiplugins/index.vue'))
|
|
84
|
+
component: () => interopDefault(import('@shell/pages/c/_cluster/uiplugins/index.vue'))
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
path: '/c/:cluster/uiplugins/catalogs',
|
|
88
|
+
component: () => interopDefault(import('@shell/pages/c/_cluster/uiplugins/catalogs.vue')),
|
|
89
|
+
name: 'c-cluster-uiplugins-catalogs'
|
|
85
90
|
},
|
|
86
|
-
|
|
87
91
|
{
|
|
88
92
|
path: '/diagnostic',
|
|
89
93
|
component: () => interopDefault(import('@shell/pages/diagnostic.vue')),
|
|
@@ -363,6 +367,10 @@ export default [
|
|
|
363
367
|
path: '/c/:cluster/manager/nodeDriver',
|
|
364
368
|
component: () => interopDefault(import('@shell/pages/c/_cluster/manager/drivers/nodeDriver/index.vue')),
|
|
365
369
|
name: 'c-cluster-manager-driver-nodedriver'
|
|
370
|
+
}, {
|
|
371
|
+
path: '/c/:cluster/manager/hostedprovider',
|
|
372
|
+
component: () => interopDefault(import('@shell/pages/c/_cluster/manager/hostedprovider/index.vue')),
|
|
373
|
+
name: 'c-cluster-manager-hostedprovider'
|
|
366
374
|
}, {
|
|
367
375
|
path: '/c/:cluster/monitoring/alertmanagerconfig',
|
|
368
376
|
component: () => interopDefault(import('@shell/pages/c/_cluster/monitoring/alertmanagerconfig/index.vue')),
|
package/config/settings.ts
CHANGED
|
@@ -111,6 +111,7 @@ export const SETTING = {
|
|
|
111
111
|
IMPORTED_CLUSTER_VERSION_MANAGEMENT: 'imported-cluster-version-management',
|
|
112
112
|
CLUSTER_AGENT_DEFAULT_PRIORITY_CLASS: 'cluster-agent-default-priority-class',
|
|
113
113
|
CLUSTER_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET: 'cluster-agent-default-pod-disruption-budget',
|
|
114
|
+
KEV2_OPERATORS: 'kev2-operators',
|
|
114
115
|
/**
|
|
115
116
|
* Dynamic Content settings
|
|
116
117
|
*/
|
|
@@ -193,7 +194,7 @@ export const PROVISIONING_SETTINGS = [
|
|
|
193
194
|
SETTING.K3S_UPGRADER_UNINSTALL_CONCURRENCY,
|
|
194
195
|
SETTING.IMPORTED_CLUSTER_VERSION_MANAGEMENT,
|
|
195
196
|
SETTING.CLUSTER_AGENT_DEFAULT_PRIORITY_CLASS,
|
|
196
|
-
SETTING.CLUSTER_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET
|
|
197
|
+
SETTING.CLUSTER_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET,
|
|
197
198
|
];
|
|
198
199
|
|
|
199
200
|
/**
|
package/config/store.js
CHANGED
|
@@ -35,11 +35,12 @@ let store = {};
|
|
|
35
35
|
resolveStoreModules(require('../store/slideInPanel.ts'), 'slideInPanel.ts');
|
|
36
36
|
resolveStoreModules(require('../store/type-map.js'), 'type-map.js');
|
|
37
37
|
resolveStoreModules(require('../store/uiplugins.ts'), 'uiplugins.ts');
|
|
38
|
-
resolveStoreModules(require('../store/wm.
|
|
38
|
+
resolveStoreModules(require('../store/wm.ts'), 'wm.ts');
|
|
39
39
|
resolveStoreModules(require('../store/customisation.js'), 'customisation.js');
|
|
40
40
|
resolveStoreModules(require('../store/cru-resource.ts'), 'cru-resource.ts');
|
|
41
41
|
resolveStoreModules(require('../store/notifications.ts'), 'notifications.ts');
|
|
42
42
|
resolveStoreModules(require('../store/cookies.ts'), 'cookies.ts');
|
|
43
|
+
resolveStoreModules(require('../store/ui-context.ts'), 'ui-context.ts');
|
|
43
44
|
|
|
44
45
|
// If the environment supports hot reloading...
|
|
45
46
|
|
|
@@ -66,11 +67,12 @@ let store = {};
|
|
|
66
67
|
'../store/slideInPanel.ts',
|
|
67
68
|
'../store/type-map.js',
|
|
68
69
|
'../store/uiplugins.ts',
|
|
69
|
-
'../store/wm.
|
|
70
|
+
'../store/wm.ts',
|
|
70
71
|
'../store/customisation.js',
|
|
71
72
|
'../store/cru-resource.ts',
|
|
72
73
|
'../store/notifications.ts',
|
|
73
74
|
'../store/cookies.ts',
|
|
75
|
+
'../store/ui-context.ts',
|
|
74
76
|
], () => {
|
|
75
77
|
// Update `root.modules` with the latest definitions.
|
|
76
78
|
updateModules();
|
package/config/table-headers.js
CHANGED
|
@@ -1183,3 +1183,11 @@ export const PROJECT = {
|
|
|
1183
1183
|
name: 'project',
|
|
1184
1184
|
labelKey: 'tableHeaders.project',
|
|
1185
1185
|
};
|
|
1186
|
+
|
|
1187
|
+
export const AUTOSCALER_ENABLED = {
|
|
1188
|
+
name: 'autoscaler',
|
|
1189
|
+
labelKey: 'tableHeaders.autoscaler',
|
|
1190
|
+
value: 'isAutoscalerEnabled',
|
|
1191
|
+
sort: ['autoscaler'],
|
|
1192
|
+
formatter: 'Autoscaler',
|
|
1193
|
+
};
|
package/config/types.js
CHANGED
|
@@ -222,6 +222,13 @@ export const MANAGEMENT = {
|
|
|
222
222
|
OIDC_CLIENT: 'management.cattle.io.oidcclient'
|
|
223
223
|
};
|
|
224
224
|
|
|
225
|
+
export const BRAND = {
|
|
226
|
+
SUSE: 'suse',
|
|
227
|
+
CSP: 'csp',
|
|
228
|
+
FEDERAL: 'federal',
|
|
229
|
+
RGS: 'rgs',
|
|
230
|
+
};
|
|
231
|
+
|
|
225
232
|
export const EXT = { USER_ACTIVITY: 'ext.cattle.io.useractivity' };
|
|
226
233
|
|
|
227
234
|
export const CAPI = {
|
|
@@ -364,3 +371,5 @@ export const DEFAULT_GRAFANA_STORAGE_SIZE = '10Gi';
|
|
|
364
371
|
|
|
365
372
|
export const DEPRECATED = 'Deprecated';
|
|
366
373
|
export const EXPERIMENTAL = 'Experimental';
|
|
374
|
+
export const AUTOSCALER_CONFIG_MAP_ID = 'kube-system/cluster-autoscaler-status';
|
|
375
|
+
export const HOSTED_PROVIDER = 'hostedprovider';
|
package/config/uiplugins.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import semver from 'semver';
|
|
2
|
+
import { isRancherPrime } from '@shell/config/version';
|
|
2
3
|
|
|
3
4
|
// Version of the plugin API supported
|
|
4
5
|
// here we inject the current shell version that we read in vue.config
|
|
@@ -43,6 +44,7 @@ export const UI_PLUGIN_CHART_ANNOTATIONS = {
|
|
|
43
44
|
EXTENSIONS_HOST: 'catalog.cattle.io/ui-extensions-host',
|
|
44
45
|
DISPLAY_NAME: 'catalog.cattle.io/display-name',
|
|
45
46
|
HIDDEN_BUILTIN: 'catalog.cattle.io/ui-hidden-builtin',
|
|
47
|
+
PRIME_ONLY: 'catalog.cattle.io/prime-only'
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
// Extension catalog labels
|
|
@@ -58,6 +60,7 @@ export const EXTENSIONS_INCOMPATIBILITY_TYPES = {
|
|
|
58
60
|
EXTENSIONS_API: 'extensionsApiVersion',
|
|
59
61
|
KUBE: 'kubeVersion',
|
|
60
62
|
HOST: 'host',
|
|
63
|
+
PRIME_ONLY: 'primeOnly',
|
|
61
64
|
};
|
|
62
65
|
|
|
63
66
|
export const EXTENSIONS_INCOMPATIBILITY_DATA = {
|
|
@@ -87,6 +90,11 @@ export const EXTENSIONS_INCOMPATIBILITY_DATA = {
|
|
|
87
90
|
tooltipKey: 'plugins.info.requiresHost',
|
|
88
91
|
mainHost: UI_PLUGIN_HOST_APP,
|
|
89
92
|
},
|
|
93
|
+
PRIME_ONLY: {
|
|
94
|
+
type: EXTENSIONS_INCOMPATIBILITY_TYPES.PRIME_ONLY,
|
|
95
|
+
cardMessageKey: 'plugins.incompatiblePrimeOnly',
|
|
96
|
+
tooltipKey: 'plugins.info.requiresRancherPrime',
|
|
97
|
+
},
|
|
90
98
|
};
|
|
91
99
|
|
|
92
100
|
export function isUIPlugin(chart) {
|
|
@@ -124,10 +132,29 @@ export function parseRancherVersion(v) {
|
|
|
124
132
|
* Check if a version is incompatible with the current environment
|
|
125
133
|
*/
|
|
126
134
|
function checkIncompatibility(currentVersion, requiredVersion, incompatibilityData, returnObj, versionObj) {
|
|
127
|
-
|
|
135
|
+
let passed = true;
|
|
136
|
+
|
|
137
|
+
switch (incompatibilityData.type) {
|
|
138
|
+
case EXTENSIONS_INCOMPATIBILITY_TYPES.PRIME_ONLY:
|
|
139
|
+
if (requiredVersion && !currentVersion) {
|
|
140
|
+
passed = false;
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
case EXTENSIONS_INCOMPATIBILITY_TYPES.EXTENSIONS_API_MISSING:
|
|
144
|
+
!requiredVersion ? passed = false : passed = true;
|
|
145
|
+
break;
|
|
146
|
+
default:
|
|
147
|
+
if (requiredVersion && !semver.satisfies(currentVersion, requiredVersion)) {
|
|
148
|
+
passed = false;
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!passed) {
|
|
128
154
|
if (!returnObj) {
|
|
129
155
|
return false;
|
|
130
156
|
}
|
|
157
|
+
|
|
131
158
|
versionObj.isVersionCompatible = false;
|
|
132
159
|
versionObj.versionIncompatibilityData = { ...incompatibilityData, required: requiredVersion };
|
|
133
160
|
|
|
@@ -137,7 +164,7 @@ function checkIncompatibility(currentVersion, requiredVersion, incompatibilityDa
|
|
|
137
164
|
return true;
|
|
138
165
|
}
|
|
139
166
|
|
|
140
|
-
// i18n-uses plugins.error.generic, plugins.error.api, plugins.error.host, plugins.error.kubeVersion, plugins.error.version, plugins.error.developerPkg, plugins.error.apiAnnotationMissing
|
|
167
|
+
// i18n-uses plugins.error.generic, plugins.error.api, plugins.error.host, plugins.error.kubeVersion, plugins.error.version, plugins.error.developerPkg, plugins.error.apiAnnotationMissing, plugins.error.primeOnly
|
|
141
168
|
|
|
142
169
|
/**
|
|
143
170
|
* Whether an extension should be loaded based on the metadata returned by the backend in the UIPlugins resource instance
|
|
@@ -148,6 +175,8 @@ function checkIncompatibility(currentVersion, requiredVersion, incompatibilityDa
|
|
|
148
175
|
* @returns String | Boolean
|
|
149
176
|
*/
|
|
150
177
|
export function shouldNotLoadPlugin(UIPluginResource, { rancherVersion, kubeVersion }, loadedPlugins) {
|
|
178
|
+
const isCurrRancherPrime = isRancherPrime();
|
|
179
|
+
|
|
151
180
|
const {
|
|
152
181
|
name, version, endpoint, compressedEndpoint
|
|
153
182
|
} = UIPluginResource;
|
|
@@ -171,6 +200,13 @@ export function shouldNotLoadPlugin(UIPluginResource, { rancherVersion, kubeVers
|
|
|
171
200
|
return 'plugins.error.api';
|
|
172
201
|
}
|
|
173
202
|
|
|
203
|
+
// Prime only
|
|
204
|
+
const primeOnlyAnnotation = UIPluginResource.metadata?.[UI_PLUGIN_CHART_ANNOTATIONS.PRIME_ONLY] === 'true';
|
|
205
|
+
|
|
206
|
+
if (!isCurrRancherPrime && primeOnlyAnnotation) {
|
|
207
|
+
return 'plugins.error.primeOnly';
|
|
208
|
+
}
|
|
209
|
+
|
|
174
210
|
// Host application
|
|
175
211
|
const requiredHost = UIPluginResource.metadata?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_HOST];
|
|
176
212
|
|
|
@@ -220,10 +256,13 @@ export function shouldNotLoadPlugin(UIPluginResource, { rancherVersion, kubeVers
|
|
|
220
256
|
* @returns Boolean | Object
|
|
221
257
|
*/
|
|
222
258
|
export function isSupportedChartVersion(versionData, returnObj = false) {
|
|
259
|
+
const isCurrRancherPrime = isRancherPrime();
|
|
260
|
+
|
|
223
261
|
const { version, rancherVersion, kubeVersion } = versionData;
|
|
224
262
|
const versionObj = {
|
|
225
263
|
...version, isVersionCompatible: true, versionIncompatibilityData: {}
|
|
226
264
|
};
|
|
265
|
+
|
|
227
266
|
const parsedRancherVersion = rancherVersion ? parseRancherVersion(rancherVersion) : '';
|
|
228
267
|
const parsedUiExtensionsApiVersion = semver.coerce(UI_EXTENSIONS_API_VERSION)?.version;
|
|
229
268
|
|
|
@@ -258,6 +297,11 @@ export function isSupportedChartVersion(versionData, returnObj = false) {
|
|
|
258
297
|
requiredVersion: version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_HOST],
|
|
259
298
|
incompatibilityData: EXTENSIONS_INCOMPATIBILITY_DATA.HOST,
|
|
260
299
|
},
|
|
300
|
+
{
|
|
301
|
+
currentVersion: isCurrRancherPrime,
|
|
302
|
+
requiredVersion: version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.PRIME_ONLY] === 'true',
|
|
303
|
+
incompatibilityData: EXTENSIONS_INCOMPATIBILITY_DATA.PRIME_ONLY,
|
|
304
|
+
}
|
|
261
305
|
];
|
|
262
306
|
|
|
263
307
|
for (const { currentVersion, requiredVersion, incompatibilityData } of checks) {
|
package/config/version.js
CHANGED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { DEVELOPER_LOAD_NAME_SUFFIX } from '@shell/core/extension-manager-impl';
|
|
2
|
+
|
|
3
|
+
// Mock external dependencies
|
|
4
|
+
jest.mock('@shell/store/type-map', () => ({ productsLoaded: jest.fn().mockReturnValue(true) }));
|
|
5
|
+
|
|
6
|
+
jest.mock('@shell/plugins/dashboard-store/model-loader', () => ({ clearModelCache: jest.fn() }));
|
|
7
|
+
|
|
8
|
+
jest.mock('@shell/config/uiplugins', () => ({ UI_PLUGIN_BASE_URL: '/api/v1/uiplugins' }));
|
|
9
|
+
|
|
10
|
+
jest.mock('@shell/plugins/clean-html', () => ({
|
|
11
|
+
addLinkInterceptor: jest.fn(),
|
|
12
|
+
removeLinkInterceptor: jest.fn(),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
// Mock the Plugin class
|
|
16
|
+
jest.mock('@shell/core/plugin', () => {
|
|
17
|
+
return {
|
|
18
|
+
Plugin: jest.fn().mockImplementation((id) => ({
|
|
19
|
+
id,
|
|
20
|
+
name: id,
|
|
21
|
+
types: {},
|
|
22
|
+
uiConfig: {},
|
|
23
|
+
l10n: {},
|
|
24
|
+
modelExtensions: {},
|
|
25
|
+
stores: [],
|
|
26
|
+
locales: [],
|
|
27
|
+
routes: [],
|
|
28
|
+
validators: {},
|
|
29
|
+
uninstallHooks: [],
|
|
30
|
+
productNames: [],
|
|
31
|
+
})),
|
|
32
|
+
EXT_IDS: {
|
|
33
|
+
MODELS: 'models',
|
|
34
|
+
MODEL_EXTENSION: 'model-extension'
|
|
35
|
+
},
|
|
36
|
+
ExtensionPoint: { EDIT_YAML: 'edit-yaml' }
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Mock PluginRoutes
|
|
41
|
+
jest.mock('@shell/core/plugin-routes', () => {
|
|
42
|
+
return { PluginRoutes: jest.fn().mockImplementation(() => ({ addRoutes: jest.fn() })) };
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('extension Manager', () => {
|
|
46
|
+
let mockStore;
|
|
47
|
+
let mockApp;
|
|
48
|
+
let context;
|
|
49
|
+
|
|
50
|
+
// These variables will be assigned the fresh functions inside beforeEach
|
|
51
|
+
let initExtensionManager;
|
|
52
|
+
let getExtensionManager;
|
|
53
|
+
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
// singleton instance for every test run, preventing mock store leaks.
|
|
56
|
+
jest.resetModules();
|
|
57
|
+
|
|
58
|
+
// Re-require the System Under Test (SUT)
|
|
59
|
+
const extensionManagerModule = require('../extension-manager-impl');
|
|
60
|
+
|
|
61
|
+
initExtensionManager = extensionManagerModule.initExtensionManager;
|
|
62
|
+
getExtensionManager = extensionManagerModule.getExtensionManager;
|
|
63
|
+
|
|
64
|
+
jest.clearAllMocks();
|
|
65
|
+
|
|
66
|
+
// Setup Mock Context
|
|
67
|
+
mockStore = {
|
|
68
|
+
getters: { 'i18n/t': jest.fn() },
|
|
69
|
+
dispatch: jest.fn(),
|
|
70
|
+
commit: jest.fn(),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
mockApp = { router: {} };
|
|
74
|
+
|
|
75
|
+
context = {
|
|
76
|
+
app: mockApp,
|
|
77
|
+
store: mockStore,
|
|
78
|
+
$axios: {},
|
|
79
|
+
redirect: jest.fn(),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Clean up DOM from previous tests
|
|
83
|
+
document.head.innerHTML = '';
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('singleton Pattern', () => {
|
|
87
|
+
it('initializes and returns the same instance', () => {
|
|
88
|
+
const instance1 = initExtensionManager(context);
|
|
89
|
+
const instance2 = getExtensionManager();
|
|
90
|
+
const instance3 = initExtensionManager(context);
|
|
91
|
+
|
|
92
|
+
expect(instance1).toBeDefined();
|
|
93
|
+
expect(instance1).toBe(instance2);
|
|
94
|
+
expect(instance1).toBe(instance3);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('registration (Dynamic)', () => {
|
|
99
|
+
it('registers and retrieves a dynamic component', () => {
|
|
100
|
+
const manager = initExtensionManager(context);
|
|
101
|
+
const mockFn = jest.fn();
|
|
102
|
+
|
|
103
|
+
manager.register('component', 'my-component', mockFn);
|
|
104
|
+
|
|
105
|
+
const retrieved = manager.getDynamic('component', 'my-component');
|
|
106
|
+
|
|
107
|
+
expect(retrieved).toBe(mockFn);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('unregisters a dynamic component', () => {
|
|
111
|
+
const manager = initExtensionManager(context);
|
|
112
|
+
const mockFn = jest.fn();
|
|
113
|
+
|
|
114
|
+
manager.register('component', 'my-component', mockFn);
|
|
115
|
+
manager.unregister('component', 'my-component');
|
|
116
|
+
|
|
117
|
+
const retrieved = manager.getDynamic('component', 'my-component');
|
|
118
|
+
|
|
119
|
+
expect(retrieved).toBeUndefined();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('loadPluginAsync (URL Generation)', () => {
|
|
124
|
+
let manager;
|
|
125
|
+
|
|
126
|
+
beforeEach(() => {
|
|
127
|
+
manager = initExtensionManager(context);
|
|
128
|
+
// Mock the internal loadAsync so we only test URL generation here
|
|
129
|
+
jest.spyOn(manager, 'loadAsync').mockImplementation().mockResolvedValue();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('generates correct URL for standard plugin', async() => {
|
|
133
|
+
const pluginData = { name: 'elemental', version: '1.0.0' };
|
|
134
|
+
const expectedId = 'elemental-1.0.0';
|
|
135
|
+
const expectedUrl = `/api/v1/uiplugins/elemental/1.0.0/plugin/elemental-1.0.0.umd.min.js`;
|
|
136
|
+
|
|
137
|
+
await manager.loadPluginAsync(pluginData);
|
|
138
|
+
|
|
139
|
+
expect(manager.loadAsync).toHaveBeenCalledWith(expectedId, expectedUrl);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('handles "direct" metadata plugins', async() => {
|
|
143
|
+
const pluginData = {
|
|
144
|
+
name: 'direct-plugin',
|
|
145
|
+
version: '1.0.0',
|
|
146
|
+
endpoint: 'http://localhost:8000/plugin.js',
|
|
147
|
+
metadata: { direct: 'true' }
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
await manager.loadPluginAsync(pluginData);
|
|
151
|
+
|
|
152
|
+
expect(manager.loadAsync).toHaveBeenCalledWith('direct-plugin-1.0.0', 'http://localhost:8000/plugin.js');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('removes developer suffix from ID but keeps it for internal logic', async() => {
|
|
156
|
+
const pluginData = {
|
|
157
|
+
name: `my-plugin${ DEVELOPER_LOAD_NAME_SUFFIX }`,
|
|
158
|
+
version: `1.0.0`
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
await manager.loadPluginAsync(pluginData);
|
|
162
|
+
|
|
163
|
+
// Expected ID passed to loadAsync should NOT have the suffix
|
|
164
|
+
const expectedIdWithoutSuffix = 'my-plugin-1.0.0';
|
|
165
|
+
|
|
166
|
+
expect(manager.loadAsync).toHaveBeenCalledWith(
|
|
167
|
+
expectedIdWithoutSuffix,
|
|
168
|
+
expect.any(String)
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('loadAsync (Script Injection)', () => {
|
|
174
|
+
let manager;
|
|
175
|
+
|
|
176
|
+
beforeEach(() => {
|
|
177
|
+
manager = initExtensionManager(context);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('resolves immediately if element already exists', async() => {
|
|
181
|
+
const id = 'existing-plugin';
|
|
182
|
+
const script = document.createElement('script');
|
|
183
|
+
|
|
184
|
+
script.id = id;
|
|
185
|
+
document.body.appendChild(script);
|
|
186
|
+
|
|
187
|
+
await expect(manager.loadAsync(id, 'url.js')).resolves.toBeUndefined();
|
|
188
|
+
|
|
189
|
+
document.body.removeChild(script);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('injects script tag and initializes plugin on load', async() => {
|
|
193
|
+
const pluginId = 'test-plugin';
|
|
194
|
+
const pluginUrl = 'http://test.com/plugin.js';
|
|
195
|
+
|
|
196
|
+
// Mock the window object to simulate the plugin loading into global scope
|
|
197
|
+
const mockPluginInit = jest.fn();
|
|
198
|
+
|
|
199
|
+
window[pluginId] = { default: mockPluginInit };
|
|
200
|
+
|
|
201
|
+
// Start the load
|
|
202
|
+
const loadPromise = manager.loadAsync(pluginId, pluginUrl);
|
|
203
|
+
|
|
204
|
+
// Find the injected script tag in the DOM
|
|
205
|
+
const script = document.head.querySelector(`script[id="${ pluginId }"]`);
|
|
206
|
+
|
|
207
|
+
expect(script).toBeTruthy();
|
|
208
|
+
expect(script.src).toBe(pluginUrl);
|
|
209
|
+
|
|
210
|
+
// Manually trigger the onload event
|
|
211
|
+
script.onload();
|
|
212
|
+
|
|
213
|
+
// Await the promise
|
|
214
|
+
await loadPromise;
|
|
215
|
+
|
|
216
|
+
// Assertions
|
|
217
|
+
expect(mockPluginInit).toHaveBeenCalledWith(expect.objectContaining({ id: pluginId }), expect.objectContaining({ ...context }));
|
|
218
|
+
expect(mockStore.dispatch).toHaveBeenCalledWith('uiplugins/addPlugin', expect.objectContaining({ id: pluginId }));
|
|
219
|
+
|
|
220
|
+
// Cleanup
|
|
221
|
+
delete window[pluginId];
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('rejects if script load fails', async() => {
|
|
225
|
+
const pluginId = 'fail-plugin';
|
|
226
|
+
const loadPromise = manager.loadAsync(pluginId, 'bad-url.js');
|
|
227
|
+
|
|
228
|
+
const script = document.head.querySelector(`script[id="${ pluginId }"]`);
|
|
229
|
+
|
|
230
|
+
// Trigger error
|
|
231
|
+
script.onerror({ target: { src: 'bad-url.js' } });
|
|
232
|
+
|
|
233
|
+
await expect(loadPromise).rejects.toThrow('Failed to load script');
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
@@ -6,6 +6,8 @@ import { UI_PLUGIN_BASE_URL } from '@shell/config/uiplugins';
|
|
|
6
6
|
import { ExtensionPoint } from './types';
|
|
7
7
|
import { addLinkInterceptor, removeLinkInterceptor } from '@shell/plugins/clean-html';
|
|
8
8
|
|
|
9
|
+
export const DEVELOPER_LOAD_NAME_SUFFIX = '-developer-load';
|
|
10
|
+
|
|
9
11
|
let extensionManagerInstance;
|
|
10
12
|
|
|
11
13
|
const createExtensionManager = (context) => {
|
|
@@ -33,13 +35,13 @@ const createExtensionManager = (context) => {
|
|
|
33
35
|
/**
|
|
34
36
|
* When an extension adds a model extension, it provides the class - we will instantiate that class and store and use that
|
|
35
37
|
*/
|
|
36
|
-
function instantiateModelExtension($
|
|
38
|
+
function instantiateModelExtension($extension, clz) {
|
|
37
39
|
const context = {
|
|
38
40
|
dispatch: store.dispatch,
|
|
39
41
|
getters: store.getters,
|
|
40
42
|
t: store.getters['i18n/t'],
|
|
41
43
|
$axios,
|
|
42
|
-
$
|
|
44
|
+
$extension,
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
return new clz(context);
|
|
@@ -63,9 +65,18 @@ const createExtensionManager = (context) => {
|
|
|
63
65
|
// Load a plugin from a UI package
|
|
64
66
|
loadPluginAsync(plugin) {
|
|
65
67
|
const { name, version } = plugin;
|
|
66
|
-
|
|
68
|
+
let id = `${ name }-${ version }`;
|
|
67
69
|
let url;
|
|
68
70
|
|
|
71
|
+
// for a developer load, we need to remove the suffix applied
|
|
72
|
+
// otherwise the extension won't load correctly
|
|
73
|
+
// but with this at least we won't hit developer loaded cards find charts
|
|
74
|
+
// when they aren't supposed to
|
|
75
|
+
if (id.includes(DEVELOPER_LOAD_NAME_SUFFIX)) {
|
|
76
|
+
id = id.replace(DEVELOPER_LOAD_NAME_SUFFIX, '');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// this is where a developer load hits (direct=true, developer=true)
|
|
69
80
|
if (plugin?.metadata?.direct === 'true') {
|
|
70
81
|
url = plugin.endpoint;
|
|
71
82
|
} else {
|
|
@@ -455,7 +466,13 @@ const createExtensionManager = (context) => {
|
|
|
455
466
|
try {
|
|
456
467
|
const provisioner = context.$extension.getDynamic('provisioner', name);
|
|
457
468
|
|
|
458
|
-
|
|
469
|
+
const defaults = {
|
|
470
|
+
isCreate: false,
|
|
471
|
+
isEdit: false,
|
|
472
|
+
isView: false
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
return new provisioner({ ...defaults, ...context });
|
|
459
476
|
} catch (e) {
|
|
460
477
|
console.error('Error loading provisioner(s) from extensions', e); // eslint-disable-line no-console
|
|
461
478
|
}
|
package/core/plugin-helpers.ts
CHANGED
|
@@ -147,8 +147,8 @@ export function getApplicableExtensionEnhancements<T>(
|
|
|
147
147
|
const extensionEnhancements: T[] = [];
|
|
148
148
|
|
|
149
149
|
// gate it so that we prevent errors on older versions of dashboard
|
|
150
|
-
if (pluginCtx.$
|
|
151
|
-
const actions = pluginCtx.$
|
|
150
|
+
if (pluginCtx.$extension?.getUIConfig) {
|
|
151
|
+
const actions = pluginCtx.$extension.getUIConfig(actionType, uiArea);
|
|
152
152
|
|
|
153
153
|
actions.forEach((action: any, i: number) => {
|
|
154
154
|
if (checkExtensionRouteBinding(currRoute, action.locationConfig, context || {})) {
|
|
@@ -195,6 +195,8 @@ export function getApplicableExtensionEnhancements<T>(
|
|
|
195
195
|
if (i < keyboardCombo.length - 1) {
|
|
196
196
|
if (key === 'meta') {
|
|
197
197
|
key = '\u2318';
|
|
198
|
+
} else if (isMac && key === 'alt') {
|
|
199
|
+
key = '⌥';
|
|
198
200
|
} else {
|
|
199
201
|
key = ucFirst(key);
|
|
200
202
|
}
|
package/core/plugins-loader.js
CHANGED
|
@@ -13,10 +13,10 @@ export default function({
|
|
|
13
13
|
store,
|
|
14
14
|
$axios,
|
|
15
15
|
redirect,
|
|
16
|
-
$
|
|
16
|
+
$extension,
|
|
17
17
|
}, inject) {
|
|
18
18
|
if (dynamicLoader) {
|
|
19
|
-
dynamicLoader.default($
|
|
19
|
+
dynamicLoader.default($extension);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// The libraries we build have Vue externalised, so we need to expose Vue as a global for
|