@rancher/shell 0.3.8 → 0.3.10
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/translations/en-us.yaml +47 -26
- package/assets/translations/zh-hans.yaml +82 -16
- package/babel.config.js +17 -4
- package/chart/istio.vue +11 -11
- package/chart/rancher-backup/S3.vue +1 -1
- package/components/AsyncButton.vue +2 -2
- package/components/ButtonGroup.vue +1 -1
- package/components/CodeMirror.vue +146 -14
- package/components/CompoundStatusBadge.vue +1 -1
- package/components/ContainerResourceLimit.vue +14 -1
- package/components/CopyCode.vue +1 -1
- package/components/CruResource.vue +21 -5
- package/components/DetailTop.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +8 -4
- package/components/GlobalRoleBindings.vue +1 -1
- package/components/GroupPanel.vue +57 -0
- package/components/HarvesterServiceAddOnConfig.vue +2 -117
- package/components/ResourceDetail/Masthead.vue +1 -1
- package/components/ResourceList/Masthead.vue +0 -6
- package/components/ResourceList/ResourceLoadingIndicator.vue +1 -9
- package/components/ResourceList/index.vue +7 -6
- package/components/ResourceTable.vue +13 -3
- package/components/SortableTable/THead.vue +3 -3
- package/components/SortableTable/index.vue +3 -3
- package/components/Tabbed/Tab.vue +1 -1
- package/components/Tabbed/index.vue +1 -1
- package/components/Wizard.vue +9 -6
- package/components/YamlEditor.vue +2 -2
- package/components/__tests__/NamespaceFilter.test.ts +26 -7
- package/components/auth/RoleDetailEdit.vue +1 -1
- package/components/auth/SelectPrincipal.vue +1 -1
- package/components/fleet/FleetRepos.vue +1 -1
- package/components/form/ArrayList.vue +2 -2
- package/components/form/KeyValue.vue +37 -3
- package/components/form/Labels.vue +34 -14
- package/components/form/MatchExpressions.vue +120 -21
- package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
- package/components/form/NameNsDescription.vue +1 -1
- package/components/form/NodeAffinity.vue +54 -4
- package/components/form/PlusMinus.vue +2 -2
- package/components/form/PodAffinity.vue +160 -47
- package/components/form/Probe.vue +1 -1
- package/components/form/ProjectMemberEditor.vue +8 -4
- package/components/form/ResourceQuota/NamespaceRow.vue +1 -1
- package/components/form/ServicePorts.vue +2 -2
- package/components/form/Tolerations.vue +70 -7
- package/components/form/WorkloadPorts.vue +2 -1
- package/components/form/__tests__/ArrayList.test.ts +3 -3
- package/components/form/__tests__/KeyValue.test.ts +17 -0
- package/components/form/__tests__/MatchExpressions.test.ts +1 -1
- package/components/formatter/ClusterLink.vue +3 -3
- package/components/formatter/LiveDate.vue +1 -1
- package/components/formatter/PodImages.vue +1 -1
- package/components/formatter/RKETemplateName.vue +1 -1
- package/components/formatter/Shortened.vue +1 -1
- package/components/nav/Header.vue +9 -7
- package/components/nav/NamespaceFilter.vue +103 -54
- package/config/labels-annotations.js +8 -5
- package/config/settings.ts +8 -6
- package/config/types.js +6 -4
- package/core/plugin-routes.ts +26 -7
- package/detail/provisioning.cattle.io.cluster.vue +4 -4
- package/edit/cis.cattle.io.clusterscan.vue +1 -1
- package/edit/configmap.vue +33 -6
- package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +19 -149
- package/edit/logging-flow/index.vue +2 -2
- package/edit/logging.banzaicloud.io.output/providers/elasticsearch.vue +12 -0
- package/edit/logging.banzaicloud.io.output/providers/opensearch.vue +12 -0
- package/edit/management.cattle.io.project.vue +7 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/routeConfig.vue +2 -2
- package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +11 -8
- package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +2 -2
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +12 -4
- package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +140 -0
- package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +158 -0
- package/edit/networking.k8s.io.networkpolicy/__tests__/utils/selectors.ts +45 -0
- package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +326 -0
- package/edit/provisioning.cattle.io.cluster/MachinePool.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +2 -2
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +143 -169
- package/edit/provisioning.cattle.io.cluster/index.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +75 -6
- package/edit/resources.cattle.io.restore.vue +2 -2
- package/edit/service.vue +22 -3
- package/edit/storage.k8s.io.storageclass/index.vue +1 -1
- package/edit/workload/Job.vue +2 -2
- package/edit/workload/index.vue +1 -1
- package/edit/workload/mixins/workload.js +7 -1
- package/edit/workload/storage/__tests__/Storage.test.ts +84 -5
- package/initialize/index.js +1 -0
- package/layouts/default.vue +1 -1
- package/mixins/chart.js +1 -1
- package/mixins/resource-fetch-namespaced.js +19 -27
- package/mixins/resource-fetch.js +0 -5
- package/models/__tests__/namespace.test.ts +125 -0
- package/models/batch.cronjob.js +18 -3
- package/models/management.cattle.io.project.js +6 -1
- package/models/persistentvolume.js +1 -1
- package/models/workload.js +1 -1
- package/models/workload.service.js +22 -7
- package/package.json +17 -6
- package/pages/auth/login.vue +47 -49
- package/pages/c/_cluster/apps/charts/chart.vue +1 -1
- package/pages/c/_cluster/apps/charts/install.vue +42 -51
- package/pages/c/_cluster/explorer/index.vue +1 -1
- package/pages/c/_cluster/monitoring/index.vue +1 -1
- package/pages/c/_cluster/settings/performance.vue +53 -18
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
- package/pages/c/_cluster/uiplugins/index.vue +16 -5
- package/pages/home.vue +1 -1
- package/pages/prefs.vue +18 -2
- package/plugins/clean-html-directive.js +1 -1
- package/plugins/clean-tooltip-directive.js +33 -0
- package/plugins/codemirror.js +158 -0
- package/plugins/dashboard-store/actions.js +4 -2
- package/plugins/dashboard-store/getters.js +6 -0
- package/plugins/dashboard-store/mutations.js +2 -2
- package/plugins/plugin.js +6 -1
- package/plugins/steve/actions.js +1 -1
- package/plugins/steve/getters.js +14 -3
- package/plugins/steve/resourceWatcher.js +36 -62
- package/plugins/steve/subscribe.js +137 -21
- package/plugins/steve/worker/index.js +7 -1
- package/plugins/steve/worker/web-worker.advanced.js +26 -8
- package/plugins/steve/worker/web-worker.basic.js +23 -4
- package/public/index.html +1 -1
- package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -2
- package/rancher-components/components/Form/Radio/RadioGroup.vue +2 -2
- package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +1 -1
- package/store/index.js +16 -61
- package/store/store-types.js +5 -0
- package/store/type-map.js +1 -1
- package/types/shell/index.d.ts +42 -7
- package/utils/__tests__/create-yaml.test.ts +63 -0
- package/utils/array.ts +4 -0
- package/utils/create-yaml.js +105 -8
- package/utils/namespace-filter.js +17 -5
- package/utils/projectAndNamespaceFiltering.utils.ts +62 -0
- package/utils/selector.js +6 -5
- package/utils/settings.ts +17 -7
- package/vue.config.js +2 -2
- package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +0 -93
|
@@ -12,6 +12,12 @@ import debounce from 'lodash/debounce';
|
|
|
12
12
|
import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
|
|
13
13
|
import { getUniqueLabelKeys } from '@shell/utils/array';
|
|
14
14
|
|
|
15
|
+
const NAMESPACE_SELECTION_OPTION_VALUES = {
|
|
16
|
+
POD: 'pod',
|
|
17
|
+
ALL: 'all',
|
|
18
|
+
SELECTED: 'selected',
|
|
19
|
+
};
|
|
20
|
+
|
|
15
21
|
export default {
|
|
16
22
|
components: {
|
|
17
23
|
ArrayListGrouped, MatchExpressions, LabeledSelect, RadioGroup, LabeledInput
|
|
@@ -26,6 +32,13 @@ export default {
|
|
|
26
32
|
}
|
|
27
33
|
},
|
|
28
34
|
|
|
35
|
+
// Field key on the value object to store the pod affinity - typically this is 'affinity'
|
|
36
|
+
// Cluster Agent Configuration uses a different field
|
|
37
|
+
field: {
|
|
38
|
+
type: String,
|
|
39
|
+
default: 'affinity'
|
|
40
|
+
},
|
|
41
|
+
|
|
29
42
|
mode: {
|
|
30
43
|
type: String,
|
|
31
44
|
default: 'create'
|
|
@@ -40,6 +53,22 @@ export default {
|
|
|
40
53
|
type: Array,
|
|
41
54
|
default: null
|
|
42
55
|
},
|
|
56
|
+
|
|
57
|
+
allNamespacesOptionAvailable: {
|
|
58
|
+
default: false,
|
|
59
|
+
type: Boolean
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
forceInputNamespaceSelection: {
|
|
63
|
+
default: false,
|
|
64
|
+
type: Boolean
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
removeLabeledInputNamespaceLabel: {
|
|
68
|
+
default: false,
|
|
69
|
+
type: Boolean
|
|
70
|
+
},
|
|
71
|
+
|
|
43
72
|
loading: {
|
|
44
73
|
default: false,
|
|
45
74
|
type: Boolean
|
|
@@ -47,32 +76,38 @@ export default {
|
|
|
47
76
|
},
|
|
48
77
|
|
|
49
78
|
data() {
|
|
50
|
-
if (!this.value.
|
|
51
|
-
this.$set(this.value,
|
|
79
|
+
if (!this.value[this.field]) {
|
|
80
|
+
this.$set(this.value, this.field, {});
|
|
52
81
|
}
|
|
53
|
-
const { podAffinity = {}, podAntiAffinity = {} } = this.value.
|
|
82
|
+
const { podAffinity = {}, podAntiAffinity = {} } = this.value[this.field];
|
|
54
83
|
const allAffinityTerms = [...(podAffinity.preferredDuringSchedulingIgnoredDuringExecution || []), ...(podAffinity.requiredDuringSchedulingIgnoredDuringExecution || [])].map((term) => {
|
|
55
|
-
|
|
84
|
+
let out = clone(term);
|
|
56
85
|
|
|
57
86
|
out._id = randomStr(4);
|
|
58
87
|
out._anti = false;
|
|
59
88
|
if (term.podAffinityTerm) {
|
|
60
89
|
Object.assign(out, term.podAffinityTerm);
|
|
61
|
-
out
|
|
90
|
+
out = this.parsePodAffinityTerm(out);
|
|
91
|
+
|
|
62
92
|
delete out.podAffinityTerm;
|
|
93
|
+
} else {
|
|
94
|
+
out = this.parsePodAffinityTerm(out);
|
|
63
95
|
}
|
|
64
96
|
|
|
65
97
|
return out;
|
|
66
98
|
});
|
|
67
99
|
const allAntiTerms = [...(podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution || []), ...(podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution || [])].map((term) => {
|
|
68
|
-
|
|
100
|
+
let out = clone(term);
|
|
69
101
|
|
|
70
102
|
out._id = randomStr(4);
|
|
71
103
|
out._anti = true;
|
|
72
104
|
if (term.podAffinityTerm) {
|
|
73
105
|
Object.assign(out, term.podAffinityTerm);
|
|
74
|
-
out
|
|
106
|
+
out = this.parsePodAffinityTerm(out);
|
|
107
|
+
|
|
75
108
|
delete out.podAffinityTerm;
|
|
109
|
+
} else {
|
|
110
|
+
out = this.parsePodAffinityTerm(out);
|
|
76
111
|
}
|
|
77
112
|
|
|
78
113
|
return out;
|
|
@@ -82,10 +117,17 @@ export default {
|
|
|
82
117
|
|
|
83
118
|
return {
|
|
84
119
|
allSelectorTerms,
|
|
85
|
-
defaultWeight:
|
|
120
|
+
defaultWeight: 1,
|
|
86
121
|
// rules in MatchExpressions.vue can not catch changes what happens on parent component
|
|
87
122
|
// we need re-render it via key changing
|
|
88
|
-
rerenderNums:
|
|
123
|
+
rerenderNums: randomStr(4),
|
|
124
|
+
NAMESPACE_SELECTION_OPTION_VALUES,
|
|
125
|
+
defaultAddValue: {
|
|
126
|
+
_namespaceOption: NAMESPACE_SELECTION_OPTION_VALUES.POD,
|
|
127
|
+
matchExpressions: [],
|
|
128
|
+
namespaces: null,
|
|
129
|
+
_namespaces: null,
|
|
130
|
+
}
|
|
89
131
|
};
|
|
90
132
|
},
|
|
91
133
|
computed: {
|
|
@@ -101,9 +143,14 @@ export default {
|
|
|
101
143
|
return NODE;
|
|
102
144
|
},
|
|
103
145
|
|
|
104
|
-
|
|
146
|
+
labeledInputNamespaceLabel() {
|
|
147
|
+
return this.removeLabeledInputNamespaceLabel ? '' : this.t('workload.scheduling.affinity.matchExpressions.inNamespaces');
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
allNamespacesOptions() {
|
|
105
151
|
const inStore = this.$store.getters['currentStore'](NAMESPACE);
|
|
106
152
|
const choices = this.namespaces || this.$store.getters[`${ inStore }/all`](NAMESPACE);
|
|
153
|
+
|
|
107
154
|
const out = sortBy(choices.map((obj) => {
|
|
108
155
|
return {
|
|
109
156
|
label: obj.nameDisplay,
|
|
@@ -122,8 +169,38 @@ export default {
|
|
|
122
169
|
return this.nodes.length;
|
|
123
170
|
},
|
|
124
171
|
|
|
172
|
+
namespaceSelectionOptions() {
|
|
173
|
+
if (this.allNamespacesOptionAvailable) {
|
|
174
|
+
return [
|
|
175
|
+
NAMESPACE_SELECTION_OPTION_VALUES.POD,
|
|
176
|
+
NAMESPACE_SELECTION_OPTION_VALUES.ALL,
|
|
177
|
+
NAMESPACE_SELECTION_OPTION_VALUES.SELECTED
|
|
178
|
+
];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return [
|
|
182
|
+
NAMESPACE_SELECTION_OPTION_VALUES.POD,
|
|
183
|
+
NAMESPACE_SELECTION_OPTION_VALUES.SELECTED
|
|
184
|
+
];
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
namespaceSelectionLabels() {
|
|
188
|
+
if (this.allNamespacesOptionAvailable) {
|
|
189
|
+
return [
|
|
190
|
+
this.t('workload.scheduling.affinity.thisPodNamespace'),
|
|
191
|
+
this.t('workload.scheduling.affinity.allNamespaces'),
|
|
192
|
+
this.t('workload.scheduling.affinity.matchExpressions.inNamespaces')
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return [
|
|
197
|
+
this.t('workload.scheduling.affinity.thisPodNamespace'),
|
|
198
|
+
this.t('workload.scheduling.affinity.matchExpressions.inNamespaces')
|
|
199
|
+
];
|
|
200
|
+
},
|
|
201
|
+
|
|
125
202
|
hasNamespaces() {
|
|
126
|
-
return this.
|
|
203
|
+
return this.allNamespacesOptions.length;
|
|
127
204
|
},
|
|
128
205
|
},
|
|
129
206
|
|
|
@@ -132,6 +209,20 @@ export default {
|
|
|
132
209
|
},
|
|
133
210
|
|
|
134
211
|
methods: {
|
|
212
|
+
parsePodAffinityTerm(out) {
|
|
213
|
+
if (out.namespaceSelector && typeof out.namespaceSelector === 'object' && !Object.keys(out.namespaceSelector).length && this.allNamespacesOptionAvailable) {
|
|
214
|
+
out._namespaceOption = NAMESPACE_SELECTION_OPTION_VALUES.ALL;
|
|
215
|
+
} else if (out.namespaces?.length) {
|
|
216
|
+
out._namespaceOption = NAMESPACE_SELECTION_OPTION_VALUES.SELECTED;
|
|
217
|
+
} else {
|
|
218
|
+
out._namespaceOption = NAMESPACE_SELECTION_OPTION_VALUES.POD;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
out._namespaces = (out.namespaces || []).toString();
|
|
222
|
+
|
|
223
|
+
return out;
|
|
224
|
+
},
|
|
225
|
+
|
|
135
226
|
update() {
|
|
136
227
|
const podAffinity = { requiredDuringSchedulingIgnoredDuringExecution: [], preferredDuringSchedulingIgnoredDuringExecution: [] };
|
|
137
228
|
const podAntiAffinity = { requiredDuringSchedulingIgnoredDuringExecution: [], preferredDuringSchedulingIgnoredDuringExecution: [] };
|
|
@@ -154,7 +245,7 @@ export default {
|
|
|
154
245
|
}
|
|
155
246
|
});
|
|
156
247
|
|
|
157
|
-
Object.assign(this.value.
|
|
248
|
+
Object.assign(this.value[this.field], { podAffinity, podAntiAffinity });
|
|
158
249
|
this.$emit('update', this.value);
|
|
159
250
|
},
|
|
160
251
|
|
|
@@ -163,17 +254,6 @@ export default {
|
|
|
163
254
|
this.queueUpdate();
|
|
164
255
|
},
|
|
165
256
|
|
|
166
|
-
addSelector() {
|
|
167
|
-
const neu = {
|
|
168
|
-
namespaces: null,
|
|
169
|
-
labelSelector: { matchExpressions: [] },
|
|
170
|
-
topologyKey: '',
|
|
171
|
-
_id: randomStr(4)
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
this.allSelectorTerms.push(neu);
|
|
175
|
-
},
|
|
176
|
-
|
|
177
257
|
changePriority(term, idx) {
|
|
178
258
|
if (term.weight) {
|
|
179
259
|
delete term.weight;
|
|
@@ -189,14 +269,41 @@ export default {
|
|
|
189
269
|
return term.weight ? this.t('workload.scheduling.affinity.preferred') : this.t('workload.scheduling.affinity.required');
|
|
190
270
|
},
|
|
191
271
|
|
|
192
|
-
changeNamespaceMode(term, idx) {
|
|
193
|
-
|
|
272
|
+
changeNamespaceMode(val, term, idx) {
|
|
273
|
+
this.$set(term, '_namespaceOption', val);
|
|
274
|
+
|
|
275
|
+
switch (val) {
|
|
276
|
+
case NAMESPACE_SELECTION_OPTION_VALUES.POD:
|
|
194
277
|
term.namespaces = null;
|
|
195
278
|
term._namespaces = null;
|
|
196
|
-
|
|
279
|
+
|
|
280
|
+
if (term.namespaceSelector || term.namespaceSelector === null) {
|
|
281
|
+
delete term.namespaceSelector;
|
|
282
|
+
}
|
|
283
|
+
break;
|
|
284
|
+
case NAMESPACE_SELECTION_OPTION_VALUES.ALL:
|
|
285
|
+
term.namespaceSelector = {};
|
|
286
|
+
|
|
287
|
+
if (term.namespaces || term.namespaces === null) {
|
|
288
|
+
delete term.namespaces;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (term._namespaces || term._namespaces === null) {
|
|
292
|
+
delete term._namespaces;
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
|
|
296
|
+
default:
|
|
197
297
|
this.$set(term, 'namespaces', []);
|
|
198
298
|
this.$set(term, '_namespaces', '');
|
|
299
|
+
|
|
300
|
+
if (term.namespaceSelector || term.namespaceSelector === null) {
|
|
301
|
+
delete term.namespaceSelector;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
break;
|
|
199
305
|
}
|
|
306
|
+
|
|
200
307
|
this.$set(this.allSelectorTerms, idx, term);
|
|
201
308
|
this.queueUpdate();
|
|
202
309
|
},
|
|
@@ -205,7 +312,7 @@ export default {
|
|
|
205
312
|
let nsArray = namespaces;
|
|
206
313
|
|
|
207
314
|
// namespaces would be String if there is no namespace
|
|
208
|
-
if (
|
|
315
|
+
if (typeof namespaces === 'string') {
|
|
209
316
|
nsArray = namespaces.split(',').map(ns => ns.trim()).filter(ns => ns?.length);
|
|
210
317
|
}
|
|
211
318
|
|
|
@@ -231,9 +338,9 @@ export default {
|
|
|
231
338
|
<ArrayListGrouped
|
|
232
339
|
v-model="allSelectorTerms"
|
|
233
340
|
class="mt-20"
|
|
234
|
-
:default-add-value="
|
|
341
|
+
:default-add-value="defaultAddValue"
|
|
235
342
|
:mode="mode"
|
|
236
|
-
:add-label="t('
|
|
343
|
+
:add-label="t('podAffinity.addLabel')"
|
|
237
344
|
@remove="remove"
|
|
238
345
|
>
|
|
239
346
|
<template #default="props">
|
|
@@ -244,6 +351,7 @@ export default {
|
|
|
244
351
|
:options="[t('workload.scheduling.affinity.affinityOption'),t('workload.scheduling.affinity.antiAffinityOption')]"
|
|
245
352
|
:value="props.row.value._anti ?t('workload.scheduling.affinity.antiAffinityOption') :t('workload.scheduling.affinity.affinityOption') "
|
|
246
353
|
:label="t('workload.scheduling.affinity.type')"
|
|
354
|
+
:data-testid="`pod-affinity-type-index${props.i}`"
|
|
247
355
|
@input="$set(props.row.value, '_anti',!props.row.value._anti)"
|
|
248
356
|
/>
|
|
249
357
|
</div>
|
|
@@ -254,41 +362,44 @@ export default {
|
|
|
254
362
|
:options="[t('workload.scheduling.affinity.preferred'),t('workload.scheduling.affinity.required')]"
|
|
255
363
|
:value="priorityDisplay(props.row.value)"
|
|
256
364
|
:label="t('workload.scheduling.affinity.priority')"
|
|
365
|
+
:data-testid="`pod-affinity-priority-index${props.i}`"
|
|
257
366
|
@input="changePriority(props.row.value, props.i)"
|
|
258
367
|
/>
|
|
259
368
|
</div>
|
|
260
369
|
</div>
|
|
261
370
|
<div class="row">
|
|
262
371
|
<RadioGroup
|
|
263
|
-
:options="
|
|
264
|
-
:labels="
|
|
372
|
+
:options="namespaceSelectionOptions"
|
|
373
|
+
:labels="namespaceSelectionLabels"
|
|
265
374
|
:name="`namespaces-${props.row.value._id}`"
|
|
266
375
|
:mode="mode"
|
|
267
|
-
:value="
|
|
268
|
-
|
|
376
|
+
:value="props.row.value._namespaceOption"
|
|
377
|
+
:data-testid="`pod-affinity-namespacetype-index${props.i}`"
|
|
378
|
+
@input="changeNamespaceMode($event, props.row.value, props.i)"
|
|
269
379
|
/>
|
|
270
380
|
</div>
|
|
271
|
-
<div class="spacer" />
|
|
272
381
|
<div
|
|
273
|
-
v-if="
|
|
274
|
-
class="row mb-20"
|
|
382
|
+
v-if="props.row.value._namespaceOption === NAMESPACE_SELECTION_OPTION_VALUES.SELECTED"
|
|
383
|
+
class="row mt-10 mb-20"
|
|
275
384
|
>
|
|
276
385
|
<LabeledSelect
|
|
277
|
-
v-if="hasNamespaces"
|
|
386
|
+
v-if="hasNamespaces && !forceInputNamespaceSelection"
|
|
278
387
|
v-model="props.row.value.namespaces"
|
|
279
388
|
:mode="mode"
|
|
280
389
|
:multiple="true"
|
|
281
390
|
:taggable="true"
|
|
282
|
-
:options="
|
|
391
|
+
:options="allNamespacesOptions"
|
|
283
392
|
:label="t('workload.scheduling.affinity.matchExpressions.inNamespaces')"
|
|
393
|
+
:data-testid="`pod-affinity-namespace-select-index${props.i}`"
|
|
284
394
|
@input="updateNamespaces(props.row.value, props.row.value.namespaces)"
|
|
285
395
|
/>
|
|
286
396
|
<LabeledInput
|
|
287
397
|
v-else
|
|
288
398
|
v-model="props.row.value._namespaces"
|
|
289
399
|
:mode="mode"
|
|
290
|
-
:label="
|
|
400
|
+
:label="labeledInputNamespaceLabel"
|
|
291
401
|
:placeholder="t('cluster.credential.harvester.affinity.namespaces.placeholder')"
|
|
402
|
+
:data-testid="`pod-affinity-namespace-input-index${props.i}`"
|
|
292
403
|
@input="updateNamespaces(props.row.value, props.row.value._namespaces)"
|
|
293
404
|
/>
|
|
294
405
|
</div>
|
|
@@ -299,11 +410,11 @@ export default {
|
|
|
299
410
|
:type="pod"
|
|
300
411
|
:value="get(props.row.value, 'labelSelector.matchExpressions')"
|
|
301
412
|
:show-remove="false"
|
|
413
|
+
:data-testid="`pod-affinity-expressions-index${props.i}`"
|
|
302
414
|
@input="e=>set(props.row.value, 'labelSelector.matchExpressions', e)"
|
|
303
415
|
/>
|
|
304
|
-
<div class="
|
|
305
|
-
|
|
306
|
-
<div class="col span-12">
|
|
416
|
+
<div class="row mt-20">
|
|
417
|
+
<div class="col span-9">
|
|
307
418
|
<LabeledSelect
|
|
308
419
|
v-if="hasNodes"
|
|
309
420
|
v-model="props.row.value.topologyKey"
|
|
@@ -317,6 +428,7 @@ export default {
|
|
|
317
428
|
:options="existingNodeLabels"
|
|
318
429
|
:disabled="mode==='view'"
|
|
319
430
|
:loading="loading"
|
|
431
|
+
:data-testid="`pod-affinity-topology-select-index${props.i}`"
|
|
320
432
|
@input="update"
|
|
321
433
|
/>
|
|
322
434
|
<LabeledInput
|
|
@@ -326,14 +438,14 @@ export default {
|
|
|
326
438
|
:label="t('workload.scheduling.affinity.topologyKey.label')"
|
|
327
439
|
:placeholder="t('workload.scheduling.affinity.topologyKey.placeholder')"
|
|
328
440
|
required
|
|
441
|
+
:data-testid="`pod-affinity-topology-input-index${props.i}`"
|
|
329
442
|
@input="update"
|
|
330
443
|
/>
|
|
331
444
|
</div>
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
<div class="col span-6">
|
|
445
|
+
<div
|
|
446
|
+
v-if="props.row.value.weight"
|
|
447
|
+
class="col span-3"
|
|
448
|
+
>
|
|
337
449
|
<LabeledInput
|
|
338
450
|
v-model.number="props.row.value.weight"
|
|
339
451
|
:mode="mode"
|
|
@@ -342,6 +454,7 @@ export default {
|
|
|
342
454
|
max="100"
|
|
343
455
|
:label="t('workload.scheduling.affinity.weight.label')"
|
|
344
456
|
:placeholder="t('workload.scheduling.affinity.weight.placeholder')"
|
|
457
|
+
:data-testid="`pod-affinity-weight-index${props.i}`"
|
|
345
458
|
/>
|
|
346
459
|
</div>
|
|
347
460
|
</div>
|
|
@@ -7,6 +7,7 @@ import { Card } from '@components/Card';
|
|
|
7
7
|
import { RadioGroup } from '@components/Form/Radio';
|
|
8
8
|
import { Checkbox } from '@components/Form/Checkbox';
|
|
9
9
|
import { DESCRIPTION } from '@shell/config/labels-annotations';
|
|
10
|
+
import DOMPurify from 'dompurify';
|
|
10
11
|
|
|
11
12
|
export default {
|
|
12
13
|
components: {
|
|
@@ -160,9 +161,9 @@ export default {
|
|
|
160
161
|
|
|
161
162
|
options() {
|
|
162
163
|
const customRoles = this.customRoles.map(role => ({
|
|
163
|
-
label: role.nameDisplay,
|
|
164
|
-
description: role.description || role.metadata?.annotations?.[DESCRIPTION] || this.t('projectMembers.projectPermissions.noDescription'),
|
|
165
|
-
value: role.id
|
|
164
|
+
label: this.purifyOption(role.nameDisplay),
|
|
165
|
+
description: this.purifyOption(role.description || role.metadata?.annotations?.[DESCRIPTION] || this.t('projectMembers.projectPermissions.noDescription')),
|
|
166
|
+
value: this.purifyOption(role.id),
|
|
166
167
|
}));
|
|
167
168
|
|
|
168
169
|
return [
|
|
@@ -245,6 +246,9 @@ export default {
|
|
|
245
246
|
}
|
|
246
247
|
|
|
247
248
|
return [permissionGroup];
|
|
249
|
+
},
|
|
250
|
+
purifyOption(option) {
|
|
251
|
+
return DOMPurify.sanitize(option, { ALLOWED_TAGS: ['span'] });
|
|
248
252
|
}
|
|
249
253
|
}
|
|
250
254
|
};
|
|
@@ -301,7 +305,7 @@ export default {
|
|
|
301
305
|
/>
|
|
302
306
|
<i
|
|
303
307
|
v-if="permission.locked"
|
|
304
|
-
v-tooltip="permission.tooltip"
|
|
308
|
+
v-clean-tooltip="permission.tooltip"
|
|
305
309
|
class="icon icon-lock icon-fw"
|
|
306
310
|
/>
|
|
307
311
|
</div>
|
|
@@ -137,7 +137,7 @@ export default {
|
|
|
137
137
|
<span class="port">
|
|
138
138
|
<t k="servicePorts.rules.listening.label" />
|
|
139
139
|
<i
|
|
140
|
-
v-tooltip="t('servicesPage.listeningPorts')"
|
|
140
|
+
v-clean-tooltip="t('servicesPage.listeningPorts')"
|
|
141
141
|
class="icon icon-info flex"
|
|
142
142
|
/>
|
|
143
143
|
<span class="text-error">*</span>
|
|
@@ -151,7 +151,7 @@ export default {
|
|
|
151
151
|
<span class="target-port">
|
|
152
152
|
<t k="servicePorts.rules.target.label" />
|
|
153
153
|
<i
|
|
154
|
-
v-tooltip="t('servicesPage.targetPorts')"
|
|
154
|
+
v-clean-tooltip="t('servicesPage.targetPorts')"
|
|
155
155
|
class="icon icon-info flex"
|
|
156
156
|
/>
|
|
157
157
|
<span class="text-error">*</span>
|
|
@@ -27,7 +27,27 @@ export default {
|
|
|
27
27
|
},
|
|
28
28
|
|
|
29
29
|
data() {
|
|
30
|
-
|
|
30
|
+
const rules = [];
|
|
31
|
+
|
|
32
|
+
// on creation in agent configuration, the backend "eats"
|
|
33
|
+
// the empty "effect" string, which doesn't happen on edit
|
|
34
|
+
// just to make sure we populate it correcty, let's consider
|
|
35
|
+
// no prop "effect" as an empty string which means all
|
|
36
|
+
// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#toleration-v1-core
|
|
37
|
+
if (this.value.length) {
|
|
38
|
+
this.value.forEach((v) => {
|
|
39
|
+
if (!Object.keys(v).includes('effect')) {
|
|
40
|
+
rules.push({
|
|
41
|
+
...v,
|
|
42
|
+
effect: ''
|
|
43
|
+
});
|
|
44
|
+
} else {
|
|
45
|
+
rules.push(v);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { rules };
|
|
31
51
|
},
|
|
32
52
|
|
|
33
53
|
computed: {
|
|
@@ -83,7 +103,7 @@ export default {
|
|
|
83
103
|
return [
|
|
84
104
|
{
|
|
85
105
|
label: this.t('workload.scheduling.tolerations.effectOptions.all'),
|
|
86
|
-
value: '
|
|
106
|
+
value: ''
|
|
87
107
|
},
|
|
88
108
|
{
|
|
89
109
|
label: this.t('workload.scheduling.tolerations.effectOptions.noSchedule'),
|
|
@@ -115,17 +135,43 @@ export default {
|
|
|
115
135
|
},
|
|
116
136
|
|
|
117
137
|
update() {
|
|
118
|
-
|
|
138
|
+
// let's delete the vKey prop as it's only poluting the data
|
|
139
|
+
const rules = this.rules.map((rule) => {
|
|
140
|
+
const newRule = { ...rule };
|
|
141
|
+
|
|
142
|
+
// prevent vKey from being sent as data
|
|
143
|
+
if (newRule.vKey) {
|
|
144
|
+
delete newRule.vKey;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// let's clear the value field if operator is Exists
|
|
148
|
+
if (newRule.operator === 'Exists' && newRule.value) {
|
|
149
|
+
newRule.value = null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// remove effect from payload sent upstream, as it's empty
|
|
153
|
+
// it should be null, but the Select input doesn't seem to like it
|
|
154
|
+
// so we keep it as '' and sanitize it here
|
|
155
|
+
if (newRule.effect === '') {
|
|
156
|
+
delete newRule.effect;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return newRule;
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
this.$emit('input', rules);
|
|
119
163
|
},
|
|
120
164
|
|
|
121
165
|
addToleration() {
|
|
122
|
-
this.rules.push({ vKey: random32() });
|
|
166
|
+
this.rules.push({ vKey: random32(), effect: '' });
|
|
123
167
|
},
|
|
124
168
|
|
|
125
169
|
updateEffect(neu, rule) {
|
|
126
170
|
if (neu !== 'NoExecute' && rule.tolerationSeconds) {
|
|
127
171
|
delete rule.tolerationSeconds;
|
|
128
172
|
}
|
|
173
|
+
|
|
174
|
+
this.update();
|
|
129
175
|
}
|
|
130
176
|
}
|
|
131
177
|
|
|
@@ -146,7 +192,7 @@ export default {
|
|
|
146
192
|
<span />
|
|
147
193
|
</div>
|
|
148
194
|
<div
|
|
149
|
-
v-for="rule in rules"
|
|
195
|
+
v-for="(rule, index) in rules"
|
|
150
196
|
:key="rule.vKey"
|
|
151
197
|
class="rule"
|
|
152
198
|
>
|
|
@@ -154,6 +200,9 @@ export default {
|
|
|
154
200
|
<LabeledInput
|
|
155
201
|
v-model="rule.key"
|
|
156
202
|
:mode="mode"
|
|
203
|
+
:data-testid="`toleration-key-index${ index }`"
|
|
204
|
+
class="height-adjust-input"
|
|
205
|
+
@input="update"
|
|
157
206
|
/>
|
|
158
207
|
</div>
|
|
159
208
|
<div class="col">
|
|
@@ -162,6 +211,7 @@ export default {
|
|
|
162
211
|
v-model="rule.operator"
|
|
163
212
|
:options="operatorOpts"
|
|
164
213
|
:mode="mode"
|
|
214
|
+
:data-testid="`toleration-operator-index${ index }`"
|
|
165
215
|
@input="update"
|
|
166
216
|
/>
|
|
167
217
|
</div>
|
|
@@ -171,6 +221,7 @@ export default {
|
|
|
171
221
|
value="n/a"
|
|
172
222
|
:mode="mode"
|
|
173
223
|
disabled
|
|
224
|
+
class="height-adjust-input"
|
|
174
225
|
/>
|
|
175
226
|
</div>
|
|
176
227
|
</template>
|
|
@@ -179,6 +230,9 @@ export default {
|
|
|
179
230
|
<LabeledInput
|
|
180
231
|
v-model="rule.value"
|
|
181
232
|
:mode="mode"
|
|
233
|
+
:data-testid="`toleration-value-index${ index }`"
|
|
234
|
+
class="height-adjust-input"
|
|
235
|
+
@input="update"
|
|
182
236
|
/>
|
|
183
237
|
</div>
|
|
184
238
|
</template>
|
|
@@ -187,6 +241,7 @@ export default {
|
|
|
187
241
|
v-model="rule.effect"
|
|
188
242
|
:options="effectOpts"
|
|
189
243
|
:mode="mode"
|
|
244
|
+
:data-testid="`toleration-effect-index${ index }`"
|
|
190
245
|
@input="e=>updateEffect(e, rule)"
|
|
191
246
|
/>
|
|
192
247
|
</div>
|
|
@@ -196,6 +251,9 @@ export default {
|
|
|
196
251
|
:disabled="rule.effect !== 'NoExecute'"
|
|
197
252
|
:mode="mode"
|
|
198
253
|
suffix="Seconds"
|
|
254
|
+
:data-testid="`toleration-seconds-index${ index }`"
|
|
255
|
+
class="height-adjust-input"
|
|
256
|
+
@input="update"
|
|
199
257
|
/>
|
|
200
258
|
</div>
|
|
201
259
|
<div class="col remove">
|
|
@@ -204,6 +262,7 @@ export default {
|
|
|
204
262
|
type="button"
|
|
205
263
|
class="btn role-link"
|
|
206
264
|
:disabled="mode==='view'"
|
|
265
|
+
:data-testid="`toleration-remove-index${ index }`"
|
|
207
266
|
@click="remove(rule)"
|
|
208
267
|
>
|
|
209
268
|
<t k="generic.remove" />
|
|
@@ -214,6 +273,7 @@ export default {
|
|
|
214
273
|
v-if="!isView"
|
|
215
274
|
type="button"
|
|
216
275
|
class="btn role-tertiary"
|
|
276
|
+
data-testid="add-toleration-btn"
|
|
217
277
|
@click="addToleration"
|
|
218
278
|
>
|
|
219
279
|
<t k="workload.scheduling.tolerations.addToleration" />
|
|
@@ -228,8 +288,8 @@ export default {
|
|
|
228
288
|
|
|
229
289
|
.rule, .toleration-headers{
|
|
230
290
|
display: grid;
|
|
231
|
-
grid-template-columns: 20% 10% 20% 15% 20%
|
|
232
|
-
grid-gap:
|
|
291
|
+
grid-template-columns: 20% 10% 20% 15% 20% 15%;
|
|
292
|
+
grid-gap: 10px;
|
|
233
293
|
align-items: center;
|
|
234
294
|
}
|
|
235
295
|
|
|
@@ -247,4 +307,7 @@ export default {
|
|
|
247
307
|
.remove BUTTON {
|
|
248
308
|
padding: 0px;
|
|
249
309
|
}
|
|
310
|
+
.height-adjust-input {
|
|
311
|
+
min-height: 42px;
|
|
312
|
+
}
|
|
250
313
|
</style>
|
|
@@ -50,6 +50,8 @@ export default {
|
|
|
50
50
|
row._showHost = true;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
row._ipam = '';
|
|
54
|
+
|
|
53
55
|
return row;
|
|
54
56
|
});
|
|
55
57
|
|
|
@@ -387,7 +389,6 @@ export default {
|
|
|
387
389
|
:mode="mode"
|
|
388
390
|
:options="ipamOptions"
|
|
389
391
|
:label="t('servicesPage.harvester.ipam.label')"
|
|
390
|
-
:disabled="mode === 'edit'"
|
|
391
392
|
@input="queueUpdate"
|
|
392
393
|
/>
|
|
393
394
|
</div>
|