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

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 (473) hide show
  1. package/apis/impl/apis.ts +6 -0
  2. package/apis/index.ts +26 -0
  3. package/apis/intf/resources-api/cluster-api.ts +18 -0
  4. package/apis/intf/resources-api/mgmt-api.ts +15 -0
  5. package/apis/intf/resources-api/resource-base.ts +107 -0
  6. package/apis/intf/resources-api/resource-constants.ts +147 -0
  7. package/apis/intf/resources-api/resources-api.ts +143 -0
  8. package/apis/intf/resources.ts +49 -0
  9. package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
  10. package/apis/intf/shell-api/proxy.ts +216 -0
  11. package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
  12. package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
  13. package/apis/intf/shell.ts +12 -6
  14. package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
  15. package/apis/resources/index.ts +22 -0
  16. package/apis/resources/resources-api-class.ts +187 -0
  17. package/apis/shell/__tests__/proxy.test.ts +369 -0
  18. package/apis/shell/index.ts +8 -1
  19. package/apis/shell/modal.ts +4 -1
  20. package/apis/shell/notifications.ts +9 -6
  21. package/apis/shell/proxy.ts +256 -0
  22. package/apis/shell/slide-in.ts +4 -1
  23. package/apis/vue-shim.d.ts +2 -1
  24. package/assets/data/aws-regions.json +4 -0
  25. package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
  26. package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
  27. package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
  28. package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
  29. package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
  30. package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
  31. package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
  32. package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
  33. package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
  34. package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
  35. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
  36. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
  37. package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
  38. package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
  39. package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
  40. package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
  41. package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
  42. package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
  43. package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
  44. package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
  45. package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
  46. package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
  47. package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
  48. package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
  49. package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
  50. package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
  51. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
  52. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
  53. package/assets/styles/base/_variables.scss +2 -0
  54. package/assets/styles/fonts/_fontstack.scss +132 -8
  55. package/assets/styles/global/_layout.scss +4 -0
  56. package/assets/translations/en-us.yaml +165 -45
  57. package/assets/translations/zh-hans.yaml +1 -7
  58. package/chart/monitoring/ClusterSelector.vue +0 -21
  59. package/chart/monitoring/index.vue +10 -1
  60. package/chart/monitoring/prometheus/index.vue +6 -3
  61. package/components/ActionDropdownShell.vue +2 -1
  62. package/components/CruResource.vue +161 -14
  63. package/components/CruResourceFooter.vue +9 -5
  64. package/components/ExplorerMembers.vue +8 -4
  65. package/components/ExplorerProjectsNamespaces.vue +11 -7
  66. package/components/GrowlManager.vue +4 -0
  67. package/components/InstallHelmCharts.vue +2 -2
  68. package/components/LandingPagePreference.vue +14 -5
  69. package/components/MgmtNodeList.vue +184 -0
  70. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  71. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  72. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  73. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  74. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +17 -1
  75. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  76. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  77. package/components/Resource/Detail/Metadata/index.vue +6 -0
  78. package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
  79. package/components/Resource/Detail/SpacedRow.vue +3 -1
  80. package/components/Resource/Detail/TitleBar/index.vue +10 -11
  81. package/components/ResourceDetail/index.vue +1 -1
  82. package/components/ResourceList/Masthead.vue +19 -9
  83. package/components/ResourceList/index.vue +82 -1
  84. package/components/RichTranslation.vue +5 -2
  85. package/components/SelectIconGrid.vue +0 -10
  86. package/components/Setting.vue +1 -0
  87. package/components/SingleClusterInfo.vue +1 -0
  88. package/components/SortableTable/__tests__/sorting.test.ts +126 -0
  89. package/components/SortableTable/index.vue +6 -9
  90. package/components/SortableTable/selection.js +23 -5
  91. package/components/SortableTable/sorting.js +6 -3
  92. package/components/SubtleLink.vue +31 -6
  93. package/components/Tabbed/Tab.vue +29 -3
  94. package/components/Tabbed/index.vue +25 -3
  95. package/components/TableOfContents/TableOfContents.vue +109 -0
  96. package/components/TableOfContents/composables.ts +258 -0
  97. package/components/Window/ContainerShell.vue +21 -11
  98. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  99. package/components/Wizard.vue +23 -17
  100. package/components/fleet/AppCoChartGrid.vue +401 -0
  101. package/components/fleet/AppCoEmptyState.vue +127 -0
  102. package/components/fleet/AppCoPageHeader.vue +119 -0
  103. package/components/fleet/AppCoVersionSelect.vue +70 -0
  104. package/components/fleet/FleetBundles.vue +100 -12
  105. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  106. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  107. package/components/fleet/FleetClusterTargets/index.vue +226 -161
  108. package/components/fleet/FleetIntro.vue +7 -3
  109. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  110. package/components/fleet/FleetSecretSelector.vue +5 -3
  111. package/components/fleet/FleetValuesFrom.vue +8 -2
  112. package/components/fleet/GitRepoTargetTab.vue +0 -2
  113. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  114. package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
  115. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  116. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  117. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  118. package/components/fleet/HelmOpTargetTab.vue +64 -60
  119. package/components/fleet/HelmOpValuesTab.vue +129 -105
  120. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  121. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  122. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  123. package/components/fleet/__tests__/FleetClusterTargets.test.ts +402 -115
  124. package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
  125. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  126. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  127. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  128. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  129. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  130. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  131. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  132. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  133. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  134. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  135. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  136. package/components/fleet/dashboard/Empty.vue +8 -4
  137. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  138. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  139. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  140. package/components/form/ArrayList.vue +61 -4
  141. package/components/form/KeyValue.vue +23 -2
  142. package/components/form/LabeledSelect.vue +59 -4
  143. package/components/form/Labels.vue +22 -3
  144. package/components/form/NameNsDescription.vue +24 -5
  145. package/components/form/ResourceTabs/index.vue +1 -0
  146. package/components/form/Security.vue +6 -2
  147. package/components/form/WorkloadPorts.vue +2 -7
  148. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  149. package/components/form/__tests__/Security.test.ts +76 -0
  150. package/components/formatter/Autoscaler.vue +4 -4
  151. package/components/formatter/ClusterKubeVersion.vue +27 -0
  152. package/components/formatter/ClusterLink.vue +1 -7
  153. package/components/formatter/ClusterProvider.vue +6 -10
  154. package/components/formatter/FleetSummaryGraph.vue +0 -3
  155. package/components/formatter/InternalExternalIP.vue +10 -4
  156. package/components/formatter/MachineSummaryGraph.vue +1 -1
  157. package/components/formatter/PodsUsage.vue +2 -2
  158. package/components/formatter/ServiceTargets.vue +26 -7
  159. package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
  160. package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
  161. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  162. package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
  163. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  164. package/components/nav/Header.vue +4 -0
  165. package/components/nav/NamespaceFilter.vue +2 -2
  166. package/components/nav/TopLevelMenu.helper.ts +15 -3
  167. package/components/nav/TopLevelMenu.vue +22 -6
  168. package/components/nav/__tests__/Header.test.ts +15 -0
  169. package/components/nav/__tests__/TopLevelMenu.test.ts +263 -21
  170. package/components/templates/default.vue +9 -4
  171. package/components/templates/home.vue +23 -0
  172. package/components/templates/plain.vue +23 -0
  173. package/components/templates/standalone.vue +17 -0
  174. package/composables/useFormValidation.ts +93 -0
  175. package/composables/useHelmOpResources.test.ts +56 -0
  176. package/composables/useHelmOpResources.ts +32 -0
  177. package/composables/useStateColor.test.ts +325 -0
  178. package/composables/useStateColor.ts +128 -0
  179. package/composables/useVeeValidateField.test.ts +159 -0
  180. package/composables/useVeeValidateField.ts +67 -0
  181. package/config/home-links.js +1 -1
  182. package/config/labels-annotations.js +1 -0
  183. package/config/pagination-table-headers.js +18 -1
  184. package/config/product/explorer.js +17 -4
  185. package/config/product/manager.js +84 -21
  186. package/config/router/index.js +16 -0
  187. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  188. package/config/router/navigation-guards/authentication.js +10 -4
  189. package/config/router/routes.js +26 -6
  190. package/config/settings.ts +0 -2
  191. package/config/table-headers.js +23 -5
  192. package/config/types.js +11 -1
  193. package/core/__tests__/plugin-products.test.ts +904 -20
  194. package/core/plugin-products-base.ts +110 -10
  195. package/core/plugin-products.ts +4 -0
  196. package/core/plugin-types.ts +194 -31
  197. package/core/plugin.ts +18 -7
  198. package/core/productDebugger.js +9 -4
  199. package/core/types-provisioning.ts +77 -31
  200. package/core/types.ts +72 -22
  201. package/detail/__tests__/pod.test.ts +41 -0
  202. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  203. package/detail/__tests__/workload.test.ts +3 -152
  204. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  205. package/detail/harvesterhci.io.management.cluster.vue +6 -2
  206. package/detail/pod.vue +1 -1
  207. package/detail/provisioning.cattle.io.cluster.vue +34 -14
  208. package/detail/workload/index.vue +12 -55
  209. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  210. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
  211. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  212. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  213. package/edit/auth/__tests__/azuread.test.ts +247 -39
  214. package/edit/auth/__tests__/github.test.ts +234 -0
  215. package/edit/auth/__tests__/oidc.test.ts +26 -6
  216. package/edit/auth/__tests__/saml.test.ts +196 -0
  217. package/edit/auth/azuread.vue +197 -56
  218. package/edit/auth/github.vue +72 -13
  219. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  220. package/edit/auth/ldap/config.vue +8 -0
  221. package/edit/auth/ldap/index.vue +75 -1
  222. package/edit/auth/oidc.vue +119 -73
  223. package/edit/auth/saml.vue +76 -12
  224. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  225. package/edit/fleet.cattle.io.helmop.vue +491 -136
  226. package/edit/management.cattle.io.user.vue +5 -2
  227. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
  228. package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
  229. package/edit/networking.k8s.io.ingress/index.vue +75 -20
  230. package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
  231. package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
  232. package/edit/provisioning.cattle.io.cluster/rke2.vue +92 -14
  233. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +22 -0
  234. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
  235. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
  236. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
  237. package/edit/secret/__tests__/ssh.test.ts +5 -6
  238. package/edit/secret/basic.vue +31 -0
  239. package/edit/secret/index.vue +68 -17
  240. package/edit/secret/registry.vue +38 -0
  241. package/edit/secret/ssh.vue +29 -0
  242. package/edit/secret/tls.vue +30 -0
  243. package/edit/service.vue +4 -4
  244. package/edit/workload/Upgrading.vue +3 -3
  245. package/edit/workload/__tests__/Upgrading.test.ts +6 -9
  246. package/edit/workload/mixins/workload.js +2 -1
  247. package/list/fleet.cattle.io.bundle.vue +7 -104
  248. package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
  249. package/list/group.principal.vue +5 -4
  250. package/list/harvesterhci.io.management.cluster.vue +8 -9
  251. package/list/management.cattle.io.user.vue +12 -9
  252. package/list/provisioning.cattle.io.cluster.vue +268 -180
  253. package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
  254. package/mixins/__tests__/auth-config.test.ts +90 -0
  255. package/mixins/__tests__/chart.test.ts +206 -0
  256. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  257. package/mixins/auth-config.js +7 -0
  258. package/mixins/brand.js +2 -1
  259. package/mixins/chart.js +22 -9
  260. package/mixins/child-hook.js +12 -6
  261. package/mixins/create-edit-view/impl.js +5 -3
  262. package/mixins/resource-fetch-api-pagination.js +62 -6
  263. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  264. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  265. package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
  266. package/models/__tests__/fleet-application.test.ts +175 -0
  267. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  268. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  269. package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
  270. package/models/__tests__/management.cattle.io.node.ts +28 -5
  271. package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
  272. package/models/__tests__/namespace.test.ts +36 -0
  273. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +81 -11
  274. package/models/__tests__/workload.test.ts +401 -26
  275. package/models/base-cluster.x-k8s.io.js +26 -0
  276. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  277. package/models/cluster.js +1 -1
  278. package/models/cluster.x-k8s.io.machine.js +4 -22
  279. package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
  280. package/models/cluster.x-k8s.io.machineset.js +2 -20
  281. package/models/compliance.cattle.io.clusterscan.js +165 -2
  282. package/models/ext.cattle.io.kubeconfig.ts +4 -7
  283. package/models/fleet-application.js +7 -1
  284. package/models/fleet.cattle.io.helmop.js +20 -1
  285. package/models/management.cattle.io.cluster.js +434 -41
  286. package/models/management.cattle.io.node.js +50 -7
  287. package/models/management.cattle.io.nodepool.js +1 -1
  288. package/models/namespace.js +1 -1
  289. package/models/networking.k8s.io.ingress.js +12 -4
  290. package/models/pod.js +33 -1
  291. package/models/provisioning.cattle.io.cluster.js +51 -334
  292. package/models/rke.cattle.io.etcdsnapshot.js +1 -2
  293. package/models/workload.js +108 -13
  294. package/models/workload.service.js +5 -0
  295. package/package.json +22 -39
  296. package/pages/__tests__/readme.test.ts +49 -0
  297. package/pages/about.vue +5 -6
  298. package/pages/auth/login.vue +0 -35
  299. package/pages/auth/setup.vue +13 -3
  300. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  301. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  302. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
  303. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  304. package/pages/c/_cluster/apps/charts/chart.vue +62 -9
  305. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  306. package/pages/c/_cluster/apps/charts/install.vue +122 -113
  307. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  308. package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
  309. package/pages/c/_cluster/explorer/index.vue +5 -49
  310. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  311. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  312. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  313. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  314. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  315. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  316. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  317. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  318. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  319. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  320. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  321. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  322. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  323. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  324. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  325. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  326. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  327. package/pages/c/_cluster/fleet/index.vue +2 -2
  328. package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
  329. package/pages/c/_cluster/istio/index.vue +21 -6
  330. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
  331. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +815 -2
  332. package/pages/c/_cluster/uiplugins/index.vue +218 -197
  333. package/pages/diagnostic.vue +13 -17
  334. package/pages/fail-whale.vue +30 -7
  335. package/pages/home.vue +93 -306
  336. package/pages/readme.vue +88 -0
  337. package/plugins/clean-html.d.ts +9 -0
  338. package/plugins/dashboard-store/__tests__/resource-class.test.ts +181 -0
  339. package/plugins/dashboard-store/actions.js +40 -18
  340. package/plugins/dashboard-store/resource-class.js +67 -9
  341. package/plugins/steve/__tests__/actions.test.ts +212 -0
  342. package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
  343. package/plugins/steve/actions.js +96 -0
  344. package/plugins/steve/steve-pagination-utils.ts +12 -4
  345. package/plugins/steve/subscribe.js +35 -5
  346. package/rancher-components/Accordion/Accordion.vue +53 -9
  347. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  348. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
  349. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
  350. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  351. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  352. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  353. package/rancher-components/RcButton/RcButton.test.ts +140 -1
  354. package/rancher-components/RcButton/RcButton.vue +126 -17
  355. package/rancher-components/RcButton/types.ts +3 -0
  356. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
  357. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  358. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  359. package/rancher-components/RcSection/RcSection.vue +28 -3
  360. package/scripts/extension/helm/package/Dockerfile +1 -1
  361. package/scripts/test-plugins-build.sh +2 -1
  362. package/store/__tests__/catalog.test.ts +115 -1
  363. package/store/__tests__/notifications.test.ts +434 -0
  364. package/store/__tests__/type-map.test.ts +556 -1
  365. package/store/action-menu.js +8 -3
  366. package/store/auth.js +1 -1
  367. package/store/aws.js +27 -16
  368. package/store/catalog.js +84 -3
  369. package/store/digitalocean.js +20 -38
  370. package/store/index.js +2 -0
  371. package/store/linode.js +25 -40
  372. package/store/plugins.js +7 -4
  373. package/store/pnap.js +1 -0
  374. package/store/type-map.js +111 -29
  375. package/tsconfig.paths.json +8 -8
  376. package/types/components/buttonGroup.ts +5 -0
  377. package/types/kube/kube-api.ts +14 -1
  378. package/types/rancher/steve.api.ts +12 -12
  379. package/types/resources/settings.d.ts +2 -1
  380. package/types/shell/index.d.ts +206 -72
  381. package/types/store/dashboard-store.types.ts +108 -11
  382. package/types/store/pagination.types.ts +6 -3
  383. package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
  384. package/utils/__tests__/async.test.ts +87 -0
  385. package/utils/__tests__/auth.test.ts +273 -0
  386. package/utils/__tests__/aws.test.ts +140 -0
  387. package/utils/__tests__/banners.test.ts +176 -0
  388. package/utils/__tests__/chart.test.ts +64 -1
  389. package/utils/__tests__/color.test.ts +226 -0
  390. package/utils/__tests__/computed.test.ts +193 -0
  391. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  392. package/utils/__tests__/dom.test.ts +81 -0
  393. package/utils/__tests__/duration.test.ts +176 -0
  394. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  395. package/utils/__tests__/fleet-appco.test.ts +312 -0
  396. package/utils/__tests__/fleet.test.ts +340 -0
  397. package/utils/__tests__/ingress.test.ts +553 -0
  398. package/utils/__tests__/kube.test.ts +68 -0
  399. package/utils/__tests__/monitoring.test.ts +130 -0
  400. package/utils/__tests__/namespace-filter.test.ts +109 -0
  401. package/utils/__tests__/object.test.ts +22 -0
  402. package/utils/__tests__/pagination-utils.test.ts +361 -0
  403. package/utils/__tests__/parse-externalid.test.ts +137 -0
  404. package/utils/__tests__/perf-setting.utils.test.ts +98 -0
  405. package/utils/__tests__/platform.test.ts +91 -0
  406. package/utils/__tests__/poller-sequential.test.ts +177 -0
  407. package/utils/__tests__/poller.test.ts +170 -0
  408. package/utils/__tests__/position.test.ts +237 -0
  409. package/utils/__tests__/promise.test.ts +346 -0
  410. package/utils/__tests__/provider.test.ts +51 -1
  411. package/utils/__tests__/queue.test.ts +232 -0
  412. package/utils/__tests__/release-notes.test.ts +221 -0
  413. package/utils/__tests__/router.test.js +254 -1
  414. package/utils/__tests__/select.test.ts +208 -0
  415. package/utils/__tests__/settings.test.ts +140 -0
  416. package/utils/__tests__/sort-utils.test.ts +301 -0
  417. package/utils/__tests__/string-utils.test.ts +798 -0
  418. package/utils/__tests__/string.test.ts +23 -1
  419. package/utils/__tests__/style.test.ts +154 -0
  420. package/utils/__tests__/svg-filter.test.ts +184 -0
  421. package/utils/__tests__/time.test.ts +265 -1
  422. package/utils/__tests__/title.test.ts +47 -0
  423. package/utils/__tests__/units.test.ts +417 -0
  424. package/utils/__tests__/versions.test.ts +128 -0
  425. package/utils/__tests__/width.test.ts +53 -0
  426. package/utils/__tests__/window.test.ts +158 -0
  427. package/utils/__tests__/xccdf.test.ts +511 -0
  428. package/utils/chart.js +36 -0
  429. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  430. package/utils/crypto/__tests__/index.test.ts +144 -0
  431. package/utils/duration.ts +104 -0
  432. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  433. package/utils/dynamic-content/info.ts +2 -1
  434. package/utils/error.js +13 -0
  435. package/utils/fleet-appco.ts +323 -0
  436. package/utils/fleet.ts +13 -3
  437. package/utils/gatekeeper/__tests__/util.test.ts +174 -0
  438. package/utils/gc/__tests__/gc-interval.test.ts +119 -0
  439. package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
  440. package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
  441. package/utils/gc/__tests__/gc.test.ts +487 -0
  442. package/utils/ingress.ts +9 -1
  443. package/utils/object.js +22 -2
  444. package/utils/pagination-utils.ts +2 -1
  445. package/utils/provider.ts +12 -0
  446. package/utils/string.js +25 -2
  447. package/utils/uiplugins.ts +5 -5
  448. package/utils/validators/__tests__/cluster-name.test.ts +110 -0
  449. package/utils/validators/__tests__/container-images.test.ts +104 -0
  450. package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
  451. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  452. package/utils/validators/__tests__/index.test.ts +481 -0
  453. package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
  454. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  455. package/utils/validators/__tests__/misc-validators.test.ts +246 -0
  456. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  457. package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
  458. package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
  459. package/utils/validators/__tests__/role-template.test.ts +149 -0
  460. package/utils/validators/__tests__/service.test.ts +283 -0
  461. package/utils/validators/__tests__/setting.test.js +32 -0
  462. package/utils/validators/formRules/__tests__/index.test.ts +50 -0
  463. package/utils/validators/formRules/index.ts +5 -5
  464. package/utils/validators/machine-pool.ts +1 -1
  465. package/utils/validators/setting.js +18 -3
  466. package/utils/xccdf.ts +415 -0
  467. package/vue.config.js +1 -1
  468. package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
  469. package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
  470. package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
  471. package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
  472. package/pages/support/index.vue +0 -264
  473. package/utils/duration.js +0 -43
