@rancher/shell 3.0.9-rc.5 → 3.0.9

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 (172) 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/IconOrSvg.vue +61 -42
  10. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +109 -0
  11. package/components/Resource/Detail/Card/StatusCard/index.vue +21 -4
  12. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +19 -2
  13. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +19 -11
  14. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +12 -0
  15. package/components/Resource/Detail/ResourcePopover/index.vue +2 -0
  16. package/components/Resource/Detail/ResourceRow.vue +2 -2
  17. package/components/ResourceList/index.vue +7 -4
  18. package/components/SortableTable/index.vue +2 -2
  19. package/components/Window/ContainerLogs.vue +48 -37
  20. package/components/fleet/FleetClusterTargets/TargetsList.vue +2 -2
  21. package/components/fleet/FleetClusterTargets/index.vue +6 -1
  22. package/components/fleet/GitRepoAdvancedTab.vue +333 -0
  23. package/components/fleet/GitRepoMetadataTab.vue +43 -0
  24. package/components/fleet/GitRepoRepositoryTab.vue +101 -0
  25. package/components/fleet/GitRepoTargetTab.vue +77 -0
  26. package/components/fleet/HelmOpAdvancedTab.vue +247 -0
  27. package/components/fleet/HelmOpChartTab.vue +158 -0
  28. package/components/fleet/HelmOpMetadataTab.vue +46 -0
  29. package/components/fleet/HelmOpTargetTab.vue +84 -0
  30. package/components/fleet/HelmOpValuesTab.vue +147 -0
  31. package/components/fleet/__tests__/FleetClusterTargets.test.ts +119 -70
  32. package/components/form/BannerSettings.vue +2 -2
  33. package/components/form/NodeScheduling.vue +81 -7
  34. package/components/form/NotificationSettings.vue +2 -2
  35. package/components/form/PodAffinity.vue +1 -36
  36. package/components/form/ResourceLabeledSelect.vue +8 -4
  37. package/components/form/ResourceQuota/Namespace.vue +30 -9
  38. package/components/form/ResourceQuota/NamespaceRow.vue +25 -7
  39. package/components/form/ResourceQuota/Project.vue +140 -82
  40. package/components/form/ResourceQuota/ResourceQuotaEntry.vue +145 -0
  41. package/components/form/ResourceQuota/__tests__/Namespace.test.ts +307 -0
  42. package/components/form/ResourceQuota/__tests__/NamespaceRow.test.ts +281 -0
  43. package/components/form/ResourceQuota/__tests__/Project.test.ts +274 -27
  44. package/components/form/ResourceQuota/__tests__/ResourceQuotaEntry.test.ts +215 -0
  45. package/components/form/SchedulingCustomization.vue +14 -6
  46. package/components/form/SelectOrCreateAuthSecret.vue +107 -18
  47. package/components/form/__tests__/NodeScheduling.test.ts +12 -9
  48. package/components/form/__tests__/PodAffinity.test.ts +21 -2
  49. package/components/form/__tests__/SchedulingCustomization.test.ts +240 -0
  50. package/components/formatter/ClusterLink.vue +8 -0
  51. package/components/formatter/SecretOrigin.vue +79 -0
  52. package/config/labels-annotations.js +7 -6
  53. package/config/pagination-table-headers.js +6 -4
  54. package/config/product/explorer.js +1 -11
  55. package/config/product/manager.js +0 -1
  56. package/config/query-params.js +3 -0
  57. package/config/settings.ts +15 -2
  58. package/config/table-headers.js +21 -17
  59. package/config/types.js +23 -8
  60. package/detail/fleet.cattle.io.cluster.vue +1 -1
  61. package/detail/workload/index.vue +11 -16
  62. package/dialog/DeactivateDriverDialog.vue +1 -1
  63. package/dialog/FeatureFlagListDialog.vue +1 -1
  64. package/dialog/Ipv6NetworkingDialog.vue +156 -0
  65. package/dialog/ScalePoolDownDialog.vue +2 -2
  66. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
  67. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +1 -0
  68. package/edit/__tests__/management.cattle.io.project.test.js +56 -128
  69. package/edit/auth/oidc.vue +1 -1
  70. package/edit/catalog.cattle.io.clusterrepo.vue +155 -25
  71. package/edit/fleet.cattle.io.gitrepo.vue +153 -283
  72. package/edit/fleet.cattle.io.helmop.vue +190 -332
  73. package/edit/management.cattle.io.project.vue +5 -42
  74. package/edit/management.cattle.io.setting.vue +6 -0
  75. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
  76. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
  77. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
  78. package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
  79. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
  80. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
  81. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
  82. package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
  83. package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
  84. package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
  85. package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
  86. package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
  87. package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
  88. package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
  89. package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
  90. package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
  91. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +55 -24
  92. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +1 -103
  93. package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +13 -1
  94. package/edit/provisioning.cattle.io.cluster/__tests__/rke2-fleet-cluster-agent.test.ts +283 -0
  95. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -49
  96. package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +114 -0
  97. package/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue +158 -0
  98. package/edit/provisioning.cattle.io.cluster/rke2.vue +167 -69
  99. package/edit/provisioning.cattle.io.cluster/shared.ts +36 -1
  100. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +2 -1
  101. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +70 -7
  102. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +343 -0
  103. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +2 -1
  104. package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +13 -1
  105. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +10 -44
  106. package/edit/secret/index.vue +1 -1
  107. package/edit/token.vue +68 -29
  108. package/edit/workload/__tests__/index.test.ts +2 -37
  109. package/edit/workload/index.vue +6 -2
  110. package/edit/workload/mixins/workload.js +0 -32
  111. package/list/__tests__/management.cattle.io.setting.test.ts +198 -0
  112. package/list/management.cattle.io.setting.vue +13 -0
  113. package/list/provisioning.cattle.io.cluster.vue +50 -1
  114. package/list/secret.vue +4 -9
  115. package/list/service.vue +6 -8
  116. package/machine-config/amazonec2.vue +11 -4
  117. package/machine-config/components/EC2Networking.vue +46 -30
  118. package/machine-config/components/__tests__/EC2Networking.test.ts +7 -7
  119. package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +0 -9
  120. package/machine-config/digitalocean.vue +3 -3
  121. package/models/__tests__/chart.test.ts +2 -2
  122. package/models/__tests__/namespace.test.ts +11 -0
  123. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +96 -0
  124. package/models/__tests__/workload.test.ts +42 -1
  125. package/models/catalog.cattle.io.clusterrepo.js +30 -4
  126. package/models/chart.js +3 -3
  127. package/models/ext.cattle.io.token.js +48 -0
  128. package/models/kontainerdriver.js +2 -2
  129. package/models/namespace.js +7 -1
  130. package/models/nodedriver.js +2 -2
  131. package/models/provisioning.cattle.io.cluster.js +28 -7
  132. package/models/secret.js +0 -17
  133. package/models/service.js +44 -1
  134. package/models/token.js +4 -0
  135. package/models/workload.js +12 -6
  136. package/package.json +1 -1
  137. package/pages/account/index.vue +96 -67
  138. package/pages/auth/setup.vue +5 -14
  139. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +45 -18
  140. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +4 -1
  141. package/pages/c/_cluster/apps/charts/index.vue +82 -3
  142. package/pages/c/_cluster/apps/charts/install.vue +317 -42
  143. package/pages/c/_cluster/explorer/tools/index.vue +1 -1
  144. package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
  145. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -4
  146. package/pages/c/_cluster/settings/index.vue +3 -1
  147. package/pages/c/_cluster/uiplugins/index.vue +1 -1
  148. package/plugins/dashboard-store/__tests__/getters.test.ts +108 -0
  149. package/plugins/dashboard-store/__tests__/resource-class.test.ts +27 -0
  150. package/plugins/dashboard-store/actions.js +3 -8
  151. package/plugins/dashboard-store/getters.js +7 -5
  152. package/plugins/dashboard-store/mutations.js +4 -1
  153. package/plugins/dashboard-store/resource-class.js +3 -3
  154. package/plugins/steve/__tests__/steve-class.test.ts +102 -141
  155. package/plugins/steve/steve-class.js +12 -3
  156. package/plugins/steve/steve-pagination-utils.ts +6 -2
  157. package/rancher-components/RcIcon/types.ts +2 -0
  158. package/rancher-components/RcItemCard/RcItemCard.vue +72 -20
  159. package/store/prefs.js +3 -0
  160. package/types/aws-sdk.d.ts +121 -0
  161. package/types/resources/node.ts +15 -0
  162. package/types/shell/index.d.ts +537 -506
  163. package/types/store/pagination.types.ts +5 -5
  164. package/utils/__tests__/array.test.ts +1 -29
  165. package/utils/__tests__/cluster-agent-configuration.test.ts +203 -0
  166. package/utils/array.ts +0 -11
  167. package/utils/aws.ts +21 -0
  168. package/utils/cluster.js +22 -2
  169. package/utils/selector-typed.ts +1 -1
  170. package/utils/svg-filter.js +4 -3
  171. package/components/__tests__/ProjectRow.test.ts +0 -206
  172. package/components/form/ResourceQuota/ProjectRow.vue +0 -277
