@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.
Files changed (145) hide show
  1. package/assets/translations/en-us.yaml +47 -26
  2. package/assets/translations/zh-hans.yaml +82 -16
  3. package/babel.config.js +17 -4
  4. package/chart/istio.vue +11 -11
  5. package/chart/rancher-backup/S3.vue +1 -1
  6. package/components/AsyncButton.vue +2 -2
  7. package/components/ButtonGroup.vue +1 -1
  8. package/components/CodeMirror.vue +146 -14
  9. package/components/CompoundStatusBadge.vue +1 -1
  10. package/components/ContainerResourceLimit.vue +14 -1
  11. package/components/CopyCode.vue +1 -1
  12. package/components/CruResource.vue +21 -5
  13. package/components/DetailTop.vue +1 -1
  14. package/components/ExplorerProjectsNamespaces.vue +8 -4
  15. package/components/GlobalRoleBindings.vue +1 -1
  16. package/components/GroupPanel.vue +57 -0
  17. package/components/HarvesterServiceAddOnConfig.vue +2 -117
  18. package/components/ResourceDetail/Masthead.vue +1 -1
  19. package/components/ResourceList/Masthead.vue +0 -6
  20. package/components/ResourceList/ResourceLoadingIndicator.vue +1 -9
  21. package/components/ResourceList/index.vue +7 -6
  22. package/components/ResourceTable.vue +13 -3
  23. package/components/SortableTable/THead.vue +3 -3
  24. package/components/SortableTable/index.vue +3 -3
  25. package/components/Tabbed/Tab.vue +1 -1
  26. package/components/Tabbed/index.vue +1 -1
  27. package/components/Wizard.vue +9 -6
  28. package/components/YamlEditor.vue +2 -2
  29. package/components/__tests__/NamespaceFilter.test.ts +26 -7
  30. package/components/auth/RoleDetailEdit.vue +1 -1
  31. package/components/auth/SelectPrincipal.vue +1 -1
  32. package/components/fleet/FleetRepos.vue +1 -1
  33. package/components/form/ArrayList.vue +2 -2
  34. package/components/form/KeyValue.vue +37 -3
  35. package/components/form/Labels.vue +34 -14
  36. package/components/form/MatchExpressions.vue +120 -21
  37. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  38. package/components/form/NameNsDescription.vue +1 -1
  39. package/components/form/NodeAffinity.vue +54 -4
  40. package/components/form/PlusMinus.vue +2 -2
  41. package/components/form/PodAffinity.vue +160 -47
  42. package/components/form/Probe.vue +1 -1
  43. package/components/form/ProjectMemberEditor.vue +8 -4
  44. package/components/form/ResourceQuota/NamespaceRow.vue +1 -1
  45. package/components/form/ServicePorts.vue +2 -2
  46. package/components/form/Tolerations.vue +70 -7
  47. package/components/form/WorkloadPorts.vue +2 -1
  48. package/components/form/__tests__/ArrayList.test.ts +3 -3
  49. package/components/form/__tests__/KeyValue.test.ts +17 -0
  50. package/components/form/__tests__/MatchExpressions.test.ts +1 -1
  51. package/components/formatter/ClusterLink.vue +3 -3
  52. package/components/formatter/LiveDate.vue +1 -1
  53. package/components/formatter/PodImages.vue +1 -1
  54. package/components/formatter/RKETemplateName.vue +1 -1
  55. package/components/formatter/Shortened.vue +1 -1
  56. package/components/nav/Header.vue +9 -7
  57. package/components/nav/NamespaceFilter.vue +103 -54
  58. package/config/labels-annotations.js +8 -5
  59. package/config/settings.ts +8 -6
  60. package/config/types.js +6 -4
  61. package/core/plugin-routes.ts +26 -7
  62. package/detail/provisioning.cattle.io.cluster.vue +4 -4
  63. package/edit/cis.cattle.io.clusterscan.vue +1 -1
  64. package/edit/configmap.vue +33 -6
  65. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +19 -149
  66. package/edit/logging-flow/index.vue +2 -2
  67. package/edit/logging.banzaicloud.io.output/providers/elasticsearch.vue +12 -0
  68. package/edit/logging.banzaicloud.io.output/providers/opensearch.vue +12 -0
  69. package/edit/management.cattle.io.project.vue +7 -0
  70. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
  71. package/edit/monitoring.coreos.com.alertmanagerconfig/routeConfig.vue +2 -2
  72. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +11 -8
  73. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +2 -2
  74. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +12 -4
  75. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +140 -0
  76. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +158 -0
  77. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/selectors.ts +45 -0
  78. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
  79. package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +326 -0
  80. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +1 -1
  81. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -1
  82. package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +2 -2
  83. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +143 -169
  84. package/edit/provisioning.cattle.io.cluster/index.vue +1 -0
  85. package/edit/provisioning.cattle.io.cluster/rke2.vue +75 -6
  86. package/edit/resources.cattle.io.restore.vue +2 -2
  87. package/edit/service.vue +22 -3
  88. package/edit/storage.k8s.io.storageclass/index.vue +1 -1
  89. package/edit/workload/Job.vue +2 -2
  90. package/edit/workload/index.vue +1 -1
  91. package/edit/workload/mixins/workload.js +7 -1
  92. package/edit/workload/storage/__tests__/Storage.test.ts +84 -5
  93. package/initialize/index.js +1 -0
  94. package/layouts/default.vue +1 -1
  95. package/mixins/chart.js +1 -1
  96. package/mixins/resource-fetch-namespaced.js +19 -27
  97. package/mixins/resource-fetch.js +0 -5
  98. package/models/__tests__/namespace.test.ts +125 -0
  99. package/models/batch.cronjob.js +18 -3
  100. package/models/management.cattle.io.project.js +6 -1
  101. package/models/persistentvolume.js +1 -1
  102. package/models/workload.js +1 -1
  103. package/models/workload.service.js +22 -7
  104. package/package.json +17 -6
  105. package/pages/auth/login.vue +47 -49
  106. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  107. package/pages/c/_cluster/apps/charts/install.vue +42 -51
  108. package/pages/c/_cluster/explorer/index.vue +1 -1
  109. package/pages/c/_cluster/monitoring/index.vue +1 -1
  110. package/pages/c/_cluster/settings/performance.vue +53 -18
  111. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  112. package/pages/c/_cluster/uiplugins/index.vue +16 -5
  113. package/pages/home.vue +1 -1
  114. package/pages/prefs.vue +18 -2
  115. package/plugins/clean-html-directive.js +1 -1
  116. package/plugins/clean-tooltip-directive.js +33 -0
  117. package/plugins/codemirror.js +158 -0
  118. package/plugins/dashboard-store/actions.js +4 -2
  119. package/plugins/dashboard-store/getters.js +6 -0
  120. package/plugins/dashboard-store/mutations.js +2 -2
  121. package/plugins/plugin.js +6 -1
  122. package/plugins/steve/actions.js +1 -1
  123. package/plugins/steve/getters.js +14 -3
  124. package/plugins/steve/resourceWatcher.js +36 -62
  125. package/plugins/steve/subscribe.js +137 -21
  126. package/plugins/steve/worker/index.js +7 -1
  127. package/plugins/steve/worker/web-worker.advanced.js +26 -8
  128. package/plugins/steve/worker/web-worker.basic.js +23 -4
  129. package/public/index.html +1 -1
  130. package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -2
  131. package/rancher-components/components/Form/Radio/RadioGroup.vue +2 -2
  132. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +1 -1
  133. package/store/index.js +16 -61
  134. package/store/store-types.js +5 -0
  135. package/store/type-map.js +1 -1
  136. package/types/shell/index.d.ts +42 -7
  137. package/utils/__tests__/create-yaml.test.ts +63 -0
  138. package/utils/array.ts +4 -0
  139. package/utils/create-yaml.js +105 -8
  140. package/utils/namespace-filter.js +17 -5
  141. package/utils/projectAndNamespaceFiltering.utils.ts +62 -0
  142. package/utils/selector.js +6 -5
  143. package/utils/settings.ts +17 -7
  144. package/vue.config.js +2 -2
  145. package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +0 -93
