@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
@@ -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, INGRESS_NONE
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,25 @@ export default {
165
171
 
166
172
  return out;
167
173
  },
174
+ ingressController: {
175
+ get() {
176
+ if (this.serverConfig) {
177
+ if (this.serverConfig[INGRESS_CONTROLLER]) {
178
+ return this.serverConfig[INGRESS_CONTROLLER];
179
+ } else {
180
+ if (this.serverConfig.disable && this.serverConfig.disable.includes(RKE2_INGRESS_NGINX)) {
181
+ return INGRESS_NONE;
182
+ }
183
+ }
184
+ }
185
+
186
+ return INGRESS_NGINX;
187
+ },
188
+
189
+ set(neu) {
190
+ this.serverConfig[INGRESS_CONTROLLER] = neu;
191
+ },
192
+ },
168
193
 
169
194
  /**
170
195
  * Allow to display override if PSA is needed and profile is set
@@ -229,13 +254,25 @@ export default {
229
254
  },
230
255
 
231
256
  disableOptions() {
232
- return (this.serverArgs.disable.options || []).map((value) => {
257
+ // For RKE2 clusters Ingress is configured separately, so we should not allow disabling it here
258
+ return (this.serverArgs.disable.options || []).filter((value) => value !== RKE2_INGRESS_NGINX && value !== RKE2_TRAEFIK).map((value) => {
233
259
  return {
234
260
  label: this.$store.getters['i18n/withFallback'](`cluster.${ this.value.isK3s ? 'k3s' : 'rke2' }.systemService."${ value }"`, null, value.replace(/^(rke2|rancher)-/, '')),
235
261
  value,
236
262
  };
237
263
  });
238
264
  },
265
+ nginxSupported() {
266
+ if (Object.keys(this.serverArgs).length === 0 || this.serverArgs?.disable?.options.includes(RKE2_INGRESS_NGINX)) {
267
+ return true;
268
+ }
269
+
270
+ return false;
271
+ },
272
+ // If version is too old and we couldn't get serverArgs, it has to be NGINX
273
+ traefikSupported() {
274
+ return Object.keys(this.serverArgs).length > 0;
275
+ },
239
276
 
240
277
  serverArgs() {
241
278
  return this.selectedVersion?.serverArgs || {};
@@ -336,6 +373,13 @@ export default {
336
373
  }
337
374
  },
338
375
 
376
+ nginxChart() {
377
+ return this.chartVersionKey(RKE2_INGRESS_NGINX);
378
+ },
379
+ traefikChart() {
380
+ return this.chartVersionKey(RKE2_TRAEFIK);
381
+ },
382
+
339
383
  canNotEditCloudProvider() {
340
384
  if (!this.isEdit) {
341
385
  return false;
@@ -363,6 +407,9 @@ export default {
363
407
  */
364
408
  showCloudProviderMigrateAzureWarning() {
365
409
  return this.showCloudProvider && this.isEdit && this.canAzureMigrateOnEdit;
410
+ },
411
+ showIngress() {
412
+ return !this.value?.isK3s;
366
413
  }
367
414
  },
368
415
 