@@ -0,0 +1,158 @@
1
+ <script setup lang="ts">
2
+ import { computed, watch } from 'vue';
3
+ import { _CREATE } from '@shell/config/query-params';
4
+ import { useStore } from 'vuex';
5
+ import { useI18n } from '@shell/composables/useI18n';
6
+ import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
7
+ import { INGRESS_DUAL, TRAEFIK, INGRESS_NGINX } from '@shell/edit/provisioning.cattle.io.cluster/shared';
8
+ import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
9
+ import formRulesGenerator from '@shell/utils/validators/formRules';
10
+
11
+ interface Props {
12
+ mode?: string;
13
+ ingressSelection: string;
14
+ }
15
+ const {
16
+ mode = _CREATE,
17
+ ingressSelection,
18
+ } = defineProps<Props>();
19
+ const store = useStore();
20
+ const { t } = useI18n(store);
21
+
22
+ const emit = defineEmits(['validation-changed']);
23
+
24
+ const compatibilityMode = defineModel<boolean>('compatibilityMode');
25
+ const nginxHttp = defineModel<number>('nginxHttp');
26
+ const nginxHttps = defineModel<number>('nginxHttps');
27
+ const traefikHttp = defineModel<number>('traefikHttp');
28
+ const traefikHttps = defineModel<number>('traefikHttps');
29
+
30
+ const nginxHttpRules = computed(() => {
31
+ const { portNumber, isInteger } = formRulesGenerator(t, { key: t('cluster.ingress.configurationOptions.nginx.http') });
32
+ const portRules = [(val: any) => !val ? undefined : isInteger(val), portNumber];
33
+
34
+ return [...portRules,
35
+ (val: any) => {
36
+ if (ingressSelection === INGRESS_DUAL && String(val) === String(traefikHttp.value)) {
37
+ return t('cluster.ingress.validation.portsMatch');
38
+ }
39
+
40
+ return undefined;
41
+ }
42
+ ];
43
+ });
44
+
45
+ const nginxHttpsRules = computed(() => {
46
+ const { portNumber, isInteger } = formRulesGenerator(t, { key: t('cluster.ingress.configurationOptions.nginx.https') });
47
+ const portRules = [(val: any) => !val ? undefined : isInteger(val), portNumber];
48
+
49
+ return [...portRules,
50
+ (val: any) => {
51
+ if (ingressSelection === INGRESS_DUAL && String(val) === String(traefikHttps.value)) {
52
+ return t('cluster.ingress.validation.portsMatch');
53
+ }
54
+
55
+ return undefined;
56
+ }
57
+ ];
58
+ });
59
+
60
+ const traefikHttpRules = computed(() => {
61
+ const { portNumber, isInteger } = formRulesGenerator(t, { key: t('cluster.ingress.configurationOptions.traefik.http') });
62
+ const portRules = [(val: any) => !val ? undefined : isInteger(val), portNumber];
63
+
64
+ return [...portRules, (val: any) => {
65
+ if (ingressSelection === INGRESS_DUAL && String(val) === String(nginxHttp.value)) {
66
+ return t('cluster.ingress.validation.portsMatch');
67
+ }
68
+
69
+ return undefined;
70
+ }
71
+ ];
72
+ });
73
+
74
+ const traefikHttpsRules = computed(() => {
75
+ const { portNumber, isInteger } = formRulesGenerator(t, { key: t('cluster.ingress.configurationOptions.traefik.https') });
76
+ const portRules = [(val: any) => !val ? undefined : isInteger(val), portNumber];
77
+
78
+ return [...portRules, (val: any) => {
79
+ if (ingressSelection === INGRESS_DUAL && String(val) === String(nginxHttps.value)) {
80
+ return t('cluster.ingress.validation.portsMatch');
81
+ }
82
+
83
+ return undefined;
84
+ }
85
+ ];
86
+ });
87
+
88
+ const isValid = computed(() => {
89
+ const check = (val: any, rules: any[]) => rules.every((rule) => rule(val) === undefined);
90
+
91
+ if (ingressSelection === INGRESS_DUAL) {
92
+ return check(nginxHttp.value, nginxHttpRules.value) && check(nginxHttps.value, nginxHttpsRules.value) &&
93
+ check(traefikHttp.value, traefikHttpRules.value) && check(traefikHttps.value, traefikHttpsRules.value);
94
+ }
95
+ if (ingressSelection === TRAEFIK) {
96
+ return check(traefikHttp.value, traefikHttpRules.value) && check(traefikHttps.value, traefikHttpsRules.value);
97
+ }
98
+ if (ingressSelection === INGRESS_NGINX) {
99
+ return check(nginxHttp.value, nginxHttpRules.value) && check(nginxHttps.value, nginxHttpsRules.value);
100
+ }
101
+
102
+ return true;
103
+ });
104
+
105
+ watch(isValid, (val) => emit('validation-changed', val), { immediate: true });
106
+ </script>
107
+ <template>
108
+ <h4>{{ t('cluster.ingress.configurationOptions.title') }}</h4>
109
+ <Checkbox
110
+ v-if="ingressSelection === INGRESS_DUAL"
111
+ v-model:value="compatibilityMode"
112
+ label-key="cluster.ingress.configurationOptions.compatibilityMode"
113
+ class="mb-10"
114
+ />
115
+ <div
116
+ v-if="ingressSelection === TRAEFIK || ingressSelection === INGRESS_DUAL"
117
+ class="row"
118
+ :class="ingressSelection === INGRESS_DUAL ? 'mb-10' : ''"
119
+ >
120
+ <div class="span-2 mr-20">
121
+ <LabeledInput
122
+ v-model:value="traefikHttp"
123
+ label-key="cluster.ingress.configurationOptions.traefik.http"
124
+ :mode="mode"
125
+ :rules="traefikHttpRules"
126
+ />
127
+ </div>
128
+ <div class="span-2">
129
+ <LabeledInput
130
+ v-model:value="traefikHttps"
131
+ label-key="cluster.ingress.configurationOptions.traefik.https"
132
+ :mode="mode"
133
+ :rules="traefikHttpsRules"
134
+ />
135
+ </div>
136
+ </div>
137
+ <div
138
+ v-if="ingressSelection === INGRESS_NGINX || ingressSelection === INGRESS_DUAL"
139
+ class="row"
140
+ >
141
+ <div class="span-2 mr-20">
142
+ <LabeledInput
143
+ v-model:value="nginxHttp"
144
+ label-key="cluster.ingress.configurationOptions.nginx.http"
145
+ :mode="mode"
146
+ :rules="nginxHttpRules"
147
+ />
148
+ </div>
149
+ <div class="span-2">
150
+ <LabeledInput
151
+ v-model:value="nginxHttps"
152
+ label-key="cluster.ingress.configurationOptions.nginx.https"
153
+ :mode="mode"
154
+ :rules="nginxHttpsRules"
155
+ />
156
+ </div>
157
+ </div>
158
+ </template>
@@ -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
 
