@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,283 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import { nextTick } from 'vue';
3
+ import RKE2 from '@shell/edit/provisioning.cattle.io.cluster/rke2.vue';
4
+ import { AGENT_CONFIGURATION_TYPES } from '@shell/config/settings';
5
+
6
+ jest.mock('@shell/edit/provisioning.cattle.io.cluster/shared', () => ({
7
+ RETENTION_DEFAULT: 5,
8
+ RKE2_INGRESS_NGINX: 'rke2-ingress-nginx',
9
+ RKE2_TRAEFIK: 'rke2-traefik',
10
+ INGRESS_NGINX: 'ingress-nginx',
11
+ INGRESS_CONTROLLER: 'ingress-controller',
12
+ TRAEFIK: 'traefik',
13
+ HARVESTER: 'harvester',
14
+ INGRESS_DUAL: 'dual',
15
+ INGRESS_NONE: 'none',
16
+ INGRESS_OPTIONS: [],
17
+ INGRESS_MIGRATION_KB_LINK: 'mock-link'
18
+ }));
19
+ const mockStore = {
20
+ getters: {
21
+ 'management/schemaFor': jest.fn().mockReturnValue(null),
22
+ 'management/byId': jest.fn().mockReturnValue({}),
23
+ 'management/canList': jest.fn().mockReturnValue(true),
24
+ 'management/all': jest.fn().mockReturnValue([]),
25
+ 'rancher/all': jest.fn().mockReturnValue([]),
26
+ 'rancher/byId': jest.fn().mockReturnValue({}),
27
+ 'i18n/t': jest.fn().mockImplementation((key) => key),
28
+ 'i18n/withFallback': jest.fn().mockImplementation((key) => key),
29
+ 'features/get': jest.fn().mockReturnValue(() => true),
30
+ 'plugins/cloudProviderForDriver': jest.fn().mockReturnValue(undefined),
31
+ currentCluster: {},
32
+ 'customisation/getPreviewCluster': {
33
+ badge: {
34
+ iconText: '', color: '', text: ''
35
+ }
36
+ },
37
+ productId: 'rancher',
38
+ currentStore: jest.fn().mockReturnValue('management')
39
+ },
40
+ dispatch: jest.fn().mockResolvedValue({ data: [] })
41
+ };
42
+
43
+ const mockRoute = {
44
+ query: {},
45
+ name: 'test'
46
+ };
47
+
48
+ const mockValue = {
49
+ metadata: {
50
+ name: 'test-cluster',
51
+ annotations: {}
52
+ },
53
+ spec: {
54
+ kubernetesVersion: 'v1.25.0+rke2r1',
55
+ rkeConfig: {
56
+ machineGlobalConfig: {},
57
+ networking: { stackPreference: 'ipv4' },
58
+ machineSelectorConfig: [{ config: {} }],
59
+ dataDirectories: {}
60
+ },
61
+ clusterAgentDeploymentCustomization: {},
62
+ fleetAgentDeploymentCustomization: {}
63
+ },
64
+ agentConfig: {}
65
+ };
66
+
67
+ const createWrapper = (propsData: any = {}) => {
68
+ return shallowMount(RKE2, {
69
+ propsData: {
70
+ mode: 'create',
71
+ value: { ...mockValue, ...propsData.value },
72
+ provider: 'custom',
73
+ ...propsData
74
+ },
75
+ global: {
76
+ mocks: {
77
+ $store: mockStore,
78
+ $route: mockRoute,
79
+ $router: { replace: jest.fn() },
80
+ t: jest.fn().mockImplementation((key) => key),
81
+ $extension: { getDynamic: jest.fn().mockReturnValue(undefined) },
82
+ $fetchState: { pending: false, error: null }
83
+ }
84
+ },
85
+ data() {
86
+ return {
87
+ loadedOnce: true,
88
+ rke2Versions: null,
89
+ k3sVersions: null,
90
+ defaultRke2: 'v1.25.0+rke2r1',
91
+ defaultK3s: 'v1.25.0+k3s1'
92
+ } as any;
93
+ }
94
+ });
95
+ };
96
+
97
+ describe('component: RKE2 - Fleet Agent Configuration', () => {
98
+ let wrapper: any;
99
+
100
+ beforeEach(() => {
101
+ jest.clearAllMocks();
102
+ wrapper = createWrapper();
103
+ });
104
+
105
+ afterEach(() => {
106
+ if (wrapper) {
107
+ wrapper.unmount();
108
+ }
109
+ });
110
+
111
+ describe('fleet Agent Scheduling Customization', () => {
112
+ beforeEach(async() => {
113
+ // Initialize the component with fleet agent defaults
114
+ wrapper.vm.fleetAgentDefaultPC = { value: 100, preemptionPolicy: 'PreemptLowerPriority' };
115
+ wrapper.vm.fleetAgentDefaultPDB = { maxUnavailable: 1 };
116
+ wrapper.vm.schedulingCustomizationFeatureEnabled = true;
117
+ await nextTick();
118
+ });
119
+
120
+ it('should enable Fleet Agent scheduling customization when event is true', async() => {
121
+ const event = { event: true, agentType: AGENT_CONFIGURATION_TYPES.FLEET };
122
+
123
+ wrapper.vm.setSchedulingCustomization(event);
124
+
125
+ expect(wrapper.vm.value.spec.fleetAgentDeploymentCustomization.schedulingCustomization).toStrictEqual({
126
+ priorityClass: wrapper.vm.fleetAgentDefaultPC,
127
+ podDisruptionBudget: wrapper.vm.fleetAgentDefaultPDB
128
+ });
129
+ });
130
+
131
+ it('should disable Fleet Agent scheduling customization when event is false', async() => {
132
+ // First enable it
133
+ const enableEvent = { event: true, agentType: AGENT_CONFIGURATION_TYPES.FLEET };
134
+
135
+ wrapper.vm.setSchedulingCustomization(enableEvent);
136
+
137
+ // Then disable it
138
+ const disableEvent = { event: false, agentType: AGENT_CONFIGURATION_TYPES.FLEET };
139
+
140
+ wrapper.vm.setSchedulingCustomization(disableEvent);
141
+
142
+ expect(wrapper.vm.value.spec.fleetAgentDeploymentCustomization.schedulingCustomization).toBeUndefined();
143
+ });
144
+
145
+ it('should enable Cluster Agent scheduling customization when event is true', async() => {
146
+ // Initialize cluster agent defaults
147
+ wrapper.vm.clusterAgentDefaultPC = { value: 200, preemptionPolicy: 'PreemptLowerPriority' };
148
+ wrapper.vm.clusterAgentDefaultPDB = { maxUnavailable: 2 };
149
+
150
+ const event = { event: true, agentType: AGENT_CONFIGURATION_TYPES.CLUSTER };
151
+
152
+ wrapper.vm.setSchedulingCustomization(event);
153
+
154
+ expect(wrapper.vm.value.spec.clusterAgentDeploymentCustomization.schedulingCustomization).toStrictEqual({
155
+ priorityClass: wrapper.vm.clusterAgentDefaultPC,
156
+ podDisruptionBudget: wrapper.vm.clusterAgentDefaultPDB
157
+ });
158
+ });
159
+
160
+ it('should disable Cluster Agent scheduling customization when event is false', async() => {
161
+ // Initialize cluster agent defaults
162
+ wrapper.vm.clusterAgentDefaultPC = { value: 200, preemptionPolicy: 'PreemptLowerPriority' };
163
+ wrapper.vm.clusterAgentDefaultPDB = { maxUnavailable: 2 };
164
+
165
+ // First enable it
166
+ const enableEvent = { event: true, agentType: AGENT_CONFIGURATION_TYPES.CLUSTER };
167
+
168
+ wrapper.vm.setSchedulingCustomization(enableEvent);
169
+
170
+ // Then disable it
171
+ const disableEvent = { event: false, agentType: AGENT_CONFIGURATION_TYPES.CLUSTER };
172
+
173
+ wrapper.vm.setSchedulingCustomization(disableEvent);
174
+
175
+ expect(wrapper.vm.value.spec.clusterAgentDeploymentCustomization.schedulingCustomization).toBeUndefined();
176
+ });
177
+
178
+ it('should handle unknown agent types gracefully', async() => {
179
+ const event = { event: true, agentType: 'unknown' };
180
+
181
+ // Should not throw an error
182
+ expect(() => wrapper.vm.setSchedulingCustomization(event)).not.toThrow();
183
+
184
+ // Should not modify any agent configuration
185
+ expect(wrapper.vm.value.spec.clusterAgentDeploymentCustomization.schedulingCustomization).toBeUndefined();
186
+ expect(wrapper.vm.value.spec.fleetAgentDeploymentCustomization.schedulingCustomization).toBeUndefined();
187
+ });
188
+
189
+ it('should pass the correct agent type to SchedulingCustomization components', async() => {
190
+ await wrapper.vm.$nextTick();
191
+
192
+ // Check if AGENT_CONFIGURATION_TYPES is available in the component data
193
+ expect(wrapper.vm.AGENT_CONFIGURATION_TYPES).toBeDefined();
194
+ expect(wrapper.vm.AGENT_CONFIGURATION_TYPES.CLUSTER).toBe('cluster');
195
+ expect(wrapper.vm.AGENT_CONFIGURATION_TYPES.FLEET).toBe('fleet');
196
+ });
197
+ });
198
+
199
+ describe('fleet Agent Configuration Data Initialization', () => {
200
+ it('should initialize fleet agent default configuration properties', () => {
201
+ expect(wrapper.vm.fleetAgentDefaultPC).toBeDefined();
202
+ expect(wrapper.vm.fleetAgentDefaultPDB).toBeDefined();
203
+ expect(wrapper.vm.AGENT_CONFIGURATION_TYPES).toBeDefined();
204
+ });
205
+
206
+ it('should have AGENT_CONFIGURATION_TYPES with correct values', () => {
207
+ expect(wrapper.vm.AGENT_CONFIGURATION_TYPES).toStrictEqual({
208
+ CLUSTER: 'cluster',
209
+ FLEET: 'fleet'
210
+ });
211
+ });
212
+ });
213
+
214
+ describe('fleet Agent Configuration Template', () => {
215
+ it('should render AgentConfiguration for fleet agent with correct props', async() => {
216
+ // Set up fleet agent configuration to ensure it renders
217
+ wrapper.vm.value.spec.fleetAgentDeploymentCustomization = {};
218
+ wrapper.vm.schedulingCustomizationFeatureEnabled = true;
219
+ wrapper.vm.fleetAgentDefaultPC = { value: 100 };
220
+ wrapper.vm.fleetAgentDefaultPDB = { maxUnavailable: 1 };
221
+ await nextTick();
222
+
223
+ // Force re-render
224
+ await wrapper.vm.$forceUpdate();
225
+ await nextTick();
226
+
227
+ // The template should contain fleet agent configuration
228
+ // This tests the template structure even if the component isn't fully mounted
229
+ expect(wrapper.vm.value.spec.fleetAgentDeploymentCustomization).toBeDefined();
230
+ });
231
+ });
232
+
233
+ describe('flannel Masquerade Configuration', () => {
234
+ let k3sWrapper: any;
235
+
236
+ beforeEach(() => {
237
+ const k3sValue = {
238
+ ...mockValue,
239
+ spec: {
240
+ ...mockValue.spec,
241
+ rkeConfig: {
242
+ ...mockValue.spec.rkeConfig,
243
+ machineGlobalConfig: { 'flannel-backend': 'vxlan' } // flannel enabled
244
+ }
245
+ }
246
+ };
247
+
248
+ k3sWrapper = createWrapper({ value: k3sValue });
249
+ });
250
+
251
+ afterEach(() => {
252
+ if (k3sWrapper) {
253
+ k3sWrapper.unmount();
254
+ }
255
+ });
256
+
257
+ it('should handle flannel masquerade configuration change', async() => {
258
+ k3sWrapper.vm.handleFlannelMasqChanged(true);
259
+
260
+ expect(k3sWrapper.vm.serverConfig['flannel-ipv6-masq']).toBe(true);
261
+ });
262
+
263
+ it('should remove flannel masquerade configuration when set to false', async() => {
264
+ // First set it to true
265
+ k3sWrapper.vm.handleFlannelMasqChanged(true);
266
+ expect(k3sWrapper.vm.serverConfig['flannel-ipv6-masq']).toBe(true);
267
+
268
+ // Then set it to false
269
+ k3sWrapper.vm.handleFlannelMasqChanged(false);
270
+ expect(k3sWrapper.vm.serverConfig['flannel-ipv6-masq']).toBe(false);
271
+ });
272
+
273
+ it('should remove flannel masquerade configuration when set to null/undefined', async() => {
274
+ // First set it to true
275
+ k3sWrapper.vm.handleFlannelMasqChanged(true);
276
+ expect(k3sWrapper.vm.serverConfig['flannel-ipv6-masq']).toBe(true);
277
+
278
+ // Then set it to null
279
+ k3sWrapper.vm.handleFlannelMasqChanged(null);
280
+ expect(k3sWrapper.vm.serverConfig['flannel-ipv6-masq']).toBeUndefined();
281
+ });
282
+ });
283
+ });
@@ -1,10 +1,26 @@
1
1
  import { mount, shallowMount } from '@vue/test-utils';