@@ -0,0 +1,323 @@
1
+ import { base64Encode } from '@shell/utils/crypto';
2
+ import { CATALOG as CATALOG_TYPES, SECRET } from '@shell/config/types';
3
+ import { CATALOG, DESCRIPTION, FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
4
+ import { SECRET_TYPES } from '@shell/config/secret';
5
+
6
+ export const SUSE_APP_COLLECTION_REPO_URL = 'oci://dp.apps.rancher.io/charts';
7
+ export const FLEET_APPCO_AUTH_GENERATE_NAME = 'fleet-appco-auth-';
8
+ export const IMAGE_PULL_SECRET_SUFFIX = '-image-pull-secret';
9
+ export const SUSE_APPCO_DISPLAY_NAME = 'SUSE AppCo';
10
+
11
+ // Used when the Rancher version can't be parsed (e.g. dev builds), so we point
12
+ // at the latest, unversioned Fleet docs.
13
+ export const FLEET_DOWNSTREAM_RESOURCES_DOCS_FALLBACK_URL = 'https://fleet.rancher.io/next/downstream-resources';
14
+
15
+ /**
16
+ * Build the URL to the Fleet "downstream resources" docs for the running Rancher version.
17
+ *
18
+ * Rancher `2.X.0` (for X >= 15) ships with Fleet `0.(X+1)`, whose docs are published at
19
+ * `https://fleet.rancher.io/0.<minor + 1>/downstream-resources`. For anything older or
20
+ * unparseable we fall back to the unversioned `next` docs.
21
+ */
22
+ export function getDownstreamResourcesDocsUrl(rancherVersion?: string): string {
23
+ // Harcoded to 2.X.0, it is fragile because if the version changes it will break.
24
+ // Ideally it would require a correlation between versions, but we have the fallback in place.
25
+ const match = /^v?2\.(\d+)/.exec(rancherVersion || '');
26
+ const minor = match ? parseInt(match[1], 10) : NaN;
27
+
28
+ // It should only exists after version 0.15.0, which will be fleet 0.16.0.
29
+ if (!isNaN(minor) && minor >= 15) {
30
+ return `https://fleet.rancher.io/0.${ minor + 1 }/downstream-resources`;
31
+ }
32
+
33
+ return FLEET_DOWNSTREAM_RESOURCES_DOCS_FALLBACK_URL;
34
+ }
35
+
36
+ interface AuthCredentials {
37
+ publicKey: string;
38
+ privateKey: string;
39
+ }
40
+
41
+ export interface RepoState {
42
+ repoName: string;
43
+ stateDisplay: string;
44
+ stateBackground: string;
45
+ transitioning: boolean;
46
+ error: boolean;
47
+ errorMessage: string;
48
+ }
49
+
50
+ interface WaitResult {
51
+ repo: any;
52
+ state: RepoState | null;
53
+ // True only when the repo definitively does not exist (404), as opposed to the
54
+ // lookup failing for another reason (network/API error). Distinguishes "repo
55
+ // absent, safe to create" from "couldn't reach the repo".
56
+ notFound?: boolean;
57
+ }
58
+
59
+ interface VuexStore {
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ dispatch: (action: string, payload?: any) => Promise<any>;
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ getters: Record<string, any>;
64
+ }
65
+
66
+ export async function createAppCoAuthSecret(store: VuexStore, credentials: AuthCredentials, namespace: string) {
67
+ const { publicKey, privateKey } = credentials;
68
+
69
+ const secret = await store.dispatch(`${ CATALOG._MANAGEMENT }/create`, {
70
+ type: SECRET,
71
+ metadata: {
72
+ namespace,
73
+ generateName: FLEET_APPCO_AUTH_GENERATE_NAME,
74
+ labels: { [FLEET_LABELS.MANAGED]: 'true' }
75
+ }
76
+ });
77
+
78
+ secret._type = SECRET_TYPES.BASIC;
79
+ secret.data = {
80
+ username: base64Encode(publicKey),
81
+ password: base64Encode(privateKey),
82
+ };
83
+
84
+ await secret.save();
85
+
86
+ return secret;
87
+ }
88
+
89
+ export async function ensureAppCoImagePullSecret(store: VuexStore, authSecretName: string, namespace: string): Promise<string | undefined> {
90
+ const imagePullSecretName = `${ authSecretName }${ IMAGE_PULL_SECRET_SUFFIX }`;
91
+
92
+ let imagePullSecret = store.getters[`${ CATALOG._MANAGEMENT }/byId`](SECRET, `${ namespace }/${ imagePullSecretName }`);
93
+
94
+ if (!imagePullSecret) {
95
+ try {
96
+ imagePullSecret = await store.dispatch(`${ CATALOG._MANAGEMENT }/find`, { type: SECRET, id: `${ namespace }/${ imagePullSecretName }` });
97
+ } catch (e) {
98
+ let authSecret;
99
+
100
+ try {
101
+ authSecret = await store.dispatch(`${ CATALOG._MANAGEMENT }/find`, { type: SECRET, id: `${ namespace }/${ authSecretName }` });
102
+ } catch (_) {
103
+ console.warn(`AppCo: auth secret "${ authSecretName }" not found in namespace "${ namespace }", skipping image-pull-secret creation`); // eslint-disable-line no-console
104
+
105
+ return;
106
+ }
107
+
108
+ const registryHost = new URL(SUSE_APP_COLLECTION_REPO_URL.replace('oci://', 'https://')).host;
109
+ const username = authSecret.decodedData?.username || '';
110
+ const password = authSecret.decodedData?.password || '';
111
+ const config = { auths: { [registryHost]: { username, password } } };
112
+
113
+ const newSecret = await store.dispatch(`${ CATALOG._MANAGEMENT }/create`, {
114
+ type: SECRET,
115
+ _type: SECRET_TYPES.DOCKER_JSON,
116
+ metadata: {
117
+ name: imagePullSecretName,
118
+ namespace,
119
+ labels: { [FLEET_LABELS.MANAGED]: 'true' }
120
+ }
121
+ });
122
+
123
+ newSecret.setData('.dockerconfigjson', JSON.stringify(config));
124
+ await newSecret.save();
125
+ }
126
+ }
127
+
128
+ return imagePullSecretName;
129
+ }
130
+
131
+ export async function ensureAppCoClusterRepo(store: VuexStore, authSecretName: string, namespace: string, t: (key: string) => string): Promise<string> {
132
+ const repoName = deriveRepoName(authSecretName);
133
+ let repo = store.getters[`${ CATALOG._MANAGEMENT }/byId`](CATALOG_TYPES.CLUSTER_REPO, repoName);
134
+
135
+ if (!repo) {
136
+ try {
137
+ repo = await store.dispatch(`${ CATALOG._MANAGEMENT }/find`, { type: CATALOG_TYPES.CLUSTER_REPO, id: repoName });
138
+ } catch (e) {
139
+ try {
140
+ repo = await store.dispatch(`${ CATALOG._MANAGEMENT }/create`, {
141
+ type: CATALOG_TYPES.CLUSTER_REPO,
142
+ metadata: {
143
+ name: repoName,
144
+ annotations: {
145
+ [DESCRIPTION]: t('catalog.repo.target.suseAppCollection.description'),
146
+ [CATALOG.SUSE_APP_COLLECTION]: 'true',
147
+ },
148
+ },
149
+ spec: {
150
+ url: SUSE_APP_COLLECTION_REPO_URL,
151
+ clientSecret: {
152
+ namespace,
153
+ name: authSecretName,
154
+ },
155
+ },
156
+ });
157
+
158
+ await repo.save();
159
+ } catch (err: any) {
160
+ if (err.status === 409) {
161
+ return repoName;
162
+ }
163
+
164
+ throw err;
165
+ }
166
+ }
167
+ }
168
+
169
+ return repoName;
170
+ }
171
+
172
+ /**
173
+ * Verify the auth secret exists, then ensure its image-pull secret and ClusterRepo
174
+ * exist. Returns false (and creates nothing) if the secret cannot be found.
175
+ */
176
+ export async function ensureAppCoResources(
177
+ store: VuexStore,
178
+ authSecretName: string,
179
+ namespace: string,
180
+ t: (key: string) => string
181
+ ): Promise<boolean> {
182
+ const secretId = `${ namespace }/${ authSecretName }`;
183
+ let authSecret = store.getters[`${ CATALOG._MANAGEMENT }/byId`](SECRET, secretId);
184
+
185
+ if (!authSecret) {
186
+ try {
187
+ authSecret = await store.dispatch(`${ CATALOG._MANAGEMENT }/find`, { type: SECRET, id: secretId });
188
+ } catch (e) {
189
+ return false;
190
+ }
191
+ }
192
+
193
+ await Promise.all([
194
+ ensureAppCoImagePullSecret(store, authSecretName, namespace),
195
+ ensureAppCoClusterRepo(store, authSecretName, namespace, t),
196
+ ]);
197
+
198
+ return true;
199
+ }
200
+
201
+ const REPO_WAIT_TIMEOUT_MS = 90000;
202
+ const REPO_WAIT_INTERVAL_MS = 3000;
203
+
204
+ function getRepoState(repo: any, repoName: string): { state: RepoState; isReady: boolean; hasError: boolean } {
205
+ const state = repo.metadata?.state;
206
+ const conditions = repo.status?.conditions || [];
207
+ const ociCondition = conditions.find((c: any) => c.type === 'OCIDownloaded');
208
+ const isReady = ociCondition?.status === 'True';
209
+ const hasError = !!(state?.error || ociCondition?.error);
210
+
211
+ const repoState: RepoState = {
212
+ repoName,
213
+ stateDisplay: repo.stateDisplay,
214
+ stateBackground: repo.stateBackground,
215
+ transitioning: !isReady && !hasError,
216
+ error: hasError,
217
+ errorMessage: state?.message || ociCondition?.message || '',
218
+ };
219
+
220
+ return {
221
+ state: repoState, isReady, hasError
222
+ };
223
+ }
224
+
225
+ async function waitForRepoReady(
226
+ store: VuexStore,
227
+ repoName: string,
228
+ { onStateChange, signal }: { onStateChange?: (state: RepoState) => void; signal?: AbortSignal } = {}
229
+ ): Promise<WaitResult> {
230
+ let repo;
231
+
232
+ // `find` with `force: true` re-fetches and registers a watch, so the store's
233
+ // cached resource is kept up to date via subscription while we wait below.
234
+ try {
235
+ repo = await store.dispatch(`${ CATALOG._MANAGEMENT }/find`, {
236
+ type: CATALOG_TYPES.CLUSTER_REPO,
237
+ id: repoName,
238
+ opt: { force: true },
239
+ });
240
+ } catch (e: any) {
241
+ // A 404 means the repo simply doesn't exist yet; any other error means the
242
+ // lookup itself failed (network/API), which callers must not treat as "absent".
243
+ return {
244
+ repo: null, state: null, notFound: e?.status === 404
245
+ };
246
+ }
247
+
248
+ let result: WaitResult = { repo: null, state: null };
249
+
250
+ try {
251
+ await repo.waitForTestFn(() => {
252
+ if (signal?.aborted) {
253
+ return true;
254
+ }
255
+
256
+ // Read the latest resource from the store, kept fresh by the watch above.
257
+ const current = store.getters[`${ CATALOG._MANAGEMENT }/byId`](CATALOG_TYPES.CLUSTER_REPO, repoName);
258
+
259
+ if (!current) {
260
+ return true;
261
+ }
262
+
263
+ const { state, isReady, hasError } = getRepoState(current, repoName);
264
+
265
+ onStateChange?.(state);
266
+
267
+ if (hasError) {
268
+ result = { repo: null, state };
269
+
270
+ return true;
271
+ }
272
+
273
+ if (isReady) {
274
+ result = { repo: current, state };
275
+
276
+ return true;
277
+ }
278
+
279
+ return false;
280
+ }, `appco repo ${ repoName } ready`, REPO_WAIT_TIMEOUT_MS, REPO_WAIT_INTERVAL_MS);
281
+ } catch (e) {
282
+ // Timed out waiting for the repo to become ready
283
+ return result;
284
+ }
285
+
286
+ if (signal?.aborted) {
287
+ return { repo: null, state: null };
288
+ }
289
+
290
+ return result;
291
+ }
292
+
293
+ interface FetchChartsResult {
294
+ entries: Record<string, any[]> | null;
295
+ repoState: RepoState | null;
296
+ // True only when the repo does not exist (404); see WaitResult.notFound.
297
+ notFound?: boolean;
298
+ }
299
+
300
+ export async function fetchAppCoCharts(
301
+ store: VuexStore,
302
+ repoName: string,
303
+ onStateChange?: (state: RepoState) => void,
304
+ // Used to stop on unmount or when repoName changes
305
+ signal?: AbortSignal
306
+ ): Promise<FetchChartsResult> {
307
+ const { repo, state: repoState, notFound } = await waitForRepoReady(store, repoName, { onStateChange, signal });
308
+
309
+ if (!repo) {
310
+ return {
311
+ entries: null, repoState, notFound
312
+ };
313
+ }
314
+
315
+ const index = await repo.followLink('index');
316
+ const entries = index?.entries || {};
317
+
318
+ return { entries, repoState };
319
+ }
320
+
321
+ export function deriveRepoName(secretName: string): string {
322
+ return secretName ? secretName.replace('auth', 'repo') : '';
323
+ }
package/utils/fleet.ts CHANGED
@@ -33,6 +33,13 @@ function conditionIsTrue(conditions: Condition[] | undefined, type: string): boo
33
33
  }
