@rancher/shell 0.3.24 → 0.3.26

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 (115) hide show
  1. package/assets/styles/themes/_light.scss +1 -1
  2. package/assets/translations/en-us.yaml +36 -7
  3. package/assets/translations/zh-hans.yaml +1 -1
  4. package/components/ClusterIconMenu.vue +143 -0
  5. package/components/CruResource.vue +10 -1
  6. package/components/ExplorerProjectsNamespaces.vue +11 -1
  7. package/components/FixedBanner.vue +17 -1
  8. package/components/Markdown.vue +1 -1
  9. package/components/Questions/__tests__/Yaml.test.ts +3 -2
  10. package/components/SortableTable/index.vue +3 -2
  11. package/components/__tests__/ProjectRow.test.ts +63 -0
  12. package/components/auth/RoleDetailEdit.vue +19 -2
  13. package/components/auth/__tests__/RoleDetailEdit.test.ts +41 -0
  14. package/components/auth/login/saml.vue +12 -1
  15. package/components/form/LabeledSelect.vue +12 -5
  16. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  17. package/components/form/Members/MembershipEditor.vue +6 -1
  18. package/components/form/ResourceQuota/ProjectRow.vue +6 -2
  19. package/components/form/__tests__/KeyValue.test.ts +6 -3
  20. package/components/form/__tests__/LabeledSelect.test.ts +18 -0
  21. package/components/formatter/PodsUsage.vue +11 -36
  22. package/components/formatter/PrincipalGroupBindings.vue +8 -5
  23. package/components/formatter/__tests__/PodsUsage.test.ts +36 -19
  24. package/components/nav/Group.vue +25 -27
  25. package/components/nav/Header.vue +12 -5
  26. package/components/nav/Pinned.vue +47 -0
  27. package/components/nav/TopLevelMenu.vue +233 -60
  28. package/components/nav/Type.vue +57 -3
  29. package/config/home-links.js +1 -1
  30. package/config/product/istio.js +15 -5
  31. package/config/router.js +3 -9
  32. package/config/table-headers.js +5 -6
  33. package/config/uiplugins.js +1 -0
  34. package/core/plugin-helpers.js +3 -0
  35. package/core/types.ts +6 -1
  36. package/creators/app/files/.vscode/settings.json +0 -1
  37. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +118 -0
  38. package/detail/autoscaling.horizontalpodautoscaler/index.vue +4 -4
  39. package/detail/provisioning.cattle.io.cluster.vue +7 -5
  40. package/edit/__tests__/management.cattle.io.clusterroletemplatebinding.test.ts +58 -0
  41. package/edit/__tests__/namespace.test.ts +5 -3
  42. package/edit/management.cattle.io.clusterroletemplatebinding.vue +3 -11
  43. package/edit/namespace.vue +8 -4
  44. package/edit/provisioning.cattle.io.cluster/Basics.vue +662 -0
  45. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +6 -0
  46. package/edit/provisioning.cattle.io.cluster/DrainOptions.vue +13 -8
  47. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -2
  48. package/edit/provisioning.cattle.io.cluster/MemberRoles.vue +40 -0
  49. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +237 -0
  50. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.tests.ts +71 -23
  51. package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +52 -0
  52. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -142
  53. package/edit/provisioning.cattle.io.cluster/rke2.vue +211 -599
  54. package/edit/workload/storage/__tests__/Storage.test.ts +2 -2
  55. package/edit/workload/storage/persistentVolumeClaim/__tests__/persistentvolumeclaim.test.ts +36 -0
  56. package/edit/workload/storage/persistentVolumeClaim/persistentvolumeclaim.vue +15 -7
  57. package/initialize/index.js +5 -5
  58. package/layouts/default.vue +6 -6
  59. package/layouts/home.vue +6 -2
  60. package/layouts/plain.vue +9 -2
  61. package/list/fleet.cattle.io.cluster.vue +2 -2
  62. package/list/management.cattle.io.feature.vue +1 -1
  63. package/machine-config/vmwarevsphere.vue +48 -7
  64. package/mixins/brand.js +0 -8
  65. package/mixins/child-hook.js +2 -2
  66. package/mixins/create-edit-view/impl.js +3 -3
  67. package/models/__tests__/management.cattle.io.node.ts +96 -0
  68. package/models/__tests__/node.ts +74 -0
  69. package/models/cluster/node.js +6 -5
  70. package/models/cluster.x-k8s.io.machinedeployment.js +2 -2
  71. package/models/management.cattle.io.cluster.js +22 -1
  72. package/models/management.cattle.io.clusterroletemplatebinding.js +3 -3
  73. package/models/management.cattle.io.globalrole.js +17 -2
  74. package/models/management.cattle.io.node.js +6 -4
  75. package/models/management.cattle.io.projectroletemplatebinding.js +3 -3
  76. package/models/management.cattle.io.roletemplate.js +17 -2
  77. package/package.json +2 -6
  78. package/pages/about.vue +2 -0
  79. package/pages/auth/setup.vue +5 -4
  80. package/pages/c/_cluster/monitoring/index.vue +8 -3
  81. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +9 -66
  82. package/pages/c/_cluster/uiplugins/CatalogList/CatalogUninstallDialog.vue +182 -0
  83. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +15 -32
  84. package/pages/c/_cluster/uiplugins/UninstallDialog.vue +8 -46
  85. package/pages/c/_cluster/uiplugins/index.vue +64 -64
  86. package/pages/diagnostic.vue +0 -39
  87. package/pages/home.vue +1 -1
  88. package/plugins/dashboard-store/normalize.js +4 -4
  89. package/plugins/int-number.js +5 -2
  90. package/plugins/positive-int-number.js +19 -0
  91. package/plugins/steve/__tests__/getters.spec.ts +15 -0
  92. package/plugins/steve/getters.js +22 -10
  93. package/rancher-components/Form/LabeledInput/LabeledInput.vue +0 -8
  94. package/rancher-components/Form/Radio/RadioButton.test.ts +3 -7
  95. package/scripts/extension/helm/charts/ui-plugin-server/Chart.yaml +2 -2
  96. package/store/index.js +4 -0
  97. package/store/prefs.js +1 -0
  98. package/types/shell/index.d.ts +13 -4
  99. package/utils/__tests__/cluster.test.ts +55 -0
  100. package/utils/__tests__/object.test.ts +21 -2
  101. package/utils/cluster.js +47 -1
  102. package/utils/object.js +12 -5
  103. package/utils/validators/formRules/__tests__/index.test.ts +13 -1
  104. package/utils/validators/formRules/index.ts +4 -0
  105. package/utils/validators/role-template.js +9 -1
  106. package/utils/version.js +1 -1
  107. package/yarn-error.log +16 -16
  108. package/components/ClusterProviderIconMenu.vue +0 -161
  109. package/content/docs/en-us/getting-started.md +0 -224
  110. package/content/docs/en-us/whats-new.md +0 -29
  111. package/content/docs/zh-hans/getting-started.md +0 -224
  112. package/content/docs/zh-hans/whats-new.md +0 -28
  113. package/pages/docs/_doc.vue +0 -345
  114. package/pages/docs/toc.js +0 -27
  115. package/plugins/console.js +0 -34
