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

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 (315) hide show
  1. package/assets/styles/global/_button.scss +1 -1
  2. package/assets/styles/global/_layout.scss +4 -0
  3. package/assets/translations/en-us.yaml +183 -51
  4. package/assets/translations/zh-hans.yaml +1 -7
  5. package/chart/monitoring/ClusterSelector.vue +0 -21
  6. package/chart/monitoring/prometheus/index.vue +6 -3
  7. package/components/ActionDropdownShell.vue +5 -3
  8. package/components/ButtonGroup.vue +26 -1
  9. package/components/CruResource.vue +212 -16
  10. package/components/ExplorerMembers.vue +8 -4
  11. package/components/ExplorerProjectsNamespaces.vue +10 -6
  12. package/components/GrowlManager.vue +4 -0
  13. package/components/MgmtNodeList.vue +184 -0
  14. package/components/PromptRestore.vue +93 -32
  15. package/components/Questions/index.vue +1 -0
  16. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  17. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  18. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  19. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  20. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  21. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  22. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  23. package/components/ResourceDetail/index.vue +1 -1
  24. package/components/ResourceList/Masthead.vue +7 -1
  25. package/components/ResourceList/index.vue +82 -1
  26. package/components/ResourceTable.vue +1 -0
  27. package/components/RichTranslation.vue +5 -2
  28. package/components/Setting.vue +1 -0
  29. package/components/SortableTable/index.vue +4 -3
  30. package/components/SubtleLink.vue +31 -6
  31. package/components/Tabbed/Tab.vue +29 -3
  32. package/components/Tabbed/index.vue +25 -3
  33. package/components/TableOfContents/TableOfContents.vue +109 -0
  34. package/components/TableOfContents/composables.ts +258 -0
  35. package/components/Window/ContainerShell.vue +21 -11
  36. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  37. package/components/Wizard.vue +23 -5
  38. package/components/__tests__/ButtonGroup.test.ts +56 -0
  39. package/components/__tests__/PromptRestore.test.ts +169 -19
  40. package/components/fleet/AppCoChartGrid.vue +401 -0
  41. package/components/fleet/AppCoEmptyState.vue +127 -0
  42. package/components/fleet/AppCoPageHeader.vue +119 -0
  43. package/components/fleet/AppCoVersionSelect.vue +70 -0
  44. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  45. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  46. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  47. package/components/fleet/FleetIntro.vue +7 -3
  48. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  49. package/components/fleet/FleetSecretSelector.vue +5 -3
  50. package/components/fleet/FleetValuesFrom.vue +8 -2
  51. package/components/fleet/GitRepoAdvancedTab.vue +1 -0
  52. package/components/fleet/GitRepoMetadataTab.vue +5 -0
  53. package/components/fleet/GitRepoTargetTab.vue +0 -2
  54. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  55. package/components/fleet/HelmOpAppCoConfigTab.vue +597 -0
  56. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  57. package/components/fleet/HelmOpMetadataTab.vue +5 -0
  58. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  59. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  60. package/components/fleet/HelmOpTargetTab.vue +64 -60
  61. package/components/fleet/HelmOpValuesTab.vue +129 -105
  62. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  63. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  64. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  65. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  66. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  67. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  68. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  69. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  70. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  71. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  72. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  73. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  74. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  75. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  76. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  77. package/components/fleet/dashboard/Empty.vue +8 -4
  78. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  79. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  80. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  81. package/components/form/ArrayList.vue +61 -4
  82. package/components/form/FileSelector.vue +39 -1
  83. package/components/form/KeyValue.vue +23 -2
  84. package/components/form/LabeledSelect.vue +39 -1
  85. package/components/form/Labels.vue +22 -3
  86. package/components/form/NameNsDescription.vue +13 -5
  87. package/components/form/PrivateRegistry.constants.ts +7 -0
  88. package/components/form/PrivateRegistry.vue +253 -18
  89. package/components/form/ResourceTabs/index.vue +1 -0
  90. package/components/form/SelectOrCreateAuthSecret.vue +140 -17
  91. package/components/form/__tests__/FileSelector.test.ts +23 -0
  92. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  93. package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
  94. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
  95. package/components/formatter/EtcdSnapshotName.vue +73 -0
  96. package/components/formatter/InternalExternalIP.vue +10 -4
  97. package/components/formatter/ServiceTargets.vue +26 -7
  98. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  99. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  100. package/components/nav/Header.vue +12 -1
  101. package/components/nav/TopLevelMenu.vue +7 -2
  102. package/components/nav/__tests__/Header.test.ts +15 -0
  103. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  104. package/components/templates/default.vue +16 -4
  105. package/components/templates/home.vue +9 -4
  106. package/components/templates/plain.vue +9 -4
  107. package/composables/useHelmOpResources.test.ts +56 -0
  108. package/composables/useHelmOpResources.ts +32 -0
  109. package/composables/useStateColor.test.ts +325 -0
  110. package/composables/useStateColor.ts +128 -0
  111. package/config/features.js +1 -0
  112. package/config/home-links.js +1 -1
  113. package/config/labels-annotations.js +3 -0
  114. package/config/product/explorer.js +17 -4
  115. package/config/product/manager.js +8 -0
  116. package/config/router/index.js +16 -0
  117. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  118. package/config/router/navigation-guards/authentication.js +10 -4
  119. package/config/router/routes.js +20 -6
  120. package/config/secret.ts +10 -0
  121. package/config/settings.ts +6 -4
  122. package/config/table-headers.js +3 -4
  123. package/config/types.js +16 -0
  124. package/core/plugin-products-base.ts +3 -3
  125. package/core/plugin-types.ts +83 -30
  126. package/core/plugin.ts +3 -0
  127. package/core/types-provisioning.ts +34 -1
  128. package/core/types.ts +15 -2
  129. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  130. package/detail/__tests__/workload.test.ts +3 -152
  131. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  132. package/detail/provisioning.cattle.io.cluster.vue +109 -7
  133. package/detail/workload/index.vue +12 -55
  134. package/dialog/RotateEncryptionKeyDialog.vue +33 -9
  135. package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
  136. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  137. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
  138. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +206 -0
  139. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  140. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  141. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  142. package/edit/auth/__tests__/azuread.test.ts +34 -9
  143. package/edit/auth/__tests__/github.test.ts +234 -0
  144. package/edit/auth/__tests__/oidc.test.ts +26 -6
  145. package/edit/auth/__tests__/saml.test.ts +196 -0
  146. package/edit/auth/azuread.vue +128 -95
  147. package/edit/auth/github.vue +72 -13
  148. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  149. package/edit/auth/ldap/config.vue +8 -0
  150. package/edit/auth/ldap/index.vue +75 -1
  151. package/edit/auth/oidc.vue +119 -73
  152. package/edit/auth/saml.vue +76 -12
  153. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  154. package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
  155. package/edit/fleet.cattle.io.gitrepo.vue +70 -16
  156. package/edit/fleet.cattle.io.helmop.vue +542 -141
  157. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  158. package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
  159. package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
  160. package/edit/management.cattle.io.user.vue +5 -2
  161. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
  162. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
  163. package/edit/provisioning.cattle.io.cluster/rke2.vue +89 -11
  164. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  165. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
  166. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
  167. package/list/group.principal.vue +5 -4
  168. package/list/harvesterhci.io.management.cluster.vue +8 -9
  169. package/list/management.cattle.io.user.vue +12 -9
  170. package/list/provisioning.cattle.io.cluster.vue +16 -10
  171. package/mixins/__tests__/auth-config.test.ts +90 -0
  172. package/mixins/__tests__/chart.test.ts +94 -0
  173. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  174. package/mixins/auth-config.js +7 -0
  175. package/mixins/chart.js +11 -2
  176. package/mixins/child-hook.js +12 -6
  177. package/mixins/create-edit-view/impl.js +5 -3
  178. package/mixins/resource-fetch-api-pagination.js +21 -1
  179. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  180. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  181. package/models/__tests__/fleet-application.test.ts +175 -0
  182. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  183. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  184. package/models/__tests__/management.cattle.io.node.ts +22 -0
  185. package/models/__tests__/namespace.test.ts +36 -0
  186. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +205 -0
  187. package/models/__tests__/secret.test.ts +68 -1
  188. package/models/__tests__/workload.test.ts +401 -26
  189. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  190. package/models/compliance.cattle.io.clusterscan.js +39 -4
  191. package/models/fleet-application.js +4 -0
  192. package/models/fleet.cattle.io.helmop.js +20 -1
  193. package/models/management.cattle.io.cluster.js +39 -5
  194. package/models/management.cattle.io.node.js +44 -3
  195. package/models/namespace.js +1 -1
  196. package/models/pod.js +46 -3
  197. package/models/provisioning.cattle.io.cluster.js +64 -14
  198. package/models/rke.cattle.io.etcdsnapshot.js +17 -9
  199. package/models/secret.js +19 -0
  200. package/models/workload.js +120 -20
  201. package/models/workload.service.js +5 -0
  202. package/package.json +14 -13
  203. package/pages/about.vue +5 -6
  204. package/pages/auth/login.vue +0 -35
  205. package/pages/auth/setup.vue +11 -0
  206. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  207. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  208. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  209. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
  210. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  211. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  212. package/pages/c/_cluster/apps/charts/install.vue +236 -144
  213. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  214. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  215. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  216. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  217. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  218. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  219. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  220. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  221. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  222. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  223. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  224. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  225. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  226. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  227. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  228. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  229. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  230. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  231. package/pages/c/_cluster/fleet/index.vue +2 -2
  232. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  233. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  234. package/pages/fail-whale.vue +16 -11
  235. package/pages/home.vue +16 -46
  236. package/pkg/require-asset.lib.js +25 -0
  237. package/pkg/vue.config.js +7 -0
  238. package/plugins/clean-html.d.ts +9 -0
  239. package/plugins/dashboard-store/__tests__/resource-class.test.ts +177 -0
  240. package/plugins/dashboard-store/getters.js +0 -1
  241. package/plugins/dashboard-store/resource-class.js +114 -19
  242. package/plugins/steve/__tests__/actions.test.ts +212 -0
  243. package/plugins/steve/actions.js +96 -0
  244. package/plugins/steve/steve-pagination-utils.ts +1 -1
  245. package/rancher-components/Accordion/Accordion.vue +53 -9
  246. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  247. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  248. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  249. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
  250. package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
  251. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  252. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  253. package/rancher-components/RcButton/RcButton.vue +94 -15
  254. package/rancher-components/RcButton/index.ts +1 -1
  255. package/rancher-components/RcButton/types.ts +3 -0
  256. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
  257. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  258. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  259. package/rancher-components/RcSection/RcSection.vue +28 -3
  260. package/scripts/extension/helm/package/Dockerfile +1 -1
  261. package/scripts/test-plugins-build.sh +2 -1
  262. package/store/__tests__/features.test.ts +131 -0
  263. package/store/__tests__/growl.test.ts +374 -0
  264. package/store/__tests__/modal.test.ts +131 -0
  265. package/store/__tests__/notifications.test.ts +434 -0
  266. package/store/__tests__/slideInPanel.test.ts +88 -0
  267. package/store/__tests__/type-map.utils.test.ts +433 -0
  268. package/store/catalog.js +57 -0
  269. package/store/features.js +4 -0
  270. package/store/plugins.js +7 -4
  271. package/types/components/buttonGroup.ts +5 -0
  272. package/types/shell/index.d.ts +166 -70
  273. package/utils/__tests__/auth.test.ts +273 -0
  274. package/utils/__tests__/computed.test.ts +193 -0
  275. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  276. package/utils/__tests__/dom.test.ts +81 -0
  277. package/utils/__tests__/duration.test.ts +37 -1
  278. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  279. package/utils/__tests__/fleet-appco.test.ts +312 -0
  280. package/utils/__tests__/monitoring.test.ts +130 -0
  281. package/utils/__tests__/object.test.ts +22 -0
  282. package/utils/__tests__/operation-cr.test.ts +34 -0
  283. package/utils/__tests__/platform.test.ts +91 -0
  284. package/utils/__tests__/position.test.ts +237 -0
  285. package/utils/__tests__/provider.test.ts +51 -1
  286. package/utils/__tests__/queue.test.ts +232 -0
  287. package/utils/__tests__/release-notes.test.ts +221 -0
  288. package/utils/__tests__/router.test.js +254 -1
  289. package/utils/__tests__/select.test.ts +208 -0
  290. package/utils/__tests__/time.test.ts +265 -1
  291. package/utils/__tests__/title.test.ts +47 -0
  292. package/utils/__tests__/width.test.ts +53 -0
  293. package/utils/__tests__/window.test.ts +158 -0
  294. package/utils/__tests__/xccdf.test.ts +126 -6
  295. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  296. package/utils/crypto/__tests__/index.test.ts +144 -0
  297. package/utils/duration.ts +104 -0
  298. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  299. package/utils/dynamic-content/info.ts +2 -1
  300. package/utils/error.js +13 -0
  301. package/utils/fleet-appco.ts +323 -0
  302. package/utils/object.js +22 -2
  303. package/utils/operation-cr.js +19 -0
  304. package/utils/provider.ts +12 -0
  305. package/utils/require-asset.ts +7 -0
  306. package/utils/validators/__tests__/container-images.test.ts +104 -0
  307. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  308. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  309. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  310. package/utils/validators/__tests__/private-registry.test.ts +27 -15
  311. package/utils/validators/private-registry.ts +15 -4
  312. package/utils/xccdf.ts +39 -42
  313. package/vue.config.js +1 -1
  314. package/pages/support/index.vue +0 -264
  315. package/utils/duration.js +0 -43
