@rancher/shell 3.0.12-rc.1 → 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 (376) 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/images/providers/entraid-black.svg +4 -0
  54. package/assets/images/providers/entraid.svg +9 -0
  55. package/assets/images/vendor/entraid.svg +9 -0
  56. package/assets/styles/app.scss +0 -1
  57. package/assets/styles/base/_variables.scss +2 -0
  58. package/assets/styles/fonts/_fontstack.scss +132 -8
  59. package/assets/translations/en-us.yaml +41 -22
  60. package/assets/translations/zh-hans.yaml +4 -8
  61. package/chart/__tests__/S3.test.ts +10 -3
  62. package/chart/monitoring/index.vue +10 -1
  63. package/components/ActionDropdownShell.vue +2 -1
  64. package/components/CountBox.vue +20 -0
  65. package/components/CreateDriver.vue +0 -12
  66. package/components/CruResourceFooter.vue +9 -5
  67. package/components/DetailText.vue +12 -3
  68. package/components/ExplorerProjectsNamespaces.vue +1 -1
  69. package/components/InstallHelmCharts.vue +2 -2
  70. package/components/LandingPagePreference.vue +14 -5
  71. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
  72. package/components/Resource/Detail/Metadata/index.vue +6 -0
  73. package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
  74. package/components/Resource/Detail/SpacedRow.vue +3 -1
  75. package/components/Resource/Detail/TitleBar/index.vue +10 -11
  76. package/components/ResourceList/Masthead.vue +12 -8
  77. package/components/SelectIconGrid.vue +5 -10
  78. package/components/SingleClusterInfo.vue +1 -0
  79. package/components/SortableTable/__tests__/sorting.test.ts +126 -0
  80. package/components/SortableTable/index.vue +6 -9
  81. package/components/SortableTable/selection.js +23 -5
  82. package/components/SortableTable/sorting.js +6 -3
  83. package/components/Wizard.vue +14 -13
  84. package/components/__tests__/CountBox.test.ts +72 -0
  85. package/components/__tests__/DetailText.test.ts +113 -0
  86. package/components/fleet/FleetBundles.vue +100 -12
  87. package/components/fleet/FleetClusterTargets/index.vue +54 -15
  88. package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
  89. package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
  90. package/components/form/InputWithSelect.vue +18 -10
  91. package/components/form/KeyValue.vue +17 -1
  92. package/components/form/LabeledSelect.vue +101 -26
  93. package/components/form/NameNsDescription.vue +11 -0
  94. package/components/form/Security.vue +6 -2
  95. package/components/form/Select.vue +73 -56
  96. package/components/form/ServiceNameSelect.vue +13 -11
  97. package/components/form/WorkloadPorts.vue +2 -7
  98. package/components/form/__tests__/KeyValue.test.ts +66 -0
  99. package/components/form/__tests__/NodeScheduling.test.ts +9 -0
  100. package/components/form/__tests__/Security.test.ts +76 -0
  101. package/components/form/labeled-select-utils/useLabeledSelectPagination.ts +138 -0
  102. package/components/formatter/Autoscaler.vue +4 -4
  103. package/components/formatter/ClusterKubeVersion.vue +27 -0
  104. package/components/formatter/ClusterLink.vue +1 -7
  105. package/components/formatter/ClusterProvider.vue +6 -10
  106. package/components/formatter/FleetSummaryGraph.vue +0 -3
  107. package/components/formatter/MachineSummaryGraph.vue +1 -1
  108. package/components/formatter/PodsUsage.vue +2 -2
  109. package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
  110. package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
  111. package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
  112. package/components/nav/Group.vue +7 -6
  113. package/components/nav/Header.vue +24 -3
  114. package/components/nav/NamespaceFilter.vue +2 -2
  115. package/components/nav/NotificationCenter/Notification.vue +4 -1
  116. package/components/nav/NotificationCenter/NotificationHeader.vue +20 -8
  117. package/components/nav/NotificationCenter/__tests__/NotificationHeader.test.ts +80 -0
  118. package/components/nav/TopLevelMenu.helper.ts +15 -3
  119. package/components/nav/TopLevelMenu.vue +16 -5
  120. package/components/nav/Type.vue +8 -7
  121. package/components/nav/WindowManager/index.vue +2 -1
  122. package/components/nav/WorkspaceSwitcher.vue +13 -0
  123. package/components/nav/__tests__/Group.test.ts +67 -0
  124. package/components/nav/__tests__/Header.test.ts +235 -0
  125. package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
  126. package/components/nav/__tests__/Type.test.ts +20 -3
  127. package/components/templates/default.vue +34 -4
  128. package/components/templates/home.vue +30 -25
  129. package/components/templates/plain.vue +31 -26
  130. package/components/templates/standalone.vue +17 -0
  131. package/composables/useFormValidation.ts +93 -0
  132. package/composables/useLabeledFormElement.ts +10 -2
  133. package/composables/useLabeledSelect.ts +60 -0
  134. package/composables/useUserRetentionValidation.ts +1 -49
  135. package/composables/useVeeValidateField.test.ts +159 -0
  136. package/composables/useVeeValidateField.ts +67 -0
  137. package/config/cookies.js +0 -1
  138. package/config/labels-annotations.js +1 -0
  139. package/config/pagination-table-headers.js +18 -1
  140. package/config/product/manager.js +82 -21
  141. package/config/query-params.js +1 -0
  142. package/config/router/routes.js +6 -8
  143. package/config/table-headers.js +20 -1
  144. package/config/types.js +2 -1
  145. package/core/__tests__/plugin-products.test.ts +1505 -30
  146. package/core/plugin-products-base.ts +137 -20
  147. package/core/plugin-products-helpers.ts +5 -4
  148. package/core/plugin-products.ts +4 -0
  149. package/core/plugin-types.ts +129 -4
  150. package/core/plugin.ts +15 -7
  151. package/core/productDebugger.js +9 -4
  152. package/core/types-provisioning.ts +43 -30
  153. package/core/types.ts +58 -19
  154. package/detail/__tests__/management.cattle.io.fleetworkspace.test.ts +128 -0
  155. package/detail/__tests__/pod.test.ts +41 -0
  156. package/detail/harvesterhci.io.management.cluster.vue +6 -2
  157. package/detail/management.cattle.io.fleetworkspace.vue +49 -0
  158. package/detail/pod.vue +1 -1
  159. package/detail/provisioning.cattle.io.cluster.vue +4 -10
  160. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +9 -0
  161. package/edit/__tests__/kontainerDriver.test.ts +0 -13
  162. package/edit/__tests__/nodeDriver.test.ts +5 -11
  163. package/edit/__tests__/resources.cattle.io.restore.test.ts +9 -0
  164. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  165. package/edit/auth/__tests__/azuread.test.ts +217 -34
  166. package/edit/auth/__tests__/oidc.test.ts +54 -0
  167. package/edit/auth/azuread.vue +123 -15
  168. package/edit/auth/oidc.vue +10 -2
  169. package/edit/kontainerDriver.vue +1 -2
  170. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
  171. package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
  172. package/edit/networking.k8s.io.ingress/index.vue +75 -20
  173. package/edit/nodeDriver.vue +0 -2
  174. package/edit/provisioning.cattle.io.cluster/AgentEnv.vue +1 -0
  175. package/edit/provisioning.cattle.io.cluster/__tests__/AgentEnv.test.ts +25 -0
  176. package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
  177. package/edit/provisioning.cattle.io.cluster/index.vue +81 -106
  178. package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
  179. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  180. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
  181. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
  182. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
  183. package/edit/secret/__tests__/ssh.test.ts +5 -6
  184. package/edit/secret/basic.vue +31 -0
  185. package/edit/secret/index.vue +68 -17
  186. package/edit/secret/registry.vue +38 -0
  187. package/edit/secret/ssh.vue +29 -0
  188. package/edit/secret/tls.vue +30 -0
  189. package/edit/service.vue +4 -4
  190. package/edit/workload/Upgrading.vue +3 -3
  191. package/edit/workload/__tests__/Upgrading.test.ts +6 -9
  192. package/edit/workload/mixins/workload.js +2 -1
  193. package/initialize/App.vue +29 -2
  194. package/initialize/install-plugins.js +0 -2
  195. package/list/__tests__/management.cattle.io.feature.test.ts +105 -0
  196. package/list/catalog.cattle.io.app.vue +25 -5
  197. package/list/fleet.cattle.io.bundle.vue +7 -104
  198. package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
  199. package/list/management.cattle.io.feature.vue +1 -1
  200. package/list/management.cattle.io.fleetworkspace.vue +8 -0
  201. package/list/provisioning.cattle.io.cluster.vue +262 -180
  202. package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
  203. package/machine-config/amazonec2.vue +1 -0
  204. package/mixins/__tests__/chart.test.ts +112 -0
  205. package/mixins/brand.js +2 -1
  206. package/mixins/chart.js +50 -15
  207. package/mixins/resource-fetch-api-pagination.js +41 -5
  208. package/models/__tests__/catalog.cattle.io.app.test.ts +15 -1
  209. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +84 -0
  210. package/models/__tests__/chart.test.ts +99 -6
  211. package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
  212. package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
  213. package/models/__tests__/management.cattle.io.feature.test.ts +131 -0
  214. package/models/__tests__/management.cattle.io.node.ts +6 -5
  215. package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
  216. package/models/__tests__/monitoring.coreos.com.alertmanagerconfig.test.ts +98 -0
  217. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
  218. package/models/base-cluster.x-k8s.io.js +26 -0
  219. package/models/catalog.cattle.io.app.js +21 -17
  220. package/models/catalog.cattle.io.clusterrepo.js +39 -11
  221. package/models/chart.js +33 -19
  222. package/models/cluster.js +1 -1
  223. package/models/cluster.x-k8s.io.machine.js +4 -22
  224. package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
  225. package/models/cluster.x-k8s.io.machineset.js +2 -20
  226. package/models/compliance.cattle.io.clusterscan.js +130 -2
  227. package/models/ext.cattle.io.kubeconfig.ts +4 -7
  228. package/models/fleet-application.js +4 -2
  229. package/models/fleet.cattle.io.bundle.js +1 -1
  230. package/models/kontainerdriver.js +11 -0
  231. package/models/management.cattle.io.authconfig.js +5 -1
  232. package/models/management.cattle.io.cluster.js +402 -78
  233. package/models/management.cattle.io.feature.js +3 -3
  234. package/models/management.cattle.io.kontainerdriver.js +1 -26
  235. package/models/management.cattle.io.node.js +6 -4
  236. package/models/management.cattle.io.nodepool.js +1 -1
  237. package/models/monitoring.coreos.com.alertmanagerconfig.js +31 -17
  238. package/models/networking.k8s.io.ingress.js +12 -4
  239. package/models/nodedriver.js +7 -0
  240. package/models/provisioning.cattle.io.cluster.js +47 -330
  241. package/models/rke.cattle.io.etcdsnapshot.js +1 -2
  242. package/package.json +20 -37
  243. package/pages/__tests__/readme.test.ts +49 -0
  244. package/pages/auth/setup.vue +2 -3
  245. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +265 -0
  246. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +55 -0
  247. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +53 -0
  248. package/pages/c/_cluster/apps/charts/chart.vue +275 -39
  249. package/pages/c/_cluster/apps/charts/index.vue +2 -2
  250. package/pages/c/_cluster/apps/charts/install.vue +18 -10
  251. package/pages/c/_cluster/auth/user.retention/index.vue +55 -22
  252. package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
  253. package/pages/c/_cluster/explorer/index.vue +5 -49
  254. package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
  255. package/pages/c/_cluster/istio/index.vue +21 -6
  256. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -7
  257. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +40 -2
  258. package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +61 -0
  259. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +735 -13
  260. package/pages/c/_cluster/uiplugins/index.vue +226 -222
  261. package/pages/diagnostic.vue +13 -17
  262. package/pages/fail-whale.vue +18 -0
  263. package/pages/home.vue +77 -260
  264. package/pages/readme.vue +88 -0
  265. package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
  266. package/plugins/dashboard-store/actions.js +40 -18
  267. package/plugins/dashboard-store/resource-class.js +5 -2
  268. package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
  269. package/plugins/steve/steve-pagination-utils.ts +11 -3
  270. package/plugins/steve/subscribe.js +35 -5
  271. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +211 -1
  272. package/rancher-components/Form/LabeledInput/LabeledInput.vue +37 -4
  273. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +1 -1
  274. package/rancher-components/RcButton/RcButton.test.ts +37 -1
  275. package/rancher-components/RcButton/RcButton.vue +38 -8
  276. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
  277. package/scripts/test-plugins-build.sh +5 -2
  278. package/server/server-middleware.js +2 -2
  279. package/static/humans.txt +1 -0
  280. package/static/robots.txt +34 -0
  281. package/static/welcome-cow.svg +18 -0
  282. package/store/__tests__/catalog.test.ts +276 -12
  283. package/store/__tests__/type-map.test.ts +556 -1
  284. package/store/action-menu.js +8 -3
  285. package/store/auth.js +1 -4
  286. package/store/aws.js +27 -16
  287. package/store/catalog.js +87 -11
  288. package/store/digitalocean.js +20 -38
  289. package/store/index.js +2 -0
  290. package/store/linode.js +25 -40
  291. package/store/pnap.js +1 -0
  292. package/store/type-map.js +111 -29
  293. package/tsconfig.paths.json +8 -8
  294. package/types/kube/kube-api.ts +14 -1
  295. package/types/rancher/steve.api.ts +12 -12
  296. package/types/resources/settings.d.ts +2 -1
  297. package/types/shell/index.d.ts +128 -24
  298. package/types/store/dashboard-store.types.ts +108 -11
  299. package/types/store/pagination.types.ts +6 -3
  300. package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
  301. package/utils/__tests__/async.test.ts +87 -0
  302. package/utils/__tests__/aws.test.ts +140 -0
  303. package/utils/__tests__/banners.test.ts +176 -0
  304. package/utils/__tests__/chart.test.ts +64 -1
  305. package/utils/__tests__/color.test.ts +226 -0
  306. package/utils/__tests__/duration.test.ts +140 -0
  307. package/utils/__tests__/fleet.test.ts +340 -0
  308. package/utils/__tests__/git.test.ts +270 -0
  309. package/utils/__tests__/inactivity.test.ts +316 -0
  310. package/utils/__tests__/ingress.test.ts +553 -0
  311. package/utils/__tests__/kube.test.ts +68 -0
  312. package/utils/__tests__/namespace-filter.test.ts +109 -0
  313. package/utils/__tests__/object.test.ts +77 -0
  314. package/utils/__tests__/pagination-utils.test.ts +361 -0
  315. package/utils/__tests__/parse-externalid.test.ts +137 -0
  316. package/utils/__tests__/perf-setting.utils.test.ts +98 -0
  317. package/utils/__tests__/poller-sequential.test.ts +177 -0
  318. package/utils/__tests__/poller.test.ts +170 -0
  319. package/utils/__tests__/promise.test.ts +346 -0
  320. package/utils/__tests__/settings.test.ts +140 -0
  321. package/utils/__tests__/sort-utils.test.ts +301 -0
  322. package/utils/__tests__/string-utils.test.ts +798 -0
  323. package/utils/__tests__/string.test.ts +23 -1
  324. package/utils/__tests__/style.test.ts +154 -0
  325. package/utils/__tests__/svg-filter.test.ts +184 -0
  326. package/utils/__tests__/time.test.ts +14 -1
  327. package/utils/__tests__/units.test.ts +417 -0
  328. package/utils/__tests__/url.test.ts +246 -0
  329. package/utils/__tests__/versions.test.ts +128 -0
  330. package/utils/__tests__/xccdf.test.ts +391 -0
  331. package/utils/chart.js +36 -0
  332. package/utils/fleet.ts +13 -3
  333. package/utils/gatekeeper/__tests__/util.test.ts +174 -0
  334. package/utils/gc/__tests__/gc-interval.test.ts +119 -0
  335. package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
  336. package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
  337. package/utils/gc/__tests__/gc.test.ts +487 -0
  338. package/utils/ingress.ts +9 -1
  339. package/utils/object.js +33 -2
  340. package/utils/pagination-utils.ts +2 -1
  341. package/utils/string.js +25 -2
  342. package/utils/time.ts +5 -0
  343. package/utils/uiplugins.ts +5 -5
  344. package/utils/validators/__tests__/cluster-name.test.ts +110 -0
  345. package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
  346. package/utils/validators/__tests__/index.test.ts +481 -0
  347. package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
  348. package/utils/validators/__tests__/misc-validators.test.ts +246 -0
  349. package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
  350. package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
  351. package/utils/validators/__tests__/role-template.test.ts +149 -0
  352. package/utils/validators/__tests__/service.test.ts +283 -0
  353. package/utils/validators/__tests__/setting.test.js +32 -0
  354. package/utils/validators/formRules/__tests__/index.test.ts +50 -0
  355. package/utils/validators/formRules/index.ts +5 -5
  356. package/utils/validators/machine-pool.ts +1 -1
  357. package/utils/validators/setting.js +18 -3
  358. package/utils/xccdf.ts +418 -0
  359. package/vue.config.js +0 -9
  360. package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
  361. package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
  362. package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
  363. package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
  364. package/assets/images/providers/azuread-black.svg +0 -22
  365. package/assets/images/providers/azuread.svg +0 -25
  366. package/assets/images/vendor/azuread.svg +0 -18
  367. package/assets/styles/fonts/_dots.scss +0 -18
  368. package/components/EmberPage.vue +0 -622
  369. package/components/EmberPageView.vue +0 -39
  370. package/components/form/labeled-select-utils/labeled-select-pagination.ts +0 -116
  371. package/mixins/labeled-form-element.ts +0 -225
  372. package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -28
  373. package/pages/c/_cluster/manager/pages/_page.vue +0 -22
  374. package/pages/c/_cluster/mcapps/pages/_page.vue +0 -22
  375. package/plugins/ember-cookie.js +0 -17
  376. package/utils/ember-page.js +0 -30