@@ -3,7 +3,6 @@ import difference from 'lodash/difference';
3
3
  import throttle from 'lodash/throttle';
4
4
  import isArray from 'lodash/isArray';
5
5
  import merge from 'lodash/merge';
6
- import { mapGetters } from 'vuex';
7
6
  import CreateEditView from '@shell/mixins/create-edit-view';
8
7
  import FormValidation from '@shell/mixins/form-validation';
9
8
  import { normalizeName } from '@shell/utils/kube';
@@ -29,7 +28,7 @@ import {
29
28
  import { allHash } from '@shell/utils/promise';
30
29
  import { sortBy } from '@shell/utils/sort';
31
30
 
32
- import { camelToTitle, nlToBr } from '@shell/utils/string';
31
+ import { camelToTitle } from '@shell/utils/string';
33
32
  import { compare, sortable } from '@shell/utils/version';
34
33
  import { isHarvesterSatisfiesVersion } from '@shell/utils/cluster';
35
34
  import * as VERSION from '@shell/utils/version';
@@ -39,7 +38,7 @@ import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
39
38
  import { BadgeState } from '@components/BadgeState';
40
39
  import { Banner } from '@components/Banner';
41
40
  import { Checkbox } from '@components/Form/Checkbox';
42
- import CruResource from '@shell/components/CruResource';
41
+ import CruResource, { CONTEXT_HOOK_EDIT_YAML } from '@shell/components/CruResource';
43
42
  import { LabeledInput } from '@components/Form/LabeledInput';
44
43
  import LabeledSelect from '@shell/components/form/LabeledSelect';
45
44
  import Loading from '@shell/components/Loading';
@@ -52,9 +51,8 @@ import UnitInput from '@shell/components/form/UnitInput';
52
51
  import YamlEditor from '@shell/components/YamlEditor';
53
52
  import Questions from '@shell/components/Questions';
54
53
 
55
- import ClusterMembershipEditor, { canViewClusterMembershipEditor } from '@shell/components/form/Members/ClusterMembershipEditor';
54
+ import { canViewClusterMembershipEditor } from '@shell/components/form/Members/ClusterMembershipEditor';
56
55
  import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret';
57
- import { LEGACY } from '@shell/store/features';
58
56
  import semver from 'semver';
59
57
 
60
58
  import { SETTING } from '@shell/config/settings';
@@ -74,10 +72,8 @@ import { ELEMENTAL_SCHEMA_IDS, KIND, ELEMENTAL_CLUSTER_PROVIDER } from '../../co
74
72
  import AgentConfiguration from './AgentConfiguration';
75
73
  import { getApplicableExtensionEnhancements } from '@shell/core/plugin-helpers';
76
74
  import { ExtensionPoint, TabLocation } from '@shell/core/types';
77
-
78
- const PUBLIC = 'public';
79
- const PRIVATE = 'private';
80
- const ADVANCED = 'advanced';
75
+ import MemberRoles from '@shell/edit/provisioning.cattle.io.cluster/MemberRoles';
76
+ import Basics from '@shell/edit/provisioning.cattle.io.cluster/Basics';
81
77
 
82
78
  const HARVESTER = 'harvester';
83
79
  const HARVESTER_CLOUD_PROVIDER = 'harvester-cloud-provider';
@@ -115,7 +111,6 @@ export default {
115
111
  Banner,
116
112
  Checkbox,
117
113
  AgentConfiguration,
118
- ClusterMembershipEditor,
119
114
  CruResource,
120
115
  DrainOptions,
121
116
  LabeledInput,
@@ -136,6 +131,8 @@ export default {
136
131
  Tabbed,
137
132
  UnitInput,
138
133
  YamlEditor,
134
+ MemberRoles,
135
+ Basics
139
136
  },
140
137
 
141
138
  mixins: [CreateEditView, FormValidation],
@@ -210,7 +207,6 @@ export default {
210
207
  lastIdx: 0,
211
208
  allPSPs: null,
212
209
  allPSAs: [],
213
- nodeComponent: null,
214
210
  credentialId: '',
215
211
  credential: null,
216
212
  machinePools: null,
@@ -251,21 +247,14 @@ export default {
251
247
  truncateLimit,
252
248
  busy: false,
253
249
  machinePoolValidation: {}, // map of validation states for each machine pool
250
+ machinePoolErrors: {},
254
251
  allNamespaces: [],
255
- initialCloudProvider: this.value?.agentConfig?.['cloud-provider-name'],
252
+ initialCloudProvider: this.value?.agentConfig?.['cloud-provider-name'] || '',
256
253
  extensionTabs: getApplicableExtensionEnhancements(this, ExtensionPoint.TAB, TabLocation.CLUSTER_CREATE_RKE2, this.$route, this),
257
254
  };
258
255
  },
259
256
 
260
257
  computed: {
261
- ...mapGetters({ allCharts: 'catalog/charts' }),
262
- ...mapGetters(['currentCluster']),
263
- ...mapGetters({ features: 'features/get' }),
264
- ...mapGetters(['namespaces']),
265
-
266
- PUBLIC: () => PUBLIC,
267
- PRIVATE: () => PRIVATE,
268
- ADVANCED: () => ADVANCED,
269
258
 
270
259
  rkeConfig() {
271
260
  return this.value.spec.rkeConfig;
@@ -275,13 +264,6 @@ export default {
275
264
  return this.truncateLimit && this.truncateLimit !== NETBIOS_TRUNCATION_LENGTH;
276
265
  },
277
266
 
278
- /**
279
- * Check presence of PSPs as template or CLI creation
280
- */
281
- hasPsps() {
282
- return !!this.psps?.count;
283
- },
284
-
285
267
  isElementalCluster() {
286
268
  return this.provider === ELEMENTAL_CLUSTER_PROVIDER || this.value?.machineProvider?.toLowerCase() === KIND.MACHINE_INV_SELECTOR_TEMPLATES.toLowerCase();
287
269
  },
@@ -304,17 +286,6 @@ export default {
304
286
  return this.value.agentConfig;
305
287
  },
306
288
 
307
- /**
308
- * Define introduction of PSA and return need of PSA templates based on min k8s version
309
- */
310
- needsPSA() {
311
- const release = this.value?.spec?.kubernetesVersion || '';
312
- const version = release.match(/\d+/g);
313
- const isRequiredVersion = version?.length ? +version[0] > 1 || +version[1] >= 23 : false;
314
-
315
- return isRequiredVersion;
316
- },
317
-
318
289
  /**
319
290
  * Define PSP deprecation and restrict use of PSP based on min k8s version
320
291
  */
@@ -410,196 +381,6 @@ export default {
410
381
  return (this.value?.spec?.kubernetesVersion || '').includes('k3s');
411
382
  },
412
383
 
413
- profileOptions() {
414
- const out = (this.agentArgs?.profile?.options || []).map((x) => {
415
- return { label: x, value: x };
416
- });
417
-
418
- out.unshift({
419
- label: this.$store.getters['i18n/t']('cluster.rke2.cisProfile.option'),
420
- value: ''
421
- });
422
-
423
- return out;
424
- },
425
-
426
- /**
427
- * Allow to display override if PSA is needed and profile is set
428
- */
429
- hasCisOverride() {
430
- return (this.serverConfig?.profile || this.agentConfig?.profile) && this.needsPSA &&
431
- // Also check other cases on when to display the override
432
- this.hasPsaTemplates && this.showCisProfile && this.isCisSupported;
433
- },
434
-
435
- pspOptions() {
436
- if ( this.isK3s ) {
437
- return null;
438
- }
439
-
440
- const out = [{
441
- label: this.$store.getters['i18n/t']('cluster.rke2.defaultPodSecurityPolicyTemplateName.option'),
442
- value: ''
443
- }];
444
-
445
- if ( this.allPSPs ) {
446
- for ( const pspt of this.allPSPs ) {
447
- out.push({
448
- label: pspt.nameDisplay,
449
- value: pspt.id,
450
- });
451
- }
452
- }
453
-
454
- const cur = this.value.spec.defaultPodSecurityPolicyTemplateName;
455
-
456
- if ( cur && !out.find((x) => x.value === cur) ) {
457
- out.unshift({ label: `${ cur } (Current)`, value: cur });
458
- }
459
-
460
- return out;
461
- },
462
-
463
- /**
464
- * Disable PSA if CIS hardening is enabled, except override
465
- */
466
- isPsaDisabled() {
467
- const cisValue = this.agentConfig?.profile || this.serverConfig?.profile;
468
-
469
- return !(!cisValue || this.cisOverride) && this.hasPsaTemplates && this.isCisSupported;
470
- },
471
-
472
- /**
473
- * Get the default label for the PSA template option
474
- */
475
- defaultPsaOptionLabel() {
476
- const optionCase = !this.needsPSP && !this.isK3s ? 'default' : 'none';
477
-
478
- return this.$store.getters['i18n/t'](`cluster.rke2.defaultPodSecurityAdmissionConfigurationTemplateName.option.${ optionCase }`);
479
- },
480
-
481
- /**
482
- * Convert PSA templates into options, sorting and flagging if any selected
483
- */
484
- psaOptions() {
485
- if ( !this.needsPSA ) {
486
- return [];
487
- }
488
- const out = [{
489
- label: this.defaultPsaOptionLabel,
490
- value: ''
491
- }];
492
-
493
- if ( this.allPSAs ) {
494
- for ( const psa of this.allPSAs ) {
495
- out.push({
496
- label: psa.nameDisplay,
497
- value: psa.id,
498
- });
499
- }
500
- }
501
- const cur = this.value.spec.defaultPodSecurityAdmissionConfigurationTemplateName;
502
-
503
- if ( cur && !out.find((x) => x.value === cur) ) {
504
- out.unshift({ label: `${ cur } (Current)`, value: cur });
505
- }
506
-
507
- return out;
508
- },
509
-
510
- /**
511
- * Check if current CIS profile is required and listed in the options
512
- */
513
- isCisSupported() {
514
- const cisProfile = this.serverConfig.profile || this.agentConfig.profile;
515
-
516
- return !cisProfile || this.profileOptions.map((option) => option.value).includes(cisProfile);
517
- },
518
-
519
- disableOptions() {
520
- return this.serverArgs.disable.options.map((value) => {
521
- return {
522
- label: this.$store.getters['i18n/withFallback'](`cluster.${ this.isK3s ? 'k3s' : 'rke2' }.systemService."${ value }"`, null, value.replace(/^(rke2|rancher)-/, '')),
523
- value,
524
- };
525
- });
526
- },
527
-
528
- cloudProviderOptions() {
529
- const out = [{
530
- label: this.$store.getters['i18n/t']('cluster.rke2.cloudProvider.defaultValue.label'),
531
- value: '',
532
- }];
533
-
534
- const preferred = this.$store.getters['plugins/cloudProviderForDriver'](this.provider);
535
-
536
- for ( const opt of this.agentArgs['cloud-provider-name'].options ) {
537
- // If we don't have a preferred provider... show all options
538
- const showAllOptions = preferred === undefined;
539
- // If we have a preferred provider... only show default, preferred and external
540
- const isPreferred = opt === preferred;
541
- const isExternal = opt === 'external';
542
- let disabled = false;
543
-
544
- if ((this.isHarvesterExternalCredential || this.isHarvesterIncompatible) && isPreferred) {
545
- disabled = true;
546
- }
547
-
548
- if (showAllOptions || isPreferred || isExternal) {
549
- out.push({
550
- label: this.$store.getters['i18n/withFallback'](`cluster.cloudProvider."${ opt }".label`, null, opt),
551
- value: opt,
552
- disabled,
553
- });
554
- }
555
- }
556
-
557
- const cur = this.agentConfig['cloud-provider-name'];
558
-
559
- if (cur && !out.find((x) => x.value === cur)) {
560
- // Localization missing
561
- // Look up cur in the localization file
562
- const label = this.$store.getters['i18n/withFallback'](`cluster.cloudProvider."${ cur }".label`, null, cur);
563
-
564
- out.unshift({
565
- label: `${ label } (Current)`,
566
- value: cur,
567
- unsupported: true,
568
- disabled: true
569
- });
570
- }
571
-
572
- const initial = this.initialCloudProvider;
573
-
574
- if (cur !== initial && initial && !out.find((x) => x.value === initial)) {
575
- const label = this.$store.getters['i18n/withFallback'](`cluster.cloudProvider."${ initial }".label`, null, initial);
576
-
577
- out.unshift({
578
- label: `${ label } (Current)`,
579
- value: initial,
580
- unsupported: true,
581
- disabled: true
582
- });
583
- }
584
-
585
- return out;
586
- },
587
-
588
- unsupportedCloudProvider() {
589
- // The current cloud provider
590
- const cur = this.initialCloudProvider;
591
-
592
- const provider = cur && this.cloudProviderOptions.find((x) => x.value === cur);
593
-
594
- return !!provider?.unsupported;
595
- },
596
-
597
- canNotEditCloudProvider() {
598
- const canNotEdit = this.clusterIsAlreadyCreated && !this.unsupportedCloudProvider;
599
-
600
- return canNotEdit;
601
- },
602
-
603
384
  /**
604
385
  * Kube Version
605
386
  */
@@ -636,10 +417,6 @@ export default {
636
417
  return this.selectedVersion?.charts || {};
637
418
  },
638
419
 
639
- showCisProfile() {
640
- return (this.provider === 'custom' || this.isElementalCluster) && ( this.serverArgs?.profile || this.agentArgs?.profile );
641
- },
642
-
643
420
  needCredential() {
644
421
  if ( this.provider === 'custom' || this.provider === 'import' || this.isElementalCluster || this.mode === _VIEW ) {
645
422
  return false;
@@ -789,56 +566,12 @@ export default {
789
566
  return out;
790
567
  },
791
568
 
792
- enabledSystemServices: {
793
- get() {
794
- const out = difference(this.serverArgs.disable.options, this.serverConfig.disable || []);
795
-
796
- return out;
797
- },
798
-
799
- set(neu) {
800
- const out = difference(this.serverArgs.disable.options, neu);
801
-
802
- set(this.serverConfig, 'disable', out);
803
- },
804
- },
805
-
806
- showCloudConfigYaml() {
807
- if ( !this.agentArgs['cloud-provider-name'] ) {
808
- return false;
809
- }
810
-
811
- const name = this.agentConfig['cloud-provider-name'];
812
-
813
- if ( !name ) {
814
- return false;
815
- }
816
-
817
- switch ( name ) {
818
- case 'none': return false;
819
- case 'aws': return false;
820
- case 'rancher-vsphere': return false;
821
- case HARVESTER: return false;
822
- default: return true;
823
- }
824
- },
825
-
826
- showVsphereNote() {
827
- if ( !this.agentArgs['cloud-provider-name'] ) {
828
- return false;
829
- }
830
-
831
- const name = this.agentConfig['cloud-provider-name'];
832
-
833
- return name === 'rancher-vsphere';
834
- },
835
-
836
569
  showCni() {
837
570
  return !!this.serverArgs.cni;
838
571
  },
839
572
 
840
573
  showCloudProvider() {
841
- return this.agentArgs['cloud-provider-name'];
574
+ return !!this.agentArgs['cloud-provider-name'];
842
575
  },
843
576
 
844
577
  /**
@@ -878,15 +611,66 @@ export default {
878
611
  return versions.filter((x) => !!x);
879
612
  },
880
613
 
881
- showk8s21LegacyWarning() {
882
- const isLegacyEnabled = this.features(LEGACY);
614
+ cloudProviderOptions() {
615
+ const out = [{
616
+ label: this.$store.getters['i18n/t']('cluster.rke2.cloudProvider.defaultValue.label'),
617
+ value: '',
618
+ }];
883
619
 
884
- if (!isLegacyEnabled) {
885
- return false;
620
+ if ( !!this.agentArgs['cloud-provider-name']?.options ) {
621
+ const preferred = this.$store.getters['plugins/cloudProviderForDriver'](this.provider);
622
+
623
+ for ( const opt of this.agentArgs['cloud-provider-name']?.options ) {
624
+ // If we don't have a preferred provider... show all options
625
+ const showAllOptions = preferred === undefined;
626
+ // If we have a preferred provider... only show default, preferred and external
627
+ const isPreferred = opt === preferred;
628
+ const isExternal = opt === 'external';
629
+ let disabled = false;
630
+
631
+ if ((this.isHarvesterExternalCredential || this.isHarvesterIncompatible) && isPreferred) {
632
+ disabled = true;
633
+ }
634
+
635
+ if (showAllOptions || isPreferred || isExternal) {
636
+ out.push({
637
+ label: this.$store.getters['i18n/withFallback'](`cluster.cloudProvider."${ opt }".label`, null, opt),
638
+ value: opt,
639
+ disabled,
640
+ });
641
+ }
642
+ }
643
+ }
644
+
645
+ const cur = this.agentConfig['cloud-provider-name'];
646
+
647
+ if (cur && !out.find((x) => x.value === cur)) {
648
+ // Localization missing
649
+ // Look up cur in the localization file
650
+ const label = this.$store.getters['i18n/withFallback'](`cluster.cloudProvider."${ cur }".label`, null, cur);
651
+
652
+ out.unshift({
653
+ label: `${ label } (Current)`,
654
+ value: cur,
655
+ unsupported: true,
656
+ disabled: true
657
+ });
886
658
  }
887
- const selectedVersion = semver.coerce(this.value.spec.kubernetesVersion);
888
659
 
889
- return semver.satisfies(selectedVersion, '>=1.21.0');
660
+ const initial = this.initialCloudProvider;
661
+
662
+ if (cur !== initial && initial && !out.find((x) => x.value === initial)) {
663
+ const label = this.$store.getters['i18n/withFallback'](`cluster.cloudProvider."${ initial }".label`, null, initial);
664
+
665
+ out.unshift({
666
+ label: `${ label } (Current)`,
667
+ value: initial,
668
+ unsupported: true,
669
+ disabled: true
670
+ });
671
+ }
672
+
673
+ return out;
890
674
  },
891
675
 
892
676
  canManageMembers() {
@@ -923,33 +707,6 @@ export default {
923
707
  return out;
924
708
  },
925
709
 
926
- ciliumIpv6: {
927
- get() {
928
- // eslint-disable-next-line no-unused-vars
929
- const cni = this.serverConfig.cni; // force this property to recalculate if cni was changed away from cilium and chartValues['rke-cilium'] deleted
930
-
931
- return this.userChartValues[this.chartVersionKey('rke2-cilium')]?.cilium?.ipv6?.enabled || false;
932
- },
933
- set(val) {
934
- const name = this.chartVersionKey('rke2-cilium');
935
- const values = this.userChartValues[name];
936
-
937
- set(this, 'userChartValues', {
938
- ...this.userChartValues,
939
- [name]: {
940
- ...values,
941
- cilium: {
942
- ...values?.cilium,
943
- ipv6: {
944
- ...values?.cilium?.ipv6,
945
- enabled: val
946
- }
947
- }
948
- }
949
- });
950
- }
951
- },
952
-
953
710
  showIpv6Warning() {
954
711
  const clusterCIDR = this.serverConfig['cluster-cidr'] || '';
955
712
  const serviceCIDR = this.serverConfig['service-cidr'] || '';
@@ -1025,6 +782,14 @@ export default {
1025
782
 
1026
783
  return validRequiredPools && base;
1027
784
  },
785
+ unsupportedCloudProvider() {
786
+ // The current cloud provider
787
+ const cur = this.initialCloudProvider;
788
+
789
+ const provider = cur && this.cloudProviderOptions.find((x) => x.value === cur);
790
+
791
+ return !!provider?.unsupported;
792
+ },
1028
793
  },
1029
794
 
1030
795
  watch: {
@@ -1096,15 +861,10 @@ export default {
1096
861
  set(this.agentConfig, 'cloud-provider-name', undefined);
1097
862
  }
1098
863
  },
1099
-
1100
- },
1101
-
1102
- mounted() {
1103
- window.rke = this;
1104
864
  },
1105
865
 
1106
866
  created() {
1107
- this.registerBeforeHook(this.saveMachinePools, 'save-machine-pools');
867
+ this.registerBeforeHook(this.saveMachinePools, 'save-machine-pools', 1);
1108
868
  this.registerBeforeHook(this.setRegistryConfig, 'set-registry-config');
1109
869
  this.registerAfterHook(this.cleanupMachinePools, 'cleanup-machine-pools');
1110
870
  this.registerAfterHook(this.saveRoleBindings, 'save-role-bindings');
@@ -1116,7 +876,6 @@ export default {
1116
876
  },
1117
877
 
1118
878
  methods: {
1119
- nlToBr,
1120
879
  set,
1121
880
 
1122
881
  /**
@@ -1400,6 +1159,7 @@ export default {
1400
1159
  }
1401
1160
 
1402
1161
  const name = `pool${ ++this.lastIdx }`;
1162
+
1403
1163
  const pool = {
1404
1164
  id: name,
1405
1165
  config,
@@ -1420,6 +1180,7 @@ export default {
1420
1180
  kind: this.machineConfigSchema.attributes?.kind,
1421
1181
  name: null,
1422
1182
  },
1183
+ drainBeforeDelete: true
1423
1184
  },
1424
1185
  };
1425
1186
 
@@ -1472,7 +1233,27 @@ export default {
1472
1233
  }
1473
1234
  },
1474
1235
 
1475
- async saveMachinePools() {
1236
+ async saveMachinePools(hookContext) {
1237
+ if (hookContext === CONTEXT_HOOK_EDIT_YAML) {
1238
+ await new Promise((resolve, reject) => {
1239
+ this.$store.dispatch('cluster/promptModal', {
1240
+ component: 'GenericPrompt',
1241
+ componentProps: {
1242
+ title: this.t('cluster.rke2.modal.editYamlMachinePool.title'),
1243
+ body: this.t('cluster.rke2.modal.editYamlMachinePool.body'),
1244
+ applyMode: 'editAndContinue',
1245
+ confirm: (confirmed) => {
1246
+ if (confirmed) {
1247
+ resolve();
1248
+ } else {
1249
+ reject(new Error('User Cancelled'));
1250
+ }
1251
+ }
1252
+ },
1253
+ });
1254
+ });
1255
+ }
1256
+
1476
1257
  const finalPools = [];
1477
1258
 
1478
1259
  // If the extension provider wants to do this, let them
@@ -1865,16 +1646,6 @@ export default {
1865
1646
  this.syncChartValues(name);
1866
1647
  },
1867
1648
 
1868
- initQuestions(name) {
1869
- const defaultChartValue = this.versionInfo[name];
1870
- const startingChartValue = this.initYamlEditor(name);
1871
-
1872
- return {
1873
- ...defaultChartValue,
1874
- values: startingChartValue,
1875
- };
1876
- },
1877
-
1878
1649
  initYamlEditor(name) {
1879
1650
  const defaultChartValue = this.versionInfo[name];
1880
1651
  const key = this.chartVersionKey(name);
@@ -2305,6 +2076,9 @@ export default {
2305
2076
  this.lastDefaultPodSecurityPolicyTemplateName = value;
2306
2077
  },
2307
2078
 
2079
+ handleShowDeprecatedPatchVersionsChanged(value) {
2080
+ this.showDeprecatedPatchVersions = value;
2081
+ },
2308
2082
  /**
2309
2083
  * Track Machine Pool validation status
2310
2084
  */
@@ -2314,6 +2088,71 @@ export default {
2314
2088
  } else {
2315
2089
  this.$set(this.machinePoolValidation, id, value);
2316
2090
  }
2091
+ },
2092
+ handleEnabledSystemServicesChanged(val) {
2093
+ set(this.serverConfig, 'disable', val);
2094
+ },
2095
+ handleCiliumIpv6Changed(neu) {
2096
+ const name = this.chartVersionKey('rke2-cilium');
2097
+ const values = this.userChartValues[name];
2098
+
2099
+ set(this, 'userChartValues', {
2100
+ ...this.userChartValues,
2101
+ [name]: {
2102
+ ...values,
2103
+ cilium: {
2104
+ ...values?.cilium,
2105
+ ipv6: {
2106
+ ...values?.cilium?.ipv6,
2107
+ enabled: neu
2108
+ }
2109
+ }
2110
+ }
2111
+ });
2112
+ },
2113
+ handlePspChanged(neu) {
2114
+ this.handlePspChange(neu);
2115
+ },
2116
+ handleCisChanged() {
2117
+ this.handleCisChange();
2118
+ },
2119
+ handlePsaDefaultChanged() {
2120
+ this.togglePsaDefault();
2121
+ },
2122
+ handleMachinePoolError(error) {
2123
+ this.machinePoolErrors = merge(this.machinePoolErrors, error);
2124
+
2125
+ const errors = Object.entries(this.machinePoolErrors)
2126
+ .map((x) => {
2127
+ if (!x[1].length) {
2128
+ return;
2129
+ }
2130
+
2131
+ const formattedFields = (() => {
2132
+ switch (x[1].length) {
2133
+ case 1:
2134
+ return x[1][0];
2135
+ case 2:
2136
+ return `${ x[1][0] } and ${ x[1][1] }`;
2137
+ default: {
2138
+ const [head, ...rest] = x[1];
2139
+
2140
+ return `${ rest.join(', ') }, and ${ head }`;
2141
+ }
2142
+ }
2143
+ })();
2144
+
2145
+ return this.t('cluster.banner.machinePoolError', {
2146
+ count: x[1].length, pool_name: x[0], fields: formattedFields
2147
+ }, true);
2148
+ } )
2149
+ .filter((x) => x);
2150
+
2151
+ if (!errors) {
2152
+ return;
2153
+ }
2154
+
2155
+ this.errors = errors;
2317
2156
  }
2318
2157
  },
2319
2158
  };
@@ -2447,7 +2286,8 @@ export default {
2447
2286
  :idx="idx"
2448
2287
  :machine-pools="machinePools"
2449
2288
  :busy="busy"
2450
- @error="e=>errors = e"
2289
+ :pool-id="obj.id"
2290
+ @error="handleMachinePoolError"
2451
2291
  @validationChanged="v=>machinePoolValidationChanged(obj.id, v)"
2452
2292
  />
2453
2293
  </Tab>
@@ -2465,270 +2305,49 @@ export default {
2465
2305
  :side-tabs="true"
2466
2306
  class="min-height"
2467
2307
  >
2468
- <!-- Basic -->
2469
2308
  <Tab
2470
2309
  name="basic"
2471
2310
  label-key="cluster.tabs.basic"
2472
2311
  :weight="11"
2473
2312
  @active="refreshYamls"
2474
2313
  >
2475
- <Banner
2476
- v-if="!haveArgInfo"
2477
- color="warning"
2478
- :label="t('cluster.banner.haveArgInfo')"
2479
- />
2480
- <Banner
2481
- v-if="showk8s21LegacyWarning"
2482
- color="warning"
2483
- :label="t('cluster.legacyWarning')"
2484
- />
2485
- <Banner
2486
- v-if="isHarvesterDriver && isHarvesterIncompatible && showCloudProvider"
2487
- color="warning"
2488
- >
2489
- <span
2490
- v-clean-html="t('cluster.harvester.warning.cloudProvider.incompatible', null, true)"
2491
- />
2492
- </Banner>
2493
- <div class="row mb-10">
2494
- <div class="col span-6">
2495
- <LabeledSelect
2496
- v-model="value.spec.kubernetesVersion"
2497
- :mode="mode"
2498
- :options="versionOptions"
2499
- label-key="cluster.kubernetesVersion.label"
2500
- @input="handleKubernetesChange($event)"
2501
- />
2502
- <Checkbox
2503
- v-model="showDeprecatedPatchVersions"
2504
- :label="t('cluster.kubernetesVersion.deprecatedPatches')"
2505
- :tooltip="t('cluster.kubernetesVersion.deprecatedPatchWarning')"
2506
- class="patch-version"
2507
- />
2508
- </div>
2509
- <div
2510
- v-if="showCloudProvider"
2511
- class="col span-6"
2512
- >
2513
- <LabeledSelect
2514
- v-model="agentConfig['cloud-provider-name']"
2515
- :mode="mode"
2516
- :disabled="canNotEditCloudProvider"
2517
- :options="cloudProviderOptions"
2518
- :label="t('cluster.rke2.cloudProvider.label')"
2519
- />
2520
- </div>
2521
- </div>
2522
- <div
2523
- v-if="showCni"
2524
- :style="{'align-items':'center'}"
2525
- class="row"
2526
- >
2527
- <div class="col span-6">
2528
- <LabeledSelect
2529
- v-model="serverConfig.cni"
2530
- :mode="mode"
2531
- :disabled="clusterIsAlreadyCreated"
2532
- :options="serverArgs.cni.options"
2533
- :label="t('cluster.rke2.cni.label')"
2534
- />
2535
- </div>
2536
- <div
2537
- v-if="serverConfig.cni === 'cilium' || serverConfig.cni === 'multus,cilium'"
2538
- class="col"
2539
- >
2540
- <Checkbox
2541
- v-model="ciliumIpv6"
2542
- :mode="mode"
2543
- :label="t('cluster.rke2.address.ipv6.enable')"
2544
- />
2545
- </div>
2546
- </div>
2547
- <template v-if="showVsphereNote">
2548
- <Banner
2549
- color="warning"
2550
- label-key="cluster.cloudProvider.rancher-vsphere.note"
2551
- />
2552
- </template>
2553
- <template v-else-if="showCloudConfigYaml">
2554
- <div class="spacer" />
2555
-
2556
- <div class="col span-12">
2557
- <Banner
2558
- v-if="unsupportedCloudProvider"
2559
- class="error mt-5"
2560
- >
2561
- {{ t('cluster.rke2.cloudProvider.unsupported') }}
2562
- </Banner>
2563
- <h3>
2564
- {{ t('cluster.rke2.cloudProvider.header') }}
2565
- </h3>
2566
- <YamlEditor
2567
- ref="yaml"
2568
- v-model="agentConfig['cloud-provider-config']"
2569
- :editor-mode="mode === 'view' ? 'VIEW_CODE' : 'EDIT_CODE'"
2570
- initial-yaml-values="# Cloud Provider Config"
2571
- class="yaml-editor"
2572
- />
2573
- </div>
2574
- </template>
2575
-
2576
- <div class="spacer" />
2577
-
2578
- <h3>
2579
- {{ t('cluster.rke2.security.header') }}
2580
- </h3>
2581
- <Banner
2582
- v-if="isEdit && !needsPSP && hasPsps"
2583
- color="warning"
2584
- :label="t('cluster.banner.invalidPsps')"
2585
- />
2586
- <Banner
2587
- v-else-if="isCreate && !needsPSP"
2588
- color="info"
2589
- :label="t('cluster.banner.removedPsp')"
2590
- />
2591
- <Banner
2592
- v-else-if="isCreate && hasPsps"
2593
- color="info"
2594
- :label="t('cluster.banner.deprecatedPsp')"
2314
+ <!-- Basic -->
2315
+ <Basics
2316
+ v-model="value"
2317
+ :live-value="liveValue"
2318
+ :mode="mode"
2319
+ :provider="provider"
2320
+ :psps="psps"
2321
+ :user-chart-values="userChartValues"
2322
+ :credential="credential"
2323
+ :cis-override="cisOverride"
2324
+ :cis-psa-change-banner="cisPsaChangeBanner"
2325
+ :all-psps="allPSPs"
2326
+ :all-psas="allPSAs"
2327
+ :addon-versions="addonVersions"
2328
+ :show-deprecated-patch-versions="showDeprecatedPatchVersions"
2329
+ :needs-psp="needsPSP"
2330
+ :selected-version="selectedVersion"
2331
+ :is-harvester-driver="isHarvesterDriver"
2332
+ :is-harvester-incompatible="isHarvesterIncompatible"
2333
+ :version-options="versionOptions"
2334
+ :cluster-is-already-created="clusterIsAlreadyCreated"
2335
+ :is-elemental-cluster="isElementalCluster"
2336
+ :has-psa-templates="hasPsaTemplates"
2337
+ :is-k3s="isK3s"
2338
+ :have-arg-info="haveArgInfo"
2339
+ :show-cni="showCni"
2340
+ :show-cloud-provider="showCloudProvider"
2341
+ :unsupported-cloud-provider="unsupportedCloudProvider"
2342
+ :cloud-provider-options="cloudProviderOptions"
2343
+ @cilium-ipv6-changed="handleCiliumIpv6Changed"
2344
+ @enabled-system-services-changed="handleEnabledSystemServicesChanged"
2345
+ @kubernetes-changed="handleKubernetesChange"
2346
+ @psp-changed="handlePspChanged"
2347
+ @cis-changed="handleCisChanged"
2348
+ @psa-default-changed="handlePsaDefaultChanged"
2349
+ @show-deprecated-patch-versions-changed="handleShowDeprecatedPatchVersionsChanged"
2595
2350
  />
2596
-
2597
- <Banner
2598
- v-if="showCisProfile && !isCisSupported && isEdit"
2599
- color="info"
2600
- >
2601
- <p v-clean-html="t('cluster.rke2.banner.cisUnsupported', {cisProfile: serverConfig.profile || agentConfig.profile}, true)" />
2602
- </Banner>
2603
-
2604
- <div class="row mb-10">
2605
- <div
2606
- v-if="pspOptions && needsPSP"
2607
- class="col span-6"
2608
- >
2609
- <!-- PSP template selector -->
2610
- <LabeledSelect
2611
- v-model="value.spec.defaultPodSecurityPolicyTemplateName"
2612
- data-testid="rke2-custom-edit-psp"
2613
- :mode="mode"
2614
- :options="pspOptions"
2615
- :label="t('cluster.rke2.defaultPodSecurityPolicyTemplateName.label')"
2616
- @input="handlePspChange($event)"
2617
- />
2618
- </div>
2619
-
2620
- <div
2621
- v-if="showCisProfile"
2622
- class="col span-6"
2623
- >
2624
- <LabeledSelect
2625
- v-if="serverArgs && serverArgs.profile"
2626
- v-model="serverConfig.profile"
2627
- :mode="mode"
2628
- :options="profileOptions"
2629
- :label="t('cluster.rke2.cis.sever')"
2630
- @input="handleCisChange"
2631
- />
2632
- <LabeledSelect
2633
- v-else-if="agentArgs && agentArgs.profile"
2634
- v-model="agentConfig.profile"
2635
- data-testid="rke2-custom-edit-cis-agent"
2636
- :mode="mode"
2637
- :options="profileOptions"
2638
- :label="t('cluster.rke2.cis.agent')"
2639
- @input="handleCisChange"
2640
- />
2641
- </div>
2642
- </div>
2643
-
2644
- <template v-if="hasCisOverride">
2645
- <Checkbox
2646
- v-model="cisOverride"
2647
- :mode="mode"
2648
- :label="t('cluster.rke2.cis.override')"
2649
- @input="togglePsaDefault"
2650
- />
2651
-
2652
- <Banner
2653
- v-if="cisOverride"
2654
- color="warning"
2655
- :label="t('cluster.rke2.banner.cisOverride')"
2656
- />
2657
- <Banner
2658
- v-if="cisPsaChangeBanner && !cisOverride"
2659
- color="info"
2660
- :label="t('cluster.rke2.banner.psaChange')"
2661
- />
2662
- </template>
2663
-
2664
- <div
2665
- v-if="needsPSA"
2666
- class="row mb-10 mt-10"
2667
- >
2668
- <div class="col span-6">
2669
- <!-- PSA template selector -->
2670
- <LabeledSelect
2671
- v-model="value.spec.defaultPodSecurityAdmissionConfigurationTemplateName"
2672
- :mode="mode"
2673
- data-testid="rke2-custom-edit-psa"
2674
- :options="psaOptions"
2675
- :disabled="isPsaDisabled"
2676
- :label="t('cluster.rke2.defaultPodSecurityAdmissionConfigurationTemplateName.label')"
2677
- />
2678
- </div>
2679
- </div>
2680
-
2681
- <div class="row">
2682
- <div class="col span-12 mt-20">
2683
- <Checkbox
2684
- v-if="serverArgs['secrets-encryption']"
2685
- v-model="serverConfig['secrets-encryption']"
2686
- :mode="mode"
2687
- label="Encrypt Secrets"
2688
- />
2689
- <Checkbox
2690
- v-model="value.spec.enableNetworkPolicy"
2691
- :mode="mode"
2692
- :label="t('cluster.rke2.enableNetworkPolicy.label')"
2693
- />
2694
- <!-- <Checkbox v-if="agentArgs.selinux" v-model="agentConfig.selinux" :mode="mode" label="SELinux" /> -->
2695
- </div>
2696
- </div>
2697
-
2698
- <div
2699
- v-if="serverConfig.cni === 'cilium' && value.spec.enableNetworkPolicy"
2700
- class="row"
2701
- >
2702
- <div class="col span-12">
2703
- <Banner
2704
- color="info"
2705
- :label="t('cluster.rke2.enableNetworkPolicy.warning')"
2706
- />
2707
- </div>
2708
- </div>
2709
-
2710
- <div class="spacer" />
2711
-
2712
- <div
2713
- v-if="serverArgs.disable"
2714
- class="row"
2715
- >
2716
- <div class="col span-12">
2717
- <div>
2718
- <h3>
2719
- {{ t('cluster.rke2.systemService.header') }}
2720
- </h3>
2721
- </div>
2722
- <Checkbox
2723
- v-for="opt in disableOptions"
2724
- :key="opt.value"
2725
- v-model="enabledSystemServices"
2726
- :mode="mode"
2727
- :label="opt.label"
2728
- :value-when-true="opt.value"
2729
- />
2730
- </div>
2731
- </div>
2732
2351
  </Tab>
2733
2352
 
2734
2353
  <!-- Member Roles -->
@@ -2738,19 +2357,12 @@ export default {
2738
2357
  label-key="cluster.tabs.memberRoles"
2739
2358
  :weight="10"
2740
2359
  >
2741
- <Banner
2742
- v-if="isEdit"
2743
- color="info"
2744
- >
2745
- {{ t('cluster.memberRoles.removeMessage') }}
2746
- </Banner>
2747
- <ClusterMembershipEditor
2360
+ <MemberRoles
2361
+ v-model="value"
2748
2362
  :mode="mode"
2749
- :parent-id="value.mgmt ? value.mgmt.id : null"
2750
- @membership-update="onMembershipUpdate"
2363
+ :on-membership-update="onMembershipUpdate"
2751
2364
  />
2752
2365
  </Tab>
2753
-
2754
2366
  <!-- etcd -->
2755
2367
  <Tab
2756
2368
  name="etcd"
@@ -2961,11 +2573,11 @@ export default {
2961
2573
  v-if="get(rkeConfig, 'upgradeStrategy.controlPlaneDrainOptions.deleteEmptyDirData')"
2962
2574
  color="warning"
2963
2575
  >
2964
- {{ t('cluster.rke2.deleteEmptyDir', {}, true) }}
2576
+ {{ t('cluster.rke2.drain.deleteEmptyDir.warning', {}, true) }}
2965
2577
  </Banner>
2966
2578
  <div class="row">
2967
2579
  <div class="col span-6">
2968
- <h3>Control Plane</h3>
2580
+ <h3>{{ t('cluster.rke2.controlPlaneConcurrency.header') }}</h3>
2969
2581
  <LabeledInput
2970
2582
  v-model="rkeConfig.upgradeStrategy.controlPlaneConcurrency"
2971
2583
  :mode="mode"
@@ -3003,7 +2615,7 @@ export default {
3003
2615
  label-key="cluster.tabs.registry"
3004
2616
  >
3005
2617
  <div class="row">
3006
- <h3>Registry for Rancher System Container Images</h3>
2618
+ <h3>{{ t('cluster.privateRegistry.label') }}</h3>
3007
2619
  </div>
3008
2620
  <div class="row">
3009
2621
  <div class="col span-12">