@@ -0,0 +1,119 @@
1
+ import { matching, interval } from '@shell/utils/validators/monitoring-route';
2
+
3
+ const mockGetters = {
4
+ 'i18n/t': (key: string, args?: object) => (args ? `${ key }:${ JSON.stringify(args) }` : key),
5
+ 'i18n/exists': () => false,
6
+ };
7
+
8
+ describe('validators/monitoring-route', () => {
9
+ describe('matching', () => {
10
+ it('adds error when both match and match_re are empty', () => {
11
+ const errors: string[] = [];
12
+
13
+ matching({ match: {}, match_re: {} }, mockGetters, errors, []);
14
+
15
+ expect(errors).toStrictEqual(['validation.monitoring.route.match']);
16
+ });
17
+
18
+ it('adds error when spec is empty object', () => {
19
+ const errors: string[] = [];
20
+
21
+ matching({}, mockGetters, errors, []);
22
+
23
+ expect(errors).toStrictEqual(['validation.monitoring.route.match']);
24
+ });
25
+
26
+ it('adds error when spec is null or undefined', () => {
27
+ const errors: string[] = [];
28
+
29
+ matching(null, mockGetters, errors, []);
30
+
31
+ expect(errors).toStrictEqual(['validation.monitoring.route.match']);
32
+ });
33
+
34
+ it('adds no error when match has entries', () => {
35
+ const errors: string[] = [];
36
+
37
+ matching({ match: { severity: 'critical' } }, mockGetters, errors, []);
38
+
39
+ expect(errors).toStrictEqual([]);
40
+ });
41
+
42
+ it('adds no error when match_re has entries', () => {
43
+ const errors: string[] = [];
44
+
45
+ matching({ match_re: { alertname: '.*' } }, mockGetters, errors, []);
46
+
47
+ expect(errors).toStrictEqual([]);
48
+ });
49
+
50
+ it('adds no error when both match and match_re have entries', () => {
51
+ const errors: string[] = [];
52
+
53
+ matching(
54
+ { match: { severity: 'warning' }, match_re: { alertname: '.*' } },
55
+ mockGetters,
56
+ errors,
57
+ []
58
+ );
59
+
60
+ expect(errors).toStrictEqual([]);
61
+ });
62
+ });
63
+
64
+ describe('interval', () => {
65
+ it.each([
66
+ {
67
+ desc: 'seconds unit',
68
+ value: '30s',
69
+ },
70
+ {
71
+ desc: 'minutes unit',
72
+ value: '5m',
73
+ },
74
+ {
75
+ desc: 'hours unit',
76
+ value: '2h',
77
+ },
78
+ {
79
+ desc: 'multi-digit seconds',
80
+ value: '120s',
81
+ },
82
+ ])('adds no error for valid interval: $desc', ({ value }) => {
83
+ const errors: string[] = [];
84
+
85
+ interval(value, mockGetters, errors, [], 'Interval');
86
+
87
+ expect(errors).toStrictEqual([]);
88
+ });
89
+
90
+ it.each([
91
+ {
92
+ desc: 'no unit',
93
+ value: '30',
94
+ },
95
+ {
96
+ desc: 'unknown unit',
97
+ value: '5d',
98
+ },
99
+ {
100
+ desc: 'empty string',
101
+ value: '',
102
+ },
103
+ {
104
+ desc: 'fractional value',
105
+ value: '1.5s',
106
+ },
107
+ {
108
+ desc: 'letters only',
109
+ value: 'abc',
110
+ },
111
+ ])('adds error for invalid interval: $desc', ({ value }) => {
112
+ const errors: string[] = [];
113
+
114
+ interval(value, mockGetters, errors, [], 'Interval');
115
+
116
+ expect(errors).toStrictEqual(['validation.monitoring.route.interval:{"key":"Interval"}']);
117
+ });
118
+ });
119
+ });
@@ -3,28 +3,22 @@ import { privateRegistryRequired } from '@shell/utils/validators/private-registr
3
3
  const makeCtx = (overrides: any = {}) => ({
4
4
  t: jest.fn((key: string, params?: any) => (params ? `${ key }:${ JSON.stringify(params) }` : key)),
5
5
  privateRegistryEnabled: false,
6
- normanCluster: { importedConfig: { privateRegistryURL: null } },
7
- isImportedCluster: true,
6
+ normanCluster: { importedConfig: {} },
7
+ $store: { getters: { 'management/byId': () => null } },
8
8
  ...overrides,
9
9
  });
