@rancher/shell 3.0.9-rc.5 → 3.0.9-rc.6

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 (142) hide show
  1. package/assets/images/providers/oci-open-containers.svg +22 -0
  2. package/assets/images/providers/traefik.png +0 -0
  3. package/assets/styles/themes/_dark.scss +2 -0
  4. package/assets/styles/themes/_light.scss +2 -0
  5. package/assets/styles/themes/_modern.scss +6 -0
  6. package/assets/translations/en-us.yaml +129 -25
  7. package/components/CruResource.vue +3 -1
  8. package/components/ExplorerProjectsNamespaces.vue +12 -12
  9. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +109 -0
  10. package/components/Resource/Detail/Card/StatusCard/index.vue +21 -4
  11. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +19 -2
  12. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +19 -11
  13. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +12 -0
  14. package/components/Resource/Detail/ResourcePopover/index.vue +2 -0
  15. package/components/Resource/Detail/ResourceRow.vue +2 -2
  16. package/components/ResourceList/index.vue +7 -4
  17. package/components/Window/ContainerLogs.vue +48 -37
  18. package/components/fleet/FleetClusterTargets/TargetsList.vue +2 -2
  19. package/components/fleet/FleetClusterTargets/index.vue +6 -1
  20. package/components/fleet/GitRepoAdvancedTab.vue +333 -0
  21. package/components/fleet/GitRepoMetadataTab.vue +43 -0
  22. package/components/fleet/GitRepoRepositoryTab.vue +101 -0
  23. package/components/fleet/GitRepoTargetTab.vue +77 -0
  24. package/components/fleet/HelmOpAdvancedTab.vue +247 -0
  25. package/components/fleet/HelmOpChartTab.vue +158 -0
  26. package/components/fleet/HelmOpMetadataTab.vue +46 -0
  27. package/components/fleet/HelmOpTargetTab.vue +84 -0
  28. package/components/fleet/HelmOpValuesTab.vue +147 -0
  29. package/components/fleet/__tests__/FleetClusterTargets.test.ts +119 -70
  30. package/components/form/NodeScheduling.vue +81 -7
  31. package/components/form/PodAffinity.vue +1 -36
  32. package/components/form/ResourceLabeledSelect.vue +8 -4
  33. package/components/form/ResourceQuota/Namespace.vue +30 -9
  34. package/components/form/ResourceQuota/NamespaceRow.vue +25 -7
  35. package/components/form/ResourceQuota/Project.vue +140 -82
  36. package/components/form/ResourceQuota/ResourceQuotaEntry.vue +145 -0
  37. package/components/form/ResourceQuota/__tests__/Namespace.test.ts +307 -0
  38. package/components/form/ResourceQuota/__tests__/NamespaceRow.test.ts +281 -0
  39. package/components/form/ResourceQuota/__tests__/Project.test.ts +274 -27
  40. package/components/form/ResourceQuota/__tests__/ResourceQuotaEntry.test.ts +215 -0
  41. package/components/form/SchedulingCustomization.vue +14 -6
  42. package/components/form/SelectOrCreateAuthSecret.vue +107 -18
  43. package/components/form/__tests__/NodeScheduling.test.ts +12 -9
  44. package/components/form/__tests__/PodAffinity.test.ts +21 -2
  45. package/components/form/__tests__/SchedulingCustomization.test.ts +240 -0
  46. package/components/formatter/ClusterLink.vue +8 -0
  47. package/components/formatter/SecretOrigin.vue +79 -0
  48. package/config/labels-annotations.js +7 -6
  49. package/config/pagination-table-headers.js +6 -4
  50. package/config/product/explorer.js +1 -11
  51. package/config/query-params.js +3 -0
  52. package/config/settings.ts +15 -2
  53. package/config/table-headers.js +21 -17
  54. package/config/types.js +23 -8
  55. package/detail/workload/index.vue +11 -16
  56. package/dialog/DeactivateDriverDialog.vue +1 -1
  57. package/dialog/Ipv6NetworkingDialog.vue +156 -0
  58. package/dialog/ScalePoolDownDialog.vue +2 -2
  59. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
  60. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +1 -0
  61. package/edit/__tests__/management.cattle.io.project.test.js +56 -128
  62. package/edit/auth/oidc.vue +1 -1
  63. package/edit/catalog.cattle.io.clusterrepo.vue +155 -25
  64. package/edit/fleet.cattle.io.gitrepo.vue +153 -283
  65. package/edit/fleet.cattle.io.helmop.vue +190 -332
  66. package/edit/management.cattle.io.project.vue +5 -42
  67. package/edit/management.cattle.io.setting.vue +6 -0
  68. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +55 -24
  69. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +1 -103
  70. package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +13 -1
  71. package/edit/provisioning.cattle.io.cluster/__tests__/rke2-fleet-cluster-agent.test.ts +283 -0
  72. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -49
  73. package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +112 -0
  74. package/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue +158 -0
  75. package/edit/provisioning.cattle.io.cluster/rke2.vue +171 -72
  76. package/edit/provisioning.cattle.io.cluster/shared.ts +36 -1
  77. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +2 -1
  78. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +55 -7
  79. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +319 -0
  80. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +2 -1
  81. package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +13 -1
  82. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +10 -44
  83. package/edit/secret/index.vue +1 -1
  84. package/edit/token.vue +68 -29
  85. package/edit/workload/__tests__/index.test.ts +2 -37
  86. package/edit/workload/index.vue +6 -2
  87. package/edit/workload/mixins/workload.js +0 -32
  88. package/list/__tests__/management.cattle.io.setting.test.ts +198 -0
  89. package/list/management.cattle.io.setting.vue +13 -0
  90. package/list/provisioning.cattle.io.cluster.vue +50 -1
  91. package/list/secret.vue +4 -9
  92. package/list/service.vue +6 -8
  93. package/machine-config/amazonec2.vue +11 -4
  94. package/machine-config/components/EC2Networking.vue +46 -30
  95. package/machine-config/components/__tests__/EC2Networking.test.ts +7 -7
  96. package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +0 -9
  97. package/machine-config/digitalocean.vue +3 -3
  98. package/models/__tests__/namespace.test.ts +11 -0
  99. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +96 -0
  100. package/models/__tests__/workload.test.ts +42 -1
  101. package/models/catalog.cattle.io.clusterrepo.js +30 -4
  102. package/models/ext.cattle.io.token.js +48 -0
  103. package/models/kontainerdriver.js +2 -2
  104. package/models/namespace.js +7 -1
  105. package/models/nodedriver.js +2 -2
  106. package/models/provisioning.cattle.io.cluster.js +28 -7
  107. package/models/secret.js +0 -17
  108. package/models/service.js +44 -1
  109. package/models/token.js +4 -0
  110. package/models/workload.js +12 -6
  111. package/package.json +1 -1
  112. package/pages/account/index.vue +96 -67
  113. package/pages/auth/setup.vue +5 -14
  114. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +4 -1
  115. package/pages/c/_cluster/apps/charts/index.vue +93 -4
  116. package/pages/c/_cluster/apps/charts/install.vue +317 -42
  117. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -4
  118. package/pages/c/_cluster/settings/index.vue +3 -1
  119. package/plugins/dashboard-store/__tests__/getters.test.ts +108 -0
  120. package/plugins/dashboard-store/__tests__/resource-class.test.ts +27 -0
  121. package/plugins/dashboard-store/actions.js +3 -8
  122. package/plugins/dashboard-store/getters.js +7 -5
  123. package/plugins/dashboard-store/mutations.js +4 -1
  124. package/plugins/dashboard-store/resource-class.js +3 -3
  125. package/plugins/steve/__tests__/steve-class.test.ts +102 -141
  126. package/plugins/steve/steve-class.js +12 -3
  127. package/plugins/steve/steve-pagination-utils.ts +6 -2
  128. package/rancher-components/RcIcon/types.ts +2 -0
  129. package/rancher-components/RcItemCard/RcItemCard.vue +64 -19
  130. package/store/prefs.js +3 -0
  131. package/types/aws-sdk.d.ts +121 -0
  132. package/types/resources/node.ts +15 -0
  133. package/types/shell/index.d.ts +536 -506
  134. package/types/store/pagination.types.ts +5 -5
  135. package/utils/__tests__/array.test.ts +1 -29
  136. package/utils/__tests__/cluster-agent-configuration.test.ts +203 -0
  137. package/utils/array.ts +0 -11
  138. package/utils/aws.ts +21 -0
  139. package/utils/cluster.js +22 -2
  140. package/utils/selector-typed.ts +1 -1
  141. package/components/__tests__/ProjectRow.test.ts +0 -206
  142. package/components/form/ResourceQuota/ProjectRow.vue +0 -277
