@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
@@ -23,7 +23,7 @@ describe('the ArrayList', () => {
23
23
  initialEmptyRow: true
24
24
  },
25
25
  });
26
- const arrayListBoxes = wrapper.findAll('[data-testid="array-list-box"]');
26
+ const arrayListBoxes = wrapper.findAll('[data-testid^="array-list-box"]');
27
27
 
28
28
  expect(arrayListBoxes).toHaveLength(1);
29
29
  });
@@ -40,7 +40,7 @@ describe('the ArrayList', () => {
40
40
 
41
41
  await arrayListButton.click();
42
42
  await arrayListButton.click();
43
- const arrayListBoxes = wrapper.findAll('[data-testid="array-list-box"]');
43
+ const arrayListBoxes = wrapper.findAll('[data-testid^="array-list-box"]');
44
44
 
45
45
  expect(arrayListBoxes).toHaveLength(2);
46
46
  });
@@ -55,7 +55,7 @@ describe('the ArrayList', () => {
55
55
  const deleteButton = wrapper.get('[data-testid^="remove-item"]').element as HTMLElement;
56
56
 
57
57
  await deleteButton.click();
58
- const arrayListBoxes = wrapper.findAll('[data-testid="array-list-box"]');
58
+ const arrayListBoxes = wrapper.findAll('[data-testid^="array-list-box"]');
59
59
 
60
60
  expect(arrayListBoxes).toHaveLength(1);
61
61
  });
@@ -15,4 +15,21 @@ describe('component: KeyValue', () => {
15
15
 
16
16
  expect(inputValue.value).toBe(value);
17
17
  });
18
+
19
+ it('should display a markdown-multiline field with new lines visible', () => {
20
+ const wrapper = mount(KeyValue, {
21
+ propsData: {
22
+ value: 'test',
23
+ valueMarkdownMultiline: true,
24
+ },
25
+ mocks: { $store: { getters: { 'i18n/t': jest.fn() } } },
26
+ directives: { t }
27
+ });
28
+
29
+ const inputFieldTextArea = wrapper.find('textarea').element;
30
+ const inputFieldMultiline = wrapper.find('[data-testid="code-mirror-multiline-field"]').element;
31
+
32
+ expect(inputFieldTextArea).toBeUndefined();
33
+ expect(inputFieldMultiline).toBeDefined();
34
+ });
18
35
  });
@@ -21,7 +21,7 @@ describe('component: MatchExpressions', () => {
21
21
 
22
22
  const inputWraps = wrapper.findAll('[data-testid^=input-match-expression-]');
23
23
 
24
- expect(inputWraps).toHaveLength(3);
24
+ expect(inputWraps).toHaveLength(8);
25
25
  });
26
26
 
