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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/assets/brand/suse/metadata.json +2 -1
  2. package/assets/images/providers/oci-open-containers.svg +22 -0
  3. package/assets/images/providers/traefik.png +0 -0
  4. package/assets/styles/themes/_dark.scss +2 -0
  5. package/assets/styles/themes/_light.scss +2 -0
  6. package/assets/styles/themes/_modern.scss +6 -0
  7. package/assets/translations/en-us.yaml +218 -26
  8. package/components/ActionMenuShell.vue +1 -1
  9. package/components/CruResource.vue +3 -1
  10. package/components/ExplorerProjectsNamespaces.vue +12 -12
  11. package/components/Inactivity.vue +2 -2
  12. package/components/Resource/Detail/Card/ExtrasCard.vue +49 -15
  13. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +109 -0
  14. package/components/Resource/Detail/Card/StatusCard/index.vue +21 -4
  15. package/components/Resource/Detail/Card/__tests__/ExtrasCard.test.ts +111 -0
  16. package/components/Resource/Detail/Masthead/__tests__/index.test.ts +0 -17
  17. package/components/Resource/Detail/Masthead/index.vue +11 -4
  18. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +19 -2
  19. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +19 -11
  20. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +3 -1
  21. package/components/Resource/Detail/Metadata/index.vue +1 -1
  22. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +12 -0
  23. package/components/Resource/Detail/ResourcePopover/index.vue +2 -0
  24. package/components/Resource/Detail/ResourceRow.vue +3 -3
  25. package/components/ResourceDetail/Masthead/latest.vue +12 -2
  26. package/components/ResourceList/index.vue +12 -0
  27. package/components/ResourceTable.vue +38 -4
  28. package/components/Tabbed/Tab.vue +4 -0
  29. package/components/Tabbed/index.vue +4 -1
  30. package/components/Window/ContainerLogs.vue +48 -37
  31. package/components/fleet/FleetClusterTargets/TargetsList.vue +2 -2
  32. package/components/fleet/FleetClusterTargets/index.vue +6 -1
  33. package/components/fleet/GitRepoAdvancedTab.vue +333 -0
  34. package/components/fleet/GitRepoMetadataTab.vue +43 -0
  35. package/components/fleet/GitRepoRepositoryTab.vue +101 -0
  36. package/components/fleet/GitRepoTargetTab.vue +77 -0
  37. package/components/fleet/HelmOpAdvancedTab.vue +247 -0
  38. package/components/fleet/HelmOpChartTab.vue +158 -0
  39. package/components/fleet/HelmOpMetadataTab.vue +46 -0
  40. package/components/fleet/HelmOpTargetTab.vue +84 -0
  41. package/components/fleet/HelmOpValuesTab.vue +147 -0
  42. package/components/fleet/__tests__/FleetClusterTargets.test.ts +119 -70
  43. package/components/form/ChangePassword.vue +41 -35
  44. package/components/form/NodeScheduling.vue +81 -7
  45. package/components/form/PodAffinity.vue +1 -36
  46. package/components/form/ResourceLabeledSelect.vue +8 -4
  47. package/components/form/ResourceQuota/Namespace.vue +30 -9
  48. package/components/form/ResourceQuota/NamespaceRow.vue +25 -7
  49. package/components/form/ResourceQuota/Project.vue +150 -51
  50. package/components/form/ResourceQuota/ResourceQuotaEntry.vue +145 -0
  51. package/components/form/ResourceQuota/__tests__/Namespace.test.ts +307 -0
  52. package/components/form/ResourceQuota/__tests__/NamespaceRow.test.ts +281 -0
  53. package/components/form/ResourceQuota/__tests__/Project.test.ts +310 -0
  54. package/components/form/ResourceQuota/__tests__/ResourceQuotaEntry.test.ts +215 -0
  55. package/components/form/SchedulingCustomization.vue +14 -6
  56. package/components/form/SelectOrCreateAuthSecret.vue +113 -19
  57. package/components/form/__tests__/NodeScheduling.test.ts +12 -9
  58. package/components/form/__tests__/PodAffinity.test.ts +21 -2
  59. package/components/form/__tests__/SchedulingCustomization.test.ts +240 -0
  60. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +35 -0
  61. package/components/formatter/ClusterLink.vue +8 -0
  62. package/components/formatter/MachineSummaryGraph.vue +10 -2
  63. package/components/formatter/SecretOrigin.vue +79 -0
  64. package/components/nav/TopLevelMenu.helper.ts +50 -2
  65. package/components/nav/TopLevelMenu.vue +14 -0
  66. package/components/nav/Type.vue +5 -0
  67. package/components/nav/__tests__/TopLevelMenu.test.ts +3 -3
  68. package/components/nav/__tests__/Type.test.ts +6 -4
  69. package/config/labels-annotations.js +7 -6
  70. package/config/pagination-table-headers.js +6 -4
  71. package/config/product/explorer.js +5 -14
  72. package/config/product/manager.js +18 -1
  73. package/config/query-params.js +3 -0
  74. package/config/router/navigation-guards/authentication.js +8 -9
  75. package/config/settings.ts +15 -2
  76. package/config/table-headers.js +21 -17
  77. package/config/types.js +33 -10
  78. package/detail/auditlog.cattle.io.auditpolicy.vue +19 -0
  79. package/detail/management.cattle.io.user.vue +1 -2
  80. package/detail/node.vue +0 -1
  81. package/detail/provisioning.cattle.io.cluster.vue +2 -1
  82. package/detail/workload/index.vue +11 -16
  83. package/dialog/ChangePasswordDialog.vue +8 -0
  84. package/dialog/DeactivateDriverDialog.vue +1 -1
  85. package/dialog/GenericPrompt.vue +20 -3
  86. package/dialog/Ipv6NetworkingDialog.vue +156 -0
  87. package/dialog/ScaleMachineDownDialog.vue +65 -15
  88. package/dialog/ScalePoolDownDialog.vue +2 -2
  89. package/dialog/SearchDialog.vue +10 -2
  90. package/dialog/__tests__/ScaleMachineDownDialog.test.ts +184 -0
  91. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +89 -0
  92. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +1 -0
  93. package/edit/__tests__/management.cattle.io.project.test.js +81 -98
  94. package/edit/auditlog.cattle.io.auditpolicy/AdditionalRedactions.vue +114 -0
  95. package/edit/auditlog.cattle.io.auditpolicy/Filters.vue +119 -0
  96. package/edit/auditlog.cattle.io.auditpolicy/General.vue +180 -0
  97. package/edit/auditlog.cattle.io.auditpolicy/__tests__/AdditionalRedactions.test.ts +327 -0
  98. package/edit/auditlog.cattle.io.auditpolicy/__tests__/Filters.test.ts +449 -0
  99. package/edit/auditlog.cattle.io.auditpolicy/__tests__/General.test.ts +472 -0
  100. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/AdditionalRedactions.test.ts.snap +27 -0
  101. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/Filters.test.ts.snap +39 -0
  102. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +174 -0
  103. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +29 -0
  104. package/edit/auditlog.cattle.io.auditpolicy/__tests__/index.test.ts +215 -0
  105. package/edit/auditlog.cattle.io.auditpolicy/index.vue +104 -0
  106. package/edit/auditlog.cattle.io.auditpolicy/types.ts +28 -0
  107. package/edit/auth/oidc.vue +1 -1
  108. package/edit/catalog.cattle.io.clusterrepo.vue +155 -25
  109. package/edit/fleet.cattle.io.gitrepo.vue +161 -276
  110. package/edit/fleet.cattle.io.helmop.vue +190 -332
  111. package/edit/management.cattle.io.project.vue +11 -42
  112. package/edit/management.cattle.io.setting.vue +6 -0
  113. package/edit/management.cattle.io.user.vue +29 -34
  114. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +55 -24
  115. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +1 -103
  116. package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +13 -1
  117. package/edit/provisioning.cattle.io.cluster/__tests__/rke2-fleet-cluster-agent.test.ts +283 -0
  118. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +196 -2
  119. package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +112 -0
  120. package/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue +158 -0
  121. package/edit/provisioning.cattle.io.cluster/rke2.vue +184 -66
  122. package/edit/provisioning.cattle.io.cluster/shared.ts +39 -0
  123. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +2 -1
  124. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +56 -7
  125. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +319 -0
  126. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +2 -1
  127. package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +1 -1
  128. package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +13 -1
  129. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +10 -44
  130. package/edit/secret/index.vue +1 -1
  131. package/edit/token.vue +68 -29
  132. package/edit/workload/__tests__/index.test.ts +2 -37
  133. package/edit/workload/index.vue +6 -2
  134. package/edit/workload/mixins/workload.js +0 -32
  135. package/list/__tests__/management.cattle.io.setting.test.ts +198 -0
  136. package/list/auditlog.cattle.io.auditpolicy.vue +63 -0
  137. package/list/group.principal.vue +11 -15
  138. package/list/management.cattle.io.setting.vue +13 -0
  139. package/list/management.cattle.io.user.vue +11 -21
  140. package/list/provisioning.cattle.io.cluster.vue +50 -1
  141. package/list/secret.vue +4 -9
  142. package/list/service.vue +6 -8
  143. package/machine-config/amazonec2.vue +11 -4
  144. package/machine-config/azure.vue +14 -0
  145. package/machine-config/components/EC2Networking.vue +46 -30
  146. package/machine-config/components/__tests__/EC2Networking.test.ts +7 -7
  147. package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +0 -9
  148. package/machine-config/digitalocean.vue +3 -3
  149. package/mixins/browser-tab-visibility.js +5 -4
  150. package/mixins/fetch.client.js +6 -0
  151. package/models/__tests__/auditlog.cattle.io.auditpolicy.test.ts +117 -0
  152. package/models/__tests__/namespace.test.ts +11 -0
  153. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +96 -0
  154. package/models/__tests__/workload.test.ts +90 -6
  155. package/models/auditlog.cattle.io.auditpolicy.js +46 -0
  156. package/models/catalog.cattle.io.clusterrepo.js +30 -4
  157. package/models/cluster.x-k8s.io.machine.js +1 -1
  158. package/models/cluster.x-k8s.io.machinedeployment.js +5 -5
  159. package/models/event.js +5 -0
  160. package/models/ext.cattle.io.groupmembershiprefreshrequest.js +15 -0
  161. package/models/ext.cattle.io.passwordchangerequest.js +15 -0
  162. package/models/ext.cattle.io.selfuser.js +15 -0
  163. package/models/ext.cattle.io.token.js +48 -0
  164. package/models/fleet-application.js +17 -7
  165. package/models/kontainerdriver.js +2 -2
  166. package/models/management.cattle.io.user.js +28 -31
  167. package/models/namespace.js +7 -1
  168. package/models/nodedriver.js +2 -2
  169. package/models/provisioning.cattle.io.cluster.js +28 -7
  170. package/models/schema.js +18 -0
  171. package/models/secret.js +27 -41
  172. package/models/service.js +44 -1
  173. package/models/steve-schema.ts +39 -2
  174. package/models/token.js +4 -0
  175. package/models/workload.js +13 -6
  176. package/package.json +1 -1
  177. package/pages/account/index.vue +108 -72
  178. package/pages/auth/login.vue +15 -8
  179. package/pages/auth/setup.vue +55 -27
  180. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +4 -1
  181. package/pages/c/_cluster/apps/charts/index.vue +93 -4
  182. package/pages/c/_cluster/apps/charts/install.vue +317 -42
  183. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -4
  184. package/pages/c/_cluster/settings/index.vue +3 -1
  185. package/pages/home.vue +9 -3
  186. package/plugins/dashboard-store/__tests__/getters.test.ts +108 -0
  187. package/plugins/dashboard-store/__tests__/resource-class.test.ts +28 -3
  188. package/plugins/dashboard-store/actions.js +10 -8
  189. package/plugins/dashboard-store/getters.js +30 -6
  190. package/plugins/dashboard-store/index.js +3 -2
  191. package/plugins/dashboard-store/mutations.js +8 -1
  192. package/plugins/dashboard-store/resource-class.js +15 -8
  193. package/plugins/steve/__tests__/steve-class.test.ts +128 -0
  194. package/plugins/steve/schema.d.ts +5 -0
  195. package/plugins/steve/steve-class.js +28 -0
  196. package/plugins/steve/steve-pagination-utils.ts +7 -2
  197. package/rancher-components/RcIcon/types.ts +2 -0
  198. package/rancher-components/RcItemCard/RcItemCard.vue +64 -19
  199. package/store/auth.js +57 -19
  200. package/store/notifications.ts +1 -1
  201. package/store/prefs.js +3 -0
  202. package/store/type-map.js +12 -1
  203. package/types/aws-sdk.d.ts +121 -0
  204. package/types/resources/node.ts +15 -0
  205. package/types/shell/index.d.ts +542 -516
  206. package/types/store/dashboard-store.types.ts +7 -0
  207. package/types/store/pagination.types.ts +5 -5
  208. package/utils/__tests__/array.test.ts +1 -29
  209. package/utils/__tests__/cluster-agent-configuration.test.ts +203 -0
  210. package/utils/array.ts +0 -11
  211. package/utils/aws.ts +21 -0
  212. package/utils/cluster.js +22 -2
  213. package/utils/pagination-wrapper.ts +11 -3
  214. package/utils/selector-typed.ts +1 -1
  215. package/vue.config.js +26 -13
  216. package/components/__tests__/ProjectRow.test.ts +0 -146
  217. package/components/form/ResourceQuota/ProjectRow.vue +0 -210
  218. package/edit/provisioning.cattle.io.cluster/defaults.ts +0 -1
