@rancher/shell 0.3.7 → 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.
Files changed (48) hide show
  1. package/assets/translations/en-us.yaml +55 -14
  2. package/babel.config.js +17 -4
  3. package/components/CodeMirror.vue +146 -14
  4. package/components/ContainerResourceLimit.vue +14 -1
  5. package/components/CruResource.vue +21 -5
  6. package/components/ExplorerProjectsNamespaces.vue +5 -1
  7. package/components/GroupPanel.vue +57 -0
  8. package/components/Inactivity.vue +229 -0
  9. package/components/YamlEditor.vue +2 -2
  10. package/components/form/ArrayList.vue +1 -1
  11. package/components/form/KeyValue.vue +34 -1
  12. package/components/form/MatchExpressions.vue +120 -21
  13. package/components/form/NodeAffinity.vue +54 -4
  14. package/components/form/PodAffinity.vue +160 -47
  15. package/components/form/Tolerations.vue +40 -4
  16. package/components/form/__tests__/ArrayList.test.ts +3 -3
  17. package/components/form/__tests__/MatchExpressions.test.ts +1 -1
  18. package/components/nav/Header.vue +2 -0
  19. package/config/settings.ts +10 -1
  20. package/core/plugins-loader.js +0 -2
  21. package/creators/app/files/.gitignore +73 -0
  22. package/creators/app/init +1 -0
  23. package/edit/configmap.vue +33 -6
  24. package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +326 -0
  25. package/edit/provisioning.cattle.io.cluster/index.vue +1 -0
  26. package/edit/provisioning.cattle.io.cluster/rke2.vue +63 -15
  27. package/edit/workload/mixins/workload.js +12 -4
  28. package/layouts/blank.vue +4 -0
  29. package/layouts/default.vue +3 -0
  30. package/layouts/home.vue +4 -1
  31. package/layouts/plain.vue +4 -1
  32. package/mixins/chart.js +1 -1
  33. package/models/batch.cronjob.js +18 -3
  34. package/models/provisioning.cattle.io.cluster.js +24 -0
  35. package/models/workload.js +1 -1
  36. package/package.json +2 -3
  37. package/pages/auth/login.vue +1 -0
  38. package/pages/c/_cluster/explorer/index.vue +1 -4
  39. package/pages/c/_cluster/settings/performance.vue +61 -7
  40. package/pages/prefs.vue +18 -2
  41. package/pkg/vue.config.js +0 -1
  42. package/plugins/codemirror.js +158 -0
  43. package/public/index.html +1 -1
  44. package/store/index.js +36 -21
  45. package/types/shell/index.d.ts +20 -1
  46. package/utils/create-yaml.js +105 -8
  47. package/utils/settings.ts +12 -0
  48. package/vue.config.js +2 -2
