@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.
Files changed (219) hide show
  1. package/assets/images/providers/entraid-black.svg +4 -0
  2. package/assets/images/providers/entraid.svg +9 -0
  3. package/assets/images/vendor/entraid.svg +9 -0
  4. package/assets/styles/app.scss +0 -1
  5. package/assets/styles/base/_mixins.scss +31 -0
  6. package/assets/styles/base/_variables.scss +2 -0
  7. package/assets/styles/themes/_modern.scss +6 -5
  8. package/assets/translations/en-us.yaml +24 -21
  9. package/assets/translations/zh-hans.yaml +4 -11
  10. package/chart/__tests__/S3.test.ts +10 -3
  11. package/components/CountBox.vue +20 -0
  12. package/components/CreateDriver.vue +0 -12
  13. package/components/DetailText.vue +12 -3
  14. package/components/EmptyProductPage.vue +76 -0
  15. package/components/Resource/Detail/CopyToClipboard.vue +1 -2
  16. package/components/Resource/Detail/Metadata/KeyValueRow.vue +9 -3
  17. package/components/Resource/Detail/TitleBar/__tests__/__snapshots__/index.test.ts.snap +31 -0
  18. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +45 -1
  19. package/components/Resource/Detail/TitleBar/index.vue +1 -1
  20. package/components/Resource/Detail/ViewOptions/__tests__/__snapshots__/index.test.ts.snap +9 -0
  21. package/components/Resource/Detail/ViewOptions/__tests__/index.test.ts +62 -0
  22. package/components/Resource/Detail/ViewOptions/index.vue +2 -1
  23. package/components/ResourceList/Masthead.vue +25 -2
  24. package/components/SelectIconGrid.vue +5 -0
  25. package/components/SideNav.vue +13 -0
  26. package/components/__tests__/CountBox.test.ts +72 -0
  27. package/components/__tests__/DetailText.test.ts +113 -0
  28. package/components/__tests__/PromptModal.test.ts +2 -0
  29. package/components/fleet/FleetClusterTargets/index.vue +18 -1
  30. package/components/fleet/FleetClusters.vue +1 -0
  31. package/components/fleet/__tests__/FleetClusters.test.ts +71 -0
  32. package/components/form/InputWithSelect.vue +18 -10
  33. package/components/form/KeyValue.vue +17 -1
  34. package/components/form/LabeledSelect.vue +82 -24
  35. package/components/form/NodeScheduling.vue +17 -3
  36. package/components/form/PrivateRegistry.vue +69 -0
  37. package/components/form/Select.vue +73 -56
  38. package/components/form/ServiceNameSelect.vue +13 -11
  39. package/components/form/__tests__/KeyValue.test.ts +66 -0
  40. package/components/form/__tests__/NodeScheduling.test.ts +9 -0
  41. package/components/form/__tests__/PrivateRegistry.test.ts +133 -0
  42. package/components/form/labeled-select-utils/useLabeledSelectPagination.ts +138 -0
  43. package/components/formatter/WorkloadHealthScale.vue +3 -1
  44. package/components/nav/Group.vue +33 -9
  45. package/components/nav/Header.vue +56 -10
  46. package/components/nav/NotificationCenter/Notification.vue +4 -1
  47. package/components/nav/NotificationCenter/NotificationHeader.vue +20 -8
  48. package/components/nav/NotificationCenter/__tests__/NotificationHeader.test.ts +80 -0
  49. package/components/nav/TopLevelMenu.vue +15 -1
  50. package/components/nav/Type.vue +8 -7
  51. package/components/nav/WindowManager/index.vue +2 -1
  52. package/components/nav/WorkspaceSwitcher.vue +13 -0
  53. package/components/nav/__tests__/Group.test.ts +67 -0
  54. package/components/nav/__tests__/Header.test.ts +235 -0
  55. package/components/nav/__tests__/Type.test.ts +20 -3
  56. package/components/templates/default.vue +34 -4
  57. package/components/templates/home.vue +12 -25
  58. package/components/templates/plain.vue +13 -26
  59. package/composables/useLabeledFormElement.ts +10 -2
  60. package/composables/useLabeledSelect.ts +60 -0
  61. package/composables/useUserRetentionValidation.ts +1 -49
  62. package/config/cookies.js +0 -1
  63. package/config/labels-annotations.js +1 -0
  64. package/config/pagination-table-headers.js +8 -1
  65. package/config/product/apps.js +2 -1
  66. package/config/product/auth.js +1 -0
  67. package/config/product/backup.js +1 -0
  68. package/config/product/compliance.js +1 -1
  69. package/config/product/explorer.js +25 -6
  70. package/config/product/fleet.js +1 -0
  71. package/config/product/gatekeeper.js +1 -0
  72. package/config/product/istio.js +1 -0
  73. package/config/product/logging.js +1 -0
  74. package/config/product/longhorn.js +2 -1
  75. package/config/product/manager.js +1 -0
  76. package/config/product/monitoring.js +1 -0
  77. package/config/product/navlinks.js +1 -0
  78. package/config/product/neuvector.js +2 -1
  79. package/config/product/settings.js +1 -0
  80. package/config/product/uiplugins.js +1 -0
  81. package/config/query-params.js +1 -0
  82. package/config/router/routes.js +0 -8
  83. package/core/__tests__/plugin-products-helpers.test.ts +454 -0
  84. package/core/__tests__/plugin-products.test.ts +3810 -0
  85. package/core/extension-manager-impl.js +30 -1
  86. package/core/plugin-products-base.ts +392 -0
  87. package/core/plugin-products-extending.ts +44 -0
  88. package/core/plugin-products-helpers.ts +263 -0
  89. package/core/plugin-products-top-level.ts +66 -0
  90. package/core/plugin-products-type-guards.ts +33 -0
  91. package/core/plugin-products.ts +50 -0
  92. package/core/plugin-types.ts +237 -0
  93. package/core/plugin.ts +45 -10
  94. package/core/productDebugger.js +48 -0
  95. package/core/types.ts +97 -11
  96. package/detail/__tests__/__snapshots__/fleet.cattle.io.bundle.test.ts.snap +52 -0
  97. package/detail/__tests__/fleet.cattle.io.bundle.test.ts +171 -0
  98. package/detail/__tests__/management.cattle.io.fleetworkspace.test.ts +128 -0
  99. package/detail/fleet.cattle.io.bundle.vue +21 -34
  100. package/detail/management.cattle.io.fleetworkspace.vue +49 -0
  101. package/dialog/ExtensionCatalogInstallDialog.vue +1 -1
  102. package/dialog/InstallExtensionDialog.vue +6 -27
  103. package/dialog/UninstallExistingExtensionDialog.vue +141 -0
  104. package/dialog/UninstallExtensionDialog.vue +4 -26
  105. package/dialog/__tests__/UninstallExistingExtensionDialog.test.ts +114 -0
  106. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -0
  107. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +9 -0
  108. package/edit/__tests__/kontainerDriver.test.ts +0 -13
  109. package/edit/__tests__/nodeDriver.test.ts +5 -11
  110. package/edit/__tests__/resources.cattle.io.restore.test.ts +9 -0
  111. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  112. package/edit/auth/__tests__/oidc.test.ts +54 -0
  113. package/edit/auth/azuread.vue +1 -1
  114. package/edit/auth/oidc.vue +8 -0
  115. package/edit/kontainerDriver.vue +1 -2
  116. package/edit/nodeDriver.vue +0 -2
  117. package/edit/provisioning.cattle.io.cluster/AgentEnv.vue +1 -0
  118. package/edit/provisioning.cattle.io.cluster/__tests__/AgentEnv.test.ts +25 -0
  119. package/edit/provisioning.cattle.io.cluster/__tests__/Ingress.test.ts +176 -0
  120. package/edit/provisioning.cattle.io.cluster/index.vue +70 -99
  121. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -1
  122. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +6 -0
  123. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +7 -2
  124. package/initialize/App.vue +29 -2
  125. package/initialize/install-plugins.js +0 -2
  126. package/list/__tests__/management.cattle.io.feature.test.ts +105 -0
  127. package/list/catalog.cattle.io.app.vue +25 -5
  128. package/list/management.cattle.io.feature.vue +1 -1
  129. package/list/management.cattle.io.fleetworkspace.vue +8 -0
  130. package/list/provisioning.cattle.io.cluster.vue +0 -1
  131. package/list/workload.vue +11 -4
  132. package/machine-config/amazonec2.vue +1 -0
  133. package/mixins/chart.js +40 -9
  134. package/mixins/resource-fetch.js +12 -3
  135. package/models/__tests__/catalog.cattle.io.app.test.ts +15 -1
  136. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +84 -0
  137. package/models/__tests__/chart.test.ts +99 -6
  138. package/models/__tests__/management.cattle.io.feature.test.ts +131 -0
  139. package/models/__tests__/monitoring.coreos.com.alertmanagerconfig.test.ts +98 -0
  140. package/models/catalog.cattle.io.app.js +21 -17
  141. package/models/catalog.cattle.io.clusterrepo.js +39 -11
  142. package/models/chart.js +33 -19
  143. package/models/fleet-application.js +1 -1
  144. package/models/fleet.cattle.io.bundle.js +1 -1
  145. package/models/kontainerdriver.js +11 -0
  146. package/models/management.cattle.io.authconfig.js +5 -1
  147. package/models/management.cattle.io.cluster.js +0 -53
  148. package/models/management.cattle.io.feature.js +3 -3
  149. package/models/management.cattle.io.kontainerdriver.js +1 -26
  150. package/models/monitoring.coreos.com.alertmanagerconfig.js +31 -17
  151. package/models/nodedriver.js +7 -0
  152. package/models/pod.js +18 -0
  153. package/models/workload.js +20 -2
  154. package/package.json +13 -13
  155. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +0 -1
  156. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +189 -0
  157. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +55 -0
  158. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +53 -0
  159. package/pages/c/_cluster/apps/charts/chart.vue +217 -33
  160. package/pages/c/_cluster/apps/charts/index.vue +2 -2
  161. package/pages/c/_cluster/apps/charts/install.vue +8 -3
  162. package/pages/c/_cluster/auth/user.retention/index.vue +55 -22
  163. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -7
  164. package/pages/c/_cluster/settings/brand.vue +4 -4
  165. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +39 -2
  166. package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +61 -0
  167. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +246 -23
  168. package/pages/c/_cluster/uiplugins/index.vue +166 -62
  169. package/plugins/dashboard-store/__tests__/resource-class.test.ts +1 -0
  170. package/plugins/dashboard-store/actions.js +3 -2
  171. package/plugins/dashboard-store/resource-class.js +62 -6
  172. package/plugins/plugin.js +16 -0
  173. package/plugins/steve/steve-pagination-utils.ts +7 -0
  174. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +205 -1
  175. package/rancher-components/Form/LabeledInput/LabeledInput.vue +82 -4
  176. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +1 -1
  177. package/scripts/test-plugins-build.sh +5 -2
  178. package/scripts/typegen.sh +13 -1
  179. package/server/server-middleware.js +2 -2
  180. package/static/humans.txt +1 -0
  181. package/static/robots.txt +34 -0
  182. package/static/welcome-cow.svg +18 -0
  183. package/store/__tests__/catalog.test.ts +161 -11
  184. package/store/__tests__/type-map.test.ts +84 -24
  185. package/store/auth.js +0 -3
  186. package/store/catalog.js +60 -8
  187. package/store/type-map.js +42 -3
  188. package/tsconfig.paths.json +1 -0
  189. package/types/resources/pod.ts +18 -0
  190. package/types/shell/index.d.ts +8539 -2938
  191. package/types/store/dashboard-store.types.ts +5 -0
  192. package/types/store/pagination.types.ts +6 -0
  193. package/utils/__tests__/git.test.ts +270 -0
  194. package/utils/__tests__/inactivity.test.ts +316 -0
  195. package/utils/__tests__/object.test.ts +77 -0
  196. package/utils/__tests__/time.test.ts +14 -1
  197. package/utils/__tests__/url.test.ts +246 -0
  198. package/utils/axios.js +1 -4
  199. package/utils/dynamic-importer.js +3 -2
  200. package/utils/object.js +33 -2
  201. package/utils/pagination-utils.ts +1 -1
  202. package/utils/time.ts +5 -0
  203. package/utils/uiplugins.ts +12 -16
  204. package/utils/validators/__tests__/private-registry.test.ts +76 -0
  205. package/utils/validators/private-registry.ts +28 -0
  206. package/vue.config.js +0 -9
  207. package/assets/images/providers/azuread-black.svg +0 -22
  208. package/assets/images/providers/azuread.svg +0 -25
  209. package/assets/images/vendor/azuread.svg +0 -18
  210. package/assets/styles/fonts/_dots.scss +0 -18
  211. package/components/EmberPage.vue +0 -622
  212. package/components/EmberPageView.vue +0 -39
  213. package/components/form/labeled-select-utils/labeled-select-pagination.ts +0 -116
  214. package/mixins/labeled-form-element.ts +0 -225
  215. package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -28
  216. package/pages/c/_cluster/manager/pages/_page.vue +0 -22
  217. package/pages/c/_cluster/mcapps/pages/_page.vue +0 -22
  218. package/plugins/ember-cookie.js +0 -17
  219. package/utils/ember-page.js +0 -30
