@rancher/shell 3.0.5-rc.6 → 3.0.5-rc.8

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 (243) hide show
  1. package/assets/brand/classic/metadata.json +3 -0
  2. package/assets/styles/app.scss +1 -0
  3. package/assets/styles/base/_color.scss +16 -0
  4. package/assets/styles/base/_helpers.scss +10 -0
  5. package/assets/styles/base/_variables.scss +18 -12
  6. package/assets/styles/fonts/_icons.scss +1 -32
  7. package/assets/styles/global/_layout.scss +1 -1
  8. package/assets/styles/themes/_dark.scss +262 -258
  9. package/assets/styles/themes/_light.scss +538 -509
  10. package/assets/styles/themes/_modern.scss +914 -0
  11. package/assets/translations/en-us.yaml +110 -29
  12. package/chart/__tests__/S3.test.ts +2 -1
  13. package/cloud-credential/generic.vue +18 -10
  14. package/cloud-credential/harvester.vue +1 -9
  15. package/components/AdvancedSection.vue +8 -0
  16. package/components/ChartReadme.vue +17 -7
  17. package/components/CodeMirror.vue +1 -1
  18. package/components/Drawer/Chrome.vue +0 -1
  19. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +27 -28
  20. package/components/Drawer/ResourceDetailDrawer/composables.ts +4 -24
  21. package/components/Drawer/ResourceDetailDrawer/index.vue +18 -4
  22. package/components/InstallHelmCharts.vue +656 -0
  23. package/components/LazyImage.vue +60 -4
  24. package/components/Loading.vue +1 -1
  25. package/components/LocaleSelector.vue +7 -2
  26. package/components/Markdown.vue +4 -0
  27. package/components/PaginatedResourceTable.vue +46 -1
  28. package/components/PromptRestore.vue +22 -44
  29. package/components/Resource/Detail/Masthead/composable.ts +16 -0
  30. package/components/Resource/Detail/Masthead/index.vue +37 -0
  31. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +10 -2
  32. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +26 -7
  33. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +8 -1
  34. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -10
  35. package/components/Resource/Detail/Metadata/Rectangle.vue +3 -1
  36. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +10 -17
  37. package/components/Resource/Detail/Metadata/composables.ts +9 -7
  38. package/components/Resource/Detail/Metadata/index.vue +17 -2
  39. package/components/Resource/Detail/Page.vue +35 -21
  40. package/components/Resource/Detail/SpacedRow.vue +1 -1
  41. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +8 -9
  42. package/components/Resource/Detail/TitleBar/composables.ts +5 -5
  43. package/components/Resource/Detail/TitleBar/index.vue +12 -3
  44. package/components/ResourceDetail/Masthead/legacy.vue +1 -1
  45. package/components/ResourceDetail/index.vue +569 -72
  46. package/components/ResourceList/index.vue +1 -0
  47. package/components/ResourceTable.vue +6 -1
  48. package/components/ResourceYaml.vue +1 -1
  49. package/components/RichTranslation.vue +106 -0
  50. package/components/SlideInPanelManager.vue +13 -10
  51. package/components/SortableTable/index.vue +5 -5
  52. package/components/SortableTable/selection.js +0 -1
  53. package/components/Tabbed/index.vue +35 -4
  54. package/components/__tests__/LazyImage.spec.ts +121 -0
  55. package/components/__tests__/PromptRestore.test.ts +1 -65
  56. package/components/__tests__/RichTranslation.test.ts +115 -0
  57. package/components/fleet/FleetStatus.vue +4 -0
  58. package/components/fleet/dashboard/ResourcePanel.vue +2 -1
  59. package/components/form/ClusterAppearance.vue +5 -0
  60. package/components/form/FileImageSelector.vue +1 -1
  61. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  62. package/components/form/NameNsDescription.vue +1 -0
  63. package/components/form/Networking.vue +24 -19
  64. package/components/form/ProjectMemberEditor.vue +1 -1
  65. package/components/form/ResourceLabeledSelect.vue +22 -8
  66. package/components/form/ResourceTabs/index.vue +20 -0
  67. package/components/form/SecretSelector.vue +9 -0
  68. package/components/form/SelectOrCreateAuthSecret.vue +6 -3
  69. package/components/form/__tests__/Networking.test.ts +116 -0
  70. package/components/form/labeled-select-utils/labeled-select-pagination.ts +3 -38
  71. package/components/formatter/FleetApplicationSource.vue +25 -17
  72. package/components/formatter/PodImages.vue +1 -1
  73. package/components/formatter/__tests__/LiveDate.test.ts +10 -2
  74. package/components/google/AccountAccess.vue +44 -46
  75. package/components/nav/Favorite.vue +4 -0
  76. package/components/nav/Group.vue +4 -1
  77. package/components/nav/NotificationCenter/Notification.vue +1 -27
  78. package/components/nav/WindowManager/index.vue +3 -3
  79. package/composables/resources.ts +2 -2
  80. package/config/labels-annotations.js +3 -2
  81. package/config/pagination-table-headers.js +8 -1
  82. package/config/product/explorer.js +27 -2
  83. package/config/product/manager.js +0 -1
  84. package/config/query-params.js +10 -0
  85. package/config/router/routes.js +21 -1
  86. package/config/system-namespaces.js +1 -1
  87. package/config/table-headers.js +30 -1
  88. package/config/types.js +1 -1
  89. package/config/version.js +1 -1
  90. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +11 -0
  91. package/detail/__tests__/workload.test.ts +164 -0
  92. package/detail/configmap.vue +33 -75
  93. package/detail/projectsecret.vue +11 -0
  94. package/detail/provisioning.cattle.io.cluster.vue +351 -369
  95. package/detail/secret.vue +49 -308
  96. package/detail/workload/index.vue +38 -21
  97. package/dialog/InstallExtensionDialog.vue +8 -5
  98. package/dialog/RotateEncryptionKeyDialog.vue +10 -30
  99. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  100. package/edit/auth/ldap/__tests__/config.test.ts +14 -0
  101. package/edit/auth/ldap/config.vue +24 -0
  102. package/edit/compliance.cattle.io.clusterscan.vue +1 -1
  103. package/edit/configmap.vue +4 -1
  104. package/edit/fleet.cattle.io.gitrepo.vue +5 -6
  105. package/edit/fleet.cattle.io.helmop.vue +78 -56
  106. package/edit/logging.banzaicloud.io.output/index.vue +1 -1
  107. package/edit/logging.banzaicloud.io.output/providers/awsElasticsearch.vue +5 -6
  108. package/edit/networking.k8s.io.ingress/Certificate.vue +20 -22
  109. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +8 -3
  110. package/edit/networking.k8s.io.ingress/Rule.vue +2 -5
  111. package/edit/networking.k8s.io.ingress/RulePath.vue +17 -11
  112. package/edit/networking.k8s.io.ingress/__tests__/Certificate.test.ts +165 -0
  113. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +11 -10
  114. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -3
  115. package/edit/networking.k8s.io.networkpolicy/index.vue +17 -17
  116. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +3 -2
  117. package/edit/provisioning.cattle.io.cluster/rke2.vue +123 -61
  118. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +9 -7
  119. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +22 -13
  120. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +10 -12
  121. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +39 -38
  122. package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +41 -19
  123. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +16 -3
  124. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +32 -33
  125. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +9 -10
  126. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +1 -3
  127. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +16 -9
  128. package/edit/secret/basic.vue +1 -0
  129. package/edit/secret/index.vue +126 -15
  130. package/edit/workload/index.vue +5 -14
  131. package/list/projectsecret.vue +345 -0
  132. package/list/provisioning.cattle.io.cluster.vue +1 -69
  133. package/list/secret.vue +109 -0
  134. package/machine-config/__tests__/vmwarevsphere.test.ts +5 -7
  135. package/machine-config/google.vue +9 -1
  136. package/machine-config/vmwarevsphere.vue +7 -17
  137. package/mixins/__tests__/brand.spec.ts +2 -2
  138. package/mixins/chart.js +0 -2
  139. package/mixins/create-edit-view/impl.js +10 -1
  140. package/mixins/resource-fetch-api-pagination.js +11 -12
  141. package/mixins/resource-fetch.js +3 -1
  142. package/models/__tests__/chart.test.ts +111 -80
  143. package/models/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  144. package/models/__tests__/node.test.ts +7 -63
  145. package/models/catalog.cattle.io.app.js +1 -1
  146. package/models/catalog.cattle.io.operation.js +1 -1
  147. package/models/chart.js +36 -20
  148. package/models/cloudcredential.js +2 -163
  149. package/models/cluster/node.js +7 -7
  150. package/models/cluster.x-k8s.io.machine.js +3 -3
  151. package/models/cluster.x-k8s.io.machinedeployment.js +11 -2
  152. package/models/compliance.cattle.io.clusterscan.js +2 -2
  153. package/models/configmap.js +4 -0
  154. package/models/constraints.gatekeeper.sh.constraint.js +1 -1
  155. package/models/fleet-application.js +0 -17
  156. package/models/fleet.cattle.io.cluster.js +2 -2
  157. package/models/fleet.cattle.io.gitrepo.js +15 -1
  158. package/models/fleet.cattle.io.helmop.js +26 -22
  159. package/models/management.cattle.io.setting.js +4 -0
  160. package/models/persistentvolumeclaim.js +1 -1
  161. package/models/pod.js +2 -2
  162. package/models/provisioning.cattle.io.cluster.js +39 -67
  163. package/models/rke.cattle.io.etcdsnapshot.js +1 -1
  164. package/models/secret.js +161 -2
  165. package/models/storage.k8s.io.storageclass.js +2 -2
  166. package/models/workload.js +3 -3
  167. package/package.json +11 -10
  168. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +1 -0
  169. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +4 -1
  170. package/pages/c/_cluster/apps/charts/__tests__/AppChartCardFooter.spec.js +41 -0
  171. package/pages/c/_cluster/apps/charts/chart.vue +422 -174
  172. package/pages/c/_cluster/apps/charts/index.vue +46 -35
  173. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  174. package/pages/c/_cluster/explorer/projectsecret.vue +24 -0
  175. package/pages/c/_cluster/fleet/__tests__/index.test.ts +608 -314
  176. package/pages/c/_cluster/fleet/index.vue +103 -45
  177. package/pages/c/_cluster/manager/cloudCredential/index.vue +2 -59
  178. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +10 -3
  179. package/pages/c/_cluster/uiplugins/index.vue +36 -25
  180. package/plugins/dashboard-store/__tests__/normalize.test.ts +223 -0
  181. package/plugins/dashboard-store/__tests__/resource-class.test.ts +191 -0
  182. package/plugins/dashboard-store/__tests__/utils/normalize-usecases.ts +1526 -0
  183. package/plugins/dashboard-store/actions.js +42 -22
  184. package/plugins/dashboard-store/normalize.js +29 -17
  185. package/plugins/dashboard-store/resource-class.js +83 -17
  186. package/plugins/steve/__tests__/getters.test.ts +1 -1
  187. package/plugins/steve/__tests__/subscribe.spec.ts +259 -1
  188. package/plugins/steve/getters.js +8 -2
  189. package/plugins/steve/resourceWatcher.js +10 -3
  190. package/plugins/steve/steve-pagination-utils.ts +14 -3
  191. package/plugins/steve/subscribe.js +192 -19
  192. package/plugins/steve/worker/web-worker.advanced.js +2 -0
  193. package/rancher-components/Card/Card.vue +0 -18
  194. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.test.ts +15 -0
  195. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +65 -0
  196. package/rancher-components/Pill/RcStatusBadge/index.ts +2 -0
  197. package/rancher-components/Pill/RcStatusBadge/types.ts +5 -0
  198. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.test.ts +33 -0
  199. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +75 -0
  200. package/rancher-components/Pill/RcStatusIndicator/index.ts +2 -0
  201. package/rancher-components/Pill/RcStatusIndicator/types.ts +7 -0
  202. package/rancher-components/Pill/types.ts +2 -0
  203. package/rancher-components/RcButton/RcButton.vue +1 -1
  204. package/rancher-components/RcDropdown/RcDropdown.test.ts +98 -0
  205. package/rancher-components/RcDropdown/RcDropdown.vue +5 -0
  206. package/rancher-components/RcDropdown/RcDropdownItem.vue +7 -1
  207. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +2 -1
  208. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +2 -1
  209. package/rancher-components/RcDropdown/useDropdownContext.ts +21 -0
  210. package/rancher-components/RcDropdown/useDropdownItem.ts +30 -1
  211. package/rancher-components/RcItemCard/RcItemCard.test.ts +20 -0
  212. package/rancher-components/RcItemCard/RcItemCard.vue +40 -6
  213. package/store/__tests__/catalog.test.ts +93 -1
  214. package/store/aws.js +19 -8
  215. package/store/catalog.js +8 -3
  216. package/types/kube/kube-api.ts +12 -0
  217. package/types/resources/settings.d.ts +1 -1
  218. package/types/shell/index.d.ts +643 -585
  219. package/types/store/pagination.types.ts +16 -6
  220. package/types/uiplugins.ts +73 -0
  221. package/utils/__tests__/back-off.test.ts +354 -0
  222. package/utils/__tests__/create-yaml.test.ts +235 -0
  223. package/utils/__tests__/kontainer.test.ts +19 -0
  224. package/utils/__tests__/uiplugins.test.ts +84 -0
  225. package/utils/back-off.ts +176 -0
  226. package/utils/create-yaml.js +103 -9
  227. package/utils/dynamic-importer.js +8 -0
  228. package/utils/kontainer.ts +3 -5
  229. package/utils/pagination-utils.ts +18 -0
  230. package/utils/style.ts +3 -0
  231. package/utils/uiplugins.ts +29 -2
  232. package/utils/validators/__tests__/setting.test.js +92 -0
  233. package/utils/validators/formRules/__tests__/index.test.ts +88 -7
  234. package/utils/validators/formRules/index.ts +83 -8
  235. package/utils/validators/setting.js +17 -0
  236. package/cloud-credential/__tests__/harvester.test.ts +0 -18
  237. package/components/ResourceDetail/__tests__/index.test.ts +0 -135
  238. package/components/ResourceDetail/legacy.vue +0 -562
  239. package/components/formatter/CloudCredExpired.vue +0 -69
  240. package/models/etcdbackup.js +0 -45
  241. package/pages/explorer/resource/detail/configmap.vue +0 -42
  242. package/pages/explorer/resource/detail/secret.vue +0 -50
  243. package/utils/aws.js +0 -0