@@ -87,21 +87,21 @@ export default {
87
87
  path: 'spec.rules.http.paths.path', rules: ['absolutePath'], translationKey: 'ingress.rules.path.label'
88
88
  },
89
89
  {
90
- path: 'spec.rules.http.paths.backend.service.port.number', rules: ['required'], translationKey: 'ingress.rules.port.label'
90
+ path: 'spec.rules.http.paths.backend.service.port', rules: ['portRequired', 'portRange'], translationKey: 'ingress.rules.port.label'
91
91
  },
92
92
  {
93
93
  path: 'spec.rules.http.paths.backend.service.name', rules: ['required'], translationKey: 'ingress.rules.target.label'
94
94
  },
95
95
  { path: 'spec', rules: ['backEndOrRules'] },
96
96
  {
97
- path: 'spec.defaultBackend.service.name', rules: ['required'], translationKey: 'ingress.defaultBackend.targetService.label'
97
+ path: 'spec.defaultBackend.service.name', rules: ['defaultBackendNameRequired'], translationKey: 'ingress.defaultBackend.targetService.label'
98
98
  },
99
99
  {
100
- path: 'spec.defaultBackend.service.port.number', rules: ['required', 'requiredInt', 'portNumber'], translationKey: 'ingress.defaultBackend.port.label'
100
+ path: 'spec.defaultBackend.service.port', rules: ['defaultBackendPortRequired', 'portRange'], translationKey: 'ingress.defaultBackend.port.label'
101
101
  },
