@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.3

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 (272) hide show
  1. package/apis/impl/apis.ts +6 -0
  2. package/apis/index.ts +26 -0
  3. package/apis/intf/resources-api/cluster-api.ts +18 -0
  4. package/apis/intf/resources-api/mgmt-api.ts +15 -0
  5. package/apis/intf/resources-api/resource-base.ts +107 -0
  6. package/apis/intf/resources-api/resource-constants.ts +147 -0
  7. package/apis/intf/resources-api/resources-api.ts +143 -0
  8. package/apis/intf/resources.ts +49 -0
  9. package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
  10. package/apis/intf/shell-api/proxy.ts +216 -0
  11. package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
  12. package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
  13. package/apis/intf/shell.ts +12 -6
  14. package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
  15. package/apis/resources/index.ts +22 -0
  16. package/apis/resources/resources-api-class.ts +187 -0
  17. package/apis/shell/__tests__/proxy.test.ts +369 -0
  18. package/apis/shell/index.ts +8 -1
  19. package/apis/shell/modal.ts +4 -1
  20. package/apis/shell/notifications.ts +9 -6
  21. package/apis/shell/proxy.ts +256 -0
  22. package/apis/shell/slide-in.ts +4 -1
  23. package/apis/vue-shim.d.ts +2 -1
  24. package/assets/data/aws-regions.json +4 -0
  25. package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
  26. package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
  27. package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
  28. package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
  29. package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
  30. package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
  31. package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
  32. package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
  33. package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
  34. package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
  35. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
  36. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
  37. package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
  38. package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
  39. package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
  40. package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
  41. package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
  42. package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
  43. package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
  44. package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
  45. package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
  46. package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
  47. package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
  48. package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
  49. package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
  50. package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
  51. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
  52. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
  53. package/assets/styles/base/_variables.scss +2 -0
  54. package/assets/styles/fonts/_fontstack.scss +132 -8
  55. package/assets/translations/en-us.yaml +22 -5
  56. package/chart/monitoring/index.vue +10 -1
  57. package/components/ActionDropdownShell.vue +2 -1
  58. package/components/CruResourceFooter.vue +9 -5
  59. package/components/ExplorerProjectsNamespaces.vue +1 -1
  60. package/components/InstallHelmCharts.vue +2 -2
  61. package/components/LandingPagePreference.vue +14 -5
  62. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
  63. package/components/Resource/Detail/Metadata/index.vue +6 -0
  64. package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
  65. package/components/Resource/Detail/SpacedRow.vue +3 -1
  66. package/components/Resource/Detail/TitleBar/index.vue +10 -11
  67. package/components/ResourceList/Masthead.vue +12 -8
  68. package/components/SelectIconGrid.vue +0 -10
  69. package/components/SingleClusterInfo.vue +1 -0
  70. package/components/SortableTable/__tests__/sorting.test.ts +126 -0
  71. package/components/SortableTable/index.vue +6 -9
  72. package/components/SortableTable/selection.js +23 -5
  73. package/components/SortableTable/sorting.js +6 -3
  74. package/components/Wizard.vue +14 -13
  75. package/components/fleet/FleetBundles.vue +100 -12
  76. package/components/fleet/FleetClusterTargets/index.vue +37 -15
  77. package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
  78. package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
  79. package/components/form/LabeledSelect.vue +20 -3
  80. package/components/form/NameNsDescription.vue +11 -0
  81. package/components/form/Security.vue +6 -2
  82. package/components/form/WorkloadPorts.vue +2 -7
  83. package/components/form/__tests__/Security.test.ts +76 -0
  84. package/components/formatter/Autoscaler.vue +4 -4
  85. package/components/formatter/ClusterKubeVersion.vue +27 -0
  86. package/components/formatter/ClusterLink.vue +1 -7
  87. package/components/formatter/ClusterProvider.vue +6 -10
  88. package/components/formatter/FleetSummaryGraph.vue +0 -3
  89. package/components/formatter/MachineSummaryGraph.vue +1 -1
  90. package/components/formatter/PodsUsage.vue +2 -2
  91. package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
  92. package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
  93. package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
  94. package/components/nav/NamespaceFilter.vue +2 -2
  95. package/components/nav/TopLevelMenu.helper.ts +15 -3
  96. package/components/nav/TopLevelMenu.vue +16 -5
  97. package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
  98. package/components/templates/home.vue +18 -0
  99. package/components/templates/plain.vue +18 -0
  100. package/components/templates/standalone.vue +17 -0
  101. package/composables/useFormValidation.ts +93 -0
  102. package/composables/useVeeValidateField.test.ts +159 -0
  103. package/composables/useVeeValidateField.ts +67 -0
  104. package/config/pagination-table-headers.js +18 -1
  105. package/config/product/manager.js +82 -21
  106. package/config/router/routes.js +6 -0
  107. package/config/table-headers.js +20 -1
  108. package/config/types.js +2 -1
  109. package/core/__tests__/plugin-products.test.ts +904 -20
  110. package/core/plugin-products-base.ts +107 -7
  111. package/core/plugin-products.ts +4 -0
  112. package/core/plugin-types.ts +111 -1
  113. package/core/plugin.ts +15 -7
  114. package/core/productDebugger.js +9 -4
  115. package/core/types-provisioning.ts +43 -30
  116. package/core/types.ts +57 -20
  117. package/detail/__tests__/pod.test.ts +41 -0
  118. package/detail/harvesterhci.io.management.cluster.vue +6 -2
  119. package/detail/pod.vue +1 -1
  120. package/detail/provisioning.cattle.io.cluster.vue +4 -10
  121. package/edit/auth/__tests__/azuread.test.ts +217 -34
  122. package/edit/auth/azuread.vue +122 -14
  123. package/edit/auth/oidc.vue +2 -2
  124. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
  125. package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
  126. package/edit/networking.k8s.io.ingress/index.vue +75 -20
  127. package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
  128. package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
  129. package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
  130. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  131. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
  132. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
  133. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
  134. package/edit/secret/__tests__/ssh.test.ts +5 -6
  135. package/edit/secret/basic.vue +31 -0
  136. package/edit/secret/index.vue +68 -17
  137. package/edit/secret/registry.vue +38 -0
  138. package/edit/secret/ssh.vue +29 -0
  139. package/edit/secret/tls.vue +30 -0
  140. package/edit/service.vue +4 -4
  141. package/edit/workload/Upgrading.vue +3 -3
  142. package/edit/workload/__tests__/Upgrading.test.ts +6 -9
  143. package/edit/workload/mixins/workload.js +2 -1
  144. package/list/fleet.cattle.io.bundle.vue +7 -104
  145. package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
  146. package/list/provisioning.cattle.io.cluster.vue +262 -180
  147. package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
  148. package/mixins/__tests__/chart.test.ts +112 -0
  149. package/mixins/brand.js +2 -1
  150. package/mixins/chart.js +12 -8
  151. package/mixins/resource-fetch-api-pagination.js +41 -5
  152. package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
  153. package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
  154. package/models/__tests__/management.cattle.io.node.ts +6 -5
  155. package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
  156. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
  157. package/models/base-cluster.x-k8s.io.js +26 -0
  158. package/models/cluster.js +1 -1
  159. package/models/cluster.x-k8s.io.machine.js +4 -22
  160. package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
  161. package/models/cluster.x-k8s.io.machineset.js +2 -20
  162. package/models/compliance.cattle.io.clusterscan.js +130 -2
  163. package/models/ext.cattle.io.kubeconfig.ts +4 -7
  164. package/models/fleet-application.js +3 -1
  165. package/models/management.cattle.io.cluster.js +417 -40
  166. package/models/management.cattle.io.node.js +6 -4
  167. package/models/management.cattle.io.nodepool.js +1 -1
  168. package/models/networking.k8s.io.ingress.js +12 -4
  169. package/models/provisioning.cattle.io.cluster.js +47 -330
  170. package/models/rke.cattle.io.etcdsnapshot.js +1 -2
  171. package/package.json +11 -29
  172. package/pages/__tests__/readme.test.ts +49 -0
  173. package/pages/auth/setup.vue +2 -3
  174. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
  175. package/pages/c/_cluster/apps/charts/chart.vue +60 -8
  176. package/pages/c/_cluster/apps/charts/install.vue +10 -7
  177. package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
  178. package/pages/c/_cluster/explorer/index.vue +5 -49
  179. package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
  180. package/pages/c/_cluster/istio/index.vue +21 -6
  181. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
  182. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
  183. package/pages/c/_cluster/uiplugins/index.vue +203 -197
  184. package/pages/diagnostic.vue +13 -17
  185. package/pages/fail-whale.vue +18 -0
  186. package/pages/home.vue +77 -260
  187. package/pages/readme.vue +88 -0
  188. package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
  189. package/plugins/dashboard-store/actions.js +40 -18
  190. package/plugins/dashboard-store/resource-class.js +5 -2
  191. package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
  192. package/plugins/steve/steve-pagination-utils.ts +11 -3
  193. package/plugins/steve/subscribe.js +35 -5
  194. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
  195. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
  196. package/rancher-components/RcButton/RcButton.test.ts +37 -1
  197. package/rancher-components/RcButton/RcButton.vue +38 -8
  198. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
  199. package/store/__tests__/catalog.test.ts +115 -1
  200. package/store/__tests__/type-map.test.ts +556 -1
  201. package/store/action-menu.js +8 -3
  202. package/store/auth.js +1 -1
  203. package/store/aws.js +27 -16
  204. package/store/catalog.js +27 -3
  205. package/store/digitalocean.js +20 -38
  206. package/store/index.js +2 -0
  207. package/store/linode.js +25 -40
  208. package/store/pnap.js +1 -0
  209. package/store/type-map.js +111 -29
  210. package/tsconfig.paths.json +8 -8
  211. package/types/kube/kube-api.ts +14 -1
  212. package/types/rancher/steve.api.ts +12 -12
  213. package/types/resources/settings.d.ts +2 -1
  214. package/types/shell/index.d.ts +102 -2
  215. package/types/store/dashboard-store.types.ts +108 -11
  216. package/types/store/pagination.types.ts +6 -3
  217. package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
  218. package/utils/__tests__/async.test.ts +87 -0
  219. package/utils/__tests__/aws.test.ts +140 -0
  220. package/utils/__tests__/banners.test.ts +176 -0
  221. package/utils/__tests__/chart.test.ts +64 -1
  222. package/utils/__tests__/color.test.ts +226 -0
  223. package/utils/__tests__/duration.test.ts +140 -0
  224. package/utils/__tests__/fleet.test.ts +340 -0
  225. package/utils/__tests__/ingress.test.ts +553 -0
  226. package/utils/__tests__/kube.test.ts +68 -0
  227. package/utils/__tests__/namespace-filter.test.ts +109 -0
  228. package/utils/__tests__/pagination-utils.test.ts +361 -0
  229. package/utils/__tests__/parse-externalid.test.ts +137 -0
  230. package/utils/__tests__/perf-setting.utils.test.ts +98 -0
  231. package/utils/__tests__/poller-sequential.test.ts +177 -0
  232. package/utils/__tests__/poller.test.ts +170 -0
  233. package/utils/__tests__/promise.test.ts +346 -0
  234. package/utils/__tests__/settings.test.ts +140 -0
  235. package/utils/__tests__/sort-utils.test.ts +301 -0
  236. package/utils/__tests__/string-utils.test.ts +798 -0
  237. package/utils/__tests__/string.test.ts +23 -1
  238. package/utils/__tests__/style.test.ts +154 -0
  239. package/utils/__tests__/svg-filter.test.ts +184 -0
  240. package/utils/__tests__/units.test.ts +417 -0
  241. package/utils/__tests__/versions.test.ts +128 -0
  242. package/utils/__tests__/xccdf.test.ts +391 -0
  243. package/utils/chart.js +36 -0
  244. package/utils/fleet.ts +13 -3
  245. package/utils/gatekeeper/__tests__/util.test.ts +174 -0
  246. package/utils/gc/__tests__/gc-interval.test.ts +119 -0
  247. package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
  248. package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
  249. package/utils/gc/__tests__/gc.test.ts +487 -0
  250. package/utils/ingress.ts +9 -1
  251. package/utils/pagination-utils.ts +2 -1
  252. package/utils/string.js +25 -2
  253. package/utils/uiplugins.ts +5 -5
  254. package/utils/validators/__tests__/cluster-name.test.ts +110 -0
  255. package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
  256. package/utils/validators/__tests__/index.test.ts +481 -0
  257. package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
  258. package/utils/validators/__tests__/misc-validators.test.ts +246 -0
  259. package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
  260. package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
  261. package/utils/validators/__tests__/role-template.test.ts +149 -0
  262. package/utils/validators/__tests__/service.test.ts +283 -0
  263. package/utils/validators/__tests__/setting.test.js +32 -0
  264. package/utils/validators/formRules/__tests__/index.test.ts +50 -0
  265. package/utils/validators/formRules/index.ts +5 -5
  266. package/utils/validators/machine-pool.ts +1 -1
  267. package/utils/validators/setting.js +18 -3
  268. package/utils/xccdf.ts +418 -0
  269. package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
  270. package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
  271. package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
  272. package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