@@ -964,7 +979,6 @@ export default {
964
979
  }
965
980
 
966
981
  this.versionInfo = {}; // Invalidate cache such that version info relevant to selected kube version is updated
967
-
968
982
  // Allow time for addonNames to update... then fetch any missing addons
969
983
  this.$nextTick(() => this.initAddons());
970
984
  if (this.mode === _CREATE) {
@@ -995,22 +1009,11 @@ export default {
995
1009
  this.agentConfig['cloud-provider-name'] = undefined;
996
1010
  }
997
1011
  },
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
1012
  },
1011
1013
 
1012
1014
  created() {
1013
- this.registerBeforeHook(this.saveMachinePools, 'save-machine-pools', 1);
1015
+ this.registerBeforeHook(this.showIpv6Warning, 'show-ipv6-warning', 1);
1016
+ this.registerBeforeHook(this.saveMachinePools, 'save-machine-pools', 2);
1014
1017
  this.registerBeforeHook(this.setRegistryConfig, 'set-registry-config');
1015
1018
  this.registerBeforeHook(this.handleVsphereCpiSecret, 'sync-vsphere-cpi');
1016
1019
  this.registerBeforeHook(this.handleVsphereCsiSecret, 'sync-vsphere-csi');
@@ -1162,11 +1165,27 @@ export default {
1162
1165
  }
1163
1166
  },