102
102
  { path: 'spec.tls.hosts', rules: ['required', 'wildcardHostname'] }
103
103
  ],
104
- fvReportedValidationPaths: ['spec.rules.http.paths.backend.service.port.number', 'spec.rules.http.paths.path', 'spec.rules.http.paths.backend.service.name']
104
+ fvReportedValidationPaths: ['spec.rules.http.paths.backend.service.port', 'spec.rules.http.paths.path', 'spec.rules.http.paths.backend.service.name']
105
105
  };
106
106
  },
107
107
 
@@ -125,35 +125,89 @@ export default {
125
125
  }
126
126
  };
127
127
 
128
- return { backEndOrRules };
128
+ const portLabel = this.t('ingress.rules.port.label');
129
+
130
+ // Built-in `required` won't work: it passes for empty objects like {} or { name: '' }.
131
+ const portRequired = (port) => {
132
+ if (typeof port === 'string' || typeof port === 'number') {
133
+ if (!port) {
134
+ return this.t('validation.required', { key: portLabel });
135
+ }
136
+ } else if (!port || (!port.number && !port.name)) {
137
+ return this.t('validation.required', { key: portLabel });
138
+ }
139
+ };
140
+
141
+ const portRange = (port) => {
142
+ let num;
143
+
144
+ if (typeof port === 'number') {
145
+ num = port;
146
+ } else if (typeof port === 'string') {
147
+ num = Number.parseInt(port);
148
+ } else if (port?.number) {
149
+ num = port.number;
150
+ }
151
+
152
+ if (num !== undefined && !Number.isNaN(num) && (num < 1 || num > 65535)) {
153
+ return this.t('validation.number.between', {
154
+ key: portLabel, min: '1', max: '65535'
155
+ });
156
+ }
157
+ };
158
+
159
+ const hasDefaultBackendService = () => {
160
+ const backend = get(this.value?.spec, this.value.defaultBackendPath);
161
+
162
+ return !!get(backend, this.value.serviceNamePath);
163
+ };
164
+
165
+ const nameLabel = this.t('ingress.defaultBackend.targetService.label');
166
+
167
+ // Only enforce required on the default backend when a service is selected.
168
+ // Selecting "None" means the user wants to remove the backend; willSave() handles cleanup.
169
+ const defaultBackendNameRequired = (name) => {
170
+ if (hasDefaultBackendService() && !name) {
171
+ return this.t('validation.required', { key: nameLabel });
172
+ }
173
+ };
174
+
175
+ const defaultBackendPortRequired = (port) => {
176
+ if (!hasDefaultBackendService()) {
177
+ return;
178
+ }
179
+
180
+ return portRequired(port);
181
+ };
182
+
183
+ return {
184
+ backEndOrRules,
185
+ portRequired,
186
+ portRange,
187
+ defaultBackendNameRequired,
188
+ defaultBackendPortRequired,
189
+ };
129
190
  },