@@ -1,20 +1,25 @@
1
- import { CATALOG, CLUSTER_BADGE } from '@shell/config/labels-annotations';
1
+ import { CATALOG, CLUSTER_BADGE, NODE_ARCHITECTURE } from '@shell/config/labels-annotations';
2
2
  import {
3
- NODE, FLEET, MANAGEMENT, CAPI, EXT
3
+ NODE, FLEET, MANAGEMENT, CAPI, EXT,
4
+ NORMAN,
5
+ HCI
4
6
  } from '@shell/config/types';
5
7
  import { insertAt, addObject, removeObject } from '@shell/utils/array';
6
8
  import { downloadFile } from '@shell/utils/download';
7
9
  import { parseSi } from '@shell/utils/units';
8
10
  import { parseColor, textColor } from '@shell/utils/color';
9
- import { isEmpty } from '@shell/utils/object';
11
+ import { isEmpty, isEqual } from '@shell/utils/object';
10
12
  import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
11
13
  import { isHarvesterCluster } from '@shell/utils/cluster';
12
14
  import SteveModel from '@shell/plugins/steve/steve-class';
13
15
  import { LINUX, WINDOWS } from '@shell/store/catalog';
16
+ import { KEV1 } from './management.cattle.io.kontainerdriver';
14
17
  import { requireAsset } from '@shell/utils/require-asset';
