@rancher/shell 3.0.0-rc.8 → 3.0.0

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 (84) hide show
  1. package/assets/translations/en-us.yaml +12 -11
  2. package/assets/translations/zh-hans.yaml +1 -4
  3. package/components/EmberPage.vue +0 -8
  4. package/components/ResourceTable.vue +26 -1
  5. package/components/SortableTable/actions.js +1 -1
  6. package/components/SortableTable/index.vue +17 -1
  7. package/components/SortableTable/selection.js +1 -1
  8. package/components/SortableTable/sorting.js +11 -3
  9. package/components/fleet/FleetClusters.vue +0 -3
  10. package/components/form/ColorInput.vue +13 -2
  11. package/components/form/HookOption.vue +31 -29
  12. package/components/form/LabeledSelect.vue +0 -1
  13. package/components/form/LifecycleHooks.vue +2 -2
  14. package/components/form/__tests__/ColorInput.test.ts +27 -0
  15. package/components/formatter/SecretData.vue +1 -1
  16. package/components/nav/Header.vue +10 -13
  17. package/components/nav/TopLevelMenu.vue +0 -40
  18. package/components/nav/WorkspaceSwitcher.vue +0 -1
  19. package/config/private-label.js +2 -1
  20. package/config/router/routes.js +2 -26
  21. package/config/settings.ts +5 -0
  22. package/config/version.js +2 -0
  23. package/detail/catalog.cattle.io.app.vue +17 -4
  24. package/detail/fleet.cattle.io.cluster.vue +11 -9
  25. package/detail/fleet.cattle.io.gitrepo.vue +1 -1
  26. package/edit/cis.cattle.io.clusterscan.vue +4 -3
  27. package/edit/cis.cattle.io.clusterscanbenchmark.vue +2 -0
  28. package/edit/constraints.gatekeeper.sh.constraint/index.vue +2 -0
  29. package/edit/fleet.cattle.io.cluster.vue +4 -0
  30. package/edit/fleet.cattle.io.gitrepo.vue +11 -8
  31. package/edit/helm.cattle.io.projecthelmchart.vue +2 -0
  32. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +2 -0
  33. package/edit/kontainerDriver.vue +2 -0
  34. package/edit/logging-flow/index.vue +2 -0
  35. package/edit/management.cattle.io.project.vue +2 -1
  36. package/edit/management.cattle.io.projectroletemplatebinding.vue +2 -1
  37. package/edit/management.cattle.io.user.vue +3 -0
  38. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +2 -0
  39. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -0
  40. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +5 -5
  41. package/edit/monitoring.coreos.com.prometheusrule/index.vue +3 -1
  42. package/edit/monitoring.coreos.com.receiver/index.vue +2 -0
  43. package/edit/monitoring.coreos.com.route.vue +2 -0
  44. package/edit/networking.istio.io.destinationrule/index.vue +2 -0
  45. package/edit/nodeDriver.vue +2 -0
  46. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +26 -0
  47. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +63 -149
  48. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +4 -1
  49. package/edit/provisioning.cattle.io.cluster/import.vue +2 -0
  50. package/edit/provisioning.cattle.io.cluster/rke2.vue +5 -3
  51. package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +7 -2
  52. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +108 -35
  53. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +1 -1
  54. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +1 -1
  55. package/edit/ui.cattle.io.navlink.vue +4 -3
  56. package/edit/workload/mixins/workload.js +1 -1
  57. package/mixins/browser-tab-visibility.js +1 -1
  58. package/mixins/chart.js +6 -2
  59. package/mixins/metric-poller.js +1 -1
  60. package/mixins/resource-fetch.js +1 -1
  61. package/models/catalog.cattle.io.app.js +108 -21
  62. package/models/cloudcredential.js +4 -4
  63. package/models/fleet.cattle.io.gitrepo.js +8 -13
  64. package/models/management.cattle.io.cluster.js +13 -2
  65. package/models/management.cattle.io.project.js +4 -0
  66. package/models/provisioning.cattle.io.cluster.js +1 -2
  67. package/models/workload.js +1 -1
  68. package/package.json +11 -4
  69. package/pages/auth/logout.vue +7 -9
  70. package/pages/auth/setup.vue +3 -0
  71. package/pages/c/_cluster/apps/charts/install.vue +11 -3
  72. package/pages/c/_cluster/explorer/__tests__/index.test.ts +7 -4
  73. package/pages/c/_cluster/explorer/index.vue +45 -24
  74. package/pages/c/_cluster/fleet/index.vue +11 -5
  75. package/pages/c/_cluster/uiplugins/index.vue +4 -2
  76. package/pages/diagnostic.vue +1 -0
  77. package/plugins/steve/mutations.js +4 -1
  78. package/plugins/steve/subscribe.js +3 -4
  79. package/types/shell/index.d.ts +13 -0
  80. package/utils/__tests__/object.test.ts +152 -1
  81. package/utils/object.js +37 -0
  82. package/utils/string.js +9 -0
  83. package/utils/validators/formRules/index.ts +1 -1
  84. package/config/product/multi-cluster-apps.js +0 -61