34
34
 
35
35
  class Application {
36
+ /**
37
+ * gitrepos/helmops are already restricted to clusters in their own namespace
38
+ *
39
+ * this empty selector means all applicable clusters will be selected
40
+ */
41
+ includeAllWorkgroupRule = { clusterSelector: { matchExpressions: [] } }
42
+
36
43
  excludeHarvesterRule = {
37
44
  clusterSelector: {
38
45
  matchExpressions: [{
@@ -45,7 +52,7 @@ class Application {
45
52
  },
46
53
  };
47
54
 
48
- getTargetMode(targets: Target[], namespace: string): TargetMode {
55
+ getTargetMode(targets: Target[], namespace: string, areHarvesterHostsVisible: boolean): TargetMode {
49
56
  if (namespace === 'fleet-local') {
50
57
  return 'local';
51
58
  }
@@ -83,8 +90,11 @@ class Application {
83
90
  return target;
84
91
  });
85
92
 
86
- // Check if targets contains only harvester rule after name normalizing
87
- if (isEqual(normalized, [this.excludeHarvesterRule])) {
93
+ // Check if targets contains only harvester rule or no rule at all after name normalizing
94
+ // That means the ALL option has been selected previously
95
+ // one case for feature harvester-baremetal-container-workload ON
96
+ // and another for feature harvester-baremetal-container-workload OFF
97
+ if (isEqual(normalized, [this.includeAllWorkgroupRule]) || isEqual(normalized, [this.excludeHarvesterRule])) {
88
98
  mode = 'all';
89
99
  }
90
100
 
@@ -0,0 +1,174 @@
1
+ import { findTemplateType, findAllConstraintTypes, findAllTemplates, findAllConstraints } from '../util';
2
+ import { GATEKEEPER, SCHEMA } from '@shell/config/types';
3
+
4
+ describe('gatekeeper/util.js', () => {
5
+ describe('findTemplateType', () => {
6
+ it('returns the schema id when a matching schema exists', () => {
7
+ const schemas = [
8
+ {
9
+ id: 'templates.gatekeeper.sh.constrainttemplate',
10
+ attributes: { group: 'templates.gatekeeper.sh', kind: 'ConstraintTemplate' },
11
+ },
12
+ ];
13
+
14
+ expect(findTemplateType(schemas)).toStrictEqual('templates.gatekeeper.sh.constrainttemplate');
15
+ });
16
+
17
+ it('returns undefined when no schemas match', () => {
18
+ const schemas = [
19
+ {
20
+ id: 'other.group.somekind',
21
+ attributes: { group: 'other.group', kind: 'SomeKind' },
22
+ },
23
+ ];
24
+
25
+ expect(findTemplateType(schemas)).toBeUndefined();
26
+ });
27
+
28
+ it('returns undefined for an empty schemas array', () => {
29
+ expect(findTemplateType([])).toBeUndefined();
30
+ });
31
+
32
+ it('matches only on the correct group and kind together', () => {
33
+ const schemas = [
34
+ {
35
+ id: 'templates.gatekeeper.sh.other',
36
+ attributes: { group: 'templates.gatekeeper.sh', kind: 'Other' },
37
+ },
38
+ {
39
+ id: 'other.group.constrainttemplate',
40
+ attributes: { group: 'other.group', kind: 'ConstraintTemplate' },
41
+ },
42
+ ];
43
+
44
+ expect(findTemplateType(schemas)).toBeUndefined();
45
+ });
46
+
47
+ it('returns undefined when schema has no attributes', () => {
48
+ const schemas = [{ id: 'some.id' }];
49
+
50
+ expect(findTemplateType(schemas)).toBeUndefined();
51
+ });
52
+
53
+ it('returns the first matching schema id when multiple matches exist', () => {
54
+ const schemas = [
55
+ {
56
+ id: 'first-match',
57
+ attributes: { group: 'templates.gatekeeper.sh', kind: 'ConstraintTemplate' },
58
+ },
59
+ {
60
+ id: 'second-match',
61
+ attributes: { group: 'templates.gatekeeper.sh', kind: 'ConstraintTemplate' },
62
+ },
63
+ ];
64
+
65
+ expect(findTemplateType(schemas)).toStrictEqual('first-match');
66
+ });
67
+
68
+ it('handles schemas with null attributes gracefully', () => {
69
+ const schemas = [
70
+ { id: 'null-attrs', attributes: null },
71
+ {
72
+ id: 'templates.gatekeeper.sh.constrainttemplate',
73
+ attributes: { group: 'templates.gatekeeper.sh', kind: 'ConstraintTemplate' },
74
+ },
75
+ ];
76
+
77
+ expect(findTemplateType(schemas)).toStrictEqual('templates.gatekeeper.sh.constrainttemplate');
78
+ });
79
+ });
80
+
81
+ describe('findAllConstraintTypes', () => {
82
+ it('returns constraint type schema ids filtered by constraints.gatekeeper.sh group', () => {
83
+ const schemas = [
84
+ { id: 'constraints.gatekeeper.sh.k8srequiredlabels', attributes: { group: 'constraints.gatekeeper.sh' } },
85
+ { id: 'constraints.gatekeeper.sh.k8sallowedrepos', attributes: { group: 'constraints.gatekeeper.sh' } },
86
+ { id: 'other.group.something', attributes: { group: 'other.group' } },
87
+ ];
88
+ const store = { getters: { [`cluster/all`]: () => schemas } };
89
+
90
+ const result = findAllConstraintTypes(store);
91
+
92
+ expect(result).toStrictEqual([
93
+ 'constraints.gatekeeper.sh.k8srequiredlabels',
94
+ 'constraints.gatekeeper.sh.k8sallowedrepos',
95
+ ]);
96
+ });
97
+
98
+ it('returns an empty array when no constraint schemas exist', () => {
99
+ const store = { getters: { [`cluster/all`]: () => [] } };
100
+
101
+ expect(findAllConstraintTypes(store)).toStrictEqual([]);
102
+ });
103
+
104
+ it('calls store.getters["cluster/all"] with SCHEMA constant', () => {
105
+ const mockAll = jest.fn().mockReturnValue([]);
106
+ const store = { getters: { [`cluster/all`]: mockAll } };
107
+
108
+ findAllConstraintTypes(store);
109
+
110
+ expect(mockAll).toHaveBeenCalledWith(SCHEMA);
111
+ });
112
+
113
+ it('filters out schemas with missing or non-matching group', () => {
114
+ const schemas = [
115
+ { id: 'no-attrs' },
116
+ { id: 'null-group', attributes: { group: null } },
117
+ { id: 'constraints.gatekeeper.sh.valid', attributes: { group: 'constraints.gatekeeper.sh' } },
118
+ ];
119
+ const store = { getters: { [`cluster/all`]: () => schemas } };
120
+
121
+ const result = findAllConstraintTypes(store);
122
+
123
+ expect(result).toStrictEqual(['constraints.gatekeeper.sh.valid']);
124
+ });
125
+ });
126
+
127
+ describe('findAllTemplates', () => {
128
+ it('dispatches cluster/findAll with the constraint template type', async() => {
129
+ const expectedResult = [{ id: GATEKEEPER.CONSTRAINT_TEMPLATE }];
130
+ const mockDispatch = jest.fn().mockResolvedValue(expectedResult);
131
+ const store = { dispatch: mockDispatch };
132
+
133
+ const result = await findAllTemplates(store);
134
+
135
+ expect(mockDispatch).toHaveBeenCalledWith('cluster/findAll', { type: GATEKEEPER.CONSTRAINT_TEMPLATE });
136
+ expect(result).toStrictEqual(expectedResult);
137
+ });
138
+ });
139
+
140
+ describe('findAllConstraints', () => {
141
+ it('returns all constraint resources flattened across all constraint types', async() => {
142
+ const schemas = [
143
+ { id: 'constraints.gatekeeper.sh.type1', attributes: { group: 'constraints.gatekeeper.sh' } },
144
+ { id: 'constraints.gatekeeper.sh.type2', attributes: { group: 'constraints.gatekeeper.sh' } },
145
+ ];
146
+ const type1Resources = [{ id: 'r1' }, { id: 'r2' }];
147
+ const type2Resources = [{ id: 'r3' }];
148
+ const mockDispatch = jest.fn()
149
+ .mockResolvedValueOnce(type1Resources)
150
+ .mockResolvedValueOnce(type2Resources);
151
+ const store = {
152
+ getters: { [`cluster/all`]: () => schemas },
153
+ dispatch: mockDispatch,
154
+ };
155
+
156
+ const result = await findAllConstraints(store);
157
+
158
+ expect(mockDispatch).toHaveBeenCalledWith('cluster/findAll', { type: 'constraints.gatekeeper.sh.type1', opt: { force: true } });
159
+ expect(mockDispatch).toHaveBeenCalledWith('cluster/findAll', { type: 'constraints.gatekeeper.sh.type2', opt: { force: true } });
160
+ expect(result).toStrictEqual([{ id: 'r1' }, { id: 'r2' }, { id: 'r3' }]);
161
+ });
162
+
163
+ it('returns an empty array when there are no constraint types', async() => {
164
+ const store = {
165
+ getters: { [`cluster/all`]: () => [] },
166
+ dispatch: jest.fn(),
167
+ };
168
+
169
+ const result = await findAllConstraints(store);
170
+
171
+ expect(result).toStrictEqual([]);
172
+ });
173
+ });
174
+ });
@@ -0,0 +1,119 @@
1
+ describe('gc-interval', () => {
2
+ let gci: any;
3
+ let mockGc: any;
4
+
5
+ beforeEach(() => {
6
+ jest.useFakeTimers();
7
+
8
+ mockGc = {
9
+ gcEnabledSetting: jest.fn(),
10
+ gcEnabledInterval: jest.fn(),
11
+ };
12
+
13
+ jest.resetModules();
14
+ jest.mock('../gc', () => ({ default: mockGc, __esModule: true }));
15
+
16
+ gci = require('../gc-interval').default;
17
+ });
18
+
19
+ afterEach(() => {
20
+ jest.useRealTimers();
21
+ jest.restoreAllMocks();
22
+ });
23
+
24
+ describe('gcStartIntervals', () => {
25
+ it.each([
26
+ {
27
+ desc: 'gcEnabledSetting returns false',
28
+ enabledSetting: false,
29
+ enabledInterval: { enabled: true, interval: 60 },
30
+ },
31
+ {
32
+ desc: 'gcEnabledInterval returns enabled=false',
33
+ enabledSetting: true,
34
+ enabledInterval: { enabled: false, interval: 60 },
35
+ },
36
+ ])('does not start interval when $desc', ({ enabledSetting, enabledInterval }) => {
37
+ mockGc.gcEnabledSetting.mockReturnValue(enabledSetting);
38
+ mockGc.gcEnabledInterval.mockReturnValue(enabledInterval);
39
+
40
+ const ctx = { dispatch: jest.fn() };
41
+
42
+ gci.gcStartIntervals(ctx);
43
+
44
+ jest.advanceTimersByTime(120_000);
45
+ expect(ctx.dispatch).not.toHaveBeenCalled();
46
+ });
47
+
48
+ it('starts interval and dispatches garbageCollect when gc is enabled', () => {
49
+ mockGc.gcEnabledSetting.mockReturnValue(true);
50
+ mockGc.gcEnabledInterval.mockReturnValue({ enabled: true, interval: 60 });
51
+
52
+ const ctx = { dispatch: jest.fn() };
53
+
54
+ gci.gcStartIntervals(ctx);
55
+
56
+ jest.advanceTimersByTime(60_000);
57
+ expect(ctx.dispatch).toHaveBeenCalledWith('garbageCollect');
58
+ });
59
+
60
+ it('dispatches garbageCollect repeatedly according to interval', () => {
61
+ mockGc.gcEnabledSetting.mockReturnValue(true);
62
+ mockGc.gcEnabledInterval.mockReturnValue({ enabled: true, interval: 30 });
63
+
64
+ const ctx = { dispatch: jest.fn() };
65
+
66
+ gci.gcStartIntervals(ctx);
67
+
68
+ jest.advanceTimersByTime(90_000);
69
+ expect(ctx.dispatch).toHaveBeenCalledTimes(3);
70
+ expect(ctx.dispatch).toHaveBeenCalledWith('garbageCollect');
71
+ });
72
+
73
+ it('does not start a second interval if already running', () => {
74
+ mockGc.gcEnabledSetting.mockReturnValue(true);
75
+ mockGc.gcEnabledInterval.mockReturnValue({ enabled: true, interval: 60 });
76
+
77
+ const ctx = { dispatch: jest.fn() };
78
+
79
+ gci.gcStartIntervals(ctx);
80
+ gci.gcStartIntervals(ctx); // second call should be a no-op
81
+
82
+ jest.advanceTimersByTime(60_000);
83
+ expect(ctx.dispatch).toHaveBeenCalledTimes(1);
84
+ });
85
+ });
86
+
87
+ describe('gcStopIntervals', () => {
88
+ it('clears the running interval', () => {
89
+ mockGc.gcEnabledSetting.mockReturnValue(true);
90
+ mockGc.gcEnabledInterval.mockReturnValue({ enabled: true, interval: 60 });
91
+
92
+ const ctx = { dispatch: jest.fn() };
93
+
94
+ gci.gcStartIntervals(ctx);
95
+ gci.gcStopIntervals();
96
+
97
+ jest.advanceTimersByTime(120_000);
98
+ expect(ctx.dispatch).not.toHaveBeenCalled();
99
+ });
100
+
101
+ it('is safe to call when no interval is running', () => {
102
+ expect(() => gci.gcStopIntervals()).not.toThrow();
103
+ });
104
+
105
+ it('allows a new interval to be started after stopping', () => {
106
+ mockGc.gcEnabledSetting.mockReturnValue(true);
107
+ mockGc.gcEnabledInterval.mockReturnValue({ enabled: true, interval: 60 });
108
+
109
+ const ctx = { dispatch: jest.fn() };
110
+
111
+ gci.gcStartIntervals(ctx);
112
+ gci.gcStopIntervals();
113
+ gci.gcStartIntervals(ctx);
114
+
115
+ jest.advanceTimersByTime(60_000);
116
+ expect(ctx.dispatch).toHaveBeenCalledTimes(1);
117
+ });
118
+ });
119
+ });