130
191
  tabErrors() {
131
192
  return {
132
- rules: this.fvGetPathErrors(['spec.rules.host', 'spec.rules.http.paths.path', 'spec.rules.http.paths.backend.service.port.number', 'spec.rules.http.paths.backend.service.name'])?.length > 0,
133
- defaultBackend: this.fvGetPathErrors(['spec.defaultBackend.service.name', 'spec.defaultBackend.service.port.number'])?.length > 0
193
+ rules: this.fvGetPathErrors(['spec.rules.host', 'spec.rules.http.paths.path', 'spec.rules.http.paths.backend.service.port', 'spec.rules.http.paths.backend.service.name'])?.length > 0,
194
+ defaultBackend: this.fvGetPathErrors(['spec.defaultBackend.service.name', 'spec.defaultBackend.service.port'])?.length > 0
134
195
  };
135
196
  },
136
197
  rulesPathRules() {
137
198
  return {
138
199
  requestHost: this.fvGetAndReportPathRules('spec.rules.host'),
139
200
  path: this.fvGetAndReportPathRules('spec.rules.http.paths.path'),
140
- port: this.fvGetAndReportPathRules('spec.rules.http.paths.backend.service.port.number'),
201
+ port: this.fvGetAndReportPathRules('spec.rules.http.paths.backend.service.port'),
141
202
  target: this.fvGetAndReportPathRules('spec.rules.http.paths.backend.service.name'),
142
203
 
143
204
  };
144
205
  },
145
206
  defaultBackendPathRules() {
146
- const rulesExist = (this.value?.spec?.rules || []).length > 0;
147
- const defaultBackendExist = !!this.value?.spec?.defaultBackend?.service;
148
-
149
- if (!rulesExist || defaultBackendExist) {
150
- return {
151
- name: this.fvGetAndReportPathRules('spec.defaultBackend.service.name'),
152
- port: this.fvGetAndReportPathRules('spec.defaultBackend.service.port.number'),
153
- };
154
- }
155
-
156
- return { name: [], port: [] };
207
+ return {
208
+ name: this.fvGetAndReportPathRules('spec.defaultBackend.service.name'),
209
+ port: this.fvGetAndReportPathRules('spec.defaultBackend.service.port'),
210
+ };
157
211
  },