27
27
  it.each([
@@ -64,17 +64,17 @@ export default {
64
64
  <span v-else>{{ value }}</span>
65
65
  <i
66
66
  v-if="row.unavailableMachines"
67
- v-tooltip="row.unavailableMachines"
67
+ v-clean-tooltip="row.unavailableMachines"
68
68
  class="conditions-alert-icon icon-alert icon"
69
69
  />
70
70
  <i
71
71
  v-if="row.rkeTemplateUpgrade"
72
- v-tooltip="t('cluster.rkeTemplateUpgrade', { name: row.rkeTemplateUpgrade })"
72
+ v-clean-tooltip="t('cluster.rkeTemplateUpgrade', { name: row.rkeTemplateUpgrade })"
73
73
  class="template-upgrade-icon icon-alert icon"
74
74
  />
75
75
  <i
76
76
  v-if="row.hasError"
77
- v-tooltip="{ content: `<div>${formattedConditions}</div>`, html: true }"
77
+ v-clean-tooltip="{ content: `<div>${formattedConditions}</div>`, html: true }"
78
78
  class="conditions-alert-icon icon-error icon-lg"
79
79
  />
80
80
  </span>
@@ -135,7 +135,7 @@ export default {
135
135
  </span>
136
136
  <span
137
137
  v-else-if="showTooltip"
138
- v-tooltip="{content: title, placement: tooltipPlacement}"
138
+ v-clean-tooltip="{content: title, placement: tooltipPlacement}"
139
139
  class="live-date"
140
140
  >
141
141
  {{ suffixedLabel }}
@@ -59,7 +59,7 @@ export default {
59
59
  <span>{{ mainImage }}</span><br>
60
60
  <span
61
61
  v-if="images.length-1>0"
62
- v-tooltip.bottom="imageLabels"
62
+ v-clean-tooltip.bottom="imageLabels"
63
63
  class="plus-more"
64
64
  >{{ t('generic.plusMore', {n:images.length-1}) }}</span>
65
65
  </span>
@@ -14,7 +14,7 @@ export default {
14
14
  <span>{{ value.displayName }}</span>
15
15
  <i
16
16
  v-if="value.upgrade"
17
- v-tooltip="t('cluster.rkeTemplateUpgrade', { name: value.upgrade })"
17
+ v-clean-tooltip="t('cluster.rkeTemplateUpgrade', { name: value.upgrade })"
18
18
  class="template-upgrade-icon icon-alert icon"
19
19
  />
20
20
  </div>
@@ -45,7 +45,7 @@ export default {
45
45
  </span>
46
46
  <span
47
47
  v-else-if="showTooltip"
48
- v-tooltip="{content: longValue, placement: tooltipPlacement}"
48
+ v-clean-tooltip="{content: longValue, placement: tooltipPlacement}"
49
49
  >
50
50
  {{ value }}
51
51
  </span>
@@ -360,7 +360,7 @@ export default {
360
360
  >
361
361
  <div
362
362
  v-if="currentProduct && currentProduct.showClusterSwitcher"
363
- v-tooltip="nameTooltip"
363
+ v-clean-tooltip="nameTooltip"
364
364
  class="cluster cluster-clipped"
365
365
  >
366
366
  <div
@@ -455,7 +455,7 @@ export default {
455
455
  <template v-if="currentProduct && currentProduct.showClusterSwitcher">
456
456
  <button
457
457
  v-if="showImportYaml"
458
- v-tooltip="t('nav.import')"
458
+ v-clean-tooltip="t('nav.import')"
459
459
  :disabled="!importEnabled"
460
460
  type="button"
461
461
  class="btn header-btn role-tertiary"
@@ -478,7 +478,7 @@ export default {
478
478
 
479
479
  <button
480
480
  v-if="showKubeShell"
481
- v-tooltip="t('nav.shellShortcut', {key: shellShortcut})"
481
+ v-clean-tooltip="t('nav.shellShortcut', {key: shellShortcut})"
482
482
  v-shortkey="{windows: ['ctrl', '`'], mac: ['meta', '`']}"
483
483
  :disabled="!shellEnabled"
484
484
  type="button"
@@ -491,7 +491,7 @@ export default {
491
491
 
492
492
  <button
493
493
  v-if="showKubeConfig"
494
- v-tooltip="t('nav.kubeconfig.download')"
494
+ v-clean-tooltip="t('nav.kubeconfig.download')"
495
495
  :disabled="!kubeConfigEnabled"
496
496
  type="button"
497
497
  class="btn header-btn role-tertiary"
@@ -502,7 +502,7 @@ export default {
502
502
 
503
503
  <button
504
504
  v-if="showCopyConfig"
505
- v-tooltip="t('nav.kubeconfig.copy')"
505
+ v-clean-tooltip="t('nav.kubeconfig.copy')"
506
506
  :disabled="!kubeConfigEnabled"
507
507
  type="button"
508
508
  class="btn header-btn role-tertiary"
@@ -521,7 +521,7 @@ export default {
521
521
 
522
522
  <button
523
523
  v-if="showSearch"
524
- v-tooltip="t('nav.resourceSearch.toolTip', {key: searchShortcut})"
524
+ v-clean-tooltip="t('nav.resourceSearch.toolTip', {key: searchShortcut})"
525
525
  v-shortkey="{windows: ['ctrl', 'k'], mac: ['meta', 'k']}"
526
526
  type="button"
527
527
  class="btn header-btn role-tertiary"
@@ -549,7 +549,7 @@ export default {
549
549
  <button
550
550
  v-for="action, i in extensionHeaderActions"
551
551
  :key="`${action.label}${i}`"
552
- v-tooltip="handleExtensionTooltip(action)"
552
+ v-clean-tooltip="handleExtensionTooltip(action)"
553
553
  v-shortkey="action.shortcutKey"
554
554
  :disabled="action.enabled ? !action.enabled(ctx) : false"
555
555
  type="button"
@@ -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
@@ -5,7 +5,6 @@ import { NAMESPACE, MANAGEMENT } from '@shell/config/types';
5
5
  import { sortBy } from '@shell/utils/sort';
6
6
  import { isArray, addObjects, findBy, filterBy } from '@shell/utils/array';
7
7
  import {
8
- NAMESPACE_FILTER_SPECIAL as SPECIAL,
9
8
  NAMESPACE_FILTER_ALL_USER as ALL_USER,
10
9
  NAMESPACE_FILTER_ALL as ALL,
11
10
  NAMESPACE_FILTER_ALL_SYSTEM as ALL_SYSTEM,
@@ -13,23 +12,42 @@ import {
13
12
  NAMESPACE_FILTER_NAMESPACED_YES as NAMESPACED_YES,
14
13
  NAMESPACE_FILTER_NAMESPACED_NO as NAMESPACED_NO,
15
14
  createNamespaceFilterKey,
15
+ NAMESPACE_FILTER_KINDS,
16
+ NAMESPACE_FILTER_NS_FULL_PREFIX,
17
+ NAMESPACE_FILTER_P_FULL_PREFIX,
16
18
  } from '@shell/utils/namespace-filter';
17
19
  import { KEY } from '@shell/utils/platform';
20
+ import pAndNFiltering from '@shell/utils/projectAndNamespaceFiltering.utils';
21
+ import { SETTING } from '@shell/config/settings';
22
+
23
+ const forcedNamespaceValidTypes = [NAMESPACE_FILTER_KINDS.DIVIDER, NAMESPACE_FILTER_KINDS.PROJECT, NAMESPACE_FILTER_KINDS.NAMESPACE];
18
24
 
19
25
  export default {
26
+
20
27
  data() {
21
28
  return {
22
- isOpen: false,
23
- filter: '',
24
- hidden: 0,
25
- total: 0,
26
- activeElement: null,
27
- cachedFiltered: [],
29
+ isOpen: false,
30
+ filter: '',
31
+ hidden: 0,
32
+ total: 0,
33
+ activeElement: null,
34
+ cachedFiltered: [],
35
+ NAMESPACE_FILTER_KINDS,
36
+ namespaceFilterMode: undefined,
28
37
  };
29
38
  },
30
39
 
40
+ async fetch() {
41
+ // Determine if filtering by specific namespaces/projects is required
42
+ // This is done once and up front
43
+ // - it doesn't need to be re-active
44
+ // - added it as a computed caused massive amounts of churn around the `filtered` watcher
45
+ await this.$store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.UI_PERFORMANCE });
46
+ this.namespaceFilterMode = this.calcNamespaceFilterMode();
47
+ },
48
+
31
49
  computed: {
32
- ...mapGetters(['currentProduct', 'namespaceFilterMode']),
50
+ ...mapGetters(['currentProduct']),
33
51
 
34
52
  hasFilter() {
35
53
  return this.filter.length > 0;
@@ -40,23 +58,27 @@ export default {
40
58
 
41
59
  out = out.filter((item) => {
42
60
  // Filter out anything not applicable to singleton selection
43
- if (this.namespaceFilterMode) {
61
+ if (this.namespaceFilterMode?.length) {
44
62
  // We always show dividers, projects and namespaces
45
- if (!['divider', 'project', this.namespaceFilterMode].includes(item.kind)) {
46
- // Hide any invalid option that's not selected
47
- return this.value.findIndex(v => v.id === item.id) >= 0;
63
+ if (!forcedNamespaceValidTypes.includes(item.kind)) {
64
+ const validCustomType = this.namespaceFilterMode.find(prefix => item.kind.startsWith(prefix));
65
+
66
+ if (!validCustomType) {
67
+ // Hide any invalid option that's not selected
68
+ return this.value.findIndex(v => v.id === item.id) >= 0;
69
+ }
48
70
  }
49
71
  }
50
72
 
51
73
  // Filter by the current filter
52
74
  if (this.hasFilter) {
53
- return item.kind !== SPECIAL && item.label.toLowerCase().includes(this.filter.toLowerCase());
75
+ return item.kind !== NAMESPACE_FILTER_KINDS.SPECIAL && item.label.toLowerCase().includes(this.filter.toLowerCase());
54
76
  }
55
77
 
56
78
  return true;
57
79
  });
58
80
 
59
- if (out?.[0]?.kind === 'divider') {
81
+ if (out?.[0]?.kind === NAMESPACE_FILTER_KINDS.DIVIDER) {
60
82
  out.splice(0, 1);
61
83
  }
62
84
 
@@ -70,8 +92,15 @@ export default {
70
92
  out.forEach((i) => {
71
93
  i.selected = !!mapped[i.id] || (i.id === ALL && this.value && this.value.length === 0);
72
94
  i.elementId = (i.id || '').replace('://', '_');
73
- // Are we in singleton resource type mode, if so is this an allowed type?
74
- i.enabled = !this.namespaceFilterMode || i.kind === this.namespaceFilterMode;
95
+ i.enabled = true;
96
+ // Are we in restricted resource type mode, if so is this an allowed type?
97
+ if (this.namespaceFilterMode?.length) {
98
+ const isLastSelected = i.selected && (i.id === ALL || this.value.length === 1);
99
+ const kindAllowed = this.namespaceFilterMode.find(f => f === i.kind);
100
+ const isNotInProjectGroup = i.id === ALL_ORPHANS;
101
+
102
+ i.enabled = (!isLastSelected && kindAllowed) && !isNotInProjectGroup;
103
+ }
75
104
  });
76
105
 
77
106
  return out;
@@ -134,27 +163,27 @@ export default {
134
163
  out = [
135
164
  {
136
165
  id: ALL,
137
- kind: SPECIAL,
166
+ kind: NAMESPACE_FILTER_KINDS.SPECIAL,
138
167
  label: t('nav.ns.all'),
139
168
  },
140
169
  {
141
170
  id: ALL_USER,
142
- kind: SPECIAL,
171
+ kind: NAMESPACE_FILTER_KINDS.SPECIAL,
143
172
  label: t('nav.ns.user'),
144
173
  },
145
174
  {
146
175
  id: ALL_SYSTEM,
147
- kind: SPECIAL,
176
+ kind: NAMESPACE_FILTER_KINDS.SPECIAL,
148
177
  label: t('nav.ns.system'),
149
178
  },
150
179
  {
151
180
  id: NAMESPACED_YES,
152
- kind: SPECIAL,
181
+ kind: NAMESPACE_FILTER_KINDS.SPECIAL,
153
182
  label: t('nav.ns.namespaced'),
154
183
  },
155
184
  {
156
185
  id: NAMESPACED_NO,
157
- kind: SPECIAL,
186
+ kind: NAMESPACE_FILTER_KINDS.SPECIAL,
158
187
  label: t('nav.ns.clusterLevel'),
159
188
  },
160
189
  ];
@@ -224,8 +253,8 @@ export default {
224
253
  }
225
254
 
226
255
  out.push({
227
- id: `project://${ id }`,
228
- kind: 'project',
256
+ id: `${ NAMESPACE_FILTER_P_FULL_PREFIX }${ id }`,
257
+ kind: NAMESPACE_FILTER_KINDS.PROJECT,
229
258
  label: t('nav.ns.project', { name: project.nameDisplay }),
230
259
  });
231
260
 
@@ -243,7 +272,7 @@ export default {
243
272
 
244
273
  out.push({
245
274
  id: ALL_ORPHANS,
246
- kind: 'project',
275
+ kind: NAMESPACE_FILTER_KINDS.PROJECT,
247
276
  label: t('nav.ns.orphan'),
248
277
  disabled: true,
249
278
  });
@@ -265,8 +294,8 @@ export default {
265
294
  out,
266
295
  namespaces.map((namespace) => {
267
296
  return {
268
- id: `ns://${ namespace.id }`,
269
- kind: 'namespace',
297
+ id: `${ NAMESPACE_FILTER_NS_FULL_PREFIX }${ namespace.id }`,
298
+ kind: NAMESPACE_FILTER_KINDS.NAMESPACE,
270
299
  label: t('nav.ns.namespace', { name: namespace.nameDisplay }),
271
300
  };
272
301
  })
@@ -275,7 +304,7 @@ export default {
275
304
 
276
305
  function divider(out) {
277
306
  out.push({
278
- kind: 'divider',
307
+ kind: NAMESPACE_FILTER_KINDS.DIVIDER,
279
308
  label: `Divider ${ out.length }`,
280
309
  disabled: true,
281
310
  });
@@ -283,16 +312,14 @@ export default {
283
312
  },
284
313
 
285
314
  isSingleSpecial() {
286
- return this.value && this.value.length === 1 && this.value[0].kind === 'special';
315
+ return this.value && this.value.length === 1 && this.value[0].kind === NAMESPACE_FILTER_KINDS.SPECIAL;
287
316
  },
288
317
 
289
318
  value: {
290
319
  get() {
291
320
  // Use last picked filter from user preferences
292
321
  const prefs = this.$store.getters['prefs/get'](NAMESPACE_FILTERS);
293
-
294
- const prefDefault = this.currentProduct?.customNamespaceFilter ? [] : [ALL_USER];
295
- const values = prefs && prefs[this.key] ? prefs[this.key] : prefDefault;
322
+ const values = prefs && prefs[this.key] ? prefs[this.key] : this.defaultOption();
296
323
  const options = this.options;
297
324
 
298
325
  // Remove values that are not valid options
@@ -311,7 +338,7 @@ export default {
311
338
  neu = neu.filter(x => !!x.id);
312
339
 
313
340
  const last = neu[neu.length - 1];
314
- const lastIsSpecial = last?.kind === SPECIAL;
341
+ const lastIsSpecial = last?.kind === NAMESPACE_FILTER_KINDS.SPECIAL;
315
342
  const hadUser = !!old.find(x => x.id === ALL_USER);
316
343
  const hadAll = !!old.find(x => x.id === ALL);
317
344
 
@@ -320,7 +347,7 @@ export default {
320
347
  }
321
348
 
322
349
  if (neu.length > 1) {
323
- neu = neu.filter(x => x.kind !== SPECIAL);
350
+ neu = neu.filter(x => x.kind !== NAMESPACE_FILTER_KINDS.SPECIAL);
324
351
  }
325
352
 
326
353
  if (neu.find(x => x.id === 'all')) {
@@ -332,7 +359,7 @@ export default {
332
359
  // If there was something selected and you remove it, go back to user by default
333
360
  // Unless it was user or all
334
361
  if (neu.length === 0 && !hadUser && !hadAll) {
335
- ids = this.currentProduct?.customNamespaceFilter ? [] : [ALL_USER];
362
+ ids = this.defaultOption();
336
363
  } else {
337
364
  ids = neu.map(x => x.id);
338
365
  }
@@ -579,28 +606,27 @@ export default {
579
606
  },
580
607
  selectOption(option) {
581
608
  // Ignore click for a divider
582
- if (option.kind === 'divider') {
609
+ if (option.kind === NAMESPACE_FILTER_KINDS.DIVIDER) {
583
610
  return;
584
611
  }
585
612
 
586
613
  const current = this.value;
587
- const exists = current.findIndex(v => v.id === option.id);
588
- const optionIsSelected = exists !== -1;
589
614
 
590
- // Any type of mode means only a single resource can be selected. So clear out any stale
591
- // values (multiple selected in another context OR a single one selected in this context)
592
- if (this.namespaceFilterMode) {
593
- if (current.length === 1 && optionIsSelected) {
594
- // Don't deselect the only selected option
595
- return;
596
- }
597
- current.length = 0;
615
+ // Remove invalid
616
+ if (!!this.namespaceFilterMode?.length) {
617
+ this.value.forEach((v) => {
618
+ if (!this.namespaceFilterMode.find(f => f === v.kind)) {
619
+ const index = current.findIndex(c => c.id === v.id);
620
+
621
+ current.splice(index, 1);
622
+ }
623
+ });
598
624
  }
599
625
 
600
- const remove = !this.namespaceFilterMode && optionIsSelected;
626
+ const exists = current.findIndex(v => v.id === option.id);
601
627
 
602
628
  // Remove if it exists (or always add if in singleton mode - we've reset the list above)
603
- if (remove) {
629
+ if (exists !== -1) {
604
630
  current.splice(exists, 1);
605
631
  } else {
606
632
  current.push(option);
@@ -624,13 +650,35 @@ export default {
624
650
  this.selectOption(ns);
625
651
  event.preventDefault();
626
652
  event.stopPropagation();
627
- }
653
+ },
654
+
655
+ defaultOption() {
656
+ // Note - This is one place where a default ns/project filter value is provided (ALL_USER)
657
+ // There's also..
658
+ // - dashboard root store `loadCluster` --> when `updateNamespaces` is dispatched
659
+ // - harvester root store `loadCluster` --> when `updateNamespaces` is dispatched (can be discarded)
660
+ // Due to this, we can't really set a nicer default when forced ns/project filtering is on (ALL_USER is invalid)
661
+ if (this.currentProduct?.customNamespaceFilter) {
662
+ return [];
663
+ }
664
+
665
+ return [ALL_USER];
666
+ },
667
+
668
+ calcNamespaceFilterMode() {
669
+ if (pAndNFiltering.isEnabled(this.$store.getters)) {
670
+ return [NAMESPACE_FILTER_KINDS.NAMESPACE, NAMESPACE_FILTER_KINDS.PROJECT];
671
+ }
672
+
673
+ return null;
674
+ },
628
675
  }
629
676
  };
630
677
  </script>
631
678
 
632
679
  <template>
633
680
  <div
681
+ v-if="!$fetchState.pending"
634
682
  class="ns-filter"
635
683
  data-testid="namespaces-filter"
636
684
  tabindex="0"
@@ -674,7 +722,7 @@ export default {
674
722
  <div
675
723
  v-else
676
724
  ref="values"
677
- v-tooltip="tooltip"
725
+ v-clean-tooltip="tooltip"
678
726
  data-testid="namespaces-values"
679
727
  class="ns-values"
680
728
  >
@@ -694,8 +742,9 @@ export default {
694
742
  class="ns-value"
695
743
  >
696
744
  <div>{{ ns.label }}</div>
745
+ <!-- block user from removing the last selection if ns forced filtering is on -->
697
746
  <i
698
- v-if="!namespaceFilterMode"
747
+ v-if="!namespaceFilterMode || value.length > 1"
699
748
  class="icon icon-close"
700
749
  :data-testid="`namespaces-values-close-${j}`"
701
750
  @click="removeOption(ns, $event)"
@@ -708,7 +757,7 @@ export default {
708
757
  <div
709
758
  v-if="hidden > 0"
710
759
  ref="more"
711
- v-tooltip="tooltip"
760
+ v-clean-tooltip="tooltip"
712
761
  class="ns-more"
713
762
  >
714
763
  {{ t('namespaceFilter.more', { more: hidden }) }}
@@ -754,7 +803,7 @@ export default {
754
803
  class="ns-singleton-info"
755
804
  >
756
805
  <i
757
- v-tooltip="t('resourceList.nsFilterToolTip', { mode: namespaceFilterMode})"
806
+ v-clean-tooltip="t('resourceList.nsFilterToolTip')"
758
807
  class="icon icon-info"
759
808
  />
760
809
  </div>
@@ -791,7 +840,7 @@ export default {
791
840
  @keydown="itemKeyHandler($event, opt)"
792
841
  >
793
842
  <div
794
- v-if="opt.kind === 'divider'"
843
+ v-if="opt.kind === NAMESPACE_FILTER_KINDS.DIVIDER"
795
844
  class="ns-divider"
796
845
  />
797
846
  <div
@@ -799,7 +848,7 @@ export default {
799
848
  class="ns-item"
800
849
  >
801
850
  <i
802
- v-if="opt.kind === 'namespace'"
851
+ v-if="opt.kind === NAMESPACE_FILTER_KINDS.NAMESPACE"
803
852
  class="icon icon-folder"
804
853
  />
805
854
  <div>{{ opt.label }}</div>
@@ -130,11 +130,14 @@ export const ANNOTATIONS_TO_FOLD = [
130
130
  ];
131
131
 
132
132
  export const HCI = {
133
- CLOUD_INIT: 'harvesterhci.io/cloud-init-template',
134
- CLOUD_PROVIDER_IPAM: 'cloudprovider.harvesterhci.io/ipam',
135
- NETWORK_ROUTE: 'network.harvesterhci.io/route',
136
- IMAGE_NAME: 'harvesterhci.io/image-name',
137
- NETWORK_TYPE: 'network.harvesterhci.io/type',
133
+ CLOUD_INIT: 'harvesterhci.io/cloud-init-template',
134
+ CLOUD_PROVIDER_IPAM: 'cloudprovider.harvesterhci.io/ipam',
135
+ NETWORK_ROUTE: 'network.harvesterhci.io/route',
136
+ IMAGE_NAME: 'harvesterhci.io/image-name',
137
+ NETWORK_TYPE: 'network.harvesterhci.io/type',
138
+ CLOUD_PROVIDER_NAMESPACE: 'cloudprovider.harvesterhci.io/namespace',
139
+ CLOUD_PROVIDER_NETWORK: 'cloudprovider.harvesterhci.io/network',
140
+ CLOUD_PROVIDER_PROJECT: 'cloudprovider.harvesterhci.io/project',
138
141
  };
139
142
 
140
143
  // Annotations that can be on management.cattle.io.cluster to configure a custom badge
@@ -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
@@ -154,9 +159,6 @@ export const DEFAULT_PERF_SETTING = {
154
159
  },
155
160
  disableWebsocketNotification: true,
156
161
  garbageCollection: GC_DEFAULTS,
157
- forceNsFilter: {
158
- enabled: false,
159
- threshold: 1500,
160
- },
161
- advancedWorker: { enabled: false },
162
+ forceNsFilterV2: { enabled: false },
163
+ advancedWorker: { enabled: false },
162
164
  };
package/config/types.js CHANGED
@@ -182,6 +182,7 @@ export const MANAGEMENT = {
182
182
  GLOBAL_ROLE: 'management.cattle.io.globalrole',
183
183
  GLOBAL_ROLE_BINDING: 'management.cattle.io.globalrolebinding',
184
184
  POD_SECURITY_POLICY_TEMPLATE: 'management.cattle.io.podsecuritypolicytemplate',
185
+ PSP_TEMPLATE_BINDING: 'management.cattle.io.podsecuritypolicytemplateprojectbinding',
185
186
  PSA: 'management.cattle.io.podsecurityadmissionconfigurationtemplate',
186
187
  MANAGED_CHART: 'management.cattle.io.managedchart',
187
188
  USER_NOTIFICATION: 'management.cattle.io.rancherusernotification',
@@ -285,10 +286,11 @@ export const VIRTUAL_TYPES = {
285
286
 
286
287
  // harvester
287
288
  export const HCI = {
288
- CLUSTER: 'harvesterhci.io.management.cluster',
289
- DASHBOARD: 'harvesterhci.io.dashboard',
290
- IMAGE: 'harvesterhci.io.virtualmachineimage',
291
- SETTING: 'harvesterhci.io.setting',
289
+ CLUSTER: 'harvesterhci.io.management.cluster',
290
+ DASHBOARD: 'harvesterhci.io.dashboard',
291
+ IMAGE: 'harvesterhci.io.virtualmachineimage',
292
+ SETTING: 'harvesterhci.io.setting',
293
+ HARVESTER_CONFIG: 'rke-machine-config.cattle.io.harvesterconfig',
292
294
  };
293
295
 
294
296
  export const VIRTUAL_HARVESTER_PROVIDER = 'harvester';