@@ -28,8 +28,9 @@ import {
28
28
  } from '@shell/utils/object';
29
29
  import { allHash } from '@shell/utils/promise';
30
30
  import {
31
- getAllOptionsAfterCurrentVersion, filterOutDeprecatedPatchVersions, isHarvesterSatisfiesVersion, labelForAddon, initSchedulingCustomization, addonConfigPreserve
31
+ getAllOptionsAfterCurrentVersion, filterOutDeprecatedPatchVersions, isHarvesterSatisfiesVersion, labelForAddon, initSchedulingCustomization, addonConfigPreserve,
32
32
  } from '@shell/utils/cluster';
33
+ import { AGENT_CONFIGURATION_TYPES, SETTING } from '@shell/config/settings';
33
34
 
34
35
  import { BadgeState } from '@components/BadgeState';
35
36
  import { Banner } from '@components/Banner';
@@ -43,7 +44,6 @@ import { canViewClusterMembershipEditor } from '@shell/components/form/Members/C
43
44
  import semver from 'semver';
44
45
 
45
46
  import { CLOUD_CREDENTIAL_OVERRIDE } from '@shell/models/nodedriver';
46
- import { SETTING } from '@shell/config/settings';
47
47
  import { base64Encode } from '@shell/utils/crypto';
48
48
  import { CAPI as CAPI_ANNOTATIONS, CLUSTER_BADGE } from '@shell/config/labels-annotations';
49
49
  import AgentEnv from '@shell/edit/provisioning.cattle.io.cluster/AgentEnv';
@@ -66,9 +66,11 @@ import { DEFAULT_COMMON_BASE_PATH, DEFAULT_SUBDIRS } from '@shell/edit/provision
66
66
  import ClusterAppearance from '@shell/components/form/ClusterAppearance';
67
67
  import AddOnAdditionalManifest from '@shell/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest';
68
68
  import VsphereUtils, { VMWARE_VSPHERE } from '@shell/utils/v-sphere';