158
212
  serviceTargets() {
159
213
  return this.ingressHelper.findAndMapServiceTargets(this.services);
@@ -188,7 +242,8 @@ export default {
188
242
  willSave() {
189
243
  const backend = get(this.value.spec, this.value.defaultBackendPath);
190
244
  const serviceName = get(backend, this.value.serviceNamePath);
191
- const servicePort = get(backend, this.value.servicePortPath);
245
+ const servicePort = get(backend, this.value.servicePortPath) ||
246
+ get(backend, this.value.servicePortNamePath);
192
247
 
193
248
  if (backend && (!serviceName || !servicePort)) {
194
249
  const path = this.value.defaultBackendPath;
@@ -21,7 +21,6 @@ export default {
21
21
  return {
22
22
  fvFormRuleSets: [
23
23
  { path: 'url', rules: ['required', 'url'] },
24
- { path: 'uiUrl', rules: ['url'] },
25
24
  { path: 'checksum', rules: ['alphanumeric'] },
26
25
  { path: 'whitelistDomains', rules: ['wildcardHostname'] }
27
26
  ]
@@ -63,7 +62,6 @@ export default {
63
62
  :value="value"
64
63
  :rules="{
65
64
  url: fvGetAndReportPathRules('url'),
66
- uiUrl: fvGetAndReportPathRules('uiUrl'),
67
65
  checksum: fvGetAndReportPathRules('checksum'),
68
66
  whitelistDomains: fvGetAndReportPathRules('whitelistDomains')
69
67
  }"
@@ -33,6 +33,7 @@ export default {
33
33
  :supported="(row) => typeof row.valueFrom === 'undefined'"
34
34
  :read-allowed="true"
35
35
  :value-can-be-empty="true"
36
+ :read-accept="'text/plain'"
36
37
  :key-label="t('cluster.agentEnvVars.keyLabel')"
37
38
  :parse-lines-from-file="true"
38
39
  />
@@ -0,0 +1,25 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import AgentEnv from '@shell/edit/provisioning.cattle.io.cluster/AgentEnv.vue';
3
+
4
+ describe('component: AgentEnv', () => {
5
+ it('should only accept text files (not binary) on the KeyValue file upload', () => {
6
+ const wrapper = mount(AgentEnv, {
7
+ props: {
8
+ mode: 'edit',
9
+ value: { spec: { agentEnvVars: [] } },
10
+ },
11
+ global: {
12
+ mocks: { t: (key: string) => key },
13
+ stubs: {
14
+ Tab: { template: '<div><slot /></div>' },
15
+ KeyValue: true,
16
+ },
17
+ },
18
+ });
19
+
20
+ const keyValue = wrapper.findComponent({ name: 'KeyValue' });
21
+
22
+ expect(keyValue.exists()).toBe(true);
23
+ expect(keyValue.props('readAccept')).toBe('text/plain');
24
+ });
25
+ });
@@ -0,0 +1,104 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import MachinePool from '@shell/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue';
3
+
4
+ const TRANSLATION_KEY = '%cluster.machinePool.name.unique%';
5
+
6
+ function createPool(name: string, { remove = false } = {}) {
7
+ return {
8
+ id: `pool-${ name }`,
9
+ remove,
10
+ create: false,
11
+ update: true,
12
+ pool: {
13
+ name,
14
+ etcdRole: false,
15
+ controlPlaneRole: false,
16
+ workerRole: true,
17
+ quantity: 1,
18
+ },
19
+ config: null,
20
+ };
21
+ }
22
+
23
+ function mountMachinePool(currentPool: ReturnType<typeof createPool>, allPools: ReturnType<typeof createPool>[]) {
24
+ return shallowMount(MachinePool, {
25
+ props: {
26
+ value: currentPool,
27
+ mode: 'create',
28
+ provider: 'custom',
29
+ idx: 0,
30
+ machinePools: allPools,
31
+ poolId: currentPool.id,
32
+ poolCreateMode: true,
33
+ },
34
+ global: {
35
+ mocks: {
36
+ $store: {
37
+ getters: {
38
+ 'i18n/t': (key: string) => key,
39
+ 'i18n/exists': () => false,
40
+ 'type-map/hasCustomMachineConfigComponent': () => false,
41
+ 'type-map/importMachineConfig': () => null,
42
+ 'features/get': () => false,
43
+ },
44
+ dispatch: jest.fn(),
45
+ },
46
+ },
47
+ stubs: {
48
+ LabeledInput: true,
49
+ Checkbox: true,
50
+ Taints: true,
51
+ KeyValue: true,
52
+ AdvancedSection: true,
53
+ Banner: true,
54
+ UnitInput: true,
55
+ },
56
+ },
57
+ });
58
+ }
59
+
60
+ describe('component: MachinePool', () => {
61
+ describe('uniquePoolName validation', () => {
62
+ it('should return undefined when the name is empty', () => {
63
+ const pool = createPool('');
64
+ const wrapper = mountMachinePool(pool, [pool]);
65
+
66
+ expect(wrapper.vm.fvExtraRules.uniquePoolName('')).toBeUndefined();
67
+ });
68
+
69
+ it('should return undefined when the pool name is unique', () => {
70
+ const pool1 = createPool('pool1');
71
+ const pool2 = createPool('pool2');
72
+ const wrapper = mountMachinePool(pool1, [pool1, pool2]);
73
+
74
+ expect(wrapper.vm.fvExtraRules.uniquePoolName('pool1')).toBeUndefined();
75
+ });
76
+
77
+ it('should return an error message when the pool name is duplicated', () => {
78
+ const pool1 = createPool('same-name');
79
+ const pool2 = createPool('same-name');
80
+ const wrapper = mountMachinePool(pool1, [pool1, pool2]);
81
+
82
+ expect(wrapper.vm.fvExtraRules.uniquePoolName('same-name')).toStrictEqual(TRANSLATION_KEY);
83
+ });
84
+
85
+ it('should ignore pools marked for removal', () => {
86
+ const pool1 = createPool('same-name');
87
+ const pool2 = createPool('same-name', { remove: true });
88
+ const wrapper = mountMachinePool(pool1, [pool1, pool2]);
89
+
90
+ expect(wrapper.vm.fvExtraRules.uniquePoolName('same-name')).toBeUndefined();
91
+ });
92
+
93
+ it.each([
94
+ ['Pool1', 'pool1'],
95
+ ['POOL', 'pool'],
96
+ ])('should flag names that differ only by case as duplicates (%s vs %s)', (nameA, nameB) => {
97
+ const pool1 = createPool(nameA);
98
+ const pool2 = createPool(nameB);
99
+ const wrapper = mountMachinePool(pool1, [pool1, pool2]);
100
+
101
+ expect(wrapper.vm.fvExtraRules.uniquePoolName(nameA)).toStrictEqual(TRANSLATION_KEY);
102
+ });
103
+ });
104
+ });
@@ -4,9 +4,8 @@ import Loading from '@shell/components/Loading';
4
4
  import { Banner } from '@components/Banner';
5
5
  import CruResource from '@shell/components/CruResource';
6
6
  import SelectIconGrid from '@shell/components/SelectIconGrid';
7
- import EmberPage from '@shell/components/EmberPage';
8
7
  import {
9
- CHART, FROM_CLUSTER, SUB_TYPE, RKE_TYPE, _EDIT, _IMPORT, _CONFIG, _VIEW
8
+ CHART, FROM_CLUSTER, SUB_TYPE, RKE_TYPE, _EDIT, _IMPORT, _CONFIG, _VIEW, _CREATE
10
9
  } from '@shell/config/query-params';
11
10
  import { mapGetters } from 'vuex';
12
11
  import { sortBy } from '@shell/utils/sort';
@@ -18,8 +17,8 @@ import { mapFeature, RKE2 as RKE2_FEATURE } from '@shell/store/features';
18
17
  import { allHash } from '@shell/utils/promise';
19
18
  import { BLANK_CLUSTER } from '@shell/store/store-types.js';
20
19
  import { ELEMENTAL_PRODUCT_NAME, ELEMENTAL_CLUSTER_PROVIDER } from '../../config/elemental-types';
20
+ import { KONTAINER_TO_DRIVER } from '@shell/models/management.cattle.io.kontainerdriver';
21
21
  import Rke2Config from './rke2';
22
- import { DRIVER_TO_IMPORT } from '@shell/models/management.cattle.io.kontainerdriver';
23
22
  import { requireAsset } from '@shell/utils/require-asset';
24
23
 
25
24
  const SORT_GROUPS = {
@@ -44,7 +43,6 @@ export default {
44
43
 
45
44
  components: {
46
45
  CruResource,
47
- EmberPage,
48
46
  Loading,
49
47
  Rke2Config,
50
48
  SelectIconGrid,
@@ -82,16 +80,20 @@ export default {
82
80
  },
83
81
 
84
82
  async fetch() {
85
- const hash = {
86
- // These aren't explicitly used, but need to be listening for change events
87
- mgmtClusters: this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER }),
88
- provClusters: this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER }),
89
- };
83
+ const hash = {};
84
+
85
+ if (this.mode === _CREATE) {
86
+ // After we create we wait for these to exist, so start watching
87
+ await this.$store.dispatch('management/watch', { type: MANAGEMENT.CLUSTER, registerType: true });
88
+ await this.$store.dispatch('management/watch', { type: CAPI.RANCHER_CLUSTER, registerType: true });
89
+ } else {
90
+ hash.mgmtClusters = this.value.waitForMgmt();
91
+ }
90
92
 
91
93
  // No need to fetch charts when editing an RKE1 cluster
92
94
  // The computed property `isRke1` in this file is based on the RKE1/RKE2 toggle, which is not applicable in this case
93
95
  // Instead, we should rely on the value from the model: `this.value.isRke1`
94
- if (!this.value.isRke1 || (this.value.isRke1 && this.mode !== 'edit')) {
96
+ if (!this.value.isRke1 || (this.value.isRke1 && this.mode !== _EDIT)) {
95
97
  hash['catalog'] = this.$store.dispatch('catalog/load');
96
98
  }
97
99
 
@@ -103,19 +105,6 @@ export default {
103
105
  hash.kontainerDrivers = this.$store.dispatch('management/findAll', { type: MANAGEMENT.KONTAINER_DRIVER });
104
106
  }
105
107
 
106
- // Not sure if needed for legacy hosted cluster?
107
- if ( this.value.id && !this.value.isRke2 ) {
108
- // These are needed to resolve references in the mgmt cluster -> node pool -> node template to figure out what provider the cluster is using
109
- // so that the edit iframe for ember pages can go to the right place.
110
- if (this.$store.getters[`management/canList`](MANAGEMENT.NODE_POOL)) {
111
- hash.rke1NodePools = this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_POOL });
112
- }
113
-
114
- if (this.$store.getters[`management/canList`](MANAGEMENT.NODE_TEMPLATE)) {
115
- hash.rke1NodeTemplates = this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_TEMPLATE });
116
- }
117
- }
118
-
119
108
  const res = await allHash(hash);
120
109
 
121
110
  this.nodeDrivers = res.nodeDrivers || [];
@@ -172,11 +161,50 @@ export default {
172
161
  if ( this.$route.query[SUB_TYPE]) {
173
162
  subType = this.$route.query[SUB_TYPE];
174
163
  } else if (this.value.isImported) {
164
+ // Default imported clusters to the generic imported subType.
165
+ // Imported hosted clusters (e.g. AKS, EKS, GKE) that have an extension-provided
166
+ // component will be overridden below to load the correct custom form.
175
167
  subType = IMPORTED;
176
168
  } else if (this.value.isLocal) {
177
169
  subType = LOCAL;
178
170
  }
179
171
 
172
+ // For imported hosted clusters, check if the provisioner has a matching extension
173
+ // component and override the subType so the correct custom form loads instead of
174
+ // the generic imported configuration page.
175
+ if (subType === IMPORTED && this.value?.id && this.value.provisioner) {
176
+ const provisionerLower = this.value.provisioner.toLowerCase();
177
+ const hasExtension = this.extensions.some((ext) => ext.id === provisionerLower);
178
+
179
+ if (hasExtension) {
180
+ subType = provisionerLower;
181
+ }
182
+ }
183
+
184
+ // Auto-detect subType for existing clusters being edited
185
+ if ( !subType && this.value?.id ) {
186
+ // Check for extension annotation first
187
+ const fromAnnotation = this.value.annotations?.[CAPI_ANNOTATIONS.UI_CUSTOM_PROVIDER];
188
+
189
+ if (fromAnnotation) {
190
+ subType = fromAnnotation;
191
+ } else if ( this.value.isRke2 ) {
192
+ // For custom RKE2 clusters
193
+ if ( this.value.isCustom && (this.realMode === _EDIT || (this.as === _CONFIG && this.realMode === _VIEW)) ) {
194
+ subType = 'custom';
195
+ } else if ( this.value.machineProvider ) {
196
+ // For RKE2/K3s clusters provisioned in Rancher, use the machine pool provisioner
197
+ subType = this.value.machineProvider;
198
+ }
199
+ } else if ( this.value.provisioner ) {
200
+ // For non-RKE2 clusters, try to match against extension-provided subtypes
201
+ const provisionerLower = this.value.provisioner.toLowerCase();
202
+
203
+ // This will be checked against available subtypes after they're computed
204
+ subType = provisionerLower;
205
+ }
206
+ }
207
+
180
208
  this.subType = subType;
181
209
  },
182
210
 
@@ -217,74 +245,6 @@ export default {
217
245
  },
218
246
  _RKE2: () => _RKE2,
219
247
 
220
- emberLink() {
221
- if (this.value) {
222
- // set subtype if editing EKS/GKE/AKS cluster -- this ensures that the component provided by extension is loaded instead of iframing old ember ui
223
- if (this.value.provisioner) {
224
- const matchingSubtype = this.subTypes.find((st) => {
225
- const typeLower = st.id.toLowerCase();
226
- const provisionerLower = this.value.provisioner.toLowerCase();
227
-
228
- // This allows extensions to provide type for edit without breaking edit for Ember kontainer providers
229
- return (!!st.component && (typeLower === provisionerLower)) || (DRIVER_TO_IMPORT[typeLower] === provisionerLower);
230
- });
231
-
232
- if (matchingSubtype) {
233
- this.selectType(matchingSubtype.id, false);
234
- }
235
- }
236
-
237
- // subType set by the ui during cluster creation
238
- // this is likely from a ui extension trying to load custom ui to edit the cluster
239
- const fromAnnotation = this.value.annotations?.[CAPI_ANNOTATIONS.UI_CUSTOM_PROVIDER];
240
-
241
- if (fromAnnotation) {
242
- this.selectType(fromAnnotation, false);
243
-
244
- return '';
245
- }
246
-
247
- // For custom RKE2 clusters, don't load an Ember page.
248
- // It should be the dashboard.
249
- if ( this.value.isRke2 && ((this.value.isCustom && this.mode === _EDIT) || (this.value.isCustom && this.as === _CONFIG && this.mode === _VIEW) || (this.subType || '').toLowerCase() === 'custom')) {
250
- // For admins, this.value.isCustom is used to check if it is a custom cluster.
251
- // For cluster owners, this.subtype is used.
252
- this.selectType('custom', false);
253
-
254
- return '';
255
- }
256
- // For existing RKE2/K3s clusters provisioned in Rancher,
257
- // set the subtype using the machine pool provisioner
258
- // do not use an iFramed Ember page.
259
- if ( this.value.isRke2 && this.value.machineProvider ) {
260
- this.selectType(this.value.machineProvider, false);
261
-
262
- return '';
263
- }
264
-
265
- if ( this.subType ) {
266
- // if driver type has a custom form component, don't load an ember page
267
- if (this.selectedSubType?.component) {
268
- return '';
269
- }
270
- // For RKE1 and hosted Kubernetes Clusters, set the ember link
271
- // so that we load the page rather than using RKE2 create
272
- if (this.selectedSubType?.emberLink) {
273
- return this.selectedSubType.emberLink;
274
- }
275
-
276
- return '';
277
- }
278
-
279
- if ( this.value.mgmt?.emberEditPath ) {
280
- // Iframe an old page
281
- return this.value.mgmt.emberEditPath;
282
- }
283
- }
284
-
285
- return '';
286
- },
287
-
288
248
  rke2Enabled: mapFeature(RKE2_FEATURE),
289
249
 
290
250
  // todo nb is this info stored anywhere else..?
@@ -310,6 +270,24 @@ export default {
310
270
  return this.value.isRke2;
311
271
  },
312
272
 
273
+ isEmberKontainerDriver() {
274
+ // RKE2/K3s clusters are never legacy Ember kontainer drivers
275
+ if (!this.value?.id || !this.value?.provisioner || this.value.isRke2) {
276
+ return false;
277
+ }
278
+
279
+ const provisioner = this.value.provisioner.toLowerCase();
280
+ // Resolve the provisioner to a driver name using the KONTAINER_TO_DRIVER map
281
+ const resolvedName = KONTAINER_TO_DRIVER[provisioner] || provisioner;
282
+
283
+ const driver = this.kontainerDrivers.find((d) => {
284
+ return d.driverName === resolvedName || d.driverName === provisioner || d.id === provisioner;
285
+ });
286
+
287
+ // If the driver exists and is not built-in, it's a legacy ember driver
288
+ return !!driver && !driver.spec?.builtIn;
289
+ },
290
+
313
291
  templateOptions() {
314
292
  if ( !this.rke2Enabled ) {
315
293
  return [];
@@ -327,16 +305,14 @@ export default {
327
305
  let out = [];
328
306
 
329
307
  const templates = this.templateOptions;
330
- const vueKontainerTypes = getters['plugins/clusterDrivers'];
331
308
  const machineTypes = this.nodeDrivers.filter((x) => x.spec.active && x.state === 'active');
332
309
 
333
- // Keeping this for non Rancher-managed kontainer drivers
310
+ // Kontainer drivers that don't have an extension-provided component are legacy Ember-based
311
+ // and no longer functional. Show them as disabled with an informational tooltip.
312
+ const emberRemovalTooltip = getters['i18n/t']('drivers.kontainer.emberRemovalTooltip');
313
+
334
314
  this.kontainerDrivers.filter((x) => (isImport ? x.showImport : x.showCreate)).forEach((obj) => {
335
- if ( vueKontainerTypes.includes(obj.driverName) ) {
336
- addType(this.$extension, obj.driverName, 'hosted', false);
337
- } else {
338
- addType(this.$extension, obj.driverName, 'hosted', false, (isImport ? obj.emberImportPath : obj.emberCreatePath));
339
- }
315
+ addType(this.$extension, obj.driverName, 'hosted', true, undefined, undefined, emberRemovalTooltip);
340
316
  });
341
317
  if (!isImport) {
342
318
  templates.forEach((chart) => {
@@ -360,7 +336,7 @@ export default {
360
336
  machineTypes.forEach((type) => {
361
337
  const id = type.spec.displayName || type.id;
362
338
 
363
- addType(this.$extension, id, _RKE2, false, null, undefined, type);
339
+ addType(this.$extension, id, _RKE2, false, undefined, type);
364
340
  });
365
341
 
366
342
  addType(this.$extension, 'custom', 'custom2', false);
@@ -409,7 +385,7 @@ export default {
409
385
  out.push(subtype);
410
386
  }
411
387
 
412
- function addType(plugin, id, group, disabled = false, emberLink = null, iconClass = undefined, providerConfig = undefined) {
388
+ function addType(plugin, id, group, disabled = false, iconClass = undefined, providerConfig = undefined, tooltip = undefined) {
413
389
  const label = getters['i18n/withFallback'](`cluster.provider."${ id }"`, null, id);
414
390
  const description = getters['i18n/withFallback'](`cluster.providerDescription."${ id }"`, null, '');
415
391
  const tag = '';
@@ -439,8 +415,8 @@ export default {
439
415
  iconClass,
440
416
  group,
441
417
  disabled,
442
- emberLink,
443
418
  tag,
419
+ tooltip,
444
420
  providerConfig
445
421
  };
446
422
 
@@ -594,12 +570,11 @@ export default {
594
570
  />
595
571
  </div>
596
572
  <div
597
- v-else-if="emberLink"
598
- class="embed"
573
+ v-else-if="isEmberKontainerDriver"
599
574
  >
600
- <EmberPage
601
- :force-new="true"
602
- :src="emberLink"
575
+ <Banner
576
+ color="warning"
577
+ label-key="drivers.kontainer.emberRemovalMessage"
603
578
  />
604
579
  </div>
605
580
  <CruResource
@@ -174,7 +174,7 @@ export default {
174
174
  Object.entries(this.chartValues).forEach(([name, value]) => {
175
175
  const key = this.chartVersionKey(name);
176
176
 
177
- this.set(this.userChartValues, key, value);
177
+ this.userChartValues[key] = value;
178
178
  });
179
179
  this.setAgentConfiguration();
180
180
  },
@@ -298,6 +298,7 @@ export default {
298
298
  isEmpty,
299
299
  AGENT_CONFIGURATION_TYPES,
300
300
  basicsValid: true,
301
+ registryConfigValid: true,
301
302
  originalIngressController: this.value.spec.rkeConfig.machineGlobalConfig?.[INGRESS_CONTROLLER] || INGRESS_NONE,
302
303
  };
303
304
  },
@@ -906,7 +907,8 @@ export default {
906
907
  return this.validationPassed &&
907
908
  this.fvFormIsValid &&
908
909
  this.etcdConfigValid &&
909
- this.basicsValid;
910
+ this.basicsValid &&
911
+ this.registryConfigValid;
910
912
  },
911
913
  nginxSupported() {
912
914
  if (this.serverArgs?.disable?.options.includes(RKE2_INGRESS_NGINX)) {
@@ -2518,14 +2520,14 @@ export default {
2518
2520
  >
2519
2521
  <template
2520
2522
  v-for="(obj, idx) in machinePools"
2521
- :key="idx"
2523
+ :key="obj.id"
2522
2524
  >
2523
2525
  <Tab
2524
2526
  v-if="!obj.remove"
2525
2527
  :key="obj.id"
2526
2528
  :weight="-1 * idx"
2527
2529
  :name="obj.id"
2528
- :label="obj.pool.name || '(Not Named)'"
2530
+ :label="obj.pool.name || t('cluster.machinePool.name.notNamed')"
2529
2531
  :show-header="false"
2530
2532
  :error="!machinePoolValidation[obj.id]"
2531
2533
  >
@@ -2688,6 +2690,7 @@ export default {
2688
2690
  <Tab
2689
2691
  :name="REGISTRIES_TAB_NAME"
2690
2692
  label-key="cluster.tabs.registry"
2693
+ :error="!registryConfigValid"
2691
2694
  >
2692
2695
  <Registries
2693
2696
  v-if="isActiveTabRegistries"
@@ -2703,6 +2706,7 @@ export default {
2703
2706
  @custom-registry-changed="toggleCustomRegistry"
2704
2707
  @registry-host-changed="handleRegistryHostChanged"
2705
2708
  @registry-secret-changed="handleRegistrySecretChanged"
2709
+ @registry-validation-changed="(val) => registryConfigValid = val"
2706
2710
  />
2707
2711
  </Tab>
2708
2712
 
@@ -106,6 +106,16 @@ export default {
106
106
  const max = this.value?.pool?.autoscalingMaxSize || 0;
107
107
 
108
108
  return max - min >= 0 ? undefined : this.t('cluster.machinePool.autoscaler.validation.isAutoscalerMaxGreaterThanMin');
109
+ },
110
+ uniquePoolName: (name) => {
111
+ if (!name) {
112
+ return undefined;
113
+ }
114
+
115
+ const otherPools = (this.machinePools || []).filter((p) => !p.remove && p !== this.value);
116
+ const isDuplicate = otherPools.some((p) => p.pool.name?.toLowerCase() === name.toLowerCase());
117
+
118
+ return isDuplicate ? this.t('cluster.machinePool.name.unique') : undefined;
109
119
  }
110
120
  }
111
121
  };
@@ -295,6 +305,7 @@ export default {
295
305
  :label="t('cluster.machinePool.name.label')"
296
306
  :required="true"
297
307
  :disabled="!value.config || !!value.config.id || busy"
308
+ :require-dirty="false"
298
309
  :rules="fvGetAndReportPathRules(MACHINE_POOL_VALIDATION.FIELDS.NAME)"
299
310
  data-testid="machine-pool-name-input"
300
311
  />