@@ -41,26 +41,54 @@ export default class ClusterRepo extends SteveModel {
41
41
  });
42
42
 
43
43
  insertAt(out, 0, {
44
- action: 'refresh',
45
- label: this.t('action.refresh'),
46
- icon: 'icon icon-refresh',
47
- enabled: !!this.links.update,
48
- bulkable: true,
44
+ action: 'refresh',
45
+ label: this.t('action.refresh'),
46
+ icon: 'icon icon-refresh',
47
+ enabled: !!this.links.update,
48
+ bulkable: true,
49
+ bulkAction: 'refreshBulk',
49
50
  });
50
51
  }
51
52
 
52
53
  return out;
53
54
  }
54
55
 
55
- async refresh() {
56
- const now = (new Date()).toISOString().replace(/\.\d+Z$/, 'Z');
56
+ /**
57
+ * Refreshes the repository by updating its forceUpdate annotation and waiting for it to become active.
58
+ * @param {boolean} dispatchLoad - Whether to dispatch the catalog/load action after refreshing. Defaults to true.
59
+ */
60
+ async refresh(dispatchLoad = true) {
61
+ try {
62
+ const now = (new Date()).toISOString().replace(/\.\d+Z$/, 'Z');
57
63
 
58
- this.spec.forceUpdate = now;
59
- await this.save();
64
+ this.spec.forceUpdate = now;
65
+ await this.save();
66
+
67
+ await this.waitForState('active', 10000, 1000);
60
68
 
61
- await this.waitForState('active', 10000, 1000);
69
+ if (dispatchLoad) {
70
+ this.$dispatch('catalog/load', { force: true, repoKeys: [this._key] }, { root: true });
71
+ }
72
+ } catch (err) {
73
+ this.$dispatch('growl/fromError', {
74
+ title: this.t('catalog.repo.error.refresh', {}, 'Error refreshing repository'),
75
+ err,
76
+ }, { root: true });
77
+ }
78
+ }
62
79
 
63
- this.$dispatch('catalog/load', { force: true, reset: true }, { root: true });
80
+ /**
81
+ * Performs a bulk refresh on multiple repositories concurrently, bypassing individual
82
+ * catalog loads, and dispatches a single catalog/load for all repositories once they are active.
83
+ * @param {ClusterRepo[]} items - Array of repository instances to refresh.
84
+ */
85
+ async refreshBulk(items) {
86
+ await Promise.allSettled(items.map((item) => item.refresh(false)));
87
+
88
+ this.$dispatch('catalog/load', {
89
+ force: true,
90
+ repoKeys: items.map((item) => item._key)
91
+ }, { root: true });
64
92
  }
65
93
 
66
94
  async disableClusterRepo() {
package/models/chart.js CHANGED
@@ -5,8 +5,9 @@ import {
5
5
  import { BLANK_CLUSTER } from '@shell/store/store-types.js';
6
6
  import { SHOW_PRE_RELEASE } from '@shell/store/prefs';
7
7
  import { getLatestCompatibleVersion } from '@shell/utils/chart';
8
+ import { isMissingDate } from '@shell/utils/time';
8
9
  import SteveModel from '@shell/plugins/steve/steve-class';
9
- import { CATALOG, ZERO_TIME } from '@shell/config/types';
10
+ import { CATALOG } from '@shell/config/types';
10
11
  import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
11
12
  import day from 'dayjs';
12
13
 
@@ -89,22 +90,33 @@ export default class Chart extends SteveModel {
89
90
  }
90
91
 
91
92
  /**
92
- * Determines if the chart is installed by checking if exactly one matching installed app is found.
93
+ * Determines if the chart is installed by checking if at least one matching installed app is found.
93
94
  *
94
95
  * @returns {boolean} `true` if the chart is currently installed.
95
96
  */
96
97
  get isInstalled() {
97
- return this.matchingInstalledApps.length === 1;
98
+ return this.matchingInstalledApps.length >= 1;
98
99
  }
99
100
 
100
101
  /**
101
- * Determines if the installed app has a single available upgrade.
102
+ * Determines if at least one installed instance has an available upgrade.
102
103
  * Requires the chart to be installed.
103
104
  *
104
- * @returns {boolean} `true` if the app is installed and has a single upgrade available.
105
+ * @returns {boolean} `true` if the app is installed and at least one instance has an upgrade available.
105
106
  */
106
107
  get upgradeable() {
107
- return this.isInstalled && this.matchingInstalledApps[0].upgradeAvailable === APP_UPGRADE_STATUS.SINGLE_UPGRADE;
108
+ return this.isInstalled && this.matchingInstalledApps.some(
109
+ (app) => app.upgradeAvailable === APP_UPGRADE_STATUS.SINGLE_UPGRADE
110
+ );
111
+ }
112
+
113
+ /**
114
+ * Returns the number of installed instances of this chart.
115
+ *
116
+ * @returns {number} The count of installed instances.
117
+ */
118
+ get installedCount() {
119
+ return this.matchingInstalledApps.length;
108
120
  }
109
121
 
110
122
  /**
@@ -140,25 +152,19 @@ export default class Chart extends SteveModel {
140
152
  const subHeaderItems = [];
141
153
 
142
154
  if (latestVersion) {
143
- const hasZeroTime = latestVersion.created === ZERO_TIME;
144
-
145
155
  subHeaderItems.push({
146
156
  icon: 'icon-version-alt',
147
157
  iconTooltip: { key: 'tableHeaders.version' },
148
158
  label: latestVersion.version
149
159
  });
150
160
 
151
- const lastUpdatedItem = {
152
- icon: 'icon-refresh-alt',
153
- iconTooltip: { key: 'tableHeaders.lastUpdated' },
154
- label: hasZeroTime ? this.t('generic.na') : day(latestVersion.created).format('MMM D, YYYY')
155
- };
156
-
157
- if (hasZeroTime) {
158
- lastUpdatedItem.labelTooltip = this.t('catalog.charts.appChartCard.subHeaderItem.missingVersionDate');
161
+ if (!isMissingDate(latestVersion.created)) {
162
+ subHeaderItems.push({
163
+ icon: 'icon-refresh-alt',
164
+ iconTooltip: { key: 'tableHeaders.lastUpdated' },
165
+ label: day(latestVersion.created).format('MMM D, YYYY')
166
+ });
159
167
  }
160
-
161
- subHeaderItems.push(lastUpdatedItem);
162
168
  }
163
169
 
164
170
  const footerItems = [
@@ -206,10 +212,18 @@ export default class Chart extends SteveModel {
206
212
  }
207
213
 
208
214
  if (this.isInstalled) {
215
+ const hasMultipleInstances = this.installedCount > 1;
209
216
  const installedVersion = this.matchingInstalledApps[0]?.spec?.chart?.metadata?.version;
210
217
 
218
+ // When multiple instances, don't show version in tooltip since each may have different versions
219
+ let tooltipText = hasMultipleInstances ? this.t('generic.installedMultiple') : this.t('generic.installed');
220
+
221
+ if (!hasMultipleInstances) {
222
+ tooltipText = `${ tooltipText } (${ installedVersion })`;
223
+ }
224
+
211
225
  statuses.push({
212
- icon: 'icon-confirmation-alt', color: 'success', tooltip: { text: `${ this.t('generic.installed') } (${ installedVersion })` }
226
+ icon: 'icon-confirmation-alt', color: 'success', tooltip: { text: tooltipText }
213
227
  });
214
228
  }
215
229
 
@@ -105,7 +105,7 @@ export default class FleetApplication extends SteveModel {
105
105
 
106
106
  for (const tgt of this.spec.targets) {
107
107
  if (tgt.clusterName) {
108
- const cluster = findBy(clusters, 'metadata.name', tgt.clusterName);
108
+ const cluster = findBy(clusters, 'metadata.name', tgt.clusterName) || findBy(clusters, 'nameDisplay', tgt.clusterName);
109
109
 
110
110
  if (cluster) {
111
111
  addObject(out, cluster);
@@ -47,7 +47,7 @@ export default class FleetBundle extends SteveModel {
47
47
 
48
48
  for (const tgt of this.spec.targets) {
49
49
  if (tgt.clusterName) {
50
- const cluster = findBy(clusters, 'metadata.name', tgt.clusterName);
50
+ const cluster = findBy(clusters, 'metadata.name', tgt.clusterName) || findBy(clusters, 'nameDisplay', tgt.clusterName);
51
51
 
52
52
  if (cluster) {
53
53
  addObject(out, cluster);
@@ -5,6 +5,13 @@ export default class KontainerDriver extends Driver {
5
5
  return 'c-cluster-manager-driver-kontainerdriver';
6
6
  }
7
7
 
8
+ get parentLocationOverride() {
9
+ return {
10
+ name: 'c-cluster-manager-driver-kontainerdriver',
11
+ params: { cluster: this.$rootGetters['clusterId'] }
12
+ };
13
+ }
14
+
8
15
  get _availableActions() {
9
16
  const out = [
10
17
  {
@@ -54,6 +61,10 @@ export default class KontainerDriver extends Driver {
54
61
  return out;
55
62
  }
56
63
 
64
+ get isEmber() {
65
+ return !this.builtIn && !this.builtin;
66
+ }
67
+
57
68
  deactivate(resources = [this]) {
58
69
  this.$dispatch('promptModal', {
59
70
  componentProps: { drivers: resources, driverType: 'kontainerDrivers' },
@@ -21,7 +21,11 @@ export const configType = {
21
21
  cognito: 'oidc',
22
22
  };
23
23
 
24
- const imageOverrides = { keycloakoidc: 'keycloak', genericoidc: 'openid' };
24
+ const imageOverrides = {
25
+ azuread: 'entraid',
26
+ keycloakoidc: 'keycloak',
27
+ genericoidc: 'openid',
28
+ };
25
29
 
26
30
  export default class AuthConfig extends SteveModel {
27
31
  get _availableActions() {
@@ -6,13 +6,11 @@ import { insertAt, addObject, removeObject } from '@shell/utils/array';
6
6
  import { downloadFile } from '@shell/utils/download';
7
7
  import { parseSi } from '@shell/utils/units';
8
8
  import { parseColor, textColor } from '@shell/utils/color';
9
- import { addParams } from '@shell/utils/url';
10
9
  import { isEmpty } from '@shell/utils/object';
11
10
  import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
12
11
  import { isHarvesterCluster } from '@shell/utils/cluster';
13
12
  import SteveModel from '@shell/plugins/steve/steve-class';
14
13
  import { LINUX, WINDOWS } from '@shell/store/catalog';
15
- import { KONTAINER_TO_DRIVER } from './management.cattle.io.kontainerdriver';
16
14
  import { requireAsset } from '@shell/utils/require-asset';
17
15
  import { PINNED_CLUSTERS } from '@shell/store/prefs';
18
16
  import { copyTextToClipboard } from '@shell/utils/clipboard';
@@ -114,57 +112,6 @@ export default class MgmtCluster extends SteveModel {
114
112
  return null;
115
113
  }
116
114
 
117
- get providerForEmberParam() {
118
- // Ember wants one word called provider to tell what component to show, but has much indirect mapping to figure out what it is.
119
- let provider;
120
-
121
- // provisioner is status.driver
122
- const provisioner = KONTAINER_TO_DRIVER[(this.provisioner || '').toLowerCase()] || this.provisioner;
123
-
124
- if ( provisioner === 'rancherKubernetesEngine') {
125
- // Look for a cloud provider in one of the node templates
126
- if ( this.machinePools?.[0] ) {
127
- provider = this.machinePools[0]?.nodeTemplate?.spec?.driver || null;
128
- } else {
129
- provider = 'custom';
130
- }
131
- } else if ( this.driver ) {
132
- provider = this.driver;
133
- } else if ( provisioner && provisioner.endsWith('v2') ) {
134
- provider = provisioner;
135
- } else {
136
- provider = 'import';
137
- }
138
-
139
- return provider;
140
- }
141
-
142
- get emberEditPath() {
143
- const provider = this.providerForEmberParam;
144
-
145
- // Avoid passing falsy values as query parameters
146
- const qp = { };
147
-
148
- if (provider) {
149
- qp['provider'] = provider;
150
- }
151
-
152
- // Copied out of https://github.com/rancher/ui/blob/20f56dc54c4fc09b5f911e533cb751c13609adaf/app/models/cluster.js#L844
153
- if ( provider === 'import' && isEmpty(this.eksConfig) && isEmpty(this.gkeConfig) ) {
154
- qp.importProvider = 'other';
155
- } else if (
156
- (provider === 'amazoneks' && !isEmpty(this.eksConfig) ) ||
157
- (provider === 'gke' && !isEmpty(this.gkeConfig) )
158
- // || something for aks v2
159
- ) {
160
- qp.importProvider = KONTAINER_TO_DRIVER[provider];
161
- }
162
-
163
- const path = addParams(`/c/${ escape(this.id) }/edit`, qp);
164
-
165
- return path;
166
- }
167
-
168
115
  get groupByLabel() {
169
116
  return this.$rootGetters['i18n/t']('resourceTable.groupLabel.notInAWorkspace');
170
117
  }
@@ -8,7 +8,7 @@ export default class Feature extends HybridModel {
8
8
 
9
9
  get enabled() {
10
10
  // If lockedValue is not null, then this is the value that the flag is locked to, so that should be used
11
- if (this.status.lockedValue !== null) {
11
+ if (this.status && this.status.lockedValue !== null) {
12
12
  return this.status.lockedValue;
13
13
  }
14
14
 
@@ -17,7 +17,7 @@ export default class Feature extends HybridModel {
17
17
  }
18
18
 
19
19
  get restartRequired() {
20
- return !this.status.dynamic;
20
+ return !this.status?.dynamic;
21
21
  }
22
22
 
23
23
  get canYaml() {
@@ -43,7 +43,7 @@ export default class Feature extends HybridModel {
43
43
  // User can not disable or enable if the feature flag is locked
44
44
  // Note: lockedValue is the value that the feature flag is locked to, so it can be true or false
45
45
  // It can also be null, which indicates that the feature flag is not locked
46
- enableAction.enabled = enableAction.enabled && (this.status.lockedValue === null);
46
+ enableAction.enabled = enableAction.enabled && (this.status && this.status.lockedValue === null);
47
47
 
48
48
  out.unshift(enableAction);
49
49
 
@@ -2,10 +2,9 @@ import HybridModel from '@shell/plugins/steve/hybrid-class';
2
2
 
3
3
  const HIDDEN = ['rke', 'rancherkubernetesengine'];
4
4
 
5
- const V2 = ['amazoneks', 'googlegke', 'azureaks'];
6
5
  const IMPORTABLE = ['amazoneks', 'googlegke', 'azureaks'];
7
6
 
8
- // The Ember create page has short names that don't match the full kontainer driver
7
+ // Short names that map from the full kontainer driver name
9
8
  export const KONTAINER_TO_DRIVER = {
10
9
  amazonelasticcontainerservice: 'amazoneks',
11
10
  azurekubernetesservice: 'azureaks',
@@ -36,14 +35,6 @@ export const KEV1 = [
36
35
  'googlekubernetesengine',
37
36
  ];
38
37
 
39
- // And the Import page has even shorter ones that don't match kontainer or create...
40
- export const DRIVER_TO_IMPORT = {
41
- googlegke: 'gke',
42
- amazoneks: 'eks',
43
- azureaks: 'aks',
44
- alibaba: 'alibabacloud'
45
- };
46
-
47
38
  export default class KontainerDriver extends HybridModel {
48
39
  get showCreate() {
49
40
  if ( HIDDEN.includes(this.driverName) ) {
@@ -57,22 +48,6 @@ export default class KontainerDriver extends HybridModel {
57
48
  return this.showCreate && IMPORTABLE.includes(this.driverName);
58
49
  }
59
50
 
60
- get emberCreatePath() {
61
- let driver = this.driverName;
62
-
63
- if ( V2.includes(driver) && !driver.endsWith('v2') ) {
64
- driver += 'v2';
65
- }
66
-
67
- return `/g/clusters/add/launch/${ driver }`;
68
- }
69
-
70
- get emberImportPath() {
71
- const provider = DRIVER_TO_IMPORT[this.driverName] || this.driverName;
72
-
73
- return `/g/clusters/add/launch/import?importProvider=${ provider }`;
74
- }
75
-
76
51
  get driverName() {
77
52
  if (!this.spec.builtIn) {
78
53
  // if the driver is not built in, there's a good change its a custom one
@@ -5,25 +5,39 @@ import { set } from '@shell/utils/object';
5
5
 
6
6
  export default class AlertmanagerConfig extends SteveModel {
7
7
  applyDefaults() {
8
- if (this.spec) {
9
- return this.spec;
10
- }
11
- const existingReceivers = this.spec?.route?.receivers || [];
12
-
13
- const defaultSpec = {
14
- receivers: [...existingReceivers],
15
- route: {
16
- receivers: this.spec?.route?.receivers || [],
17
- groupBy: this.spec?.route?.groupBy || [],
18
- groupWait: this.spec?.route?.groupWait || '30s',
19
- groupInterval: this.spec?.route?.groupInterval || '5m',
20
- repeatInterval: this.spec?.route?.repeatInterval || '4h',
21
- match: this.spec?.route?.match || {},
22
- matchRe: this.spec?.route?.matchRe || {}
8
+ const spec = this.spec || {};
9
+
10
+ spec.receivers = spec.receivers || [];
11
+
12
+ // Always provide a route object so the Route tab has something to bind to,
13
+ // even when loading a resource that was saved without `spec.route`.
14
+ const route = { ...(spec.route || {}) };
15
+
16
+ route.groupBy = route.groupBy || [];
17
+ route.groupWait = route.groupWait || '30s';
18
+ route.groupInterval = route.groupInterval || '5m';
19
+ route.repeatInterval = route.repeatInterval || '4h';
20
+ route.matchers = route.matchers || [];
21
+
22
+ spec.route = route;
23
+
24
+ set(this, 'spec', spec);
25
+ }
26
+
27
+ cleanForSave(data, forNew) {
28
+ const val = super.cleanForSave(data, forNew);
29
+ const route = val?.spec?.route;
30
+
31
+ if (route) {
32
+ // When a route is present its `receiver` is required and must match an
33
+ // entry in `spec.receivers`. Until the user has defined a receiver the
34
+ // root route can't direct alerts anywhere, so drop it entirely
35
+ if (!route.receiver) {
36
+ delete val.spec.route;
23
37
  }
24
- };
38
+ }
25
39
 
26
- set(this, 'spec', defaultSpec);
40
+ return val;
27
41
  }
28
42
 
29
43
  get _availableActions() {
@@ -13,6 +13,13 @@ export default class NodeDriver extends Driver {
13
13
  return 'c-cluster-manager-driver-nodedriver';
14
14
  }
15
15
 
16
+ get parentLocationOverride() {
17
+ return {
18
+ name: 'c-cluster-manager-driver-nodedriver',
19
+ params: { cluster: this.$rootGetters['clusterId'] }
20
+ };
21
+ }
22
+
16
23
  get _availableActions() {
17
24
  const out = [
18
25
  {
package/models/pod.js CHANGED
@@ -4,6 +4,7 @@ import { NODE, WORKLOAD_TYPES } from '@shell/config/types';
4
4
  import { escapeHtml, shortenedImage } from '@shell/utils/string';
5
5
  import WorkloadService from '@shell/models/workload.service';
6
6
  import { deleteProperty } from '@shell/utils/object';
7
+ import { POD_RESTARTS_REG_EX } from '@shell/types/resources/pod';
7
8
 
8
9
  export const WORKLOAD_PRIORITY = {
9
10
  [WORKLOAD_TYPES.DEPLOYMENT]: 1,
@@ -222,6 +223,9 @@ export default class Pod extends WorkloadService {
222
223
  return this.$rootGetters['i18n/t']('resourceTable.groupLabel.node', { name: escapeHtml(name) });
223
224
  }
224
225
 
226
+ /**
227
+ * How many times has the first container restarted
228
+ */
225
229
  get restartCount() {
226
230
  if (this.status.containerStatuses) {
227
231
  return this.status?.containerStatuses[0].restartCount || 0;
@@ -230,6 +234,20 @@ export default class Pod extends WorkloadService {
230
234
  return 0;
231
235
  }
232
236
 
237
+ /**
238
+ * How many times does native kube report this pod has restarted
239
+ */
240
+ get restartsCount() {
241
+ return this.metadata?.fields?.[3]?.match(POD_RESTARTS_REG_EX)?.[1] || '';
242
+ }
243
+
244
+ /**
245
+ * When does native kube think the last pod restart happen?
246
+ */
247
+ get restartsLaster() {
248
+ return this.metadata?.fields?.[3]?.match(POD_RESTARTS_REG_EX)?.[2] || '';
249
+ }
250
+
233
251
  processSaveResponse(res) {
234
252
  if (res._headers && res._headers.warning) {
235
253
  const warnings = res._headers.warning.split('299') || [];
@@ -9,6 +9,7 @@ import WorkloadService from '@shell/models/workload.service';
9
9
  import { matching } from '@shell/utils/selector-typed';
10
10
  import { defineAsyncComponent, markRaw } from 'vue';
11
11
  import { useResourceCardRow } from '@shell/components/Resource/Detail/Card/StateCard/composables';
12
+ import { colorForState as colorForStateFn, stateDisplay as stateDisplayFn } from '@shell/plugins/dashboard-store/resource-class';
12
13
 
13
14
  export const defaultContainer = {
14
15
  imagePullPolicy: 'Always',
@@ -622,12 +623,29 @@ export default class Workload extends WorkloadService {
622
623
 
623
624
  calcPodGauges(pods) {
624
625
  const out = { };
626
+ let refPods = pods;
625
627
 
626
- if (!pods) {
628
+ if (this.metadata.associatedData) {
629
+ refPods = [];
630
+ this.metadata.associatedData.forEach((w) => {
631
+ if (w.gvk.kind.toLowerCase() !== POD) {
632
+ return;
633
+ }
634
+
635
+ return w.data.forEach((p) => {
636
+ refPods.push({
637
+ stateColor: colorForStateFn(p.state.name, p.state.error === 'true', p.state.transitioning === 'true'),
638
+ stateDisplay: stateDisplayFn(p.state.name),
639
+ });
640
+ });
641
+ });
642
+ }
643
+
644
+ if (!refPods) {
627
645
  return out;
628
646
  }
629
647
 
630
- pods.map((pod) => {
648
+ refPods.map((pod) => {
631
649
  const { stateColor, stateDisplay } = pod;
632
650
 
633
651
  if (out[stateDisplay]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.11",
3
+ "version": "3.0.12-rc.2",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancher/dashboard",
6
6
  "license": "Apache-2.0",
@@ -32,7 +32,6 @@
32
32
  "@aws-sdk/client-eks": "3.879.0",
33
33
  "@aws-sdk/client-iam": "3.863.0",
34
34
  "@aws-sdk/client-kms": "3.863.0",
35
- "@smithy/fetch-http-handler": "5.1.1",
36
35
  "@babel/plugin-proposal-optional-chaining": "7.21.0",
37
36
  "@babel/plugin-proposal-private-methods": "7.18.6",
38
37
  "@babel/plugin-proposal-private-property-in-object": "7.14.5",
@@ -40,6 +39,7 @@
40
39
  "@novnc/novnc": "1.2.0",
41
40
  "@popperjs/core": "2.11.8",
42
41
  "@rancher/icons": "2.0.55",
42
+ "@smithy/fetch-http-handler": "5.1.1",
43
43
  "@types/is-url": "1.2.30",
44
44
  "@types/node": "25.3.3",
45
45
  "@types/semver": "^7.5.8",
@@ -52,19 +52,19 @@
52
52
  "@vue/vue3-jest": "27.0.0",
53
53
  "add": "2.0.6",
54
54
  "ansi_up": "5.0.0",
55
+ "axios": "1.15.0",
55
56
  "axios-retry": "3.1.9",
56
- "axios": "1.13.5",
57
57
  "babel-eslint": "10.1.0",
58
58
  "babel-plugin-module-resolver": "5.0.2",
59
59
  "babel-preset-vue": "2.0.2",
60
60
  "cache-loader": "4.1.0",
61
61
  "chart.js": "4.5.1",
62
62
  "clipboard-polyfill": "4.0.1",
63
- "codemirror-editor-vue3": "2.8.0",
64
63
  "codemirror": ">=5.64.0 <6",
64
+ "codemirror-editor-vue3": "2.8.0",
65
65
  "color": "5.0.3",
66
- "cookie-universal": "2.2.2",
67
66
  "cookie": "0.7.0",
67
+ "cookie-universal": "2.2.2",
68
68
  "core-js": "3.48.0",
69
69
  "cron-validator": "1.4.0",
70
70
  "cronstrue": "3.9.0",
@@ -73,14 +73,14 @@
73
73
  "csv-loader": "3.0.3",
74
74
  "custom-event-polyfill": "^1.0.7",
75
75
  "cypress": "11.1.0",
76
- "d3-selection": "3.0.0",
77
76
  "d3": "7.3.0",
77
+ "d3-selection": "3.0.0",
78
78
  "dayjs": "1.11.18",
79
- "defu": "6.1.4",
80
79
  "diff2html": "3.4.24",
81
80
  "dompurify": "3.2.5",
82
81
  "element-matches": "^0.1.2",
83
82
  "entities": "4.5.0",
83
+ "eslint": "7.32.0",
84
84
  "eslint-config-standard": "16.0.3",
85
85
  "eslint-import-resolver-node": "0.3.9",
86
86
  "eslint-module-utils": "2.6.1",
@@ -89,7 +89,6 @@
89
89
  "eslint-plugin-jest": "24.4.0",
90
90
  "eslint-plugin-n": "15.2.0",
91
91
  "eslint-plugin-vue": "9.32.0",
92
- "eslint": "7.32.0",
93
92
  "event-target-shim": "5.0.1",
94
93
  "express": "4.17.1",
95
94
  "file-saver": "2.0.2",
@@ -100,13 +99,13 @@
100
99
  "intl-messageformat": "7.8.4",
101
100
  "ip": "2.0.1",
102
101
  "is-url": "1.2.4",
102
+ "jest": "27.5.1",
103
103
  "jest-serializer-vue": "2.0.2",
104
104
  "jexl": "2.3.0",
105
- "jest": "27.5.1",
106
105
  "jquery": "3.5.1",
107
106
  "js-cookie": "3.0.5",
108
- "js-yaml-loader": "1.2.2",
109
107
  "js-yaml": "4.1.1",
108
+ "js-yaml-loader": "1.2.2",
110
109
  "jsdiff": "1.1.1",
111
110
  "jsonpath-plus": "10.3.0",
112
111
  "jsrsasign": "11.1.1",
@@ -118,8 +117,8 @@
118
117
  "nyc": "17.1.0",
119
118
  "papaparse": "5.3.0",
120
119
  "portal-vue": "~3.0.0",
121
- "sass-loader": "12.6.0",
122
120
  "sass": "1.97.3",
121
+ "sass-loader": "12.6.0",
123
122
  "serve-static": "1.14.1",
124
123
  "set-cookie-parser": "2.4.6",
125
124
  "shell-quote": "1.7.3",
@@ -131,10 +130,11 @@
131
130
  "ufo": "0.7.11",
132
131
  "unfetch": "4.2.0",
133
132
  "url-parse": "1.5.10",
133
+ "vee-validate": "^4.15.1",
134
+ "vue": "3.5.29",
134
135
  "vue-router": "4.6.4",
135
136
  "vue-select": "4.0.0-beta.6",
136
137
  "vue-server-renderer": "2.7.16",
137
- "vue": "3.5.29",
138
138
  "vue3-resize": "0.2.0",
139
139
  "vue3-virtual-scroll-list": "0.2.1",
140
140
  "vuedraggable": "4.1.0",
@@ -142,12 +142,12 @@
142
142
  "webpack-bundle-analyzer": "4.10.2",
143
143
  "webpack-virtual-modules": "0.6.2",
144
144
  "worker-loader": "3.0.8",
145
+ "xterm": "5.2.1",
145
146
  "xterm-addon-canvas": "0.5.0",
146
147
  "xterm-addon-fit": "0.8.0",
147
148
  "xterm-addon-search": "0.13.0",
148
149
  "xterm-addon-web-links": "0.9.0",
149
150
  "xterm-addon-webgl": "0.16.0",
150
- "xterm": "5.2.1",
151
151
  "yarn": "1.22.22"
152
152
  },
153
153
  "resolutions": {
@@ -183,7 +183,6 @@ function getTooltip(key: string, fallback?: string): string | undefined {
183
183
  }
184
184
 
185
185
  &-button {
186
- text-transform: capitalize;
187
186
  min-width: 0;
188
187
  max-width: 100%;
189
188