69
- import { RETENTION_DEFAULT, NGINX_SUPPORTED, INGRESS_CONTROLLER, INGRESS_NGINX } from '@shell/edit/provisioning.cattle.io.cluster/shared';
69
+ import {
70
+ HARVESTER, RETENTION_DEFAULT, RKE2_INGRESS_NGINX, INGRESS_CONTROLLER, INGRESS_NGINX, TRAEFIK, INGRESS_NONE
71
+ } from '@shell/edit/provisioning.cattle.io.cluster/shared';
70
72
  import { mapGetters } from 'vuex';
71
- const HARVESTER = 'harvester';
73
+
72
74
  const GOOGLE = 'google';
73
75
  const HARVESTER_CLOUD_PROVIDER = 'harvester-cloud-provider';
74
76
  const NETBIOS_TRUNCATION_LENGTH = 15;
@@ -159,6 +161,8 @@ export default {
159
161
 
160
162
  this.clusterAgentDefaultPC = sc.clusterAgentDefaultPC;
161
163
  this.clusterAgentDefaultPDB = sc.clusterAgentDefaultPDB;
164
+ this.fleetAgentDefaultPC = sc.fleetAgentDefaultPC;
165
+ this.fleetAgentDefaultPDB = sc.fleetAgentDefaultPDB;
162
166
  this.schedulingCustomizationFeatureEnabled = sc.schedulingCustomizationFeatureEnabled;
163
167
  this.schedulingCustomizationOriginallyEnabled = sc.schedulingCustomizationOriginallyEnabled;
164
168
  this.errors = this.errors.concat(sc.errors);
@@ -170,9 +174,8 @@ export default {
170
174
  Object.entries(this.chartValues).forEach(([name, value]) => {
171
175
  const key = this.chartVersionKey(name);
172
176
 
173
- this.userChartValues[key] = value;
177
+ this.set(this.userChartValues, key, value);
174
178
  });
175
-
176
179
  this.setAgentConfiguration();
177
180
  },
178
181
 
@@ -273,7 +276,7 @@ export default {
273
276
  machinePoolValidation: {}, // map of validation states for each machine pool
274
277
  machinePoolErrors: {},
275
278
  addonConfigValidation: {}, // validation state of each addon config (boolean of whether codemirror's yaml lint passed)
276
- stackPreferenceError: false, // spec.networking.stackPreference is validated in conjunction with hasSomeIpv6Pools
279
+ stackPreferenceError: false, // spec.networking.stackPreference is validated in conjunction with hasOnlyIpv6Pools
277
280
  allNamespaces: [],
278
281
  extensionTabs: getApplicableExtensionEnhancements(this, ExtensionPoint.TAB, TabLocation.CLUSTER_CREATE_RKE2, this.$route, this),
279
282
  clusterAgentDeploymentCustomization: null,
@@ -281,6 +284,8 @@ export default {
281
284
  schedulingCustomizationOriginallyEnabled: false,
282
285
  clusterAgentDefaultPC: null,
283
286
  clusterAgentDefaultPDB: null,
287
+ fleetAgentDefaultPC: null,
288
+ fleetAgentDefaultPDB: null,
284
289
  activeTab: null,
285
290
  isGoogle,
286
291
  isAuthenticated: !isGoogle || this.mode === _EDIT,
@@ -291,11 +296,16 @@ export default {
291
296
  addonConfigDiffs: {},
292
297
  originalKubeVersion: null,
293
298
  isEmpty,
299
+ AGENT_CONFIGURATION_TYPES,
300
+ basicsValid: true
294
301
  };
295
302
  },
296
303
 
297
304
  computed: {
298
305
  ...mapGetters({ features: 'features/get' }),
306
+ isK3s() {
307
+ return this.value?.isK3s;
308
+ },
299
309
 
300
310
  isActiveTabRegistries() {
301
311
  return this.activeTab?.selectedName === REGISTRIES_TAB_NAME;
@@ -849,8 +859,12 @@ export default {
849
859
  }
850
860
  },
851
861
 
852
- hasSomeIpv6Pools() {
853
- return !!(this.machinePools || []).find((p) => p.hasIpv6);
862
+ hasOnlyIpv6Pools() {
863
+ return !(this.machinePools || []).find((p) => !p.isIpv6 || p.isDualStack);
864
+ },
865
+
866
+ hasDualStackPools() {
867
+ return !!(this.machinePools || []).find((p) => p.isDualStack);
854
868
  },
855
869
 
856
870
  validationPassed() {
@@ -890,10 +904,11 @@ export default {
890
904
  overallFormValidationPassed() {
891
905
  return this.validationPassed &&
892
906
  this.fvFormIsValid &&
893
- this.etcdConfigValid;
907
+ this.etcdConfigValid &&
908
+ this.basicsValid;
894
909
  },
895
910
  nginxSupported() {
896
- if (this.serverArgs?.disable?.options.includes(NGINX_SUPPORTED)) {
911
+ if (this.serverArgs?.disable?.options.includes(RKE2_INGRESS_NGINX)) {
897
912
  return true;
898
913
  }
899
914
 
@@ -963,7 +978,7 @@ export default {
963
978
  );
964
979
  }
965
980
 
966
- this.versionInfo = {}; // Invalidate cache such that version info relevant to selected kube version is updated
981
+ Object.keys(this.versionInfo).forEach((key) => delete this.versionInfo[key]); // Invalidate cache such that version info relevant to selected kube version is updated
967
982
 
968
983
  // Allow time for addonNames to update... then fetch any missing addons
969
984
  this.$nextTick(() => this.initAddons());
@@ -995,22 +1010,11 @@ export default {
995
1010
  this.agentConfig['cloud-provider-name'] = undefined;
996
1011
  }
997
1012
  },
998
-
999
- hasSomeIpv6Pools(neu) {
1000
- if (this.isCreate && this.localValue.spec.rkeConfig.networking.stackPreference !== STACK_PREFS.IPV6) { // if stack pref is ipv6, the user has manually configured that and we shouldn't change it
1001
- if (neu) {
1002
- this.localValue.spec.rkeConfig.networking.stackPreference = STACK_PREFS.DUAL;
1003
-
1004
- return;
1005
- }
1006
-
1007
- this.localValue.spec.rkeConfig.networking.stackPreference = STACK_PREFS.IPV4;
1008
- }
1009
- }
1010
1013
  },
1011
1014
 
1012
1015
  created() {
1013
- this.registerBeforeHook(this.saveMachinePools, 'save-machine-pools', 1);
1016
+ this.registerBeforeHook(this.showIpv6Warning, 'show-ipv6-warning', 1);
1017
+ this.registerBeforeHook(this.saveMachinePools, 'save-machine-pools', 2);
1014
1018
  this.registerBeforeHook(this.setRegistryConfig, 'set-registry-config');
1015
1019
  this.registerBeforeHook(this.handleVsphereCpiSecret, 'sync-vsphere-cpi');
1016
1020
  this.registerBeforeHook(this.handleVsphereCsiSecret, 'sync-vsphere-csi');
@@ -1162,11 +1166,27 @@ export default {
1162
1166
  }
1163
1167
  },
1164
1168
 
1165
- setSchedulingCustomization(val) {
1166
- if (val) {
1167
- set(this.value, 'spec.clusterAgentDeploymentCustomization.schedulingCustomization', { priorityClass: this.clusterAgentDefaultPC, podDisruptionBudget: this.clusterAgentDefaultPDB });
1169
+ setSchedulingCustomization({ event, agentType }) {
1170
+ if (event) {
1171
+ switch (agentType) {
1172
+ case AGENT_CONFIGURATION_TYPES.CLUSTER:
1173
+ set(this.value, 'spec.clusterAgentDeploymentCustomization.schedulingCustomization', { priorityClass: this.clusterAgentDefaultPC, podDisruptionBudget: this.clusterAgentDefaultPDB });
1174
+ break;
1175
+ case AGENT_CONFIGURATION_TYPES.FLEET:
1176
+ set(this.value, 'spec.fleetAgentDeploymentCustomization.schedulingCustomization', { priorityClass: this.fleetAgentDefaultPC, podDisruptionBudget: this.fleetAgentDefaultPDB });
1177
+ break;
1178
+ default:
1179
+ }
1168
1180
  } else {
1169
- delete this.value.spec.clusterAgentDeploymentCustomization.schedulingCustomization;
1181
+ switch (agentType) {
1182
+ case AGENT_CONFIGURATION_TYPES.CLUSTER:
1183
+ delete this.value.spec.clusterAgentDeploymentCustomization.schedulingCustomization;
1184
+ break;
1185
+ case AGENT_CONFIGURATION_TYPES.FLEET:
1186
+ delete this.value.spec.fleetAgentDeploymentCustomization.schedulingCustomization;
1187
+ break;
1188
+ default:
1189
+ }
1170
1190
  }
1171
1191
  },
1172
1192
 
@@ -1335,14 +1355,15 @@ export default {
1335
1355
  const name = `pool${ ++this.lastIdx }`;
1336
1356
 
1337
1357
  const pool = {
1338
- id: name,
1358
+ id: name,
1339
1359
  config,
1340
- remove: false,
1341
- create: true,
1342
- update: false,
1343
- uid: name,
1344
- hasIpv6: false,
1345
- pool: {
1360
+ remove: false,
1361
+ create: true,
1362
+ update: false,
1363
+ uid: name,
1364
+ isIpv6: false,
1365
+ isDualStack: false,
1366
+ pool: {
1346
1367
  name,
1347
1368
  etcdRole: numCurrentPools === 0,
1348
1369
  controlPlaneRole: numCurrentPools === 0,
@@ -1532,6 +1553,59 @@ export default {
1532
1553
  }
1533
1554
  },
1534
1555
 
1556
+ async showIpv6Warning(hookContext) {
1557
+ if (this.mode !== _CREATE || !this.machinePools?.length) {
1558
+ return;
1559
+ }
1560
+ const stackPreference = this.value.spec.rkeConfig.networking.stackPreference;
1561
+ const isK3s = (this.selectedVersion?.label || '').toLowerCase().includes('k3s');
1562
+ const flannelMasqEnabled = this.serverConfig['flannel-ipv6-masq'];
1563
+ const clusterCIDR = (this.serverConfig['cluster-cidr'] || '');
1564
+ const serviceCIDR = (this.serverConfig['service-cidr'] || '');
1565
+
1566
+ const isDualStack = this.hasDualStackPools;
1567
+ const isIpv6 = this.hasOnlyIpv6Pools;
1568
+
1569
+ const flannelMasqInvalid = isIpv6 && isK3s && !flannelMasqEnabled;
1570
+ const stackPrefInvalid = (isIpv6 && stackPreference !== STACK_PREFS.IPV6) || (isDualStack && ![STACK_PREFS.IPV6, STACK_PREFS.DUAL].includes(stackPreference));
1571
+
1572
+ const clusterCIDRInvalid = (isIpv6 || isDualStack) && !clusterCIDR.includes(':');
1573
+ const serviceCIDRInvalid = (isIpv6 || isDualStack) && !serviceCIDR.includes(':');
1574
+
1575
+ if (!stackPrefInvalid && !flannelMasqInvalid && !clusterCIDRInvalid && !serviceCIDRInvalid) {
1576
+ return;
1577
+ }
1578
+
1579
+ const warnings = [];
1580
+
1581
+ if (stackPrefInvalid) {
1582
+ warnings.push('cluster.rke2.modal.ipv6Warning.stackPrefInvalid');
1583
+ }
1584
+ if (flannelMasqInvalid) {
1585
+ warnings.push('cluster.rke2.modal.ipv6Warning.flannelMasqInvalid');
1586
+ }
1587
+ if (clusterCIDRInvalid || serviceCIDRInvalid) {
1588
+ warnings.push(isK3s ? 'cluster.rke2.modal.ipv6Warning.cidrInvalidK3s' : 'cluster.rke2.modal.ipv6Warning.cidrInvalidRke2');
1589
+ }
1590
+
1591
+ await new Promise((resolve, reject) => {
1592
+ this.$store.dispatch('cluster/promptModal', {
1593
+ component: 'Ipv6NetworkingDialog',
1594
+ componentProps: {
1595
+ warnings,
1596
+ isK3s,
1597
+ confirm: (confirmed) => {
1598
+ if (confirmed) {
1599
+ resolve();
1600
+ } else {
1601
+ reject(new Error('User Cancelled'));
1602
+ }
1603
+ }
1604
+ },
1605
+ });
1606
+ });
1607
+ },
1608
+
1535
1609
  /**
1536
1610
  * Ensure that all the existing node roles pool are at least 1 each
1537
1611
  */
@@ -1778,25 +1852,10 @@ export default {
1778
1852
  });
1779
1853
  },
1780
1854
 
1781
- /**
1782
- * Ensure all chart information required to show addons is available
1783
- *
1784
- * This basically means
1785
- * 1) That the full chart relating to the addon is fetched (which includes core chart, readme and values)
1786
- * 2) We're ready to cache any values the user provides for each addon
1787
- */
1788
- async initAddons() {
1789
- this.addonConfigValidation = {};
1790
-
1791
- for (const chartName of this.addonNames) {
1792
- const entry = this.chartVersions[chartName];
1793
-
1794
- // prevent fetching of addon config for 'none' CNI option
1795
- // https://github.com/rancher/dashboard/issues/10338
1796
- if (this.versionInfo[chartName] || chartName.includes('none')) {
1797
- continue;
1798
- }
1855
+ async getChartValue(chartName) {
1856
+ const entry = this.chartVersions[chartName];
1799
1857
 
1858
+ if (entry) {
1800
1859
  try {
1801
1860
  const res = await this.$store.dispatch('catalog/getVersionInfo', {
1802
1861
  repoType: 'cluster',
@@ -1805,11 +1864,11 @@ export default {
1805
1864
  versionName: entry.version,
1806
1865
  });
1807
1866
 
1808
- this.versionInfo[chartName] = res;
1867
+ this.set(this.versionInfo, chartName, res);
1809
1868
  const key = this.chartVersionKey(chartName);
1810
1869
 
1811
1870
  if (!this.userChartValues[key]) {
1812
- this.userChartValues[key] = {};
1871
+ this.set(this.userChartValues, key, {});
1813
1872
  }
1814
1873
  } catch (e) {
1815
1874
  console.error(`Failed to fetch or process chart info for ${ chartName }`); // eslint-disable-line no-console
@@ -1817,6 +1876,28 @@ export default {
1817
1876
  }
1818
1877
  },
1819
1878
 
1879
+ /**
1880
+ * Ensure all chart information required to show addons is available
1881
+ *
1882
+ * This basically means
1883
+ * 1) That the full chart relating to the addon is fetched (which includes core chart, readme and values)
1884
+ * 2) We're ready to cache any values the user provides for each addon
1885
+ */
1886
+ async initAddons() {
1887
+ this.addonConfigValidation = {};
1888
+ const ingressCharts = !this.isK3s ? ['rke2-ingress-nginx', 'rke2-traefik'] : [];
1889
+
1890
+ for (const chartName of [...this.addonNames, ...ingressCharts]) {
1891
+ // prevent fetching of addon config for 'none' CNI option
1892
+ // https://github.com/rancher/dashboard/issues/10338
1893
+ if (this.versionInfo[chartName] || chartName.includes('none')) {
1894
+ continue;
1895
+ }
1896
+
1897
+ await this.getChartValue(chartName);
1898
+ }
1899
+ },
1900
+
1820
1901
  showAddons(key) {
1821
1902
  this.addonsRev++;
1822
1903
  this.addonNames.forEach((name) => {
@@ -1859,7 +1940,7 @@ export default {
1859
1940
  const fromUser = this.userChartValuesTemp[name];
1860
1941
  const different = diff(fromChart, fromUser);
1861
1942
 
1862
- this.userChartValues[this.chartVersionKey(name)] = different;
1943
+ this.set(this.userChartValues, this.chartVersionKey(name), different);
1863
1944
  }, 250, { leading: true }),
1864
1945
 
1865
1946
  initYamlEditor(name) {
@@ -2148,11 +2229,21 @@ export default {
2148
2229
  }
2149
2230
  },
2150
2231
 
2151
- updateNginxConfiguration(val) {
2152
- if (val.includes(NGINX_SUPPORTED) || !this.nginxSupported) {
2153
- this.serverConfig[INGRESS_CONTROLLER] = undefined;
2154
- } else if (this.serverConfig[INGRESS_CONTROLLER] !== INGRESS_NGINX) {
2155
- this.serverConfig[INGRESS_CONTROLLER] = INGRESS_NGINX;
2232
+ updateNginxConfiguration(disabledServerConfig) {
2233
+ // We only need to explicitly set INGRESS_CONTROLLER for RKE2, we continue to rely on disable list for K3s
2234
+ if (!this.isK3s) {
2235
+ // For new instances, we want Traefik to be default
2236
+ if (this.isCreate) {
2237
+ this.serverConfig[INGRESS_CONTROLLER] = TRAEFIK;
2238
+ // Older existing instances might be relying on default setting, which is changing from nginx to traefik
2239
+ // so we need to make sure to set it to nginx explicitly to avoid breaking existing clusters
2240
+ } else if (!this.serverConfig[INGRESS_CONTROLLER]) {
2241
+ if (!disabledServerConfig.includes(RKE2_INGRESS_NGINX) && this.nginxSupported) {
2242
+ this.serverConfig[INGRESS_CONTROLLER] = INGRESS_NGINX;
2243
+ } else {
2244
+ this.serverConfig[INGRESS_CONTROLLER] = INGRESS_NONE;
2245
+ }
2246
+ }
2156
2247
  }
2157
2248
  },
2158
2249
 
@@ -2241,9 +2332,9 @@ export default {
2241
2332
 
2242
2333
  handleFlannelMasqChanged(neu) {
2243
2334
  if (neu || neu === false) {
2244
- this.serverConfig['enable-flannel-masq'] = neu;
2335
+ this.serverConfig['flannel-ipv6-masq'] = neu;
2245
2336
  } else {
2246
- delete this.serverConfig['enable-flannel-masq'];
2337
+ delete this.serverConfig['flannel-ipv6-masq'];
2247
2338
  }
2248
2339
  },
2249
2340
 
@@ -2286,7 +2377,6 @@ export default {
2286
2377
  handleTabChange(data) {
2287
2378
  this.activeTab = data;
2288
2379
  },
2289
-
2290
2380
  }
2291
2381
  };
2292
2382
  </script>
@@ -2482,10 +2572,10 @@ export default {
2482
2572
  <Basics
2483
2573
  ref="tab-Basics"
2484
2574
  v-model:value="localValue"
2485
- :live-value="liveValue"
2486
2575
  :mode="mode"
2487
2576
  :provider="provider"
2488
2577
  :user-chart-values="userChartValues"
2578
+ :version-info="versionInfo"
2489
2579
  :credential="credential"
2490
2580
  :compliance-override="complianceOverride"
2491
2581
  :all-psas="allPSAs"
@@ -2502,7 +2592,7 @@ export default {
2502
2592
  :cloud-provider-options="cloudProviderOptions"
2503
2593
  :is-azure-provider-unsupported="isAzureProviderUnsupported"
2504
2594
  :can-azure-migrate-on-edit="canAzureMigrateOnEdit"
2505
- :has-some-ipv6-pools="hasSomeIpv6Pools"
2595
+ :has-some-ipv6-pools="hasOnlyIpv6Pools"
2506
2596
  @update:value="$emit('input', $event)"
2507
2597
  @cilium-values-changed="handleCiliumValuesChanged"
2508
2598
  @enabled-system-services-changed="handleEnabledSystemServicesChanged"
@@ -2510,6 +2600,10 @@ export default {
2510
2600
  @compliance-changed="handleComplianceChanged"
2511
2601
  @psa-default-changed="handlePsaDefaultChanged"
2512
2602
  @show-deprecated-patch-versions-changed="handleShowDeprecatedPatchVersionsChanged"
2603
+ @update-values="updateValues"
2604
+ @yaml-validation-changed="e => addonConfigValidationChanged(e.name, e.val)"
2605
+ @config-validation-changed="(val)=>basicsValid = val"
2606
+ @error="e=>errors.push(e)"
2513
2607
  />
2514
2608
  </Tab>
2515
2609
 
@@ -2558,8 +2652,8 @@ export default {
2558
2652
  :selected-version="selectedVersion"
2559
2653
  :truncate-limit="truncateLimit"
2560
2654
  :machine-pools="machinePools"
2561
- :has-some-ipv6-pools="hasSomeIpv6Pools"
2562
- :enable-flannel-masq="serverConfig['enable-flannel-masq']"
2655
+ :has-some-ipv6-pools="hasOnlyIpv6Pools"
2656
+ :flannel-ipv6-masq="serverConfig['flannel-ipv6-masq']"
2563
2657
  @truncate-hostname-changed="truncateHostname"
2564
2658
  @cluster-cidr-changed="(val)=>localValue.spec.rkeConfig.machineGlobalConfig['cluster-cidr'] = val"
2565
2659
  @service-cidr-changed="(val)=>localValue.spec.rkeConfig.machineGlobalConfig['service-cidr'] = val"
@@ -2572,7 +2666,7 @@ export default {
2572
2666
  @fqdn-changed="(val)=>localValue.spec.localClusterAuthEndpoint.fqdn = val"
2573
2667
  @stack-preference-changed="(val)=>localValue.spec.rkeConfig.networking.stackPreference = val"
2574
2668
  @validationChanged="(val)=>stackPreferenceError = !val"
2575
- @enable-flannel-masq-changed="handleFlannelMasqChanged"
2669
+ @flannel-ipv6-masq-changed="handleFlannelMasqChanged"
2576
2670
  />
2577
2671
  </Tab>
2578
2672
 
@@ -2664,7 +2758,7 @@ export default {
2664
2758
  <AgentConfiguration
2665
2759
  v-model:value="value.spec.clusterAgentDeploymentCustomization"
2666
2760
  data-testid="rke2-cluster-agent-config"
2667
- type="cluster"
2761
+ :type="AGENT_CONFIGURATION_TYPES.CLUSTER"
2668
2762
  :mode="mode"
2669
2763
  :scheduling-customization-feature-enabled="schedulingCustomizationFeatureEnabled"
2670
2764
  :default-p-c="clusterAgentDefaultPC"
@@ -2683,8 +2777,13 @@ export default {
2683
2777
  v-if="value.spec.fleetAgentDeploymentCustomization"
2684
2778
  v-model:value="value.spec.fleetAgentDeploymentCustomization"
2685
2779
  data-testid="rke2-fleet-agent-config"
2686
- type="fleet"
2780
+ :type="AGENT_CONFIGURATION_TYPES.FLEET"
2687
2781
  :mode="mode"
2782
+ :scheduling-customization-feature-enabled="schedulingCustomizationFeatureEnabled"
2783
+ :default-p-c="fleetAgentDefaultPC"
2784
+ :default-p-d-b="fleetAgentDefaultPDB"
2785
+ :scheduling-customization-originally-enabled="schedulingCustomizationOriginallyEnabled"
2786
+ @scheduling-customization-changed="setSchedulingCustomization"
2688
2787
  />
2689
2788
  </Tab>
2690
2789
 
@@ -1,4 +1,39 @@
1
1
  export const RETENTION_DEFAULT = 5;
2
- export const NGINX_SUPPORTED = 'rke2-ingress-nginx';
2
+ export const RKE2_INGRESS_NGINX = 'rke2-ingress-nginx';
3
+ export const RKE2_TRAEFIK = 'rke2-traefik';
3
4
  export const INGRESS_NGINX = 'ingress-nginx';
4
5
  export const INGRESS_CONTROLLER = 'ingress-controller';
6
+ export const TRAEFIK = 'traefik';
7
+ export const HARVESTER = 'harvester';
8
+ export const INGRESS_DUAL = 'dual';
9
+ export const INGRESS_NONE = 'none';
10
+ export const INGRESS_OPTIONS = [
11
+ {
12
+ id: TRAEFIK,
13
+ image: { src: require('@shell/assets/images/providers/traefik.png'), alt: 'Traefik' },
14
+ header: { title: { key: 'cluster.ingress.traefik.header' } },
15
+ subHeader: { label: { key: 'cluster.ingress.recommended' } },
16
+ content: { key: 'cluster.ingress.traefik.content' },
17
+ doc: { url: 'https://docs.rke2.io/networking/networking_services?_highlight=ingress#ingress-controller' }
18
+ },
19
+ {
20
+ id: INGRESS_NGINX,
21
+ image: { src: require('@shell/assets/images/providers/kubernetes.svg'), alt: 'NGINX' },
22
+ header: { title: { key: 'cluster.ingress.nginx.header' } },
23
+ subHeader: { label: { key: 'cluster.ingress.legacy' } },
24
+ content: { key: 'cluster.ingress.nginx.content' },
25
+ doc: { url: 'https://www.kubernetes.dev/blog/2025/11/12/ingress-nginx-retirement/' }
26
+ },
27
+ {
28
+ id: INGRESS_DUAL,
29
+ header: { title: { key: 'cluster.ingress.dual.header' } },
30
+ subHeader: { label: { key: 'cluster.ingress.migration' } },
31
+ content: { key: 'cluster.ingress.dual.content' }
32
+ }
33
+ ];
34
+
35
+ export const INGRESS_MIGRATION_KB_LINK = 'https://support.scc.suse.com/s/kb/How-to-migrate-the-Rancher-Ingress-to-Traefik-in-an-RKE2-cluster';
36
+ export const INGRESS_CLASS_DEFAULT = 'nginx';
37
+ export const INGRESS_CONTROLLER_CLASS_DEFAULT = 'k8s.io/ingress-nginx';
38
+ export const INGRESS_CLASS_MIGRATION = 'rke2-ingress-nginx-migration';
39
+ export const INGRESS_CONTROLLER_CLASS_MIGRATION = 'rke2.cattle.io/ingress-nginx-migration';
@@ -41,7 +41,7 @@ export default {
41
41
  },
42
42
 
43
43
  type: {
44
- type: String,
44
+ type: String, // AGENT_CONFIGURATION_TYPES
45
45
  required: true,
46
46
  },
47
47
  schedulingCustomizationFeatureEnabled: {
@@ -325,6 +325,7 @@ export default {
325
325
  <SchedulingCustomization
326
326
  :value="value.schedulingCustomization"
327
327
  :mode="mode"
328
+ :type="type"
328
329
  :feature="schedulingCustomizationFeatureEnabled"
329
330
  :default-p-c="defaultPC"
330
331
  :default-p-d-b="defaultPDB"
@@ -10,17 +10,20 @@ import LabeledSelect from '@shell/components/form/LabeledSelect';
10
10
  import YamlEditor from '@shell/components/YamlEditor';
11
11
  import { LEGACY } from '@shell/store/features';
12
12
  import semver from 'semver';
13
-
14
- const HARVESTER = 'harvester';
13
+ import Ingress from '@shell/edit/provisioning.cattle.io.cluster/tabs/Ingress';
14
+ import {
15
+ HARVESTER, RKE2_INGRESS_NGINX, RKE2_TRAEFIK, INGRESS_CONTROLLER, INGRESS_NGINX
16
+ } from '@shell/edit/provisioning.cattle.io.cluster/shared';
15
17
 
16
18
  export default {
17
- emits: ['enabled-system-services-changed', 'cilium-values-changed', 'kubernetes-changed', 'show-deprecated-patch-versions-changed', 'compliance-changed', 'psa-default-changed'],
19
+ emits: ['enabled-system-services-changed', 'cilium-values-changed', 'kubernetes-changed', 'show-deprecated-patch-versions-changed', 'compliance-changed', 'psa-default-changed', 'update-values', 'error', 'yaml-validation-changed', 'config-validation-changed'],
18
20
 
19
21
  components: {
20
22
  Banner,
21
23
  Checkbox,
22
24
  LabeledSelect,
23
25
  YamlEditor,
26
+ Ingress
24
27
  },
25
28
 
26
29
  mixins: [CreateEditView, FormValidation],
@@ -46,11 +49,14 @@ export default {
46
49
  default: null,
47
50
  required: false
48
51
  },
49
-
50
52
  userChartValues: {
51
53
  type: Object,
52
54
  required: true
53
55
  },
56
+ versionInfo: {
57
+ type: Object,
58
+ required: true
59
+ },
54
60
  complianceOverride: {
55
61
  type: Boolean,
56
62
  required: true
@@ -165,6 +171,15 @@ export default {
165
171
 
166
172
  return out;
167
173
  },
174
+ ingressController: {
175
+ get() {
176
+ return this.serverConfig && this.serverConfig[INGRESS_CONTROLLER] ? this.serverConfig[INGRESS_CONTROLLER] : INGRESS_NGINX ;
177
+ },
178
+
179
+ set(neu) {
180
+ this.serverConfig[INGRESS_CONTROLLER] = neu;
181
+ },
182
+ },
168
183
 
169
184
  /**
170
185
  * Allow to display override if PSA is needed and profile is set
@@ -229,13 +244,21 @@ export default {
229
244
  },
230
245
 
231
246
  disableOptions() {
232
- return (this.serverArgs.disable.options || []).map((value) => {
247
+ // For RKE2 clusters Ingress is configured separately, so we should not allow disabling it here
248
+ return (this.serverArgs.disable.options || []).filter((value) => value !== RKE2_INGRESS_NGINX && value !== RKE2_TRAEFIK).map((value) => {
233
249
  return {
234
250
  label: this.$store.getters['i18n/withFallback'](`cluster.${ this.value.isK3s ? 'k3s' : 'rke2' }.systemService."${ value }"`, null, value.replace(/^(rke2|rancher)-/, '')),
235
251
  value,
236
252
  };
237
253
  });
238
254
  },
255
+ nginxSupported() {
256
+ if (this.serverArgs?.disable?.options.includes(RKE2_INGRESS_NGINX)) {
257
+ return true;
258
+ }
259
+
260
+ return false;
261
+ },
239
262
 
240
263
  serverArgs() {
241
264
  return this.selectedVersion?.serverArgs || {};
@@ -336,6 +359,13 @@ export default {
336
359
  }
337
360
  },
338
361
 
362
+ nginxChart() {
363
+ return this.chartVersionKey(RKE2_INGRESS_NGINX);
364
+ },
365
+ traefikChart() {
366
+ return this.chartVersionKey(RKE2_TRAEFIK);
367
+ },
368
+
339
369
  canNotEditCloudProvider() {
340
370
  if (!this.isEdit) {
341
371
  return false;
@@ -363,6 +393,9 @@ export default {
363
393
  */
364
394
  showCloudProviderMigrateAzureWarning() {
365
395
  return this.showCloudProvider && this.isEdit && this.canAzureMigrateOnEdit;
396
+ },
397
+ showIngress() {
398
+ return !this.value?.isK3s;
366
399
  }
367
400
  },
368
401
 
@@ -382,7 +415,7 @@ export default {
382
415
  <template>
383
416
  <div>
384
417
  <Banner
385
- v-if="!haveArgInfo"
418
+ v-if="!haveArgInfo || ((!nginxChart || !traefikChart) && showIngress)"
386
419
  color="warning"
387
420
  :label="t('cluster.banner.haveArgInfo')"
388
421
  />
@@ -623,7 +656,7 @@ export default {
623
656
 
624
657
  <div
625
658
  v-if="serverArgs.disable"
626
- class="row"
659
+ class="row mb-30"
627
660
  >
628
661
  <div class="col span-12">
629
662
  <div>
@@ -641,6 +674,21 @@ export default {
641
674
  />
642
675
  </div>
643
676
  </div>
677
+ <!-- Ingress -->
678
+ <Ingress
679
+ v-if="showIngress"
680
+ v-model:value="ingressController"
681
+ :mode="mode"
682
+ :nginx-supported="nginxSupported"
683
+ :nginx-chart="nginxChart"
684
+ :traefik-chart="traefikChart"
685
+ :user-chart-values="userChartValues"
686
+ :version-info="versionInfo"
687
+ @update-values="(name, val) => $emit('update-values', name, val)"
688
+ @error="$emit('error', $event)"
689
+ @yaml-validation-changed="e => $emit('yaml-validation-changed', e)"
690
+ @config-validation-changed="e => $emit('config-validation-changed', e)"
691
+ />
644
692
  </div>
645
693
  </template>
646
694