2
2
  import { SECRET } from '@shell/config/types';
3
- import { _CREATE } from '@shell/config/query-params';
3
+ import { _CREATE, _EDIT } from '@shell/config/query-params';
4
4
  import rke2 from '@shell/edit/provisioning.cattle.io.cluster/rke2.vue';
5
5
  import { get } from '@shell/utils/object';
6
6
  import { rke2TestTable } from './utils/rke2-test-data';
7
- import { NGINX_SUPPORTED, INGRESS_CONTROLLER, INGRESS_NGINX } from '@shell/edit/provisioning.cattle.io.cluster/shared';
7
+ import {
8
+ RKE2_INGRESS_NGINX, INGRESS_CONTROLLER, INGRESS_NGINX, TRAEFIK, INGRESS_NONE, RKE2_TRAEFIK
9
+ } from '@shell/edit/provisioning.cattle.io.cluster/shared';
10
+
11
+ jest.mock('@shell/edit/provisioning.cattle.io.cluster/shared', () => ({
12
+ RETENTION_DEFAULT: 5,
13
+ RKE2_INGRESS_NGINX: 'rke2-ingress-nginx',
14
+ RKE2_TRAEFIK: 'rke2-traefik',
15
+ INGRESS_NGINX: 'ingress-nginx',
16
+ INGRESS_CONTROLLER: 'ingress-controller',
17
+ TRAEFIK: 'traefik',
18
+ HARVESTER: 'harvester',
19
+ INGRESS_DUAL: 'dual',
20
+ INGRESS_NONE: 'none',
21
+ INGRESS_OPTIONS: [],
22
+ INGRESS_MIGRATION_KB_LINK: 'mock-link'
23
+ }));
8
24
 
