@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
@@ -90,36 +90,36 @@ describe('component: FleetClusters', () => {
90
90
  const wrapper = createWrapper();
91
91
  const reposReady = wrapper.vm.headers.find((h: any) => h.name === 'reposReady');
92
92
 
93
- expect(reposReady.labelKey).toBe('tableHeaders.reposReady');
94
- expect(reposReady.value).toBe('status.readyGitRepos');
95
- expect(reposReady.search).toBe(false);
93
+ expect(reposReady?.labelKey).toBe('tableHeaders.reposReady');
94
+ expect(reposReady?.value).toBe('status.readyGitRepos');
95
+ expect(reposReady?.search).toBe(false);
96
96
  });
97
97
 
98
98
  it('should configure helmOpsReady column correctly', () => {
99
99
  const wrapper = createWrapper();
100
100
  const helmOpsReady = wrapper.vm.headers.find((h: any) => h.name === 'helmOpsReady');
101
101
 
102
- expect(helmOpsReady.labelKey).toBe('tableHeaders.helmOpsReady');
103
- expect(helmOpsReady.value).toBe('status.readyHelmOps');
104
- expect(helmOpsReady.search).toBe(false);
102
+ expect(helmOpsReady?.labelKey).toBe('tableHeaders.helmOpsReady');
103
+ expect(helmOpsReady?.value).toBe('status.readyHelmOps');
104
+ expect(helmOpsReady?.search).toBe(false);
105
105
  });
106
106
 
107
107
  it('should configure bundlesReady column correctly', () => {
108
108
  const wrapper = createWrapper();
109
109
  const bundlesReady = wrapper.vm.headers.find((h: any) => h.name === 'bundlesReady');
110
110
 
111
- expect(bundlesReady.labelKey).toBe('tableHeaders.bundlesReady');
112
- expect(bundlesReady.value).toBe('status.display.readyBundles');
113
- expect(bundlesReady.search).toBe(false);
111
+ expect(bundlesReady?.labelKey).toBe('tableHeaders.bundlesReady');
112
+ expect(bundlesReady?.value).toBe('status.display.readyBundles');
113
+ expect(bundlesReady?.search).toBe(false);
114
114
  });
115
115
 
116
116
  it('should configure lastSeen column with LiveDate formatter', () => {
117
117
  const wrapper = createWrapper();
118
118
  const lastSeen = wrapper.vm.headers.find((h: any) => h.name === 'lastSeen');
119
119
 
120
- expect(lastSeen.formatter).toBe('LiveDate');
121
- expect(lastSeen.formatterOpts).toStrictEqual({ addSuffix: true });
122
- expect(lastSeen.width).toBe(120);
120
+ expect(lastSeen?.formatter).toBe('LiveDate');
121
+ expect(lastSeen?.formatterOpts).toStrictEqual({ addSuffix: true });
122
+ expect(lastSeen?.width).toBe(120);
123
123
  });
124
124
  });
125
125
 
@@ -12,7 +12,8 @@ import { _VIEW } from '@shell/config/query-params';
12
12
  import { useClickOutside } from '@shell/composables/useClickOutside';
13
13
  import { useLabeledFormElement, labeledFormElementProps } from '@shell/composables/useLabeledFormElement';
14
14
  import { useLabeledSelect } from '@shell/composables/useLabeledSelect';
15
- import { ref } from 'vue';
15
+ import { ref, toRef } from 'vue';
16
+ import { useVeeValidateField } from '@shell/composables/useVeeValidateField';
16
17
 
