@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.
- package/assets/images/providers/oci-open-containers.svg +22 -0
- package/assets/images/providers/traefik.png +0 -0
- package/assets/styles/themes/_dark.scss +2 -0
- package/assets/styles/themes/_light.scss +2 -0
- package/assets/styles/themes/_modern.scss +6 -0
- package/assets/translations/en-us.yaml +129 -25
- package/components/CruResource.vue +3 -1
- package/components/ExplorerProjectsNamespaces.vue +12 -12
- package/components/IconOrSvg.vue +61 -42
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +109 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +21 -4
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +19 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +19 -11
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +12 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +2 -0
- package/components/Resource/Detail/ResourceRow.vue +2 -2
- package/components/ResourceList/index.vue +7 -4
- package/components/SortableTable/index.vue +2 -2
- package/components/Window/ContainerLogs.vue +48 -37
- package/components/fleet/FleetClusterTargets/TargetsList.vue +2 -2
- package/components/fleet/FleetClusterTargets/index.vue +6 -1
- package/components/fleet/GitRepoAdvancedTab.vue +333 -0
- package/components/fleet/GitRepoMetadataTab.vue +43 -0
- package/components/fleet/GitRepoRepositoryTab.vue +101 -0
- package/components/fleet/GitRepoTargetTab.vue +77 -0
- package/components/fleet/HelmOpAdvancedTab.vue +247 -0
- package/components/fleet/HelmOpChartTab.vue +158 -0
- package/components/fleet/HelmOpMetadataTab.vue +46 -0
- package/components/fleet/HelmOpTargetTab.vue +84 -0
- package/components/fleet/HelmOpValuesTab.vue +147 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +119 -70
- package/components/form/BannerSettings.vue +2 -2
- package/components/form/NodeScheduling.vue +81 -7
- package/components/form/NotificationSettings.vue +2 -2
- package/components/form/PodAffinity.vue +1 -36
- package/components/form/ResourceLabeledSelect.vue +8 -4
- package/components/form/ResourceQuota/Namespace.vue +30 -9
- package/components/form/ResourceQuota/NamespaceRow.vue +25 -7
- package/components/form/ResourceQuota/Project.vue +140 -82
- package/components/form/ResourceQuota/ResourceQuotaEntry.vue +145 -0
- package/components/form/ResourceQuota/__tests__/Namespace.test.ts +307 -0
- package/components/form/ResourceQuota/__tests__/NamespaceRow.test.ts +281 -0
- package/components/form/ResourceQuota/__tests__/Project.test.ts +274 -27
- package/components/form/ResourceQuota/__tests__/ResourceQuotaEntry.test.ts +215 -0
- package/components/form/SchedulingCustomization.vue +14 -6
- package/components/form/SelectOrCreateAuthSecret.vue +107 -18
- package/components/form/__tests__/NodeScheduling.test.ts +12 -9
- package/components/form/__tests__/PodAffinity.test.ts +21 -2
- package/components/form/__tests__/SchedulingCustomization.test.ts +240 -0
- package/components/formatter/ClusterLink.vue +8 -0
- package/components/formatter/SecretOrigin.vue +79 -0
- package/config/labels-annotations.js +7 -6
- package/config/pagination-table-headers.js +6 -4
- package/config/product/explorer.js +1 -11
- package/config/product/manager.js +0 -1
- package/config/query-params.js +3 -0
- package/config/settings.ts +15 -2
- package/config/table-headers.js +21 -17
- package/config/types.js +23 -8
- package/detail/fleet.cattle.io.cluster.vue +1 -1
- package/detail/workload/index.vue +11 -16
- package/dialog/DeactivateDriverDialog.vue +1 -1
- package/dialog/FeatureFlagListDialog.vue +1 -1
- package/dialog/Ipv6NetworkingDialog.vue +156 -0
- package/dialog/ScalePoolDownDialog.vue +2 -2
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +1 -0
- package/edit/__tests__/management.cattle.io.project.test.js +56 -128
- package/edit/auth/oidc.vue +1 -1
- package/edit/catalog.cattle.io.clusterrepo.vue +155 -25
- package/edit/fleet.cattle.io.gitrepo.vue +153 -283
- package/edit/fleet.cattle.io.helmop.vue +190 -332
- package/edit/management.cattle.io.project.vue +5 -42
- package/edit/management.cattle.io.setting.vue +6 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
- package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
- package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
- package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +55 -24
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +1 -103
- package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2-fleet-cluster-agent.test.ts +283 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -49
- package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +114 -0
- package/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue +158 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +167 -69
- package/edit/provisioning.cattle.io.cluster/shared.ts +36 -1
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +70 -7
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +343 -0
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +10 -44
- package/edit/secret/index.vue +1 -1
- package/edit/token.vue +68 -29
- package/edit/workload/__tests__/index.test.ts +2 -37
- package/edit/workload/index.vue +6 -2
- package/edit/workload/mixins/workload.js +0 -32
- package/list/__tests__/management.cattle.io.setting.test.ts +198 -0
- package/list/management.cattle.io.setting.vue +13 -0
- package/list/provisioning.cattle.io.cluster.vue +50 -1
- package/list/secret.vue +4 -9
- package/list/service.vue +6 -8
- package/machine-config/amazonec2.vue +11 -4
- package/machine-config/components/EC2Networking.vue +46 -30
- package/machine-config/components/__tests__/EC2Networking.test.ts +7 -7
- package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +0 -9
- package/machine-config/digitalocean.vue +3 -3
- package/models/__tests__/chart.test.ts +2 -2
- package/models/__tests__/namespace.test.ts +11 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +96 -0
- package/models/__tests__/workload.test.ts +42 -1
- package/models/catalog.cattle.io.clusterrepo.js +30 -4
- package/models/chart.js +3 -3
- package/models/ext.cattle.io.token.js +48 -0
- package/models/kontainerdriver.js +2 -2
- package/models/namespace.js +7 -1
- package/models/nodedriver.js +2 -2
- package/models/provisioning.cattle.io.cluster.js +28 -7
- package/models/secret.js +0 -17
- package/models/service.js +44 -1
- package/models/token.js +4 -0
- package/models/workload.js +12 -6
- package/package.json +1 -1
- package/pages/account/index.vue +96 -67
- package/pages/auth/setup.vue +5 -14
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +45 -18
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +4 -1
- package/pages/c/_cluster/apps/charts/index.vue +82 -3
- package/pages/c/_cluster/apps/charts/install.vue +317 -42
- package/pages/c/_cluster/explorer/tools/index.vue +1 -1
- package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -4
- package/pages/c/_cluster/settings/index.vue +3 -1
- package/pages/c/_cluster/uiplugins/index.vue +1 -1
- package/plugins/dashboard-store/__tests__/getters.test.ts +108 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +27 -0
- package/plugins/dashboard-store/actions.js +3 -8
- package/plugins/dashboard-store/getters.js +7 -5
- package/plugins/dashboard-store/mutations.js +4 -1
- package/plugins/dashboard-store/resource-class.js +3 -3
- package/plugins/steve/__tests__/steve-class.test.ts +102 -141
- package/plugins/steve/steve-class.js +12 -3
- package/plugins/steve/steve-pagination-utils.ts +6 -2
- package/rancher-components/RcIcon/types.ts +2 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +72 -20
- package/store/prefs.js +3 -0
- package/types/aws-sdk.d.ts +121 -0
- package/types/resources/node.ts +15 -0
- package/types/shell/index.d.ts +537 -506
- package/types/store/pagination.types.ts +5 -5
- package/utils/__tests__/array.test.ts +1 -29
- package/utils/__tests__/cluster-agent-configuration.test.ts +203 -0
- package/utils/array.ts +0 -11
- package/utils/aws.ts +21 -0
- package/utils/cluster.js +22 -2
- package/utils/selector-typed.ts +1 -1
- package/utils/svg-filter.js +4 -3
- package/components/__tests__/ProjectRow.test.ts +0 -206
- 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,
|
|
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
|
-
|
|
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
|
|
package/utils/selector-typed.ts
CHANGED
|
@@ -110,7 +110,7 @@ export async function matching({
|
|
|
110
110
|
return generateMatchingResponse([], inScopeCount || 0);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
if ($store.getters[`${ inStore }/paginationEnabled`]?.(
|
|
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
|
package/utils/svg-filter.js
CHANGED
|
@@ -169,9 +169,10 @@ export class Solver {
|
|
|
169
169
|
const result = this.solveNarrow(this.solveWide());
|
|
170
170
|
|
|
171
171
|
return {
|
|
172
|
-
values:
|
|
173
|
-
loss:
|
|
174
|
-
filter:
|
|
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
|
-
});
|