@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
@@ -1,6 +1,9 @@
1
1
  <script lang="ts" setup>
2
- import { ref, reactive, watch, onMounted } from 'vue';
2
+ import {
3
+ ref, reactive, watch, onMounted, computed
4
+ } from 'vue';
3
5
  import { useRouter, onBeforeRouteUpdate } from 'vue-router';
6
+ import { useForm } from 'vee-validate';
4
7
 
5
8
  import UserRetentionHeader from '@shell/components/user.retention/user-retention-header.vue';
6
9
  import Footer from '@shell/components/form/Footer.vue';
@@ -20,8 +23,15 @@ import { ToggleSwitch } from '@components/Form/ToggleSwitch';
20
23
 
21
24
  import dayjs from 'dayjs';
22
25
 
26
+ type UserRetentionSettingId =
27
+ | typeof SETTING.DISABLE_INACTIVE_USER_AFTER
28
+ | typeof SETTING.DELETE_INACTIVE_USER_AFTER
29
+ | typeof SETTING.USER_RETENTION_CRON
30
+ | typeof SETTING.USER_RETENTION_DRY_RUN
31
+ | typeof SETTING.USER_LAST_LOGIN_DEFAULT;
32
+
23
33
  const store = useStore();
24
- const userRetentionSettings = reactive<{[id: string]: string | null }>({
34
+ const userRetentionSettings = reactive<Record<UserRetentionSettingId, string | null>>({
25
35
  [SETTING.DISABLE_INACTIVE_USER_AFTER]: null,
26
36
  [SETTING.DELETE_INACTIVE_USER_AFTER]: null,
27
37
  [SETTING.USER_RETENTION_CRON]: null,
@@ -38,18 +48,28 @@ const {
38
48
  validateDeleteInactiveUserAfterDuration,
39
49
  validateDeleteInactiveUserAfter,
40
50
  validateDurationAgainstAuthUserSession,
41
- setValidation,
42
- removeValidation,
43
- addValidation,
44
- isFormValid,
45
51
  } = useUserRetentionValidation(disableAfterPeriod, deleteAfterPeriod, authUserSessionTtlMinutes);
52
+
53
+ const { errors, validate: validateForm, validateField } = useForm({
54
+ validationSchema: {
55
+ [SETTING.DISABLE_INACTIVE_USER_AFTER]: (value: string) => validateDisableInactiveUserAfterDuration(value) ??
56
+ validateDurationAgainstAuthUserSession(value) ??
57
+ true,
58
+ [SETTING.DELETE_INACTIVE_USER_AFTER]: (value: string) => validateDeleteInactiveUserAfterDuration(value) ??
59
+ validateDurationAgainstAuthUserSession(value) ??
60
+ validateDeleteInactiveUserAfter(value) ??
61
+ true,
62
+ [SETTING.USER_RETENTION_CRON]: (value: string) => validateUserRetentionCron(value) ?? true,
63
+ },
64
+ });
65
+
46
66
  let settings: { [id: string]: Setting } = {};
47
67
 
48
68
  /**
49
69
  * Watches the disable after period and removes the value if the checkbox is
50
70
  * not selected. Lookup the value when the checkbox is selected.
51
71
  */
52
- watch(disableAfterPeriod, (newVal) => {
72
+ watch(disableAfterPeriod, async(newVal) => {
53
73
  if (!newVal) {
54
74
  userRetentionSettings[SETTING.DISABLE_INACTIVE_USER_AFTER] = null;
55
75
 
@@ -57,13 +77,14 @@ watch(disableAfterPeriod, (newVal) => {
57
77
  }
58
78
 
59
79
  userRetentionSettings[SETTING.DISABLE_INACTIVE_USER_AFTER] = settings[SETTING.DISABLE_INACTIVE_USER_AFTER].value;
80
+ await validateField(SETTING.DISABLE_INACTIVE_USER_AFTER);
60
81
  });
61
82
 
62
83
  /**
63
84
  * Watches the delete after period and removes the value if the checkbox is
64
85
  * not selected. Lookup the value when the checkbox is selected.
65
86
  */
66
- watch(deleteAfterPeriod, (newVal) => {
87
+ watch(deleteAfterPeriod, async(newVal) => {
67
88
  if (!newVal) {
68
89
  userRetentionSettings[SETTING.DELETE_INACTIVE_USER_AFTER] = null;
69
90
 
@@ -71,6 +92,8 @@ watch(deleteAfterPeriod, (newVal) => {
71
92
  }
72
93
 
73
94
  userRetentionSettings[SETTING.DELETE_INACTIVE_USER_AFTER] = settings[SETTING.DELETE_INACTIVE_USER_AFTER].value;
95
+ await validateField(SETTING.DELETE_INACTIVE_USER_AFTER);
96
+ await validateField(SETTING.USER_RETENTION_CRON);
74
97
  });
75
98
 
76
99
  /**
@@ -84,18 +107,19 @@ watch([disableAfterPeriod, deleteAfterPeriod], ([newDisableAfterPeriod, newDelet
84
107
  userRetentionSettings[key] = null;
85
108
  });
86
109
 
87
- removeValidation(SETTING.USER_RETENTION_CRON);
88
-
89
110
  return;
90
111
  }
91
112
 
92
- ids.filter((id) => ![SETTING.DISABLE_INACTIVE_USER_AFTER, SETTING.DELETE_INACTIVE_USER_AFTER].includes(id))
93
- .forEach(assignSettings);
113
+ const skippedIds: readonly UserRetentionSettingId[] = [
114
+ SETTING.DISABLE_INACTIVE_USER_AFTER,
115
+ SETTING.DELETE_INACTIVE_USER_AFTER,
116
+ ];
94
117
 
95
- addValidation(SETTING.USER_RETENTION_CRON);
118
+ ids.filter((id) => !skippedIds.includes(id))
119
+ .forEach(assignSettings);
96
120
  });
97
121
 
98
- const assignSettings = (key: string) => {
122
+ const assignSettings = (key: UserRetentionSettingId) => {
99
123
  if (settings[key].id === SETTING.USER_LAST_LOGIN_DEFAULT && settings[key].value && typeof settings[key].value === 'string') {
100
124
  const value = settings[key].value as string;
101
125
 
@@ -111,7 +135,7 @@ const fetchSetting = async(id: string) => {
111
135
  return await store.dispatch('management/find', { type: MANAGEMENT.SETTING, id });
112
136
  };
113
137
 
114
- const ids = Object.keys(userRetentionSettings);
138
+ const ids = Object.keys(userRetentionSettings) as UserRetentionSettingId[];
115
139
  const settingPromises = ids.map((id) => fetchSetting(id));
116
140
 
117
141
  onMounted(async() => {
@@ -136,6 +160,14 @@ onMounted(async() => {
136
160
  const { t } = useI18n(store);
137
161
  const error = ref<string | null>(null);
138
162
  const save = async(btnCB: (arg: boolean) => void) => {
163
+ const { valid } = await validateForm();
164
+
165
+ if (!valid) {
166
+ btnCB(false);
167
+
168
+ return;
169
+ }
170
+
139
171
  try {
140
172
  error.value = null;
141
173
  ids.forEach((key) => {
@@ -164,6 +196,10 @@ const save = async(btnCB: (arg: boolean) => void) => {
164
196
  }
165
197
  };
166
198
 
199
+ const isFormInvalid = computed(() => {
200
+ return Object.keys(errors.value).length > 0;
201
+ });
202
+
167
203
  const router = useRouter();
168
204
  const routeBack = () => {
169
205
  router.back();
@@ -199,12 +235,11 @@ onBeforeRouteUpdate((_to: unknown, _from: unknown) => {
199
235
  <labeled-input
200
236
  v-model:value="userRetentionSettings[SETTING.DISABLE_INACTIVE_USER_AFTER]"
201
237
  data-testid="disableAfterPeriodInput"
238
+ :name="SETTING.DISABLE_INACTIVE_USER_AFTER"
202
239
  :tooltip="t('user.retention.edit.form.disableAfter.input.tooltip')"
203
240
  class="input-field"
204
241
  :label="t('user.retention.edit.form.disableAfter.input.label')"
205
242
  :disabled="!disableAfterPeriod"
206
- :rules="[validateDisableInactiveUserAfterDuration, validateDurationAgainstAuthUserSession]"
207
- @update:validation="e => setValidation(SETTING.DISABLE_INACTIVE_USER_AFTER, e)"
208
243
  />
209
244
  </div>
210
245
  <div class="input-fieldset">
@@ -216,13 +251,12 @@ onBeforeRouteUpdate((_to: unknown, _from: unknown) => {
216
251
  <labeled-input
217
252
  v-model:value="userRetentionSettings[SETTING.DELETE_INACTIVE_USER_AFTER]"
218
253
  data-testid="deleteAfterPeriodInput"
254
+ :name="SETTING.DELETE_INACTIVE_USER_AFTER"
219
255
  :tooltip="t('user.retention.edit.form.deleteAfter.input.tooltip')"
220
256
  class="input-field"
221
257
  :label="t('user.retention.edit.form.deleteAfter.input.label')"
222
258
  :sub-label="t('user.retention.edit.form.deleteAfter.input.subLabel')"
223
259
  :disabled="!deleteAfterPeriod"
224
- :rules="[validateDeleteInactiveUserAfterDuration, validateDurationAgainstAuthUserSession, validateDeleteInactiveUserAfter]"
225
- @update:validation="e => setValidation(SETTING.DELETE_INACTIVE_USER_AFTER, e)"
226
260
  />
227
261
  </div>
228
262
  <template
@@ -232,14 +266,13 @@ onBeforeRouteUpdate((_to: unknown, _from: unknown) => {
232
266
  <labeled-input
233
267
  v-model:value="userRetentionSettings[SETTING.USER_RETENTION_CRON]"
234
268
  data-testid="userRetentionCron"
269
+ :name="SETTING.USER_RETENTION_CRON"
235
270
  class="input-field"
236
271
  required
237
272
  type="cron"
238
273
  :tooltip="t('user.retention.edit.form.cron.subLabel')"
239
- :rules="[validateUserRetentionCron]"
240
274
  :label="t('user.retention.edit.form.cron.label')"
241
275
  :require-dirty="false"
242
- @update:validation="e => setValidation(SETTING.USER_RETENTION_CRON, e)"
243
276
  />
244
277
  </div>
245
278
  <div class="input-fieldset condensed pt-12">
@@ -268,7 +301,7 @@ onBeforeRouteUpdate((_to: unknown, _from: unknown) => {
268
301
  <Footer
269
302
  class="footer-user-retention"
270
303
  mode="edit"
271
- :disable-save="!isFormValid"
304
+ :disable-save="isFormInvalid"
272
305
  @save="save"
273
306
  @done="routeBack"
274
307
  />
@@ -1,12 +1,11 @@
1
- import { clone } from '@shell/utils/object';
1
+ import { mergeWithReplace } from '@shell/utils/object';
2
2
  import Dashboard from '@shell/pages/c/_cluster/explorer/index.vue';
3
3
  import { shallowMount } from '@vue/test-utils';
4
4
  import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
5
- import { NODE_ARCHITECTURE } from '@shell/config/labels-annotations';
6
5
  import { WORKLOAD_TYPES } from '@shell/config/types';
7
6
 
8
7
  describe('page: cluster dashboard', () => {
9
- const mountOptions = {
8
+ const createMountOptions = () => ({
10
9
  global: {
11
10
  stubs: {
12
11
  'router-link': true,
@@ -17,12 +16,11 @@ describe('page: cluster dashboard', () => {
17
16
  dispatch: jest.fn(),
18
17
  getters: {
19
18
  currentCluster: {
20
- id: 'cluster',
21
- metadata: { creationTimestamp: Date.now() },
22
- status: { provider: 'foo' },
23
- kubernetesVersionBase: '0.0.0',
24
- kubernetesVersionExtension: 'k3s',
19
+ id: 'cluster',
20
+ metadata: { creationTimestamp: Date.now() },
21
+ status: { provider: 'foo' },
25
22
  },
23
+ 'management/byId': jest.fn(),
26
24
  'cluster/inError': () => false,
27
25
  'cluster/schemaFor': jest.fn(),
28
26
  'cluster/canList': jest.fn(),
@@ -36,7 +34,7 @@ describe('page: cluster dashboard', () => {
36
34
  }
37
35
  },
38
36
  }
39
- };
37
+ });
40
38
 
41
39
  describe.each([
42
40
  'etcd',
@@ -49,7 +47,7 @@ describe('page: cluster dashboard', () => {
49
47
  [STATES_ENUM.HEALTHY, 'icon-checkmark', `${ componentId }foo`, [{ status: 'True' }]],
50
48
  [STATES_ENUM.UNHEALTHY, 'icon-warning', `${ componentId }foo`, [{ status: 'False' }]],
51
49
  ])('should show %p status', (status, iconClass, name, conditions) => {
52
- const options = clone(mountOptions);
50
+ const options = createMountOptions();
53
51
 
54
52
  options.global.mocks.$store.getters.currentCluster.status = {
55
53
  provider: 'provider',
@@ -100,7 +98,7 @@ describe('page: cluster dashboard', () => {
100
98
  ]]
101
99
  ])('%p cluster - %p agent health box :', (_, agentId, isLocal, agentResources, statuses) => {
102
100
  it.each(statuses)('should NOT show %p status due to missing canList permissions', (status, iconClass, isLoaded, disconnected, error, conditions, readyReplicas, unavailableReplicas) => {
103
- const options = clone(mountOptions);
101
+ const options = createMountOptions();
104
102
 
105
103
  options.global.mocks.$store.getters.currentCluster.isLocal = isLocal;
106
104
 
@@ -168,7 +166,7 @@ describe('page: cluster dashboard', () => {
168
166
  it.each(statuses)('should show %p status', async(status, iconClass, clickable, isLoaded, disconnected, error, conditions, readyReplicas, unavailableReplicas) => {
169
167
  let agentRoute = null;
170
168
 
171
- const options = clone(mountOptions);
169
+ const options = createMountOptions();
172
170
 
173
171
  options.global.mocks.$store.getters.currentCluster.isLocal = isLocal;
174
172
 
@@ -221,7 +219,7 @@ describe('page: cluster dashboard', () => {
221
219
  });
222
220
 
223
221
  it('local cluster - cattle agent health box - should be hidden', () => {
224
- const options = clone(mountOptions);
222
+ const options = createMountOptions();
225
223
 
226
224
  options.global.mocks.$store.getters.currentCluster.isLocal = true;
227
225
 
@@ -241,18 +239,18 @@ describe('page: cluster dashboard', () => {
241
239
 
242
240
  describe('cluster details', () => {
243
241
  it.each([
244
- ['clusterProvider', 'other', []],
245
- ['kubernetesVersion', 'glance.version', []],
246
- ['created', 'glance.created', []],
247
- ['architecture', 'mixed', [{ labels: { [NODE_ARCHITECTURE]: 'amd64' } }, { labels: { [NODE_ARCHITECTURE]: 'intel' } }]],
248
- ['architecture', 'mixed', [{ labels: { [NODE_ARCHITECTURE]: 'amd64' } }, { labels: { } }]],
249
- ['architecture', 'amd64', [{ labels: { [NODE_ARCHITECTURE]: 'amd64' } }]],
250
- ['architecture', 'unknown', [{ labels: { } }]],
251
- ['architecture', 'glance.architecture', [{ metadata: { state: { transitioning: true } } }]],
252
- ])('should show %p label %p', (label, text, nodes) => {
253
- const options = clone(mountOptions);
254
-
255
- options.global.mocks.$store.getters['cluster/all'] = () => nodes;
242
+ ['clusterProvider', 'abc', { provisionerDisplay: 'abc' }],
243
+ ['kubernetesVersion', 'glance.version', null],
244
+ ['created', 'glance.created', null],
245
+ ['architecture', 'mixed', { architecture: { label: 'mixed' } }],
246
+ ['architecture', 'amd64', { architecture: { label: 'amd64' } }],
247
+ ['architecture', 'glance.architecture', { architecture: { label: null } }],
248
+ ])('should show %p label %p', (label, text, mgmtCluster) => {
249
+ const options = createMountOptions();
250
+
251
+ const currentCluster = options.global.mocks.$store.getters['currentCluster'];
252
+
253
+ options.global.mocks.$store.getters['currentCluster'] = mgmtCluster ? mergeWithReplace(currentCluster, mgmtCluster) : currentCluster; // eslint-disable-line jest/no-if
256
254
 
257
255
  const wrapper = shallowMount(Dashboard, options);
258
256
 
@@ -17,7 +17,6 @@ import {
17
17
  CATALOG,
18
18
  SECRET
19
19
  } from '@shell/config/types';
20
- import { NODE_ARCHITECTURE } from '@shell/config/labels-annotations';
21
20
  import { setPromiseResult } from '@shell/utils/promise';
22
21
  import AlertTable from '@shell/components/AlertTable';
23
22
  import { Banner } from '@components/Banner';
@@ -220,51 +219,11 @@ export default {
220
219
  },
221
220
 
222
221
  displayProvider() {
223
- const other = 'other';
224
-
225
- let provider = this.currentCluster?.status?.provider || this.currentCluster?.status?.driver.toLowerCase() || other;
226
-
227
- if (provider === 'rke.windows') {
228
- provider = 'rkeWindows';
229
- }
230
-
231
- if (!this.$store.getters['i18n/exists'](`cluster.provider.${ provider }`)) {
232
- provider = 'other';
233
- }
234
-
235
- return this.t(`cluster.provider.${ provider }`);
236
- },
237
-
238
- nodesArchitecture() {
239
- const obj = {};
240
-
241
- this.nodes?.forEach((node) => {
242
- if (!node.metadata?.state?.transitioning) {
243
- const architecture = node.labels?.[NODE_ARCHITECTURE];
244
-
245
- const key = architecture || this.t('cluster.architecture.label.unknown');
246
-
247
- obj[key] = (obj[key] || 0) + 1;
248
- }
249
- });
250
-
251
- return obj;
222
+ return this.currentCluster?.provisionerDisplay;
252
223
  },
253
224
 
254
225
  architecture() {
255
- const keys = Object.keys(this.nodesArchitecture);
256
-
257
- switch (keys.length) {
258
- case 0:
259
- return { label: this.t('generic.provisioning') };
260
- case 1:
261
- return { label: keys[0] };
262
- default:
263
- return {
264
- label: this.t('cluster.architecture.label.mixed'),
265
- tooltip: keys.reduce((acc, k) => `${ acc }${ k }: ${ this.nodesArchitecture[k] }<br>`, '')
266
- };
267
- }
226
+ return this.currentCluster?.architecture;
268
227
  },
269
228
 
270
229
  isHarvesterCluster() {
@@ -395,6 +354,7 @@ export default {
395
354
 
396
355
  metricAggregations() {
397
356
  const metrics = this.nodeMetrics.filter((nodeMetrics) => {
357
+ // This should use cluster/byId getter
398
358
  const node = this.nodes.find((nd) => nd.id === nodeMetrics.id);
399
359
 
400
360
  return node;
@@ -464,9 +424,6 @@ export default {
464
424
  }
465
425
  };
466
426
  },
467
- hasNodes() {
468
- return this.nodes?.length > 0;
469
- },
470
427
  kubernetesVersion() {
471
428
  const base = this.currentCluster?.kubernetesVersionBase || '';
472
429
  const extension = this.currentCluster?.kubernetesVersionExtension || '';
@@ -584,8 +541,7 @@ export default {
584
541
 
585
542
  async goToHarvesterCluster() {
586
543
  try {
587
- const provClusters = await this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER });
588
- const provCluster = provClusters.find((p) => p.mgmt.id === this.currentCluster.id);
544
+ const provCluster = await this.$store.dispatch('management/find', { type: CAPI.RANCHER_CLUSTER, id: this.currentCluster.provClusterId });
589
545
 
590
546
  await provCluster.goToHarvesterCluster();
591
547
  } catch {
@@ -653,7 +609,7 @@ export default {
653
609
  <span>{{ kubernetesVersion }}</span>
654
610
  </div>
655
611
  <div
656
- v-if="hasNodes"
612
+ v-if="architecture"
657
613
  data-testid="architecture__label"
658
614
  >
659
615
  <label>{{ t('glance.architecture') }}: </label>
@@ -0,0 +1,194 @@
1
+ import { nextTick } from 'vue';
2
+ import { shallowMount } from '@vue/test-utils';
3
+ import IstioOverview from '@shell/pages/c/_cluster/istio/index.vue';
4
+ import { SERVICE } from '@shell/config/types';
5
+ import Loading from '@shell/components/Loading';
6
+
7
+ const createMockService = (name: string, namespace: string, label: string, proxyUrlReturn: string) => ({
8
+ id: `${ namespace }/${ name }`,
9
+ type: 'service',
10
+ metadata: {
11
+ name,
12
+ namespace,
13
+ labels: { app: label },
14
+ },
15
+ proxyUrl: jest.fn(() => proxyUrlReturn),
16
+ });
17
+
18
+ describe('page: IstioOverview', () => {
19
+ const createWrapper = (overrides: Record<string, any> = {}) => {
20
+ const mockDispatch = jest.fn();
21
+ const mockSchemaFor = jest.fn();
22
+ const mockResolve = jest.fn(() => ({ href: '/c/_/monitoring' }));
23
+
24
+ const defaultMocks = {
25
+ $fetchState: { pending: false },
26
+ $store: {
27
+ dispatch: mockDispatch,
28
+ getters: {
29
+ 'prefs/theme': 'dark',
30
+ 'i18n/t': (key: string) => key,
31
+ 'cluster/schemaFor': mockSchemaFor,
32
+ },
33
+ },
34
+ $route: { params: { cluster: '_' } },
35
+ $router: { resolve: mockResolve },
36
+ };
37
+
38
+ const wrapper = shallowMount(IstioOverview, {
39
+ global: {
40
+ mocks: { ...defaultMocks, ...(overrides.mocks || {}) },
41
+ directives: { 'clean-html': () => {} },
42
+ stubs: { t: true, ...(overrides.stubs || {}) },
43
+ },
44
+ });
45
+
46
+ return {
47
+ wrapper, mockDispatch, mockSchemaFor, mockResolve
48
+ };
49
+ };
50
+
51
+ describe('fetch', () => {
52
+ it('dispatches findLabelSelector for kiali with correct label selector', async() => {
53
+ const { wrapper, mockDispatch, mockSchemaFor } = createWrapper();
54
+
55
+ mockSchemaFor.mockReturnValue({});
56
+ mockDispatch.mockResolvedValue({ data: [] });
57
+
58
+ await (IstioOverview as any).fetch.call(wrapper.vm);
59
+
60
+ expect(mockDispatch).toHaveBeenCalledWith('cluster/findLabelSelector', {
61
+ type: SERVICE,
62
+ matching: { labelSelector: { matchLabels: { app: 'kiali' } } },
63
+ opt: { transient: true },
64
+ });
65
+ });
66
+
67
+ it('dispatches findLabelSelector for jaeger with correct label selector', async() => {
68
+ const { wrapper, mockDispatch, mockSchemaFor } = createWrapper();
69
+
70
+ mockSchemaFor.mockReturnValue({});
71
+ mockDispatch.mockResolvedValue({ data: [] });
72
+
73
+ await (IstioOverview as any).fetch.call(wrapper.vm);
74
+
75
+ expect(mockDispatch).toHaveBeenCalledWith('cluster/findLabelSelector', {
76
+ type: SERVICE,
77
+ matching: { labelSelector: { matchLabels: { app: 'jaeger' } } },
78
+ opt: { transient: true },
79
+ });
80
+ });
81
+
82
+ it('sets kialiService and jaegerService from response data', async() => {
83
+ const mockKiali = createMockService('kiali', 'istio-system', 'kiali', '/proxy/kiali');
84
+ const mockJaeger = createMockService('jaeger-query', 'istio-system', 'jaeger', '/proxy/jaeger');
85
+ const { wrapper, mockDispatch, mockSchemaFor } = createWrapper();
86
+
87
+ mockSchemaFor.mockReturnValue({});
88
+ mockDispatch
89
+ .mockResolvedValueOnce({ data: [mockKiali] })
90
+ .mockResolvedValueOnce({ data: [mockJaeger] });
91
+
92
+ await (IstioOverview as any).fetch.call(wrapper.vm);
93
+
94
+ expect(wrapper.vm.kialiService).toStrictEqual(mockKiali);
95
+ expect(wrapper.vm.jaegerService).toStrictEqual(mockJaeger);
96
+ });
97
+
98
+ it('skips dispatches when schemaFor returns falsy', async() => {
99
+ const { wrapper, mockDispatch, mockSchemaFor } = createWrapper();
100
+
101
+ mockSchemaFor.mockReturnValue(null);
102
+
103
+ await (IstioOverview as any).fetch.call(wrapper.vm);
104
+
105
+ expect(mockDispatch).not.toHaveBeenCalledWith('cluster/findLabelSelector', expect.anything());
106
+ expect(wrapper.vm.kialiService).toBeNull();
107
+ expect(wrapper.vm.jaegerService).toBeNull();
108
+ });
109
+
110
+ it('sets services to null when response data is empty', async() => {
111
+ const { wrapper, mockDispatch, mockSchemaFor } = createWrapper();
112
+
113
+ mockSchemaFor.mockReturnValue({});
114
+ mockDispatch.mockResolvedValue({ data: [] });
115
+
116
+ await (IstioOverview as any).fetch.call(wrapper.vm);
117
+
118
+ expect(wrapper.vm.kialiService).toBeNull();
119
+ expect(wrapper.vm.jaegerService).toBeNull();
120
+ });
121
+ });
122
+
123
+ describe('computed properties', () => {
124
+ it('kialiUrl returns null when kialiService is null', () => {
125
+ const { wrapper } = createWrapper();
126
+
127
+ expect(wrapper.vm.kialiUrl).toBeNull();
128
+ });
129
+
130
+ it('kialiUrl calls proxyUrl with correct arguments when service exists', async() => {
131
+ const mockKiali = createMockService('kiali', 'istio-system', 'kiali', '/proxy/kiali');
132
+ const { wrapper } = createWrapper();
133
+
134
+ wrapper.setData({ kialiService: mockKiali });
135
+ await nextTick();
136
+
137
+ expect(wrapper.vm.kialiUrl).toStrictEqual('/proxy/kiali');
138
+ expect(mockKiali.proxyUrl).toHaveBeenCalledWith('http', '20001');
139
+ });
140
+
141
+ it('jaegerUrl returns null when jaegerService is null', () => {
142
+ const { wrapper } = createWrapper();
143
+
144
+ expect(wrapper.vm.jaegerUrl).toBeNull();
145
+ });
146
+
147
+ it('jaegerUrl calls proxyUrl with correct arguments and appends /jaeger/search', async() => {
148
+ const mockJaeger = createMockService('jaeger-query', 'istio-system', 'jaeger', '/proxy/jaeger');
149
+ const { wrapper } = createWrapper();
150
+
151
+ wrapper.setData({ jaegerService: mockJaeger });
152
+ await nextTick();
153
+
154
+ expect(wrapper.vm.jaegerUrl).toStrictEqual('/proxy/jaeger/jaeger/search');
155
+ expect(mockJaeger.proxyUrl).toHaveBeenCalledWith('http', '16686');
156
+ });
157
+ });
158
+
159
+ describe('template rendering', () => {
160
+ it('shows Loading component when fetchState is pending', () => {
161
+ const { wrapper } = createWrapper({ mocks: { $fetchState: { pending: true } } });
162
+
163
+ expect(wrapper.findComponent(Loading).exists()).toBe(true);
164
+ expect(wrapper.find('.links').exists()).toBe(false);
165
+ });
166
+
167
+ it('shows disabled state when service URLs are null', () => {
168
+ const { wrapper } = createWrapper();
169
+ const containers = wrapper.findAll('.link-container');
170
+
171
+ expect(containers).toHaveLength(2);
172
+ containers.forEach((container) => {
173
+ expect(container.classes()).toContain('disabled');
174
+ });
175
+ expect(wrapper.findAll('.disabled-msg')).toHaveLength(2);
176
+ });
177
+
178
+ it('hides disabled state when services are set', async() => {
179
+ const mockKiali = createMockService('kiali', 'istio-system', 'kiali', '/proxy/kiali');
180
+ const mockJaeger = createMockService('jaeger-query', 'istio-system', 'jaeger', '/proxy/jaeger');
181
+ const { wrapper } = createWrapper();
182
+
183
+ wrapper.setData({ kialiService: mockKiali, jaegerService: mockJaeger });
184
+ await nextTick();
185
+
186
+ const containers = wrapper.findAll('.link-container');
187
+
188
+ containers.forEach((container) => {
189
+ expect(container.classes()).not.toContain('disabled');
190
+ });
191
+ expect(wrapper.findAll('.disabled-msg')).toHaveLength(0);
192
+ });
193
+ });
194
+ });
@@ -8,12 +8,27 @@ export default {
8
8
  components: { Loading },
9
9
 
10
10
  async fetch() {
11
- try {
12
- this.kialiService = await this.$store.dispatch('cluster/find', { type: SERVICE, id: 'istio-system/kiali' });
13
- } catch {}
14
- try {
15
- this.jaegerService = await this.$store.dispatch('cluster/find', { type: SERVICE, id: 'istio-system/tracing' });
16
- } catch {}
11
+ if (this.$store.getters['cluster/schemaFor'](SERVICE)) {
12
+ try {
13
+ const kialiResponse = await this.$store.dispatch('cluster/findLabelSelector', {
14
+ type: SERVICE,
15
+ matching: { labelSelector: { matchLabels: { app: 'kiali' } } },
16
+ opt: { transient: true }
17
+ });
18
+
19
+ this.kialiService = kialiResponse.data?.[0] || null;
20
+ } catch {}
21
+
22
+ try {
23
+ const jaegerResponse = await this.$store.dispatch('cluster/findLabelSelector', {
24
+ type: SERVICE,
25
+ matching: { labelSelector: { matchLabels: { app: 'jaeger' } } },
26
+ opt: { transient: true }
27
+ });
28
+
29
+ this.jaegerService = jaegerResponse.data?.[0] || null;
30
+ } catch {}
31
+ }
17
32
  },
18
33
 
19
34
  data() {
@@ -1,6 +1,5 @@
1
1
  <script>
2
2
  import { NORMAN } from '@shell/config/types';
3
- import { isAdminUser } from '@shell/store/type-map';
4
3
  import ResourceTable from '@shell/components/ResourceTable';
5
4
  import AsyncButton from '@shell/components/AsyncButton';
6
5
  import Loading from '@shell/components/Loading';
@@ -20,18 +19,19 @@ export default {
20
19
  data() {
21
20
  return {
22
21
  allDrivers: null,
23
- canRefreshK8sMetadata: true,
24
22
  resource: NORMAN.KONTAINER_DRIVER,
25
23
  schema: this.$store.getters['rancher/schemaFor'](NORMAN.KONTAINER_DRIVER),
26
24
  useQueryParamsForSimpleFiltering: false,
27
25
  forceUpdateLiveAndDelayed: 10,
28
- showDeprecationBanner: isAdminUser(this.$store.getters),
29
26
  };
30
27
  },
31
28
  computed: {
32
29
  rows() {
33
30
  return this.allDrivers || [];
34
31
  },
32
+ hasEmberUiDrivers() {
33
+ return this.rows.some((driver) => driver.active && driver.isEmber);
34
+ },
35
35
  },
36
36
  methods: {
37
37
  async refreshK8sMetadata(buttonDone) {
@@ -63,7 +63,6 @@ export default {
63
63
  >
64
64
  <template #extraActions>
65
65
  <AsyncButton
66
- v-if="canRefreshK8sMetadata"
67
66
  mode="refresh"
68
67
  :action-label="t('drivers.actions.refresh')"
69
68
  :waiting-label="t('drivers.actions.refresh')"
@@ -75,10 +74,9 @@ export default {
75
74
  </template>
76
75
  </Masthead>
77
76
  <Banner
78
- v-if="showDeprecationBanner"
77
+ v-if="hasEmberUiDrivers"
79
78
  color="warning"
80
- label-key="drivers.kontainer.emberDeprecationMessage"
81
- data-testid="kontainer-driver-ember-deprecation-banner"
79
+ label-key="drivers.kontainer.emberRemovalMessage"
82
80
  />
83
81
  <ResourceTable
84
82
  :schema="schema"