1164
1167
 
1165
- setSchedulingCustomization(val) {
1166
- if (val) {
1167
- set(this.value, 'spec.clusterAgentDeploymentCustomization.schedulingCustomization', { priorityClass: this.clusterAgentDefaultPC, podDisruptionBudget: this.clusterAgentDefaultPDB });
1168
+ setSchedulingCustomization({ event, agentType }) {
1169
+ if (event) {
1170
+ switch (agentType) {
1171
+ case AGENT_CONFIGURATION_TYPES.CLUSTER:
1172
+ set(this.value, 'spec.clusterAgentDeploymentCustomization.schedulingCustomization', { priorityClass: this.clusterAgentDefaultPC, podDisruptionBudget: this.clusterAgentDefaultPDB });
1173
+ break;
1174
+ case AGENT_CONFIGURATION_TYPES.FLEET:
1175
+ set(this.value, 'spec.fleetAgentDeploymentCustomization.schedulingCustomization', { priorityClass: this.fleetAgentDefaultPC, podDisruptionBudget: this.fleetAgentDefaultPDB });
1176
+ break;
1177
+ default:
1178
+ }
1168
1179
  } else {
1169
- delete this.value.spec.clusterAgentDeploymentCustomization.schedulingCustomization;
1180
+ switch (agentType) {
1181
+ case AGENT_CONFIGURATION_TYPES.CLUSTER:
1182
+ delete this.value.spec.clusterAgentDeploymentCustomization.schedulingCustomization;
1183
+ break;
1184
+ case AGENT_CONFIGURATION_TYPES.FLEET:
1185
+ delete this.value.spec.fleetAgentDeploymentCustomization.schedulingCustomization;
1186
+ break;
1187
+ default:
1188
+ }
1170
1189
  }
1171
1190
  },
1172
1191
 
@@ -1335,14 +1354,15 @@ export default {
1335
1354
  const name = `pool${ ++this.lastIdx }`;
1336
1355
 
1337
1356
  const pool = {
1338
- id: name,
1357
+ id: name,
1339
1358
  config,
1340
- remove: false,
1341
- create: true,
1342
- update: false,
1343
- uid: name,
1344
- hasIpv6: false,
1345
- pool: {
1359
+ remove: false,
1360
+ create: true,
1361
+ update: false,
1362
+ uid: name,
1363
+ isIpv6: false,
1364
+ isDualStack: false,
1365
+ pool: {
1346
1366
  name,
1347
1367
  etcdRole: numCurrentPools === 0,
1348
1368
  controlPlaneRole: numCurrentPools === 0,
@@ -1532,6 +1552,59 @@ export default {
1532
1552
  }
1533
1553
  },
1534
1554
 
1555
+ async showIpv6Warning(hookContext) {
1556
+ if (this.mode !== _CREATE || !this.machinePools?.length) {
1557
+ return;
1558
+ }
1559
+ const stackPreference = this.value.spec.rkeConfig.networking.stackPreference;
1560
+ const isK3s = (this.selectedVersion?.label || '').toLowerCase().includes('k3s');
1561
+ const flannelMasqEnabled = this.serverConfig['flannel-ipv6-masq'];
1562
+ const clusterCIDR = (this.serverConfig['cluster-cidr'] || '');
1563
+ const serviceCIDR = (this.serverConfig['service-cidr'] || '');
1564
+
1565
+ const isDualStack = this.hasDualStackPools;
1566
+ const isIpv6 = this.hasOnlyIpv6Pools;
1567
+
1568
+ const flannelMasqInvalid = isIpv6 && isK3s && !flannelMasqEnabled;
1569
+ const stackPrefInvalid = (isIpv6 && stackPreference !== STACK_PREFS.IPV6) || (isDualStack && ![STACK_PREFS.IPV6, STACK_PREFS.DUAL].includes(stackPreference));
1570
+
1571
+ const clusterCIDRInvalid = (isIpv6 || isDualStack) && !clusterCIDR.includes(':');
1572
+ const serviceCIDRInvalid = (isIpv6 || isDualStack) && !serviceCIDR.includes(':');
1573
+
1574
+ if (!stackPrefInvalid && !flannelMasqInvalid && !clusterCIDRInvalid && !serviceCIDRInvalid) {
1575
+ return;
1576
+ }
1577
+
1578
+ const warnings = [];
1579
+
1580
+ if (stackPrefInvalid) {
1581
+ warnings.push('cluster.rke2.modal.ipv6Warning.stackPrefInvalid');
1582
+ }
1583
+ if (flannelMasqInvalid) {
1584
+ warnings.push('cluster.rke2.modal.ipv6Warning.flannelMasqInvalid');
1585
+ }
1586
+ if (clusterCIDRInvalid || serviceCIDRInvalid) {
1587
+ warnings.push(isK3s ? 'cluster.rke2.modal.ipv6Warning.cidrInvalidK3s' : 'cluster.rke2.modal.ipv6Warning.cidrInvalidRke2');
1588
+ }
1589
+
1590
+ await new Promise((resolve, reject) => {
1591
+ this.$store.dispatch('cluster/promptModal', {
1592
+ component: 'Ipv6NetworkingDialog',
1593
+ componentProps: {
1594
+ warnings,
1595
+ isK3s,
1596
+ confirm: (confirmed) => {
1597
+ if (confirmed) {
1598
+ resolve();
1599
+ } else {
1600
+ reject(new Error('User Cancelled'));
1601
+ }
1602
+ }
1603
+ },
1604
+ });
1605
+ });
1606
+ },
1607
+
1535
1608
  /**
1536
1609
  * Ensure that all the existing node roles pool are at least 1 each
1537
1610
  */
@@ -1778,25 +1851,10 @@ export default {
1778
1851
  });
1779
1852
  },
1780
1853
 
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
- }
1854
+ async getChartValue(chartName) {
1855
+ const entry = this.chartVersions[chartName];
1799
1856
 
1857
+ if (entry) {
1800
1858
  try {
1801
1859
  const res = await this.$store.dispatch('catalog/getVersionInfo', {
1802
1860
  repoType: 'cluster',
@@ -1817,6 +1875,28 @@ export default {
1817
1875
  }
1818
1876
  },
1819
1877
 
1878
+ /**
1879
+ * Ensure all chart information required to show addons is available
1880
+ *
1881
+ * This basically means
1882
+ * 1) That the full chart relating to the addon is fetched (which includes core chart, readme and values)
1883
+ * 2) We're ready to cache any values the user provides for each addon
1884
+ */
1885
+ async initAddons() {
1886
+ this.addonConfigValidation = {};
1887
+ const ingressCharts = !this.isK3s ? ['rke2-ingress-nginx', 'rke2-traefik'] : [];
1888
+
1889
+ for (const chartName of [...this.addonNames, ...ingressCharts]) {
1890
+ // prevent fetching of addon config for 'none' CNI option
1891
+ // https://github.com/rancher/dashboard/issues/10338
1892
+ if (this.versionInfo[chartName] || chartName.includes('none')) {
1893
+ continue;
1894
+ }
1895
+
1896
+ await this.getChartValue(chartName);
1897
+ }
1898
+ },
1899
+
1820
1900
  showAddons(key) {
1821
1901
  this.addonsRev++;
1822
1902
  this.addonNames.forEach((name) => {
@@ -2148,11 +2228,21 @@ export default {
2148
2228
  }
2149
2229
  },
2150
2230
 
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;
2231
+ updateNginxConfiguration(disabledServerConfig) {
2232
+ // We only need to explicitly set INGRESS_CONTROLLER for RKE2, we continue to rely on disable list for K3s
2233
+ if (!this.isK3s) {
2234
+ // For new instances, we want Traefik to be default
2235
+ if (this.isCreate) {
2236
+ this.serverConfig[INGRESS_CONTROLLER] = TRAEFIK;
2237
+ // Older existing instances might be relying on default setting, which is changing from nginx to traefik
2238
+ // so we need to make sure to set it to nginx explicitly to avoid breaking existing clusters
2239
+ } else if (!this.serverConfig[INGRESS_CONTROLLER]) {
2240
+ if (!disabledServerConfig.includes(RKE2_INGRESS_NGINX) && this.nginxSupported) {
2241
+ this.serverConfig[INGRESS_CONTROLLER] = INGRESS_NGINX;
2242
+ } else {
2243
+ this.serverConfig[INGRESS_CONTROLLER] = INGRESS_NONE;
2244
+ }
2245
+ }
2156
2246
  }
2157
2247
  },
2158
2248
 
@@ -2241,9 +2331,9 @@ export default {
2241
2331
 
2242
2332
  handleFlannelMasqChanged(neu) {
2243
2333
  if (neu || neu === false) {
2244
- this.serverConfig['enable-flannel-masq'] = neu;
2334
+ this.serverConfig['flannel-ipv6-masq'] = neu;
2245
2335
  } else {
2246
- delete this.serverConfig['enable-flannel-masq'];
2336
+ delete this.serverConfig['flannel-ipv6-masq'];
2247
2337
  }
2248
2338
  },
2249
2339
 
@@ -2286,7 +2376,6 @@ export default {
2286
2376
  handleTabChange(data) {
2287
2377
  this.activeTab = data;
2288
2378
  },
2289
-
2290
2379
  }
2291
2380
  };
2292
2381
  </script>
@@ -2482,10 +2571,10 @@ export default {
2482
2571
  <Basics
2483
2572
  ref="tab-Basics"
2484
2573
  v-model:value="localValue"
2485
- :live-value="liveValue"
2486
2574
  :mode="mode"
2487
2575
  :provider="provider"
2488
2576
  :user-chart-values="userChartValues"
2577
+ :version-info="versionInfo"
2489
2578
  :credential="credential"
2490
2579
  :compliance-override="complianceOverride"
2491
2580
  :all-psas="allPSAs"
@@ -2502,7 +2591,7 @@ export default {
2502
2591
  :cloud-provider-options="cloudProviderOptions"
2503
2592
  :is-azure-provider-unsupported="isAzureProviderUnsupported"
2504
2593
  :can-azure-migrate-on-edit="canAzureMigrateOnEdit"
2505
- :has-some-ipv6-pools="hasSomeIpv6Pools"
2594
+ :has-some-ipv6-pools="hasOnlyIpv6Pools"
2506
2595
  @update:value="$emit('input', $event)"
2507
2596
  @cilium-values-changed="handleCiliumValuesChanged"
2508
2597
  @enabled-system-services-changed="handleEnabledSystemServicesChanged"
@@ -2510,6 +2599,10 @@ export default {
2510
2599
  @compliance-changed="handleComplianceChanged"
2511
2600
  @psa-default-changed="handlePsaDefaultChanged"
2512
2601
  @show-deprecated-patch-versions-changed="handleShowDeprecatedPatchVersionsChanged"
2602
+ @update-values="updateValues"
2603
+ @yaml-validation-changed="e => addonConfigValidationChanged(e.name, e.val)"
2604
+ @config-validation-changed="(val)=>basicsValid = val"
2605
+ @error="e=>errors.push(e)"
2513
2606
  />
2514
2607
  </Tab>
2515
2608
 
@@ -2558,8 +2651,8 @@ export default {
2558
2651
  :selected-version="selectedVersion"
2559
2652
  :truncate-limit="truncateLimit"
2560
2653
  :machine-pools="machinePools"
2561
- :has-some-ipv6-pools="hasSomeIpv6Pools"
2562
- :enable-flannel-masq="serverConfig['enable-flannel-masq']"
2654
+ :has-some-ipv6-pools="hasOnlyIpv6Pools"
2655
+ :flannel-ipv6-masq="serverConfig['flannel-ipv6-masq']"
2563
2656
  @truncate-hostname-changed="truncateHostname"
2564
2657
  @cluster-cidr-changed="(val)=>localValue.spec.rkeConfig.machineGlobalConfig['cluster-cidr'] = val"
2565
2658
  @service-cidr-changed="(val)=>localValue.spec.rkeConfig.machineGlobalConfig['service-cidr'] = val"
@@ -2572,7 +2665,7 @@ export default {
2572
2665
  @fqdn-changed="(val)=>localValue.spec.localClusterAuthEndpoint.fqdn = val"
2573
2666
  @stack-preference-changed="(val)=>localValue.spec.rkeConfig.networking.stackPreference = val"
2574
2667
  @validationChanged="(val)=>stackPreferenceError = !val"
2575
- @enable-flannel-masq-changed="handleFlannelMasqChanged"
2668
+ @flannel-ipv6-masq-changed="handleFlannelMasqChanged"
2576
2669
  />
2577
2670
  </Tab>
2578
2671
 
@@ -2664,7 +2757,7 @@ export default {
2664
2757
  <AgentConfiguration
2665
2758
  v-model:value="value.spec.clusterAgentDeploymentCustomization"
2666
2759
  data-testid="rke2-cluster-agent-config"
2667
- type="cluster"
2760
+ :type="AGENT_CONFIGURATION_TYPES.CLUSTER"
2668
2761
  :mode="mode"
2669
2762
  :scheduling-customization-feature-enabled="schedulingCustomizationFeatureEnabled"
2670
2763
  :default-p-c="clusterAgentDefaultPC"
@@ -2683,8 +2776,13 @@ export default {
2683
2776
  v-if="value.spec.fleetAgentDeploymentCustomization"
2684
2777
  v-model:value="value.spec.fleetAgentDeploymentCustomization"
2685
2778
  data-testid="rke2-fleet-agent-config"
2686
- type="fleet"
2779
+ :type="AGENT_CONFIGURATION_TYPES.FLEET"
2687
2780
  :mode="mode"
2781
+ :scheduling-customization-feature-enabled="schedulingCustomizationFeatureEnabled"
2782
+ :default-p-c="fleetAgentDefaultPC"
2783
+ :default-p-d-b="fleetAgentDefaultPDB"
2784
+ :scheduling-customization-originally-enabled="schedulingCustomizationOriginallyEnabled"
2785
+ @scheduling-customization-changed="setSchedulingCustomization"
2688
2786
  />
2689
2787
  </Tab>
2690
2788
 
@@ -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"