@rancher/shell 3.0.11 → 3.0.12-rc.2
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/images/providers/entraid-black.svg +4 -0
- package/assets/images/providers/entraid.svg +9 -0
- package/assets/images/vendor/entraid.svg +9 -0
- package/assets/styles/app.scss +0 -1
- package/assets/styles/base/_mixins.scss +31 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/themes/_modern.scss +6 -5
- package/assets/translations/en-us.yaml +24 -21
- package/assets/translations/zh-hans.yaml +4 -11
- package/chart/__tests__/S3.test.ts +10 -3
- package/components/CountBox.vue +20 -0
- package/components/CreateDriver.vue +0 -12
- package/components/DetailText.vue +12 -3
- package/components/EmptyProductPage.vue +76 -0
- package/components/Resource/Detail/CopyToClipboard.vue +1 -2
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +9 -3
- package/components/Resource/Detail/TitleBar/__tests__/__snapshots__/index.test.ts.snap +31 -0
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +45 -1
- package/components/Resource/Detail/TitleBar/index.vue +1 -1
- package/components/Resource/Detail/ViewOptions/__tests__/__snapshots__/index.test.ts.snap +9 -0
- package/components/Resource/Detail/ViewOptions/__tests__/index.test.ts +62 -0
- package/components/Resource/Detail/ViewOptions/index.vue +2 -1
- package/components/ResourceList/Masthead.vue +25 -2
- package/components/SelectIconGrid.vue +5 -0
- package/components/SideNav.vue +13 -0
- package/components/__tests__/CountBox.test.ts +72 -0
- package/components/__tests__/DetailText.test.ts +113 -0
- package/components/__tests__/PromptModal.test.ts +2 -0
- package/components/fleet/FleetClusterTargets/index.vue +18 -1
- package/components/fleet/FleetClusters.vue +1 -0
- package/components/fleet/__tests__/FleetClusters.test.ts +71 -0
- package/components/form/InputWithSelect.vue +18 -10
- package/components/form/KeyValue.vue +17 -1
- package/components/form/LabeledSelect.vue +82 -24
- package/components/form/NodeScheduling.vue +17 -3
- package/components/form/PrivateRegistry.vue +69 -0
- package/components/form/Select.vue +73 -56
- package/components/form/ServiceNameSelect.vue +13 -11
- package/components/form/__tests__/KeyValue.test.ts +66 -0
- package/components/form/__tests__/NodeScheduling.test.ts +9 -0
- package/components/form/__tests__/PrivateRegistry.test.ts +133 -0
- package/components/form/labeled-select-utils/useLabeledSelectPagination.ts +138 -0
- package/components/formatter/WorkloadHealthScale.vue +3 -1
- package/components/nav/Group.vue +33 -9
- package/components/nav/Header.vue +56 -10
- package/components/nav/NotificationCenter/Notification.vue +4 -1
- package/components/nav/NotificationCenter/NotificationHeader.vue +20 -8
- package/components/nav/NotificationCenter/__tests__/NotificationHeader.test.ts +80 -0
- package/components/nav/TopLevelMenu.vue +15 -1
- package/components/nav/Type.vue +8 -7
- package/components/nav/WindowManager/index.vue +2 -1
- package/components/nav/WorkspaceSwitcher.vue +13 -0
- package/components/nav/__tests__/Group.test.ts +67 -0
- package/components/nav/__tests__/Header.test.ts +235 -0
- package/components/nav/__tests__/Type.test.ts +20 -3
- package/components/templates/default.vue +34 -4
- package/components/templates/home.vue +12 -25
- package/components/templates/plain.vue +13 -26
- package/composables/useLabeledFormElement.ts +10 -2
- package/composables/useLabeledSelect.ts +60 -0
- package/composables/useUserRetentionValidation.ts +1 -49
- package/config/cookies.js +0 -1
- package/config/labels-annotations.js +1 -0
- package/config/pagination-table-headers.js +8 -1
- package/config/product/apps.js +2 -1
- package/config/product/auth.js +1 -0
- package/config/product/backup.js +1 -0
- package/config/product/compliance.js +1 -1
- package/config/product/explorer.js +25 -6
- package/config/product/fleet.js +1 -0
- package/config/product/gatekeeper.js +1 -0
- package/config/product/istio.js +1 -0
- package/config/product/logging.js +1 -0
- package/config/product/longhorn.js +2 -1
- package/config/product/manager.js +1 -0
- package/config/product/monitoring.js +1 -0
- package/config/product/navlinks.js +1 -0
- package/config/product/neuvector.js +2 -1
- package/config/product/settings.js +1 -0
- package/config/product/uiplugins.js +1 -0
- package/config/query-params.js +1 -0
- package/config/router/routes.js +0 -8
- package/core/__tests__/plugin-products-helpers.test.ts +454 -0
- package/core/__tests__/plugin-products.test.ts +3810 -0
- package/core/extension-manager-impl.js +30 -1
- package/core/plugin-products-base.ts +392 -0
- package/core/plugin-products-extending.ts +44 -0
- package/core/plugin-products-helpers.ts +263 -0
- package/core/plugin-products-top-level.ts +66 -0
- package/core/plugin-products-type-guards.ts +33 -0
- package/core/plugin-products.ts +50 -0
- package/core/plugin-types.ts +237 -0
- package/core/plugin.ts +45 -10
- package/core/productDebugger.js +48 -0
- package/core/types.ts +97 -11
- package/detail/__tests__/__snapshots__/fleet.cattle.io.bundle.test.ts.snap +52 -0
- package/detail/__tests__/fleet.cattle.io.bundle.test.ts +171 -0
- package/detail/__tests__/management.cattle.io.fleetworkspace.test.ts +128 -0
- package/detail/fleet.cattle.io.bundle.vue +21 -34
- package/detail/management.cattle.io.fleetworkspace.vue +49 -0
- package/dialog/ExtensionCatalogInstallDialog.vue +1 -1
- package/dialog/InstallExtensionDialog.vue +6 -27
- package/dialog/UninstallExistingExtensionDialog.vue +141 -0
- package/dialog/UninstallExtensionDialog.vue +4 -26
- package/dialog/__tests__/UninstallExistingExtensionDialog.test.ts +114 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +9 -0
- package/edit/__tests__/kontainerDriver.test.ts +0 -13
- package/edit/__tests__/nodeDriver.test.ts +5 -11
- package/edit/__tests__/resources.cattle.io.restore.test.ts +9 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
- package/edit/auth/__tests__/oidc.test.ts +54 -0
- package/edit/auth/azuread.vue +1 -1
- package/edit/auth/oidc.vue +8 -0
- package/edit/kontainerDriver.vue +1 -2
- package/edit/nodeDriver.vue +0 -2
- package/edit/provisioning.cattle.io.cluster/AgentEnv.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/AgentEnv.test.ts +25 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Ingress.test.ts +176 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +70 -99
- package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +6 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +7 -2
- package/initialize/App.vue +29 -2
- package/initialize/install-plugins.js +0 -2
- package/list/__tests__/management.cattle.io.feature.test.ts +105 -0
- package/list/catalog.cattle.io.app.vue +25 -5
- package/list/management.cattle.io.feature.vue +1 -1
- package/list/management.cattle.io.fleetworkspace.vue +8 -0
- package/list/provisioning.cattle.io.cluster.vue +0 -1
- package/list/workload.vue +11 -4
- package/machine-config/amazonec2.vue +1 -0
- package/mixins/chart.js +40 -9
- package/mixins/resource-fetch.js +12 -3
- package/models/__tests__/catalog.cattle.io.app.test.ts +15 -1
- package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +84 -0
- package/models/__tests__/chart.test.ts +99 -6
- package/models/__tests__/management.cattle.io.feature.test.ts +131 -0
- package/models/__tests__/monitoring.coreos.com.alertmanagerconfig.test.ts +98 -0
- package/models/catalog.cattle.io.app.js +21 -17
- package/models/catalog.cattle.io.clusterrepo.js +39 -11
- package/models/chart.js +33 -19
- package/models/fleet-application.js +1 -1
- package/models/fleet.cattle.io.bundle.js +1 -1
- package/models/kontainerdriver.js +11 -0
- package/models/management.cattle.io.authconfig.js +5 -1
- package/models/management.cattle.io.cluster.js +0 -53
- package/models/management.cattle.io.feature.js +3 -3
- package/models/management.cattle.io.kontainerdriver.js +1 -26
- package/models/monitoring.coreos.com.alertmanagerconfig.js +31 -17
- package/models/nodedriver.js +7 -0
- package/models/pod.js +18 -0
- package/models/workload.js +20 -2
- package/package.json +13 -13
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +0 -1
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +189 -0
- package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +55 -0
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +53 -0
- package/pages/c/_cluster/apps/charts/chart.vue +217 -33
- package/pages/c/_cluster/apps/charts/index.vue +2 -2
- package/pages/c/_cluster/apps/charts/install.vue +8 -3
- package/pages/c/_cluster/auth/user.retention/index.vue +55 -22
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -7
- package/pages/c/_cluster/settings/brand.vue +4 -4
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +39 -2
- package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +61 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +246 -23
- package/pages/c/_cluster/uiplugins/index.vue +166 -62
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +1 -0
- package/plugins/dashboard-store/actions.js +3 -2
- package/plugins/dashboard-store/resource-class.js +62 -6
- package/plugins/plugin.js +16 -0
- package/plugins/steve/steve-pagination-utils.ts +7 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +205 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +82 -4
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +1 -1
- package/scripts/test-plugins-build.sh +5 -2
- package/scripts/typegen.sh +13 -1
- package/server/server-middleware.js +2 -2
- package/static/humans.txt +1 -0
- package/static/robots.txt +34 -0
- package/static/welcome-cow.svg +18 -0
- package/store/__tests__/catalog.test.ts +161 -11
- package/store/__tests__/type-map.test.ts +84 -24
- package/store/auth.js +0 -3
- package/store/catalog.js +60 -8
- package/store/type-map.js +42 -3
- package/tsconfig.paths.json +1 -0
- package/types/resources/pod.ts +18 -0
- package/types/shell/index.d.ts +8539 -2938
- package/types/store/dashboard-store.types.ts +5 -0
- package/types/store/pagination.types.ts +6 -0
- package/utils/__tests__/git.test.ts +270 -0
- package/utils/__tests__/inactivity.test.ts +316 -0
- package/utils/__tests__/object.test.ts +77 -0
- package/utils/__tests__/time.test.ts +14 -1
- package/utils/__tests__/url.test.ts +246 -0
- package/utils/axios.js +1 -4
- package/utils/dynamic-importer.js +3 -2
- package/utils/object.js +33 -2
- package/utils/pagination-utils.ts +1 -1
- package/utils/time.ts +5 -0
- package/utils/uiplugins.ts +12 -16
- package/utils/validators/__tests__/private-registry.test.ts +76 -0
- package/utils/validators/private-registry.ts +28 -0
- package/vue.config.js +0 -9
- package/assets/images/providers/azuread-black.svg +0 -22
- package/assets/images/providers/azuread.svg +0 -25
- package/assets/images/vendor/azuread.svg +0 -18
- package/assets/styles/fonts/_dots.scss +0 -18
- package/components/EmberPage.vue +0 -622
- package/components/EmberPageView.vue +0 -39
- package/components/form/labeled-select-utils/labeled-select-pagination.ts +0 -116
- package/mixins/labeled-form-element.ts +0 -225
- package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -28
- package/pages/c/_cluster/manager/pages/_page.vue +0 -22
- package/pages/c/_cluster/mcapps/pages/_page.vue +0 -22
- package/plugins/ember-cookie.js +0 -17
- package/utils/ember-page.js +0 -30
|
@@ -5,7 +5,8 @@ import { mapPref, PLUGIN_DEVELOPER } from '@shell/store/prefs';
|
|
|
5
5
|
import { sortBy } from '@shell/utils/sort';
|
|
6
6
|
import genericPluginSvg from '~shell/assets/images/generic-plugin.svg';
|
|
7
7
|
import { allHash } from '@shell/utils/promise';
|
|
8
|
-
import { CATALOG, UI_PLUGIN, MANAGEMENT
|
|
8
|
+
import { CATALOG, UI_PLUGIN, MANAGEMENT } from '@shell/config/types';
|
|
9
|
+
import { isMissingDate } from '@shell/utils/time';
|
|
9
10
|
import { SETTING } from '@shell/config/settings';
|
|
10
11
|
import { fetchOrCreateSetting } from '@shell/utils/settings';
|
|
11
12
|
import { getVersionData, isRancherPrime } from '@shell/config/version';
|
|
@@ -101,6 +102,14 @@ export default {
|
|
|
101
102
|
hash.helmOps = this.$store.dispatch('management/findAll', { type: CATALOG.OPERATION });
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
// Load apps in UI_PLUGIN_NAMESPACE to determine which repo an extension was installed from
|
|
106
|
+
if (this.$store.getters['management/schemaFor'](CATALOG.APP)) {
|
|
107
|
+
hash.apps = this.$store.dispatch('management/findAll', {
|
|
108
|
+
type: CATALOG.APP,
|
|
109
|
+
opt: { namespaced: UI_PLUGIN_NAMESPACE }
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
104
113
|
if (this.$store.getters['management/schemaFor'](CATALOG.CLUSTER_REPO)) {
|
|
105
114
|
hash.repos = this.$store.dispatch('management/findAll', { type: CATALOG.CLUSTER_REPO }, { force: true });
|
|
106
115
|
}
|
|
@@ -127,6 +136,12 @@ export default {
|
|
|
127
136
|
...mapGetters({ uiErrors: 'uiplugins/errors' }),
|
|
128
137
|
...mapGetters({ theme: 'prefs/theme' }),
|
|
129
138
|
|
|
139
|
+
// Computed to reactively update when new apps are installed
|
|
140
|
+
apps() {
|
|
141
|
+
return this.$store.getters['management/all'](CATALOG.APP)
|
|
142
|
+
.filter((app) => app.metadata?.namespace === UI_PLUGIN_NAMESPACE);
|
|
143
|
+
},
|
|
144
|
+
|
|
130
145
|
charts() {
|
|
131
146
|
const c = this.$store.getters['catalog/rawCharts'];
|
|
132
147
|
|
|
@@ -313,8 +328,8 @@ export default {
|
|
|
313
328
|
}
|
|
314
329
|
}
|
|
315
330
|
|
|
316
|
-
if (this.installing[item.
|
|
317
|
-
item.installing = this.installing[item.
|
|
331
|
+
if (this.installing[item.id]) {
|
|
332
|
+
item.installing = this.installing[item.id];
|
|
318
333
|
}
|
|
319
334
|
|
|
320
335
|
return item;
|
|
@@ -359,15 +374,23 @@ export default {
|
|
|
359
374
|
|
|
360
375
|
// Go through the CRs for the plugins and wire them into the catalog
|
|
361
376
|
this.plugins.forEach((p) => {
|
|
362
|
-
|
|
377
|
+
let chart;
|
|
378
|
+
const app = this.apps.find((a) => a.metadata.name === p.name && a.metadata.namespace === UI_PLUGIN_NAMESPACE);
|
|
379
|
+
const originalRepoName = app?.spec?.chart?.metadata?.annotations?.[CATALOG_ANNOTATIONS.SOURCE_REPO_NAME] || app?.metadata?.labels?.[CATALOG_ANNOTATIONS.CLUSTER_REPO_NAME];
|
|
363
380
|
|
|
381
|
+
// Find the chart from the original repo to avoid picking a wrong chart with the same name
|
|
382
|
+
if (originalRepoName) {
|
|
383
|
+
chart = all.find((c) => c.name === p.name && c.chart?.repoName === originalRepoName);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// If original repo was removed, don't fall back to another repo's chart (would break Available tab)
|
|
364
387
|
if (chart) {
|
|
365
388
|
chart.installed = true;
|
|
366
389
|
chart.uiplugin = p;
|
|
367
390
|
chart.installedVersion = p.version;
|
|
368
391
|
|
|
369
392
|
// Can't do this here
|
|
370
|
-
chart.installing = this.installing[chart.
|
|
393
|
+
chart.installing = this.installing[chart.id];
|
|
371
394
|
|
|
372
395
|
// Check for upgrade
|
|
373
396
|
const latestInstallableVersion = chart.installableVersions?.[0];
|
|
@@ -385,20 +408,34 @@ export default {
|
|
|
385
408
|
chart.upgrade = getPluginChartVersionLabel(latestInstallableVersion);
|
|
386
409
|
}
|
|
387
410
|
} else {
|
|
388
|
-
// No chart
|
|
411
|
+
// No chart available - original repo was removed or developer-loaded plugin
|
|
412
|
+
const appChartMeta = app?.spec?.chart?.metadata;
|
|
413
|
+
const appAnnotations = appChartMeta?.annotations || {};
|
|
414
|
+
let originalRepoDisplayName = null;
|
|
415
|
+
|
|
416
|
+
if (originalRepoName) {
|
|
417
|
+
originalRepoDisplayName = this.$store.getters['i18n/withFallback'](`catalog.repo.name."${ originalRepoName }"`, null, originalRepoName);
|
|
418
|
+
}
|
|
419
|
+
|
|
389
420
|
const item = {
|
|
390
|
-
name:
|
|
391
|
-
label:
|
|
392
|
-
description:
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
421
|
+
name: p.name,
|
|
422
|
+
label: appAnnotations[UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME] || appChartMeta?.name || p.name,
|
|
423
|
+
description: appChartMeta?.description || p.description || '-',
|
|
424
|
+
icon: appChartMeta?.icon || appAnnotations['catalog.cattle.io/ui-icon'],
|
|
425
|
+
id: `${ p.name }-${ p.version }`,
|
|
426
|
+
versions: [],
|
|
427
|
+
displayVersion: p.version,
|
|
428
|
+
displayVersionLabel: p.version || '-',
|
|
429
|
+
isDeveloper: p.isDeveloper,
|
|
430
|
+
installed: true,
|
|
431
|
+
installedVersion: p.version,
|
|
432
|
+
installing: false,
|
|
433
|
+
builtin: false,
|
|
434
|
+
uiplugin: p,
|
|
435
|
+
primeOnly: appAnnotations[CATALOG_ANNOTATIONS.PRIME_ONLY] === 'true',
|
|
436
|
+
experimental: appAnnotations[CATALOG_ANNOTATIONS.EXPERIMENTAL] === 'true',
|
|
437
|
+
certified: appAnnotations[CATALOG_ANNOTATIONS.CERTIFIED] === CATALOG_ANNOTATIONS._RANCHER,
|
|
438
|
+
originalRepoNameDisplay: originalRepoDisplayName,
|
|
402
439
|
};
|
|
403
440
|
|
|
404
441
|
all.push(item);
|
|
@@ -422,7 +459,7 @@ export default {
|
|
|
422
459
|
|
|
423
460
|
// Merge in the plugin load errors from help ops
|
|
424
461
|
Object.keys(this.errors).forEach((e) => {
|
|
425
|
-
const chart = all.find((c) => c.
|
|
462
|
+
const chart = all.find((c) => c.id === e);
|
|
426
463
|
|
|
427
464
|
if (chart) {
|
|
428
465
|
chart.helmError = !!this.errors[e];
|
|
@@ -456,28 +493,50 @@ export default {
|
|
|
456
493
|
const op = pluginOps.find((o) => o.status?.releaseName === plugin.name);
|
|
457
494
|
|
|
458
495
|
if (op) {
|
|
496
|
+
const allWithSameName = this.available.filter((p) => p.name === plugin.name);
|
|
497
|
+
let targetPluginId;
|
|
498
|
+
|
|
499
|
+
// When multiple plugins share the same name (from different repositories),
|
|
500
|
+
// a single helm operation will trigger updates. We need to correctly identify
|
|
501
|
+
// the specific plugin that is either currently being installed/updated or is already installed
|
|
502
|
+
// so we don't accidentally mark all identically named plugins as "installing".
|
|
503
|
+
const installingPlugin = allWithSameName.find((p) => this.installing[p.id]);
|
|
504
|
+
const installedPlugin = allWithSameName.find((p) => p.installed);
|
|
505
|
+
|
|
506
|
+
if (installingPlugin) {
|
|
507
|
+
targetPluginId = installingPlugin.id;
|
|
508
|
+
} else if (installedPlugin) {
|
|
509
|
+
targetPluginId = installedPlugin.id;
|
|
510
|
+
} else {
|
|
511
|
+
targetPluginId = allWithSameName[0]?.id;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (plugin.id !== targetPluginId) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
|
|
459
518
|
const active = op.metadata.state?.transitioning;
|
|
460
519
|
const error = op.metadata.state?.error;
|
|
461
520
|
|
|
462
|
-
this.errors[plugin.
|
|
521
|
+
this.errors[plugin.id] = error;
|
|
463
522
|
|
|
464
523
|
if (active) {
|
|
465
524
|
// Can use the status directly, apart from upgrade, which maps to update
|
|
466
525
|
const status = op.status.action;
|
|
467
526
|
|
|
468
|
-
if (status === 'upgrade' && this.installing[plugin.
|
|
527
|
+
if (status === 'upgrade' && this.installing[plugin.id] === 'downgrade') {
|
|
469
528
|
// Helm op is an upgrade, but we initiated a downgrade, so keep the 'downgrade' status
|
|
470
529
|
} else {
|
|
471
|
-
this.updatePluginInstallStatus(plugin.
|
|
530
|
+
this.updatePluginInstallStatus(plugin.id, status);
|
|
472
531
|
}
|
|
473
532
|
} else if (op.status.action === 'uninstall') {
|
|
474
533
|
// Uninstall has finished
|
|
475
|
-
this.updatePluginInstallStatus(plugin.
|
|
534
|
+
this.updatePluginInstallStatus(plugin.id, false);
|
|
476
535
|
} else if (error) {
|
|
477
|
-
this.updatePluginInstallStatus(plugin.
|
|
536
|
+
this.updatePluginInstallStatus(plugin.id, false);
|
|
478
537
|
}
|
|
479
538
|
} else {
|
|
480
|
-
this.updatePluginInstallStatus(plugin.
|
|
539
|
+
this.updatePluginInstallStatus(plugin.id, false);
|
|
481
540
|
}
|
|
482
541
|
});
|
|
483
542
|
},
|
|
@@ -503,7 +562,11 @@ export default {
|
|
|
503
562
|
changes++;
|
|
504
563
|
}
|
|
505
564
|
|
|
506
|
-
this.
|
|
565
|
+
(this.available || []).forEach((c) => {
|
|
566
|
+
if (c.name === plugin.name) {
|
|
567
|
+
this.updatePluginInstallStatus(c.id, false);
|
|
568
|
+
}
|
|
569
|
+
});
|
|
507
570
|
}
|
|
508
571
|
});
|
|
509
572
|
|
|
@@ -596,6 +659,35 @@ export default {
|
|
|
596
659
|
ev?.preventDefault?.();
|
|
597
660
|
ev?.stopPropagation?.();
|
|
598
661
|
|
|
662
|
+
// Check if a plugin with the same name is already installed from a different repo
|
|
663
|
+
if (action === 'install') {
|
|
664
|
+
const installedPlugin = this.available.find((p) => p.name === plugin.name && p.installed);
|
|
665
|
+
const isInstalledFromDifferentSource = !!installedPlugin && installedPlugin.id !== plugin.id;
|
|
666
|
+
|
|
667
|
+
if (isInstalledFromDifferentSource) {
|
|
668
|
+
// Show a different dialog that prompts the user to uninstall the existing version first
|
|
669
|
+
this.$store.dispatch('management/promptModal', {
|
|
670
|
+
component: 'UninstallExistingExtensionDialog',
|
|
671
|
+
testId: 'uninstall-existing-extension-modal',
|
|
672
|
+
returnFocusSelector: `[data-testid="extension-card-${ action }-btn-${ plugin?.name }"]`,
|
|
673
|
+
returnFocusFirstIterableNodeSelector: '#extensions-main-page',
|
|
674
|
+
componentProps: {
|
|
675
|
+
installedPlugin,
|
|
676
|
+
updateStatus: (pluginId, type) => {
|
|
677
|
+
this.updatePluginInstallStatus(pluginId, type);
|
|
678
|
+
},
|
|
679
|
+
closed: (res) => {
|
|
680
|
+
if (res?.uninstalled) {
|
|
681
|
+
this.didUninstall(res.plugin);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
599
691
|
this.$store.dispatch('management/promptModal', {
|
|
600
692
|
component: 'InstallExtensionDialog',
|
|
601
693
|
testId: 'install-extension-modal',
|
|
@@ -605,8 +697,8 @@ export default {
|
|
|
605
697
|
plugin,
|
|
606
698
|
action,
|
|
607
699
|
initialVersion,
|
|
608
|
-
updateStatus: (
|
|
609
|
-
this.updatePluginInstallStatus(
|
|
700
|
+
updateStatus: (pluginId, type) => {
|
|
701
|
+
this.updatePluginInstallStatus(pluginId, type);
|
|
610
702
|
},
|
|
611
703
|
closed: (res) => {
|
|
612
704
|
this.didInstall(res);
|
|
@@ -627,8 +719,8 @@ export default {
|
|
|
627
719
|
returnFocusFirstIterableNodeSelector: '#extensions-main-page',
|
|
628
720
|
componentProps: {
|
|
629
721
|
plugin,
|
|
630
|
-
updateStatus: (
|
|
631
|
-
this.updatePluginInstallStatus(
|
|
722
|
+
updateStatus: (pluginId, type) => {
|
|
723
|
+
this.updatePluginInstallStatus(pluginId, type);
|
|
632
724
|
},
|
|
633
725
|
closed: (res) => {
|
|
634
726
|
this.didUninstall(res);
|
|
@@ -639,7 +731,7 @@ export default {
|
|
|
639
731
|
|
|
640
732
|
didUninstall(plugin) {
|
|
641
733
|
if (plugin) {
|
|
642
|
-
this.updatePluginInstallStatus(plugin.
|
|
734
|
+
this.updatePluginInstallStatus(plugin.id, 'uninstall');
|
|
643
735
|
|
|
644
736
|
if (plugin.catalog) {
|
|
645
737
|
this.refreshCharts();
|
|
@@ -666,8 +758,8 @@ export default {
|
|
|
666
758
|
this.$refs.infoPanel.show({ ...plugin, tags });
|
|
667
759
|
},
|
|
668
760
|
|
|
669
|
-
updatePluginInstallStatus(
|
|
670
|
-
this.installing[
|
|
761
|
+
updatePluginInstallStatus(id, status) {
|
|
762
|
+
this.installing[id] = status;
|
|
671
763
|
},
|
|
672
764
|
|
|
673
765
|
setMenu(event) {
|
|
@@ -805,18 +897,12 @@ export default {
|
|
|
805
897
|
label: plugin.displayVersionLabel,
|
|
806
898
|
}];
|
|
807
899
|
|
|
808
|
-
if (plugin.created) {
|
|
809
|
-
|
|
810
|
-
const lastUpdatedItem = {
|
|
900
|
+
if (!isMissingDate(plugin.created)) {
|
|
901
|
+
items.push({
|
|
811
902
|
icon: 'icon-refresh-alt',
|
|
812
903
|
iconTooltip: { key: 'tableHeaders.lastUpdated' },
|
|
813
|
-
label:
|
|
814
|
-
};
|
|
815
|
-
|
|
816
|
-
if (hasZeroTime) {
|
|
817
|
-
lastUpdatedItem.labelTooltip = this.t('catalog.charts.appChartCard.subHeaderItem.missingVersionDate');
|
|
818
|
-
}
|
|
819
|
-
items.push(lastUpdatedItem);
|
|
904
|
+
label: day(plugin.created).format('MMM D, YYYY')
|
|
905
|
+
});
|
|
820
906
|
}
|
|
821
907
|
|
|
822
908
|
if (plugin.installing) {
|
|
@@ -850,6 +936,17 @@ export default {
|
|
|
850
936
|
},
|
|
851
937
|
|
|
852
938
|
getFooterItems(plugin) {
|
|
939
|
+
const footerItems = [];
|
|
940
|
+
const repoNameToDisplay = plugin?.chart?.repoNameDisplay || plugin?.originalRepoNameDisplay;
|
|
941
|
+
|
|
942
|
+
if (repoNameToDisplay) {
|
|
943
|
+
footerItems.push({
|
|
944
|
+
icon: 'repository-alt',
|
|
945
|
+
iconTooltip: { key: 'tableHeaders.repoName' },
|
|
946
|
+
labels: [repoNameToDisplay]
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
|
|
853
950
|
const labels = [];
|
|
854
951
|
|
|
855
952
|
// "developer load" tag
|
|
@@ -873,34 +970,41 @@ export default {
|
|
|
873
970
|
labels.push(this.t('plugins.labels.experimental'));
|
|
874
971
|
}
|
|
875
972
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
973
|
+
if (labels.length) {
|
|
974
|
+
footerItems.push({
|
|
975
|
+
icon: 'tag-alt',
|
|
976
|
+
iconTooltip: { key: 'generic.tags' },
|
|
977
|
+
labels,
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
return footerItems;
|
|
881
982
|
},
|
|
882
983
|
|
|
883
984
|
getStatuses(plugin) {
|
|
884
985
|
const statuses = [];
|
|
885
986
|
|
|
886
|
-
const
|
|
887
|
-
const
|
|
987
|
+
const errorMsg = plugin.installedError || (plugin.helmError ? this.t('plugins.helmError') : null);
|
|
988
|
+
const incompatibilityMsg = plugin.incompatibilityMessage;
|
|
989
|
+
const isDeprecated = uiPluginHasAnnotation(plugin?.chart, CATALOG_ANNOTATIONS.DEPRECATED, 'true');
|
|
888
990
|
|
|
889
|
-
|
|
890
|
-
let tooltip;
|
|
991
|
+
const tooltipMsgs = [];
|
|
891
992
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
993
|
+
if (isDeprecated) {
|
|
994
|
+
tooltipMsgs.push(this.t('generic.deprecated'));
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (errorMsg) {
|
|
998
|
+
tooltipMsgs.push(errorMsg);
|
|
999
|
+
} else if (incompatibilityMsg) {
|
|
1000
|
+
tooltipMsgs.push(incompatibilityMsg);
|
|
1001
|
+
}
|
|
899
1002
|
|
|
1003
|
+
if (tooltipMsgs.length) {
|
|
900
1004
|
statuses.push({
|
|
901
|
-
icon:
|
|
902
|
-
color:
|
|
903
|
-
tooltip
|
|
1005
|
+
icon: 'icon-alert-alt',
|
|
1006
|
+
color: 'error',
|
|
1007
|
+
tooltip: { text: tooltipMsgs.join('<br/>') }
|
|
904
1008
|
});
|
|
905
1009
|
}
|
|
906
1010
|
|
|
@@ -436,6 +436,7 @@ describe('class: Resource', () => {
|
|
|
436
436
|
}, {
|
|
437
437
|
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
438
438
|
dispatch: jest.fn(),
|
|
439
|
+
rootState: { $extension: { getPlugins: () => ({}) } },
|
|
439
440
|
rootGetters: {
|
|
440
441
|
'i18n/t': (key: string) => key,
|
|
441
442
|
currentCluster: undefined,
|
|
@@ -494,8 +494,9 @@ export default {
|
|
|
494
494
|
// Of type @StorePaginationResult
|
|
495
495
|
const pagination = opt.pagination ? {
|
|
496
496
|
request: {
|
|
497
|
-
namespace:
|
|
498
|
-
pagination:
|
|
497
|
+
namespace: opt.namespaced,
|
|
498
|
+
pagination: opt.pagination,
|
|
499
|
+
includeAssociatedData: opt.includeAssociatedData,
|
|
499
500
|
},
|
|
500
501
|
result: {
|
|
501
502
|
count: out.count,
|
|
@@ -1358,7 +1358,35 @@ export default class Resource {
|
|
|
1358
1358
|
return window.$globalApp.$router;
|
|
1359
1359
|
}
|
|
1360
1360
|
|
|
1361
|
+
get isProdRegistrationV2TopLevelProductResoure() {
|
|
1362
|
+
// this is the logic to determine if the resource is top level product or not
|
|
1363
|
+
// changes c-cluster-product-resource to product-c-cluster-resource
|
|
1364
|
+
// this is for the new extension product registration model
|
|
1365
|
+
let currPluginName = '';
|
|
1366
|
+
const plugins = this.$extension.getPlugins();
|
|
1367
|
+
|
|
1368
|
+
Object.keys(plugins).forEach((key) => {
|
|
1369
|
+
if (plugins[key].productNames.includes(this.$rootGetters['productId'])) {
|
|
1370
|
+
currPluginName = key;
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
// the flag "topLevelProduct" only exists in the V2 product registration model
|
|
1375
|
+
return plugins[currPluginName]?.topLevelProduct || false;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1361
1378
|
get listLocation() {
|
|
1379
|
+
if (this.isProdRegistrationV2TopLevelProductResoure) {
|
|
1380
|
+
return {
|
|
1381
|
+
name: `${ this.$rootGetters['productId'] }-c-cluster-resource`,
|
|
1382
|
+
params: {
|
|
1383
|
+
product: this.$rootGetters['productId'],
|
|
1384
|
+
cluster: this.$rootGetters['clusterId'],
|
|
1385
|
+
resource: this.type,
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1362
1390
|
return {
|
|
1363
1391
|
name: `c-cluster-product-resource`,
|
|
1364
1392
|
params: {
|
|
@@ -1375,6 +1403,20 @@ export default class Resource {
|
|
|
1375
1403
|
|
|
1376
1404
|
const id = this.id?.replace(/.*\//, '');
|
|
1377
1405
|
|
|
1406
|
+
if (this.isProdRegistrationV2TopLevelProductResoure) {
|
|
1407
|
+
return {
|
|
1408
|
+
name: `${ this.$rootGetters['productId'] }-c-cluster-resource${ schema?.attributes?.namespaced ? '-namespace' : '' }-id`,
|
|
1409
|
+
params: {
|
|
1410
|
+
product: this.$rootGetters['productId'],
|
|
1411
|
+
cluster: this.$rootGetters['clusterId'],
|
|
1412
|
+
resource: this.type,
|
|
1413
|
+
namespace: isNamespaced && this.metadata?.namespace ? this.metadata.namespace : undefined,
|
|
1414
|
+
id,
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
// normal cluster scoped resource route as we know
|
|
1378
1420
|
return {
|
|
1379
1421
|
name: `c-cluster-product-resource${ schema?.attributes?.namespaced ? '-namespace' : '' }-id`,
|
|
1380
1422
|
params: {
|
|
@@ -1935,6 +1977,25 @@ export default class Resource {
|
|
|
1935
1977
|
|
|
1936
1978
|
get _glance() {
|
|
1937
1979
|
const type = this.parentNameOverride || this.$rootGetters['type-map/labelFor'](this.schema);
|
|
1980
|
+
let toRoute = null;
|
|
1981
|
+
|
|
1982
|
+
if (this.isProdRegistrationV2TopLevelProductResoure) {
|
|
1983
|
+
toRoute = {
|
|
1984
|
+
name: `${ this.$rootGetters['productId'] }-c-cluster-resource-id`,
|
|
1985
|
+
params: {
|
|
1986
|
+
product: this.$rootGetters['currentProduct']?.id,
|
|
1987
|
+
cluster: this.$rootGetters['currentCluster']?.id,
|
|
1988
|
+
resource: this.type,
|
|
1989
|
+
}
|
|
1990
|
+
};
|
|
1991
|
+
} else {
|
|
1992
|
+
toRoute = {
|
|
1993
|
+
name: `c-cluster-product-resource-id`,
|
|
1994
|
+
product: this.$rootGetters['currentProduct']?.id,
|
|
1995
|
+
cluster: this.$rootGetters['currentCluster']?.id,
|
|
1996
|
+
resource: this.type
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1938
1999
|
|
|
1939
2000
|
return [
|
|
1940
2001
|
{
|
|
@@ -1958,12 +2019,7 @@ export default class Resource {
|
|
|
1958
2019
|
label: this.t('component.resource.detail.glance.namespace'),
|
|
1959
2020
|
formatter: this.$rootGetters['currentProduct']?.id && this.$rootGetters['currentCluster']?.id ? 'Link' : undefined,
|
|
1960
2021
|
formatterOpts: {
|
|
1961
|
-
to:
|
|
1962
|
-
name: `c-cluster-product-resource-id`,
|
|
1963
|
-
product: this.$rootGetters['currentProduct']?.id,
|
|
1964
|
-
cluster: this.$rootGetters['currentCluster']?.id,
|
|
1965
|
-
resource: this.type
|
|
1966
|
-
},
|
|
2022
|
+
to: toRoute,
|
|
1967
2023
|
row: {},
|
|
1968
2024
|
options: { internal: true }
|
|
1969
2025
|
},
|
package/plugins/plugin.js
CHANGED
|
@@ -3,6 +3,8 @@ import { allHashSettled } from '@shell/utils/promise';
|
|
|
3
3
|
import { shouldNotLoadPlugin, UI_PLUGIN_BASE_URL } from '@shell/config/uiplugins';
|
|
4
4
|
import { getKubeVersionData, getVersionData } from '@shell/config/version';
|
|
5
5
|
import versions from '@shell/utils/versions';
|
|
6
|
+
import { listProducts, loadProduct } from '@shell/utils/dynamic-importer';
|
|
7
|
+
import { Plugin as UIPlugin } from '@shell/core/plugin';
|
|
6
8
|
|
|
7
9
|
export default async function(context) {
|
|
8
10
|
if (process.env.excludeOperatorPkg === 'true') {
|
|
@@ -43,6 +45,20 @@ export default async function(context) {
|
|
|
43
45
|
try {
|
|
44
46
|
const res = await allHashSettled(fetches);
|
|
45
47
|
|
|
48
|
+
// Allow legacy products in shell/config/product to be loaded as if they came from an extension (if migrated to use $init)
|
|
49
|
+
for (const product of listProducts()) {
|
|
50
|
+
const impl = await loadProduct(product);
|
|
51
|
+
|
|
52
|
+
if (impl?.$init) {
|
|
53
|
+
const p = new UIPlugin(product);
|
|
54
|
+
|
|
55
|
+
impl.$init(p);
|
|
56
|
+
|
|
57
|
+
context.$plugin._add(product, p);
|
|
58
|
+
context.$plugin.applyPlugin(p);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
46
62
|
// Initialize the built-in extensions now - this is now done here so that built-in extensions get the same, correct environment data (version etc)
|
|
47
63
|
context.$extension.loadBuiltinExtensions();
|
|
48
64
|
|
|
@@ -23,6 +23,7 @@ import { PaginationSettingsStores } from '@shell/types/resources/settings';
|
|
|
23
23
|
import paginationUtils from '@shell/utils/pagination-utils';
|
|
24
24
|
import { KubeLabelSelector, KubeLabelSelectorExpression } from '@shell/types/kube/kube-api';
|
|
25
25
|
import { parseField } from '@shell/utils/sort';
|
|
26
|
+
import { POD_LAST_RESTART_FIELD, POD_RESTART_FIELD } from '@shell/types/resources/pod';
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* This is a workaround for a ts build issue found in check-plugins-build.
|
|
@@ -240,6 +241,8 @@ class StevePaginationUtils extends NamespaceProjectFilters {
|
|
|
240
241
|
[POD]: [
|
|
241
242
|
{ field: 'spec.containers.image' },
|
|
242
243
|
{ field: 'spec.nodeName' },
|
|
244
|
+
{ field: POD_RESTART_FIELD },
|
|
245
|
+
{ field: POD_LAST_RESTART_FIELD },
|
|
243
246
|
],
|
|
244
247
|
[MANAGEMENT.NODE]: [
|
|
245
248
|
{ field: 'status.nodeName' },
|
|
@@ -514,6 +517,10 @@ class StevePaginationUtils extends NamespaceProjectFilters {
|
|
|
514
517
|
}
|
|
515
518
|
}
|
|
516
519
|
|
|
520
|
+
if (opt.includeAssociatedData) {
|
|
521
|
+
params.push('includeAssociatedData=true');
|
|
522
|
+
}
|
|
523
|
+
|
|
517
524
|
// Note - There is a `limit` property that is by default 100,000. This can be disabled by using `limit=-1`,
|
|
518
525
|
// but we shouldn't be fetching any pages big enough to exceed the default
|
|
519
526
|
|