@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.
Files changed (98) hide show
  1. package/assets/styles/base/_mixins.scss +31 -0
  2. package/assets/styles/base/_variables.scss +2 -0
  3. package/assets/styles/themes/_modern.scss +6 -5
  4. package/assets/translations/en-us.yaml +5 -4
  5. package/assets/translations/zh-hans.yaml +0 -3
  6. package/components/EmptyProductPage.vue +76 -0
  7. package/components/Resource/Detail/CopyToClipboard.vue +1 -2
  8. package/components/Resource/Detail/Metadata/KeyValueRow.vue +9 -3
  9. package/components/Resource/Detail/TitleBar/__tests__/__snapshots__/index.test.ts.snap +31 -0
  10. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +45 -1
  11. package/components/Resource/Detail/TitleBar/index.vue +1 -1
  12. package/components/Resource/Detail/ViewOptions/__tests__/__snapshots__/index.test.ts.snap +9 -0
  13. package/components/Resource/Detail/ViewOptions/__tests__/index.test.ts +62 -0
  14. package/components/Resource/Detail/ViewOptions/index.vue +2 -1
  15. package/components/ResourceList/Masthead.vue +25 -2
  16. package/components/SideNav.vue +13 -0
  17. package/components/__tests__/PromptModal.test.ts +2 -0
  18. package/components/fleet/FleetClusters.vue +1 -0
  19. package/components/fleet/__tests__/FleetClusters.test.ts +71 -0
  20. package/components/form/NodeScheduling.vue +17 -3
  21. package/components/form/PrivateRegistry.vue +69 -0
  22. package/components/form/__tests__/PrivateRegistry.test.ts +133 -0
  23. package/components/formatter/WorkloadHealthScale.vue +3 -1
  24. package/components/nav/Group.vue +26 -3
  25. package/components/nav/Header.vue +32 -7
  26. package/components/nav/TopLevelMenu.vue +15 -1
  27. package/config/pagination-table-headers.js +8 -1
  28. package/config/product/apps.js +2 -1
  29. package/config/product/auth.js +1 -0
  30. package/config/product/backup.js +1 -0
  31. package/config/product/compliance.js +1 -1
  32. package/config/product/explorer.js +25 -6
  33. package/config/product/fleet.js +1 -0
  34. package/config/product/gatekeeper.js +1 -0
  35. package/config/product/istio.js +1 -0
  36. package/config/product/logging.js +1 -0
  37. package/config/product/longhorn.js +2 -1
  38. package/config/product/manager.js +1 -0
  39. package/config/product/monitoring.js +1 -0
  40. package/config/product/navlinks.js +1 -0
  41. package/config/product/neuvector.js +2 -1
  42. package/config/product/settings.js +1 -0
  43. package/config/product/uiplugins.js +1 -0
  44. package/core/__tests__/plugin-products-helpers.test.ts +454 -0
  45. package/core/__tests__/plugin-products.test.ts +3219 -0
  46. package/core/extension-manager-impl.js +30 -1
  47. package/core/plugin-products-base.ts +375 -0
  48. package/core/plugin-products-extending.ts +44 -0
  49. package/core/plugin-products-helpers.ts +262 -0
  50. package/core/plugin-products-top-level.ts +66 -0
  51. package/core/plugin-products-type-guards.ts +33 -0
  52. package/core/plugin-products.ts +50 -0
  53. package/core/plugin-types.ts +222 -0
  54. package/core/plugin.ts +45 -10
  55. package/core/productDebugger.js +48 -0
  56. package/core/types.ts +95 -11
  57. package/detail/__tests__/__snapshots__/fleet.cattle.io.bundle.test.ts.snap +52 -0
  58. package/detail/__tests__/fleet.cattle.io.bundle.test.ts +171 -0
  59. package/detail/fleet.cattle.io.bundle.vue +21 -34
  60. package/dialog/ExtensionCatalogInstallDialog.vue +1 -1
  61. package/dialog/InstallExtensionDialog.vue +6 -27
  62. package/dialog/UninstallExistingExtensionDialog.vue +141 -0
  63. package/dialog/UninstallExtensionDialog.vue +4 -26
  64. package/dialog/__tests__/UninstallExistingExtensionDialog.test.ts +114 -0
  65. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -0
  66. package/edit/provisioning.cattle.io.cluster/__tests__/Ingress.test.ts +176 -0
  67. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -1
  68. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +6 -0
  69. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +7 -2
  70. package/list/provisioning.cattle.io.cluster.vue +0 -1
  71. package/list/workload.vue +11 -4
  72. package/mixins/resource-fetch.js +12 -3
  73. package/models/pod.js +18 -0
  74. package/models/workload.js +20 -2
  75. package/package.json +1 -2
  76. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +0 -1
  77. package/pages/c/_cluster/settings/brand.vue +4 -4
  78. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +231 -13
  79. package/pages/c/_cluster/uiplugins/index.vue +143 -37
  80. package/plugins/dashboard-store/__tests__/resource-class.test.ts +1 -0
  81. package/plugins/dashboard-store/actions.js +3 -2
  82. package/plugins/dashboard-store/resource-class.js +62 -6
  83. package/plugins/plugin.js +16 -0
  84. package/plugins/steve/steve-pagination-utils.ts +7 -0
  85. package/scripts/typegen.sh +13 -1
  86. package/store/__tests__/type-map.test.ts +84 -24
  87. package/store/type-map.js +42 -3
  88. package/tsconfig.paths.json +1 -0
  89. package/types/resources/pod.ts +18 -0
  90. package/types/shell/index.d.ts +8506 -2909
  91. package/types/store/dashboard-store.types.ts +5 -0
  92. package/types/store/pagination.types.ts +6 -0
  93. package/utils/axios.js +1 -4
  94. package/utils/dynamic-importer.js +3 -2
  95. package/utils/pagination-utils.ts +1 -1
  96. package/utils/uiplugins.ts +12 -16
  97. package/utils/validators/__tests__/private-registry.test.ts +76 -0
  98. 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.name]) {
317
- item.installing = this.installing[item.name];
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
- const chart = all.find((c) => c.name === p.name);
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.name];
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, so add a card for the plugin based on its Custom resource being present
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: p.name,
391
- label: p.name,
392
- description: p.description || '-',
393
- id: `${ p.name }-${ p.version }`,
394
- versions: [],
395
- displayVersion: p.version,
396
- displayVersionLabel: p.version || '-',
397
- isDeveloper: p.isDeveloper,
398
- installed: true,
399
- installing: false,
400
- builtin: false,
401
- uiplugin: p,
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.name === e);
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.name] = error;
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.name] === 'downgrade') {
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.name, status);
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.name, false);
533
+ this.updatePluginInstallStatus(plugin.id, false);
476
534
  } else if (error) {
477
- this.updatePluginInstallStatus(plugin.name, false);
535
+ this.updatePluginInstallStatus(plugin.id, false);
478
536
  }
479
537
  } else {
480
- this.updatePluginInstallStatus(plugin.name, false);
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.updatePluginInstallStatus(plugin.name, false);
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: (pluginName, type) => {
609
- this.updatePluginInstallStatus(pluginName, type);
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: (pluginName, type) => {
631
- this.updatePluginInstallStatus(pluginName, type);
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.name, 'uninstall');
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(name, status) {
670
- this.installing[name] = status;
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
- return labels.length ? [{
877
- icon: 'tag-alt',
878
- iconTooltip: { key: 'generic.tags' },
879
- labels,
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: opt.namespaced,
498
- pagination: opt.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
 
@@ -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