@rancher/shell 0.3.8 → 0.3.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/translations/en-us.yaml +28 -2
- package/babel.config.js +17 -4
- package/components/CodeMirror.vue +146 -14
- package/components/ContainerResourceLimit.vue +14 -1
- package/components/CruResource.vue +21 -5
- package/components/ExplorerProjectsNamespaces.vue +5 -1
- package/components/GroupPanel.vue +57 -0
- package/components/YamlEditor.vue +2 -2
- package/components/form/ArrayList.vue +1 -1
- package/components/form/KeyValue.vue +34 -1
- package/components/form/MatchExpressions.vue +120 -21
- package/components/form/NodeAffinity.vue +54 -4
- package/components/form/PodAffinity.vue +160 -47
- package/components/form/Tolerations.vue +40 -4
- package/components/form/__tests__/ArrayList.test.ts +3 -3
- package/components/form/__tests__/MatchExpressions.test.ts +1 -1
- package/components/nav/Header.vue +2 -0
- package/config/settings.ts +6 -1
- package/core/plugins-loader.js +0 -2
- package/edit/configmap.vue +33 -6
- package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +326 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +60 -0
- package/mixins/chart.js +1 -1
- package/models/batch.cronjob.js +18 -3
- package/models/workload.js +1 -1
- package/package.json +2 -3
- package/pages/auth/login.vue +1 -0
- package/pages/prefs.vue +18 -2
- package/pkg/vue.config.js +0 -1
- package/plugins/codemirror.js +158 -0
- package/public/index.html +1 -1
- package/types/shell/index.d.ts +20 -1
- package/utils/create-yaml.js +105 -8
- package/utils/settings.ts +12 -0
- package/vue.config.js +2 -2
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Banner } from '@components/Banner';
|
|
3
|
+
import GroupPanel from '@shell/components/GroupPanel';
|
|
4
|
+
import PodAffinity from '@shell/components/form/PodAffinity';
|
|
5
|
+
import NodeAffinity from '@shell/components/form/NodeAffinity';
|
|
6
|
+
import ContainerResourceLimit from '@shell/components/ContainerResourceLimit';
|
|
7
|
+
import Tolerations from '@shell/components/form/Tolerations';
|
|
8
|
+
import { cleanUp } from '@shell/utils/object';
|
|
9
|
+
import { fetchSetting } from '@shell/utils/settings';
|
|
10
|
+
import { RadioGroup } from '@components/Form/Radio';
|
|
11
|
+
|
|
12
|
+
export function cleanAgentConfiguration(model, key) {
|
|
13
|
+
if (!model || !model[key]) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const v = model[key];
|
|
18
|
+
|
|
19
|
+
if (Array.isArray(v) && v.length === 0) {
|
|
20
|
+
delete model[key];
|
|
21
|
+
} else if (v && typeof v === 'object') {
|
|
22
|
+
Object.keys(v).forEach((k) => {
|
|
23
|
+
// delete these auxiliary props used in podAffinity and nodeAffinity that shouldn't be sent to the server
|
|
24
|
+
if (k === '_namespaceOption' || k === '_namespaces' || k === '_anti' || k === '_id') {
|
|
25
|
+
delete v[k];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// prevent cleanup of namespaceSelector when an empty object because it represents all namespaces in pod/node affinity
|
|
29
|
+
// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#podaffinityterm-v1-core
|
|
30
|
+
if (k !== 'namespaceSelector') {
|
|
31
|
+
cleanAgentConfiguration(v, k);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (Object.keys(v).length === 0) {
|
|
36
|
+
delete model[key];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Affinity radio button choices
|
|
42
|
+
const DEFAULT = 'default';
|
|
43
|
+
const CUSTOM = 'custom';
|
|
44
|
+
|
|
45
|
+
// This is the form for Agent Configuration
|
|
46
|
+
// Used for both Cluster Agent and Fleet Agent configuration
|
|
47
|
+
export default {
|
|
48
|
+
components: {
|
|
49
|
+
Banner,
|
|
50
|
+
ContainerResourceLimit,
|
|
51
|
+
GroupPanel,
|
|
52
|
+
PodAffinity,
|
|
53
|
+
NodeAffinity,
|
|
54
|
+
RadioGroup,
|
|
55
|
+
Tolerations,
|
|
56
|
+
},
|
|
57
|
+
props: {
|
|
58
|
+
value: {
|
|
59
|
+
type: Object,
|
|
60
|
+
default: () => {},
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
mode: {
|
|
64
|
+
type: String,
|
|
65
|
+
required: true,
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
type: {
|
|
69
|
+
type: String,
|
|
70
|
+
required: true,
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
async fetch() {
|
|
75
|
+
// Default affinity
|
|
76
|
+
const settingId = `${ this.type }-agent-default-affinity`;
|
|
77
|
+
const setting = await fetchSetting(this.$store, settingId);
|
|
78
|
+
|
|
79
|
+
if (setting) {
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(setting.value || setting.default);
|
|
82
|
+
|
|
83
|
+
this.defaultAffinity = parsed || {};
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.error('Could not parse agent default setting', e); // eslint-disable-line no-console
|
|
86
|
+
this.defaultAffinity = {};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
data() {
|
|
92
|
+
const nodeAffinity = this.value?.overrideAffinity?.nodeAffinity;
|
|
93
|
+
const podAffinity = this.value?.overrideAffinity?.podAffinity;
|
|
94
|
+
const podAntiAffinity = this.value?.overrideAffinity?.podAntiAffinity;
|
|
95
|
+
|
|
96
|
+
let hasAffinityPopulated = false;
|
|
97
|
+
|
|
98
|
+
if ((nodeAffinity && Object.keys(nodeAffinity).length) ||
|
|
99
|
+
(podAffinity && Object.keys(podAffinity).length) ||
|
|
100
|
+
(podAntiAffinity && Object.keys(podAntiAffinity).length)) {
|
|
101
|
+
hasAffinityPopulated = true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
defaultAffinity: {},
|
|
106
|
+
affinitySetting: hasAffinityPopulated ? CUSTOM : DEFAULT,
|
|
107
|
+
nodeAffinity: {}
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
created() {
|
|
112
|
+
this.ensureValue();
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
computed: {
|
|
116
|
+
flatResources: {
|
|
117
|
+
get() {
|
|
118
|
+
const { limits = {}, requests = {} } = this.value.overrideResourceRequirements || {};
|
|
119
|
+
const {
|
|
120
|
+
cpu: limitsCpu,
|
|
121
|
+
memory: limitsMemory,
|
|
122
|
+
} = limits;
|
|
123
|
+
const { cpu: requestsCpu, memory: requestsMemory } = requests;
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
limitsCpu,
|
|
127
|
+
limitsMemory,
|
|
128
|
+
requestsCpu,
|
|
129
|
+
requestsMemory,
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
set(neu) {
|
|
133
|
+
const {
|
|
134
|
+
limitsCpu,
|
|
135
|
+
limitsMemory,
|
|
136
|
+
requestsCpu,
|
|
137
|
+
requestsMemory,
|
|
138
|
+
} = neu;
|
|
139
|
+
|
|
140
|
+
const existing = this.value?.overrideResourceRequirements || {};
|
|
141
|
+
|
|
142
|
+
delete existing.requests;
|
|
143
|
+
delete existing.limits;
|
|
144
|
+
|
|
145
|
+
const out = {
|
|
146
|
+
...existing,
|
|
147
|
+
requests: {
|
|
148
|
+
cpu: requestsCpu,
|
|
149
|
+
memory: requestsMemory,
|
|
150
|
+
},
|
|
151
|
+
limits: {
|
|
152
|
+
cpu: limitsCpu,
|
|
153
|
+
memory: limitsMemory,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
this.$set(this.value, 'overrideResourceRequirements', cleanUp(out));
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
affinityOptions() {
|
|
162
|
+
return [{
|
|
163
|
+
label: this.t('cluster.agentConfig.affinity.default'),
|
|
164
|
+
value: DEFAULT,
|
|
165
|
+
}, {
|
|
166
|
+
label: this.t('cluster.agentConfig.affinity.custom'),
|
|
167
|
+
value: CUSTOM,
|
|
168
|
+
}];
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
canEditAffinity() {
|
|
172
|
+
return this.affinitySetting === CUSTOM;
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
watch: {
|
|
177
|
+
value() {
|
|
178
|
+
this.ensureValue();
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
methods: {
|
|
183
|
+
ensureValue() {
|
|
184
|
+
// Ensure we have the model structure needed for the form controls
|
|
185
|
+
if (this.value) {
|
|
186
|
+
this.value.overrideAffinity = this.value.overrideAffinity || {};
|
|
187
|
+
this.value.appendTolerations = this.value.appendTolerations || [];
|
|
188
|
+
this.value.overrideResourceRequirements = this.value.overrideResourceRequirements || {};
|
|
189
|
+
|
|
190
|
+
this.nodeAffinity = this.value?.overrideAffinity?.nodeAffinity || {};
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
affinitySettingChange() {
|
|
195
|
+
if (this.affinitySetting === CUSTOM) {
|
|
196
|
+
const parsedDefaultAffinites = JSON.parse(JSON.stringify(this.defaultAffinity));
|
|
197
|
+
|
|
198
|
+
// Copy the default so that the user can edit it
|
|
199
|
+
// this will cover the pod affinities
|
|
200
|
+
this.$set(this.value, 'overrideAffinity', parsedDefaultAffinites);
|
|
201
|
+
|
|
202
|
+
// in order not to break the node affinity component, let's go for a slightly different way of handling the logic here
|
|
203
|
+
if (parsedDefaultAffinites.nodeAffinity) {
|
|
204
|
+
this.nodeAffinity = parsedDefaultAffinites.nodeAffinity;
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
this.$set(this.value, 'overrideAffinity', {});
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
updateNodeAffinity(val) {
|
|
211
|
+
this.$set(this.value.overrideAffinity, 'nodeAffinity', val);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
</script>
|
|
216
|
+
|
|
217
|
+
<template>
|
|
218
|
+
<div v-if="value && Object.keys(value).length">
|
|
219
|
+
<Banner
|
|
220
|
+
:closable="false"
|
|
221
|
+
color="info"
|
|
222
|
+
label-key="cluster.agentConfig.banners.advanced"
|
|
223
|
+
/>
|
|
224
|
+
|
|
225
|
+
<GroupPanel
|
|
226
|
+
label-key="cluster.agentConfig.groups.podRequestsAndLimits"
|
|
227
|
+
class="mt-20"
|
|
228
|
+
>
|
|
229
|
+
<Banner
|
|
230
|
+
:closable="false"
|
|
231
|
+
color="info"
|
|
232
|
+
label-key="cluster.agentConfig.banners.limits"
|
|
233
|
+
/>
|
|
234
|
+
<ContainerResourceLimit
|
|
235
|
+
v-model="flatResources"
|
|
236
|
+
:mode="mode"
|
|
237
|
+
:show-tip="false"
|
|
238
|
+
:handle-gpu-limit="false"
|
|
239
|
+
class="mt-10"
|
|
240
|
+
/>
|
|
241
|
+
</GroupPanel>
|
|
242
|
+
|
|
243
|
+
<GroupPanel
|
|
244
|
+
label-key="cluster.agentConfig.groups.podTolerations"
|
|
245
|
+
class="mt-20"
|
|
246
|
+
>
|
|
247
|
+
<Banner
|
|
248
|
+
:closable="false"
|
|
249
|
+
color="info"
|
|
250
|
+
label-key="cluster.agentConfig.banners.tolerations"
|
|
251
|
+
/>
|
|
252
|
+
<Tolerations
|
|
253
|
+
v-model="value.appendTolerations"
|
|
254
|
+
:mode="mode"
|
|
255
|
+
class="mt-10"
|
|
256
|
+
/>
|
|
257
|
+
</GroupPanel>
|
|
258
|
+
|
|
259
|
+
<GroupPanel
|
|
260
|
+
label-key="cluster.agentConfig.groups.podAffinity"
|
|
261
|
+
class="mt-20"
|
|
262
|
+
>
|
|
263
|
+
<RadioGroup
|
|
264
|
+
v-model="affinitySetting"
|
|
265
|
+
name="affinity-override"
|
|
266
|
+
:mode="mode"
|
|
267
|
+
:options="affinityOptions"
|
|
268
|
+
class="mt-10"
|
|
269
|
+
data-testid="affinity-options"
|
|
270
|
+
@input="affinitySettingChange"
|
|
271
|
+
/>
|
|
272
|
+
|
|
273
|
+
<Banner
|
|
274
|
+
v-if="canEditAffinity"
|
|
275
|
+
:closable="false"
|
|
276
|
+
color="warning"
|
|
277
|
+
>
|
|
278
|
+
<p v-clean-html="t('cluster.agentConfig.banners.windowsCompatibility', {}, true)" />
|
|
279
|
+
</Banner>
|
|
280
|
+
|
|
281
|
+
<h4 v-if="canEditAffinity">
|
|
282
|
+
{{ t('cluster.agentConfig.subGroups.podAffinityAnti') }}
|
|
283
|
+
</h4>
|
|
284
|
+
|
|
285
|
+
<PodAffinity
|
|
286
|
+
v-if="canEditAffinity"
|
|
287
|
+
v-model="value"
|
|
288
|
+
field="overrideAffinity"
|
|
289
|
+
:mode="mode"
|
|
290
|
+
class="mt-0 mb-20"
|
|
291
|
+
:all-namespaces-option-available="true"
|
|
292
|
+
:force-input-namespace-selection="true"
|
|
293
|
+
:remove-labeled-input-namespace-label="true"
|
|
294
|
+
data-testid="pod-affinity"
|
|
295
|
+
/>
|
|
296
|
+
|
|
297
|
+
<div
|
|
298
|
+
v-if="canEditAffinity"
|
|
299
|
+
class="separator"
|
|
300
|
+
/>
|
|
301
|
+
<h4
|
|
302
|
+
v-if="canEditAffinity"
|
|
303
|
+
class="mt-20"
|
|
304
|
+
>
|
|
305
|
+
{{ t('cluster.agentConfig.subGroups.nodeAffinity') }}
|
|
306
|
+
</h4>
|
|
307
|
+
|
|
308
|
+
<NodeAffinity
|
|
309
|
+
v-if="canEditAffinity"
|
|
310
|
+
v-model="nodeAffinity"
|
|
311
|
+
:matching-selector-display="true"
|
|
312
|
+
:mode="mode"
|
|
313
|
+
class="mt-0"
|
|
314
|
+
data-testid="node-affinity"
|
|
315
|
+
@input="updateNodeAffinity"
|
|
316
|
+
/>
|
|
317
|
+
</GroupPanel>
|
|
318
|
+
</div>
|
|
319
|
+
</template>
|
|
320
|
+
|
|
321
|
+
<style lang="scss" scoped>
|
|
322
|
+
.separator {
|
|
323
|
+
width: 100%;
|
|
324
|
+
border-top: 1px solid var(--border);
|
|
325
|
+
}
|
|
326
|
+
</style>
|
|
@@ -69,6 +69,7 @@ import S3Config from './S3Config';
|
|
|
69
69
|
import SelectCredential from './SelectCredential';
|
|
70
70
|
import AdvancedSection from '@shell/components/AdvancedSection.vue';
|
|
71
71
|
import { ELEMENTAL_SCHEMA_IDS, KIND, ELEMENTAL_CLUSTER_PROVIDER } from '../../config/elemental-types';
|
|
72
|
+
import AgentConfiguration, { cleanAgentConfiguration } from './AgentConfiguration';
|
|
72
73
|
|
|
73
74
|
const PUBLIC = 'public';
|
|
74
75
|
const PRIVATE = 'private';
|
|
@@ -96,6 +97,8 @@ const NODE_TOTAL = {
|
|
|
96
97
|
icon: 'icon-checkmark'
|
|
97
98
|
}
|
|
98
99
|
};
|
|
100
|
+
const CLUSTER_AGENT_CUSTOMIZATION = 'clusterAgentDeploymentCustomization';
|
|
101
|
+
const FLEET_AGENT_CUSTOMIZATION = 'fleetAgentDeploymentCustomization';
|
|
99
102
|
|
|
100
103
|
export default {
|
|
101
104
|
components: {
|
|
@@ -107,6 +110,7 @@ export default {
|
|
|
107
110
|
BadgeState,
|
|
108
111
|
Banner,
|
|
109
112
|
Checkbox,
|
|
113
|
+
AgentConfiguration,
|
|
110
114
|
ClusterMembershipEditor,
|
|
111
115
|
CruResource,
|
|
112
116
|
DrainOptions,
|
|
@@ -274,6 +278,18 @@ export default {
|
|
|
274
278
|
|
|
275
279
|
this.userChartValues[key] = value;
|
|
276
280
|
});
|
|
281
|
+
|
|
282
|
+
// Ensure we have empty models for the two agent configurations
|
|
283
|
+
|
|
284
|
+
// Cluster Agent Configuration
|
|
285
|
+
if ( !this.value.spec[CLUSTER_AGENT_CUSTOMIZATION]) {
|
|
286
|
+
set(this.value.spec, CLUSTER_AGENT_CUSTOMIZATION, {});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Fleet Agent Configuration
|
|
290
|
+
if ( !this.value.spec[FLEET_AGENT_CUSTOMIZATION] ) {
|
|
291
|
+
set(this.value.spec, FLEET_AGENT_CUSTOMIZATION, {});
|
|
292
|
+
}
|
|
277
293
|
},
|
|
278
294
|
|
|
279
295
|
data() {
|
|
@@ -1081,6 +1097,7 @@ export default {
|
|
|
1081
1097
|
created() {
|
|
1082
1098
|
this.registerBeforeHook(this.saveMachinePools, 'save-machine-pools');
|
|
1083
1099
|
this.registerBeforeHook(this.setRegistryConfig, 'set-registry-config');
|
|
1100
|
+
this.registerBeforeHook(this.agentConfigurationCleanup, 'cleanup-agent-config');
|
|
1084
1101
|
this.registerAfterHook(this.cleanupMachinePools, 'cleanup-machine-pools');
|
|
1085
1102
|
this.registerAfterHook(this.saveRoleBindings, 'save-role-bindings');
|
|
1086
1103
|
},
|
|
@@ -1089,6 +1106,12 @@ export default {
|
|
|
1089
1106
|
nlToBr,
|
|
1090
1107
|
set,
|
|
1091
1108
|
|
|
1109
|
+
agentConfigurationCleanup() {
|
|
1110
|
+
// Clean agent configuration objects, so we only send values when the user has configured something
|
|
1111
|
+
cleanAgentConfiguration(this.value.spec, CLUSTER_AGENT_CUSTOMIZATION);
|
|
1112
|
+
cleanAgentConfiguration(this.value.spec, FLEET_AGENT_CUSTOMIZATION);
|
|
1113
|
+
},
|
|
1114
|
+
|
|
1092
1115
|
/**
|
|
1093
1116
|
* set instanceNameLimit to 15 to all pool machine if truncateHostnames checkbox is clicked
|
|
1094
1117
|
*/
|
|
@@ -1467,7 +1490,20 @@ export default {
|
|
|
1467
1490
|
delete this.value.spec.rkeConfig.machineGlobalConfig.profile;
|
|
1468
1491
|
}
|
|
1469
1492
|
|
|
1493
|
+
// store the current data for fleet and cluster agent so that we can re-apply it later if the save fails
|
|
1494
|
+
// we also have a before hook (check created() hooks) where the cleanup of the data occurs
|
|
1495
|
+
const clusterAgentDeploymentCustomization = JSON.parse(JSON.stringify(this.value.spec[CLUSTER_AGENT_CUSTOMIZATION]));
|
|
1496
|
+
const fleetAgentDeploymentCustomization = JSON.parse(JSON.stringify(this.value.spec[FLEET_AGENT_CUSTOMIZATION]));
|
|
1497
|
+
|
|
1470
1498
|
await this.save(btnCb);
|
|
1499
|
+
|
|
1500
|
+
// comes from createEditView mixin
|
|
1501
|
+
// if there are any errors saving, restore the agent config data
|
|
1502
|
+
if (this.errors?.length) {
|
|
1503
|
+
// Ensure the agent configuration is set back to the values before we changed (cleaned) it
|
|
1504
|
+
set(this.value.spec, CLUSTER_AGENT_CUSTOMIZATION, clusterAgentDeploymentCustomization);
|
|
1505
|
+
set(this.value.spec, FLEET_AGENT_CUSTOMIZATION, fleetAgentDeploymentCustomization);
|
|
1506
|
+
}
|
|
1471
1507
|
},
|
|
1472
1508
|
// create a secret to reference the harvester cluster kubeconfig in rkeConfig
|
|
1473
1509
|
async createKubeconfigSecret(kubeconfig = '') {
|
|
@@ -2821,6 +2857,30 @@ export default {
|
|
|
2821
2857
|
</div>
|
|
2822
2858
|
</Tab>
|
|
2823
2859
|
|
|
2860
|
+
<!-- Cluster Agent Configuration -->
|
|
2861
|
+
<Tab
|
|
2862
|
+
name="clusteragentconfig"
|
|
2863
|
+
label-key="cluster.agentConfig.tabs.cluster"
|
|
2864
|
+
>
|
|
2865
|
+
<AgentConfiguration
|
|
2866
|
+
v-model="value.spec.clusterAgentDeploymentCustomization"
|
|
2867
|
+
type="cluster"
|
|
2868
|
+
:mode="mode"
|
|
2869
|
+
/>
|
|
2870
|
+
</Tab>
|
|
2871
|
+
|
|
2872
|
+
<!-- Fleet Agent Configuration -->
|
|
2873
|
+
<Tab
|
|
2874
|
+
name="fleetagentconfig"
|
|
2875
|
+
label-key="cluster.agentConfig.tabs.fleet"
|
|
2876
|
+
>
|
|
2877
|
+
<AgentConfiguration
|
|
2878
|
+
v-model="value.spec.fleetAgentDeploymentCustomization"
|
|
2879
|
+
type="fleet"
|
|
2880
|
+
:mode="mode"
|
|
2881
|
+
/>
|
|
2882
|
+
</Tab>
|
|
2883
|
+
|
|
2824
2884
|
<!-- Advanced -->
|
|
2825
2885
|
<Tab
|
|
2826
2886
|
v-if="haveArgInfo || agentArgs['protect-kernel-defaults']"
|
package/mixins/chart.js
CHANGED
|
@@ -14,7 +14,7 @@ import { CAPI, CATALOG } from '@shell/config/types';
|
|
|
14
14
|
import { isPrerelease } from '@shell/utils/version';
|
|
15
15
|
import difference from 'lodash/difference';
|
|
16
16
|
import { LINUX } from '@shell/store/catalog';
|
|
17
|
-
import { clone } from 'utils/object';
|
|
17
|
+
import { clone } from '@shell/utils/object';
|
|
18
18
|
import { merge } from 'lodash';
|
|
19
19
|
|
|
20
20
|
export default {
|
package/models/batch.cronjob.js
CHANGED
|
@@ -2,6 +2,7 @@ import { insertAt } from '@shell/utils/array';
|
|
|
2
2
|
import { clone } from '@shell/utils/object';
|
|
3
3
|
import { WORKLOAD_TYPES } from '@shell/config/types';
|
|
4
4
|
import Workload from './workload';
|
|
5
|
+
import { WORKLOAD_TYPE_TO_KIND_MAPPING } from '@shell/detail/workload/index';
|
|
5
6
|
|
|
6
7
|
export default class CronJob extends Workload {
|
|
7
8
|
get state() {
|
|
@@ -47,12 +48,26 @@ export default class CronJob extends Workload {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
async runNow() {
|
|
50
|
-
const
|
|
51
|
+
const ownerRef = {
|
|
52
|
+
apiVersion: this.apiVersion,
|
|
53
|
+
controller: true,
|
|
54
|
+
kind: this.kind,
|
|
55
|
+
name: this.metadata.name,
|
|
56
|
+
uid: this.metadata.uid
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Set type and kind to ensure the correct model is returned (via classify). This object will be persisted to the store
|
|
60
|
+
const job = await this.$dispatch('create', {
|
|
61
|
+
type: WORKLOAD_TYPES.JOB,
|
|
62
|
+
kind: WORKLOAD_TYPE_TO_KIND_MAPPING[WORKLOAD_TYPES.JOB],
|
|
63
|
+
...clone(this.spec.jobTemplate)
|
|
64
|
+
});
|
|
51
65
|
|
|
52
|
-
job.type = WORKLOAD_TYPES.JOB;
|
|
53
66
|
job.metadata = job.metadata || {};
|
|
54
67
|
job.metadata.namespace = this.metadata.namespace;
|
|
55
|
-
|
|
68
|
+
// Can't use `generatedName` and no `name`... as this fails schema validation
|
|
69
|
+
job.metadata.name = `${ this.metadata.name }-${ Date.now() }`;
|
|
70
|
+
job.metadata.ownerReferences = [ownerRef];
|
|
56
71
|
|
|
57
72
|
await job.save();
|
|
58
73
|
|
package/models/workload.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rancher/shell",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Rancher Dashboard Shell",
|
|
5
5
|
"repository": "https://github.com/rancherlabs/dashboard",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"dagre-d3": "0.6.4",
|
|
70
70
|
"dayjs": "1.8.29",
|
|
71
71
|
"diff2html": "2.11.2",
|
|
72
|
-
"dompurify": "2.
|
|
72
|
+
"dompurify": "2.4.5",
|
|
73
73
|
"eslint": "7.32.0",
|
|
74
74
|
"eslint-config-standard": "16.0.3",
|
|
75
75
|
"eslint-import-resolver-node": "0.3.4",
|
|
@@ -89,7 +89,6 @@
|
|
|
89
89
|
"jest": "27.5.1",
|
|
90
90
|
"jest-serializer-vue": "2.0.2",
|
|
91
91
|
"jexl": "2.2.2",
|
|
92
|
-
"jquery": "3.5.1",
|
|
93
92
|
"js-cookie": "2.2.1",
|
|
94
93
|
"js-yaml": "4.1.0",
|
|
95
94
|
"js-yaml-loader": "1.2.2",
|
package/pages/auth/login.vue
CHANGED
package/pages/prefs.vue
CHANGED
|
@@ -163,7 +163,9 @@ export default {
|
|
|
163
163
|
<h4 v-t="'prefs.language'" />
|
|
164
164
|
<div class="row">
|
|
165
165
|
<div class="col span-4">
|
|
166
|
-
<LocaleSelector
|
|
166
|
+
<LocaleSelector
|
|
167
|
+
data-testid="prefs__languageSelector"
|
|
168
|
+
/>
|
|
167
169
|
</div>
|
|
168
170
|
</div>
|
|
169
171
|
</div>
|
|
@@ -173,6 +175,7 @@ export default {
|
|
|
173
175
|
<h4 v-t="'prefs.theme.label'" />
|
|
174
176
|
<ButtonGroup
|
|
175
177
|
v-model="theme"
|
|
178
|
+
data-testid="prefs__themeOptions"
|
|
176
179
|
:options="themeOptions"
|
|
177
180
|
/>
|
|
178
181
|
<div class="mt-10">
|
|
@@ -190,7 +193,9 @@ export default {
|
|
|
190
193
|
>
|
|
191
194
|
<hr>
|
|
192
195
|
<h4 v-t="'prefs.landing.label'" />
|
|
193
|
-
<LandingPagePreference
|
|
196
|
+
<LandingPagePreference
|
|
197
|
+
data-testid="prefs__landingPagePreference"
|
|
198
|
+
/>
|
|
194
199
|
</div>
|
|
195
200
|
<!-- Display Settings -->
|
|
196
201
|
<div class="mt-10 mb-10">
|
|
@@ -203,6 +208,7 @@ export default {
|
|
|
203
208
|
<div class="col span-4">
|
|
204
209
|
<LabeledSelect
|
|
205
210
|
v-model="dateFormat"
|
|
211
|
+
data-testid="prefs__displaySetting__dateFormat"
|
|
206
212
|
:label="t('prefs.dateFormat.label')"
|
|
207
213
|
:options="dateOptions"
|
|
208
214
|
/>
|
|
@@ -210,6 +216,7 @@ export default {
|
|
|
210
216
|
<div class="col span-4">
|
|
211
217
|
<LabeledSelect
|
|
212
218
|
v-model="timeFormat"
|
|
219
|
+
data-testid="prefs__displaySetting__timeFormat"
|
|
213
220
|
:label="t('prefs.timeFormat.label')"
|
|
214
221
|
:options="timeOptions"
|
|
215
222
|
/>
|
|
@@ -220,6 +227,7 @@ export default {
|
|
|
220
227
|
<div class="col span-4">
|
|
221
228
|
<LabeledSelect
|
|
222
229
|
v-model.number="perPage"
|
|
230
|
+
data-testid="prefs__displaySetting__perPage"
|
|
223
231
|
:label="t('prefs.perPage.label')"
|
|
224
232
|
:options="perPageOptions"
|
|
225
233
|
option-key="value"
|
|
@@ -230,6 +238,7 @@ export default {
|
|
|
230
238
|
<div class="col span-4">
|
|
231
239
|
<LabeledSelect
|
|
232
240
|
v-model.number="menuMaxClusters"
|
|
241
|
+
data-testid="prefs__displaySetting__menuMaxClusters"
|
|
233
242
|
:label="t('prefs.clusterToShow.label')"
|
|
234
243
|
:options="menuClusterOptions"
|
|
235
244
|
option-key="value"
|
|
@@ -245,6 +254,7 @@ export default {
|
|
|
245
254
|
<h4 v-t="'prefs.confirmationSetting.title'" />
|
|
246
255
|
<Checkbox
|
|
247
256
|
v-model="scalingDownPrompt"
|
|
257
|
+
data-testid="prefs__scalingDownPrompt"
|
|
248
258
|
:label="t('prefs.confirmationSetting.scalingDownPrompt')"
|
|
249
259
|
class="mt-10"
|
|
250
260
|
/>
|
|
@@ -255,18 +265,21 @@ export default {
|
|
|
255
265
|
<h4 v-t="'prefs.advFeatures.title'" />
|
|
256
266
|
<Checkbox
|
|
257
267
|
v-model="viewInApi"
|
|
268
|
+
data-testid="prefs__viewInApi"
|
|
258
269
|
:label="t('prefs.advFeatures.viewInApi', {}, true)"
|
|
259
270
|
class="mt-10"
|
|
260
271
|
/>
|
|
261
272
|
<br>
|
|
262
273
|
<Checkbox
|
|
263
274
|
v-model="allNamespaces"
|
|
275
|
+
data-testid="prefs__allNamespaces"
|
|
264
276
|
:label="t('prefs.advFeatures.allNamespaces', {}, true)"
|
|
265
277
|
class="mt-20"
|
|
266
278
|
/>
|
|
267
279
|
<br>
|
|
268
280
|
<Checkbox
|
|
269
281
|
v-model="themeShortcut"
|
|
282
|
+
data-testid="prefs__themeShortcut"
|
|
270
283
|
:label="t('prefs.advFeatures.themeShortcut', {}, true)"
|
|
271
284
|
class="mt-20"
|
|
272
285
|
/>
|
|
@@ -274,6 +287,7 @@ export default {
|
|
|
274
287
|
<Checkbox
|
|
275
288
|
v-if="!isSingleProduct"
|
|
276
289
|
v-model="hideDescriptions"
|
|
290
|
+
data-testid="prefs__hideDescriptions"
|
|
277
291
|
:label="t('prefs.hideDesc.label')"
|
|
278
292
|
class="mt-20"
|
|
279
293
|
/>
|
|
@@ -292,6 +306,7 @@ export default {
|
|
|
292
306
|
<h4 v-t="'prefs.keymap.label'" />
|
|
293
307
|
<ButtonGroup
|
|
294
308
|
v-model="keymap"
|
|
309
|
+
data-testid="prefs__keymapOptions"
|
|
295
310
|
:options="keymapOptions"
|
|
296
311
|
/>
|
|
297
312
|
</div>
|
|
@@ -304,6 +319,7 @@ export default {
|
|
|
304
319
|
<h4 v-t="'prefs.helm.label'" />
|
|
305
320
|
<ButtonGroup
|
|
306
321
|
v-model="showPreRelease"
|
|
322
|
+
data-testid="prefs__helmOptions"
|
|
307
323
|
:options="helmOptions"
|
|
308
324
|
/>
|
|
309
325
|
</div>
|
package/pkg/vue.config.js
CHANGED
|
@@ -82,7 +82,6 @@ module.exports = function(dir) {
|
|
|
82
82
|
// These modules will be externalised and not included with the build of a package library
|
|
83
83
|
// This helps reduce the package size, but these dependencies must be provided by the hosting application
|
|
84
84
|
config.externals = {
|
|
85
|
-
jquery: '$',
|
|
86
85
|
jszip: '__jszip',
|
|
87
86
|
'js-yaml': '__jsyaml'
|
|
88
87
|
};
|