@@ -7,6 +7,7 @@ import CreateEditView from '@shell/mixins/create-edit-view';
7
7
  import FormValidation from '@shell/mixins/form-validation';
8
8
  import { normalizeName } from '@shell/utils/kube';
9
9
  import AccountAccess from '@shell/components/google/AccountAccess.vue';
10
+ import { handleConflict } from '@shell/plugins/dashboard-store/normalize';
10
11
 
11
12
  import {
12
13
  CAPI,
@@ -170,7 +171,7 @@ export default {
170
171
  this.setAgentConfiguration();
171
172
  },
172
173
 
173
- data() {
174
+ beforeCreate() {
174
175
  if (!this.value.spec.rkeConfig) {
175
176
  this.value.spec.rkeConfig = {};
176
177
  }
@@ -217,15 +218,16 @@ export default {
217
218
  if (!this.value.spec.rkeConfig.machineSelectorConfig?.length) {
218
219
  this.value.spec.rkeConfig.machineSelectorConfig = [{ config: {} }];
219
220
  }
221
+ },
220
222
 
221
- const truncateLimit = this.value.defaultHostnameLengthLimit || 0;
222
-
223
+ data() {
223
224
  return {
224
225
  loadedOnce: false,
225
226
  lastIdx: 0,
226
227
  allPSAs: [],
227
228
  credentialId: '',
228
229
  credential: null,
230
+ initialMachinePoolsValues: {},
229
231
  machinePools: null,
230
232
  rke2Versions: null,
231
233
  k3sVersions: null,
@@ -255,7 +257,7 @@ export default {
255
257
  }],
256
258
  harvesterVersionRange: {},
257
259
  complianceOverride: false,
258
- truncateLimit,
260
+ truncateLimit: this.value.defaultHostnameLengthLimit || 0,
259
261
  busy: false,
260
262
  machinePoolValidation: {}, // map of validation states for each machine pool
261
263
  machinePoolErrors: {},
@@ -271,13 +273,14 @@ export default {
271
273
  isAuthenticated: this.provider !== GOOGLE || this.mode === _EDIT,
272
274
  projectId: null,
273
275
  REGISTRIES_TAB_NAME,
274
- labelForAddon
275
-
276
+ labelForAddon,
277
+ etcdConfigValid: true,
276
278
  };
277
279
  },
278
280
 
279
281
  computed: {
280
282
  ...mapGetters({ features: 'features/get' }),
283
+
281
284
  isActiveTabRegistries() {
282
285
  return this.activeTab?.selectedName === REGISTRIES_TAB_NAME;
283
286
  },
@@ -856,7 +859,16 @@ export default {
856
859
  set(newValue) {
857
860
  this.$emit('update:value', newValue);
858
861
  }
859
- }
862
+ },
863
+ hideFooter() {
864
+ return this.needCredential && !this.credential;
865
+ },
866
+
867
+ overallFormValidationPassed() {
868
+ return this.validationPassed &&
869
+ this.fvFormIsValid &&
870
+ this.etcdConfigValid;
871
+ },
860
872
  },
861
873
 
862
874
  watch: {
@@ -946,6 +958,7 @@ export default {
946
958
  this.registerBeforeHook(this.setRegistryConfig, 'set-registry-config');
947
959
  this.registerBeforeHook(this.handleVsphereCpiSecret, 'sync-vsphere-cpi');
948
960
  this.registerBeforeHook(this.handleVsphereCsiSecret, 'sync-vsphere-csi');
961
+ this.registerBeforeHook(this.setHarvesterChartValues, 'set-harvester-chart-values');
949
962
  this.registerAfterHook(this.cleanupMachinePools, 'cleanup-machine-pools');
950
963
  this.registerAfterHook(this.saveRoleBindings, 'save-role-bindings');
951
964
 
@@ -1214,7 +1227,7 @@ export default {
1214
1227
  // @TODO what if the pool is missing?
1215
1228
  const id = `pool${ ++this.lastIdx }`;
1216
1229
 
1217
- out.push({
1230
+ const poolData = {
1218
1231
  id,
1219
1232
  remove: false,
1220
1233
  create: false,
@@ -1222,7 +1235,15 @@ export default {
1222
1235
  pool: clone(pool),
1223
1236
  config: config ? await this.$store.dispatch('management/clone', { resource: config }) : null,
1224
1237
  configMissing
1225
- });
1238
+ };
1239
+
1240
+ // add data to machine pools array
1241
+ out.push(poolData);
1242
+
1243
+ // but we also store the initial data so that we can handle conflicts
1244
+ if (poolData?.config?.id) {
1245
+ this.initialMachinePoolsValues[poolData.config.id] = structuredClone(poolData.config);
1246
+ }
1226
1247
  }
1227
1248
  }
1228
1249
 
@@ -1317,17 +1338,25 @@ export default {
1317
1338
  const _latestConfig = await this.$store.dispatch('management/request', { url: `/v1/${ machinePool.config.type }s/${ machinePool.config.id }` });
1318
1339
  const latestConfig = await this.$store.dispatch('management/create', _latestConfig);
1319
1340
 
1320
- const clonedCurrentConfig = await this.$store.dispatch('management/clone', { resource: machinePool.config });
1321
- const clonedLatestConfig = await this.$store.dispatch('management/clone', { resource: latestConfig });
1322
-
1323
- // We don't allow the user to edit any of the fields in metadata from the UI so it's safe to override it with the
1324
- // metadata defined by the latest backend value. This is primarily used to ensure the resourceVersion is up to date.
1325
- delete clonedCurrentConfig.metadata;
1341
+ const _initialMachinePoolValue = this.initialMachinePoolsValues[machinePool?.config?.id] || {};
1342
+ const initialMachinePoolValue = await this.$store.dispatch('management/create', _initialMachinePoolValue);
1343
+
1344
+ // if there's the initial machine pool config, we are in a good position to apply the handleConflict function
1345
+ // to deal with out-of-sync data between machinePools configs. This also mutates the data inside machinePool.config through object reference
1346
+ const conflict = await handleConflict(
1347
+ initialMachinePoolValue,
1348
+ machinePool.config,
1349
+ latestConfig,
1350
+ {
1351
+ dispatch: this.$store.dispatch,
1352
+ getters: this.$store.getters
1353
+ },
1354
+ 'management'
1355
+ );
1326
1356
 
1327
- if (this.provider === VMWARE_VSPHERE || this.provider === GOOGLE) {
1328
- machinePool.config = mergeWithReplace(clonedLatestConfig, clonedCurrentConfig, { mutateOriginal: true });
1329
- } else {
1330
- machinePool.config = merge(clonedLatestConfig, clonedCurrentConfig);
1357
+ // if there's conflicts, throw Error stops save process and surfaces error to user
1358
+ if (conflict) {
1359
+ throw Error(conflict);
1331
1360
  }
1332
1361
  }
1333
1362
  },
@@ -1536,41 +1565,7 @@ export default {
1536
1565
  }
1537
1566
 
1538
1567
  try {
1539
- const clusterId = get(this.credential, 'decodedData.clusterId') || '';
1540
-
1541
1568
  this.applyChartValues(this.value.spec.rkeConfig);
1542
-
1543
- const isUpgrade = this.isEdit && this.liveValue?.spec?.kubernetesVersion !== this.value?.spec?.kubernetesVersion;
1544
-
1545
- if (this.agentConfig?.['cloud-provider-name'] === HARVESTER && clusterId && (this.isCreate || isUpgrade)) {
1546
- const namespace = this.machinePools?.[0]?.config?.vmNamespace;
1547
-
1548
- const res = await this.$store.dispatch('management/request', {
1549
- url: `/k8s/clusters/${ clusterId }/v1/harvester/kubeconfig`,
1550
- method: 'POST',
1551
- data: {
1552
- csiClusterRoleName: 'harvesterhci.io:csi-driver',
1553
- clusterRoleName: 'harvesterhci.io:cloudprovider',
1554
- namespace,
1555
- serviceAccountName: this.value.metadata.name,
1556
- },
1557
- });
1558
-
1559
- const kubeconfig = res.data;
1560
-
1561
- const harvesterKubeconfigSecret = await this.createKubeconfigSecret(kubeconfig);
1562
-
1563
- this.agentConfig['cloud-provider-config'] = `secret://fleet-default:${ harvesterKubeconfigSecret?.metadata?.name }`;
1564
-
1565
- if (this.isCreate) {
1566
- set(this.chartValues, `${ HARVESTER_CLOUD_PROVIDER }.global.cattle.clusterName`, this.value.metadata.name);
1567
- }
1568
-
1569
- const distroSubdir = this.value?.spec?.kubernetesVersion?.includes('k3s') ? DEFAULT_SUBDIRS.K8S_DISTRO_K3S : DEFAULT_SUBDIRS.K8S_DISTRO_RKE2;
1570
- const distroRoot = this.value?.spec?.rkeConfig?.dataDirectories?.k8sDistro?.length ? this.value?.spec?.rkeConfig?.dataDirectories?.k8sDistro : `${ DEFAULT_COMMON_BASE_PATH }/${ distroSubdir }`;
1571
-
1572
- set(this.chartValues, `${ HARVESTER_CLOUD_PROVIDER }.cloudConfigPath`, `${ distroRoot }/etc/config-files/cloud-provider-config`);
1573
- }
1574
1569
  } catch (err) {
1575
1570
  this.errors.push(err);
1576
1571
 
@@ -1617,6 +1612,62 @@ export default {
1617
1612
  }
1618
1613
  },
1619
1614
 
1615
+ async setHarvesterChartValues() {
1616
+ const isHarvester = this.agentConfig?.['cloud-provider-name'] === HARVESTER;
1617
+
1618
+ if (!isHarvester) {
1619
+ return;
1620
+ }
1621
+ try {
1622
+ const clusterId = get(this.credential, 'decodedData.clusterId') || '';
1623
+ const isUpgrade = this.isEdit && this.liveValue?.spec?.kubernetesVersion !== this.value?.spec?.kubernetesVersion;
1624
+
1625
+ if (!this.value?.metadata?.name) {
1626
+ const err = this.t('cluster.harvester.kubeconfigSecret.nameRequired');
1627
+
1628
+ throw new Error(err);
1629
+ }
1630
+
1631
+ if (clusterId && (this.isCreate || isUpgrade)) {
1632
+ const namespace = this.machinePools?.[0]?.config?.vmNamespace;
1633
+
1634
+ const res = await this.$store.dispatch('management/request', {
1635
+ url: `/k8s/clusters/${ clusterId }/v1/harvester/kubeconfig`,
1636
+ method: 'POST',
1637
+ data: {
1638
+ csiClusterRoleName: 'harvesterhci.io:csi-driver',
1639
+ clusterRoleName: 'harvesterhci.io:cloudprovider',
1640
+ namespace,
1641
+ serviceAccountName: this.value.metadata.name,
1642
+ },
1643
+ });
1644
+
1645
+ const kubeconfig = res.data;
1646
+
1647
+ const harvesterKubeconfigSecret = await this.createKubeconfigSecret(kubeconfig);
1648
+
1649
+ this.agentConfig['cloud-provider-config'] = `secret://fleet-default:${ harvesterKubeconfigSecret?.metadata?.name }`;
1650
+
1651
+ const harvesterCloudProviderKey = this.chartVersionKey(HARVESTER_CLOUD_PROVIDER);
1652
+
1653
+ if (this.isCreate) {
1654
+ set(this.userChartValues, `'${ harvesterCloudProviderKey }'.global.cattle.clusterName`, this.value.metadata.name);
1655
+ }
1656
+
1657
+ const distroSubdir = this.value?.spec?.kubernetesVersion?.includes('k3s') ? DEFAULT_SUBDIRS.K8S_DISTRO_K3S : DEFAULT_SUBDIRS.K8S_DISTRO_RKE2;
1658
+ const distroRoot = this.value?.spec?.rkeConfig?.dataDirectories?.k8sDistro?.length ? this.value?.spec?.rkeConfig?.dataDirectories?.k8sDistro : `${ DEFAULT_COMMON_BASE_PATH }/${ distroSubdir }`;
1659
+
1660
+ set(this.userChartValues, `'${ harvesterCloudProviderKey }'.cloudConfigPath`, `${ distroRoot }/etc/config-files/cloud-provider-config`);
1661
+ }
1662
+ } catch (e) {
1663
+ const cause = e.errors ? e.errors.join('; ') : e?.message;
1664
+ const msg = this.t('cluster.harvester.kubeconfigSecret.error', { err: cause });
1665
+
1666
+ this.errors.push(msg);
1667
+ throw new Error(msg);
1668
+ }
1669
+ },
1670
+
1620
1671
  // create a secret to reference the harvester cluster kubeconfig in rkeConfig
1621
1672
  async createKubeconfigSecret(kubeconfig = '') {
1622
1673
  const clusterName = this.value.metadata.name;
@@ -1806,12 +1857,12 @@ export default {
1806
1857
 
1807
1858
  const hasMirrorsOrAuthConfig = Object.keys(regs.configs).length > 0 || Object.keys(regs.mirrors).length > 0;
1808
1859
 
1809
- if (this.registryHost || registrySecret || hasMirrorsOrAuthConfig) {
1860
+ if (this.registryHost || registrySecret) {
1810
1861
  this.showCustomRegistryInput = true;
1862
+ }
1811
1863
 
1812
- if (hasMirrorsOrAuthConfig) {
1813
- this.showCustomRegistryAdvancedInput = true;
1814
- }
1864
+ if (hasMirrorsOrAuthConfig) {
1865
+ this.showCustomRegistryAdvancedInput = true;
1815
1866
  }
1816
1867
  },
1817
1868
 
@@ -1893,6 +1944,7 @@ export default {
1893
1944
 
1894
1945
  charts.forEach((name) => {
1895
1946
  const key = this.chartVersionKey(name);
1947
+
1896
1948
  const userValues = this.userChartValues[key];
1897
1949
 
1898
1950
  if (userValues) {
@@ -2126,7 +2178,7 @@ export default {
2126
2178
 
2127
2179
  handleTabChange(data) {
2128
2180
  this.activeTab = data;
2129
- }
2181
+ },
2130
2182
  }
2131
2183
  };
2132
2184
  </script>
@@ -2142,7 +2194,7 @@ export default {
2142
2194
  v-else
2143
2195
  ref="cruresource"
2144
2196
  :mode="mode"
2145
- :validation-passed="validationPassed && fvFormIsValid"
2197
+ :validation-passed="overallFormValidationPassed"
2146
2198
  :resource="value"
2147
2199
  :errors="errors"
2148
2200
  :cancel-event="true"
@@ -2174,7 +2226,10 @@ export default {
2174
2226
  @error="e=>errors.push(e)"
2175
2227
  @cancel-credential="cancelCredential"
2176
2228
  />
2177
- <div v-else>
2229
+ <div
2230
+ v-else
2231
+ class="authenticated"
2232
+ >
2178
2233
  <SelectCredential
2179
2234
  v-if="needCredential"
2180
2235
  v-model:value="credentialId"
@@ -2376,6 +2431,7 @@ export default {
2376
2431
  @update:value="$emit('input', $event)"
2377
2432
  @s3-backup-changed="handleS3BackupChanged"
2378
2433
  @config-etcd-expose-metrics-changed="handleConfigEtcdExposeMetricsChanged"
2434
+ @etcd-validation-changed="(val)=>etcdConfigValid = val"
2379
2435
  />
2380
2436
  </Tab>
2381
2437
 
@@ -2568,7 +2624,7 @@ export default {
2568
2624
  />
2569
2625
  </div>
2570
2626
  <template
2571
- v-if="needCredential && !credentialId"
2627
+ v-if="hideFooter"
2572
2628
  #form-footer
2573
2629
  >
2574
2630
  <div><!-- Hide the outer footer --></div>
@@ -2577,6 +2633,12 @@ export default {
2577
2633
  </template>
2578
2634
 
2579
2635
  <style lang="scss" scoped>
2636
+ .authenticated {
2637
+ display:flex;
2638
+ flex-direction: column;
2639
+ flex-grow: 1;
2640
+ }
2641
+
2580
2642
  .min-height {
2581
2643
  min-height: 40em;
2582
2644
  }
@@ -81,6 +81,14 @@ export default {
81
81
  },
82
82
 
83
83
  data() {
84
+ return {
85
+ defaultAffinity: {},
86
+ affinitySetting: DEFAULT,
87
+ nodeAffinity: {}
88
+ };
89
+ },
90
+
91
+ created() {
84
92
  const nodeAffinity = this.value?.overrideAffinity?.nodeAffinity;
85
93
  const podAffinity = this.value?.overrideAffinity?.podAffinity;
86
94
  const podAntiAffinity = this.value?.overrideAffinity?.podAntiAffinity;
@@ -93,14 +101,8 @@ export default {
93
101
  hasAffinityPopulated = true;
94
102
  }
95
103
 
96
- return {
97
- defaultAffinity: {},
98
- affinitySetting: hasAffinityPopulated ? CUSTOM : DEFAULT,
99
- nodeAffinity: {}
100
- };
101
- },
104
+ this.affinitySetting = hasAffinityPopulated ? CUSTOM : DEFAULT;
102
105
 
103
- created() {
104
106
  this.ensureValue();
105
107
  },
106
108
 
@@ -3,7 +3,6 @@ import difference from 'lodash/difference';
3
3
  import { mapGetters } from 'vuex';
4
4
  import CreateEditView from '@shell/mixins/create-edit-view';
5
5
  import FormValidation from '@shell/mixins/form-validation';
6
-
7
6
  import { set, get } from '@shell/utils/object';
8
7
  import { Banner } from '@components/Banner';
9
8
  import { Checkbox } from '@components/Form/Checkbox';
@@ -11,7 +10,6 @@ import LabeledSelect from '@shell/components/form/LabeledSelect';
11
10
  import YamlEditor from '@shell/components/YamlEditor';
12
11
  import { LEGACY } from '@shell/store/features';
13
12
  import semver from 'semver';
14
- import { _CREATE, _EDIT } from '@shell/config/query-params';
15
13
 
16
14
  const HARVESTER = 'harvester';
17
15
 
@@ -119,12 +117,23 @@ export default {
119
117
  }
120
118
  },
121
119
 
120
+ data() {
121
+ return {
122
+ showEnablingComplianceWarning: false,
123
+ initialAgentProfile: this.value.agentConfig?.profile || ''
124
+ };
125
+ },
126
+
122
127
  watch: {
123
128
  selectedVersion(neu, old) {
124
129
  if (neu?.value !== old?.value && this.ciliumIpv6) {
125
130
  // Re-assign so that the setter updates the structure for the new k8s version if needed
126
131
  this.ciliumIpv6 = !!this.ciliumIpv6;
127
132
  }
133
+ },
134
+
135
+ 'agentConfig.profile'(newValue) {
136
+ this.showEnablingComplianceWarning = this.provider === 'custom' && this.isEdit && !!newValue && newValue !== this.initialAgentProfile;
128
137
  }
129
138
  },
130
139
 
@@ -382,10 +391,6 @@ export default {
382
391
  }
383
392
  },
384
393
 
385
- isEdit() {
386
- return this.mode === _EDIT;
387
- },
388
-
389
394
  canNotEditCloudProvider() {
390
395
  if (!this.isEdit) {
391
396
  return false;
@@ -405,14 +410,14 @@ export default {
405
410
  * Display warning about unsupported Azure provider if k8s >= 1.30
406
411
  */
407
412
  showCloudProviderUnsupportedAzureWarning() {
408
- return this.showCloudProvider && this.mode === _CREATE && this.isAzureProviderUnsupported;
413
+ return this.showCloudProvider && this.isCreate && this.isAzureProviderUnsupported;
409
414
  },
410
415
 
411
416
  /**
412
417
  * Display warning about Azure provider migration from k8s versions >= 1.27 to External provider
413
418
  */
414
419
  showCloudProviderMigrateAzureWarning() {
415
- return this.showCloudProvider && this.mode === _EDIT && this.canAzureMigrateOnEdit;
420
+ return this.showCloudProvider && this.isEdit && this.canAzureMigrateOnEdit;
416
421
  }
417
422
  },
418
423
 
@@ -579,11 +584,8 @@ export default {
579
584
  <p v-clean-html="t('cluster.rke2.banner.complianceUnsupported', {profile: serverConfig.profile || agentConfig.profile}, true)" />
580
585
  </Banner>
581
586
 
582
- <div class="row mb-10">
583
- <div
584
- v-if="showComplianceProfile"
585
- class="col span-6"
586
- >
587
+ <div v-if="showComplianceProfile">
588
+ <div class="col span-6">
587
589
  <LabeledSelect
588
590
  v-if="serverArgs && serverArgs.profile && serverConfig"
589
591
  v-model:value="serverConfig.profile"
@@ -602,6 +604,13 @@ export default {
602
604
  @update:value="$emit('compliance-changed')"
603
605
  />
604
606
  </div>
607
+ <div class="row mb-10">
608
+ <Banner
609
+ v-if="showEnablingComplianceWarning"
610
+ color="warning"
611
+ label-key="cluster.rke2.compliance.warning"
612
+ />
613
+ </div>
605
614
  </div>
606
615
 
607
616
  <template v-if="hasComplianceOverride">
@@ -44,23 +44,21 @@ export default {
44
44
  },
45
45
  },
46
46
  data() {
47
- let dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.DEFAULT;
48
- let k8sDistroSubDir = DEFAULT_SUBDIRS.K8S_DISTRO_RKE2;
49
-
47
+ return {
48
+ DATA_DIR_RADIO_OPTIONS,
49
+ dataConfigRadioValue: DATA_DIR_RADIO_OPTIONS.DEFAULT,
50
+ k8sDistroSubDir: DEFAULT_SUBDIRS.K8S_DISTRO_RKE2,
51
+ commonConfig: '',
52
+ };
53
+ },
54
+ created() {
50
55
  if (this.k8sVersion && this.k8sVersion.includes('k3s')) {
51
- k8sDistroSubDir = DEFAULT_SUBDIRS.K8S_DISTRO_K3S;
56
+ this.k8sDistroSubDir = DEFAULT_SUBDIRS.K8S_DISTRO_K3S;
52
57
  }
53
58
 
54
59
  if (this.mode !== _CREATE) {
55
- dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.CUSTOM;
60
+ this.dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.CUSTOM;
56
61
  }
57
-
58
- return {
59
- DATA_DIR_RADIO_OPTIONS,
60
- dataConfigRadioValue,
61
- k8sDistroSubDir,
62
- commonConfig: '',
63
- };
64
62
  },
65
63
  watch: {
66
64
  commonConfig(neu) {
@@ -89,47 +89,10 @@ export default {
89
89
  },
90
90
 
91
91
  data() {
92
- const parseDuration = (duration) => {
93
- // The back end stores the timeout in Duration format, for example, "42d31h10m30s".
94
- // Here we convert that string to an integer and return the duration as seconds.
95
- const splitStr = duration.split(/([a-z])/);
96
-
97
- const durationsAsSeconds = splitStr.reduce((old, neu, idx) => {
98
- const parsed = parseInt(neu);
99
-
100
- if ( isNaN(parsed) ) {
101
- return old;
102
- }
103
-
104
- const interval = splitStr[(idx + 1)];
105
-
106
- switch (interval) {
107
- case 'd':
108
- old.push(parsed * 24 * 60 * 60);
109
- break;
110
- case 'h':
111
- old.push(parsed * 60 * 60);
112
- break;
113
- case 'm':
114
- old.push(parsed * 60);
115
- break;
116
- case 's':
117
- old.push(parsed);
118
- break;
119
- default:
120
- break;
121
- }
122
-
123
- return old;
124
- }, []);
125
-
126
- return durationsAsSeconds.reduce((old, neu) => old + neu);
127
- };
128
-
129
92
  return {
130
93
  uuid: randomStr(),
131
94
 
132
- unhealthyNodeTimeoutInteger: this.value.pool.unhealthyNodeTimeout ? parseDuration(this.value.pool.unhealthyNodeTimeout) : 0,
95
+ unhealthyNodeTimeoutInteger: 0,
133
96
 
134
97
  validationErrors: [],
135
98
 
@@ -183,6 +146,8 @@ export default {
183
146
  * On creation, ensure that the pool is marked valid - custom machine pools can emit further validation events
184
147
  */
185
148
  created() {
149
+ this.unhealthyNodeTimeoutInteger = this.value.pool.unhealthyNodeTimeout ? this.parseDuration(this.value.pool.unhealthyNodeTimeout) : 0;
150
+
186
151
  this.$emit('validationChanged', true);
187
152
  },
188
153
 
@@ -192,6 +157,42 @@ export default {
192
157
  },
193
158
 
194
159
  methods: {
160
+ parseDuration(duration) {
161
+ // The back end stores the timeout in Duration format, for example, "42d31h10m30s".
162
+ // Here we convert that string to an integer and return the duration as seconds.
163
+ const splitStr = duration.split(/([a-z])/);
164
+
165
+ const durationsAsSeconds = splitStr.reduce((old, neu, idx) => {
166
+ const parsed = parseInt(neu);
167
+
168
+ if ( isNaN(parsed) ) {
169
+ return old;
170
+ }
171
+
172
+ const interval = splitStr[(idx + 1)];
173
+
174
+ switch (interval) {
175
+ case 'd':
176
+ old.push(parsed * 24 * 60 * 60);
177
+ break;
178
+ case 'h':
179
+ old.push(parsed * 60 * 60);
180
+ break;
181
+ case 'm':
182
+ old.push(parsed * 60);
183
+ break;
184
+ case 's':
185
+ old.push(parsed);
186
+ break;
187
+ default:
188
+ break;
189
+ }
190
+
191
+ return old;
192
+ }, []);
193
+
194
+ return durationsAsSeconds.reduce((old, neu) => old + neu);
195
+ },
195
196
  emitError(e) {
196
197
  this.$emit('error', e);
197
198
  },