15
18
  import { PINNED_CLUSTERS } from '@shell/store/prefs';
16
19
  import { copyTextToClipboard } from '@shell/utils/clipboard';
17
20
  import { isHostedProvider } from '@shell/utils/provider';
21
+ import { ucFirst } from '@shell/utils/string';
22
+ import { sortBy } from '@shell/utils/sort';
18
23
 
19
24
  const DEFAULT_BADGE_COLOR = '#707070';
20
25
 
@@ -55,6 +60,30 @@ export default class MgmtCluster extends SteveModel {
55
60
  return (schema?.collectionMethods || []).includes('POST');
56
61
  }
57
62
 
63
+ get availableActions() {
64
+ // If on the Cluster Management Cluster List use the provisioning cluster actions instead of the management cluster
65
+ // This resolution feels a bit hacky, however the alternative would be to create a table prop and lots of weird plumbing
66
+ // It's a small use case, so just doing a limited solution
67
+ const listLocation = this.provCluster?.listLocation || {};
68
+ const currentRoute = this.currentRoute();
69
+
70
+ // Compare params, but exclude cluster (could be a bad link which provides a specific cluster)
71
+ const { cluster: _1, ...lLProduct } = listLocation.params || {};
72
+ const { cluster: _2, ...cRParams } = currentRoute.params;
73
+ const paramsEqual = isEqual(lLProduct, cRParams);
74
+
75
+ const isClusterManagementListPage = listLocation?.name === currentRoute?.name && paramsEqual;
76
+
77
+ if (isClusterManagementListPage) {
78
+ return this.provCluster?.availableActions.map((action) => ({
79
+ ...action,
80
+ altResource: this.provCluster, // actions work by using the row's resource with the function name. this overrides that
81
+ }));
82
+ }
83
+
84
+ return super.availableActions;
85
+ }
86
+
58
87
  get _availableActions() {
59
88
  const out = super._availableActions;
60
89
 
@@ -96,24 +125,171 @@ export default class MgmtCluster extends SteveModel {
96
125
  }
97
126
 
98
127
  get provisioner() {
128
+ // Sometimes the driver will be empty (like unconnected custom rke2 clusters), so fall back on the new and improved status.provider
129
+ if (this.status?.provider) {
130
+ if (['gke', 'eks', 'aks', 'k3s'].includes(this.status.provider)) {
131
+ // Defensive coding. we're now using status.provider which uses lowercase for some distro's
132
+ // To support the old use case of upper case status.driver values make status.provider upper case...
133
+ return this.status.provider.toUpperCase();
134
+ }
135
+
136
+ return this.status.provider;
137
+ }
138
+
99
139
  // For imported K3s clusters, this.status.driver is 'k3s.'
100
- return this.status?.driver ? this.status.driver : 'imported';
140
+ if (this.status?.driver) {
141
+ return this.status.driver;
142
+ }
143
+
144
+ return 'imported';
145
+ }
146
+
147
+ get provisionerDisplay() {
148
+ // Allow a model extension to override the display of the provisioner
149
+ if (this.customProvisionerHelper?.provisionerDisplay) {
150
+ return this.customProvisionerHelper?.provisionerDisplay(this);
151
+ }
152
+
153
+ let provisioner = this.provisioner?.toLowerCase();
154
+
155
+ if (provisioner === 'rke.windows') {
156
+ provisioner = 'rkeWindows';
157
+ }
158
+
159
+ if (!this.$rootGetters['i18n/exists'](`cluster.provider.${ provisioner }`)) {
160
+ provisioner = 'other';
161
+ }
162
+
163
+ return this.$rootGetters['i18n/withFallback'](`cluster.provider."${ provisioner }"`, null, ucFirst(this.provisioner));
164
+ }
165
+
166
+ /**
167
+ * Get the custom provisioner helper for this model
168
+ */
169
+ get customProvisionerHelper() {
170
+ if (!this.provCluster) {
171
+ return null;
172
+ }
173
+
174
+ // Find the first model extension that says it can be used for this model
175
+ return this.modelExtensions.find((modelExt) => modelExt.useFor ? modelExt.useFor(this.provCluster) : false);
176
+ }
177
+
178
+ get provider() {
179
+ return this.status?.provider;
101
180
  }
102
181
 
103
182
  get machineProvider() {
104
- const kind = this.machinePools?.[0]?.provider;
183
+ return this.status?.info.machineProvider;
184
+ }
185
+
186
+ get machineProviderDisplay() {
187
+ if (this.customProvisionerHelper?.machineProviderDisplay) {
188
+ return this.customProvisionerHelper?.machineProviderDisplay(this);
189
+ }
190
+
191
+ const provider = (this.machineProvider || '').toLowerCase();
192
+
193
+ if ( provider ) {
194
+ return this.$rootGetters['i18n/withFallback'](`cluster.provider."${ provider }"`, null, provider);
195
+ } else {
196
+ return this.$rootGetters['i18n/t']('generic.unknown');
197
+ }
198
+ }
199
+
200
+ // Is the cluster a legacy (unsupported) KEV1 cluster?
201
+ get isKev1() {
202
+ return KEV1.includes(this.spec?.genericEngineConfig?.driverName);
203
+ }
204
+
205
+ get isImported() {
206
+ const provider = this.status?.provider;
207
+ const driver = this.status?.driver;
208
+
209
+ // The main case
210
+ if (provider === 'k3s' || provider === 'rke2') {
211
+ return driver === provider;
212
+ }
213
+ // The 'waiting' case
214
+ if (!provider && (driver === 'k3s' || driver === 'rke2')) {
215
+ return true;
216
+ }
217
+
218
+ // imported KEv2
219
+ // we can't rely on this.provisioner to determine imported-ness for these clusters, as it will return 'aks' 'eks' 'gke' for both provisioned and imported clusters
220
+ if (this.isHostedKubernetesProvider && !!this.provCluster?.providerConfig?.imported) {
221
+ return true;
222
+ }
223
+
224
+ return this.provisioner === 'imported';
225
+ }
105
226
 
106
- if ( kind ) {
107
- return kind.replace(/config$/i, '').toLowerCase();
108
- } else if ( this.spec?.internal ) {
109
- return 'local';
227
+ get isCustom() {
228
+ if ( this.isRke2 ) {
229
+ return !(this.provCluster?.spec?.rkeConfig?.machinePools?.length);
110
230
  }
111
231
 
112
- return null;
232
+ return false;
233
+ }
234
+
235
+ get isImportedK3s() {
236
+ return this.isImported && this.isK3s;
237
+ }
238
+
239
+ get isImportedRke2() {
240
+ return this.isImported && this.provider?.startsWith('rke2');
241
+ }
242
+
243
+ get isK3s() {
244
+ return this.status ? this.status.provider === 'k3s' : (this.kubernetesVersion || '').includes('k3s') ;
245
+ }
246
+
247
+ get isRke2() {
248
+ return !!this.provCluster?.isRke2;
249
+ }
250
+
251
+ // identify v2 provisioning clusters created using upstream capi infrastructure providers instead of rancher/machine
252
+ get isCapiHybrid() {
253
+ if (!this.isRke2) {
254
+ return false;
255
+ }
256
+
257
+ const machineReferences = (this.provCluster.spec?.rkeConfig?.machinePools || []).map((pool) => pool.machineConfigRef);
258
+
259
+ const capiMachines = machineReferences.find((r) => r?.apiVersion?.includes('cluster.x-k8s.io'));
260
+
261
+ return !!capiMachines;
262
+ }
263
+
264
+ get isHostedKubernetesProvider() {
265
+ const context = {
266
+ dispatch: this.$dispatch,
267
+ getters: this.$getters,
268
+ axios: this.$axios,
269
+ $extension: this.$extension,
270
+ t: (...args) => this.t.apply(this, args),
271
+ };
272
+
273
+ return isHostedProvider(context, this.provisioner);
274
+ }
275
+
276
+ get isPrivateHostedProvider() {
277
+ if (this.isHostedKubernetesProvider && this.provisioner) {
278
+ switch (this.provisioner.toLowerCase()) {
279
+ case 'gke':
280
+ return this.spec?.gkeConfig?.privateClusterConfig?.enablePrivateEndpoint;
281
+ case 'eks':
282
+ return this.spec?.eksConfig?.privateAccess;
283
+ case 'aks':
284
+ return this.spec?.aksConfig?.privateCluster;
285
+ }
286
+ }
287
+
288
+ return false;
113
289
  }
114
290
 
115
291
  get groupByLabel() {
116
- return this.$rootGetters['i18n/t']('resourceTable.groupLabel.notInAWorkspace');
292
+ return `Workspace: ${ this.spec.fleetWorkspaceName }`;
117
293
  }
118
294
 
119
295
  get isReady() {
@@ -138,10 +314,7 @@ export default class MgmtCluster extends SteveModel {
138
314
  }
139
315
 
140
316
  get kubernetesVersionRaw() {
141
- const fromStatus = this.status?.version?.gitVersion;
142
- const fromSpec = this.config?.kubernetesVersion;
143
-
144
- return fromStatus || fromSpec;
317
+ return this.statusInfo.kubernetesVersion;
145
318
  }
146
319
 
147
320
  get kubernetesVersion() {
@@ -161,7 +334,7 @@ export default class MgmtCluster extends SteveModel {
161
334
  }
162
335
 
163
336
  get providerOs() {
164
- if ( this.status?.provider.endsWith('.windows')) {
337
+ if ( this.provider?.endsWith('.windows')) {
165
338
  return 'windows';
166
339
  }
167
340
 
@@ -204,20 +377,8 @@ export default class MgmtCluster extends SteveModel {
204
377
  return isHarvesterCluster(this);
205
378
  }
206
379
 
207
- get isHostedKubernetesProvider() {
208
- const context = {
209
- dispatch: this.$dispatch,
210
- getters: this.$getters,
211
- axios: this.$axios,
212
- $extension: this.$extension,
213
- t: (...args) => this.t.apply(this, args),
214
- };
215
-
216
- return isHostedProvider(context, this.provisioner);
217
- }
218
-
219
380
  get providerLogo() {
220
- let provider = this.status?.provider || 'kubernetes';
381
+ let provider = this.provider || 'kubernetes';
221
382
 
222
383
  if (this.isHarvester) {
223
384
  provider = HARVESTER;
@@ -248,6 +409,136 @@ export default class MgmtCluster extends SteveModel {
248
409
  return this.providerLogo;
249
410
  }
250
411
 
412
+ get pools() {
413
+ const deployments = this.$rootGetters['management/all'](CAPI.MACHINE_DEPLOYMENT).filter((pool) => pool.spec?.clusterName === this.provClusterName);
414
+
415
+ if (!!deployments.length) {
416
+ return deployments;
417
+ }
418
+
419
+ return this.machinePools;
420
+ }
421
+
422
+ get desired() {
423
+ return this.pools.reduce((acc, pool) => acc + (pool.desired || 0), 0);
424
+ }
425
+
426
+ get pending() {
427
+ return this.pools.reduce((acc, pool) => acc + (pool.pending || 0), 0);
428
+ }
429
+
430
+ get outdated() {
431
+ return this.pools.reduce((acc, pool) => acc + (pool.outdated || 0), 0);
432
+ }
433
+
434
+ get ready() {
435
+ return this.pools.reduce((acc, pool) => acc + (pool.ready || 0), 0);
436
+ }
437
+
438
+ get stateParts() {
439
+ const out = [
440
+ {
441
+ label: 'Pending',
442
+ color: 'bg-info',
443
+ textColor: 'text-info',
444
+ value: this.pending,
445
+ sort: 1,
446
+ },
447
+ {
448
+ label: 'Outdated',
449
+ color: 'bg-warning',
450
+ textColor: 'text-warning',
451
+ value: this.outdated,
452
+ sort: 2,
453
+ },
454
+ {
455
+ label: 'Unavailable',
456
+ color: 'bg-error',
457
+ textColor: 'text-error',
458
+ value: this.unavailable,
459
+ sort: 3,
460
+ },
461
+ {
462
+ label: 'Ready',
463
+ color: 'bg-success',
464
+ textColor: 'text-success',
465
+ value: this.ready,
466
+ sort: 4,
467
+ },
468
+ ].filter((x) => x.value > 0);
469
+
470
+ return sortBy(out, 'sort:desc');
471
+ }
472
+
473
+ get nodes() {
474
+ return this.$getters['all'](MANAGEMENT.NODE).filter((node) => node.id.startsWith(this.id));
475
+ }
476
+
477
+ get nodesArchitecture() {
478
+ const obj = {};
479
+
480
+ this.nodes?.forEach((node) => {
481
+ if (!node.metadata?.state?.transitioning) {
482
+ const architecture = node.status?.nodeLabels?.[NODE_ARCHITECTURE];
483
+
484
+ const key = architecture || this.t('cluster.architecture.label.unknown');
485
+
486
+ obj[key] = (obj[key] || 0) + 1;
487
+ }
488
+ });
489
+
490
+ return obj;
491
+ }
492
+
493
+ get architecture() {
494
+ if (!this.statusInfo.arch) {
495
+ return { label: this.t('generic.provisioning') };
496
+ }
497
+ if (this.statusInfo.arch === 'mixed') {
498
+ return { label: this.t('cluster.architecture.label.mixed') };
499
+ }
500
+
501
+ return { label: this.statusInfo.arch };
502
+ }
503
+
504
+ get machines() {
505
+ return this.$rootGetters['management/all'](CAPI.MACHINE).filter((machine) => {
506
+ if ( machine.metadata?.namespace !== this.provClusterNamespace ) {
507
+ return false;
508
+ }
509
+
510
+ return machine.spec?.clusterName === this.provClusterName;
511
+ });
512
+ }
513
+
514
+ get unavailable() {
515
+ return this.pools.reduce((acc, pool) => acc + (pool.unavailable || 0), 0);
516
+ }
517
+
518
+ get unavailableMachines() {
519
+ if (this.isReady) {
520
+ const names = this.machines.filter((machine) => {
521
+ return machine.status?.conditions?.find((c) => c.error && c.type === 'NodeHealthy');
522
+ }).map((machine) => {
523
+ if (machine.status?.nodeRef?.name) {
524
+ return this.t('cluster.availabilityWarnings.node', { name: machine.status.nodeRef.name });
525
+ }
526
+
527
+ return this.t('cluster.availabilityWarnings.machine', { name: machine.metadata.name });
528
+ });
529
+
530
+ return names.join('<br>');
531
+ }
532
+
533
+ return '';
534
+ }
535
+
536
+ // nodeGroups can be undefined for an EKS cluster that has just been created and has not
537
+ // had any node groups added to it
538
+ get eksNodeGroups() {
539
+ return this.spec?.eksConfig?.nodeGroups || [];
540
+ }
541
+
251
542
  // Color to use as the underline for the icon in the app bar
252
543
  get iconColor() {
253
544
  return this.metadata?.annotations?.[CLUSTER_BADGE.COLOR];
@@ -301,8 +592,8 @@ export default class MgmtCluster extends SteveModel {
301
592
  }
302
593
 
303
594
  get availableCpu() {
304
- const reserved = parseSi(this.status.requested?.cpu);
305
- const allocatable = parseSi(this.status.allocatable?.cpu);
595
+ const reserved = parseSi(this.status?.requested?.cpu);
596
+ const allocatable = parseSi(this.status?.allocatable?.cpu);
306
597
 
307
598
  if ( allocatable > 0 && reserved >= 0 ) {
308
599
  return Math.max(0, allocatable - reserved);
@@ -312,8 +603,8 @@ export default class MgmtCluster extends SteveModel {
312
603
  }
313
604
 
314
605
  get availableMemory() {
315
- const reserved = parseSi(this.status.requested?.memory);
316
- const allocatable = parseSi(this.status.allocatable?.memory);
606
+ const reserved = parseSi(this.status?.requested?.memory);
607
+ const allocatable = parseSi(this.status?.allocatable?.memory);
317
608
 
318
609
  if ( allocatable > 0 && reserved >= 0 ) {
319
610
  return Math.max(0, allocatable - reserved);
@@ -397,17 +688,23 @@ export default class MgmtCluster extends SteveModel {
397
688
  }, initialAggregation);
398
689
  }
399
690
 
400
- get nodes() {
401
- return this.$getters['all'](MANAGEMENT.NODE).filter((node) => node.id.startsWith(this.id));
691
+ get normanCluster() {
692
+ return this.$rootGetters['rancher/byId'](NORMAN.CLUSTER, this.id);
693
+ }
694
+
695
+ async findNormanCluster() {
696
+ return await this.$dispatch('rancher/find', { type: NORMAN.CLUSTER, id: this.id }, { root: true });
697
+ }
698
+
699
+ get provCluster() {
700
+ return this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, this.provClusterId);
402
701
  }
403
702
 
404
703
  get provClusterId() {
405
- const isRKE1 = !!this.spec?.rancherKubernetesEngineConfig;
406
- // Note: RKE1 provisioning cluster IDs are in a different format. For example,
407
- // RKE2 cluster IDs include the name - fleet-default/cluster-name - whereas an RKE1
408
- // cluster has the less human readable management cluster ID in it: fleet-default/c-khk48
704
+ // Note - there's also status.info.provisioningClusterReg, which is a link to the lifecycle owning type
705
+ // for v2prov this would be a provisioning.cattle.io.cluster, but for others it wouldn't be.
409
706
 
410
- const verb = this.isLocal || isRKE1 || this.isHostedKubernetesProvider ? 'to' : 'from';
707
+ const verb = this.isLocal || this.isHostedKubernetesProvider ? 'to' : 'from';
411
708
  const res = findRelationship(verb, CAPI.RANCHER_CLUSTER, this.metadata?.relationships);
412
709
 
413
710
  if (res) {
@@ -417,6 +714,20 @@ export default class MgmtCluster extends SteveModel {
417
714
  return findRelationship(verb === 'to' ? 'from' : 'to', CAPI.RANCHER_CLUSTER, this.metadata?.relationships);
418
715
  }
419
716
 
717
+ get provClusterNamespace() {
718
+ // Could consider using `metadata.annotations."objectset.rio.cattle.io/owner-namespace"` instead of provClusterId from resource rels
719
+ const [namespace] = this.provClusterId?.split('/') || [];
720
+
721
+ return namespace;
722
+ }
723
+
724
+ get provClusterName() {
725
+ // Could consider using `metadata.annotations."objectset.rio.cattle.io/owner-name"` instead of provClusterId from resource rels
726
+ const [, name] = this.provClusterId?.split('/') || [];
727
+
728
+ return name;
729
+ }
730
+
420
731
  get pinned() {
421
732
  return this.$rootGetters['prefs/get'](PINNED_CLUSTERS).includes(this.id);
422
733
  }
@@ -436,4 +747,70 @@ export default class MgmtCluster extends SteveModel {
436
747
 
437
748
  this.$dispatch('prefs/set', { key: PINNED_CLUSTERS, value: types }, { root: true });
438
749
  }
750
+
751
+ get canExplore() {
752
+ return this.isReady && !this.hasError;
753
+ }
754
+
755
+ get hasError() {
756
+ // TODO: kinara - this was copied over from prov cluster. prov conditions vs mgmt conditions
757
+ // Before we were just checking for this.status?.conditions?.some((condition) => condition.error === true)
758
+ // but this is wrong as an error might exist but it might not be meaningful in the context of readiness of a cluster
759
+ // which is what this 'hasError' is used for.
760
+ // We now check if there's a ready condition after an error, which helps dictate the readiness of a cluster
761
+ // Based on the findings in https://github.com/rancher/dashboard/issues/10043
762
+ if (this.status?.conditions && this.status?.conditions.length) {
763
+ // if there are errors, we compare with how recent the "Ready" condition is compared to that error, otherwise we just move on
764
+ if (this.status?.conditions.some((c) => c.error === true)) {
765
+ // there's no ready condition and has an error, mark it
766
+ if (!this.status?.conditions.some((c) => c.type === 'Ready')) {
767
+ return true;
768
+ }
769
+
770
+ const filteredConditions = this.status?.conditions.filter((c) => c.error === true || c.type === 'Ready');
771
+ const mostRecentCondition = filteredConditions.reduce((a, b) => ((a.lastUpdateTime > b.lastUpdateTime) ? a : b));
772
+
773
+ return mostRecentCondition.error;
774
+ }
775
+ }
776
+
777
+ return false;
778
+ }
779
+
780
+ get isAutoscalerEnabled() {
781
+ if (!this.provCluster) {
782
+ return false;
783
+ }
784
+
785
+ return !!this.provCluster.spec?.rkeConfig?.machinePools?.some((pool) => {
786
+ return typeof pool.autoscalingMinSize !== 'undefined' || typeof pool.autoscalingMaxSize !== 'undefined';
787
+ });
788
+ }
789
+
790
+ _statusInfoWarned = false;
791
+ get statusInfo() {
792
+ if (!this.status.info) {
793
+ if (!this._statusInfoWarned) {
794
+ this._statusInfoWarned = true;
795
+ // eslint-disable-next-line no-console
796
+ console.warn(`management.cattle.io.cluster '${ this.id }' is missing 'status.info'. This could mean the resource is troubled, or an extension has brought in this new model which is used in an older rancher`);
797
+ }
798
+
799
+ return { };
800
+ }
801
+
802
+ return this.status.info;
803
+ }
804
+
805
+ async goToHarvesterCluster() {
806
+ const harvesterCluster = await this.$dispatch('create', {
807
+ ...this.provCluster,
808
+ type: HCI.CLUSTER
809
+ });
810
+
811
+ try {
812
+ await harvesterCluster.goToCluster();
813
+ } catch {
814
+ }
815
+ }
439
816
  }
@@ -1,7 +1,5 @@
1
1
  import { MANAGEMENT_NODE } from '@shell/config/labels-annotations';
2
- import {
3
- ADDRESSES, CAPI, MANAGEMENT, NODE, NORMAN
4
- } from '@shell/config/types';
2
+ import { ADDRESSES, MANAGEMENT, NODE, NORMAN } from '@shell/config/types';
5
3
  import { NAME as EXPLORER } from '@shell/config/product/explorer';
6
4
  import { listNodeRoles } from '@shell/models/cluster/node';
7
5
  import { downloadUrl } from '@shell/utils/download';
@@ -26,6 +24,10 @@ export default class MgmtNode extends HybridModel {
26
24
  return this.metadata.labels[MANAGEMENT_NODE.NODE_NAME];
27
25
  }
28
26
 
27
+ get mgmtCluster() {
28
+ return this.$rootGetters['management/byId'](MANAGEMENT.CLUSTER, this.mgmtClusterId);
29
+ }
30
+
29
31
  get mgmtClusterId() {
30
32
  return this.id.substring(0, this.id.indexOf('/'));
31
33
  }
@@ -101,7 +103,7 @@ export default class MgmtNode extends HybridModel {
101
103
  }
102
104
 
103
105
  get provisioningCluster() {
104
- return this.$getters['all'](CAPI.RANCHER_CLUSTER).find((c) => c.mgmtClusterId === this.mgmtClusterId);
106
+ return this.mgmtCluster?.provCluster;
105
107
  }
106
108
 
107
109
  get doneOverride() {
@@ -45,7 +45,7 @@ export default class MgmtNodePool extends HybridModel {
45
45
  }
46
46
 
47
47
  get provisioningCluster() {
48
- return this.$getters['all'](CAPI.RANCHER_CLUSTER).find((c) => c.name === this.spec.clusterName);
48
+ return this.$getters['byId'](CAPI.RANCHER_CLUSTER, `${ this.metadata.namespace }/${ this.spec.clusterName }`);
49
49
  }
50
50
 
51
51
  get doneOverride() {
@@ -1,5 +1,5 @@
1
1
  import { SECRET, SERVICE } from '@shell/config/types';
2
- import isUrl from 'is-url';
2
+ import { isValidUrl } from '@shell/utils/validators/setting';
3
3
  import { get } from '@shell/utils/object';
4
4
  import isEmpty from 'lodash/isEmpty';
5
5
  import SteveModel from '@shell/plugins/steve/steve-class';
@@ -76,15 +76,15 @@ export default class Ingress extends SteveModel {
76
76
  const fullPath = this.fullPath(rule, path);
77
77
 
78
78
  return {
79
- // isUrl thinks urls which contain '*' are valid so I'm adding an additional check for '*'
80
- isUrl: isUrl(fullPath) && !fullPath.includes('*'),
79
+ // is-url thinks urls which contain '*' are valid so adding an additional check for '*'
80
+ isUrl: isValidUrl(fullPath) && !fullPath.includes('*'),
81
81
  pathType: path.pathType,
82
82
  fullPath,
83
83
  serviceName,
84
84
  serviceTargetTo: this.targetTo(workloads, serviceName),
85
85
  certs: this.certLinks(rule, certificates),
86
86
  targetLink: this.targetLink(workloads, serviceName),
87
- port: get(path?.backend, this.servicePortPath)
87
+ port: get(path?.backend, this.servicePortPath) || get(path?.backend, this.servicePortNamePath)
88
88
  };
89
89
  }
90
90
 
@@ -184,6 +184,14 @@ export default class Ingress extends SteveModel {
184
184
  return this.useNestedBackendField ? nestedPath : flatPath;
185
185
  }
186
186
 
187
+ get servicePortNamePath() {
188
+ const nestedPath = 'service.port.name';
189
+ // Flat API has a single `servicePort` field for both name and number.
190
+ const flatPath = 'servicePort';
191
+
192
+ return this.useNestedBackendField ? nestedPath : flatPath;
193
+ }
194
+
187
195
  get defaultBackendPath() {
188
196
  const defaultBackend = this.$rootGetters['cluster/pathExistsInSchema'](this.type, 'spec.defaultBackend');
189
197