@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,184 @@
1
+ <script>
2
+ import { MANAGEMENT } from '@shell/config/types';
3
+ import ResourceTable from '@shell/components/ResourceTable';
4
+ import {
5
+ INTERNAL_EXTERNAL_IP,
6
+ STATE, NAME as NAME_COL, AGE, MANAGEMENT_NODE_OS
7
+ } from '@shell/config/table-headers';
8
+
9
+ // exclude roles column - it is not necessarily used (or used the same way) outside of Rancher provisioning
10
+ export const DEFAULT_HEADERS = [STATE, {
11
+ ...NAME_COL,
12
+ value: 'status.nodeName',
13
+ formatterOpts: { reference: 'kubeNodeDetailLocation' }
14
+ }, INTERNAL_EXTERNAL_IP, MANAGEMENT_NODE_OS, AGE];
15
+
16
+ export default {
17
+ name: 'ClusterScopedManagementNodeList',
18
+
19
+ components: { ResourceTable },
20
+
21
+ props: {
22
+ resource: {
23
+ type: Object,
24
+ default: () => {
25
+ return {};
26
+ }
27
+ },
28
+
29
+ // override default management node schema headers
30
+ headers: {
31
+ type: Array,
32
+ default: () => DEFAULT_HEADERS
33
+ },
34
+
35
+ // function to get node group for a given node, used for grouping nodes by pool in the table
36
+ // result should be an object with name and (optionally) description properties
37
+ getNodeGroup: {
38
+ type: Function,
39
+ default: null
40
+ }
41
+
42
+ },
43
+
44
+ /**
45
+ * avoid fetching ALL nodes to find this cluster's nodes
46
+ * mgmt nodes do not have labels that can be used with a labelSelector action
47
+ * neither the prov cluster nor mgmt cluster list mgmt nodes in their metadata.relationships nor are the node names listed in either cluster's status
48
+ * BUT mgmt nodes are scoped to a cluster's namespace, so we can fetch only mgmt nodes in the cluster's namespace
49
+ */
50
+ async fetch() {
51
+ const canList = this.$store.getters['management/canList'](MANAGEMENT.NODE);
52
+
53
+ if ( canList ) {
54
+ const hasAllMgmtNodes = this.$store.getters['management/haveAll'](MANAGEMENT.NODE);
55
+
56
+ if (hasAllMgmtNodes) {
57
+ this.mgmtNodes = this.$store.getters['management/all'](MANAGEMENT.NODE);
58
+ } else {
59
+ const res = await this.$store.dispatch('management/findLabelSelector', {
60
+ type: MANAGEMENT.NODE,
61
+ matching: {
62
+ namespace: this.resource.mgmtClusterId,
63
+ labelSelector: {
64
+ matchExpressions: [
65
+ {
66
+ key: 'management.cattle.io/nodename',
67
+ operator: 'Exists',
68
+ }
69
+ ]
70
+ }
71
+ }
72
+ } );
73
+
74
+ this.mgmtNodes = res;
75
+ }
76
+ }
77
+ },
78
+
79
+ data() {
80
+ return {
81
+ mgmtNodes: [],
82
+
83
+ mgmtNodeSchema: this.$store.getters[`management/schemaFor`](MANAGEMENT.NODE),
84
+
85
+ noneGroupOption: {
86
+ tooltipKey: 'resourceTable.groupBy.none',
87
+ icon: 'icon-list-flat',
88
+ value: 'none',
89
+ },
90
+
91
+ poolGroupOption: {
92
+ tooltipKey: 'resourceTable.groupBy.pool',
93
+ icon: 'icon-cluster',
94
+ value: 'poolRef',
95
+ field: 'poolRef.name'
96
+ }
97
+
98
+ };
99
+ },
100
+
101
+ computed: {
102
+ nodes() {
103
+ return this.mgmtNodes.filter((x) => x.mgmtClusterId === this.resource.mgmtClusterId).map((node) => {
104
+ const poolRef = typeof this.getNodeGroup === 'function' ? this.getNodeGroup(node) : null;
105
+
106
+ node.poolRef = poolRef;
107
+
108
+ return node;
109
+ });
110
+ },
111
+ },
112
+ };
113
+ </script>
114
+
115
+ <template>
116
+ <ResourceTable
117
+ v-bind="$attrs"
118
+ :schema="mgmtNodeSchema"
119
+ :headers="headers"
120
+ :rows="nodes"
121
+ :ignore-filter="true"
122
+ group-ref="poolRef"
123
+ data-testid="mgmt-node-table"
124
+ :group-options="[noneGroupOption, poolGroupOption]"
125
+ >
126
+ <template #main-row:isFake="{fullColspan}">
127
+ <tr class="main-row">
128
+ <td
129
+ :colspan="fullColspan"
130
+ class="no-entries"
131
+ >
132
+ {{ t('node.list.noNodes') }}
133
+ </td>
134
+ </tr>
135
+ </template>
136
+
137
+ <template #group-by="{group}">
138
+ <div
139
+ class="pool-row"
140
+ :class="{'has-description':group.ref}"
141
+ >
142
+ <div
143
+ v-trim-whitespace
144
+ class="group-tab"
145
+ >
146
+ {{ group.ref?.name || t('resourceTable.groupLabel.notInANodePool') }}
147
+ <div
148
+ v-if="group.ref && group.ref.description"
149
+ v-clean-html="group.ref.description"
150
+ class="description text-muted text-small"
151
+ />
152
+ </div>
153
+ </div>
154
+ </template>
155
+ </ResourceTable>
156
+ </template>
157
+
158
+ <style scoped lang="scss">
159
+ .pool-row {
160
+ display: flex;
161
+ align-items: center;
162
+ justify-content: space-between;
163
+
164
+ &.has-description {
165
+ .group-tab {
166
+ &, &::after {
167
+ height: 50px;
168
+ }
169
+
170
+ &::after {
171
+ right: -20px;
172
+ }
173
+
174
+ .description {
175
+ margin-top: -20px;
176
+ }
177
+ }
178
+ }
179
+ .group-header-buttons {
180
+ align-items: center;
181
+ display: flex;
182
+ }
183
+ }
184
+ </style>
@@ -7,16 +7,15 @@ import Date from '@shell/components/formatter/Date.vue';
7
7
  import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