@@ -382,7 +429,7 @@ export default {
382
429
  <template>
383
430
  <div>
384
431
  <Banner
385
- v-if="!haveArgInfo"
432
+ v-if="!haveArgInfo || ((!nginxChart || !traefikChart) && showIngress)"
386
433
  color="warning"
387
434
  :label="t('cluster.banner.haveArgInfo')"
388
435
  />
@@ -623,7 +670,7 @@ export default {
623
670
 
624
671
  <div
625
672
  v-if="serverArgs.disable"
626
- class="row"
673
+ class="row mb-30"
627
674
  >
628
675
  <div class="col span-12">
629
676
  <div>
@@ -641,6 +688,22 @@ export default {
641
688
  />
642
689
  </div>
643
690
  </div>
691
+ <!-- Ingress -->
692
+ <Ingress
693
+ v-if="showIngress"
694
+ v-model:value="ingressController"
695
+ :mode="mode"
696
+ :nginx-supported="nginxSupported"
697
+ :traefik-supported="traefikSupported"
698
+ :nginx-chart="nginxChart"
699
+ :traefik-chart="traefikChart"
700
+ :user-chart-values="userChartValues"
701
+ :version-info="versionInfo"
702
+ @update-values="(name, val) => $emit('update-values', name, val)"
703
+ @error="$emit('error', $event)"
704
+ @yaml-validation-changed="e => $emit('yaml-validation-changed', e)"
705
+ @config-validation-changed="e => $emit('config-validation-changed', e)"
706
+ />
644
707
  </div>
645
708
  </template>
646
709
 
@@ -0,0 +1,343 @@
1
+ <script setup lang="ts">
2
+ import { _CREATE, _VIEW, _EDIT } from '@shell/config/query-params';
3
+ import { ref, computed, useTemplateRef } from 'vue';
4
+ import { useStore } from 'vuex';
5
+ import { useI18n } from '@shell/composables/useI18n';
6
+ import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
7
+ import { Banner } from '@components/Banner';
8
+ import IngressCards from '@shell/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue';
9
+ import {
10
+ INGRESS_OPTIONS, INGRESS_DUAL, TRAEFIK, INGRESS_NGINX, INGRESS_NONE, INGRESS_MIGRATION_KB_LINK, INGRESS_CLASS_DEFAULT, INGRESS_CONTROLLER_CLASS_DEFAULT, INGRESS_CLASS_MIGRATION, INGRESS_CONTROLLER_CLASS_MIGRATION
11
+ } from '@shell/edit/provisioning.cattle.io.cluster/shared';
12
+ import IngressConfiguration from '@shell/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue';
13
+ import YamlEditor, { EDITOR_MODES } from '@shell/components/YamlEditor';
14
+ import { set, get, mergeWithReplace } from '@shell/utils/object';
15
+ import { saferDump } from '@shell/utils/create-yaml';
16
+ import RichTranslation from '@shell/components/RichTranslation.vue';
17
+
18
+ interface Props {
19
+ mode?: string;
20
+ value: string | string[];
21
+ nginxSupported: boolean;
22
+ traefikSupported: boolean;
23
+ nginxChart: string;
24
+ traefikChart: string;
25
+ userChartValues: any;
26
+ versionInfo: any;
27
+ }
28
+ const {
29
+ mode = _CREATE,
30
+ value,
31
+ nginxChart,
32
+ traefikChart,
33
+ nginxSupported,
34
+ traefikSupported,
35
+ userChartValues,
36
+ versionInfo
37
+ } = defineProps<Props>();
38
+
39
+ const emit = defineEmits(['update:value', 'error', 'config-validation-changed', 'yaml-validation-changed', 'update-values']);
40
+ const store = useStore();
41
+ const { t } = useI18n(store);
42
+ const nginxYaml = useTemplateRef('nginx-yaml');
43
+ const traefikYaml = useTemplateRef('traefik-yaml');
44
+
45
+ const showAdvanced = ref<Boolean>(false);
46
+ const isView = computed(() => mode === _VIEW);
47
+ const isEdit = computed(() => mode === _EDIT);
48
+ const showTraefikBanner = ref<Boolean>(false);
49
+ const traefikMerged = ref(initYamlEditor(traefikChart));
50
+ const nginxMerged = ref(initYamlEditor(nginxChart));
51
+ const showConfig = computed(() => !!versionInfo[traefikChart] || !!versionInfo[nginxChart]);
52
+
53
+ const ingressSelection = computed(() => {
54
+ if (Array.isArray(value) ) {
55
+ return INGRESS_DUAL;
56
+ } else if (!value || value.length === 0) {
57
+ return INGRESS_NONE;
58
+ } else {
59
+ return value;
60
+ }
61
+ });
62
+ const ingressOptions = computed(() => {
63
+ return INGRESS_OPTIONS.filter((option) => {
64
+ if (option.id === INGRESS_NGINX) {
65
+ return nginxSupported;
66
+ } else if (option.id === TRAEFIK) {
67
+ return traefikSupported;
68
+ } else if (option.id === INGRESS_DUAL) {
69
+ if (mode === _CREATE) {
70
+ return false;
71
+ } else {
72
+ return traefikSupported && nginxSupported;
73
+ }
74
+ }
75
+
76
+ return true;
77
+ }).map((option) => {
78
+ return {
79
+ ...option,
80
+ selected: option.id === ingressSelection.value
81
+ };
82
+ });
83
+ });
84
+
85
+ const ingressEnabled = computed({
86
+ get() {
87
+ return ingressSelection.value !== INGRESS_NONE;
88
+ },
89
+ set(val) {
90
+ if (!val) {
91
+ emit('update:value', INGRESS_NONE);
92
+ } else {
93
+ if (traefikSupported) {
94
+ emit('update:value', TRAEFIK);
95
+ } else if (nginxSupported) {
96
+ emit('update:value', INGRESS_NGINX);
97
+ } else {
98
+ emit('update:value', INGRESS_NONE);
99
+ }
100
+ }
101
+ }
102
+ });
103
+
104
+ function initYamlEditor(chart: string) {
105
+ const defaultChartValue = versionInfo[chart];
106
+
107
+ return mergeWithReplace(defaultChartValue?.values, userChartValues[chart]);
108
+ }
109
+
110
+ function setCompatibilityModeValues(val: boolean) {
111
+ set(traefikMerged.value, 'providers.kubernetesIngressNginx.enabled', val);
112
+ if (!val) {
113
+ set(traefikMerged.value, 'providers.kubernetesIngressNginx.ingressClass', INGRESS_CLASS_DEFAULT);
114
+ set(traefikMerged.value, 'providers.kubernetesIngressNginx.controllerClass', INGRESS_CONTROLLER_CLASS_DEFAULT);
115
+ } else {
116
+ set(traefikMerged.value, 'providers.kubernetesIngressNginx.ingressClass', INGRESS_CLASS_MIGRATION);
117
+ set(traefikMerged.value, 'providers.kubernetesIngressNginx.controllerClass', INGRESS_CONTROLLER_CLASS_MIGRATION);
118
+ }
119
+ }
120
+
121
+ function preconfigureTraefik() {
122
+ set(traefikMerged.value, 'ports.web.hostPort', 8000);
123
+ set(traefikMerged.value, 'ports.websecure.hostPort', 8443);
124
+ setCompatibilityModeValues(true);
125
+ emit('update-values', traefikChart, traefikMerged.value);
126
+ }
127
+
128
+ const nginxHttp = computed({
129
+ get() {
130
+ return get(nginxMerged.value, 'controller.hostPort.ports.http');
131
+ },
132
+ set(val: string) {
133
+ set(nginxMerged.value, 'controller.hostPort.ports.http', Number(val));
134
+ emit('update-values', nginxChart, nginxMerged.value);
135
+ updateYaml(nginxYaml.value, nginxMerged.value);
136
+ }
137
+ });
138
+ const nginxHttps = computed({
139
+ get() {
140
+ return get(nginxMerged.value, 'controller.hostPort.ports.https');
141
+ },
142
+ set(val: string) {
143
+ set(nginxMerged.value, 'controller.hostPort.ports.https', Number(val));
144
+ emit('update-values', nginxChart, nginxMerged.value);
145
+ updateYaml(nginxYaml.value, nginxMerged.value);
146
+ }
147
+ });
148
+ const traefikHttp = computed({
149
+ get() {
150
+ return get(traefikMerged.value, 'ports.web.hostPort');
151
+ },
152
+ set(val: string) {
153
+ set(traefikMerged.value, 'ports.web.hostPort', Number(val));
154
+ emit('update-values', traefikChart, traefikMerged.value);
155
+ updateYaml(traefikYaml.value, traefikMerged.value);
156
+ }
157
+ });
158
+ const traefikHttps = computed({
159
+ get() {
160
+ return get(traefikMerged.value, 'ports.websecure.hostPort');
161
+ },
162
+ set(val: string) {
163
+ set(traefikMerged.value, 'ports.websecure.hostPort', Number(val));
164
+ emit('update-values', traefikChart, traefikMerged.value);
165
+ updateYaml(traefikYaml.value, traefikMerged.value);
166
+ }
167
+ });
168
+
169
+ const compatibilityMode = computed({
170
+ get() {
171
+ return get(traefikMerged.value, 'providers.kubernetesIngressNginx.enabled');
172
+ },
173
+ set(val: boolean) {
174
+ setCompatibilityModeValues(val);
175
+ emit('update-values', traefikChart, traefikMerged.value);
176
+ updateYaml(traefikYaml.value, traefikMerged.value);
177
+ }
178
+ });
179
+
180
+ function selectIngress(id: string) {
181
+ if ( id === INGRESS_DUAL) {
182
+ emit('update:value', [TRAEFIK, INGRESS_NGINX]);
183
+ preconfigureTraefik();
184
+ } else {
185
+ emit('update:value', id);
186
+ if (id === TRAEFIK && !!isEdit.value) {
187
+ showTraefikBanner.value = true;
188
+ }
189
+ }
190
+ }
191
+
192
+ function updateYaml(component: any, value: any) {
193
+ if (component) {
194
+ component.updateValue(saferDump(value));
195
+ }
196
+ }
197
+
198
+ </script>
199
+ <template>
200
+ <h3 class="mb-10">
201
+ {{ t('cluster.ingress.title') }}
202
+ </h3>
203
+ <Checkbox
204
+ v-model:value="ingressEnabled"
205
+ :mode="mode"
206
+ :label="t('cluster.ingress.enableIngress')"
207
+ />
208
+ <div v-if="!ingressEnabled">
209
+ <Banner
210
+ color="warning"
211
+ label-key="cluster.ingress.banners.disabled.label"
212
+ />
213
+ </div>
214
+ <div v-else>
215
+ <Banner
216
+ v-if="traefikSupported"
217
+ color="info"
218
+ label-key="cluster.ingress.banners.transitioning.label"
219
+ />
220
+ <IngressCards
221
+ :options="ingressOptions"
222
+ :mode="mode"
223
+ :class="!traefikSupported ? 'mt-10' : ''"
224
+ @select="selectIngress"
225
+ />
226
+ <Banner
227
+ v-if="(isEdit && ingressSelection !== TRAEFIK) || showTraefikBanner"
228
+ color="warning"
229
+ >
230
+ <RichTranslation :k="`cluster.ingress.banners.selected.${ingressSelection}.label`">
231
+ <template #docsUrl="{ content }">
232
+ <a
233
+ :href="`${INGRESS_MIGRATION_KB_LINK}`"
234
+ tabindex="0"
235
+ target="_blank"
236
+ rel="noopener noreferrer nofollow"
237
+ >
238
+ {{ content }} <i class="icon icon-external-link" />
239
+ <span class="sr-only">{{ t('generic.opensInNewTab') }}</span>
240
+ </a>
241
+ </template>
242
+ </RichTranslation>
243
+ </Banner>
244
+ <div v-if="showConfig">
245
+ <div class="mt-20">
246
+ <IngressConfiguration
247
+ v-model:compatibility-mode="compatibilityMode"
248
+ v-model:nginxHttp="nginxHttp"
249
+ v-model:nginxHttps="nginxHttps"
250
+ v-model:traefikHttp="traefikHttp"
251
+ v-model:traefikHttps="traefikHttps"
252
+ :mode="mode"
253
+ :ingress-selection="ingressSelection"
254
+ @validation-changed="emit('config-validation-changed', $event)"
255
+ />
256
+ </div>
257
+ <div>
258
+ <button
259
+ type="button"
260
+ class="btn role-link advanced-toggle mb-0"
261
+ @click="showAdvanced = !showAdvanced"
262
+ >
263
+ {{ showAdvanced ? t('cluster.ingress.hideAdvanced') : t('cluster.ingress.showAdvanced') }}
264
+ </button>
265
+ </div>
266
+ <template v-if="showAdvanced">
267
+ <div class="row">
268
+ <div
269
+ v-if="ingressSelection === TRAEFIK || ingressSelection === INGRESS_DUAL"
270
+ :class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
271
+ >
272
+ <p
273
+ v-if="ingressSelection === INGRESS_DUAL"
274
+ class="mb-10"
275
+ >
276
+ {{ t('cluster.ingress.traefik.header') }}
277
+ </p>
278
+ <YamlEditor
279
+ ref="traefik-yaml"
280
+ class="ingress-yaml-editor"
281
+ data-testid="traefik-yaml-editor"
282
+ :value="traefikMerged"
283
+ :mode="mode"
284
+ :scrolling="true"
285
+ :as-object="true"
286
+ :editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
287
+ :hide-preview-buttons="true"
288
+ @update:value="emit('update-values', traefikChart, $event)"
289
+ @validationChanged="emit('yaml-validation-changed', {name: traefikChart, val: $event})"
290
+ />
291
+ </div>
292
+ <div
293
+ v-if="ingressSelection === INGRESS_NGINX || ingressSelection === INGRESS_DUAL"
294
+ :class="{ 'col': true, 'span-6': ingressSelection === INGRESS_DUAL, 'span-12': ingressSelection !== INGRESS_DUAL }"
295
+ >
296
+ <p
297
+ v-if="ingressSelection === INGRESS_DUAL"
298
+ class="mb-10"
299
+ >
300
+ {{ t('cluster.ingress.nginx.header') }}
301
+ </p>
302
+ <YamlEditor
303
+ ref="nginx-yaml"
304
+ class="ingress-yaml-editor"
305
+ data-testid="ingress-nginx-yaml-editor"
306
+ :value="nginxMerged"
307
+ :mode="mode"
308
+ :scrolling="true"
309
+ :as-object="true"
310
+ :editor-mode="isView ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE"
311
+ :hide-preview-buttons="true"
312
+ @update:value="emit('update-values', nginxChart, $event)"
313
+ @validationChanged="emit('yaml-validation-changed', {name: nginxChart, val: $event})"
314
+ />
315
+ </div>
316
+ </div>
317
+ </template>
318
+ </div>
319
+ </div>
320
+ </template>
321
+
322
+ <style scoped lang="scss">
323
+ .advanced-toggle {
324
+ padding: 0;
325
+ gap: 0;
326
+ min-height: 20px;
327
+
328
+ &:focus-visible {
329
+ border-color: var(--primary);
330
+ @include focus-outline;
331
+ outline-offset: -2px;
332
+ }
333
+ }
334
+
335
+ .ingress-yaml-editor {
336
+ :deep(.CodeMirror) {
337
+ height: auto !important;
338
+ }
339
+ :deep(.CodeMirror-scroll) {
340
+ max-height: 600px;
341
+ }
342
+ }
343
+ </style>
@@ -354,7 +354,8 @@ export default {
354
354
  :is="configComponent"
355
355
  v-if="value.config && configComponent"
356
356
  ref="configComponent"
357
- v-model:has-ipv6="value.hasIpv6"
357
+ v-model:is-ipv6="value.isIpv6"
358
+ v-model:is-dualStack="value.isDualStack"
358
359
  :cluster="cluster"
359
360
  :uuid="uuid"
360
361
  :mode="mode"
@@ -1,6 +1,18 @@
1
1
  import { shallowMount } from '@vue/test-utils';
2
2
  import S3Config from '@shell/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue';
3
-
3
+ jest.mock('@shell/edit/provisioning.cattle.io.cluster/shared', () => ({
4
+ RETENTION_DEFAULT: 5,
5
+ RKE2_INGRESS_NGINX: 'rke2-ingress-nginx',
6
+ RKE2_TRAEFIK: 'rke2-traefik',
7
+ INGRESS_NGINX: 'ingress-nginx',
8
+ INGRESS_CONTROLLER: 'ingress-controller',
9
+ TRAEFIK: 'traefik',
10
+ HARVESTER: 'harvester',
11
+ INGRESS_DUAL: 'dual',
12
+ INGRESS_NONE: 'none',
13
+ INGRESS_OPTIONS: [],
14
+ INGRESS_MIGRATION_KB_LINK: 'mock-link'
15
+ }));
4
16
  describe('s3Config', () => {
5
17
  const defaultProps = {
6
18
  mode: 'create',
@@ -2,7 +2,7 @@
2
2
  import { LabeledInput } from '@components/Form/LabeledInput';
3
3
  import { Banner } from '@components/Banner';
4
4
  import { Checkbox } from '@components/Form/Checkbox';
5
- import { _EDIT, _VIEW, _CREATE } from '@shell/config/query-params';
5
+ import { _EDIT, _VIEW } from '@shell/config/query-params';
6
6
  import ArrayList from '@shell/components/form/ArrayList';
7
7
  import ACE from '@shell/edit/provisioning.cattle.io.cluster/tabs/networking/ACE';
8
8
  import LabeledSelect from '@shell/components/form/LabeledSelect';
@@ -20,7 +20,7 @@ export default {
20
20
  'update:value', 'cluster-cidr-changed', 'local-cluster-auth-endpoint-changed',
21
21
  'service-cidr-changed', 'cluster-domain-changed', 'cluster-dns-changed',
22
22
  'truncate-hostname-changed', 'ca-certs-changed', 'service-node-port-range-changed',
23
- 'fqdn-changed', 'tls-san-changed', 'stack-preference-changed', 'validationChanged', 'enable-flannel-masq-changed'
23
+ 'fqdn-changed', 'tls-san-changed', 'stack-preference-changed', 'validationChanged', 'flannel-ipv6-masq-changed'
24
24
  ],
25
25
 
26
26
  components: {
@@ -59,29 +59,12 @@ export default {
59
59
  default: false
60
60
  },
61
61
 
62
- enableFlannelMasq: {
62
+ flannelIpv6Masq: {
63
63
  type: Boolean,
64
64
  default: false
65
65
  }
66
66
  },
67
67
 
68
- watch: {
69
- isProbablyIPv6(neu) {
70
- if (this.mode === _CREATE && this.showFlannelMasq) {
71
- this.$emit('enable-flannel-masq-changed', neu);
72
- }
73
- },
74
-
75
- isK3s(neu) {
76
- if (!neu) {
77
- this.$emit('enable-flannel-masq-changed', null);
78
- } else if (this.isProbablyIPv6) {
79
- this.$emit('enable-flannel-masq-changed', true);
80
- }
81
- },
82
-
83
- },
84
-
85
68
  data() {
86
69
  return { STACK_PREFS };
87
70
  },
@@ -157,11 +140,11 @@ export default {
157
140
  return this.isK3s && flannelEnabled;
158
141
  },
159
142
 
160
- hasIpv6StackPref() {
143
+ isIpv6StackPref() {
161
144
  return [STACK_PREFS.IPV6, STACK_PREFS.DUAL].includes(this.value?.spec?.rkeConfig?.networking?.stackPreference);
162
145
  },
163
146
 
164
- hasIpv6ServerConfig() {
147
+ isIpv6ServerConfig() {
165
148
  const clusterCIDR = this.serverConfig['cluster-cidr'] || '';
166
149
  const serviceCIDR = this.serverConfig['service-cidr'] || '';
167
150
 
@@ -169,25 +152,7 @@ export default {
169
152
  },
170
153
 
171
154
  isProbablyIPv6() {
172
- return this.hasSomeIpv6Pools || this.hasIpv6StackPref || this.hasIpv6ServerConfig;
173
- },
174
- },
175
-
176
- methods: {
177
- // if ipv6 pools are detected, we enforce dual-stack or ipv6 stack prefs.
178
- // If ipv6 pools are not detected we don't know for sure they aren't there so we don't validate the input
179
- // also not validating the input when editing existing clusters to ensure we don't prevent editing clusters using dual-stack VPCs provisioned before the ipv6 feature was added
180
- stackPreferenceValidator(val) {
181
- const value = val?.value || val;
182
- let isValid;
183
-
184
- if (this.hasSomeIpv6Pools && this.mode === _CREATE) {
185
- isValid = value !== STACK_PREFS.IPV4;
186
-
187
- return isValid ? null : this.t('cluster.rke2.stackPreference.errorNeedsIpv6');
188
- } else {
189
- return null;
190
- }
155
+ return this.hasSomeIpv6Pools || this.isIpv6StackPref || this.isIpv6ServerConfig;
191
156
  },
192
157
  }
193
158
  };
@@ -212,6 +177,7 @@ export default {
212
177
  :mode="mode"
213
178
  :disabled="isEdit"
214
179
  :label="t('cluster.rke2.address.clusterCidr.label')"
180
+ data-testid="cluster-cidr"
215
181
  @update:value="$emit('cluster-cidr-changed', $event)"
216
182
  />
217
183
  </div>
@@ -224,6 +190,7 @@ export default {
224
190
  :mode="mode"
225
191
  :disabled="isEdit"
226
192
  :label="t('cluster.rke2.address.serviceCidr.label')"
193
+ data-testid="service-cidr"
227
194
  @update:value="$emit('service-cidr-changed', $event)"
228
195
  />
229
196
  </div>
@@ -331,7 +298,6 @@ export default {
331
298
  :value="localValue?.spec?.rkeConfig?.networking?.stackPreference || STACK_PREFS.IPV4"
332
299
  :mode="mode"
333
300
  :options="stackPreferenceOptions"
334
- :rules="[stackPreferenceValidator]"
335
301
  data-testid="network-tab-stackpreferences"
336
302
  :require-dirty="false"
337
303
  @selecting="e=>$emit('stack-preference-changed', e)"
@@ -357,11 +323,11 @@ export default {
357
323
  class="col"
358
324
  >
359
325
  <Checkbox
360
- :value="enableFlannelMasq"
326
+ :value="flannelIpv6Masq"
361
327
  data-testid="cluster-rke2-flannel-masq-checkbox"
362
328
  :mode="mode"
363
329
  :label="t('cluster.k3s.flannelMasq.label')"
364
- @update:value="e=>$emit('enable-flannel-masq-changed', e)"
330
+ @update:value="e=>$emit('flannel-ipv6-masq-changed', e)"
365
331
  />
366
332
  </div>
367
333
  </div>
@@ -72,7 +72,7 @@ export default {
72
72
  this.value.metadata.namespace = this.filteredProjects[0].status.backingNamespace;
73
73
  this.value.metadata.labels[UI_PROJECT_SECRET] = this.filteredProjects[0].metadata.name;
74
74
  } else {
75
- this.projectName = this.filteredProjects.find((p) => p.metadata.name === projectScopedLabel).metadata.name;
75
+ this.projectName = this.filteredProjects.find((p) => p.metadata.name === projectScopedLabel)?.metadata.name;
76
76
  }
77
77
  }
78
78
  },