package/store/type-map.js CHANGED
@@ -971,7 +971,7 @@ export const getters = {
971
971
 
972
972
  groupByFor(state) {
973
973
  return (schema) => {
974
- return state.groupBy[schema.id];
974
+ return state.groupBy[schema?.id];
975
975
  };
976
976
  },
977
977
 
@@ -1738,6 +1738,7 @@ export namespace MANAGEMENT {
1738
1738
  const GLOBAL_ROLE_BINDING_1: string;
1739
1739
  export { GLOBAL_ROLE_BINDING_1 as GLOBAL_ROLE_BINDING };
1740
1740
  export const POD_SECURITY_POLICY_TEMPLATE: string;
1741
+ export const PSP_TEMPLATE_BINDING: string;
1741
1742
  export const PSA: string;
1742
1743
  export const MANAGED_CHART: string;
1743
1744
  export const USER_NOTIFICATION: string;
@@ -1840,6 +1841,7 @@ export namespace HCI {
1840
1841
  export const IMAGE: string;
1841
1842
  const SETTING_1: string;
1842
1843
  export { SETTING_1 as SETTING };
1844
+ export const HARVESTER_CONFIG: string;
1843
1845
  }
1844
1846
  export const VIRTUAL_HARVESTER_PROVIDER: "harvester";
1845
1847
  export namespace ADDRESSES {
@@ -2491,10 +2493,33 @@ export default _default;
2491
2493
  // @shell/utils/create-yaml
2492
2494
 
2493
2495
  declare module '@shell/utils/create-yaml' {
2494
- export function createYaml(schemas: any, type: any, data: any, processAlwaysAdd?: boolean, depth?: number, path?: string, rootType?: any): string;
2496
+ export function createYamlWithOptions(schemas: any, type: any, data: any, options: any): string;
2497
+ export function createYaml(schemas: any, type: any, data: any, processAlwaysAdd?: boolean, depth?: number, path?: string, rootType?: any, dataOptions?: {}): string;
2498
+ export function getBlockDescriptor(value: any, key: any): {
2499
+ header: string;
2500
+ indentation: string;
2501
+ };
2495
2502
  export function typeRef(type: any, str: any): any;
2496
2503
  export function typeMunge(type: any): any;
2497
2504
  export function saferDump(obj: any): any;
2505
+ /**
2506
+ * Handles newlines indicators in the multiline blocks.
2507
+ *
2508
+ * this is required since jsyaml.dump doesn't support chomping and scalar style at the moment.
2509
+ * see: https://github.com/nodeca/js-yaml/issues/171
2510
+ *
2511
+ * @param {*} data the multiline block
2512
+ * @param {*} options blocks indicators, see: https://yaml-multiline.info
2513
+ *
2514
+ * - scalarStyle:
2515
+ * one of '|', '>'
2516
+ * default '|'
2517
+ * - chomping:
2518
+ * one of: null, '', '-', '+'
2519
+ * default: null
2520
+ * @returns the result of jsyaml.dump with the addition of multiline indicators
2521
+ */
2522
+ export function dumpBlock(data: any, options?: any): any;
2498
2523
  export const SIMPLE_TYPES: string[];
2499
2524
  export const NEVER_ADD: string[];
2500
2525
  export const ACTIVELY_REMOVE: string[];
@@ -2655,14 +2680,24 @@ export function haveV1MonitoringWorkloads(store: any): Promise<boolean>;
2655
2680
  // @shell/utils/namespace-filter
2656
2681
 
2657
2682
  declare module '@shell/utils/namespace-filter' {
2658
- export const NAMESPACE_FILTER_SPECIAL: "special";
2683
+ export const NAMESPACE_FILTER_ALL_PREFIX: "all";
2684
+ export const NAMESPACE_FILTER_NS_PREFIX: "ns";
2685
+ export const NAMESPACE_FILTER_P_PREFIX: "project";
2686
+ export const NAMESPACE_FILTER_NS_FULL_PREFIX: string;
2687
+ export const NAMESPACE_FILTER_P_FULL_PREFIX: string;
2659
2688
  export const NAMESPACE_FILTER_ALL: "all";
2660
- export const NAMESPACE_FILTER_ALL_SYSTEM: "all://system";
2661
- export const NAMESPACE_FILTER_ALL_USER: "all://user";
2662
- export const NAMESPACE_FILTER_ALL_ORPHANS: "all://orphans";
2689
+ export const NAMESPACE_FILTER_ALL_SYSTEM: string;
2690
+ export const NAMESPACE_FILTER_ALL_USER: string;
2691
+ export const NAMESPACE_FILTER_ALL_ORPHANS: string;
2663
2692
  export const NAMESPACE_FILTER_NAMESPACED_PREFIX: "namespaced://";
2664
2693
  export const NAMESPACE_FILTER_NAMESPACED_YES: "namespaced://true";
2665
2694
  export const NAMESPACE_FILTER_NAMESPACED_NO: "namespaced://false";
2695
+ export namespace NAMESPACE_FILTER_KINDS {
2696
+ const DIVIDER: string;
2697
+ const PROJECT: string;
2698
+ const NAMESPACE: string;
2699
+ const SPECIAL: string;
2700
+ }
2666
2701
  export function createNamespaceFilterKey(clusterId: any, product: any): any;
2667
2702
  export function createNamespaceFilterKeyWithId(clusterId: any, productId: any): string;
2668
2703
  export function splitNamespaceFilterKey(key: any): {
@@ -2926,8 +2961,8 @@ export function simplify(matchExpressionsInput: any): {
2926
2961
  matchLabels: {};
2927
2962
  matchExpressions: any[];
2928
2963
  };
2929
- export function matches(obj: any, selector: any): boolean;
2930
- export function matching(ary: any, selector: any): any;
2964
+ export function matches(obj: any, selector: any, labelKey?: string): boolean;
2965
+ export function matching(ary: any, selector: any, labelKey: any): any;
2931
2966
  }
2932
2967
 
2933
2968
  // @shell/utils/socket
@@ -0,0 +1,63 @@
1
+ import {
2
+ getBlockDescriptor,
3
+ dumpBlock,
4
+ } from '@shell/utils/create-yaml';
5
+
6
+ const key = 'example';
7
+ const randomData = '\n foo\n bar\n';
8
+
9
+ const scalarStyles = ['>', '|'];
10
+ const chomping = ['+', '-', ''];
11
+ const indentations = ['4', '2', ''];
12
+
13
+ describe('fx: getBlockDescriptor', () => {
14
+ describe('should parse blocks header for all block indicators combo', () => {
15
+ scalarStyles.forEach((scalar) => {
16
+ chomping.forEach((chomping) => {
17
+ indentations.forEach((indentation) => {
18
+ const combo = `${ scalar }${ indentation }${ chomping }`;
19
+
20
+ it(`combo: ${ combo }`, () => {
21
+ const toParse = `${ key }: ${ combo }${ randomData }`;
22
+
23
+ const desc = getBlockDescriptor(toParse, key);
24
+
25
+ expect(desc?.header).toBe(`${ key }: ${ combo }`);
26
+ expect(desc?.indentation).toBe(indentation);
27
+ });
28
+ });
29
+ });
30
+ });
31
+ });
32
+ });
33
+
34
+ describe('fx: dumpBlock', () => {
35
+ describe('should create a data block replacing indicators with blocks indicator from options', () => {
36
+ const key = 'example';
37
+
38
+ scalarStyles.forEach((scalarStyle) => {
39
+ chomping.forEach((chomping) => {
40
+ const options = {
41
+ [key]: {
42
+ chomping,
43
+ scalarStyle
44
+ }
45
+ };
46
+
47
+ it(`options: { scalarStyle: ${ scalarStyle }, chomping: ${ chomping } } with indentation`, () => {
48
+ const data = { [key]: ' foo \n bar \n \n foo\n bar\n ' };
49
+ const block = dumpBlock(data, options);
50
+
51
+ expect(block.includes(`example: ${ scalarStyle }${ chomping }2`)).toBeTruthy();
52
+ });
53
+
54
+ it(`options: { scalarStyle: ${ scalarStyle }, chomping: ${ chomping } } without indentation`, () => {
55
+ const data = { [key]: 'foo \nbar \n\nfoo\nbar\n ' };
56
+ const block = dumpBlock(data, options);
57
+
58
+ expect(block.includes(`example: ${ scalarStyle }${ chomping }`)).toBeTruthy();
59
+ });
60
+ });
61
+ });
62
+ });
63
+ });
package/utils/array.ts CHANGED
@@ -188,6 +188,10 @@ export function uniq<T>(ary: T[]): T[] {
188
188
  return out;
189
189
  }
190
190
 
191
+ export function concatStrings(a: string[], b: string[]) {
192
+ return [...a.map(aa => b.map(bb => aa.concat(bb)))].reduce((acc, arr) => [...arr, ...acc], []);
193
+ }
194
+
191
195
  interface KubeResource { metadata: { labels: { [name: string]: string} } } // Migrate to central kube types resource when those are brought in
192
196
  export function getUniqueLabelKeys<T extends KubeResource>(aryResources: T[]): string[] {
193
197
  const uniqueObj = aryResources.reduce((res, r) => {
@@ -65,7 +65,26 @@ export const ACTIVELY_REMOVE = [
65
65
 
66
66
  const INDENT = 2;
67
67
 
68
- export function createYaml(schemas, type, data, processAlwaysAdd = true, depth = 0, path = '', rootType = null) {
68
+ export function createYamlWithOptions(schemas, type, data, options) {
69
+ return createYaml(
70
+ schemas,
71
+ type,
72
+ data,
73
+ true, 0, '', null,
74
+ options
75
+ );
76
+ }
77
+
78
+ export function createYaml(
79
+ schemas,
80
+ type,
81
+ data,
82
+ processAlwaysAdd = true,
83
+ depth = 0,
84
+ path = '',
85
+ rootType = null,
86
+ dataOptions = {}
87
+ ) {
69
88
  const schema = findBy(schemas, 'id', type);
70
89
 
71
90
  if ( !rootType ) {
@@ -220,25 +239,25 @@ export function createYaml(schemas, type, data, processAlwaysAdd = true, depth =
220
239
  if (data[key]) {
221
240
  try {
222
241
  const cleaned = cleanUp(data);
223
- const parsedData = jsyaml.dump(cleaned[key]);
242
+ const parsedData = dumpBlock(cleaned[key], dataOptions[key]);
224
243
 
225
- out += `\n${ indent(parsedData.trim()) }`;
244
+ out += `\n${ indent(parsedData) }`;
226
245
  } catch (e) {
227
246
  console.error(`Error: Unable to parse map data for yaml of type: ${ type }`, e); // eslint-disable-line no-console
228
247
  }
229
248
  }
230
249
 
231
250
  if ( SIMPLE_TYPES.includes(mapOf) ) {
232
- out += `\n# key: ${ mapOf }`;
251
+ out += `# key: ${ mapOf }`;
233
252
  } else {
234
253
  // If not a simple type ie some sort of object/array, recusively build out commented fields (note data = null here) per the type's (mapOf's) schema
235
- const chunk = createYaml(schemas, mapOf, null, processAlwaysAdd, depth + 1, (path ? `${ path }.${ key }` : key), rootType);
254
+ const chunk = createYaml(schemas, mapOf, null, processAlwaysAdd, depth + 1, (path ? `${ path }.${ key }` : key), rootType, dataOptions);
236
255
  let indented = indent(chunk);
237
256
 
238
257
  // convert "# foo" to "#foo"
239
258
  indented = indented.replace(/^(#)?\s\s\s\s/, '$1');
240
259
 
241
- out += `\n${ indented }`;
260
+ out += `${ indented }`;
242
261
  }
243
262
 
244
263
  return out;
@@ -263,7 +282,7 @@ export function createYaml(schemas, type, data, processAlwaysAdd = true, depth =
263
282
  if ( SIMPLE_TYPES.includes(arrayOf) ) {
264
283
  out += `\n# - ${ arrayOf }`;
265
284
  } else {
266
- const chunk = createYaml(schemas, arrayOf, null, false, depth + 1, (path ? `${ path }.${ key }` : key), rootType);
285
+ const chunk = createYaml(schemas, arrayOf, null, false, depth + 1, (path ? `${ path }.${ key }` : key), rootType, dataOptions);
267
286
  let indented = indent(chunk, 2);
268
287
 
269
288
  // turn "# foo" into "# - foo"
@@ -318,7 +337,7 @@ export function createYaml(schemas, type, data, processAlwaysAdd = true, depth =
318
337
  let chunk;
319
338
 
320
339
  if (subDef?.resourceFields && !isEmpty(subDef?.resourceFields)) {
321
- chunk = createYaml(schemas, type, data[key], processAlwaysAdd, depth + 1, (path ? `${ path }.${ key }` : key), rootType);
340
+ chunk = createYaml(schemas, type, data[key], processAlwaysAdd, depth + 1, (path ? `${ path }.${ key }` : key), rootType, dataOptions);
322
341
  } else if (data[key]) {
323
342
  // if there are no fields defined on the schema but there are in the data, just format data as yaml and add to output yaml
324
343
  try {
@@ -351,6 +370,43 @@ function serializeSimpleValue(data) {
351
370
  return jsyaml.dump(data).trim();
352
371
  }
353
372
 
373
+ export function getBlockDescriptor(value, key) {
374
+ const header = getBlockHeader(value, key);
375
+
376
+ return {
377
+ header,
378
+ indentation: getBlockIndentation(header),
379
+ };
380
+ }
381
+
382
+ /**
383
+ *
384
+ * @param {string} value the block of text to be parsed
385
+ * @param {*} blockKey the key of the block
386
+ * @returns the key + the block scalar indicators, see https://yaml-multiline.info - Block Scalars
387
+ */
388
+ function getBlockHeader(value, blockKey) {
389
+ const card = `(${ blockKey })[\\:][\\s|\\t]+[\\|\\>][\\d]*[\\-\\+]?`;
390
+ const re = new RegExp(card, 'gi');
391
+
392
+ const found = value.match(re);
393
+
394
+ return found?.[0] || '';
395
+ }
396
+
397
+ /**
398
+ *
399
+ * @param {string} blockHeader the key + the block scalar indicators
400
+ * @returns the indentation indicator from the block header, see https://yaml-multiline.info - Indentation
401
+ */
402
+ function getBlockIndentation(blockHeader) {
403
+ const blockScalars = blockHeader.substr(blockHeader.indexOf(':') + 1);
404
+
405
+ const indentation = blockScalars.match(/\d+/);
406
+
407
+ return indentation?.[0] || '';
408
+ }
409
+
354
410
  export function typeRef(type, str) {
355
411
  const re = new RegExp(`^${ type }\\[(.*)\\]$`);
356
412
  const match = str.match(re);
@@ -381,3 +437,44 @@ export function saferDump(obj) {
381
437
 
382
438
  return out;
383
439
  }
440
+
441
+ /**
442
+ * Handles newlines indicators in the multiline blocks.
443
+ *
444
+ * this is required since jsyaml.dump doesn't support chomping and scalar style at the moment.
445
+ * see: https://github.com/nodeca/js-yaml/issues/171
446
+ *
447
+ * @param {*} data the multiline block
448
+ * @param {*} options blocks indicators, see: https://yaml-multiline.info
449
+ *
450
+ * - scalarStyle:
451
+ * one of '|', '>'
452
+ * default '|'
453
+ * - chomping:
454
+ * one of: null, '', '-', '+'
455
+ * default: null
456
+ * @returns the result of jsyaml.dump with the addition of multiline indicators
457
+ */
458
+ export function dumpBlock(data, options = {}) {
459
+ const parsed = jsyaml.dump(data);
460
+
461
+ let out = parsed;
462
+
463
+ const blockFields = Object.keys(data).filter(k => data[k].includes('\n'));
464
+
465
+ if (blockFields.length) {
466
+ for (const key of blockFields) {
467
+ const { header, indentation } = getBlockDescriptor(out, key);
468
+
469
+ const scalarStyle = options[key]?.scalarStyle ?? '|';
470
+ const chomping = options[key]?.chomping ?? '';
471
+
472
+ /**
473
+ * Replace the original block indicators with the ones provided in the options param
474
+ */
475
+ out = out.replace(header, `${ key }: ${ scalarStyle }${ chomping }${ indentation }`);
476
+ }
477
+ }
478
+
479
+ return out;
480
+ }
@@ -1,13 +1,25 @@
1
- export const NAMESPACE_FILTER_SPECIAL = 'special';
1
+ export const NAMESPACE_FILTER_ALL_PREFIX = 'all';
2
+ export const NAMESPACE_FILTER_NS_PREFIX = 'ns';
3
+ export const NAMESPACE_FILTER_P_PREFIX = 'project';
2
4
 
3
- export const NAMESPACE_FILTER_ALL = 'all';
4
- export const NAMESPACE_FILTER_ALL_SYSTEM = 'all://system';
5
- export const NAMESPACE_FILTER_ALL_USER = 'all://user';
6
- export const NAMESPACE_FILTER_ALL_ORPHANS = 'all://orphans';
5
+ export const NAMESPACE_FILTER_NS_FULL_PREFIX = `${ NAMESPACE_FILTER_NS_PREFIX }://`;
6
+ export const NAMESPACE_FILTER_P_FULL_PREFIX = `${ NAMESPACE_FILTER_P_PREFIX }://`;
7
+
8
+ export const NAMESPACE_FILTER_ALL = NAMESPACE_FILTER_ALL_PREFIX;
9
+ export const NAMESPACE_FILTER_ALL_SYSTEM = `${ NAMESPACE_FILTER_ALL_PREFIX }://system`;
10
+ export const NAMESPACE_FILTER_ALL_USER = `${ NAMESPACE_FILTER_ALL_PREFIX }://user`;
11
+ export const NAMESPACE_FILTER_ALL_ORPHANS = `${ NAMESPACE_FILTER_ALL_PREFIX }://orphans`;
7
12
  export const NAMESPACE_FILTER_NAMESPACED_PREFIX = 'namespaced://';
8
13
  export const NAMESPACE_FILTER_NAMESPACED_YES = 'namespaced://true';
9
14
  export const NAMESPACE_FILTER_NAMESPACED_NO = 'namespaced://false';
10
15
 
16
+ export const NAMESPACE_FILTER_KINDS = {
17
+ DIVIDER: 'divider',
18
+ PROJECT: 'project',
19
+ NAMESPACE: 'namespace',
20
+ SPECIAL: 'special'
21
+ };
22
+
11
23
  const SEPARATOR = '__%%__';
12
24
 
13
25
  export const createNamespaceFilterKey = (clusterId, product) => {
@@ -0,0 +1,62 @@
1
+ import { NAMESPACE_FILTER_NS_FULL_PREFIX, NAMESPACE_FILTER_P_FULL_PREFIX } from '@shell/utils/namespace-filter';
2
+ import { getPerformanceSetting } from '@shell/utils/settings';
3
+
4
+ type Opt = { [key: string]: any, namespaced?: string[]}
5
+
6
+ class ProjectAndNamespaceFiltering {
7
+ static param = 'projectsornamespaces'
8
+
9
+ /**
10
+ * Does the request `opt` definition require resources are fetched from a specific set namespaces/projects?
11
+ */
12
+ isApplicable(opt: Opt): boolean {
13
+ return Array.isArray(opt.namespaced);
14
+ }
15
+
16
+ isEnabled(rootGetters: any): boolean {
17
+ const currentProduct = rootGetters['currentProduct'];
18
+
19
+ // Only enable for the cluster store at the moment. In theory this should work in management as well, as they're both 'steve' stores
20
+ if (currentProduct?.inStore !== 'cluster') {
21
+ return false;
22
+ }
23
+
24
+ if (currentProduct?.showWorkspaceSwitcher) {
25
+ return false;
26
+ }
27
+
28
+ const perfConfig = getPerformanceSetting(rootGetters);
29
+
30
+ if (!perfConfig.forceNsFilterV2?.enabled) {
31
+ return false;
32
+ }
33
+
34
+ return true;
35
+ }
36
+
37
+ /**
38
+ * Check if `opt` requires resources from specific ns/projects, if so return the required query param (x=y)
39
+ */
40
+ checkAndCreateParam(opt: Opt): string {
41
+ if (!this.isApplicable(opt)) {
42
+ return '';
43
+ }
44
+
45
+ return this.createParam(opt.namespaced);
46
+ }
47
+
48
+ private createParam(namespaceFilter: string[] | undefined): string {
49
+ if (!namespaceFilter || !namespaceFilter.length) {
50
+ return '';
51
+ }
52
+
53
+ const projectsOrNamespaces = namespaceFilter
54
+ .map(f => f.replace(NAMESPACE_FILTER_NS_FULL_PREFIX, '')
55
+ .replace(NAMESPACE_FILTER_P_FULL_PREFIX, ''))
56
+ .join(',');
57
+
58
+ return `${ ProjectAndNamespaceFiltering.param }=${ projectsOrNamespaces }`;
59
+ }
60
+ }
61
+
62
+ export default new ProjectAndNamespaceFiltering();
package/utils/selector.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isArray, addObject, findBy } from '@shell/utils/array';
2
+ import { get } from '@shell/utils/object';
2
3
 
3
4
  const parseCache = {};
4
5
 
@@ -121,7 +122,7 @@ export function convert(matchLabelsObj, matchExpressions) {
121
122
  out.push({
122
123
  key,
123
124
  operator: 'In',
124
- values: [value],
125
+ values: isArray(value) ? value : [value],
125
126
  });
126
127
  }
127
128
  }
@@ -162,7 +163,7 @@ export function simplify(matchExpressionsInput) {
162
163
  return { matchLabels, matchExpressions };
163
164
  }
164
165
 
165
- export function matches(obj, selector) {
166
+ export function matches(obj, selector, labelKey = 'metadata.labels') {
166
167
  let rules = [];
167
168
 
168
169
  if ( typeof selector === 'string' ) {
@@ -178,7 +179,7 @@ export function matches(obj, selector) {
178
179
  return false;
179
180
  }
180
181
 
181
- const labels = obj?.metadata?.labels || {};
182
+ const labels = get(obj, labelKey) || {};
182
183
 
183
184
  for ( const rule of rules ) {
184
185
  const value = labels[rule.key];
@@ -222,6 +223,6 @@ export function matches(obj, selector) {
222
223
  return true;
223
224
  }
224
225
 
225
- export function matching(ary, selector) {
226
- return ary.filter(obj => matches(obj, selector));
226
+ export function matching(ary, selector, labelKey) {
227
+ return ary.filter(obj => matches(obj, selector, labelKey));
227
228
  }
package/utils/settings.ts CHANGED
@@ -22,6 +22,18 @@ export const fetchOrCreateSetting = async(store: Store<any>, id: string, val: st
22
22
  return setting;
23
23
  };
24
24
 
25
+ /**
26
+ * Fetch a specific setting that might not exist
27
+ * We fetch all settings - reality is Rancher will have done this already, so there's no overhead in doing
28
+ * this - but if we fetch a specific setting that does not exist, we will get a 404, which we don't want
29
+ */
30
+ export const fetchSetting = async(store: Store<any>, id: string) => {
31
+ const all = await store.dispatch('management/findAll', { type: MANAGEMENT.SETTING });
32
+ const setting = (all || []).find((setting: any) => setting.id === id);
33
+
34
+ return setting;
35
+ };
36
+
25
37
  export const setSetting = async(store: Store<any>, id: string, val: string): Promise<any> => {
26
38
  const setting = await fetchOrCreateSetting(store, id, val, false);
27
39
 
@@ -32,19 +44,17 @@ export const setSetting = async(store: Store<any>, id: string, val: string): Pro
32
44
  };
33
45
 
34
46
  export const getPerformanceSetting = (rootGetters: Record<string, (arg0: string, arg1: string) => any>) => {
35
- const perfSetting = rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_PERFORMANCE);
36
- let perfConfig = {};
47
+ const perfSettingResource = rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_PERFORMANCE);
48
+ let perfSetting = {};
37
49
 
38
- if (perfSetting && perfSetting.value) {
50
+ if (perfSettingResource?.value) {
39
51
  try {
40
- perfConfig = JSON.parse(perfSetting.value);
52
+ perfSetting = JSON.parse(perfSettingResource.value);
41
53
  } catch (e) {
42
54
  console.warn('ui-performance setting contains invalid data'); // eslint-disable-line no-console
43
55
  }
44
56
  }
45
57
 
46
58
  // Start with the default and overwrite the values from the setting - ensures we have defaults for newly added options
47
- perfConfig = Object.assign(DEFAULT_PERF_SETTING, perfConfig);
48
-
49
- return perfConfig;
59
+ return Object.assign(DEFAULT_PERF_SETTING, perfSetting || {});
50
60
  };
package/vue.config.js CHANGED
@@ -408,8 +408,8 @@ module.exports = function(dir, _appConfig) {
408
408
  }),
409
409
  }));
410
410
 
411
- // The static assets need to be in the built public folder in order to get served (primarily the favicon for now)
412
- config.plugins.push(new CopyWebpackPlugin([{ from: path.join(SHELL_ABS, 'static'), to: 'public' }]));
411
+ // The static assets need to be in the built assets directory in order to get served (primarily the favicon)
412
+ config.plugins.push(new CopyWebpackPlugin([{ from: path.join(SHELL_ABS, 'static'), to: '.' }]));
413
413
 
414
414
  config.resolve.extensions.push(...['.tsx', '.ts', '.js', '.vue', '.scss']);
415
415
  config.watchOptions = config.watchOptions || {};
@@ -1,93 +0,0 @@
1
- import Vue from 'vue';
2
- import SteveModel from '@shell/plugins/steve/steve-class';
3
- import { HCI } from '@shell/config/labels-annotations';
4
-
5
- export default class NetworkAttachmentDef extends SteveModel {
6
- get _availableActions() {
7
- let out = super._availableActions;
8
- const toFilter = ['goToClone', 'cloneYaml', 'goToViewConfig', 'goToEditYaml', 'goToEdit'];
9
-
10
- out = out.filter((action) => {
11
- if (!toFilter.includes(action.action)) {
12
- return action;
13
- }
14
- });
15
-
16
- return out;
17
- }
18
-
19
- applyDefaults() {
20
- const spec = this.spec || {
21
- config: JSON.stringify({
22
- cniVersion: '0.3.1',
23
- name: '',
24
- type: 'bridge',
25
- bridge: 'harvester-br0',
26
- promiscMode: true,
27
- vlan: '',
28
- ipam: {}
29
- })
30
- };
31
-
32
- Vue.set(this, 'spec', spec);
33
- }
34
-
35
- get parseConfig() {
36
- try {
37
- return JSON.parse(this.spec.config) || {};
38
- } catch (err) {
39
- return {};
40
- }
41
- }
42
-
43
- get isIpamStatic() {
44
- return this.parseConfig.ipam?.type === 'static';
45
- }
46
-
47
- get vlanType() {
48
- const type = this.parseConfig.type;
49
-
50
- return type === 'bridge' ? 'L2VlanNetwork' : type;
51
- }
52
-
53
- get vlanId() {
54
- return this.parseConfig.vlan;
55
- }
56
-
57
- get customValidationRules() {
58
- const rules = [
59
- {
60
- nullable: false,
61
- path: 'metadata.name',
62
- required: true,
63
- minLength: 1,
64
- maxLength: 63,
65
- translationKey: 'harvester.fields.name'
66
- }
67
- ];
68
-
69
- return rules;
70
- }
71
-
72
- get connectivity() {
73
- const annotations = this.metadata?.annotations || {};
74
- const route = annotations[HCI.NETWORK_ROUTE];
75
- let config = {};
76
-
77
- try {
78
- config = JSON.parse(route || '{}');
79
- } catch {
80
- return 'invalid';
81
- }
82
-
83
- const connectivity = config.connectivity;
84
-
85
- if (connectivity === 'false') {
86
- return 'inactive';
87
- } else if (connectivity === 'true') {
88
- return 'active';
89
- } else {
90
- return connectivity;
91
- }
92
- }
93
- }