8
8
  import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
9
9
  import { exceptionToErrorsArray } from '@shell/utils/error';
10
- import { CAPI, SNAPSHOT } from '@shell/config/types';
10
+ import { CAPI, SNAPSHOT, OPERATION, MANAGEMENT } from '@shell/config/types';
11
11
  import { set } from '@shell/utils/object';
12
12
  import ChildHook, { BEFORE_SAVE_HOOKS } from '@shell/mixins/child-hook';
13
13
  import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
14
14
  import { escapeHtml } from '@shell/utils/string';
15
15
  import day from 'dayjs';
16
16
  import { sortBy } from '@shell/utils/sort';
17
- import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
18
17
  import AppModal from '@shell/components/AppModal.vue';
19
-
18
+ import { createOperationCR } from '@shell/utils/operation-cr';
20
19
  export default {
21
20
  components: {
22
21
  Card,
@@ -66,8 +65,41 @@ export default {
66
65
  return !!this.snapshot;
67
66
  },
68
67
 
69
- isRke2() {
70
- return !!this.snapshot?.rke2;
68
+ targetCluster() {
69
+ if (this.isCluster) {
70
+ return this.toRestore?.[0] || null;
71
+ }
72
+
73
+ if (this.snapshot?.cluster) {
74
+ return this.snapshot.cluster;
75
+ }
76
+
77
+ const snapshotClusterName = this.snapshot?.spec?.clusterName || this.snapshot?.clusterName;
78
+ const snapshotClusterId = this.snapshot?.clusterId || (snapshotClusterName ? `${ this.snapshot?.metadata?.namespace }/${ snapshotClusterName }` : null);
79
+
80
+ if (!snapshotClusterId) {
81
+ return null;
82
+ }
83
+
84
+ return this.$store.getters['management/byId'](CAPI.RANCHER_CLUSTER, snapshotClusterId);
85
+ },
86
+
87
+ targetMgmtCluster() {
88
+ if (this.targetCluster?.mgmt) {
89
+ return this.targetCluster.mgmt;
90
+ }
91
+
92
+ const mgmtClusterId = this.snapshot?.spec?.clusterName;
93
+
94
+ if (!mgmtClusterId) {
95
+ return null;
96
+ }
97
+
98
+ return this.$store.getters['management/byId'](MANAGEMENT.CLUSTER, mgmtClusterId);
99
+ },
100
+
101
+ isImported() {
102
+ return !!(this.targetCluster?.isImported || this.targetMgmtCluster?.isImported);
71
103
  },
72
104
 
73
105
  clusterSnapshots() {
@@ -77,6 +109,13 @@ export default {
77
109
  return [];
78
110
  }
79
111
  },
112
+ restoreModeLabels() {
113
+ return [
114
+ this.t('promptRestore.restoreMode.onlyEtcd'),
115
+ this.t('promptRestore.restoreMode.kubernetesVersionAndEtcd'),
116
+ this.t('promptRestore.restoreMode.clusterConfigKubernetesVersionAndEtcd')
117
+ ];
118
+ },
80
119
  restoreModeOptions() {
81
120
  return ['none', 'kubernetesVersion', 'all'];
82
121
  }
@@ -112,7 +151,7 @@ export default {
112
151
  const promise = this.$store.dispatch('management/findAll', { type: SNAPSHOT }).then((snapshots) => {
113
152
  const toRestoreClusterName = cluster?.clusterName || cluster?.metadata?.name;
114
153
 
115
- return snapshots.filter((s) => s?.snapshotFile?.status === STATES_ENUM.SUCCESSFUL && s.clusterName === toRestoreClusterName
154
+ return snapshots.filter((s) => s?.restoreEnabled && s.clusterName === toRestoreClusterName
116
155
  );
117
156
  });
118
157
 
@@ -143,19 +182,41 @@ export default {
143
182
 
144
183
  async apply(buttonDone) {
145
184
  try {
146
- const cluster = this.$store.getters['management/byId'](CAPI.RANCHER_CLUSTER, this.snapshot.clusterId);
185
+ const cluster = this.targetCluster;
186
+ const mgmtCluster = cluster?.mgmt || this.targetMgmtCluster;
147
187
 
148
- await this.applyHooks(BEFORE_SAVE_HOOKS);
149
-
150
- const now = cluster.spec?.rkeConfig?.etcdSnapshotRestore?.generation || 0;
188
+ const isImportedWithDayTwoOps = cluster?.isImportedWithDayTwoOps || mgmtCluster?.isDayTwoOpsEnabled;
151
189
 
152
- set(cluster, 'spec.rkeConfig.etcdSnapshotRestore', {
153
- generation: now + 1,
154
- name: this.snapshot.name,
155
- restoreRKEConfig: this.restoreMode,
156
- });
190
+ await this.applyHooks(BEFORE_SAVE_HOOKS);
157
191
 
158
- await cluster.save();
192
+ // For imported clusters with day 2 ops enabled, create an operation CR
193
+ if (isImportedWithDayTwoOps) {
194
+ if (!mgmtCluster) {
195
+ throw new Error(this.t('promptRestore.error.unableToResolveTargetCluster'));
196
+ }
197
+ const namespace = mgmtCluster?.id;
198
+ const safePrefix = mgmtCluster?.id;
199
+ const spec = {
200
+ clusterRef: {
201
+ apiVersion: 'management.cattle.io/v3',
202
+ kind: 'Cluster',
203
+ name: mgmtCluster?.id,
204
+ },
205
+ args: { name: this.snapshot?.snapshotFile?.name },
206
+ };
207
+
208
+ createOperationCR(this.$store.dispatch, OPERATION.ETCD_SNAPSHOT_RESTORE, spec, namespace, safePrefix);
209
+ } else {
210
+ const now = cluster.spec?.rkeConfig?.etcdSnapshotRestore?.generation || 0;
211
+
212
+ set(cluster, 'spec.rkeConfig.etcdSnapshotRestore', {
213
+ generation: now + 1,
214
+ name: this.snapshot.name,
215
+ restoreRKEConfig: this.restoreMode,
216
+ });
217
+
218
+ await cluster.save();
219
+ }
159
220
 
160
221
  this.$store.dispatch('growl/success', {
161
222
  title: this.t('promptRestore.notification.title'),
@@ -233,18 +294,25 @@ export default {
233
294
  />
234
295
  </p>
235
296
  </div>
236
- <div class="spacer" />
237
- <RadioGroup
238
- v-model:value="restoreMode"
239
- name="restoreMode"
240
- label="Restore Type"
241
- :labels="['Only etcd', 'Kubernetes version and etcd', 'Cluster config, Kubernetes version and etcd']"
242
- :options="restoreModeOptions"
243
- />
297
+ <div v-if="!isImported">
298
+ <div class="spacer" />
299
+ <RadioGroup
300
+ v-model:value="restoreMode"
301
+ name="restoreMode"
302
+ :label="t('promptRestore.restoreMode.label')"
303
+ :labels="restoreModeLabels"
304
+ :options="restoreModeOptions"
305
+ />
306
+ </div>
244
307
  </form>
245
308
  </div>
246
309
  </template>
247
-
310
+ <Banner
311
+ v-for="(err, i) in errors"
312
+ :key="i"
313
+ color="error"
314
+ :label="err"
315
+ />
248
316
  <template #actions>
249
317
  <div class="dialog-actions">
250
318
  <button
@@ -259,13 +327,6 @@ export default {
259
327
  :disabled="!hasSnapshot"
260
328
  @click="apply"
261
329
  />
262
-
263
- <Banner
264
- v-for="(err, i) in errors"
265
- :key="i"
266
- color="error"
267
- :label="err"
268
- />
269
330
  </div>
270
331
  </template>
271
332
  </Card>
@@ -474,6 +474,7 @@ export default {
474
474
  :value="get(value, q.variable)"
475
475
  :disabled="disabled"
476
476
  :chart-name="chartName"
477
+ :mode="mode"
477
478
  @update:value="update(q.variable, $event)"
478
479
  />
479
480
  </div>
@@ -1,4 +1,4 @@
1
- import { useResourceCardRow } from '@shell/components/Resource/Detail/Card/StateCard/composables';
1
+ import { useResourceCardRow, useResourceCardRowFromRelationships } from '@shell/components/Resource/Detail/Card/StateCard/composables';
2
2
 
3
3
  describe('useResourceCardRow', () => {
4
4
  describe('with default keys', () => {
@@ -140,3 +140,92 @@ describe('useResourceCardRow', () => {
140
140
  });
141
141
  });
142
142
  });
143
+
144
+ describe('useResourceCardRowFromRelationships', () => {
145
+ it('should return empty props for empty relationships', () => {
146
+ const result = useResourceCardRowFromRelationships('Refers to', []);
147
+
148
+ expect(result.label).toBe('Refers to');
149
+ expect(result.color).toBeUndefined();
150
+ expect(result.counts).toBeUndefined();
151
+ });
152
+
153
+ it('should aggregate relationship states', () => {
154
+ const rels = [
155
+ { toType: 'configmap', state: 'active' },
156
+ { toType: 'secret', state: 'active' },
157
+ { toType: 'serviceaccount', state: 'error' }
158
+ ];
159
+
160
+ const result = useResourceCardRowFromRelationships('Refers to', rels);
161
+
162
+ expect(result.counts).toHaveLength(2);
163
+ expect(result.counts).toContainEqual(expect.objectContaining({ label: 'active', count: 2 }));
164
+ expect(result.counts).toContainEqual(expect.objectContaining({ label: 'error', count: 1 }));
165
+ });
166
+
167
+ it('should default missing state to "missing"', () => {
168
+ const rels = [
169
+ { toType: 'configmap' },
170
+ { toType: 'secret', state: 'active' }
171
+ ];
172
+
173
+ const result = useResourceCardRowFromRelationships('Refers to', rels);
174
+
175
+ expect(result.counts).toContainEqual(expect.objectContaining({
176
+ label: 'missing', count: 1, color: 'warning'
177
+ }));
178
+ expect(result.counts).toContainEqual(expect.objectContaining({
179
+ label: 'active', count: 1, color: 'success'
180
+ }));
181
+ });
182
+
183
+ it('should set the highest alert color as main color', () => {
184
+ const rels = [
185
+ { toType: 'configmap', state: 'active' },
186
+ { toType: 'secret', state: 'error' }
187
+ ];
188
+
189
+ const result = useResourceCardRowFromRelationships('Refers to', rels);
190
+
191
+ expect(result.color).toBe('error');
192
+ });
193
+
194
+ it('should sort by alert level then by count', () => {
195
+ const rels = [
196
+ { toType: 'a', state: 'active' },
197
+ { toType: 'b', state: 'active' },
198
+ { toType: 'c', state: 'active' },
199
+ { toType: 'd', state: 'error' },
200
+ { toType: 'e', state: 'warning' },
201
+ { toType: 'f', state: 'warning' }
202
+ ];
203
+
204
+ const result = useResourceCardRowFromRelationships('Refers to', rels);
205
+
206
+ expect(result.counts![0].color).toBe('error');
207
+ expect(result.counts![1].color).toBe('warning');
208
+ expect(result.counts![2].color).toBe('success');
209
+ });
210
+
211
+ it('should pass the to parameter through', () => {
212
+ const to = { hash: '#related' };
213
+ const result = useResourceCardRowFromRelationships('Refers to', [], to);
214
+
215
+ expect(result.to).toStrictEqual(to);
216
+ });
217
+
218
+ it('should handle all relationships having no state', () => {
219
+ const rels = [
220
+ { toType: 'configmap' },
221
+ { toType: 'secret' }
222
+ ];
223
+
224
+ const result = useResourceCardRowFromRelationships('Refers to', rels);
225
+
226
+ expect(result.counts).toHaveLength(1);
227
+ expect(result.counts![0]).toStrictEqual(expect.objectContaining({
228
+ label: 'missing', count: 2, color: 'warning'
229
+ }));
230
+ });
231
+ });
@@ -1,11 +1,26 @@
1
1
  import { Count, Props as ResourceRowProps } from '@shell/components/Resource/Detail/ResourceRow.types';
2
- import { useI18n } from '@shell/composables/useI18n';
3
- import { INGRESS, SERVICE } from '@shell/config/types';
4
2
  import { isHigherAlert, StateColor } from '@shell/utils/style';
5
- import { computed, Ref, toValue } from 'vue';
6
- import { useStore } from 'vuex';
7
- import { Props as StateCardProps } from '@shell/components/Resource/Detail/Card/StateCard/types';
8
3
  import { RouteLocationRaw } from 'vue-router';
4
+ import { simpleColorForState, stateDisplay as stateDisplayFn } from '@shell/plugins/dashboard-store/resource-class';
5
+
6
+ interface Tuple extends Count {
7
+ color: StateColor;
8
+ }
9
+
10
+ export interface SummaryCountValue {
11
+ total: number;
12
+ namespace?: Record<string, number>;
13
+ }
14
+
15
+ export interface SummaryEntry {
16
+ property: string;
17
+ counts: Record<string, SummaryCountValue>;
18
+ }
19
+
20
+ export interface SummaryResult {
21
+ count: number;
22
+ summary: SummaryEntry[] | null;
23
+ }
9
24
 
10
25
  export function useResourceCardRow(label: string, resources: any[], stateColorKey = 'stateSimpleColor', stateDisplayKey = 'stateDisplay', to?: RouteLocationRaw): ResourceRowProps {
11
26
  const agg: any = {};
@@ -20,12 +35,9 @@ export function useResourceCardRow(label: string, resources: any[], stateColorKe
20
35
  agg[state].count++;
21
36
  });
22
37
 
23
- interface Tuple extends Count {
24
- color: StateColor;
25
- }
26
38
  const tuples: Tuple[] = Object.values(agg);
27
39
 
28
- tuples.sort((left: any, right: any) => {
40
+ tuples.sort((left: Tuple, right: Tuple) => {
29
41
  if (isHigherAlert(left.color, right.color)) {
30
42
  return -1;
31
43
  }
@@ -49,94 +61,52 @@ export function useResourceCardRow(label: string, resources: any[], stateColorKe
49
61
  };
50
62
  }
51
63
 
52
- export interface Pairs {
53
- label: string;
54
- to?: RouteLocationRaw;
55
- resources: any[];
56
- }
57
-
58
- export function useDefaultResources(pairs: Ref<Pairs[]>) {
59
- const pairsValue = toValue(pairs);
60
- const rows = computed(() => pairsValue.map(({ label, resources, to }) => useResourceCardRow(label, resources, undefined, undefined, to)));
61
-
62
- return rows;
63
- }
64
-
65
- export function useResourcePair(label: string, type: string, resources?: any[]) {
66
- const store = useStore();
64
+ /**
65
+ * Builds a ResourceRowProps from relationship state data.
66
+ * Each relationship entry includes a `state` field (e.g. "active", "error").
67
+ */
68
+ export function useResourceCardRowFromRelationships(label: string, relationships: any[], to?: RouteLocationRaw): ResourceRowProps {
69
+ if (!relationships.length) {
70
+ return {
71
+ label, color: undefined, counts: undefined, to
72
+ };
73
+ }
67
74
 
68
- return computed(() => {
69
- if (!resources) {
70
- return undefined;
71
- }
75
+ const agg: Record<string, { color: StateColor; label: string; count: number }> = {};
72
76
 
73
- const resourcesValue = toValue(resources);
77
+ relationships.forEach((r: any) => {
78
+ const state = (r.state || 'missing').toLowerCase();
79
+ const color = simpleColorForState(state) as StateColor;
80
+ const display = (stateDisplayFn(state) as string)?.toLowerCase() || state;
74
81
 
75
- return {
76
- label,
77
- resources: resourcesValue,
78
- to: (resourcesValue?.length > 0) ? {
79
- name: 'c-cluster-product-resource',
80
- params: {
81
- cluster: store.getters.currentCluster.id,
82
- product: store.getters.currentProduct.name,
83
- resource: type
84
- }
85
- } : undefined,
82
+ agg[state] = agg[state] || {
83
+ color, label: display, count: 0
86
84
  };
85
+ agg[state].count++;
87
86
  });
88
- }
89
87
 
90
- export function useDefaultWorkloadResources(services?: any[], ingresses?: any[], referredToBy?: any[], refersTo?: any[]): StateCardProps {
91
- const store = useStore();
92
- const i18n = useI18n(store);
93
-
94
- const pairs: Ref<(Pairs | undefined)[]> = computed(() => [
95
- useResourcePair(i18n.t('component.resource.detail.card.resourcesCard.rows.services'), SERVICE, services).value,
96
- useResourcePair(i18n.t('component.resource.detail.card.resourcesCard.rows.ingresses'), INGRESS, ingresses).value,
97
- referredToBy ? {
98
- label: i18n.t('component.resource.detail.card.resourcesCard.rows.referredToBy'),
99
- resources: toValue(referredToBy || []),
100
- to: { hash: '#related' }
101
- } : undefined,
102
- refersTo ? {
103
- label: i18n.t('component.resource.detail.card.resourcesCard.rows.refersTo'),
104
- resources: toValue(refersTo || []),
105
- to: { hash: '#related' }
106
- } : undefined
107
- ]);
108
-
109
- const remainingPairs = computed(() => pairs.value.filter((p: (Pairs | undefined)): p is Pairs => p !== undefined));
110
-
111
- const rows = useDefaultResources(remainingPairs);
88
+ const tuples: Tuple[] = Object.values(agg);
112
89
 
113
- return {
114
- title: 'Resources',
115
- rows: rows.value
116
- };
117
- }
90
+ tuples.sort((left: Tuple, right: Tuple) => {
91
+ if (isHigherAlert(left.color, right.color)) {
92
+ return -1;
93
+ }
118
94
 
119
- export function useDefaultWorkloadInsightsCardProps(): StateCardProps {
120
- const store = useStore();
121
- const i18n = useI18n(store);
122
-
123
- const rows: ResourceRowProps[] = [
124
- {
125
- label: i18n.t('component.resource.detail.card.insightsCard.rows.conditions'),
126
- to: '#conditions',
127
- color: 'disabled',
128
- counts: [{ label: 'Available', count: 1 }, { label: 'Progressing', count: 1 }]
129
- },
130
- {
131
- label: i18n.t('component.resource.detail.card.insightsCard.rows.events'),
132
- to: '#events',
133
- color: 'disabled',
134
- counts: [{ label: 'Normal', count: 2 }]
95
+ if (left.color !== right.color) {
96
+ return 1;
135
97
  }
136
- ];
98
+
99
+ if (left.count === right.count) {
100
+ return 0;
101
+ }
102
+
103
+ return left.count > right.count ? -1 : 1;
104
+ });
137
105
 
138
106
  return {
139
- title: i18n.t('component.resource.detail.card.insightsCard.title'),
140
- rows
107
+ label,
108
+ color: tuples.length ? tuples[0].color : undefined,
109
+ counts: tuples.length ? tuples : undefined,
110
+ to
141
111
  };
142
112
  }