@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
@@ -73,7 +73,7 @@ export const enum PaginationFilterEquality {
73
73
  /**
74
74
  * Field does not match a value
75
75
  */
76
- NOT_EQUALS= '!=', // eslint-disable-line no-unused-vars
76
+ NOT_EQUALS = '!=', // eslint-disable-line no-unused-vars
77
77
  /**
78
78
  * Unknown
79
79
  */
@@ -81,19 +81,19 @@ export const enum PaginationFilterEquality {
81
81
  /**
82
82
  * Field must partially match a value
83
83
  */
84
- CONTAINS= '~', // eslint-disable-line no-unused-vars
84
+ CONTAINS = '~', // eslint-disable-line no-unused-vars
85
85
  /**
86
86
  * Field must not partially match a value
87
87
  */
88
- NOT_CONTAINS= '!~', // eslint-disable-line no-unused-vars
88
+ NOT_CONTAINS = '!~', // eslint-disable-line no-unused-vars
89
89
  /**
90
90
  * Field must be greater than a value
91
91
  */
92
- GREATER_THAN= 'gt', // eslint-disable-line no-unused-vars
92
+ GREATER_THAN = 'gt', // eslint-disable-line no-unused-vars
93
93
  /**
94
94
  * Field must be less than a value
95
95
  */
96
- LESS_THAN= 'lt', // eslint-disable-line no-unused-vars
96
+ LESS_THAN = 'lt', // eslint-disable-line no-unused-vars
97
97
  }
98
98
 
99
99
  /**
@@ -1,5 +1,5 @@
1
1
  import {
2
- addObject, addObjects, clear, filterBy, findBy, getUniqueLabelKeys, insertAt, isArray, joinStringList, removeAt, removeObject, removeObjects, replaceWith, sameContents, uniq
2
+ addObject, addObjects, clear, filterBy, findBy, insertAt, isArray, joinStringList, removeAt, removeObject, removeObjects, replaceWith, sameContents, uniq
3
3
  } from '@shell/utils/array';
4
4
 
5
5
  interface Obj {
@@ -473,34 +473,6 @@ describe('fx: replaceWith', () => {
473
473
  });
474
474
  });
475
475
 
476
- describe('fx: getUniqueLabelKeys', () => {
477
- it('should get list of unique resource labels', () => {
478
- const resources = [
479
- {
480
- metadata: {
481
- labels: {
482
- keyOne: 'value',
483
- keyTwo: 'value',
484
- }
485
- }
486
- },
487
- {
488
- metadata: {
489
- labels: {
490
- keyOne: 'value',
491
- keyThree: 'value',
492
- }
493
- }
494
- },
495
- ] as unknown as { metadata: { labels: { [name: string]: string} } }[] ;
496
-
497
- const expected = ['keyOne', 'keyThree', 'keyTwo'];
498
- const result = getUniqueLabelKeys(resources);
499
-
500
- expect(result).toStrictEqual(expected);
501
- });
502
- });
503
-
504
476
  describe('fx: joinStringList', () => {
505
477
  it('should join two lists of strings', () => {
506
478
  const a = 'a b c';
@@ -0,0 +1,203 @@
1
+ import { initSchedulingCustomization } from '@shell/utils/cluster';
2
+ import { _CREATE, _EDIT } from '@shell/config/query-params';
3
+ import { AGENT_CONFIGURATION_TYPES } from '@shell/config/settings';
4
+
5
+ const mockStore = { dispatch: jest.fn() };
6
+
7
+ const mockFeatures = jest.fn();
8
+
9
+ interface MockValue {
10
+ clusterAgentDeploymentCustomization: any;
11
+ fleetAgentDeploymentCustomization: any;
12
+ [key: string]: any;
13
+ }
14
+
15
+ const createMockValue = (overrides = {}): MockValue => ({
16
+ clusterAgentDeploymentCustomization: {},
17
+ fleetAgentDeploymentCustomization: {},
18
+ ...overrides
19
+ });
20
+
21
+ describe('utils: cluster - Agent Configuration Types and Scheduling Customization', () => {
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+
25
+ // Mock successful store dispatch responses
26
+ mockStore.dispatch.mockImplementation((actionType, actionPayload) => {
27
+ if (actionType === 'management/find' && actionPayload?.id === 'cluster-agent-default-priority-class') {
28
+ return Promise.resolve({ value: JSON.stringify({ value: 100, preemptionPolicy: 'PreemptLowerPriority' }) });
29
+ }
30
+ if (actionType === 'management/find' && actionPayload?.id === 'cluster-agent-default-pod-disruption-budget') {
31
+ return Promise.resolve({ value: JSON.stringify({ maxUnavailable: 1 }) });
32
+ }
33
+ if (actionType === 'management/find' && actionPayload?.id === 'fleet-agent-default-priority-class') {
34
+ return Promise.resolve({ value: JSON.stringify({ value: 99, preemptionPolicy: 'PreemptLowerPriority' }) });
35
+ }
36
+ if (actionType === 'management/find' && actionPayload?.id === 'fleet-agent-default-pod-disruption-budget') {
37
+ return Promise.resolve({ value: JSON.stringify({ maxUnavailable: 2 }) });
38
+ }
39
+
40
+ return Promise.resolve({ value: '{}' });
41
+ });
42
+ });
43
+
44
+ describe('agent configuration types', () => {
45
+ it('should export correct agent configuration types', () => {
46
+ expect(AGENT_CONFIGURATION_TYPES).toBeDefined();
47
+ expect(AGENT_CONFIGURATION_TYPES.CLUSTER).toBe('cluster');
48
+ expect(AGENT_CONFIGURATION_TYPES.FLEET).toBe('fleet');
49
+ });
50
+
51
+ it('should have exactly two agent types', () => {
52
+ const keys = Object.keys(AGENT_CONFIGURATION_TYPES);
53
+
54
+ expect(keys).toHaveLength(2);
55
+ expect(keys).toContain('CLUSTER');
56
+ expect(keys).toContain('FLEET');
57
+ });
58
+ });
59
+
60
+ describe('initSchedulingCustomization', () => {
61
+ it('should initialize both cluster and fleet agent defaults in CREATE mode', async() => {
62
+ const value = createMockValue();
63
+
64
+ mockFeatures.mockReturnValue(true); // Enable scheduling customization feature
65
+
66
+ const result = await initSchedulingCustomization(value, mockFeatures, mockStore, _CREATE);
67
+
68
+ expect(result.clusterAgentDefaultPC).toStrictEqual({ value: 100, preemptionPolicy: 'PreemptLowerPriority' });
69
+ expect(result.clusterAgentDefaultPDB).toStrictEqual({ maxUnavailable: 1 });
70
+ expect(result.fleetAgentDefaultPC).toStrictEqual({ value: 99, preemptionPolicy: 'PreemptLowerPriority' });
71
+ expect(result.fleetAgentDefaultPDB).toStrictEqual({ maxUnavailable: 2 });
72
+ expect(result.schedulingCustomizationFeatureEnabled).toBe(true);
73
+ });
74
+
75
+ it('should set cluster agent scheduling customization in CREATE mode when feature enabled', async() => {
76
+ const value = createMockValue();
77
+
78
+ mockFeatures.mockReturnValue(true);
79
+
80
+ await initSchedulingCustomization(value, mockFeatures, mockStore, _CREATE);
81
+
82
+ expect(value.clusterAgentDeploymentCustomization.schedulingCustomization).toStrictEqual({
83
+ priorityClass: { value: 100, preemptionPolicy: 'PreemptLowerPriority' },
84
+ podDisruptionBudget: { maxUnavailable: 1 }
85
+ });
86
+ });
87
+
88
+ it('should set fleet agent scheduling customization in CREATE mode when feature enabled', async() => {
89
+ const value = createMockValue();
90
+
91
+ mockFeatures.mockReturnValue(true);
92
+
93
+ await initSchedulingCustomization(value, mockFeatures, mockStore, _CREATE);
94
+
95
+ expect(value.fleetAgentDeploymentCustomization.schedulingCustomization).toStrictEqual({
96
+ priorityClass: { value: 99, preemptionPolicy: 'PreemptLowerPriority' },
97
+ podDisruptionBudget: { maxUnavailable: 2 }
98
+ });
99
+ });
100
+
101
+ it('should not set scheduling customization when feature is disabled', async() => {
102
+ const value = createMockValue();
103
+
104
+ mockFeatures.mockReturnValue(false);
105
+
106
+ await initSchedulingCustomization(value, mockFeatures, mockStore, _CREATE);
107
+
108
+ expect(value.clusterAgentDeploymentCustomization.schedulingCustomization).toBeUndefined();
109
+ expect(value.fleetAgentDeploymentCustomization.schedulingCustomization).toBeUndefined();
110
+ });
111
+
112
+ it('should not overwrite existing cluster agent scheduling customization in CREATE mode', async() => {
113
+ const existingClusterConfig = { priorityClass: { value: 200 }, podDisruptionBudget: { maxUnavailable: 2 } };
114
+ const value = createMockValue({ clusterAgentDeploymentCustomization: { schedulingCustomization: existingClusterConfig } });
115
+
116
+ mockFeatures.mockReturnValue(true);
117
+
118
+ await initSchedulingCustomization(value, mockFeatures, mockStore, _CREATE);
119
+
120
+ expect(value.clusterAgentDeploymentCustomization.schedulingCustomization).toStrictEqual(existingClusterConfig);
121
+ });
122
+
123
+ it('should not overwrite existing fleet agent scheduling customization in CREATE mode', async() => {
124
+ const existingFleetConfig = { priorityClass: { value: 300 }, podDisruptionBudget: { maxUnavailable: 3 } };
125
+ const value = createMockValue({ fleetAgentDeploymentCustomization: { schedulingCustomization: existingFleetConfig } });
126
+
127
+ mockFeatures.mockReturnValue(true);
128
+
129
+ await initSchedulingCustomization(value, mockFeatures, mockStore, _CREATE);
130
+
131
+ expect(value.fleetAgentDeploymentCustomization.schedulingCustomization).toStrictEqual(existingFleetConfig);
132
+ });
133
+
134
+ it('should detect originally enabled scheduling customization in EDIT mode', async() => {
135
+ const value = createMockValue({ clusterAgentDeploymentCustomization: { schedulingCustomization: { priorityClass: { value: 100 } } } });
136
+
137
+ mockFeatures.mockReturnValue(true);
138
+
139
+ const result = await initSchedulingCustomization(value, mockFeatures, mockStore, _EDIT);
140
+
141
+ expect(result.schedulingCustomizationOriginallyEnabled).toBe(true);
142
+ });
143
+
144
+ it('should detect originally enabled scheduling customization for fleet agent in EDIT mode', async() => {
145
+ const value = createMockValue({ fleetAgentDeploymentCustomization: { schedulingCustomization: { priorityClass: { value: 99 } } } });
146
+
147
+ mockFeatures.mockReturnValue(true);
148
+
149
+ const result = await initSchedulingCustomization(value, mockFeatures, mockStore, _EDIT);
150
+
151
+ expect(result.schedulingCustomizationOriginallyEnabled).toBe(true);
152
+ });
153
+
154
+ it('should detect originally enabled when either cluster or fleet agent has customization', async() => {
155
+ const value = createMockValue({ fleetAgentDeploymentCustomization: { schedulingCustomization: { priorityClass: { value: 99 } } } });
156
+ // clusterAgentDeploymentCustomization has no schedulingCustomization
157
+
158
+ mockFeatures.mockReturnValue(true);
159
+
160
+ const result = await initSchedulingCustomization(value, mockFeatures, mockStore, _EDIT);
161
+
162
+ expect(result.schedulingCustomizationOriginallyEnabled).toBe(true);
163
+ });
164
+
165
+ it('should handle store dispatch errors gracefully', async() => {
166
+ const value = createMockValue();
167
+
168
+ mockFeatures.mockReturnValue(true);
169
+ mockStore.dispatch.mockRejectedValue(new Error('Store error'));
170
+
171
+ const result = await initSchedulingCustomization(value, mockFeatures, mockStore, _CREATE);
172
+
173
+ expect(result.errors).toHaveLength(4); // 4 dispatch calls, all failing
174
+ expect(result.clusterAgentDefaultPC).toBeNull();
175
+ expect(result.clusterAgentDefaultPDB).toBeNull();
176
+ expect(result.fleetAgentDefaultPC).toBeNull();
177
+ expect(result.fleetAgentDefaultPDB).toBeNull();
178
+ });
179
+
180
+ it('should handle JSON parsing errors gracefully', async() => {
181
+ const value = createMockValue();
182
+
183
+ mockFeatures.mockReturnValue(true);
184
+ mockStore.dispatch.mockResolvedValue({ value: 'invalid-json' });
185
+
186
+ const result = await initSchedulingCustomization(value, mockFeatures, mockStore, _CREATE);
187
+
188
+ expect(result.errors).toHaveLength(4); // JSON parsing errors
189
+ expect(result.clusterAgentDefaultPC).toBeNull();
190
+ expect(result.fleetAgentDefaultPC).toBeNull();
191
+ });
192
+
193
+ it('should return empty errors array when everything succeeds', async() => {
194
+ const value = createMockValue();
195
+
196
+ mockFeatures.mockReturnValue(true);
197
+
198
+ const result = await initSchedulingCustomization(value, mockFeatures, mockStore, _CREATE);
199
+
200
+ expect(result.errors).toHaveLength(0);
201
+ });
202
+ });
203
+ });
package/utils/array.ts CHANGED
@@ -231,17 +231,6 @@ export function concatStrings(a: string[], b: string[]): string[] {
231
231
  return [...a.map((aa) => b.map((bb) => aa.concat(bb)))].reduce((acc, arr) => [...arr, ...acc], []);
232
232
  }
233
233
 
234
- interface KubeResource { metadata: { labels: { [name: string]: string} } } // Migrate to central kube types resource when those are brought in
235
- export function getUniqueLabelKeys<T extends KubeResource>(aryResources: T[]): string[] {
236
- const uniqueObj = aryResources.reduce((res, r) => {
237
- Object.keys(r.metadata.labels).forEach((l) => (res[l] = true));
238
-
239
- return res;
240
- }, {} as {[label: string]: boolean});
241
-
242
- return Object.keys(uniqueObj).sort();
243
- }
244
-
245
234
  /**
246
235
  * Join list as string into a new string without duplicates
247
236
  * @param {string} a 'a b c'
package/utils/aws.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { Subnet, VPC } from '@shell/types/aws-sdk';
2
+
3
+ export function isIpv4Network(network: Subnet | VPC): boolean {
4
+ return !!network.CidrBlock;
5
+ }
6
+
7
+ export function isIpv6Network(network: Subnet | VPC): boolean {
8
+ return !!network.Ipv6CidrBlockAssociationSet?.length;
9
+ }
10
+
11
+ export function getVpcDisplayName(vpc: VPC): string {
12
+ const nameTag = vpc.Tags?.find((t) => t.Key === 'Name');
13
+
14
+ return nameTag ? `${ nameTag.Value } (${ vpc.VpcId })` : vpc.VpcId;
15
+ }
16
+
17
+ export function getSubnetDisplayName(subnet: Subnet): string {
18
+ const nameTag = subnet.Tags?.find((t) => t.Key === 'Name');
19
+
20
+ return nameTag ? `${ nameTag.Value } (${ subnet.SubnetId })` : subnet.SubnetId;
21
+ }
package/utils/cluster.js CHANGED
@@ -312,9 +312,12 @@ export async function initSchedulingCustomization(value, features, store, mode)
312
312
  const schedulingCustomizationFeatureEnabled = features(SCHEDULING_CUSTOMIZATION);
313
313
  let clusterAgentDefaultPC = null;
314
314
  let clusterAgentDefaultPDB = null;
315
+ let fleetAgentDefaultPC = null;
316
+ let fleetAgentDefaultPDB = null;
315
317
  let schedulingCustomizationOriginallyEnabled = false;
316
318
  const errors = [];
317
319
 
320
+ // Cluster Agent Config
318
321
  try {
319
322
  clusterAgentDefaultPC = JSON.parse((await store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.CLUSTER_AGENT_DEFAULT_PRIORITY_CLASS })).value) || null;
320
323
  } catch (e) {
@@ -330,12 +333,29 @@ export async function initSchedulingCustomization(value, features, store, mode)
330
333
  set(value, 'clusterAgentDeploymentCustomization.schedulingCustomization', { priorityClass: clusterAgentDefaultPC, podDisruptionBudget: clusterAgentDefaultPDB });
331
334
  }
332
335
 
333
- if (mode === _EDIT && !!value?.clusterAgentDeploymentCustomization?.schedulingCustomization) {
336
+ // Fleet Agent Config
337
+ try {
338
+ fleetAgentDefaultPC = JSON.parse((await store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.FLEET_AGENT_DEFAULT_PRIORITY_CLASS })).value) || null;
339
+ } catch (e) {
340
+ errors.push(e);
341
+ }
342
+ try {
343
+ fleetAgentDefaultPDB = JSON.parse((await store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.FLEET_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET })).value) || null;
344
+ } catch (e) {
345
+ errors.push(e);
346
+ }
347
+
348
+ if (schedulingCustomizationFeatureEnabled && mode === _CREATE && isEmptyLodash(value?.fleetAgentDeploymentCustomization?.schedulingCustomization)) {
349
+ set(value, 'fleetAgentDeploymentCustomization.schedulingCustomization', { priorityClass: fleetAgentDefaultPC, podDisruptionBudget: fleetAgentDefaultPDB });
350
+ }
351
+
352
+ // Validates if any, FleetAgent or ClusterAgent have been added before
353
+ if (mode === _EDIT && (!!value?.clusterAgentDeploymentCustomization?.schedulingCustomization || !!value?.fleetAgentDeploymentCustomization?.schedulingCustomization)) {
334
354
  schedulingCustomizationOriginallyEnabled = true;
335
355
  }
336
356
 
337
357
  return {
338
- clusterAgentDefaultPC, clusterAgentDefaultPDB, schedulingCustomizationFeatureEnabled, schedulingCustomizationOriginallyEnabled, errors
358
+ fleetAgentDefaultPC, fleetAgentDefaultPDB, clusterAgentDefaultPC, clusterAgentDefaultPDB, schedulingCustomizationFeatureEnabled, schedulingCustomizationOriginallyEnabled, errors
339
359
  };
340
360
  }
341
361
 
@@ -110,7 +110,7 @@ export async function matching({
110
110
  return generateMatchingResponse([], inScopeCount || 0);
111
111
  }
112
112
 
113
- if ($store.getters[`${ inStore }/paginationEnabled`]?.({ id: type })) {
113
+ if ($store.getters[`${ inStore }/paginationEnabled`]?.()) {
114
114
  if (isLabelSelectorEmpty(labelSelector) && (!!namespace && !safeNamespaces?.length)) {
115
115
  // no namespaces - ALL resources are candidates
116
116
  // no labels - return all candidates
@@ -169,9 +169,10 @@ export class Solver {
169
169
  const result = this.solveNarrow(this.solveWide());
170
170
 
171
171
  return {
172
- values: result.values,
173
- loss: result.loss,
174
- filter: this.css(result.values),
172
+ values: result.values,
173
+ loss: result.loss,
174
+ filter: this.css(result.values),
175
+ filterVal: this.css(result.values).replace('filter: ', '').replace(';', '')
175
176
  };
176
177
  }
177
178
 
@@ -1,206 +0,0 @@
1
- import ProjectRow from '@shell/components/form/ResourceQuota/ProjectRow.vue';
2
- import { RANCHER_TYPES, TYPES } from '@shell/components/form/ResourceQuota/shared';
3
- import { shallowMount } from '@vue/test-utils';
4
-
5
- const CONFIGMAP_STRING = TYPES.CONFIG_MAPS;
6
-
7
- describe('component: ProjectRow.vue', () => {
8
- const defaultMountOptions = {
9
- props: {
10
- mode: 'edit',
11
- types: RANCHER_TYPES,
12
- type: CONFIGMAP_STRING,
13
- index: 0,
14
- value: {
15
- spec: {
16
- namespaceDefaultResourceQuota: { limit: {} },
17
- resourceQuota: { limit: {} }
18
- }
19
- }
20
- }
21
- };
22
-
23
- it('should render the correct input fields and set the correct computed values, based on the provided data', () => {
24
- const wrapper = shallowMount(
25
- ProjectRow,
26
- { ...defaultMountOptions }
27
- );
28
-
29
- const typeInput = wrapper.find(`[data-testid="projectrow-type-input"]`);
30
- const customTypeInput = wrapper.find(`[data-testid="projectrow-custom-type-input"]`);
31
- const projectQuotaInput = wrapper.find(`[data-testid="projectrow-project-quota-input"]`);
32
- const namespaceQuotaInput = wrapper.find(`[data-testid="projectrow-namespace-quota-input"]`);
33
-
34
- expect(typeInput.exists()).toBe(true);
35
- expect(customTypeInput.exists()).toBe(true);
36
- expect(customTypeInput.attributes().disabled).toBe('true');
37
- expect(projectQuotaInput.exists()).toBe(true);
38
- expect(namespaceQuotaInput.exists()).toBe(true);
39
- expect(wrapper.vm.resourceQuotaLimit).toStrictEqual({});
40
- expect(wrapper.vm.namespaceDefaultResourceQuotaLimit).toStrictEqual({});
41
- });
42
-
43
- it('triggering "updateQuotaLimit" should trigger Vue.set with the correct data', () => {
44
- const wrapper = shallowMount(
45
- ProjectRow,
46
- { ...defaultMountOptions }
47
- );
48
-
49
- wrapper.vm.updateQuotaLimit('resourceQuota', CONFIGMAP_STRING, 10);
50
-
51
- expect(wrapper.vm.value).toStrictEqual({
52
- spec: {
53
- namespaceDefaultResourceQuota: { limit: {} },
54
- resourceQuota: { limit: { [`${ CONFIGMAP_STRING }`]: 10 } }
55
- }
56
- });
57
- });
58
-
59
- it('triggering "updateType" with the same type that existed should clear limits and trigger emit', () => {
60
- const wrapper = shallowMount(
61
- ProjectRow,
62
- { ...defaultMountOptions }
63
- );
64
-
65
- wrapper.vm.updateType(CONFIGMAP_STRING);
66
-
67
- expect(wrapper.vm.value).toStrictEqual({
68
- spec: {
69
- namespaceDefaultResourceQuota: { limit: {} },
70
- resourceQuota: { limit: {} }
71
- }
72
- });
73
-
74
- expect(wrapper.emitted('type-change')).toBeTruthy();
75
- expect(wrapper.emitted('type-change')[0]).toStrictEqual([{ index: 0, type: CONFIGMAP_STRING }]);
76
- });
77
-
78
- it('should update standard resource types', async() => {
79
- const wrapper = shallowMount(
80
- ProjectRow,
81
- { ...defaultMountOptions }
82
- );
83
-
84
- expect(wrapper.vm.isCustom).toBe(false);
85
-
86
- await wrapper.vm.updateQuotaLimit('resourceQuota', 'limitsCpu', '100m');
87
- await wrapper.vm.updateQuotaLimit('namespaceDefaultResourceQuota', 'limitsCpu', '50m');
88
-
89
- expect(wrapper.vm.value.spec.resourceQuota.limit.limitsCpu).toBe('100m');
90
- expect(wrapper.vm.value.spec.namespaceDefaultResourceQuota.limit.limitsCpu).toBe('50m');
91
- expect(wrapper.vm.value.spec.resourceQuota.limit.extended).toBeUndefined();
92
- });
93
-
94
- it('should switch to a custom resource type', async() => {
95
- const wrapper = shallowMount(
96
- ProjectRow,
97
- { ...defaultMountOptions }
98
- );
99
-
100
- await wrapper.vm.updateType(TYPES.EXTENDED);
101
-
102
- expect(wrapper.emitted('type-change')).toHaveLength(1);
103
- expect(wrapper.emitted('type-change')[0][0]).toStrictEqual({ index: 0, type: TYPES.EXTENDED });
104
- });
105
-
106
- it('should update custom resource types', async() => {
107
- const wrapper = shallowMount(
108
- ProjectRow,
109
- {
110
- ...defaultMountOptions,
111
- props: {
112
- ...defaultMountOptions.props,
113
- type: TYPES.EXTENDED
114
- }
115
- }
116
- );
117
-
118
- expect(wrapper.vm.isCustom).toBe(true);
119
-
120
- const customTypeInput = wrapper.find(`[data-testid="projectrow-custom-type-input"]`);
121
-
122
- expect(customTypeInput.attributes().disabled).toBe('false');
123
- await wrapper.vm.updateCustomType('custom.resource/foo');
124
-
125
- expect(wrapper.vm.customType).toBe('custom.resource/foo');
126
-
127
- await wrapper.vm.updateQuotaLimit('resourceQuota', 'custom.resource/foo', 1);
128
- await wrapper.vm.updateQuotaLimit('namespaceDefaultResourceQuota', 'custom.resource/foo', 2);
129
-
130
- expect(wrapper.vm.value.spec.resourceQuota.limit.extended['custom.resource/foo']).toBe(1);
131
- expect(wrapper.vm.value.spec.namespaceDefaultResourceQuota.limit.extended['custom.resource/foo']).toBe(2);
132
- });
133
-
134
- it('should handle custom resource types with periods', () => {
135
- const wrapper = shallowMount(ProjectRow, {
136
- ...defaultMountOptions,
137
- props: {
138
- ...defaultMountOptions.props,
139
- type: 'extended.requests.nvidia.com/gpu'
140
- }
141
- });
142
-
143
- expect(wrapper.vm.isCustom).toBe(true);
144
- expect(wrapper.vm.customType).toBe('requests.nvidia.com/gpu');
145
- });
146
-
147
- it('should emit update:resource-identifier when updateCustomType is called', () => {
148
- const wrapper: any = shallowMount(ProjectRow, {
149
- props: {
150
- ...defaultMountOptions.props,
151
- type: TYPES.EXTENDED,
152
- }
153
- });
154
-
155
- wrapper.vm.updateCustomType('my-custom-resource');
156
-
157
- expect(wrapper.emitted('update:resource-identifier')).toBeTruthy();
158
- expect(wrapper.emitted('update:resource-identifier')[0]).toStrictEqual([{
159
- type: TYPES.EXTENDED,
160
- customType: 'my-custom-resource',
161
- index: 0
162
- }]);
163
- });
164
-
165
- it('should not delete resource limits if there are duplicate keys in localTypeValues', () => {
166
- const value = {
167
- spec: {
168
- resourceQuota: { limit: { extended: { 'my-resource': '10' } } },
169
- namespaceDefaultResourceQuota: { limit: { extended: { 'my-resource': '5' } } }
170
- }
171
- };
172
- const wrapper: any = shallowMount(ProjectRow, {
173
- props: {
174
- ...defaultMountOptions.props,
175
- value,
176
- typeValues: ['extended.my-resource', 'extended.my-resource']
177
- }
178
- });
179
-
180
- wrapper.vm.deleteResourceLimits('my-resource', true);
181
-
182
- expect(value.spec.resourceQuota.limit.extended['my-resource']).toStrictEqual('10');
183
- expect(value.spec.namespaceDefaultResourceQuota.limit.extended['my-resource']).toStrictEqual('5');
184
- });
185
-
186
- it('should delete resource limits if there is only one key in localTypeValues', () => {
187
- const value = {
188
- spec: {
189
- resourceQuota: { limit: { extended: { 'my-resource': '10' } } },
190
- namespaceDefaultResourceQuota: { limit: { extended: { 'my-resource': '5' } } }
191
- }
192
- };
193
- const wrapper: any = shallowMount(ProjectRow, {
194
- props: {
195
- ...defaultMountOptions.props,
196
- value,
197
- typeValues: ['extended.my-resource']
198
- }
199
- });
200
-
201
- wrapper.vm.deleteResourceLimits('my-resource', true);
202
-
203
- expect(value.spec.resourceQuota.limit.extended['my-resource']).toBeUndefined();
204
- expect(value.spec.namespaceDefaultResourceQuota.limit.extended['my-resource']).toBeUndefined();
205
- });
206
- });