@@ -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,9 +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 {
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
+ }));
7
24
 
8
25
  /**
9
26
  * DISCLAIMER ***************************************************************************************
@@ -452,7 +469,11 @@ describe('component: rke2', () => {
452
469
  rke2Versions: [{
453
470
  id: k8s,
454
471
  version: k8s,
455
- serverArgs: true
472
+ serverArgs: true,
473
+ charts: {
474
+ [RKE2_INGRESS_NGINX]: {},
475
+ [RKE2_TRAEFIK]: {}
476
+ }
456
477
  }]
457
478
  });
458
479
 
@@ -592,6 +613,7 @@ describe('component: rke2', () => {
592
613
  repo: 'rancher-rke2-charts',
593
614
  version: '3.12.200'
594
615
  },
616
+ 'rke2-traefik': {}
595
617
  }
596
618
  }
597
619
  ]
@@ -612,4 +634,176 @@ describe('component: rke2', () => {
612
634
 
613
635
  expect(wrapper.vm.value.spec.rkeConfig.chartValues).toStrictEqual(expected);
614
636
  });
637
+
638
+ describe('should correctly update NGINX configuration', () => {
639
+ const k8sVersion = 'v1.25.0+rke2r1';
640
+ const createWrapper = (mode = _EDIT) => {
641
+ return shallowMount(rke2, {
642
+ props: {
643
+ mode,
644
+ value: {
645
+ spec: {
646
+ ...defaultSpec,
647
+ rkeConfig: {
648
+ machineGlobalConfig: {},
649
+ chartValues: {},
650
+ upgradeStrategy: {},
651
+ dataDirectories: {},
652
+ machineSelectorConfig: []
653
+ },
654
+ kubernetesVersion: k8sVersion,
655
+ },
656
+ agentConfig: {}
657
+ },
658
+ provider: 'custom',
659
+ },
660
+ global: {
661
+ mocks: {
662
+ ...defaultMocks,
663
+ $store: { dispatch: () => jest.fn(), getters: defaultGetters },
664
+ $extension: { getDynamic: jest.fn(() => undefined ) },
665
+ },
666
+ stubs: defaultStubs,
667
+ },
668
+ });
669
+ };
670
+ const mockCharts = {
671
+ [RKE2_INGRESS_NGINX]: {},
672
+ [RKE2_TRAEFIK]: {}
673
+ };
674
+
675
+ it('should set ingress-controller to traefik by default for new clusters', async() => {
676
+ const wrapper = createWrapper(_CREATE);
677
+
678
+ await wrapper.setData({
679
+ rke2Versions: [{
680
+ id: k8sVersion,
681
+ version: k8sVersion,
682
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
683
+ charts: mockCharts
684
+ }]
685
+ });
686
+
687
+ expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(TRAEFIK);
688
+ });
689
+
690
+ it('should set ingress-controller to None on version change when nginx is not supported', async() => {
691
+ const wrapper = createWrapper();
692
+ const newVersion = 'v1.26.0+rke2r1';
693
+
694
+ await wrapper.setData({
695
+ rke2Versions: [{
696
+ id: k8sVersion,
697
+ version: k8sVersion,
698
+ serverArgs: { disable: { options: [] } },
699
+ charts: mockCharts
700
+ },
701
+ {
702
+ id: newVersion,
703
+ version: newVersion,
704
+ serverArgs: { disable: { options: [] } },
705
+ charts: mockCharts
706
+ }]
707
+ });
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);
711
+ });
712
+
713
+ it('should set ingress-controller to ingress-nginx on change when nginx is supported and not disabled', () => {
714
+ const wrapper = createWrapper();
715
+
716
+ wrapper.setData({
717
+ rke2Versions: [{
718
+ id: k8sVersion,
719
+ version: k8sVersion,
720
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
721
+ charts: mockCharts
722
+ }]
723
+ });
724
+
725
+ (wrapper.vm as any).handleEnabledSystemServicesChanged([]);
726
+
727
+ expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NGINX);
728
+ });
729
+
730
+ it('should set ingress-controller to None when nginx is supported but disabled', () => {
731
+ const wrapper = createWrapper();
732
+
733
+ wrapper.setData({
734
+ rke2Versions: [{
735
+ id: k8sVersion,
736
+ version: k8sVersion,
737
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
738
+ charts: mockCharts
739
+ }]
740
+ });
741
+
742
+ (wrapper.vm as any).handleEnabledSystemServicesChanged([RKE2_INGRESS_NGINX]);
743
+
744
+ expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NONE);
745
+ });
746
+
747
+ it('should set ingress-controller for existing cluster to None when nginx is not supported', () => {
748
+ const wrapper = createWrapper();
749
+
750
+ wrapper.setData({
751
+ rke2Versions: [{
752
+ id: k8sVersion,
753
+ version: k8sVersion,
754
+ serverArgs: { disable: { options: [] } },
755
+ charts: mockCharts
756
+ }]
757
+ });
758
+
759
+ (wrapper.vm as any).handleEnabledSystemServicesChanged([]);
760
+
761
+ expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NONE);
762
+ });
763
+
764
+ it('should correctly update disable list in serverConfig', () => {
765
+ const wrapper = createWrapper();
766
+
767
+ wrapper.setData({
768
+ rke2Versions: [{
769
+ id: k8sVersion,
770
+ version: k8sVersion,
771
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
772
+ charts: mockCharts
773
+ }]
774
+ });
775
+ const disabledServices = ['other-service'];
776
+
777
+ (wrapper.vm as any).handleEnabledSystemServicesChanged(disabledServices);
778
+
779
+ expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig.disable).toStrictEqual(disabledServices);
780
+ });
781
+
782
+ it('should set ingress-controller to ingress-nginx on version change when nginx is supported', async() => {
783
+ const wrapper = createWrapper();
784
+ const newVersion = 'v1.26.0+rke2r1';
785
+
786
+ await wrapper.setData({
787
+ rke2Versions: [
788
+ {
789
+ id: k8sVersion,
790
+ version: k8sVersion,
791
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
792
+ charts: mockCharts
793
+ },
794
+ {
795
+ id: newVersion,
796
+ version: newVersion,
797
+ serverArgs: { disable: { options: [RKE2_INGRESS_NGINX] } },
798
+ charts: mockCharts
799
+ }
800
+ ]
801
+ });
802
+
803
+ wrapper.vm.value.spec.kubernetesVersion = newVersion;
804
+ (wrapper.vm as any).handleKubernetesChange(newVersion);
805
+
806
+ expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NGINX);
807
+ });
808
+ });
615
809
  });
@@ -0,0 +1,112 @@
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
+ <rc-item-card-action>
64
+ <a
65
+ :href="card.doc.url"
66
+ rel="nofollow noopener noreferrer"
67
+ target="_blank"
68
+ class="ingress-card-footer-button secondary-text-link"
69
+ >
70
+ {{ t('cluster.ingress.learnMore.label') }}
71
+ <i class="icon icon-external-link" />
72
+ <span class="sr-only">{{ t('generic.opensInNewTab') }}</span>
73
+ </a>
74
+ </rc-item-card-action>
75
+ </template>
76
+ </rc-item-card>
77
+ </div>
78
+ </template>
79
+
80
+ <style scoped lang="scss">
81
+ .ingress-cards{
82
+ display: grid;
83
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
84
+ grid-gap: var(--gap-md);
85
+ width: 100%;
86
+ height: max-content;
87
+ overflow: hidden;
88
+
89
+ .single-card {
90
+ max-width: 500px;
91
+ }
92
+ }
93
+ .ingress-card-sub-header {
94
+ display: flex;
95
+ flex-wrap: wrap;
96
+ gap: var(--gap) var(--gap-md);
97
+ color: var(--link-text-secondary);
98
+ margin-bottom: 8px;
99
+ }
100
+
101
+ button.variant-ghost.ingress-card-footer-button {
102
+ padding: 0;
103
+ gap: 0;
104
+ min-height: 20px;
105
+
106
+ &:focus-visible {
107
+ border-color: var(--primary);
108
+ @include focus-outline;
109
+ outline-offset: -2px;
110
+ }
111
+ }
112
+ </style>