@@ -621,6 +621,7 @@ export default {
621
621
  <div
622
622
  v-if="showUserMenu"
623
623
  class="user user-menu"
624
+ data-testid="nav_header_showUserMenu"
624
625
  tabindex="0"
625
626
  @blur="showMenu(false)"
626
627
  @click="showMenu(true)"
@@ -654,6 +655,7 @@ export default {
654
655
  >
655
656
  <ul
656
657
  class="list-unstyled dropdown"
658
+ data-testid="user-menu-dropdown"
657
659
  @click.stop="showMenu(false)"
658
660
  >
659
661
  <li
@@ -83,7 +83,12 @@ export const SETTING = {
83
83
  * both pre and post log in. If not present defaults to the usual process
84
84
  */
85
85
  THEME: 'ui-theme',
86
- SYSTEM_NAMESPACES: 'system-namespaces'
86
+ SYSTEM_NAMESPACES: 'system-namespaces',
87
+ /**
88
+ * Cluster Agent configuration
89
+ */
90
+ CLUSTER_AGENT_DEFAULT_AFFINITY: 'cluster-agent-default-affinity',
91
+ FLEET_AGENT_DEFAULT_AFFINITY: 'fleet-agent-default-affinity',
87
92
  };
88
93
 
89
94
  // These are the settings that are allowed to be edited via the UI
@@ -140,6 +145,10 @@ export const ALLOWED_SETTINGS: GlobalSetting = {
140
145
  };
141
146
 
142
147
  export const DEFAULT_PERF_SETTING = {
148
+ inactivity: {
149
+ enabled: false,
150
+ threshold: 900,
151
+ },
143
152
  incrementalLoading: {
144
153
  enabled: true,
145
154
  threshold: 1500,
@@ -1,5 +1,4 @@
1
1
  import Vue from 'vue';
2
- import $ from 'jquery';
3
2
  import JSZip from 'jszip';
4
3
  import jsyaml from 'js-yaml';
5
4
 
@@ -26,7 +25,6 @@ export default function({
26
25
  window.Vue = Vue;
27
26
 
28
27
  // Global libraries - allows us to externalise these to reduce package bundle size
29
- window.$ = $;
30
28
  window.__jszip = JSZip;
31
29
  window.__jsyaml = jsyaml;
32
30
  }
@@ -0,0 +1,73 @@
1
+ # compiled output
2
+ /dist
3
+ /tmp
4
+ /out-tsc
5
+
6
+ # Runtime data
7
+ pids
8
+ *.pid
9
+ *.seed
10
+ *.pid.lock
11
+
12
+ # Directory for instrumented libs generated by jscoverage/JSCover
13
+ lib-cov
14
+
15
+ # Coverage directory used by tools like istanbul
16
+ coverage
17
+
18
+ # nyc test coverage
19
+ .nyc_output
20
+
21
+ # IDEs and editors
22
+ .idea
23
+ .project
24
+ .classpath
25
+ .c9/
26
+ *.launch
27
+ .settings/
28
+ *.sublime-workspace
29
+
30
+ # IDE - VSCode
31
+ .vscode/*
32
+ !.vscode/settings.json
33
+ !.vscode/tasks.json
34
+ !.vscode/launch.json
35
+ !.vscode/extensions.json
36
+
37
+ # misc
38
+ .sass-cache
39
+ connect.lock
40
+ typings
41
+
42
+ # Logs
43
+ logs
44
+ *.log
45
+ npm-debug.log*
46
+ yarn-debug.log*
47
+ yarn-error.log*
48
+
49
+ # Dependency directories
50
+ node_modules/
51
+ jspm_packages/
52
+
53
+ # Optional npm cache directory
54
+ .npm
55
+
56
+ # Optional eslint cache
57
+ .eslintcache
58
+
59
+ # Optional REPL history
60
+ .node_repl_history
61
+
62
+ # Output of 'npm pack'
63
+ *.tgz
64
+
65
+ # Yarn Integrity file
66
+ .yarn-integrity
67
+
68
+ # dotenv environment variables file
69
+ .env
70
+
71
+ # System Files
72
+ .DS_Store
73
+ Thumbs.db
package/creators/app/init CHANGED
@@ -12,6 +12,7 @@ const targets = {
12
12
  const files = [
13
13
  'tsconfig.json',
14
14
  'vue.config.js',
15
+ '.gitignore',
15
16
  '.eslintignore',
16
17
  '.eslintrc.js',
17
18
  'babel.config.js',
@@ -31,40 +31,65 @@ export default {
31
31
  computed: {
32
32
  hasBinaryData() {
33
33
  return Object.keys(this.binaryData).length > 0;
34
- }
34
+ },
35
+ /**
36
+ * Keep all newlines from end, see: https://yaml-multiline.info
37
+ * Apply to 'data' field
38
+ */
39
+ yamlModifiers() {
40
+ return {
41
+ data: Object.keys(this.data).reduce((acc, key) => ({
42
+ ...acc,
43
+ [key]: { chomping: '+' },
44
+ }), {}),
45
+ };
46
+ },
47
+
48
+ validationPassed() {
49
+ return !!this.value.name;
50
+ },
35
51
  },
36
52
 
37
53
  watch: {
38
- data(neu, old) {
54
+ data(neu) {
39
55
  this.updateValue(neu, 'data');
40
56
  },
41
- binaryData(neu, old) {
57
+ binaryData(neu) {
42
58
  this.updateValue(neu, 'binaryData');
43
59
  },
44
60
  },
45
61
 
46
62
  methods: {
63
+ async saveConfigMap() {
64
+ const yaml = this.$refs.cru.createResourceYaml(this.yamlModifiers);
65
+
66
+ await this.value.saveYaml(yaml);
67
+ this.done();
68
+ },
69
+
47
70
  updateValue(val, type) {
48
71
  this.$set(this.value, type, {});
49
72
 
50
73
  Object.keys(val).forEach((key) => {
51
74
  this.$set(this.value[type], key, val[key]);
52
75
  });
53
- }
76
+ },
54
77
  }
55
78
  };
56
79
  </script>
57
80
 
58
81
  <template>
59
82
  <CruResource
83
+ ref="cru"
60
84
  :done-route="doneRoute"
61
85
  :mode="mode"
62
86
  :resource="value"
63
87
  :subtypes="[]"
64
- :validation-passed="true"
88
+ :validation-passed="validationPassed"
89
+ :yaml-modifiers="yamlModifiers"
65
90
  :errors="errors"
66
91
  @error="e=>errors = e"
67
- @finish="save"
92
+ @finish="saveConfigMap"
68
93
  @cancel="done"
69
94
  >
70
95
  <NameNsDescription
@@ -86,6 +111,8 @@ export default {
86
111
  :protip="t('configmap.tabs.data.protip')"
87
112
  :initial-empty-row="true"
88
113
  :value-can-be-empty="true"
114
+ :value-trim="false"
115
+ :value-markdown-multiline="true"
89
116
  :read-multiple="true"
90
117
  :read-accept="'*'"
91
118
  />
@@ -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>
@@ -477,6 +477,7 @@ export default {
477
477
  :errors="errors"
478
478
  :subtypes="subTypes"
479
479
  :cancel-event="true"
480
+ :prevent-enter-submit="true"
480
481
  class="create-cluster"
481
482
  @finish="save"
482
483
  @cancel="cancel"