@@ -12,6 +12,7 @@ import { WORKSPACE_ANNOTATION } from '@shell/config/labels-annotations';
12
12
  import { filterBy } from '@shell/utils/array';
13
13
  import FleetNoWorkspaces from '@shell/components/fleet/FleetNoWorkspaces.vue';
14
14
  import { NAME } from '@shell/config/product/fleet';
15
+ import { xOfy } from '@shell/utils/string';
15
16
 
16
17
  export default {
17
18
  name: 'FleetDashboard',
@@ -175,7 +176,12 @@ export default {
175
176
  }
176
177
 
177
178
  if (area === 'clusters') {
178
- group = row.targetClusters;
179
+ if (row.clusterInfo?.ready === row.clusterInfo?.total && row.clusterInfo?.ready) {
180
+ return {
181
+ badgeClass: STATES[STATES_ENUM.ACTIVE].color,
182
+ icon: STATES[STATES_ENUM.ACTIVE].compoundIcon
183
+ };
184
+ }
179
185
  } else if (area === 'bundles') {
180
186
  group = row.bundles;
181
187
  } else if (area === 'resources') {
@@ -223,7 +229,7 @@ export default {
223
229
  }
224
230
 
225
231
  if (area === 'clusters') {
226
- group = row.targetClusters;
232
+ group = '';
227
233
  } else if (area === 'bundles') {
228
234
  group = row.bundles;
229
235
  } else if (area === 'resources') {
@@ -262,11 +268,11 @@ export default {
262
268
  }
263
269
 
264
270
  if (area === 'clusters') {
265
- value = `${ row.targetClustersReady?.length || '0' }/${ row.targetClusters?.length || '?' }`;
271
+ return `${ row.clusterInfo.ready }/${ row.clusterInfo.total }`;
266
272
  } else if (area === 'bundles') {
267
- value = `${ row.bundlesReady?.length || '0' }/${ row.bundles?.length || '?' }`;
273
+ value = xOfy(row.bundlesReady?.length, row.bundles?.length);
268
274
  } else if (area === 'resources') {
269
- value = `${ row.status?.resourceCounts?.ready || '0' }/${ row.status?.resourceCounts?.desiredReady || '?' }`;
275
+ value = xOfy(row.status?.resourceCounts?.ready, row.status?.resourceCounts?.desiredReady);
270
276
  }
271
277
 
272
278
  return value;
@@ -154,7 +154,8 @@ export default {
154
154
  },
155
155
 
156
156
  showAddReposBanner() {
157
- const hasExtensionReposBannerSetting = this.addExtensionReposBannerSetting?.value === 'true';
157
+ // because of https://github.com/rancher/rancher/pull/45894 we need to consider other start values
158
+ const hasExtensionReposBannerSetting = this.addExtensionReposBannerSetting?.value === 'true' || this.addExtensionReposBannerSetting?.value === '' || this.addExtensionReposBannerSetting?.value === undefined;
158
159
  const uiPluginsRepoNotFound = isRancherPrime() && !this.repos?.find((r) => r.urlDisplay === UI_PLUGINS_REPOS.OFFICIAL.URL);
159
160
  const uiPluginsPartnersRepoNotFound = !this.repos?.find((r) => r.urlDisplay === UI_PLUGINS_REPOS.PARTNERS.URL);
160
161
 
@@ -615,7 +616,8 @@ export default {
615
616
  },
616
617
 
617
618
  updateAddReposSetting() {
618
- if (this.addExtensionReposBannerSetting?.value === 'true') {
619
+ // because of https://github.com/rancher/rancher/pull/45894 we need to consider other start values
620
+ if (this.addExtensionReposBannerSetting?.value === 'true' || this.addExtensionReposBannerSetting?.value === '' || this.addExtensionReposBannerSetting?.value === undefined) {
619
621
  this.addExtensionReposBannerSetting.value = 'false';
620
622
  this.addExtensionReposBannerSetting.save();
621
623
  }
@@ -215,6 +215,7 @@ export default {
215
215
  'linode',
216
216
  'targetRoute', // contains circular references, isn't useful (added later to store)
217
217
  '$router', // also contains a circular reference to $store, not useful for diagnostics
218
+ '$route', // also contains a circular reference to $store, not useful for diagnostics
218
219
  ];
219
220
 
220
221
  const clearListsKeys = [
@@ -13,6 +13,7 @@ import {
13
13
  import { perfLoadAll } from '@shell/plugins/steve/performanceTesting';
14
14
  import { classify } from '@shell/plugins/dashboard-store/classify';
15
15
  import SteveSchema from '@shell/models/steve-schema';
16
+ import { deepToRaw } from '@shell/utils/object';
16
17
 
17
18
  function registerNamespace(state, namespace) {
18
19
  let cache = state.podsByNamespace[namespace];
@@ -145,7 +146,9 @@ export default {
145
146
 
146
147
  if (worker) {
147
148
  // Store raw json objects, not the proxies
148
- worker.postMessage({ loadSchemas: data });
149
+ const rawData = deepToRaw(data);
150
+
151
+ worker.postMessage({ loadSchemas: rawData });
149
152
  }
150
153
  }
151
154
  },
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { addObject, clear, removeObject } from '@shell/utils/array';
11
- import { get } from '@shell/utils/object';
11
+ import { get, deepToRaw } from '@shell/utils/object';
12
12
  import { SCHEMA, MANAGEMENT } from '@shell/config/types';
13
13
  import { SETTING } from '@shell/config/settings';
14
14
  import { CSRF } from '@shell/config/cookies';
@@ -35,7 +35,6 @@ import { WORKER_MODES } from './worker';
35
35
  import acceptOrRejectSocketMessage from './accept-or-reject-socket-message';
36
36
  import { BLANK_CLUSTER, STORE } from '@shell/store/store-types.js';
37
37
  import paginationUtils from '@shell/utils/pagination-utils';
38
- import _ from 'lodash';
39
38
 
40
39
  // minimum length of time a disconnect notification is shown
41
40
  const MINIMUM_TIME_NOTIFIED = 3000;
@@ -179,9 +178,9 @@ export async function createWorker(store, ctx) {
179
178
 
180
179
  while (workerQueues[storeName]?.length) {
181
180
  const message = workerQueues[storeName].shift();
181
+ const safeMessage = deepToRaw(message);
182
182
 
183
- // TODO: 11541: Web Worker communication fails due to Proxy objects in messages
184
- store.$workers[storeName].postMessage(_.cloneDeep(message));
183
+ store.$workers[storeName].postMessage(safeMessage);
185
184
  }
186
185
  }
187
186
 
@@ -3900,6 +3900,13 @@ export function applyChangeset(obj: any, changeset: any): any;
3900
3900
  */
3901
3901
  export function pickBy(obj?: {}, predicate?: (value: any, key: any) => boolean): {};
3902
3902
  export function dropKeys(obj: any, keys: any): void;
3903
+ /**
3904
+ * Recursively convert a reactive object to a raw object
3905
+ * @param {*} obj
3906
+ * @param {*} cache
3907
+ * @returns
3908
+ */
3909
+ export function deepToRaw(obj: any, cache?: any): any;
3903
3910
  export { isEqualBasic as isEqual };
3904
3911
  export function toDictionary(array: any, callback: any): any;
3905
3912
  /**
@@ -4281,6 +4288,12 @@ export function isIpv4(ip: any): boolean;
4281
4288
  export function sanitizeKey(k: any): any;
4282
4289
  export function sanitizeValue(v: any): any;
4283
4290
  export function sanitizeIP(v: any): any;
4291
+ /**
4292
+ * Return the string `<x> / <y>`
4293
+ *
4294
+ * Each param should be a number, otherwise `?` is used
4295
+ */
4296
+ export function xOfy(x: any, y: any): string;
4284
4297
  export namespace CHARSET {
4285
4298
  export { num as NUMERIC };
4286
4299
  export const NO_VOWELS: string;
@@ -1,5 +1,6 @@
1
+ import { reactive, isReactive } from 'vue';
1
2
  import {
2
- clone, get, getter, isEmpty, toDictionary, remove, diff, definedKeys
3
+ clone, get, getter, isEmpty, toDictionary, remove, diff, definedKeys, deepToRaw
3
4
  } from '@shell/utils/object';
4
5
 
5
6
  describe('fx: get', () => {
@@ -227,3 +228,153 @@ describe('fx: definedKeys', () => {
227
228
  expect(result).toStrictEqual(expected);
228
229
  });
229
230
  });
231
+
232
+ describe('fx: deepToRaw', () => {
233
+ it('should return primitives as is', () => {
234
+ expect(deepToRaw(null)).toBeNull();
235
+ expect(deepToRaw(undefined)).toBeUndefined();
236
+ expect(deepToRaw(42)).toBe(42);
237
+ expect(deepToRaw('test')).toBe('test');
238
+ expect(deepToRaw(true)).toBe(true);
239
+
240
+ const sym = Symbol('symbol');
241
+
242
+ expect(deepToRaw(sym)).toBe(sym);
243
+ });
244
+
245
+ it('should handle simple objects', () => {
246
+ const obj = { a: 1, b: 2 };
247
+ const result = deepToRaw(obj);
248
+
249
+ expect(result).toStrictEqual({ a: 1, b: 2 });
250
+ expect(result).not.toBe(obj); // Should not be the same reference
251
+ });
252
+
253
+ it('should handle arrays', () => {
254
+ const arr = [1, 2, 3];
255
+ const result = deepToRaw(arr);
256
+
257
+ expect(result).toStrictEqual([1, 2, 3]);
258
+ expect(result).not.toBe(arr); // Should not be the same reference
259
+ });
260
+
261
+ it('should handle nested objects', () => {
262
+ const obj = { a: { b: { c: 3 } } };
263
+ const result = deepToRaw(obj);
264
+
265
+ expect(result).toStrictEqual({ a: { b: { c: 3 } } });
266
+ expect(result).not.toBe(obj);
267
+ expect(result.a).not.toBe(obj.a);
268
+ expect(result.a.b).not.toBe(obj.a.b);
269
+ });
270
+
271
+ it('should handle nested arrays', () => {
272
+ const arr = [1, [2, [3]]];
273
+ const result = deepToRaw(arr);
274
+
275
+ expect(result).toStrictEqual([1, [2, [3]]]);
276
+ expect(result).not.toBe(arr);
277
+ expect(result[1]).not.toBe(arr[1]);
278
+ });
279
+
280
+ it('should handle reactive proxies (reactive object)', () => {
281
+ const reactiveObj = reactive({ a: 1, b: { c: 2 } });
282
+ const result = deepToRaw(reactiveObj);
283
+
284
+ expect(result).toStrictEqual({ a: 1, b: { c: 2 } });
285
+ expect(result).not.toBe(reactiveObj);
286
+ expect(isReactive(result)).toBe(false);
287
+ });
288
+
289
+ it('should handle nested reactive properties', () => {
290
+ const data = reactive({
291
+ num: 1,
292
+ str: 'test',
293
+ bool: true,
294
+ nil: null,
295
+ undef: undefined,
296
+ arr: [1, 2, { a: 3 }],
297
+ obj: { nested: reactive({ a: 1 }) },
298
+ func: null,
299
+ sym: null,
300
+ });
301
+
302
+ const result = deepToRaw(data);
303
+
304
+ expect(result).toStrictEqual({
305
+ num: 1,
306
+ str: 'test',
307
+ bool: true,
308
+ nil: null,
309
+ undef: undefined,
310
+ arr: [1, 2, { a: 3 }],
311
+ obj: { nested: { a: 1 } },
312
+ func: null,
313
+ sym: null,
314
+ });
315
+
316
+ expect(isReactive(result)).toBe(false);
317
+ expect(isReactive(result.obj)).toBe(false);
318
+ expect(isReactive(result.obj.nested)).toBe(false);
319
+ });
320
+
321
+ it('should handle circular references', () => {
322
+ const obj: { name: string; [key: string]: any } = { name: 'Alice' };
323
+
324
+ obj.self = obj; // Circular reference
325
+
326
+ const result = deepToRaw(obj);
327
+
328
+ expect(result).toStrictEqual({ name: 'Alice', self: result });
329
+ });
330
+
331
+ it('should handle objects with functions and symbols', () => {
332
+ const symbolKey = Symbol('key');
333
+ const obj = {
334
+ a: 1,
335
+ b() {},
336
+ [symbolKey]: 'symbolValue',
337
+ };
338
+
339
+ const result = deepToRaw(obj);
340
+
341
+ expect(result).toStrictEqual({ a: 1, b: null });
342
+ expect(result[symbolKey]).toBeUndefined(); // Symbols are skipped
343
+ });
344
+
345
+ it('should not mutate the original data', () => {
346
+ const obj = { a: { b: 2 } };
347
+ const original = JSON.stringify(obj);
348
+
349
+ deepToRaw(obj);
350
+ expect(JSON.stringify(obj)).toBe(original);
351
+ });
352
+
353
+ it('should handle complex data structures', () => {
354
+ const data = reactive({
355
+ num: 1,
356
+ str: 'test',
357
+ bool: true,
358
+ nil: null,
359
+ undef: undefined,
360
+ arr: [1, 2, { a: 3 }],
361
+ obj: { nested: { a: 1 } },
362
+ func: () => {},
363
+ sym: Symbol('sym'),
364
+ });
365
+
366
+ const result = deepToRaw(data);
367
+
368
+ expect(result).toStrictEqual({
369
+ num: 1,
370
+ str: 'test',
371
+ bool: true,
372
+ nil: null,
373
+ undef: undefined,
374
+ arr: [1, 2, { a: 3 }],
375
+ obj: { nested: { a: 1 } },
376
+ func: null,
377
+ sym: null,
378
+ });
379
+ });
380
+ });
package/utils/object.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { toRaw } from 'vue';
1
2
  import cloneDeep from 'lodash/cloneDeep';
2
3
  import flattenDeep from 'lodash/flattenDeep';
3
4
  import compact from 'lodash/compact';
@@ -434,3 +435,39 @@ export function dropKeys(obj, keys) {
434
435
  delete obj[k];
435
436
  }
436
437
  }
438
+
439
+ /**
440
+ * Recursively convert a reactive object to a raw object
441
+ * @param {*} obj
442
+ * @param {*} cache
443
+ * @returns
444
+ */
445
+ export function deepToRaw(obj, cache = new WeakSet()) {
446
+ if (obj === null || typeof obj !== 'object') {
447
+ // If obj is null or a primitive, return it as is
448
+ return obj;
449
+ }
450
+
451
+ // If the object has already been processed, return it to prevent circular references
452
+ if (cache.has(obj)) {
453
+ return obj;
454
+ }
455
+ cache.add(obj);
456
+
457
+ if (Array.isArray(obj)) {
458
+ return obj.map((item) => deepToRaw(item, cache));
459
+ } else {
460
+ const rawObj = toRaw(obj);
461
+ const result = {};
462
+
463
+ for (const key in rawObj) {
464
+ if (typeof rawObj[key] === 'function' || typeof rawObj[key] === 'symbol') {
465
+ result[key] = null;
466
+ } else {
467
+ result[key] = deepToRaw(rawObj[key], cache);
468
+ }
469
+ }
470
+
471
+ return result;
472
+ }
473
+ }
package/utils/string.js CHANGED
@@ -321,3 +321,12 @@ export function sanitizeValue(v) {
321
321
  export function sanitizeIP(v) {
322
322
  return (v || '').replace(/[^a-z0-9.:_-]/ig, '');
323
323
  }
324
+
325
+ /**
326
+ * Return the string `<x> / <y>`
327
+ *
328
+ * Each param should be a number, otherwise `?` is used
329
+ */
330
+ export function xOfy(x, y) {
331
+ return `${ typeof x === 'number' ? x : '?' }/${ typeof y === 'number' ? y : '?' }`;
332
+ }
@@ -499,6 +499,6 @@ export default function(t: Translation, { key = 'Value' }: ValidationOptions): {
499
499
  servicePort,
500
500
  subDomain,
501
501
  testRule,
502
- wildcardHostname,
502
+ wildcardHostname
503
503
  };
504
504
  }
@@ -1,61 +0,0 @@
1
- import { LEGACY } from '@shell/store/features';
2
- import { DSL } from '@shell/store/type-map';
3
-
4
- export const NAME = 'mcapps';
5
-
6
- export function init(store) {
7
- const {
8
- product,
9
- basicType,
10
- virtualType,
11
- } = DSL(store, NAME);
12
-
13
- product({
14
- icon: 'marketplace',
15
- category: 'legacy',
16
- inStore: 'management',
17
- ifFeature: LEGACY,
18
- removable: false,
19
- showClusterSwitcher: false,
20
- showWorkspaceSwitcher: false,
21
- });
22
-
23
- virtualType({
24
- labelKey: 'legacy.catalogs',
25
- name: 'mc-catalogs',
26
- group: 'Root',
27
- namespaced: false,
28
- weight: 111,
29
- icon: 'folder',
30
- route: { name: 'c-cluster-mcapps-pages-page', params: { cluster: 'local', page: 'catalogs' } },
31
- exact: true
32
- });
33
-
34
- virtualType({
35
- labelKey: 'legacy.globalDnsEntries',
36
- name: 'global-dns-entries',
37
- group: 'Root',
38
- namespaced: false,
39
- weight: 110,
40
- icon: 'folder',
41
- route: { name: 'c-cluster-mcapps-pages-page', params: { cluster: 'local', page: 'global-dns-entries' } },
42
- exact: true
43
- });
44
-
45
- virtualType({
46
- labelKey: 'legacy.globalDnsProviders',
47
- name: 'global-dns-providers',
48
- group: 'Root',
49
- namespaced: false,
50
- weight: 109,
51
- icon: 'folder',
52
- route: { name: 'c-cluster-mcapps-pages-page', params: { cluster: 'local', page: 'global-dns-providers' } },
53
- exact: true
54
- });
55
-
56
- basicType([
57
- 'mc-catalogs',
58
- 'global-dns-entries',
59
- 'global-dns-providers',
60
- ]);
61
- }