10
10
 
11
- describe('privateRegistryRequired', () => {
12
- it('should return undefined when the cluster is not imported', () => {
13
- const ctx = makeCtx({ isImportedCluster: false, privateRegistryEnabled: true });
14
- const rule = privateRegistryRequired(ctx);
15
-
16
- expect(rule()).toBeUndefined();
17
- });
11
+ const storeWithGlobalRegistry = { getters: { 'management/byId': () => ({ value: 'registry.global.io' }) } };
18
12
 
19
- it('should default isImportedCluster to true when the property is absent from the context', () => {
20
- const ctx: any = {
21
- t: jest.fn((key: string, params?: any) => (params ? `${ key }:${ JSON.stringify(params) }` : key)),
13
+ describe('privateRegistryRequired', () => {
14
+ it('should return undefined when a global default registry is configured', () => {
15
+ const ctx = makeCtx({
22
16
  privateRegistryEnabled: true,
23
- normanCluster: { importedConfig: { privateRegistryURL: null } },
24
- };
17
+ $store: storeWithGlobalRegistry,
18
+ });
25
19
  const rule = privateRegistryRequired(ctx);
26
20
 
27
- expect(rule()).toStrictEqual('validation.required:{"key":"cluster.privateRegistry.label"}');
21
+ expect(rule()).toBeUndefined();
28
22
  });
29
23
 
30
24
  it('should return undefined when the registry toggle is off', () => {
@@ -73,4 +67,22 @@ describe('privateRegistryRequired', () => {
73
67
 
74
68
  expect(rule()).toBeUndefined();
75
69
  });
70
+
71
+ it('should handle $store getter throwing gracefully', () => {
72
+ const ctx = makeCtx({
73
+ privateRegistryEnabled: true,
74
+ normanCluster: { importedConfig: {} },
75
+ $store: {
76
+ getters: {
77
+ get 'management/byId'() {
78
+ throw new Error('no store');
79
+ }
80
+ }
81
+ },
82
+ });
83
+ const rule = privateRegistryRequired(ctx);
84
+
85
+ // Falls through to validation since no global default detected
86
+ expect(rule()).toStrictEqual('validation.required:{"key":"cluster.privateRegistry.label"}');
87
+ });
76
88
  });
@@ -1,19 +1,30 @@
1
+ import { VuexStore } from 'types/store/vuex';
1
2
  import { Translation } from '@shell/types/t';
2
3
  import formRulesGenerator from '@shell/utils/validators/formRules';
4
+ import { SETTING } from '@shell/config/settings';
5
+ import { MANAGEMENT } from '@shell/config/types';
3
6
 
4
7
  interface PrivateRegistryRuleContext {
5
8
  t: Translation;
6
9
  privateRegistryEnabled: boolean;
7
10
  normanCluster: { importedConfig?: { privateRegistryURL?: string | null } } | null;
8
- isImportedCluster?: boolean;
11
+ $store: VuexStore
9
12
  }
10
13
 
11
14
  export function privateRegistryRequired(ctx: PrivateRegistryRuleContext) {
12
15
  return () => {
13
- // Check existence using `in` rather than direct access to avoid Vue's render-time warning
14
- const isImported = 'isImportedCluster' in ctx ? !!ctx.isImportedCluster : true;
16
+ let hasGlobalDefault = false;
15
17
 
16
- if (!isImported || !ctx.privateRegistryEnabled) {
18
+ try {
19
+ // settings are loaded when the dashboard loads so using a synchronous getter is reliable here
20
+ const globalRegistrySetting = ctx.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.SYSTEM_DEFAULT_REGISTRY);
21
+
22
+ hasGlobalDefault = !!globalRegistrySetting?.value;
23
+ } catch {
24
+ // if no setting proceed like there's no global default
25
+ }
26
+
27
+ if (!ctx.privateRegistryEnabled || hasGlobalDefault) {
17
28
  return undefined;
18
29
  }
19
30
  const url = ctx.normanCluster?.importedConfig?.privateRegistryURL;
package/utils/xccdf.ts CHANGED
@@ -61,16 +61,17 @@ export interface BenchmarkMetadata {
61
61
  source?: string;
62
62
  }
63
63
 
64
- export interface StigCheckMetadata {
64
+ export interface CheckDecoration {
65
65
  ruleId?: string;
66
66
  version?: string;
67
67
  severity?: string;
68
68
  fixId?: string;
69
69
  checkId?: string;
70
- cci?: string[];
70
+ /** Generic XCCDF idents — STIG populates with CCIs, CIS with control IDs, etc. */
71
+ idents?: { system: string; value: string }[];
71
72
  }
72
73
 
73
- export type StigChecks = Record<string, StigCheckMetadata>;
74
+ export type CheckDecorations = Record<string, CheckDecoration>;
74
75
 
75
76
  export interface XccdfTargetFact {
76
77
  name: string;
@@ -82,7 +83,7 @@ export interface GenerateXccdfArgs {
82
83
  report: XccdfReport;
83
84
  benchmarkVersion: string;
84
85
  metadata?: BenchmarkMetadata;
85
- stigChecks?: StigChecks;
86
+ decorations?: CheckDecorations;
86
87
  targetAddresses?: string[];
87
88
  targetFacts?: XccdfTargetFact[];
88
89
  /** Override the cluster name used for the <target> element. Falls back to metadata.clusterName, then derived node names. */
@@ -101,32 +102,27 @@ const fixIdFor = (checkId: string): string => `${ ruleIdFor(checkId) }_fix`;
101
102
 
102
103
  const profileIdFor = (benchmarkVersion: string): string => `${ ID_PREFIX }_profile_${ safeId(benchmarkVersion) }`;
103
104
 
104
- const stigGroupId = (checkId: string): string => {
105
- const parts = checkId.split('-');
106
-
107
- if (parts.length >= 2) {
108
- return `${ parts[0] }-${ parts[1] }`;
105
+ const effectiveRuleId = (decoration: CheckDecoration, groupId: string, checkId: string, hitGroupKey: boolean): string => {
106
+ if (!decoration.ruleId) {
107
+ return ruleIdFor(checkId);
109
108
  }
109
+ if (!hitGroupKey) {
110
+ return decoration.ruleId;
111
+ }
112
+ const suffix = checkId.startsWith(`${ groupId }-`) ? checkId.slice(groupId.length + 1) : checkId;
110
113
 
111
- return checkId;
114
+ return `${ decoration.ruleId }_${ suffix }`;
112
115
  };
113
116
 
114
- const effectiveRuleId = (stig: StigCheckMetadata, checkId: string): string => {
115
- if (!stig.ruleId) {
116
- return ruleIdFor(checkId);
117
+ const lookupDecoration = (decorations: CheckDecorations, groupId: string, checkId: string): { decoration: CheckDecoration; hitGroupKey: boolean } => {
118
+ if (decorations[checkId]) {
119
+ return { decoration: decorations[checkId], hitGroupKey: false };
117
120
  }
118
- const gid = stigGroupId(checkId);
119
-
120
- if (checkId === gid) {
121
- return stig.ruleId;
121
+ if (decorations[groupId]) {
122
+ return { decoration: decorations[groupId], hitGroupKey: true };
122
123
  }
123
- const suffix = checkId.startsWith(`${ gid }-`) ? checkId.slice(gid.length + 1) : checkId;
124
-
125
- return `${ stig.ruleId }_${ suffix }`;
126
- };
127
124
 
128
- const lookupStig = (stigChecks: StigChecks, checkId: string): StigCheckMetadata => {
129
- return stigChecks[stigGroupId(checkId)] || {};
125
+ return { decoration: {}, hitGroupKey: false };
130
126
  };
131
127
 
132
128
  const mapResult = (state?: string): string => {
@@ -142,18 +138,18 @@ const mapResult = (state?: string): string => {
142
138
 
143
139
  const severityFor = (scored?: boolean): string => (scored ? 'medium' : 'low');
144
140
 
145
- const stigSeverity = (stigSev: string | undefined, scored?: boolean): string => stigSev || severityFor(scored);
141
+ const decorationSeverity = (sev: string | undefined, scored?: boolean): string => sev || severityFor(scored);
146
142
 
147
143
  const ruleWeight = (scored?: boolean): string => (scored ? '10' : '0');
148
144
 
149
145
  const ruleRole = (scored?: boolean): string => (scored ? 'full' : 'unscored');
150
146
 
151
- const stigIdents = (ccis?: string[]): { system: string; value: string }[] => {
152
- if (!ccis || ccis.length === 0) {
147
+ const decorationIdents = (idents?: { system: string; value: string }[]): { system: string; value: string }[] => {
148
+ if (!idents || idents.length === 0) {
153
149
  return [{ system: NA, value: NA }];
154
150
  }
155
151
 
156
- return ccis.map((cci) => ({ system: 'http://cyber.mil/cci', value: cci }));
152
+ return idents;
157
153
  };
158
154
 
159
155
  const collectTargets = (report: XccdfReport): string[] => {
@@ -187,7 +183,7 @@ export function generateXCCDF({
187
183
  report,
188
184
  benchmarkVersion,
189
185
  metadata = {},
190
- stigChecks = {},
186
+ decorations = {},
191
187
  targetAddresses,
192
188
  targetFacts,
193
189
  clusterName,
@@ -239,7 +235,7 @@ export function generateXCCDF({
239
235
  profile.ele('title').txt(benchmarkTitle);
240
236
  profile.ele('description').txt(na(metadata.description));
241
237
 
242
- const ruleResults: { check: XccdfReportCheck; effectiveId: string; stig: StigCheckMetadata }[] = [];
238
+ const ruleResults: { check: XccdfReportCheck; effectiveId: string; decoration: CheckDecoration }[] = [];
243
239
  const groups = report.results || [];
244
240
 
245
241
  groups.forEach((group) => {
@@ -250,10 +246,11 @@ export function generateXCCDF({
250
246
 
251
247
  (group.checks || []).forEach((check) => {
252
248
  const checkId = check.id || '';
253
- const stig = lookupStig(stigChecks, checkId);
254
- const effectiveId = effectiveRuleId(stig, checkId);
255
- const ruleFixId = stig.fixId || fixIdFor(checkId);
256
- const ruleCheckSystem = stig.checkId || CHECK_SYSTEM;
249
+ const groupId = group.id || '';
250
+ const { decoration, hitGroupKey } = lookupDecoration(decorations, groupId, checkId);
251
+ const effectiveId = effectiveRuleId(decoration, groupId, checkId, hitGroupKey);
252
+ const ruleFixId = decoration.fixId || fixIdFor(checkId);
253
+ const ruleCheckSystem = decoration.checkId || CHECK_SYSTEM;
257
254
  const checkHref = na(metadata.checkHref);
258
255
  const checkName = na(metadata.checkName);
259
256
 
@@ -262,10 +259,10 @@ export function generateXCCDF({
262
259
  selected: 'true',
263
260
  weight: ruleWeight(check.scored),
264
261
  role: ruleRole(check.scored),
265
- severity: stigSeverity(stig.severity, check.scored),
262
+ severity: decorationSeverity(decoration.severity, check.scored),
266
263
  });
267
264
 
268
- rule.ele('version').txt(na(stig.version));
265
+ rule.ele('version').txt(na(decoration.version));
269
266
  rule.ele('title').txt(na(`${ checkId } ${ check.description || '' }`));
270
267
  rule.ele('description').txt(na(check.description));
271
268
 
@@ -277,7 +274,7 @@ export function generateXCCDF({
277
274
  ruleRef.ele('type').txt(na(metadata.referenceType));
278
275
  ruleRef.ele('identifier').txt(na(metadata.referenceIdentifier));
279
276
 
280
- stigIdents(stig.cci).forEach((ident) => {
277
+ decorationIdents(decoration.idents).forEach((ident) => {
281
278
  rule.ele('ident', { system: ident.system }).txt(ident.value);
282
279
  });
283
280
 
@@ -290,7 +287,7 @@ export function generateXCCDF({
290
287
  ruleCheck.ele('check-content').txt(na(check.audit));
291
288
 
292
289
  ruleResults.push({
293
- check, effectiveId, stig
290
+ check, effectiveId, decoration
294
291
  });
295
292
  });
296
293
  });
@@ -348,8 +345,8 @@ export function generateXCCDF({
348
345
  rrCheck.ele('check-content-ref', { name: NA, href: NA });
349
346
  rrCheck.ele('check-content').txt(NA);
350
347
  } else {
351
- ruleResults.forEach(({ check, effectiveId, stig }) => {
352
- const ruleCheckSystem = stig.checkId || CHECK_SYSTEM;
348
+ ruleResults.forEach(({ check, effectiveId, decoration }) => {
349
+ const ruleCheckSystem = decoration.checkId || CHECK_SYSTEM;
353
350
  const checkHref = na(metadata.checkHref);
354
351
  const checkName = na(metadata.checkName);
355
352
 
@@ -357,12 +354,12 @@ export function generateXCCDF({
357
354
  idref: effectiveId,
358
355
  role: ruleRole(check.scored),
359
356
  time: timeStr,
360
- severity: stigSeverity(stig.severity, check.scored),
361
- version: na(stig.version),
357
+ severity: decorationSeverity(decoration.severity, check.scored),
358
+ version: na(decoration.version),
362
359
  weight: ruleWeight(check.scored),
363
360
  });
364
361
 
365
- stigIdents(stig.cci).forEach((ident) => {
362
+ decorationIdents(decoration.idents).forEach((ident) => {
366
363
  rr.ele('ident', { system: ident.system }).txt(ident.value);
367
364
  });
368
365
  rr.ele('result').txt(mapResult(check.state));
package/vue.config.js CHANGED
@@ -520,7 +520,7 @@ module.exports = function(dir, appConfig = {}) {
520
520
  @import "~shell/assets/styles/base/_variables.scss";
521
521
  @import "~shell/assets/styles/base/_functions.scss";
522
522
  @import "~shell/assets/styles/base/_mixins.scss";
523
- @import 'node_modules/xterm/css/xterm.css';
523
+ @import 'node_modules/@xterm/xterm/css/xterm.css';
524
524
  `
525
525
  }
526
526
  }
@@ -1,264 +0,0 @@
1
- <script>
2
- import BannerGraphic from '@shell/components/BannerGraphic';
3
- import IndentedPanel from '@shell/components/IndentedPanel';
4
- import CommunityLinks from '@shell/components/CommunityLinks';
5
- import { MANAGEMENT } from '@shell/config/types';
6
- import { getVendor } from '@shell/config/private-label';
7
- import { SETTING } from '@shell/config/settings';
8
- import { addParam } from '@shell/utils/url';
9
- import { isRancherPrime } from '@shell/config/version';
10
- import TabTitle from '@shell/components/TabTitle';
11
- import CspAdapterUtils from '@shell/utils/cspAdaptor';
12
-
13
- export default {
14
-
15
- components: {
16
- BannerGraphic,
17
- IndentedPanel,
18
- CommunityLinks,
19
- TabTitle
20
- },
21
-
22
- async fetch() {
23
- const fetchOrCreateSetting = async(id, val) => {
24
- let setting;
25
-
26
- try {
27
- setting = await this.$store.dispatch('management/find', { type: MANAGEMENT.SETTING, id });
28
- } catch {
29
- const schema = this.$store.getters['management/schemaFor'](MANAGEMENT.SETTING);
30
- const url = schema.linkFor('collection');
31
-
32
- setting = await this.$store.dispatch('management/create', {
33
- type: MANAGEMENT.SETTING,
34
- metadata: { name: id },
35
- value: val,
36
- default: val || ''
37
- });
38
-
39
- setting.save({ url });
40
- }
41
-
42
- return setting;
43
- };
44
-
45
- this.apps = await CspAdapterUtils.fetchCspAdaptorApp(this.$store);
46
- this.brandSetting = await fetchOrCreateSetting(SETTING.BRAND, '');
47
- this.serverUrlSetting = await fetchOrCreateSetting(SETTING.SERVER_URL, '');
48
- this.uiIssuesSetting = await this.$store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.ISSUES });
49
- this.settings = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.SETTING });
50
- },
51
-
52
- data() {
53
- return {
54
- apps: [],
55
- vendor: getVendor(),
56
- supportKey: '',
57
- brandSetting: null,
58
- uiIssuesSetting: null,
59
- serverSetting: null,
60
- settings: null,
61
- // i18n-uses support.promos.one.*
62
- // i18n-uses support.promos.two.*
63
- // i18n-uses support.promos.three.*
64
- // i18n-uses support.promos.four.*
65
- promos: [
66
- 'support.promos.one',
67
- 'support.promos.two',
68
- 'support.promos.three',
69
- 'support.promos.four',
70
- ]
71
- };
72
- },
73
-
74
- computed: {
75
- cspAdapter() {
76
- return CspAdapterUtils.hasCspAdapter({ $store: this.$store, apps: this.apps });
77
- },
78
-
79
- hasSupport() {
80
- return this.hasAWSSupport || isRancherPrime();
81
- },
82
-
83
- hasAWSSupport() {
84
- return !!this.cspAdapter;
85
- },
86
-
87
- serverUrl() {
88
- // Client-side rendered: use the current window location
89
- return window.location.origin;
90
- },
91
-
92
- supportConfigLink() {
93
- const adapter = this.cspAdapter;
94
-
95
- if (!adapter) {
96
- return false;
97
- }
98
-
99
- if (adapter.metadata.name === 'rancher-csp-billing-adapter') {
100
- return `${ this.serverUrl }/v1/generateSUSERancherSupportConfig?usePAYG=true`;
101
- } else {
102
- return `${ this.serverUrl }/v1/generateSUSERancherSupportConfig`;
103
- }
104
- },
105
-
106
- title() {
107
- return this.hasSupport ? 'support.suse.title' : 'support.community.title';
108
- },
109
-
110
- sccLink() {
111
- return this.hasAWSSupport ? addParam('https://scc.suse.com', 'from_marketplace', '1') : 'https://scc.suse.com';
112
- }
113
- },
114
-
115
- };
116
- </script>
117
- <template>
118
- <div>
119
- <BannerGraphic
120
- :title="t(title, {}, true)"
121
- :alt="t('support.bannerImage')"
122
- />
123
-
124
- <IndentedPanel>
125
- <div class="content mt-20">
126
- <div class="promo col main-panel">
127
- <div class="box mb-20 box-primary">
128
- <h2>
129
- <TabTitle breadcrumb="vendor-only">
130
- {{ t('support.suse.access.title') }}
131
- </TabTitle>
132
- </h2>
133
- <div
134
- v-if="!hasSupport"
135
- class="external support-links mt-20"
136
- >
137
- <div class="support-link">
138
- <a
139
- class="support-link"
140
- href="https://www.rancher.com/support"
141
- target="_blank"
142
- rel="noopener noreferrer nofollow"
143
- >{{ t('support.community.learnMore') }}</a>
144
- </div>
145
- <div class="support-link">
146
- <a
147
- class="support-link"
148
- href="https://rancher.com/pricing"
149
- target="_blank"
150
- rel="noopener noreferrer nofollow"
151
- >{{ t('support.community.pricing') }}</a>
152
- </div>
153
- </div>
154
- <div v-else>
155
- <p class="pb-10">
156
- {{ hasAWSSupport ? t("support.suse.access.aws.text") : t("support.suse.access.text") }}
157
- </p>
158
- <a
159
- v-if="hasAWSSupport"
160
- class="mr-5 btn role-secondary btn-sm"
161
- :href="supportConfigLink"
162
- >
163
- {{ t('support.suse.access.aws.generateConfig') }}
164
- </a>
165
- <a
166
- :href="sccLink"
167
- target="_blank"
168
- rel="noopener noreferrer nofollow"
169
- >
170
- {{ t('support.suse.access.action') }} <i class="icon icon-external-link" />
171
- </a>
172
- </div>
173
- </div>
174
- <div class="boxes">
175
- <div
176
- v-for="(key, i) in promos"
177
- :key="i"
178
- class="box"
179
- >
180
- <h2>{{ t(`${key}.title`) }}</h2>
181
- <div>{{ t(`${key}.text`) }}</div>
182
- </div>
183
- </div>
184
- </div>
185
- <CommunityLinks
186
- :is-support-page="true"
187
- class="community col side-panel span-3"
188
- />
189
- </div>
190
- </IndentedPanel>
191
- </div>
192
- </template>
193
- <style lang="scss" scoped>
194
- .content {
195
-
196
- display: flex;
197
- align-items: stretch;
198
- .col {
199
- margin: 0
200
- }
201
- .main-panel {
202
- flex: auto;
203
- }
204
-
205
- .side-panel {
206
- margin-left: 1.75%;
207
- }
208
- }
209
-
210
- .toggle-support {
211
- height: 100%;
212
-
213
- &.card-container {
214
- box-shadow: none;
215
- }
216
-
217
- &:deep() .card-actions {
218
- display: flex;
219
- justify-content: space-between;
220
- }
221
- }
222
-
223
- .support-link:not(:first-child) {
224
- margin: 15px 0 0 0;
225
- }
226
-
227
- .register {
228
- display: flex;
229
- align-items: center;
230
- margin-top: 20px;
231
- font-size: 16px;
232
- }
233
- .remove-link {
234
- cursor: pointer;
235
- font-size: 14px;
236
- }
237
- .boxes {
238
- display: grid;
239
- grid-column-gap: 20px;
240
- grid-row-gap: 20px;
241
- grid-template-columns: 50% 50%;
242
- margin-right: 20px;
243
- }
244
-
245
- .box {
246
- padding: 20px;
247
- border: 1px solid var(--border);
248
-
249
- &.box-primary {
250
- border-color: var(--primary);
251
- }
252
-
253
- > h2 {
254
- font-size: 20px;
255
- font-weight: 300;
256
- }
257
-
258
- > div {
259
- font-weight: 300;
260
- line-height: 18px;
261
- opacity: 0.8;
262
- }
263
- }
264
- </style>