@rancher/shell 3.0.11 → 3.0.12-rc.1
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/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 +5 -4
- package/assets/translations/zh-hans.yaml +0 -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/SideNav.vue +13 -0
- package/components/__tests__/PromptModal.test.ts +2 -0
- package/components/fleet/FleetClusters.vue +1 -0
- package/components/fleet/__tests__/FleetClusters.test.ts +71 -0
- package/components/form/NodeScheduling.vue +17 -3
- package/components/form/PrivateRegistry.vue +69 -0
- package/components/form/__tests__/PrivateRegistry.test.ts +133 -0
- package/components/formatter/WorkloadHealthScale.vue +3 -1
- package/components/nav/Group.vue +26 -3
- package/components/nav/Header.vue +32 -7
- package/components/nav/TopLevelMenu.vue +15 -1
- 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/core/__tests__/plugin-products-helpers.test.ts +454 -0
- package/core/__tests__/plugin-products.test.ts +3219 -0
- package/core/extension-manager-impl.js +30 -1
- package/core/plugin-products-base.ts +375 -0
- package/core/plugin-products-extending.ts +44 -0
- package/core/plugin-products-helpers.ts +262 -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 +222 -0
- package/core/plugin.ts +45 -10
- package/core/productDebugger.js +48 -0
- package/core/types.ts +95 -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/fleet.cattle.io.bundle.vue +21 -34
- 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/provisioning.cattle.io.cluster/__tests__/Ingress.test.ts +176 -0
- 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/list/provisioning.cattle.io.cluster.vue +0 -1
- package/list/workload.vue +11 -4
- package/mixins/resource-fetch.js +12 -3
- package/models/pod.js +18 -0
- package/models/workload.js +20 -2
- package/package.json +1 -2
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +0 -1
- package/pages/c/_cluster/settings/brand.vue +4 -4
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +231 -13
- package/pages/c/_cluster/uiplugins/index.vue +143 -37
- 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/scripts/typegen.sh +13 -1
- package/store/__tests__/type-map.test.ts +84 -24
- 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 +8506 -2909
- package/types/store/dashboard-store.types.ts +5 -0
- package/types/store/pagination.types.ts +6 -0
- package/utils/axios.js +1 -4
- package/utils/dynamic-importer.js +3 -2
- package/utils/pagination-utils.ts +1 -1
- package/utils/uiplugins.ts +12 -16
- package/utils/validators/__tests__/private-registry.test.ts +76 -0
- package/utils/validators/private-registry.ts +28 -0
|
@@ -101,6 +101,14 @@ export default {
|
|
|
101
101
|
hash.helmOps = this.$store.dispatch('management/findAll', { type: CATALOG.OPERATION });
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
// Load apps in UI_PLUGIN_NAMESPACE to determine which repo an extension was installed from
|
|
105
|
+
if (this.$store.getters['management/schemaFor'](CATALOG.APP)) {
|
|
106
|
+
hash.apps = this.$store.dispatch('management/findAll', {
|
|
107
|
+
type: CATALOG.APP,
|
|
108
|
+
opt: { namespaced: UI_PLUGIN_NAMESPACE }
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
104
112
|
if (this.$store.getters['management/schemaFor'](CATALOG.CLUSTER_REPO)) {
|
|
105
113
|
hash.repos = this.$store.dispatch('management/findAll', { type: CATALOG.CLUSTER_REPO }, { force: true });
|
|
106
114
|
}
|
|
@@ -127,6 +135,12 @@ export default {
|
|
|
127
135
|
...mapGetters({ uiErrors: 'uiplugins/errors' }),
|
|
128
136
|
...mapGetters({ theme: 'prefs/theme' }),
|
|
129
137
|
|
|
138
|
+
// Computed to reactively update when new apps are installed
|
|
139
|
+
apps() {
|
|
140
|
+
return this.$store.getters['management/all'](CATALOG.APP)
|
|
141
|
+
.filter((app) => app.metadata?.namespace === UI_PLUGIN_NAMESPACE);
|
|
142
|
+
},
|
|
143
|
+
|
|
130
144
|
charts() {
|
|
131
145
|
const c = this.$store.getters['catalog/rawCharts'];
|
|
132
146
|
|
|
@@ -313,8 +327,8 @@ export default {
|
|
|
313
327
|
}
|
|
314
328
|
}
|
|
315
329
|
|
|
316
|
-
if (this.installing[item.
|
|
317
|
-
item.installing = this.installing[item.
|
|
330
|
+
if (this.installing[item.id]) {
|
|
331
|
+
item.installing = this.installing[item.id];
|
|
318
332
|
}
|
|
319
333
|
|
|
320
334
|
return item;
|
|
@@ -359,15 +373,23 @@ export default {
|
|
|
359
373
|
|
|
360
374
|
// Go through the CRs for the plugins and wire them into the catalog
|
|
361
375
|
this.plugins.forEach((p) => {
|
|
362
|
-
|
|
376
|
+
let chart;
|
|
377
|
+
const app = this.apps.find((a) => a.metadata.name === p.name && a.metadata.namespace === UI_PLUGIN_NAMESPACE);
|
|
378
|
+
const originalRepoName = app?.spec?.chart?.metadata?.annotations?.[CATALOG_ANNOTATIONS.SOURCE_REPO_NAME] || app?.metadata?.labels?.[CATALOG_ANNOTATIONS.CLUSTER_REPO_NAME];
|
|
379
|
+
|
|
380
|
+
// Find the chart from the original repo to avoid picking a wrong chart with the same name
|
|
381
|
+
if (originalRepoName) {
|
|
382
|
+
chart = all.find((c) => c.name === p.name && c.chart?.repoName === originalRepoName);
|
|
383
|
+
}
|
|
363
384
|
|
|
385
|
+
// If original repo was removed, don't fall back to another repo's chart (would break Available tab)
|
|
364
386
|
if (chart) {
|
|
365
387
|
chart.installed = true;
|
|
366
388
|
chart.uiplugin = p;
|
|
367
389
|
chart.installedVersion = p.version;
|
|
368
390
|
|
|
369
391
|
// Can't do this here
|
|
370
|
-
chart.installing = this.installing[chart.
|
|
392
|
+
chart.installing = this.installing[chart.id];
|
|
371
393
|
|
|
372
394
|
// Check for upgrade
|
|
373
395
|
const latestInstallableVersion = chart.installableVersions?.[0];
|
|
@@ -385,20 +407,34 @@ export default {
|
|
|
385
407
|
chart.upgrade = getPluginChartVersionLabel(latestInstallableVersion);
|
|
386
408
|
}
|
|
387
409
|
} else {
|
|
388
|
-
// No chart
|
|
410
|
+
// No chart available - original repo was removed or developer-loaded plugin
|
|
411
|
+
const appChartMeta = app?.spec?.chart?.metadata;
|
|
412
|
+
const appAnnotations = appChartMeta?.annotations || {};
|
|
413
|
+
let originalRepoDisplayName = null;
|
|
414
|
+
|
|
415
|
+
if (originalRepoName) {
|
|
416
|
+
originalRepoDisplayName = this.$store.getters['i18n/withFallback'](`catalog.repo.name."${ originalRepoName }"`, null, originalRepoName);
|
|
417
|
+
}
|
|
418
|
+
|
|
389
419
|
const item = {
|
|
390
|
-
name:
|
|
391
|
-
label:
|
|
392
|
-
description:
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
420
|
+
name: p.name,
|
|
421
|
+
label: appAnnotations[UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME] || appChartMeta?.name || p.name,
|
|
422
|
+
description: appChartMeta?.description || p.description || '-',
|
|
423
|
+
icon: appChartMeta?.icon || appAnnotations['catalog.cattle.io/ui-icon'],
|
|
424
|
+
id: `${ p.name }-${ p.version }`,
|
|
425
|
+
versions: [],
|
|
426
|
+
displayVersion: p.version,
|
|
427
|
+
displayVersionLabel: p.version || '-',
|
|
428
|
+
isDeveloper: p.isDeveloper,
|
|
429
|
+
installed: true,
|
|
430
|
+
installedVersion: p.version,
|
|
431
|
+
installing: false,
|
|
432
|
+
builtin: false,
|
|
433
|
+
uiplugin: p,
|
|
434
|
+
primeOnly: appAnnotations[CATALOG_ANNOTATIONS.PRIME_ONLY] === 'true',
|
|
435
|
+
experimental: appAnnotations[CATALOG_ANNOTATIONS.EXPERIMENTAL] === 'true',
|
|
436
|
+
certified: appAnnotations[CATALOG_ANNOTATIONS.CERTIFIED] === CATALOG_ANNOTATIONS._RANCHER,
|
|
437
|
+
originalRepoNameDisplay: originalRepoDisplayName,
|
|
402
438
|
};
|
|
403
439
|
|
|
404
440
|
all.push(item);
|
|
@@ -422,7 +458,7 @@ export default {
|
|
|
422
458
|
|
|
423
459
|
// Merge in the plugin load errors from help ops
|
|
424
460
|
Object.keys(this.errors).forEach((e) => {
|
|
425
|
-
const chart = all.find((c) => c.
|
|
461
|
+
const chart = all.find((c) => c.id === e);
|
|
426
462
|
|
|
427
463
|
if (chart) {
|
|
428
464
|
chart.helmError = !!this.errors[e];
|
|
@@ -456,28 +492,50 @@ export default {
|
|
|
456
492
|
const op = pluginOps.find((o) => o.status?.releaseName === plugin.name);
|
|
457
493
|
|
|
458
494
|
if (op) {
|
|
495
|
+
const allWithSameName = this.available.filter((p) => p.name === plugin.name);
|
|
496
|
+
let targetPluginId;
|
|
497
|
+
|
|
498
|
+
// When multiple plugins share the same name (from different repositories),
|
|
499
|
+
// a single helm operation will trigger updates. We need to correctly identify
|
|
500
|
+
// the specific plugin that is either currently being installed/updated or is already installed
|
|
501
|
+
// so we don't accidentally mark all identically named plugins as "installing".
|
|
502
|
+
const installingPlugin = allWithSameName.find((p) => this.installing[p.id]);
|
|
503
|
+
const installedPlugin = allWithSameName.find((p) => p.installed);
|
|
504
|
+
|
|
505
|
+
if (installingPlugin) {
|
|
506
|
+
targetPluginId = installingPlugin.id;
|
|
507
|
+
} else if (installedPlugin) {
|
|
508
|
+
targetPluginId = installedPlugin.id;
|
|
509
|
+
} else {
|
|
510
|
+
targetPluginId = allWithSameName[0]?.id;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (plugin.id !== targetPluginId) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
459
517
|
const active = op.metadata.state?.transitioning;
|
|
460
518
|
const error = op.metadata.state?.error;
|
|
461
519
|
|
|
462
|
-
this.errors[plugin.
|
|
520
|
+
this.errors[plugin.id] = error;
|
|
463
521
|
|
|
464
522
|
if (active) {
|
|
465
523
|
// Can use the status directly, apart from upgrade, which maps to update
|
|
466
524
|
const status = op.status.action;
|
|
467
525
|
|
|
468
|
-
if (status === 'upgrade' && this.installing[plugin.
|
|
526
|
+
if (status === 'upgrade' && this.installing[plugin.id] === 'downgrade') {
|
|
469
527
|
// Helm op is an upgrade, but we initiated a downgrade, so keep the 'downgrade' status
|
|
470
528
|
} else {
|
|
471
|
-
this.updatePluginInstallStatus(plugin.
|
|
529
|
+
this.updatePluginInstallStatus(plugin.id, status);
|
|
472
530
|
}
|
|
473
531
|
} else if (op.status.action === 'uninstall') {
|
|
474
532
|
// Uninstall has finished
|
|
475
|
-
this.updatePluginInstallStatus(plugin.
|
|
533
|
+
this.updatePluginInstallStatus(plugin.id, false);
|
|
476
534
|
} else if (error) {
|
|
477
|
-
this.updatePluginInstallStatus(plugin.
|
|
535
|
+
this.updatePluginInstallStatus(plugin.id, false);
|
|
478
536
|
}
|
|
479
537
|
} else {
|
|
480
|
-
this.updatePluginInstallStatus(plugin.
|
|
538
|
+
this.updatePluginInstallStatus(plugin.id, false);
|
|
481
539
|
}
|
|
482
540
|
});
|
|
483
541
|
},
|
|
@@ -503,7 +561,11 @@ export default {
|
|
|
503
561
|
changes++;
|
|
504
562
|
}
|
|
505
563
|
|
|
506
|
-
this.
|
|
564
|
+
(this.available || []).forEach((c) => {
|
|
565
|
+
if (c.name === plugin.name) {
|
|
566
|
+
this.updatePluginInstallStatus(c.id, false);
|
|
567
|
+
}
|
|
568
|
+
});
|
|
507
569
|
}
|
|
508
570
|
});
|
|
509
571
|
|
|
@@ -596,6 +658,35 @@ export default {
|
|
|
596
658
|
ev?.preventDefault?.();
|
|
597
659
|
ev?.stopPropagation?.();
|
|
598
660
|
|
|
661
|
+
// Check if a plugin with the same name is already installed from a different repo
|
|
662
|
+
if (action === 'install') {
|
|
663
|
+
const installedPlugin = this.available.find((p) => p.name === plugin.name && p.installed);
|
|
664
|
+
const isInstalledFromDifferentSource = !!installedPlugin && installedPlugin.id !== plugin.id;
|
|
665
|
+
|
|
666
|
+
if (isInstalledFromDifferentSource) {
|
|
667
|
+
// Show a different dialog that prompts the user to uninstall the existing version first
|
|
668
|
+
this.$store.dispatch('management/promptModal', {
|
|
669
|
+
component: 'UninstallExistingExtensionDialog',
|
|
670
|
+
testId: 'uninstall-existing-extension-modal',
|
|
671
|
+
returnFocusSelector: `[data-testid="extension-card-${ action }-btn-${ plugin?.name }"]`,
|
|
672
|
+
returnFocusFirstIterableNodeSelector: '#extensions-main-page',
|
|
673
|
+
componentProps: {
|
|
674
|
+
installedPlugin,
|
|
675
|
+
updateStatus: (pluginId, type) => {
|
|
676
|
+
this.updatePluginInstallStatus(pluginId, type);
|
|
677
|
+
},
|
|
678
|
+
closed: (res) => {
|
|
679
|
+
if (res?.uninstalled) {
|
|
680
|
+
this.didUninstall(res.plugin);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
599
690
|
this.$store.dispatch('management/promptModal', {
|
|
600
691
|
component: 'InstallExtensionDialog',
|
|
601
692
|
testId: 'install-extension-modal',
|
|
@@ -605,8 +696,8 @@ export default {
|
|
|
605
696
|
plugin,
|
|
606
697
|
action,
|
|
607
698
|
initialVersion,
|
|
608
|
-
updateStatus: (
|
|
609
|
-
this.updatePluginInstallStatus(
|
|
699
|
+
updateStatus: (pluginId, type) => {
|
|
700
|
+
this.updatePluginInstallStatus(pluginId, type);
|
|
610
701
|
},
|
|
611
702
|
closed: (res) => {
|
|
612
703
|
this.didInstall(res);
|
|
@@ -627,8 +718,8 @@ export default {
|
|
|
627
718
|
returnFocusFirstIterableNodeSelector: '#extensions-main-page',
|
|
628
719
|
componentProps: {
|
|
629
720
|
plugin,
|
|
630
|
-
updateStatus: (
|
|
631
|
-
this.updatePluginInstallStatus(
|
|
721
|
+
updateStatus: (pluginId, type) => {
|
|
722
|
+
this.updatePluginInstallStatus(pluginId, type);
|
|
632
723
|
},
|
|
633
724
|
closed: (res) => {
|
|
634
725
|
this.didUninstall(res);
|
|
@@ -639,7 +730,7 @@ export default {
|
|
|
639
730
|
|
|
640
731
|
didUninstall(plugin) {
|
|
641
732
|
if (plugin) {
|
|
642
|
-
this.updatePluginInstallStatus(plugin.
|
|
733
|
+
this.updatePluginInstallStatus(plugin.id, 'uninstall');
|
|
643
734
|
|
|
644
735
|
if (plugin.catalog) {
|
|
645
736
|
this.refreshCharts();
|
|
@@ -666,8 +757,8 @@ export default {
|
|
|
666
757
|
this.$refs.infoPanel.show({ ...plugin, tags });
|
|
667
758
|
},
|
|
668
759
|
|
|
669
|
-
updatePluginInstallStatus(
|
|
670
|
-
this.installing[
|
|
760
|
+
updatePluginInstallStatus(id, status) {
|
|
761
|
+
this.installing[id] = status;
|
|
671
762
|
},
|
|
672
763
|
|
|
673
764
|
setMenu(event) {
|
|
@@ -850,6 +941,17 @@ export default {
|
|
|
850
941
|
},
|
|
851
942
|
|
|
852
943
|
getFooterItems(plugin) {
|
|
944
|
+
const footerItems = [];
|
|
945
|
+
const repoNameToDisplay = plugin?.chart?.repoNameDisplay || plugin?.originalRepoNameDisplay;
|
|
946
|
+
|
|
947
|
+
if (repoNameToDisplay) {
|
|
948
|
+
footerItems.push({
|
|
949
|
+
icon: 'repository-alt',
|
|
950
|
+
iconTooltip: { key: 'tableHeaders.repoName' },
|
|
951
|
+
labels: [repoNameToDisplay]
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
|
|
853
955
|
const labels = [];
|
|
854
956
|
|
|
855
957
|
// "developer load" tag
|
|
@@ -873,11 +975,15 @@ export default {
|
|
|
873
975
|
labels.push(this.t('plugins.labels.experimental'));
|
|
874
976
|
}
|
|
875
977
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
978
|
+
if (labels.length) {
|
|
979
|
+
footerItems.push({
|
|
980
|
+
icon: 'tag-alt',
|
|
981
|
+
iconTooltip: { key: 'generic.tags' },
|
|
982
|
+
labels,
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
return footerItems;
|
|
881
987
|
},
|
|
882
988
|
|
|
883
989
|
getStatuses(plugin) {
|
|
@@ -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
|
|
package/scripts/typegen.sh
CHANGED
|
@@ -22,11 +22,13 @@ ${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/config/table-headers.js --declara
|
|
|
22
22
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/config/types.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/config > /dev/null
|
|
23
23
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/config/labels-annotations.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/config > /dev/null
|
|
24
24
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/config/version.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/config > /dev/null
|
|
25
|
+
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/config/product/*.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/config/product > /dev/null
|
|
25
26
|
|
|
26
27
|
# # store
|
|
27
28
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/store/features.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/store > /dev/null
|
|
28
|
-
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/store/prefs.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/store > /dev/null
|
|
29
29
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/store/plugins.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/store > /dev/null
|
|
30
|
+
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/store/prefs.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/store > /dev/null
|
|
31
|
+
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/store/store-types.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/store > /dev/null
|
|
30
32
|
|
|
31
33
|
# # plugins
|
|
32
34
|
${BASE_DIR}/node_modules/.bin/tsc ${SHELL_DIR}/plugins/i18n.js --declaration --allowJs --emitDeclarationOnly --outDir ${SHELL_DIR}/tmp/ > /dev/null
|
|
@@ -93,6 +95,16 @@ function processDir() {
|
|
|
93
95
|
echo "declare module '${module}' {" >> ${INDEX}
|
|
94
96
|
cat $entry >> ${INDEX}
|
|
95
97
|
echo -e "}" >> ${INDEX}
|
|
98
|
+
|
|
99
|
+
# Also generate a module declaration with .js extension for JS source files
|
|
100
|
+
# TypeScript imports use .js extensions for JS files
|
|
101
|
+
if [ "${name}" != "index" ]; then
|
|
102
|
+
local moduleWithJs=${basePkg}/${name}.js
|
|
103
|
+
echo -e "\n// ${moduleWithJs}\n" >> ${INDEX}
|
|
104
|
+
echo "declare module '${moduleWithJs}' {" >> ${INDEX}
|
|
105
|
+
cat $entry >> ${INDEX}
|
|
106
|
+
echo -e "}" >> ${INDEX}
|
|
107
|
+
fi
|
|
96
108
|
fi
|
|
97
109
|
fi
|
|
98
110
|
done
|