17
18
  export default {
18
19
  name: 'LabeledSelect',
@@ -115,6 +116,11 @@ export default {
115
116
  noOptionsLabelKey: {
116
117
  type: String,
117
118
  default: 'labelSelect.noOptions.empty'
119
+ },
120
+
121
+ name: {
122
+ type: String,
123
+ default: null
118
124
  }
119
125
  },
120
126
 
@@ -161,6 +167,13 @@ export default {
161
167
  resizeHandlerFn(select);
162
168
  };
163
169
 
170
+ const { effectiveValidationMessage, veeHandleBlur, veeValidate } = useVeeValidateField({
171
+ name: toRef(props, 'name'),
172
+ rules: toRef(props, 'rules'),
173
+ value: toRef(props, 'value'),
174
+ validationMessage,
175
+ });
176
+
164
177
  return {
165
178
  isOpen,
166
179
  select,
@@ -172,7 +185,7 @@ export default {
172
185
  onFocusLabeled,
173
186
  onBlurLabeled,
174
187
  isDisabled,
175
- validationMessage,
188
+ validationMessage: effectiveValidationMessage,
176
189
  requiredField,
177
190
  isSearchable,
178
191
  isFilterable,
@@ -186,6 +199,8 @@ export default {
186
199
  paginating,
187
200
  loadMore,
188
201
  setPaginationFilter,
202
+ veeHandleBlur,
203
+ veeValidate,
189
204
  };
190
205
  },
191
206
 
@@ -221,7 +236,7 @@ export default {
221
236
  // update placeholder text to inform user they can add their own opts when none are found
222
237
  showTagPrompts() {
223
238
  return !this.options.length && this.$attrs.taggable && this.isSearchable;
224
- }
239
+ },
225
240
  },
226
241
 
227
242
  methods: {
@@ -272,6 +287,8 @@ export default {
272
287
  this.$emit('on-blur');
273
288
  this.selectedVisibility = 'visible';
274
289
  this.onBlurLabeled();
290
+ this.veeHandleBlur(undefined, false);
291
+ this.veeValidate();
275
292
  },
276
293
 
277
294
  onOpen() {
@@ -164,6 +164,14 @@ export default {
164
164
  }),
165
165
  type: Object,
166
166
  },
167
+ nameFieldName: {
168
+ type: String,
169
+ default: null,
170
+ },
171
+ namespaceFieldName: {
172
+ type: String,
173
+ default: null,
174
+ },
167
175
 
168
176
  /**
169
177
  * Inherited global identifier prefix for tests
@@ -438,6 +446,7 @@ export default {
438
446
  <LabeledInput
439
447
  ref="namespaceInput"
440
448
  v-model:value="namespace"
449
+ :name="namespaceFieldName"
441
450
  :label="t('namespace.label')"
442
451
  :placeholder="t('namespace.createNamespace')"
443
452
  :disabled="namespaceReallyDisabled"
@@ -464,6 +473,7 @@ export default {
464
473
  <LabeledSelect
465
474
  v-show="!createNamespace"
466
475
  v-model:value="namespace"
476
+ :name="namespaceFieldName"
467
477
  :clearable="true"
468
478
  :options="options"
469
479
  :disabled="namespaceReallyDisabled"
@@ -487,6 +497,7 @@ export default {
487
497
  ref="nameInput"
488
498
  key="name"
489
499
  v-model:value="name"
500
+ :name="nameFieldName"
490
501
  data-testid="NameNsDescriptionNameInput"
491
502
  :label="t(nameLabel)"
492
503
  :placeholder="t(namePlaceholder)"
@@ -140,11 +140,12 @@ export default {
140
140
  this.afterPrivilegedTickedMessage = this.t('workload.container.security.privileged.afterTick.false');
141
141
  }
142
142
 
143
- if (securityContext.fsGroup === '') {
143
+ // Drop empty values so we don't send a string for int64 fields.
144
+ if (securityContext.fsGroup === '' || securityContext.fsGroup === null || securityContext.fsGroup === undefined) {
144
145
  delete securityContext.fsGroup;
145
146
  }
146
147
 
147
- if (securityContext.runAsUser === '') {
148
+ if (securityContext.runAsUser === '' || securityContext.runAsUser === null || securityContext.runAsUser === undefined) {
148
149
  delete securityContext.runAsUser;
149
150
  }
150
151
 
@@ -175,6 +176,7 @@ export default {
175
176
  ref="firstFocusable"
176
177
  v-model:value.number="securityContext.fsGroup"
177
178
  type="number"
179
+ min="0"
178
180
  :mode="mode"
179
181
  :label="t('workload.container.security.fsGroup')"
180
182
  @update:value="update"
@@ -283,6 +285,8 @@ export default {
283
285
  </legend>
284
286
  <LabeledInput
285
287
  v-model:value.number="securityContext.runAsUser"
288
+ type="number"
289
+ min="0"
286
290
  :label="t('workload.container.security.runAsUser.label')"
287
291
  :mode="mode"
288
292
  @update:value="update"
@@ -130,14 +130,11 @@ export default {
130
130
  const version = this.provisioningCluster?.kubernetesVersion;
131
131
 
132
132
  if (this.provisioningCluster?.isRke2) {
133
+ // This is a candidate for using the prov cluster provisioner (or now the mgmt cluster provisioner directly)
133
134
  const machineSelectorConfig = this.provisioningCluster?.spec?.rkeConfig?.machineSelectorConfig || {};
134
135
  const agentConfig = (machineSelectorConfig[0] || {}).config;
135
136
 
136
137
  cloudProvider = agentConfig?.['cloud-provider-name'];
137
- } else if (this.provisioningCluster?.isRke1) {
138
- const currentCluster = this.$store.getters['currentCluster'];
139
-
140
- cloudProvider = currentCluster?.spec?.rancherKubernetesEngineConfig?.cloudProvider?.name;
141
138
  }
142
139
 
143
140
  return cloudProvider === HARVESTER &&
@@ -145,9 +142,7 @@ export default {
145
142
  },
146
143
 
147
144
  provisioningCluster() {
148
- const out = this.$store.getters['management/all'](CAPI.RANCHER_CLUSTER).find((c) => c?.status?.clusterName === this.currentCluster.metadata.name);
149
-
150
- return out;
145
+ return this.$store.getters['management/byId'](CAPI.RANCHER_CLUSTER, this.currentCluster.provClusterId);
151
146
  },
152
147
  },
153
148
 
@@ -79,6 +79,42 @@ describe('component: Security', () => {
79
79
  expect(wrapper.emitted('update:value')).toHaveLength(1);
80
80
  });
81
81
 
82
+ // Regression: clearing runAsUser must drop the key from the emitted object.
83
+ // Otherwise the spec is sent with `runAsUser: ""` and the API rejects with
84
+ // "cannot unmarshal string ... of type int64". See issue #9601.
85
+ it('should omit runAsUser from the emitted value when the input is cleared', async() => {
86
+ const wrapper = mount(Security, {
87
+ props: {
88
+ mode: _EDIT, formType: FORM_TYPES.CONTAINER, value: { runAsUser: 33 }
89
+ }
90
+ });
91
+ const input = wrapper.find('[data-testid="input-security-runAsUser"]').find('input');
92
+
93
+ await input.setValue('');
94
+
95
+ const events = wrapper.emitted('update:value') ?? [];
96
+ const last = events[events.length - 1][0] as Record<string, unknown>;
97
+
98
+ expect(Object.prototype.hasOwnProperty.call(last, 'runAsUser')).toBe(false);
99
+ });
100
+
101
+ it('should omit runAsUser from the emitted value when it is undefined', async() => {
102
+ const wrapper = mount(Security, {
103
+ props: {
104
+ mode: _EDIT, formType: FORM_TYPES.CONTAINER, value: {}
105
+ }
106
+ });
107
+
108
+ const checkbox = wrapper.find('[data-testid="input-security-runasNonRoot"]').find('label');
109
+
110
+ await checkbox.trigger('click');
111
+
112
+ const events = wrapper.emitted('update:value') ?? [];
113
+ const last = events[events.length - 1][0] as Record<string, unknown>;
114
+
115
+ expect(Object.prototype.hasOwnProperty.call(last, 'runAsUser')).toBe(false);
116
+ });
117
+
82
118
  it.each([
83
119
  'privileged',
84
120
  'allowPrivilegeEscalation',
@@ -139,5 +175,45 @@ describe('component: Security', () => {
139
175
 
140
176
  expect(wrapper.emitted('update:value')).toHaveLength(1);
141
177
  });
178
+
179
+ it.each([
180
+ 'runAsUser',
181
+ 'fsGroup',
182
+ ])('should omit %p from the emitted value when it is undefined', async(field) => {
183
+ const wrapper = mount(Security, {
184
+ props: {
185
+ mode: _EDIT, formType: FORM_TYPES.POD, value: {}
186
+ }
187
+ });
188
+
189
+ const checkbox = wrapper.find('[data-testid="input-security-runasNonRoot"]').find('label');
190
+
191
+ await checkbox.trigger('click');
192
+
193
+ const events = wrapper.emitted('update:value') ?? [];
194
+ const last = events[events.length - 1][0] as Record<string, unknown>;
195
+
196
+ expect(Object.prototype.hasOwnProperty.call(last, field)).toBe(false);
197
+ });
198
+
199
+ // Regression for #9601 — see equivalent container-level test above.
200
+ it.each([
201
+ ['runAsUser', { runAsUser: 33 }],
202
+ ['fsGroup', { fsGroup: 100 }],
203
+ ])('should omit %p from the emitted value when the input is cleared', async(field, initial) => {
204
+ const wrapper = mount(Security, {
205
+ props: {
206
+ mode: _EDIT, formType: FORM_TYPES.POD, value: initial
207
+ }
208
+ });
209
+ const input = wrapper.find(`[data-testid="input-security-${ field }"]`).find('input');
210
+
211
+ await input.setValue('');
212
+
213
+ const events = wrapper.emitted('update:value') ?? [];
214
+ const last = events[events.length - 1][0] as Record<string, unknown>;
215
+
216
+ expect(Object.prototype.hasOwnProperty.call(last, field)).toBe(false);
217
+ });
142
218
  });
143
219
  });
@@ -21,8 +21,8 @@ const store = useStore();
21
21
  const i18n = useI18n(store);
22
22
 
23
23
  const checked = computed(() => props.value === true || props.value === 'true');
24
- const actionIcon = computed(() => props.row.isAutoscalerPaused ? 'icon-play' : 'icon-pause');
25
- const actionText = computed(() => props.row.isAutoscalerPaused ? i18n.t('autoscaler.card.resume') : i18n.t('autoscaler.card.pause'));
24
+ const actionIcon = computed(() => props.row.provCluster.isAutoscalerPaused ? 'icon-play' : 'icon-pause');
25
+ const actionText = computed(() => props.row.provCluster.isAutoscalerPaused ? i18n.t('autoscaler.card.resume') : i18n.t('autoscaler.card.pause'));
26
26
  const stopPropagation = (event: Event) => {
27
27
  // This is to prevent click events from getting to the table row which ends up selecting the row
28
28
  event.stopPropagation();
@@ -45,7 +45,7 @@ const stopPropagation = (event: Event) => {
45
45
  #heading-action="{close}"
46
46
  >
47
47
  <RcButton
48
- v-if="row.canPauseResumeAutoscaler"
48
+ v-if="row.provCluster.canPauseResumeAutoscaler"
49
49
  variant="secondary"
50
50
  size="small"
51
51
  class="action"
@@ -56,7 +56,7 @@ const stopPropagation = (event: Event) => {
56
56
  </RcButton>
57
57
  </template>
58
58
  <template #card-body>
59
- <AutoscalerCard :value="props.row" />
59
+ <AutoscalerCard :value="props.row.provCluster" />
60
60
  </template>
61
61
  </PopoverCard>
62
62
  </span>
@@ -0,0 +1,27 @@
1
+ <script>
2
+
3
+ export default {
4
+ name: 'ClusterKubeVersion',
5
+
6
+ props: {
7
+ row: {
8
+ type: Object,
9
+ default: null,
10
+ },
11
+ },
12
+ };
13
+ </script>
14
+
15
+ <template>
16
+ <div>
17
+ <span>
18
+ {{ row.kubernetesVersion }}
19
+ </span>
20
+ <div
21
+ v-clean-tooltip="{content: row.architecture.tooltip, placement: 'left'}"
22
+ class="text-muted"
23
+ >
24
+ {{ row.architecture.label }}
25
+ </div>
26
+ </div>
27
+ </template>
@@ -31,7 +31,7 @@ export default {
31
31
  return this.getCustomDetailLink(this.row);
32
32
  }
33
33
 
34
- return this.row?.detailLocation;
34
+ return this.row?.provCluster?.detailLocation;
35
35
  },
36
36
 
37
37
  statusErrorConditions() {
@@ -77,12 +77,6 @@ export default {
77
77
  class="conditions-alert-icon icon-alert icon"
78
78
  data-testid="unavailable-machines-alert-icon"
79
79
  />
80
- <i
81
- v-if="row.isRke1"
82
- v-clean-tooltip="t('cluster.rke1Unsupported')"
83
- class="rke1-unsupported-icon icon-warning icon"
84
- data-testid="rke1-unsupported-icon"
85
- />
86
80
  <i
87
81
  v-if="row.hasError && statusErrorConditions.length > 0"
88
82
  v-clean-tooltip="{ content: `<div>${formattedConditions}</div>`, html: true }"
@@ -12,26 +12,22 @@ export default {
12
12
  <template>
13
13
  <div>
14
14
  <template v-if="row.machineProvider">
15
- <span v-if="row.isHarvester && row.mgmt && row.mgmt.isReady && !row.hasError">
15
+ <span v-if="row.isHarvester && row.isReady && !row.hasError">
16
16
  <a
17
- v-if="row.mgmt.isReady && !row.hasError"
18
17
  role="button"
19
18
  @click="row.goToHarvesterCluster()"
20
19
  >
21
20
  {{ row.machineProviderDisplay }}
22
21
  </a>
23
22
  </span>
24
- <span v-else>
23
+ <span v-else-if="row.machineProviderDisplay">
25
24
  {{ row.machineProviderDisplay }}
26
25
  </span>
27
26
  </template>
28
- <template v-else-if="row.isImported">
29
- {{ t('cluster.provider.imported') }}
30
- </template>
31
- <template v-else-if="row.isCustom">
32
- {{ t('cluster.provider.custom') }}
33
- </template>
34
- <div class="text-muted">
27
+ <div
28
+ v-if="row.machineProviderDisplay !== row.provisionerDisplay"
29
+ class="text-muted"
30
+ >
35
31
  {{ row.provisionerDisplay }}
36
32
  </div>
37
33
  </div>
@@ -26,9 +26,6 @@ export default {
26
26
  if (this.clusterId) {
27
27
  return this.row.statusResourceCountsForCluster(this.clusterId);
28
28
  }
29
- if (this.row.statusResourceCountsForCluster) {
30
- return this.row.statusResourceCountsForCluster;
31
- }
32
29
 
33
30
  return this.row.status?.resourceCounts || {};
34
31
  },
@@ -18,7 +18,7 @@ export default {
18
18
  computed: {
19
19
  ready() {
20
20
  // Ensure we never show more ready machines than desired
21
- // If w can address in backedn, we could revert this in the future
21
+ // If we can address in backend, we could revert this in the future
22
22
  const ready = this.row?.ready || 0;
23
23
  const desired = this.row?.desired || 0;
24
24
 
@@ -9,8 +9,8 @@ export default {
9
9
  },
10
10
  computed: {
11
11
  podsUsage() {
12
- const usedPods = this.row?.mgmt?.status?.requested?.pods;
13
- const totalPods = this.row?.mgmt?.status?.allocatable?.pods;
12
+ const usedPods = this.row?.status?.requested?.pods;
13
+ const totalPods = this.row?.status?.allocatable?.pods;
14
14
 
15
15
  if (!this.row?.isReady || !totalPods) {
16
16
  return '—';
@@ -30,6 +30,10 @@ describe('component: formatter/Autoscaler.vue', () => {
30
30
  stubs: {
31
31
  PopoverCard: PopoverCardStub,
32
32
  RcButton: { template: '<button><slot /></button>' },
33
+ // RcButton: {
34
+ // template: '<button><i v-if="icon" :class="icon" /><i v-if="leftIcon" :class="leftIcon" /><slot /></button>',
35
+ // props: ['icon', 'leftIcon']
36
+ // },
33
37
  AutoscalerCard: {
34
38
  name: 'AutoscalerCard',
35
39
  props: ['value'],
@@ -88,30 +92,26 @@ describe('component: formatter/Autoscaler.vue', () => {
88
92
  });
89
93
 
90
94
  it('should render AutoscalerCard with correct row data', () => {
91
- const rowData = { id: 'test-row' };
92
- const wrapper = createWrapper({ value: true, row: rowData });
95
+ const provCluster = { id: 'test-row' };
96
+ const wrapper = createWrapper({ value: true, row: { provCluster } });
93
97
  const card = wrapper.findComponent({ name: 'AutoscalerCard' });
94
98
 
95
99
  expect(card.exists()).toBe(true);
96
- expect(card.props('value')).toStrictEqual(rowData);
100
+ expect(card.props('value')).toStrictEqual(provCluster);
97
101
  });
98
102
  });
99
103
 
100
104
  describe('heading action button', () => {
101
105
  it('should NOT render if canExplore is false', () => {
102
- const rowData = { canExplore: false };
103
- const wrapper = createWrapper({ value: true, row: rowData });
106
+ const provCluster = { canExplore: false };
107
+ const wrapper = createWrapper({ value: true, row: { provCluster } });
104
108
 
105
109
  expect(wrapper.find('button').exists()).toBe(false);
106
110
  });
107
111
 
108
112
  it('should render "Pause" button if autoscaler is running', () => {
109
- const rowData = {
110
- canExplore: true, isAutoscalerPaused: false, canPauseResumeAutoscaler: true
111
- };
112
- const wrapper = createWrapper({
113
- value: true, row: rowData, canPauseResumeAutoscaler: true
114
- });
113
+ const provCluster = { isAutoscalerPaused: false, canPauseResumeAutoscaler: true };
114
+ const wrapper = createWrapper({ value: true, row: { canExplore: true, provCluster } });
115
115
  const button = wrapper.find('button');
116
116
 
117
117
  expect(button.exists()).toBe(true);
@@ -120,10 +120,8 @@ describe('component: formatter/Autoscaler.vue', () => {
120
120
  });
121
121
 
122
122
  it('should render "Resume" button if autoscaler is paused', () => {
123
- const rowData = {
124
- canExplore: true, isAutoscalerPaused: true, canPauseResumeAutoscaler: true
125
- };
126
- const wrapper = createWrapper({ value: true, row: rowData });
123
+ const provCluster = { isAutoscalerPaused: true, canPauseResumeAutoscaler: true };
124
+ const wrapper = createWrapper({ value: true, row: { canExplore: true, provCluster } });
127
125
  const button = wrapper.find('button');
128
126
 
129
127
  expect(button.exists()).toBe(true);
@@ -132,20 +130,19 @@ describe('component: formatter/Autoscaler.vue', () => {
132
130
  });
133
131
 
134
132
  it('should hide "Resume" button if canPauseResumeAutoscaler is false', () => {
135
- const rowData = {
136
- canExplore: true, isAutoscalerPaused: true, canPauseResumeAutoscaler: false
137
- };
138
- const wrapper = createWrapper({ value: true, row: rowData });
133
+ const provCluster = { isAutoscalerPaused: true, canPauseResumeAutoscaler: false };
134
+ const wrapper = createWrapper({ value: true, row: { canExplore: true, provCluster } });
139
135
  const button = wrapper.find('button');
140
136
 
141
137
  expect(button.exists()).toBe(false);
142
138
  });
143
139
 
144
140
  it('should call toggleAutoscalerRunner and close on click', async() => {
145
- const rowData = {
146
- canExplore: true, toggleAutoscalerRunner: mockToggleRunner, canPauseResumeAutoscaler: true
141
+ const provCluster = { canPauseResumeAutoscaler: true };
142
+ const mgmtCluster = {
143
+ toggleAutoscalerRunner: mockToggleRunner, canExplore: true, provCluster
147
144
  };
148
- const wrapper = createWrapper({ value: true, row: rowData });
145
+ const wrapper = createWrapper({ value: true, row: mgmtCluster });
149
146
 
150
147
  wrapper.find('button').trigger('click');
151
148