@rancher/shell 3.0.1-rc.4 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/data/aws-regions.json +1 -0
- package/assets/styles/base/_basic.scss +5 -0
- package/assets/styles/base/_mixins.scss +8 -0
- package/assets/styles/global/_button.scss +5 -0
- package/assets/styles/themes/_dark.scss +2 -0
- package/assets/styles/themes/_light.scss +2 -0
- package/assets/translations/en-us.yaml +27 -11
- package/assets/translations/zh-hans.yaml +1 -1
- package/chart/monitoring/StorageClassSelector.vue +1 -1
- package/components/AssignTo.vue +1 -0
- package/components/AsyncButton.vue +1 -0
- package/components/BackLink.vue +8 -2
- package/components/PaginatedResourceTable.vue +135 -0
- package/components/ResourceList/index.vue +0 -1
- package/components/ResourceTable.vue +6 -1
- package/components/SortableTable/index.vue +8 -6
- package/components/Tabbed/index.vue +35 -2
- package/components/form/ResourceLabeledSelect.vue +2 -2
- package/components/form/ResourceTabs/index.vue +0 -23
- package/components/form/Taints.vue +1 -1
- package/components/nav/TopLevelMenu.helper.ts +546 -0
- package/components/nav/TopLevelMenu.vue +124 -159
- package/components/nav/__tests__/TopLevelMenu.test.ts +338 -326
- package/config/pagination-table-headers.js +4 -4
- package/config/product/explorer.js +2 -0
- package/config/router/routes.js +1 -1
- package/config/settings.ts +13 -1
- package/core/plugin.ts +8 -1
- package/core/types-provisioning.ts +5 -0
- package/core/types.ts +26 -1
- package/dialog/DrainNode.vue +6 -6
- package/edit/catalog.cattle.io.clusterrepo.vue +95 -52
- package/edit/provisioning.cattle.io.cluster/index.vue +8 -3
- package/list/node.vue +8 -5
- package/mixins/resource-fetch-api-pagination.js +40 -5
- package/mixins/resource-fetch.js +48 -5
- package/models/management.cattle.io.nodepool.js +5 -4
- package/models/provisioning.cattle.io.cluster.js +2 -10
- package/package.json +6 -6
- package/pages/about.vue +22 -0
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +36 -24
- package/pages/c/_cluster/explorer/index.vue +100 -59
- package/pages/home.vue +308 -123
- package/plugins/dashboard-store/__tests__/mutations.test.ts +2 -0
- package/plugins/dashboard-store/actions.js +29 -19
- package/plugins/dashboard-store/getters.js +5 -2
- package/plugins/dashboard-store/mutations.js +4 -2
- package/plugins/steve/__tests__/mutations.test.ts +2 -1
- package/plugins/steve/steve-pagination-utils.ts +25 -2
- package/plugins/steve/subscribe.js +22 -8
- package/scripts/extension/parse-tag-name +2 -0
- package/scripts/test-plugins-build.sh +1 -0
- package/store/index.js +31 -9
- package/tsconfig.json +7 -1
- package/types/resources/settings.d.ts +1 -1
- package/types/shell/index.d.ts +1107 -1276
- package/types/store/dashboard-store.types.ts +4 -0
- package/types/store/pagination.types.ts +13 -0
- package/types/store/vuex.d.ts +8 -0
- package/types/vue-shim.d.ts +6 -31
- package/utils/cluster.js +92 -1
- package/utils/pagination-utils.ts +17 -8
- package/utils/pagination-wrapper.ts +70 -0
- package/utils/uiplugins.ts +18 -4
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import { CAPI, MANAGEMENT } from '@shell/config/types';
|
|
2
|
+
import { PaginationParam, PaginationParamFilter, PaginationSort } from '@shell/types/store/pagination.types';
|
|
3
|
+
import { VuexStore } from '@shell/types/store/vuex';
|
|
4
|
+
import { filterHiddenLocalCluster, filterOnlyKubernetesClusters, paginationFilterClusters } from '@shell/utils/cluster';
|
|
5
|
+
import PaginationWrapper from '@shell/utils/pagination-wrapper';
|
|
6
|
+
import { allHash } from '@shell/utils/promise';
|
|
7
|
+
import { sortBy } from '@shell/utils/sort';
|
|
8
|
+
import { LocationAsRelativeRaw } from 'vue-router';
|
|
9
|
+
|
|
10
|
+
interface TopLevelMenuCluster {
|
|
11
|
+
id: string,
|
|
12
|
+
label: string,
|
|
13
|
+
ready: boolean
|
|
14
|
+
providerNavLogo: string,
|
|
15
|
+
badge: string,
|
|
16
|
+
isLocal: boolean,
|
|
17
|
+
pinned: boolean,
|
|
18
|
+
description: string,
|
|
19
|
+
pin: () => void,
|
|
20
|
+
unpin: () => void,
|
|
21
|
+
clusterRoute: LocationAsRelativeRaw,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface UpdateArgs {
|
|
25
|
+
searchTerm: string,
|
|
26
|
+
pinnedIds: string[],
|
|
27
|
+
unPinnedMax?: number,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type MgmtCluster = {
|
|
31
|
+
[key: string]: any
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ProvCluster = {
|
|
35
|
+
[key: string]: any
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Order
|
|
40
|
+
* 1. local cluster - https://github.com/rancher/dashboard/issues/10975
|
|
41
|
+
* 2. working clusters
|
|
42
|
+
* 3. name
|
|
43
|
+
*/
|
|
44
|
+
const DEFAULT_SORT: Array<PaginationSort> = [
|
|
45
|
+
{
|
|
46
|
+
asc: false,
|
|
47
|
+
field: 'spec.internal',
|
|
48
|
+
},
|
|
49
|
+
// {
|
|
50
|
+
// asc: true,
|
|
51
|
+
// field: 'status.conditions[0].status' // Pending API changes https://github.com/rancher/rancher/issues/48092
|
|
52
|
+
// },
|
|
53
|
+
{
|
|
54
|
+
asc: true,
|
|
55
|
+
field: 'spec.displayName',
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
export interface TopLevelMenuHelper {
|
|
60
|
+
/**
|
|
61
|
+
* Filter mgmt clusters by
|
|
62
|
+
* 1. If harvester or not (filterOnlyKubernetesClusters)
|
|
63
|
+
* 2. If local or not (filterHiddenLocalCluster)
|
|
64
|
+
* 3. Is pinned
|
|
65
|
+
*
|
|
66
|
+
* Sort By
|
|
67
|
+
* 1. is local cluster (appears at top)
|
|
68
|
+
* 2. ready
|
|
69
|
+
* 3. name
|
|
70
|
+
*/
|
|
71
|
+
clustersPinned: Array<TopLevelMenuCluster>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Filter mgmt clusters by
|
|
75
|
+
* 1. If harvester or not (filterOnlyKubernetesClusters)
|
|
76
|
+
* 2. If local or not (filterHiddenLocalCluster)
|
|
77
|
+
* 3.
|
|
78
|
+
* a) if search term, filter on it
|
|
79
|
+
* b) if no search term, filter on pinned
|
|
80
|
+
*
|
|
81
|
+
* Sort By
|
|
82
|
+
* 1. is local cluster (appears at top)
|
|
83
|
+
* 2. ready
|
|
84
|
+
* 3. name
|
|
85
|
+
*/
|
|
86
|
+
clustersOthers: Array<TopLevelMenuCluster>;
|
|
87
|
+
|
|
88
|
+
update: (args: UpdateArgs) => Promise<void>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export abstract class BaseTopLevelMenuHelper {
|
|
92
|
+
protected $store: VuexStore;
|
|
93
|
+
protected hasProvCluster: boolean;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Filter mgmt clusters by
|
|
97
|
+
* 1. If harvester or not (filterOnlyKubernetesClusters)
|
|
98
|
+
* 2. If local or not (filterHiddenLocalCluster)
|
|
99
|
+
* 3. Is pinned
|
|
100
|
+
*
|
|
101
|
+
* Why aren't we filtering these by search term? Because we don't show pinned when filtering on search term
|
|
102
|
+
*
|
|
103
|
+
* Sort By
|
|
104
|
+
* 1. is local cluster (appears at top)
|
|
105
|
+
* 2. ready
|
|
106
|
+
* 3. name
|
|
107
|
+
*/
|
|
108
|
+
public clustersPinned: Array<TopLevelMenuCluster> = [];
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Filter mgmt clusters by
|
|
112
|
+
* 1. If harvester or not (filterOnlyKubernetesClusters)
|
|
113
|
+
* 2. If local or not (filterHiddenLocalCluster)
|
|
114
|
+
* 3.
|
|
115
|
+
* a) if search term, filter on it
|
|
116
|
+
* b) if no search term, filter on pinned
|
|
117
|
+
*
|
|
118
|
+
* Sort By
|
|
119
|
+
* 1. is local cluster (appears at top)
|
|
120
|
+
* 2. ready
|
|
121
|
+
* 3. name
|
|
122
|
+
*/
|
|
123
|
+
public clustersOthers: Array<TopLevelMenuCluster> = [];
|
|
124
|
+
|
|
125
|
+
constructor({ $store }: {
|
|
126
|
+
$store: VuexStore,
|
|
127
|
+
}) {
|
|
128
|
+
this.$store = $store;
|
|
129
|
+
|
|
130
|
+
this.hasProvCluster = this.$store.getters[`management/schemaFor`](CAPI.RANCHER_CLUSTER);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
protected convertToCluster(mgmtCluster: MgmtCluster, provCluster: ProvCluster): TopLevelMenuCluster {
|
|
134
|
+
return {
|
|
135
|
+
id: mgmtCluster.id,
|
|
136
|
+
label: mgmtCluster.nameDisplay,
|
|
137
|
+
ready: mgmtCluster.isReady, // && !provCluster?.hasError,
|
|
138
|
+
providerNavLogo: mgmtCluster.providerMenuLogo,
|
|
139
|
+
badge: mgmtCluster.badge,
|
|
140
|
+
isLocal: mgmtCluster.isLocal,
|
|
141
|
+
pinned: mgmtCluster.pinned,
|
|
142
|
+
description: provCluster?.description || mgmtCluster.description,
|
|
143
|
+
pin: () => mgmtCluster.pin(),
|
|
144
|
+
unpin: () => mgmtCluster.unpin(),
|
|
145
|
+
clusterRoute: { name: 'c-cluster-explorer', params: { cluster: mgmtCluster.id } }
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Helper designed to supply paginated results for the top level menu cluster resources
|
|
152
|
+
*/
|
|
153
|
+
export class TopLevelMenuHelperPagination extends BaseTopLevelMenuHelper implements TopLevelMenuHelper {
|
|
154
|
+
private args?: UpdateArgs;
|
|
155
|
+
|
|
156
|
+
private clustersPinnedWrapper: PaginationWrapper;
|
|
157
|
+
private clustersOthersWrapper: PaginationWrapper;
|
|
158
|
+
private provClusterWrapper: PaginationWrapper;
|
|
159
|
+
|
|
160
|
+
private commonClusterFilters: PaginationParam[];
|
|
161
|
+
|
|
162
|
+
constructor({ $store }: {
|
|
163
|
+
$store: VuexStore,
|
|
164
|
+
}) {
|
|
165
|
+
super({ $store });
|
|
166
|
+
|
|
167
|
+
this.commonClusterFilters = paginationFilterClusters({ getters: this.$store.getters });
|
|
168
|
+
|
|
169
|
+
this.clustersPinnedWrapper = new PaginationWrapper({
|
|
170
|
+
$store,
|
|
171
|
+
onUpdate: () => {
|
|
172
|
+
// trigger on websocket update (only need 1 trigger for this cluster type)
|
|
173
|
+
// https://github.com/rancher/rancher/issues/40773 / https://github.com/rancher/dashboard/issues/12734
|
|
174
|
+
if (this.args) {
|
|
175
|
+
this.update(this.args);
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
enabledFor: {
|
|
179
|
+
store: 'management',
|
|
180
|
+
resource: {
|
|
181
|
+
id: MANAGEMENT.CLUSTER,
|
|
182
|
+
context: 'side-bar',
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
this.clustersOthersWrapper = new PaginationWrapper({
|
|
187
|
+
$store,
|
|
188
|
+
onUpdate: (res) => {
|
|
189
|
+
// trigger on websocket update (only need 1 trigger for this cluster type)
|
|
190
|
+
// https://github.com/rancher/rancher/issues/40773 / https://github.com/rancher/dashboard/issues/12734
|
|
191
|
+
if (this.args) {
|
|
192
|
+
this.update(this.args);
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
enabledFor: {
|
|
196
|
+
store: 'management',
|
|
197
|
+
resource: {
|
|
198
|
+
id: MANAGEMENT.CLUSTER,
|
|
199
|
+
context: 'side-bar',
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
this.provClusterWrapper = new PaginationWrapper({
|
|
204
|
+
$store,
|
|
205
|
+
onUpdate: (res) => {
|
|
206
|
+
// trigger on websocket update (only need 1 trigger for this cluster type)
|
|
207
|
+
// https://github.com/rancher/rancher/issues/40773 / https://github.com/rancher/dashboard/issues/12734
|
|
208
|
+
if (this.args) {
|
|
209
|
+
this.update(this.args);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
enabledFor: {
|
|
213
|
+
store: 'management',
|
|
214
|
+
resource: {
|
|
215
|
+
id: CAPI.RANCHER_CLUSTER,
|
|
216
|
+
context: 'side-bar',
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ---------- requests ----------
|
|
223
|
+
async update(args: UpdateArgs) {
|
|
224
|
+
if (!this.hasProvCluster) {
|
|
225
|
+
// We're filtering out mgmt clusters without prov clusters, so if the user can't see any prov clusters at all
|
|
226
|
+
// exit early
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this.args = args;
|
|
231
|
+
const promises = {
|
|
232
|
+
pinned: this.updatePinned(args),
|
|
233
|
+
notPinned: this.updateOthers(args)
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const res: {
|
|
237
|
+
pinned: MgmtCluster[],
|
|
238
|
+
notPinned: MgmtCluster[]
|
|
239
|
+
} = await allHash(promises) as any;
|
|
240
|
+
const provClusters = await this.updateProvCluster(res.notPinned, res.pinned);
|
|
241
|
+
const provClustersByMgmtId = provClusters.reduce((res: { [mgmtId: string]: ProvCluster}, provCluster: ProvCluster) => {
|
|
242
|
+
if (provCluster.mgmtClusterId) {
|
|
243
|
+
res[provCluster.mgmtClusterId] = provCluster;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return res;
|
|
247
|
+
}, {} as { [mgmtId: string]: ProvCluster});
|
|
248
|
+
|
|
249
|
+
const _clustersNotPinned = res.notPinned
|
|
250
|
+
.filter((mgmtCluster) => !!provClustersByMgmtId[mgmtCluster.id])
|
|
251
|
+
.map((mgmtCluster) => this.convertToCluster(mgmtCluster, provClustersByMgmtId[mgmtCluster.id]));
|
|
252
|
+
const _clustersPinned = res.pinned
|
|
253
|
+
.filter((mgmtCluster) => !!provClustersByMgmtId[mgmtCluster.id])
|
|
254
|
+
.map((mgmtCluster) => this.convertToCluster(mgmtCluster, provClustersByMgmtId[mgmtCluster.id]));
|
|
255
|
+
|
|
256
|
+
this.clustersPinned.length = 0;
|
|
257
|
+
this.clustersOthers.length = 0;
|
|
258
|
+
|
|
259
|
+
this.clustersPinned.push(..._clustersPinned);
|
|
260
|
+
this.clustersOthers.push(..._clustersNotPinned);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private constructParams({
|
|
264
|
+
pinnedIds,
|
|
265
|
+
searchTerm,
|
|
266
|
+
includeLocal,
|
|
267
|
+
includeSearchTerm,
|
|
268
|
+
includePinned,
|
|
269
|
+
excludePinned,
|
|
270
|
+
}: {
|
|
271
|
+
pinnedIds?: string[],
|
|
272
|
+
searchTerm?: string,
|
|
273
|
+
includeLocal?: boolean,
|
|
274
|
+
includeSearchTerm?: boolean,
|
|
275
|
+
includePinned?: boolean,
|
|
276
|
+
excludePinned?: boolean,
|
|
277
|
+
}): PaginationParam[] {
|
|
278
|
+
const filters: PaginationParam[] = [...this.commonClusterFilters];
|
|
279
|
+
|
|
280
|
+
if (pinnedIds) {
|
|
281
|
+
if (includePinned) {
|
|
282
|
+
// cluster id is 1 OR 2 OR 3 OR 4...
|
|
283
|
+
filters.push(PaginationParamFilter.createMultipleFields(
|
|
284
|
+
pinnedIds.map((id) => ({
|
|
285
|
+
field: 'id', value: id, equals: true, exact: true
|
|
286
|
+
}))
|
|
287
|
+
));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (excludePinned) {
|
|
291
|
+
// cluster id is NOT 1 AND NOT 2 AND NOT 3 AND NOT 4...
|
|
292
|
+
filters.push(...pinnedIds.map((id) => PaginationParamFilter.createSingleField({
|
|
293
|
+
field: 'id', equals: false, value: id
|
|
294
|
+
})));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (searchTerm && includeSearchTerm) {
|
|
299
|
+
filters.push(PaginationParamFilter.createSingleField({
|
|
300
|
+
field: 'spec.displayName', exact: false, value: searchTerm
|
|
301
|
+
}));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (includeLocal) {
|
|
305
|
+
filters.push(PaginationParamFilter.createSingleField({ field: 'id', value: 'local' }));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return filters;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* See `clustersPinned` description for details
|
|
313
|
+
*/
|
|
314
|
+
private async updatePinned(args: UpdateArgs): Promise<MgmtCluster[]> {
|
|
315
|
+
if (args.pinnedIds?.length < 1) {
|
|
316
|
+
// Return early, otherwise we're fetching all clusters...
|
|
317
|
+
return Promise.resolve([]);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return this.clustersPinnedWrapper.request({
|
|
321
|
+
pagination: {
|
|
322
|
+
filters: this.constructParams({
|
|
323
|
+
pinnedIds: args.pinnedIds,
|
|
324
|
+
includePinned: true,
|
|
325
|
+
}),
|
|
326
|
+
page: 1,
|
|
327
|
+
sort: DEFAULT_SORT,
|
|
328
|
+
projectsOrNamespaces: []
|
|
329
|
+
},
|
|
330
|
+
classify: true,
|
|
331
|
+
}).then((r) => r.data);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* See `clustersOthers` description for details
|
|
336
|
+
*/
|
|
337
|
+
private async updateOthers(args: UpdateArgs): Promise<MgmtCluster[]> {
|
|
338
|
+
return this.clustersOthersWrapper.request({
|
|
339
|
+
pagination: {
|
|
340
|
+
filters: this.constructParams({
|
|
341
|
+
searchTerm: args.searchTerm,
|
|
342
|
+
includeSearchTerm: !!args.searchTerm,
|
|
343
|
+
pinnedIds: args.pinnedIds,
|
|
344
|
+
excludePinned: !args.searchTerm,
|
|
345
|
+
}),
|
|
346
|
+
page: 1,
|
|
347
|
+
pageSize: args.unPinnedMax,
|
|
348
|
+
sort: DEFAULT_SORT,
|
|
349
|
+
projectsOrNamespaces: []
|
|
350
|
+
},
|
|
351
|
+
classify: true,
|
|
352
|
+
}).then((r) => r.data);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Find all provisioning clusters associated with the displayed mgmt clusters
|
|
357
|
+
*/
|
|
358
|
+
private async updateProvCluster(notPinned: MgmtCluster[], pinned: MgmtCluster[]): Promise<ProvCluster[]> {
|
|
359
|
+
return this.provClusterWrapper.request({
|
|
360
|
+
pagination: {
|
|
361
|
+
|
|
362
|
+
filters: [
|
|
363
|
+
PaginationParamFilter.createMultipleFields(
|
|
364
|
+
[...notPinned, ...pinned]
|
|
365
|
+
.map((mgmtCluster) => ({
|
|
366
|
+
field: 'status.clusterName', value: mgmtCluster.id, equals: true, exact: true
|
|
367
|
+
}))
|
|
368
|
+
)
|
|
369
|
+
],
|
|
370
|
+
|
|
371
|
+
page: 1,
|
|
372
|
+
sort: [],
|
|
373
|
+
projectsOrNamespaces: []
|
|
374
|
+
},
|
|
375
|
+
classify: true,
|
|
376
|
+
}).then((r) => r.data);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Helper designed to supply non-pagainted results for the top level menu cluster resources
|
|
382
|
+
*/
|
|
383
|
+
export class TopLevelMenuHelperLegacy extends BaseTopLevelMenuHelper implements TopLevelMenuHelper {
|
|
384
|
+
constructor({ $store }: {
|
|
385
|
+
$store: VuexStore,
|
|
386
|
+
}) {
|
|
387
|
+
super({ $store });
|
|
388
|
+
|
|
389
|
+
if (this.hasProvCluster) {
|
|
390
|
+
$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER });
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async update(args: UpdateArgs) {
|
|
395
|
+
const clusters = this.updateClusters();
|
|
396
|
+
const _clustersNotPinned = this.clustersFiltered(clusters, args);
|
|
397
|
+
const _clustersPinned = this.pinFiltered(clusters, args);
|
|
398
|
+
|
|
399
|
+
this.clustersPinned.length = 0;
|
|
400
|
+
this.clustersOthers.length = 0;
|
|
401
|
+
|
|
402
|
+
this.clustersPinned.push(..._clustersPinned);
|
|
403
|
+
this.clustersOthers.push(..._clustersNotPinned);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Filter mgmt clusters by
|
|
408
|
+
* 1. Harvester type 1 (filterOnlyKubernetesClusters)
|
|
409
|
+
* 2. Harvester type 2 (filterHiddenLocalCluster)
|
|
410
|
+
* 3. There's a matching prov cluster
|
|
411
|
+
*
|
|
412
|
+
* Convert remaining clusters to special format
|
|
413
|
+
*/
|
|
414
|
+
private updateClusters(): TopLevelMenuCluster[] {
|
|
415
|
+
if (!this.hasProvCluster) {
|
|
416
|
+
// We're filtering out mgmt clusters without prov clusters, so if the user can't see any prov clusters at all
|
|
417
|
+
// exit early
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const all = this.$store.getters['management/all'](MANAGEMENT.CLUSTER);
|
|
422
|
+
const mgmtClusters = filterHiddenLocalCluster(filterOnlyKubernetesClusters(all, this.$store), this.$store);
|
|
423
|
+
const provClusters = this.$store.getters['management/all'](CAPI.RANCHER_CLUSTER);
|
|
424
|
+
const provClustersByMgmtId = provClusters.reduce((res: any, provCluster: ProvCluster) => {
|
|
425
|
+
if (provCluster.mgmt?.id) {
|
|
426
|
+
res[provCluster.mgmt.id] = provCluster;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return res;
|
|
430
|
+
}, {});
|
|
431
|
+
|
|
432
|
+
return (mgmtClusters || []).reduce((res: any, mgmtCluster: MgmtCluster) => {
|
|
433
|
+
// Filter to only show mgmt clusters that exist for the available provisioning clusters
|
|
434
|
+
// Addresses issue where a mgmt cluster can take some time to get cleaned up after the corresponding
|
|
435
|
+
// provisioning cluster has been deleted
|
|
436
|
+
if (!provClustersByMgmtId[mgmtCluster.id]) {
|
|
437
|
+
return res;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
res.push(this.convertToCluster(mgmtCluster, provClustersByMgmtId[mgmtCluster.id]));
|
|
441
|
+
|
|
442
|
+
return res;
|
|
443
|
+
}, []);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Filter clusters by
|
|
448
|
+
* 1. Not pinned
|
|
449
|
+
* 2. Includes search term
|
|
450
|
+
*
|
|
451
|
+
* Sort remaining clusters
|
|
452
|
+
*
|
|
453
|
+
* Reduce number of clusters if too many too show
|
|
454
|
+
*
|
|
455
|
+
* Important! This is used to show unpinned clusters OR results of search
|
|
456
|
+
*/
|
|
457
|
+
private clustersFiltered(clusters: TopLevelMenuCluster[], args: UpdateArgs): TopLevelMenuCluster[] {
|
|
458
|
+
const clusterFilter = args.searchTerm;
|
|
459
|
+
const maxClustersToShow = args.unPinnedMax || 10;
|
|
460
|
+
|
|
461
|
+
const search = (clusterFilter || '').toLowerCase();
|
|
462
|
+
let localCluster: MgmtCluster | null = null;
|
|
463
|
+
|
|
464
|
+
const filtered = clusters.filter((c) => {
|
|
465
|
+
// If we're searching we don't care if pinned or not
|
|
466
|
+
if (search) {
|
|
467
|
+
if (!c.label?.toLowerCase().includes(search)) {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
} else if (c.pinned) {
|
|
471
|
+
// Not searching, not pinned, don't care
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (!localCluster && c.id === 'local') {
|
|
476
|
+
// Local cluster is a special case, we're inserting it at top so don't include in the middle
|
|
477
|
+
localCluster = c;
|
|
478
|
+
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return true;
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
const sorted = sortBy(filtered, ['ready:desc', 'label']);
|
|
486
|
+
|
|
487
|
+
// put local cluster on top of list always - https://github.com/rancher/dashboard/issues/10975
|
|
488
|
+
if (localCluster) {
|
|
489
|
+
sorted.unshift(localCluster);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (search) {
|
|
493
|
+
// this.showPinClusters = false;
|
|
494
|
+
// this.searchActive = !sorted.length > 0;
|
|
495
|
+
|
|
496
|
+
return sorted;
|
|
497
|
+
}
|
|
498
|
+
// this.showPinClusters = true;
|
|
499
|
+
// this.searchActive = false;
|
|
500
|
+
|
|
501
|
+
if (sorted.length >= maxClustersToShow) {
|
|
502
|
+
return sorted.slice(0, maxClustersToShow);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return sorted;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Filter clusters by
|
|
510
|
+
* 1. Not pinned
|
|
511
|
+
* 2. Includes search term
|
|
512
|
+
*
|
|
513
|
+
* Sort remaining clusters
|
|
514
|
+
*
|
|
515
|
+
* Reduce number of clusters if too many too show
|
|
516
|
+
*
|
|
517
|
+
* Important! This is hidden if there's a filter (user searching)
|
|
518
|
+
*/
|
|
519
|
+
private pinFiltered(clusters: TopLevelMenuCluster[], args: UpdateArgs): TopLevelMenuCluster[] {
|
|
520
|
+
let localCluster = null;
|
|
521
|
+
const filtered = clusters.filter((c) => {
|
|
522
|
+
if (!c.pinned) {
|
|
523
|
+
// We only care about pinned clusters
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (c.id === 'local') {
|
|
528
|
+
// Special case, we're going to add this at the start so filter out
|
|
529
|
+
localCluster = c;
|
|
530
|
+
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return true;
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
const sorted = sortBy(filtered, ['ready:desc', 'label']);
|
|
538
|
+
|
|
539
|
+
// put local cluster on top of list always - https://github.com/rancher/dashboard/issues/10975
|
|
540
|
+
if (localCluster) {
|
|
541
|
+
sorted.unshift(localCluster);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return sorted;
|
|
545
|
+
}
|
|
546
|
+
}
|