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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) 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/translations/en-us.yaml +22 -5
  56. package/chart/monitoring/index.vue +10 -1
  57. package/components/ActionDropdownShell.vue +2 -1
  58. package/components/CruResourceFooter.vue +9 -5
  59. package/components/ExplorerProjectsNamespaces.vue +1 -1
  60. package/components/InstallHelmCharts.vue +2 -2
  61. package/components/LandingPagePreference.vue +14 -5
  62. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
  63. package/components/Resource/Detail/Metadata/index.vue +6 -0
  64. package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
  65. package/components/Resource/Detail/SpacedRow.vue +3 -1
  66. package/components/Resource/Detail/TitleBar/index.vue +10 -11
  67. package/components/ResourceList/Masthead.vue +12 -8
  68. package/components/SelectIconGrid.vue +0 -10
  69. package/components/SingleClusterInfo.vue +1 -0
  70. package/components/SortableTable/__tests__/sorting.test.ts +126 -0
  71. package/components/SortableTable/index.vue +6 -9
  72. package/components/SortableTable/selection.js +23 -5
  73. package/components/SortableTable/sorting.js +6 -3
  74. package/components/Wizard.vue +14 -13
  75. package/components/fleet/FleetBundles.vue +100 -12
  76. package/components/fleet/FleetClusterTargets/index.vue +37 -15
  77. package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
  78. package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
  79. package/components/form/LabeledSelect.vue +20 -3
  80. package/components/form/NameNsDescription.vue +11 -0
  81. package/components/form/Security.vue +6 -2
  82. package/components/form/WorkloadPorts.vue +2 -7
  83. package/components/form/__tests__/Security.test.ts +76 -0
  84. package/components/formatter/Autoscaler.vue +4 -4
  85. package/components/formatter/ClusterKubeVersion.vue +27 -0
  86. package/components/formatter/ClusterLink.vue +1 -7
  87. package/components/formatter/ClusterProvider.vue +6 -10
  88. package/components/formatter/FleetSummaryGraph.vue +0 -3
  89. package/components/formatter/MachineSummaryGraph.vue +1 -1
  90. package/components/formatter/PodsUsage.vue +2 -2
  91. package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
  92. package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
  93. package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
  94. package/components/nav/NamespaceFilter.vue +2 -2
  95. package/components/nav/TopLevelMenu.helper.ts +15 -3
  96. package/components/nav/TopLevelMenu.vue +16 -5
  97. package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
  98. package/components/templates/home.vue +18 -0
  99. package/components/templates/plain.vue +18 -0
  100. package/components/templates/standalone.vue +17 -0
  101. package/composables/useFormValidation.ts +93 -0
  102. package/composables/useVeeValidateField.test.ts +159 -0
  103. package/composables/useVeeValidateField.ts +67 -0
  104. package/config/pagination-table-headers.js +18 -1
  105. package/config/product/manager.js +82 -21
  106. package/config/router/routes.js +6 -0
  107. package/config/table-headers.js +20 -1
  108. package/config/types.js +2 -1
  109. package/core/__tests__/plugin-products.test.ts +904 -20
  110. package/core/plugin-products-base.ts +107 -7
  111. package/core/plugin-products.ts +4 -0
  112. package/core/plugin-types.ts +111 -1
  113. package/core/plugin.ts +15 -7
  114. package/core/productDebugger.js +9 -4
  115. package/core/types-provisioning.ts +43 -30
  116. package/core/types.ts +57 -20
  117. package/detail/__tests__/pod.test.ts +41 -0
  118. package/detail/harvesterhci.io.management.cluster.vue +6 -2
  119. package/detail/pod.vue +1 -1
  120. package/detail/provisioning.cattle.io.cluster.vue +4 -10
  121. package/edit/auth/__tests__/azuread.test.ts +217 -34
  122. package/edit/auth/azuread.vue +122 -14
  123. package/edit/auth/oidc.vue +2 -2
  124. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
  125. package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
  126. package/edit/networking.k8s.io.ingress/index.vue +75 -20
  127. package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
  128. package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
  129. package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
  130. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  131. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
  132. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
  133. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
  134. package/edit/secret/__tests__/ssh.test.ts +5 -6
  135. package/edit/secret/basic.vue +31 -0
  136. package/edit/secret/index.vue +68 -17
  137. package/edit/secret/registry.vue +38 -0
  138. package/edit/secret/ssh.vue +29 -0
  139. package/edit/secret/tls.vue +30 -0
  140. package/edit/service.vue +4 -4
  141. package/edit/workload/Upgrading.vue +3 -3
  142. package/edit/workload/__tests__/Upgrading.test.ts +6 -9
  143. package/edit/workload/mixins/workload.js +2 -1
  144. package/list/fleet.cattle.io.bundle.vue +7 -104
  145. package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
  146. package/list/provisioning.cattle.io.cluster.vue +262 -180
  147. package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
  148. package/mixins/__tests__/chart.test.ts +112 -0
  149. package/mixins/brand.js +2 -1
  150. package/mixins/chart.js +12 -8
  151. package/mixins/resource-fetch-api-pagination.js +41 -5
  152. package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
  153. package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
  154. package/models/__tests__/management.cattle.io.node.ts +6 -5
  155. package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
  156. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
  157. package/models/base-cluster.x-k8s.io.js +26 -0
  158. package/models/cluster.js +1 -1
  159. package/models/cluster.x-k8s.io.machine.js +4 -22
  160. package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
  161. package/models/cluster.x-k8s.io.machineset.js +2 -20
  162. package/models/compliance.cattle.io.clusterscan.js +130 -2
  163. package/models/ext.cattle.io.kubeconfig.ts +4 -7
  164. package/models/fleet-application.js +3 -1
  165. package/models/management.cattle.io.cluster.js +417 -40
  166. package/models/management.cattle.io.node.js +6 -4
  167. package/models/management.cattle.io.nodepool.js +1 -1
  168. package/models/networking.k8s.io.ingress.js +12 -4
  169. package/models/provisioning.cattle.io.cluster.js +47 -330
  170. package/models/rke.cattle.io.etcdsnapshot.js +1 -2
  171. package/package.json +11 -29
  172. package/pages/__tests__/readme.test.ts +49 -0
  173. package/pages/auth/setup.vue +2 -3
  174. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
  175. package/pages/c/_cluster/apps/charts/chart.vue +60 -8
  176. package/pages/c/_cluster/apps/charts/install.vue +10 -7
  177. package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
  178. package/pages/c/_cluster/explorer/index.vue +5 -49
  179. package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
  180. package/pages/c/_cluster/istio/index.vue +21 -6
  181. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
  182. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
  183. package/pages/c/_cluster/uiplugins/index.vue +203 -197
  184. package/pages/diagnostic.vue +13 -17
  185. package/pages/fail-whale.vue +18 -0
  186. package/pages/home.vue +77 -260
  187. package/pages/readme.vue +88 -0
  188. package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
  189. package/plugins/dashboard-store/actions.js +40 -18
  190. package/plugins/dashboard-store/resource-class.js +5 -2
  191. package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
  192. package/plugins/steve/steve-pagination-utils.ts +11 -3
  193. package/plugins/steve/subscribe.js +35 -5
  194. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
  195. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
  196. package/rancher-components/RcButton/RcButton.test.ts +37 -1
  197. package/rancher-components/RcButton/RcButton.vue +38 -8
  198. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
  199. package/store/__tests__/catalog.test.ts +115 -1
  200. package/store/__tests__/type-map.test.ts +556 -1
  201. package/store/action-menu.js +8 -3
  202. package/store/auth.js +1 -1
  203. package/store/aws.js +27 -16
  204. package/store/catalog.js +27 -3
  205. package/store/digitalocean.js +20 -38
  206. package/store/index.js +2 -0
  207. package/store/linode.js +25 -40
  208. package/store/pnap.js +1 -0
  209. package/store/type-map.js +111 -29
  210. package/tsconfig.paths.json +8 -8
  211. package/types/kube/kube-api.ts +14 -1
  212. package/types/rancher/steve.api.ts +12 -12
  213. package/types/resources/settings.d.ts +2 -1
  214. package/types/shell/index.d.ts +102 -2
  215. package/types/store/dashboard-store.types.ts +108 -11
  216. package/types/store/pagination.types.ts +6 -3
  217. package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
  218. package/utils/__tests__/async.test.ts +87 -0
  219. package/utils/__tests__/aws.test.ts +140 -0
  220. package/utils/__tests__/banners.test.ts +176 -0
  221. package/utils/__tests__/chart.test.ts +64 -1
  222. package/utils/__tests__/color.test.ts +226 -0
  223. package/utils/__tests__/duration.test.ts +140 -0
  224. package/utils/__tests__/fleet.test.ts +340 -0
  225. package/utils/__tests__/ingress.test.ts +553 -0
  226. package/utils/__tests__/kube.test.ts +68 -0
  227. package/utils/__tests__/namespace-filter.test.ts +109 -0
  228. package/utils/__tests__/pagination-utils.test.ts +361 -0
  229. package/utils/__tests__/parse-externalid.test.ts +137 -0
  230. package/utils/__tests__/perf-setting.utils.test.ts +98 -0
  231. package/utils/__tests__/poller-sequential.test.ts +177 -0
  232. package/utils/__tests__/poller.test.ts +170 -0
  233. package/utils/__tests__/promise.test.ts +346 -0
  234. package/utils/__tests__/settings.test.ts +140 -0
  235. package/utils/__tests__/sort-utils.test.ts +301 -0
  236. package/utils/__tests__/string-utils.test.ts +798 -0
  237. package/utils/__tests__/string.test.ts +23 -1
  238. package/utils/__tests__/style.test.ts +154 -0
  239. package/utils/__tests__/svg-filter.test.ts +184 -0
  240. package/utils/__tests__/units.test.ts +417 -0
  241. package/utils/__tests__/versions.test.ts +128 -0
  242. package/utils/__tests__/xccdf.test.ts +391 -0
  243. package/utils/chart.js +36 -0
  244. package/utils/fleet.ts +13 -3
  245. package/utils/gatekeeper/__tests__/util.test.ts +174 -0
  246. package/utils/gc/__tests__/gc-interval.test.ts +119 -0
  247. package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
  248. package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
  249. package/utils/gc/__tests__/gc.test.ts +487 -0
  250. package/utils/ingress.ts +9 -1
  251. package/utils/pagination-utils.ts +2 -1
  252. package/utils/string.js +25 -2
  253. package/utils/uiplugins.ts +5 -5
  254. package/utils/validators/__tests__/cluster-name.test.ts +110 -0
  255. package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
  256. package/utils/validators/__tests__/index.test.ts +481 -0
  257. package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
  258. package/utils/validators/__tests__/misc-validators.test.ts +246 -0
  259. package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
  260. package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
  261. package/utils/validators/__tests__/role-template.test.ts +149 -0
  262. package/utils/validators/__tests__/service.test.ts +283 -0
  263. package/utils/validators/__tests__/setting.test.js +32 -0
  264. package/utils/validators/formRules/__tests__/index.test.ts +50 -0
  265. package/utils/validators/formRules/index.ts +5 -5
  266. package/utils/validators/machine-pool.ts +1 -1
  267. package/utils/validators/setting.js +18 -3
  268. package/utils/xccdf.ts +418 -0
  269. package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
  270. package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
  271. package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
  272. package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