9
25
  /**
10
26
  * DISCLAIMER ***************************************************************************************
@@ -453,7 +469,11 @@ describe('component: rke2', () => {
453
469
  rke2Versions: [{
454
470
  id: k8s,
455
471
  version: k8s,
456
- serverArgs: true
472
+ serverArgs: true,
473
+ charts: {
474
+ [RKE2_INGRESS_NGINX]: {},
475
+ [RKE2_TRAEFIK]: {}
476
+ }
457
477
  }]
458
478
  });
459
479
 
@@ -593,6 +613,7 @@ describe('component: rke2', () => {
593
613
  repo: 'rancher-rke2-charts',
594
614
  version: '3.12.200'
595
615
  },
616
+ 'rke2-traefik': {}
596
617
  }
597
618
  }
598
619
  ]
@@ -616,10 +637,10 @@ describe('component: rke2', () => {
616
637
 
617
638
  describe('should correctly update NGINX configuration', () => {
618
639
  const k8sVersion = 'v1.25.0+rke2r1';
619
- const createWrapper = () => {
640
+ const createWrapper = (mode = _EDIT) => {
620
641
  return shallowMount(rke2, {
621
642
  props: {
622
- mode: _CREATE,
643
+ mode,
623
644
  value: {
624
645
  spec: {
625
646
  ...defaultSpec,
@@ -646,33 +667,47 @@ describe('component: rke2', () => {
646
667
  },
647
668
  });
648
669
  };
670
+ const mockCharts = {
671
+ [RKE2_INGRESS_NGINX]: {},
672
+ [RKE2_TRAEFIK]: {}
673
+ };
649
674
 
650
- it('should set ingress-controller to ingress-nginx by default when nginx is supported and not disabled', async() => {
651
- const wrapper = createWrapper();
675
+ it('should set ingress-controller to traefik by default for new clusters', async() => {
676
+ const wrapper = createWrapper(_CREATE);
652
677
 
653
678
  await wrapper.setData({
654
679
  rke2Versions: [{
655
680
  id: k8sVersion,
656
681
  version: k8sVersion,
657
- serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
682
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
683
+ charts: mockCharts
658
684
  }]
659
685
  });
660
686
 
661
- expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NGINX);
687
+ expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(TRAEFIK);
662
688
  });
663
689
 
664
- it('should set ingress-controller to undefined by default when nginx is not supported', async() => {
690
+ it('should set ingress-controller to None on version change when nginx is not supported', async() => {
665
691
  const wrapper = createWrapper();
692
+ const newVersion = 'v1.26.0+rke2r1';
666
693
 
667
694
  await wrapper.setData({
668
695
  rke2Versions: [{
669
696
  id: k8sVersion,
670
697
  version: k8sVersion,
671
- serverArgs: { disable: { options: [] } }
698
+ serverArgs: { disable: { options: [] } },
699
+ charts: mockCharts
700
+ },
701
+ {
702
+ id: newVersion,
703
+ version: newVersion,
704
+ serverArgs: { disable: { options: [] } },
705
+ charts: mockCharts
672
706
  }]
673
707
  });
674
-
675
- expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBeUndefined();
708
+ wrapper.vm.value.spec.kubernetesVersion = newVersion;
709
+ (wrapper.vm as any).handleKubernetesChange(newVersion);
710
+ expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NONE);
676
711
  });
677
712
 
678
713
  it('should set ingress-controller to ingress-nginx on change when nginx is supported and not disabled', () => {
@@ -682,7 +717,8 @@ describe('component: rke2', () => {
682
717
  rke2Versions: [{
683
718
  id: k8sVersion,
684
719
  version: k8sVersion,
685
- serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
720
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
721
+ charts: mockCharts
686
722
  }]
687
723
  });
688
724
 
@@ -691,36 +727,38 @@ describe('component: rke2', () => {
691
727
  expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NGINX);
692
728
  });
693
729
 
694
- it('should set ingress-controller to undefined when nginx is supported but disabled', () => {
730
+ it('should set ingress-controller to None when nginx is supported but disabled', () => {
695
731
  const wrapper = createWrapper();
696
732
 
697
733
  wrapper.setData({
698
734
  rke2Versions: [{
699
735
  id: k8sVersion,
700
736
  version: k8sVersion,
701
- serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
737
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
738
+ charts: mockCharts
702
739
  }]
703
740
  });
704
741
 
705
- (wrapper.vm as any).handleEnabledSystemServicesChanged([NGINX_SUPPORTED]);
742
+ (wrapper.vm as any).handleEnabledSystemServicesChanged([RKE2_INGRESS_NGINX]);
706
743
 
707
- expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBeUndefined();
744
+ expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NONE);
708
745
  });
709
746
 
710
- it('should set ingress-controller to undefined when nginx is not supported', () => {
747
+ it('should set ingress-controller for existing cluster to None when nginx is not supported', () => {
711
748
  const wrapper = createWrapper();
712
749
 
713
750
  wrapper.setData({
714
751
  rke2Versions: [{
715
752
  id: k8sVersion,
716
753
  version: k8sVersion,
717
- serverArgs: { disable: { options: [] } }
754
+ serverArgs: { disable: { options: [] } },
755
+ charts: mockCharts
718
756
  }]
719
757
  });
720
758
 
721
759
  (wrapper.vm as any).handleEnabledSystemServicesChanged([]);
722
760
 
723
- expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBeUndefined();
761
+ expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NONE);
724
762
  });
725
763
 
726
764
  it('should correctly update disable list in serverConfig', () => {
@@ -730,7 +768,8 @@ describe('component: rke2', () => {
730
768
  rke2Versions: [{
731
769
  id: k8sVersion,
732
770
  version: k8sVersion,
733
- serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
771
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
772
+ charts: mockCharts
734
773
  }]
735
774
  });
736
775
  const disabledServices = ['other-service'];
@@ -749,12 +788,14 @@ describe('component: rke2', () => {
749
788
  {
750
789
  id: k8sVersion,
751
790
  version: k8sVersion,
752
- serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
791
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
792
+ charts: mockCharts
753
793
  },
754
794
  {
755
795
  id: newVersion,
756
796
  version: newVersion,
757
- serverArgs: { disable: { options: [NGINX_SUPPORTED] } }
797
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
798
+ charts: mockCharts
758
799
  }
759
800
  ]
760
801
  });
@@ -764,30 +805,5 @@ describe('component: rke2', () => {
764
805
 
765
806
  expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NGINX);
766
807
  });
767
-
768
- it('should not set ingress-controller to ingress-nginx on version change when nginx is not supported', async() => {
769
- const wrapper = createWrapper();
770
- const newVersion = 'v1.26.0+rke2r1';
771
-
772
- await wrapper.setData({
773
- k3sVersions: [
774
- {
775
- id: k8sVersion,
776
- version: k8sVersion,
777
- serverArgs: { disable: { options: [] } }
778
- },
779
- {
780
- id: newVersion,
781
- version: newVersion,
782
- serverArgs: { disable: { options: [] } }
783
- }
784
- ]
785
- });
786
-
787
- wrapper.vm.value.spec.kubernetesVersion = newVersion;
788
- (wrapper.vm as any).handleKubernetesChange(newVersion);
789
-
790
- expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBeUndefined();
791
- });
792
808
  });
793
809
  });
@@ -0,0 +1,114 @@
1
+ <script setup lang="ts">
2
+ import { _CREATE, _VIEW } from '@shell/config/query-params';
3
+ import { PropType } from 'vue';
4
+ import { useStore } from 'vuex';
5
+ import { useI18n } from '@shell/composables/useI18n';
6
+ import { RcItemCard, RcItemCardAction } from '@components/RcItemCard';
7
+
8
+ interface IngressCard {
9
+ id: string;
10
+ image?: any;
11
+ header: { title: { key: string } };
12
+ subHeader: { label: { key: string } };
13
+ content: { key: string };
14
+ doc?: {url: string};
15
+ selected?: boolean;
16
+ }
17
+
18
+ defineProps({
19
+ options: {
20
+ type: Array as PropType<IngressCard[]>,
21
+ required: true
22
+ },
23
+ mode: { type: String, default: _CREATE },
24
+ });
25
+
26
+ const emit = defineEmits(['select']);
27
+
28
+ const store = useStore();
29
+ const { t } = useI18n(store);
30
+
31
+ </script>
32
+
33
+ <template>
34
+ <div class="ingress-cards">
35
+ <rc-item-card
36
+ v-for="card in options"
37
+ :id="card.id"
38
+ :key="card.id"
39
+ :header="card.header"
40
+ :image="card.image"
41
+ :content="card.content"
42
+ :selected="card.selected"
43
+ variant="small"
44
+ role="link"
45
+ :disabled="mode === _VIEW"
46
+ :class="{ 'single-card': options.length === 1 }"
47
+ :clickable="mode !== _VIEW"
48
+ @card-click="emit('select', card.id)"
49
+ >
50
+ <template
51
+ v-once
52
+ #item-card-sub-header
53
+ >
54
+ <p class="ingress-card-sub-header">
55
+ {{ t(card.subHeader.label.key) }}
56
+ </p>
57
+ </template>
58
+ <template
59
+ v-if="!!card.doc"
60
+ v-once
61
+ #item-card-footer
62
+ >
63
+ <div class="mmt-2">
64
+ <rc-item-card-action>
65
+ <a
66
+ :href="card.doc.url"
67
+ rel="nofollow noopener noreferrer"
68
+ target="_blank"
69
+ class="ingress-card-footer-button secondary-text-link"
70
+ >
71
+ {{ t('cluster.ingress.learnMore.label') }}
72
+ <i class="icon icon-external-link" />
73
+ <span class="sr-only">{{ t('generic.opensInNewTab') }}</span>
74
+ </a>
75
+ </rc-item-card-action>
76
+ </div>
77
+ </template>
78
+ </rc-item-card>
79
+ </div>
80
+ </template>
81
+
82
+ <style scoped lang="scss">
83
+ .ingress-cards{
84
+ display: grid;
85
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
86
+ grid-gap: var(--gap-md);
87
+ width: 100%;
88
+ height: max-content;
89
+ overflow: hidden;
90
+
91
+ .single-card {
92
+ max-width: 500px;
93
+ }
94
+ }
95
+ .ingress-card-sub-header {
96
+ display: flex;
97
+ flex-wrap: wrap;
98
+ gap: var(--gap) var(--gap-md);
99
+ color: var(--link-text-secondary);
100
+ margin-bottom: 8px;
101
+ }
102
+
103
+ button.variant-ghost.ingress-card-footer-button {
104
+ padding: 0;
105
+ gap: 0;
106
+ min-height: 20px;
107
+
108
+ &:focus-visible {
109
+ border-color: var(--primary);
110
+ @include focus-outline;
111
+ outline-offset: -2px;
112
+ }
113
+ }
114
+ </style>