@@ -167,11 +167,12 @@ describe('class MgmtNode', () => {
167
167
  id: nodeId
168
168
  }, {
169
169
  ...baseCtx,
170
- getters: {
171
- all: () => [{
172
- mgmtClusterId,
173
- nodes
174
- }]
170
+ rootGetters: {
171
+ ...baseCtx.rootGetters,
172
+ 'management/byId': () => ({
173
+ id: mgmtClusterId,
174
+ provCluster: { nodes }
175
+ })
175
176
  }
176
177
  });
177
178
 
@@ -66,14 +66,15 @@ describe('class MgmtNodePool', () => {
66
66
  const { spec, nodes } = data;
67
67
  const mgmtNode = new MgmtNodePool({
68
68
  spec,
69
- id: nodeId
69
+ metadata: {},
70
+ id: nodeId
70
71
  }, {
71
72
  ...baseCtx,
72
73
  getters: {
73
- all: () => [{
74
- mgmtClusterId,
74
+ byId: () => ({
75
+ id: mgmtClusterId,
75
76
  nodes
76
- }]
77
+ })
77
78
  }
78
79
  });
79
80
 
@@ -1,29 +1,36 @@
1
1
  import ProvCluster from '@shell/models/provisioning.cattle.io.cluster';
2
+ import MgmtCluster from '@shell/models/management.cattle.io.cluster';
3
+
2
4
  jest.mock('@shell/utils/provider', () => ({
3
5
  isHostedProvider: jest.fn().mockImplementation((context, provider) => {
4
6
  return ['GKE', 'EKS', 'AKS'].includes(provider);
5
7
  })
6
8
  }));
9
+
10
+ jest.mock('@shell/utils/clipboard', () => {
11
+ return { copyTextToClipboard: jest.fn(() => Promise.resolve({})) };
12
+ });
13
+
7
14
  describe('class ProvCluster', () => {
8
15
  const gkeClusterWithPrivateEndpoint = {
9
16
  clusterName: 'test',
10
17
  provisioner: 'GKE',
11
18
  spec: { },
12
- mgmt: { spec: { gkeConfig: { privateClusterConfig: { enablePrivateEndpoint: true } } } }
19
+ mgmt: new MgmtCluster({ spec: { gkeConfig: { privateClusterConfig: { enablePrivateEndpoint: true } } } }),
13
20
  };
14
21
 
15
22
  const eksClusterWithPrivateEndpoint = {
16
23
  clusterName: 'test',
17
24
  provisioner: 'EKS',
18
25
  spec: { },
19
- mgmt: { spec: { eksConfig: { privateAccess: true } } }
26
+ mgmt: new MgmtCluster({ spec: { eksConfig: { privateAccess: true } } }),
20
27
  };
21
28
 
22
29
  const aksClusterWithPrivateEndpoint = {
23
30
  clusterName: 'test',
24
31
  provisioner: 'AKS',
25
32
  spec: { },
26
- mgmt: { spec: { aksConfig: { privateCluster: true } } }
33
+ mgmt: new MgmtCluster({ spec: { aksConfig: { privateCluster: true } } }),
27
34
  };
28
35
 
29
36
  // Related to https://github.com/rancher/dashboard/issues/9402
@@ -41,10 +48,14 @@ describe('class ProvCluster', () => {
41
48
  it.each(testCases)('should return the isHostedKubernetesProvider and isPrivateHostedProvider values properly based on the props data', (clusterData: Object, expected: Boolean) => {
42
49
  const cluster = new ProvCluster({ spec: clusterData.spec });
43
50
 
51
+ jest.spyOn(clusterData.mgmt, 'provCluster', 'get').mockReturnValue(
52
+ clusterData
53
+ );
54
+
44
55
  jest.spyOn(cluster, 'mgmt', 'get').mockReturnValue(
45
56
  clusterData.mgmt
46
57
  );
47
- jest.spyOn(cluster, 'provisioner', 'get').mockReturnValue(
58
+ jest.spyOn(clusterData.mgmt, 'provisioner', 'get').mockReturnValue(
48
59
  clusterData.provisioner
49
60
  );
50
61
 
@@ -54,6 +65,7 @@ describe('class ProvCluster', () => {
54
65
  resetMocks();
55
66
  });
56
67
  });
68
+
57
69
  describe('isImported', () => {
58
70
  const testCases = [
59
71
  {
@@ -114,7 +126,8 @@ describe('class ProvCluster', () => {
114
126
  clusterData: {
115
127
  isLocal: false,
116
128
  isHostedKubernetesProvider: true,
117
- providerConfig: { imported: true }
129
+ providerConfig: { imported: true },
130
+ mgmt: { status: { provider: '', driver: 'EKS' } }
118
131
  },
119
132
  expected: true
120
133
  },
@@ -123,7 +136,8 @@ describe('class ProvCluster', () => {
123
136
  clusterData: {
124
137
  isLocal: false,
125
138
  isHostedKubernetesProvider: true,
126
- providerConfig: { imported: false }
139
+ providerConfig: { imported: false },
140
+ mgmt: { status: { provider: '', driver: 'EKS' } }
127
141
  },
128
142
  expected: false
129
143
  },
@@ -150,23 +164,27 @@ describe('class ProvCluster', () => {
150
164
  };
151
165
 
152
166
  it.each(testCases)('$description', ({ clusterData, expected }) => {
167
+ const mgmtCluster = new MgmtCluster(clusterData.mgmt || {});
153
168
  const cluster = new ProvCluster({});
154
169
 
155
170
  // Mock getters
156
171
  jest.spyOn(cluster, 'mgmt', 'get').mockReturnValue(
157
- clusterData.mgmt
172
+ mgmtCluster
173
+ );
174
+ jest.spyOn(mgmtCluster, 'provCluster', 'get').mockReturnValue(
175
+ cluster
158
176
  );
159
177
  if (clusterData.isLocal !== undefined) {
160
- jest.spyOn(cluster, 'isLocal', 'get').mockReturnValue(clusterData.isLocal);
178
+ jest.spyOn(mgmtCluster, 'isLocal', 'get').mockReturnValue(clusterData.isLocal);
161
179
  }
162
180
  if (clusterData.isHostedKubernetesProvider !== undefined) {
163
- jest.spyOn(cluster, 'isHostedKubernetesProvider', 'get').mockReturnValue(clusterData.isHostedKubernetesProvider);
181
+ jest.spyOn(mgmtCluster, 'isHostedKubernetesProvider', 'get').mockReturnValue(clusterData.isHostedKubernetesProvider);
164
182
  }
165
183
  if (clusterData.providerConfig !== undefined) {
166
184
  jest.spyOn(cluster, 'providerConfig', 'get').mockReturnValue(clusterData.providerConfig);
167
185
  }
168
186
  if (clusterData.provisioner !== undefined) {
169
- jest.spyOn(cluster, 'provisioner', 'get').mockReturnValue(clusterData.provisioner);
187
+ jest.spyOn(mgmtCluster, 'provisioner', 'get').mockReturnValue(clusterData.provisioner);
170
188
  }
171
189
 
172
190
  expect(cluster.isImported).toBe(expected);
@@ -268,7 +286,10 @@ describe('class ProvCluster', () => {
268
286
 
269
287
  it.each(testCases)('should return the hasError value properly based on the "status.conditions" props data for testcase %p', (testName: string, conditions: Array, expected: Boolean) => {
270
288
  const ctx = { rootGetters: { 'management/byId': jest.fn() } };
271
- const cluster = new ProvCluster({ status: { conditions } }, ctx);
289
+ const mgmtCluster = new MgmtCluster({ status: { conditions } }, ctx);
290
+ const cluster = new ProvCluster({}, ctx);
291
+
292
+ jest.spyOn(cluster, 'mgmt', 'get').mockReturnValue(mgmtCluster);
272
293
 
273
294
  expect(cluster.hasError).toBe(expected);
274
295
  resetMocks();
@@ -0,0 +1,26 @@
1
+ import { escapeHtml } from '@shell/utils/string';
2
+ import SteveModel from '@shell/plugins/steve/steve-class';
3
+ import { CAPI } from '@shell/config/types';
4
+
5
+ export default class CapiMachineRoot extends SteveModel {
6
+ get cluster() {
7
+ if ( !this.spec.clusterName ) {
8
+ return null;
9
+ }
10
+
11
+ // This is used to get the cluster's human name
12
+ // Ideally we get this via the management cluster. To find it though we have to go via the provisioning cluster. So might as well just use that
13
+
14
+ const clusterId = `${ this.metadata.namespace }/${ this.spec.clusterName }`;
15
+
16
+ return this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, clusterId);
17
+ }
18
+
19
+ get clusterName() {
20
+ return this.cluster?.nameDisplay || this.spec.clusterName;
21
+ }
22
+
23
+ get groupByLabel() {
24
+ return this.$rootGetters['i18n/t']('resourceTable.groupLabel.cluster', { name: escapeHtml(this.clusterName) });
25
+ }
26
+ }
package/models/cluster.js CHANGED
@@ -69,6 +69,6 @@ export default class NormanCluster extends NormanModel {
69
69
  const id = this.provisioningClusterId;
70
70
 
71
71
  return id && !!this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, id);
72
- }, this.$rootGetters['i18n/t']('cluster.managementTimeout'), timeout, interval);
72
+ }, this.$rootGetters['i18n/t']('cluster.managementTimeout', { type: CAPI.RANCHER_CLUSTER, name: this.provisioningClusterId }), timeout, interval);
73
73
  }
74
74
  }
@@ -2,11 +2,10 @@ import { ADDRESSES, CAPI, NODE } from '@shell/config/types';
2
2
  import { CAPI as CAPI_LABELS, MACHINE_ROLES } from '@shell/config/labels-annotations';
3
3
  import { NAME as EXPLORER } from '@shell/config/product/explorer';
4
4
  import { listNodeRoles } from '@shell/models/cluster/node';
5
- import { escapeHtml } from '@shell/utils/string';
6
5
  import { insertAt, findBy } from '@shell/utils/array';
7
6
  import { get } from '@shell/utils/object';
8
7
  import { downloadUrl } from '@shell/utils/download';
9
- import SteveModel from '@shell/plugins/steve/steve-class';
8
+ import CapiMachineRoot from '@shell/models/base-cluster.x-k8s.io';
10
9
 
11
10
  /**
12
11
  * Prevent scaling down control plane or etcd machines to zero
@@ -53,7 +52,8 @@ export function notOnlyOfRole(current, all) {
53
52
 
54
53
  return false;
55
54
  }
56
- export default class CapiMachine extends SteveModel {
55
+
56
+ export default class CapiMachine extends CapiMachineRoot {
57
57
  get _availableActions() {
58
58
  const out = super._availableActions;
59
59
 
@@ -144,18 +144,6 @@ export default class CapiMachine extends SteveModel {
144
144
  return await this.$dispatch('find', { type: kind, id });
145
145
  }
146
146
 
147
- get cluster() {
148
- if ( !this.spec.clusterName ) {
149
- return null;
150
- }
151
-
152
- const clusterId = `${ this.metadata.namespace }/${ this.spec.clusterName }`;
153
-
154
- const cluster = this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, clusterId);
155
-
156
- return cluster;
157
- }
158
-
159
147
  get poolName() {
160
148
  return this.metadata?.labels?.[ CAPI_LABELS.DEPLOYMENT_NAME ] || '';
161
149
  }
@@ -182,7 +170,7 @@ export default class CapiMachine extends SteveModel {
182
170
  return {
183
171
  name: 'c-cluster-product-resource-id',
184
172
  params: {
185
- cluster: this.cluster.status.clusterName,
173
+ cluster,
186
174
  product: EXPLORER,
187
175
  resource: NODE,
188
176
  id: kubeId
@@ -193,12 +181,6 @@ export default class CapiMachine extends SteveModel {
193
181
  return kubeId;
194
182
  }
195
183
 
196
- get groupByLabel() {
197
- const name = this.cluster?.nameDisplay || this.spec.clusterName;
198
-
199
- return this.$rootGetters['i18n/t']('resourceTable.groupLabel.cluster', { name: escapeHtml(name) });
200
- }
201
-
202
184
  get labels() {
203
185
  return this.metadata?.labels || {};
204
186
  }
@@ -1,33 +1,15 @@
1
1
  import { CAPI } from '@shell/config/types';
2
2
  import { escapeHtml } from '@shell/utils/string';
3
3
  import { sortBy } from '@shell/utils/sort';
4
- import SteveModel from '@shell/plugins/steve/steve-class';
5
4
  import { exceptionToErrorsArray } from '@shell/utils/error';
6
5
  import { handleConflict } from '@shell/plugins/dashboard-store/normalize';
7
6
  import { CAPI as CAPI_ANNOTATIONS, MACHINE_ROLES } from '@shell/config/labels-annotations';
8
7
  import { notOnlyOfRole } from '@shell/models/cluster.x-k8s.io.machine';
9
8
  import { KIND } from '../config/elemental-types';
10
9
  import { KIND as HARVESTER_KIND } from '../config/harvester-manager-types';
10
+ import CapiMachineRoot from '@shell/models/base-cluster.x-k8s.io';
11
11
 
12
- export default class CapiMachineDeployment extends SteveModel {
13
- get cluster() {
14
- if ( !this.spec.clusterName ) {
15
- return null;
16
- }
17
-
18
- const clusterId = `${ this.metadata.namespace }/${ this.spec.clusterName }`;
19
-
20
- const cluster = this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, clusterId);
21
-
22
- return cluster;
23
- }
24
-
25
- get groupByLabel() {
26
- const name = this.cluster?.nameDisplay || this.spec.clusterName;
27
-
28
- return this.$rootGetters['i18n/t']('resourceTable.groupLabel.cluster', { name: escapeHtml(name) });
29
- }
30
-
12
+ export default class CapiMachineDeployment extends CapiMachineRoot {
31
13
  get groupByPoolLabel() {
32
14
  return `${ this.$rootGetters['i18n/t']('resourceTable.groupLabel.machinePool', { name: escapeHtml(this.nameDisplay) }) }`;
33
15
  }
@@ -1,23 +1,5 @@
1
- import { CAPI } from '@shell/config/types';
2
- import { escapeHtml } from '@shell/utils/string';
3
- import SteveModel from '@shell/plugins/steve/steve-class';
4
1
 
5
- export default class CapiMachineSet extends SteveModel {
6
- get cluster() {
7
- if ( !this.spec.clusterName ) {
8
- return null;
9
- }
2
+ import CapiMachineRoot from '@shell/models/base-cluster.x-k8s.io';
10
3
 
11
- const clusterId = `${ this.metadata.namespace }/${ this.spec.clusterName }`;
12
-
13
- const cluster = this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, clusterId);
14
-
15
- return cluster;
16
- }
17
-
18
- get groupByLabel() {
19
- const name = this.cluster?.nameDisplay || this.spec.clusterName;
20
-
21
- return this.$rootGetters['i18n/t']('resourceTable.groupLabel.cluster', { name: escapeHtml(name) });
22
- }
4
+ export default class CapiMachineSet extends CapiMachineRoot {
23
5
  }
@@ -65,8 +65,29 @@ export default class ClusterScan extends SteveModel {
65
65
  total: 1,
66
66
  };
67
67
 
68
+ const downloadReportXCCDF = {
69
+ action: 'downloadLatestReportXCCDF',
70
+ enabled: this.hasReport,
71
+ icon: 'icon icon-download',
72
+ label: t('compliance.downloadReportXCCDF'),
73
+ total: 1,
74
+ };
75
+
76
+ const downloadAllReportsXCCDF = {
77
+ action: 'downloadAllReportsXCCDF',
78
+ enabled: this.hasReport,
79
+ icon: 'icon icon-download',
80
+ label: t('compliance.downloadAllReportsXCCDF'),
81
+ total: 1,
82
+ };
83
+
68
84
  if (this.hasReports) {
69
85
  out.unshift({ divider: true });
86
+ if (this.spec?.scheduledScanConfig?.cronSchedule) {
87
+ out.unshift(downloadAllReportsXCCDF);
88
+ downloadReportXCCDF.label = t('compliance.downloadLatestReportXCCDF');
89
+ }
90
+ out.unshift(downloadReportXCCDF);
70
91
  if (this.spec?.scheduledScanConfig?.cronSchedule) {
71
92
  out.unshift(downloadAllReports);
72
93
  downloadReport.label = t('compliance.downloadLatestReport');
@@ -121,7 +142,7 @@ export default class ClusterScan extends SteveModel {
121
142
 
122
143
  downloadFile(`${ labelFor(report) }.csv`, csv, 'application/csv');
123
144
  } catch (err) {
124
- this.$dispatch('growl/fromError', { title: 'Error downloading file', err }, { root: true });
145
+ this.$dispatch('growl/fromError', { title: this.t('compliance.scan.errorDownload'), err }, { root: true });
125
146
  }
126
147
  }
127
148
 
@@ -143,7 +164,7 @@ export default class ClusterScan extends SteveModel {
143
164
 
144
165
  toZip[`${ labelFor(report) }.csv`] = csv;
145
166
  } catch (err) {
146
- this.$dispatch('growl/fromError', { title: 'Error downloading file', err }, { root: true });
167
+ this.$dispatch('growl/fromError', { title: this.t('compliance.scan.errorDownload'), err }, { root: true });
147
168
  }
148
169
  });
149
170
  if (!isEmpty(toZip)) {
@@ -153,6 +174,113 @@ export default class ClusterScan extends SteveModel {
153
174
  }
154
175
  }
155
176
 
177
+ async _resolveBenchmark() {
178
+ const profileName = this.status?.lastRunScanProfileName;
179
+
180
+ if (!profileName) {
181
+ return null;
182
+ }
183
+ const profile = await this.$dispatch('find', { type: COMPLIANCE.CLUSTER_SCAN_PROFILE, id: profileName });
184
+ const benchmarkId = profile?.spec?.benchmarkVersion;
185
+
186
+ if (!benchmarkId) {
187
+ return null;
188
+ }
189
+
190
+ return this.$dispatch('find', { type: COMPLIANCE.BENCHMARK, id: benchmarkId });
191
+ }
192
+
193
+ async downloadLatestReportXCCDF() {
194
+ const reports = await this.getReports() || [];
195
+ const report = sortBy(reports, 'metadata.creationTimestamp', true)[0];
196
+
197
+ try {
198
+ const benchmark = await this._resolveBenchmark();
199
+ const { generateXCCDFPerNode } = await import(/* webpackChunkName: "xccdf" */'@shell/utils/xccdf');
200
+
201
+ const parsed = report.parsedReport || {};
202
+ const common = {
203
+ report: parsed,
204
+ benchmarkVersion: parsed.version || benchmark?.spec?.benchmarkVersion || '',
205
+ metadata: benchmark?.spec?.benchmarkMetadata || {},
206
+ stigChecks: benchmark?.spec?.stigChecks || {},
207
+ };
208
+
209
+ const toZip = {};
210
+
211
+ if (!Object.entries(parsed.nodes || {}).length) {
212
+ this.$dispatch('growl/fromError', { title: this.t('compliance.scan.errorNoParsedNodes') }, { root: true });
213
+ } else {
214
+ Object.entries(parsed.nodes || {}).forEach(([role, hosts]) => {
215
+ (hosts || []).forEach((hostname) => {
216
+ const xml = generateXCCDFPerNode({
217
+ ...common, hostname, role,
218
+ });
219
+
220
+ toZip[`${ labelFor(report) }--${ hostname }.xml`] = xml;
221
+ });
222
+ });
223
+
224
+ if (!isEmpty(toZip)) {
225
+ const zip = await generateZip(toZip);
226
+
227
+ downloadFile(`${ labelFor(report) }-per-node.zip`, zip, 'application/zip');
228
+ }
229
+ }
230
+ } catch (err) {
231
+ this.$dispatch('growl/fromError', { title: this.t('compliance.scan.errorDownload'), err }, { root: true });
232
+ }
233
+ }
234
+
235
+ async downloadAllReportsXCCDF() {
236
+ const toZip = {};
237
+ const reports = await this.getReports() || [];
238
+
239
+ try {
240
+ const benchmark = await this._resolveBenchmark();
241
+ const { generateXCCDFPerNode } = await import(/* webpackChunkName: "xccdf" */'@shell/utils/xccdf');
242
+
243
+ const hasParsedNodes = reports.some((report) => Object.entries(report.parsedReport?.nodes || {}).length);
244
+
245
+ if (!hasParsedNodes) {
246
+ this.$dispatch('growl/fromError', { title: this.t('compliance.scan.errorNoParsedNodes') }, { root: true });
247
+ } else {
248
+ reports.forEach((report) => {
249
+ try {
250
+ const parsed = report.parsedReport || {};
251
+ const common = {
252
+ report: parsed,
253
+ benchmarkVersion: parsed.version || benchmark?.spec?.benchmarkVersion || '',
254
+ metadata: benchmark?.spec?.benchmarkMetadata || {},
255
+ stigChecks: benchmark?.spec?.stigChecks || {},
256
+ };
257
+ const folder = labelFor(report);
258
+
259
+ Object.entries(parsed.nodes || {}).forEach(([role, hosts]) => {
260
+ (hosts || []).forEach((hostname) => {
261
+ const xml = generateXCCDFPerNode({
262
+ ...common, hostname, role,
263
+ });
264
+
265
+ toZip[`${ folder }/${ hostname }.xml`] = xml;
266
+ });
267
+ });
268
+ } catch (err) {
269
+ this.$dispatch('growl/fromError', { title: this.t('compliance.scan.errorDownload'), err }, { root: true });
270
+ }
271
+ });
272
+
273
+ if (!isEmpty(toZip)) {
274
+ generateZip(toZip).then((zip) => {
275
+ downloadFile(`${ this.id }-reports-xccdf-per-node.zip`, zip, 'application/zip');
276
+ });
277
+ }
278
+ }
279
+ } catch (err) {
280
+ this.$dispatch('growl/fromError', { title: this.t('compliance.scan.errorDownload'), err }, { root: true });
281
+ }
282
+ }
283
+
156
284
  get scanProfileLink() {
157
285
  if (this.status?.lastRunScanProfileName) {
158
286
  return {
@@ -1,4 +1,4 @@
1
- import { CAPI, MANAGEMENT } from '@shell/config/types';
1
+ import { MANAGEMENT } from '@shell/config/types';
2
2
  import SteveModel from '@shell/plugins/steve/steve-class';
3
3
  import { Location } from 'vue-router';
4
4
 
@@ -49,16 +49,13 @@ export default class Kubeconfig extends SteveModel {
49
49
  */
50
50
  get referencedClusters(): ReferencedCluster[] {
51
51
  const clusterIds = this.spec?.clusters || [];
52
- const provClusters = this.$rootGetters['management/all'](CAPI.RANCHER_CLUSTER) || [];
53
- const mgmtClusters = this.$rootGetters['management/all'](MANAGEMENT.CLUSTER) || [];
54
52
 
55
53
  return clusterIds.map((id: string) => {
56
- const provCluster = provClusters.find((c: any) => c.mgmt?.id === id || c.status?.clusterName === id);
57
- const mgmtCluster = mgmtClusters.find((c: any) => c.id === id);
58
- const cluster = provCluster || mgmtCluster;
54
+ const mgmtCluster = this.$rootGetters['management/byId'](MANAGEMENT.CLUSTER, id);
55
+ const provCluster = mgmtCluster?.provCluster;
59
56
 
60
57
  return {
61
- label: cluster?.nameDisplay || this.t('"ext.cattle.io.kubeconfig".deleted', { name: id }),
58
+ label: mgmtCluster?.nameDisplay || this.t('"ext.cattle.io.kubeconfig".deleted', { name: id }),
62
59
  location: provCluster?.detailLocation || mgmtCluster?.detailLocation || null
63
60
  };
64
61
  });
@@ -7,6 +7,7 @@ import { addObject, addObjects, findBy } from '@shell/utils/array';
7
7
  import SteveModel from '@shell/plugins/steve/steve-class';
8
8
  import { mapStateToEnum, primaryDisplayStatusFromCount, STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
9
9
  import FleetUtils from '@shell/utils/fleet';
10
+ import { HARVESTER_CONTAINER } from '@shell/store/features';
10
11
 
11
12
  function normalizeStateCounts(data) {
12
13
  if (isEmpty(data)) {
@@ -138,7 +139,8 @@ export default class FleetApplication extends SteveModel {
138
139
  }
139
140
 
140
141
  get targetInfo() {
141
- const mode = FleetUtils.Application.getTargetMode(this.spec.targets || [], this.metadata.namespace);
142
+ const areHarvesterHostsVisible = this.$rootGetters['features/get'](HARVESTER_CONTAINER);
143
+ const mode = FleetUtils.Application.getTargetMode(this.spec.targets || [], this.metadata.namespace, areHarvesterHostsVisible);
142
144
 
143
145
  return {
144
146
  mode,