@rancher/shell 0.1.3 → 0.1.4
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/brand/suse/dark/rancher-logo.svg +1 -148
- package/assets/brand/suse/rancher-logo.svg +1 -130
- package/assets/images/featured/img1.jpg +0 -0
- package/assets/images/featured.jpg +0 -0
- package/assets/images/generic-plugin.svg +7 -0
- package/assets/styles/themes/_dark.scss +3 -0
- package/assets/styles/themes/_light.scss +3 -0
- package/assets/styles/themes/_suse.scss +1 -1
- package/assets/translations/en-us.yaml +183 -45
- package/assets/translations/zh-hans.yaml +21 -24
- package/components/AsyncButton.vue +17 -2
- package/components/ButtonDropdown.vue +4 -0
- package/components/Carousel.vue +291 -0
- package/components/CommunityLinks.vue +69 -18
- package/components/CruResource.vue +11 -3
- package/components/Dialog.vue +102 -0
- package/components/ExplorerMembers.vue +2 -4
- package/components/ExplorerProjectsNamespaces.vue +6 -7
- package/components/IconMessage.vue +9 -1
- package/components/LocaleSelector.vue +62 -29
- package/components/ResourceTable.vue +7 -2
- package/components/SimpleBox.vue +6 -4
- package/components/SortableTable/index.vue +11 -21
- package/components/Tabbed/Tab.vue +5 -0
- package/components/Tabbed/index.vue +29 -2
- package/components/auth/Principal.vue +1 -0
- package/components/fleet/FleetBundles.vue +8 -3
- package/components/fleet/FleetSummary.vue +6 -0
- package/components/form/KeyValue.vue +80 -58
- package/components/form/NameNsDescription.vue +10 -4
- package/components/form/ResourceTabs/index.vue +5 -1
- package/components/formatter/ClusterLink.vue +3 -7
- package/components/nav/NamespaceFilter.vue +3 -3
- package/components/nav/TopLevelMenu.vue +10 -28
- package/config/footer.js +13 -14
- package/config/labels-annotations.js +2 -1
- package/config/product/explorer.js +5 -4
- package/config/product/legacy.js +0 -47
- package/config/product/multi-cluster-apps.js +0 -12
- package/config/product/settings.js +12 -1
- package/config/product/uiplugins.js +17 -0
- package/config/settings.js +21 -2
- package/config/types.js +5 -1
- package/config/uiplugins.js +60 -0
- package/content/docs/en-us/getting-started.md +1 -26
- package/core/plugins.js +12 -0
- package/detail/provisioning.cattle.io.cluster.vue +3 -3
- package/detail/workload/index.vue +2 -2
- package/dialog/DiagnosticTimingsDialog.vue +116 -0
- package/dialog/RotateCertificatesDialog.vue +9 -3
- package/edit/auth/azuread.vue +28 -9
- package/edit/networking.k8s.io.ingress/index.vue +2 -2
- package/edit/persistentvolume/index.vue +3 -0
- package/edit/pod.vue +27 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +76 -5
- package/edit/service.vue +7 -5
- package/edit/workload/__tests__/Upgrading.test.ts +1 -0
- package/edit/workload/index.vue +13 -1
- package/edit/workload/mixins/workload.js +13 -13
- package/edit/workload/storage/ContainerMountPaths.vue +240 -0
- package/edit/workload/storage/Mount.vue +1 -0
- package/edit/workload/storage/awsElasticBlockStore.vue +20 -1
- package/edit/workload/storage/azureDisk.vue +22 -2
- package/edit/workload/storage/azureFile.vue +20 -2
- package/edit/workload/storage/csi/index.vue +23 -1
- package/edit/workload/storage/gcePersistentDisk.vue +20 -2
- package/edit/workload/storage/index.vue +23 -49
- package/edit/workload/storage/vsphereVolume.vue +11 -1
- package/layouts/default.vue +14 -8
- package/layouts/home.vue +9 -4
- package/layouts/plain.vue +10 -5
- package/list/management.cattle.io.setting.vue +3 -3
- package/list/provisioning.cattle.io.cluster.vue +1 -1
- package/machine-config/harvester.vue +5 -3
- package/models/catalog.cattle.io.uiplugin.js +34 -0
- package/models/cluster/node.js +25 -2
- package/models/fleet.cattle.io.bundle.js +1 -1
- package/models/harvesterhci.io.management.cluster.js +11 -5
- package/models/provisioning.cattle.io.cluster.js +12 -6
- package/models/workload.js +5 -3
- package/nuxt.config.js +69 -25
- package/package.json +108 -109
- package/pages/auth/login.vue +1 -1
- package/pages/c/_cluster/apps/charts/index.vue +46 -1
- package/pages/c/_cluster/apps/charts/install.vue +10 -9
- package/pages/c/_cluster/explorer/index.vue +72 -9
- package/pages/c/_cluster/explorer/tools/index.vue +12 -5
- package/pages/c/_cluster/mcapps/index.vue +1 -1
- package/pages/c/_cluster/settings/brand.vue +0 -40
- package/pages/c/_cluster/settings/links.vue +200 -0
- package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +232 -0
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +242 -0
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +284 -0
- package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +130 -0
- package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +253 -0
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +115 -0
- package/pages/c/_cluster/uiplugins/index.vue +694 -0
- package/pages/diagnostic.vue +185 -101
- package/pages/docs/_doc.vue +3 -1
- package/pages/home.vue +21 -56
- package/pages/prefs.vue +108 -88
- package/pages/safeMode.vue +17 -0
- package/pages/support/index.vue +23 -15
- package/pkg/dynamic-importer.lib.js +4 -0
- package/plugins/dashboard-store/resource-class.js +2 -2
- package/plugins/formatters.js +15 -0
- package/plugins/plugin.js +56 -4
- package/plugins/steve/mutations.js +1 -1
- package/plugins/steve/subscribe.js +94 -72
- package/plugins/steve/web-worker.steve-sub-worker.js +24 -15
- package/promptRemove/management.cattle.io.globalrole.vue +47 -0
- package/promptRemove/management.cattle.io.roletemplate.vue +47 -0
- package/promptRemove/mixin/roleDeletionCheck.js +97 -0
- package/scripts/publish-shell.sh +1 -1
- package/scripts/sync-shell-deps +37 -0
- package/store/catalog.js +9 -8
- package/store/i18n.js +10 -1
- package/store/prefs.js +16 -0
- package/store/type-map.js +32 -5
- package/store/uiplugins.ts +15 -61
- package/utils/__tests__/object.test.ts +0 -24
- package/utils/__tests__/selector.test.ts +1 -1
- package/utils/dynamic-importer.js +4 -0
- package/utils/grafana.js +2 -6
- package/utils/socket.js +41 -20
- package/utils/string.js +1 -7
- package/utils/validators/formRules/__tests__/index.test.ts +108 -0
- package/utils/validators/formRules/index.ts +9 -1
- package/yarn-error.log +195 -0
- package/pages/plugins.vue +0 -387
- package/server/verdaccio-middleware.js +0 -56
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { addObject, clear, removeObject } from '@shell/utils/array';
|
|
2
2
|
import { get } from '@shell/utils/object';
|
|
3
|
-
import { COUNT,
|
|
4
|
-
import {
|
|
3
|
+
import { COUNT, SCHEMA } from '@shell/config/types';
|
|
4
|
+
import { getPerformanceSetting } from '@shell/config/settings';
|
|
5
5
|
import Socket, {
|
|
6
6
|
EVENT_CONNECTED,
|
|
7
7
|
EVENT_DISCONNECTED,
|
|
@@ -16,8 +16,7 @@ import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
|
|
|
16
16
|
import { escapeHtml } from '@shell/utils/string';
|
|
17
17
|
|
|
18
18
|
// eslint-disable-next-line
|
|
19
|
-
import
|
|
20
|
-
import * as Comlink from 'comlink';
|
|
19
|
+
import webworker from './web-worker.steve-sub-worker.js';
|
|
21
20
|
|
|
22
21
|
export const NO_WATCH = 'NO_WATCH';
|
|
23
22
|
export const NO_SCHEMA = 'NO_SCHEMA';
|
|
@@ -25,9 +24,6 @@ export const NO_SCHEMA = 'NO_SCHEMA';
|
|
|
25
24
|
// minimum length of time a disconnect notification is shown
|
|
26
25
|
const MINIMUM_TIME_NOTIFIED = 3000;
|
|
27
26
|
|
|
28
|
-
// minimum time a socket must be disconnected for before sending a growl
|
|
29
|
-
const MINIMUM_TIME_DISCONNECTED = 10000;
|
|
30
|
-
|
|
31
27
|
// We only create a worker for the cluster store
|
|
32
28
|
export function createWorker(store, ctx) {
|
|
33
29
|
const { getters } = ctx;
|
|
@@ -39,16 +35,33 @@ export function createWorker(store, ctx) {
|
|
|
39
35
|
return;
|
|
40
36
|
}
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
const workerActions = {
|
|
39
|
+
load: (resource) => {
|
|
40
|
+
queueChange(ctx, resource, true, 'Change');
|
|
41
|
+
},
|
|
42
|
+
destroyWorker: () => {
|
|
43
|
+
if (store.$workers) {
|
|
44
|
+
delete store.$workers[storeName];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
45
48
|
|
|
46
49
|
if (!store.$workers[storeName]) {
|
|
47
|
-
const worker =
|
|
50
|
+
const worker = new webworker();
|
|
48
51
|
|
|
49
52
|
store.$workers[storeName] = worker;
|
|
50
53
|
|
|
51
|
-
worker.initWorker
|
|
54
|
+
worker.postMessage({ initWorker: { storeName } });
|
|
55
|
+
|
|
56
|
+
store.$workers[storeName].onmessage = (e) => {
|
|
57
|
+
/* on the off chance there's more than key in the message, we handle them in the order that they "keys" method provides which is
|
|
58
|
+
// good enough for now considering that we never send more than one message action at a time right now */
|
|
59
|
+
const messageActions = Object.keys(e?.data);
|
|
60
|
+
|
|
61
|
+
messageActions.forEach((action) => {
|
|
62
|
+
workerActions[action](e?.data[action]);
|
|
63
|
+
});
|
|
64
|
+
};
|
|
52
65
|
}
|
|
53
66
|
}
|
|
54
67
|
|
|
@@ -119,6 +132,10 @@ function queueChange({ getters, state }, { data, revision }, load, label) {
|
|
|
119
132
|
}
|
|
120
133
|
}
|
|
121
134
|
|
|
135
|
+
function growlsDisabled(rootGetters) {
|
|
136
|
+
return getPerformanceSetting(rootGetters)?.disableWebsocketNotification;
|
|
137
|
+
}
|
|
138
|
+
|
|
122
139
|
export const actions = {
|
|
123
140
|
subscribe(ctx, opt) {
|
|
124
141
|
const {
|
|
@@ -145,7 +162,9 @@ export const actions = {
|
|
|
145
162
|
socket.setAutoReconnect(true);
|
|
146
163
|
socket.setUrl(url);
|
|
147
164
|
} else {
|
|
148
|
-
|
|
165
|
+
const maxTries = growlsDisabled(rootGetters) ? null : 3;
|
|
166
|
+
|
|
167
|
+
socket = new Socket(`${ state.config.baseUrl }/subscribe`, true, null, null, maxTries);
|
|
149
168
|
|
|
150
169
|
commit('setSocket', socket);
|
|
151
170
|
socket.addEventListener(EVENT_CONNECTED, (e) => {
|
|
@@ -177,24 +196,17 @@ export const actions = {
|
|
|
177
196
|
});
|
|
178
197
|
}
|
|
179
198
|
|
|
180
|
-
socket.connect(get(opt, 'metadata'));
|
|
199
|
+
socket.connect(get(opt, 'metadata') );
|
|
181
200
|
},
|
|
182
201
|
|
|
183
|
-
unsubscribe({
|
|
202
|
+
unsubscribe({ commit, getters, state }) {
|
|
184
203
|
const socket = state.socket;
|
|
185
204
|
const worker = (this.$workers || {})[getters.storeName];
|
|
186
205
|
|
|
187
206
|
commit('setWantSocket', false);
|
|
188
207
|
|
|
189
208
|
if (worker) {
|
|
190
|
-
|
|
191
|
-
worker.destroyWorker();
|
|
192
|
-
worker[Comlink.releaseProxy]();
|
|
193
|
-
} catch (e) {
|
|
194
|
-
console.error(e); // eslint-disable-line no-console
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
delete this.$workers[getters.storeName];
|
|
209
|
+
worker.postMessage({ destroyWorker: true }); // we're only passing the boolean here because the key needs to be something truthy to ensure it's passed on the object.
|
|
198
210
|
}
|
|
199
211
|
|
|
200
212
|
if ( socket ) {
|
|
@@ -396,8 +408,10 @@ export const actions = {
|
|
|
396
408
|
commit, dispatch, state, getters, rootGetters
|
|
397
409
|
}, event) {
|
|
398
410
|
state.debugSocket && console.info(`WebSocket Opened [${ getters.storeName }]`); // eslint-disable-line no-console
|
|
399
|
-
|
|
400
411
|
const socket = event.currentTarget;
|
|
412
|
+
const tries = event?.detail?.tries; // have to pull it off of the event because the socket's tries is already reset to 0
|
|
413
|
+
const t = rootGetters['i18n/t'];
|
|
414
|
+
const disableGrowl = growlsDisabled(rootGetters);
|
|
401
415
|
|
|
402
416
|
this.$socket = socket;
|
|
403
417
|
|
|
@@ -420,19 +434,16 @@ export const actions = {
|
|
|
420
434
|
if ( socket.hasReconnected ) {
|
|
421
435
|
await dispatch('reconnectWatches');
|
|
422
436
|
// Check for disconnect notifications and clear them
|
|
423
|
-
const growlErr = rootGetters['growl/find']({ key: 'url', val:
|
|
437
|
+
const growlErr = rootGetters['growl/find']({ key: 'url', val: socket.url });
|
|
424
438
|
|
|
425
439
|
if (growlErr) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
dispatch('growl/remove', growlErr.id, { root: true });
|
|
434
|
-
}, growlErr.earliestClose - now);
|
|
435
|
-
}
|
|
440
|
+
dispatch('growl/remove', growlErr.id, { root: true });
|
|
441
|
+
}
|
|
442
|
+
if (tries > 1 && !disableGrowl) {
|
|
443
|
+
dispatch('growl/success', {
|
|
444
|
+
title: t('growl.reconnected.title'),
|
|
445
|
+
message: t('growl.reconnected.message', { url: this.$socket.url, tries }),
|
|
446
|
+
}, { root: true });
|
|
436
447
|
}
|
|
437
448
|
}
|
|
438
449
|
|
|
@@ -456,42 +467,53 @@ export const actions = {
|
|
|
456
467
|
}, e) {
|
|
457
468
|
clearTimeout(state.queueTimer);
|
|
458
469
|
state.queueTimer = null;
|
|
459
|
-
if (e.type === EVENT_DISCONNECT_ERROR) {
|
|
460
|
-
// determine if websocket notifications are disabled
|
|
461
|
-
const perfSetting = rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_PERFORMANCE);
|
|
462
|
-
let disableGrowl = false;
|
|
463
470
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
}
|
|
471
|
+
// determine if websocket notifications are disabled
|
|
472
|
+
const disableGrowl = growlsDisabled(rootGetters);
|
|
467
473
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
dispatch('growl/
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
url
|
|
489
|
-
}, {
|
|
490
|
-
|
|
474
|
+
if (!disableGrowl) {
|
|
475
|
+
const dateFormat = escapeHtml( rootGetters['prefs/get'](DATE_FORMAT));
|
|
476
|
+
const timeFormat = escapeHtml( rootGetters['prefs/get'](TIME_FORMAT));
|
|
477
|
+
const time = e?.srcElement?.disconnectedAt || Date.now();
|
|
478
|
+
|
|
479
|
+
const timeFormatted = `${ day(time).format(`${ dateFormat } ${ timeFormat }`) }`;
|
|
480
|
+
const url = e?.srcElement?.url;
|
|
481
|
+
const tries = state?.socket?.tries;
|
|
482
|
+
|
|
483
|
+
const t = rootGetters['i18n/t'];
|
|
484
|
+
|
|
485
|
+
const growlErr = rootGetters['growl/find']({ key: 'url', val: url });
|
|
486
|
+
|
|
487
|
+
if (e.type === EVENT_CONNECT_ERROR) { // if this occurs, then we're at least retrying to connect
|
|
488
|
+
if (growlErr) {
|
|
489
|
+
dispatch('growl/remove', growlErr.id, { root: true });
|
|
490
|
+
}
|
|
491
|
+
dispatch('growl/error', {
|
|
492
|
+
title: t('growl.connectError.title'),
|
|
493
|
+
message: t('growl.connectError.message', {
|
|
494
|
+
url, time: timeFormatted, tries
|
|
495
|
+
}, { raw: true }),
|
|
496
|
+
icon: 'error',
|
|
497
|
+
earliestClose: time + MINIMUM_TIME_NOTIFIED,
|
|
498
|
+
url
|
|
499
|
+
}, { root: true });
|
|
500
|
+
} else if (e.type === EVENT_DISCONNECT_ERROR) { // if this occurs, we've given up on trying to reconnect
|
|
501
|
+
if (growlErr) {
|
|
502
|
+
dispatch('growl/remove', growlErr.id, { root: true });
|
|
503
|
+
}
|
|
504
|
+
dispatch('growl/error', {
|
|
505
|
+
title: t('growl.disconnectError.title'),
|
|
506
|
+
message: t('growl.disconnectError.message', {
|
|
507
|
+
url, time: timeFormatted, tries
|
|
508
|
+
}, { raw: true }),
|
|
509
|
+
icon: 'error',
|
|
510
|
+
earliestClose: time + MINIMUM_TIME_NOTIFIED,
|
|
511
|
+
url
|
|
512
|
+
}, { root: true });
|
|
513
|
+
} else {
|
|
514
|
+
// if the error is not a connect error or disconnect error, the socket never worked: log whether the current browser is safari
|
|
515
|
+
console.error(`WebSocket Connection Error [${ getters.storeName }]`, e.detail); // eslint-disable-line no-console
|
|
491
516
|
}
|
|
492
|
-
} else {
|
|
493
|
-
// if the error is not a disconnect error, the socket never worked: log whether the current browser is safari
|
|
494
|
-
console.error(`WebSocket Connection Error [${ getters.storeName }]`, e.detail); // eslint-disable-line no-console
|
|
495
517
|
}
|
|
496
518
|
},
|
|
497
519
|
|
|
@@ -583,7 +605,7 @@ export const actions = {
|
|
|
583
605
|
const worker = (this.$workers || {})[ctx.getters.storeName];
|
|
584
606
|
|
|
585
607
|
if (worker) {
|
|
586
|
-
worker.countsUpdate
|
|
608
|
+
worker.postMessage({ countsUpdate: msg });
|
|
587
609
|
|
|
588
610
|
// No further processing - let the web worker debounce the counts
|
|
589
611
|
return;
|
|
@@ -596,7 +618,7 @@ export const actions = {
|
|
|
596
618
|
const worker = (this.$workers || {})[ctx.getters.storeName];
|
|
597
619
|
|
|
598
620
|
if (worker) {
|
|
599
|
-
worker.updateSchema
|
|
621
|
+
worker.postMessage({ updateSchema: data });
|
|
600
622
|
|
|
601
623
|
// No further processing - let the web worker check the schema updates
|
|
602
624
|
return;
|
|
@@ -631,7 +653,7 @@ export const actions = {
|
|
|
631
653
|
const worker = (this.$workers || {})[ctx.getters.storeName];
|
|
632
654
|
|
|
633
655
|
if (worker) {
|
|
634
|
-
worker.removeSchema
|
|
656
|
+
worker.postMessage({ removeSchema: data.id });
|
|
635
657
|
}
|
|
636
658
|
}
|
|
637
659
|
|
|
@@ -712,7 +734,7 @@ export const mutations = {
|
|
|
712
734
|
clear(state.started);
|
|
713
735
|
clear(state.pendingFrames);
|
|
714
736
|
clear(state.queue);
|
|
715
|
-
|
|
737
|
+
clearTimeout(state.queueTimer);
|
|
716
738
|
state.deferredRequests = {};
|
|
717
739
|
state.queueTimer = null;
|
|
718
740
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as Comlink from 'comlink';
|
|
2
1
|
import { SCHEMA } from '@shell/config/types';
|
|
3
2
|
|
|
4
3
|
const COUNTS_FLUSH_TIMEOUT = 5000;
|
|
@@ -6,7 +5,6 @@ const SCHEMA_FLUSH_TIMEOUT = 2500;
|
|
|
6
5
|
|
|
7
6
|
const state = {
|
|
8
7
|
store: '', // Store name
|
|
9
|
-
load: undefined, // Load callback to load a resource into the store
|
|
10
8
|
counts: [], // Buffer of count resources recieved in a given window
|
|
11
9
|
countTimer: undefined, // Tiemr to flush the count buffer
|
|
12
10
|
flushTimer: undefined, // Timer to flush the schema chaneg queue
|
|
@@ -61,22 +59,33 @@ state.flushTimer = setTimeout(flush, SCHEMA_FLUSH_TIMEOUT);
|
|
|
61
59
|
|
|
62
60
|
// Callback to the store's load function (in the main thread) to process a load
|
|
63
61
|
function load(data) {
|
|
64
|
-
|
|
65
|
-
state.load(data);
|
|
66
|
-
}
|
|
62
|
+
self.postMessage({ load: data });
|
|
67
63
|
}
|
|
68
64
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
const workerActions = {
|
|
66
|
+
onmessage: (e) => {
|
|
67
|
+
/* on the off chance there's more than key in the message, we handle them in the order that they "keys" method provides which is
|
|
68
|
+
// good enough for now considering that we never send more than one message action at a time right now */
|
|
69
|
+
const messageActions = Object.keys(e?.data);
|
|
70
|
+
|
|
71
|
+
messageActions.forEach((action) => {
|
|
72
|
+
if (workerActions[action]) {
|
|
73
|
+
workerActions[action](e?.data[action]);
|
|
74
|
+
} else {
|
|
75
|
+
console.warn('no associated action for:', action); // eslint-disable-line no-console
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
initWorker: ({ storeName }) => {
|
|
72
80
|
state.store = storeName;
|
|
73
|
-
state.load = loadFn;
|
|
74
81
|
},
|
|
75
82
|
|
|
76
|
-
destroyWorker() {
|
|
83
|
+
destroyWorker: () => {
|
|
77
84
|
clearTimeout(state.countTimer);
|
|
78
85
|
clearTimeout(state.flushTimer);
|
|
79
86
|
|
|
87
|
+
self.postMessage({ destroyWorker: true }); // we're only passing the boolean here because the key needs to be something truthy to ensure it's passed on the object.
|
|
88
|
+
|
|
80
89
|
// Web worker global function to terminate the web worker
|
|
81
90
|
close();
|
|
82
91
|
},
|
|
@@ -98,7 +107,7 @@ const fns = {
|
|
|
98
107
|
},
|
|
99
108
|
|
|
100
109
|
// Called to load schema
|
|
101
|
-
|
|
110
|
+
loadSchemas: (schemas) => {
|
|
102
111
|
schemas.forEach((schema) => {
|
|
103
112
|
// These properties are added to the object, but aren't on the raw object, so remove them
|
|
104
113
|
// otherwise our comparison will show changes when there aren't any
|
|
@@ -107,16 +116,17 @@ const fns = {
|
|
|
107
116
|
|
|
108
117
|
state.schemas[schema.id] = hashObj(schema);
|
|
109
118
|
});
|
|
119
|
+
// console.log(JSON.parse(JSON.stringify(state.resources.schemas)));
|
|
110
120
|
},
|
|
111
121
|
|
|
112
122
|
// Called when schema is updated
|
|
113
|
-
updateSchema(schema) {
|
|
123
|
+
updateSchema: (schema) => {
|
|
114
124
|
// Add the schema to the queue to be checked to see if the schema really changed
|
|
115
125
|
state.queue.push(schema);
|
|
116
126
|
},
|
|
117
127
|
|
|
118
128
|
// Remove the cached schema
|
|
119
|
-
removeSchema(id) {
|
|
129
|
+
removeSchema: (id) => {
|
|
120
130
|
// Remove anything in the queue related to the schema - we don't want to send any pending updates later for a schema that has been removed
|
|
121
131
|
state.queue = state.queue.filter(schema => schema.id !== id);
|
|
122
132
|
|
|
@@ -125,5 +135,4 @@ const fns = {
|
|
|
125
135
|
}
|
|
126
136
|
};
|
|
127
137
|
|
|
128
|
-
//
|
|
129
|
-
Comlink.expose(fns);
|
|
138
|
+
onmessage = workerActions.onmessage; // bind everything to the worker's onmessage handler via the workerAction
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import RoleDeletionCheck from '@shell/promptRemove/mixin/roleDeletionCheck';
|
|
3
|
+
export default {
|
|
4
|
+
name: 'GlobalRolePromptRemove',
|
|
5
|
+
mixins: [RoleDeletionCheck],
|
|
6
|
+
props: {
|
|
7
|
+
value: {
|
|
8
|
+
type: Array,
|
|
9
|
+
default: () => {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
type: {
|
|
15
|
+
type: String,
|
|
16
|
+
required: true
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div>
|
|
24
|
+
<template>
|
|
25
|
+
{{ t('promptRemove.attemptingToRemove', { type }) }} <span
|
|
26
|
+
v-html="resourceNames(names, plusMore, t)"
|
|
27
|
+
></span>
|
|
28
|
+
</template>
|
|
29
|
+
<div v-if="info" class="text info mb-10 mt-20">
|
|
30
|
+
<span v-html="info" />
|
|
31
|
+
</div>
|
|
32
|
+
<div v-if="warning" class="text-warning mb-10 mt-20">
|
|
33
|
+
{{ warning }}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style lang="scss" scoped>
|
|
39
|
+
.text.info {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
|
|
43
|
+
> span {
|
|
44
|
+
margin-right: 5px;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
</style>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import RoleDeletionCheck from '@shell/promptRemove/mixin/roleDeletionCheck';
|
|
3
|
+
export default {
|
|
4
|
+
name: 'RoleTemplatePromptRemove',
|
|
5
|
+
mixins: [RoleDeletionCheck],
|
|
6
|
+
props: {
|
|
7
|
+
value: {
|
|
8
|
+
type: Array,
|
|
9
|
+
default: () => {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
type: {
|
|
15
|
+
type: String,
|
|
16
|
+
required: true
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div>
|
|
24
|
+
<template>
|
|
25
|
+
{{ t('promptRemove.attemptingToRemove', { type }) }} <span
|
|
26
|
+
v-html="resourceNames(names, plusMore, t)"
|
|
27
|
+
></span>
|
|
28
|
+
</template>
|
|
29
|
+
<div v-if="info" class="text info mb-10 mt-20">
|
|
30
|
+
<span v-html="info" />
|
|
31
|
+
</div>
|
|
32
|
+
<div v-if="warning" class="text-warning mb-10 mt-20">
|
|
33
|
+
{{ warning }}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style lang="scss" scoped>
|
|
39
|
+
.text.info {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
|
|
43
|
+
> span {
|
|
44
|
+
margin-right: 5px;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
</style>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { mapState, mapGetters } from 'vuex';
|
|
2
|
+
import { resourceNames } from '@shell/utils/string';
|
|
3
|
+
import { MANAGEMENT } from '@shell/config/types';
|
|
4
|
+
import { SUBTYPE_MAPPING } from '@shell/models/management.cattle.io.roletemplate';
|
|
5
|
+
const CLUSTER = SUBTYPE_MAPPING.CLUSTER.key;
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
data() {
|
|
9
|
+
return {
|
|
10
|
+
warning: '',
|
|
11
|
+
info: '',
|
|
12
|
+
};
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
computed: {
|
|
16
|
+
...mapState('action-menu', ['toRemove']),
|
|
17
|
+
...mapGetters({ t: 'i18n/t' }),
|
|
18
|
+
|
|
19
|
+
names() {
|
|
20
|
+
return this.toRemove.map(obj => obj.nameDisplay).slice(0, 5);
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
plusMore() {
|
|
24
|
+
const remaining = this.toRemove.length - this.names.length;
|
|
25
|
+
|
|
26
|
+
return this.t('promptRemove.andOthers', { count: remaining });
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
watch: {
|
|
30
|
+
value: {
|
|
31
|
+
handler(neu) {
|
|
32
|
+
this.handleRoleDeletionCheck(neu, neu[0].type, this.$route.hash);
|
|
33
|
+
},
|
|
34
|
+
immediate: true
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
methods: {
|
|
38
|
+
resourceNames,
|
|
39
|
+
async handleRoleDeletionCheck(rolesToRemove, resourceType, queryHash) {
|
|
40
|
+
this.warning = '';
|
|
41
|
+
let resourceToCheck;
|
|
42
|
+
let propToMatch;
|
|
43
|
+
let numberOfRolesWithBinds = 0;
|
|
44
|
+
const uniqueUsersWithBinds = new Set();
|
|
45
|
+
|
|
46
|
+
this.info = this.t('rbac.globalRoles.waiting', { count: rolesToRemove.length });
|
|
47
|
+
|
|
48
|
+
switch (resourceType) {
|
|
49
|
+
case MANAGEMENT.GLOBAL_ROLE:
|
|
50
|
+
resourceToCheck = MANAGEMENT.GLOBAL_ROLE_BINDING;
|
|
51
|
+
propToMatch = 'globalRoleName';
|
|
52
|
+
break;
|
|
53
|
+
default:
|
|
54
|
+
if (queryHash.includes(CLUSTER)) {
|
|
55
|
+
resourceToCheck = MANAGEMENT.CLUSTER_ROLE_TEMPLATE_BINDING;
|
|
56
|
+
} else {
|
|
57
|
+
resourceToCheck = MANAGEMENT.PROJECT_ROLE_TEMPLATE_BINDING;
|
|
58
|
+
}
|
|
59
|
+
propToMatch = 'roleTemplateName';
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const request = await this.$store.dispatch('management/request', {
|
|
65
|
+
url: `/v1/${ resourceToCheck }`,
|
|
66
|
+
method: 'get',
|
|
67
|
+
}, { root: true });
|
|
68
|
+
|
|
69
|
+
if (request.data && request.data.length) {
|
|
70
|
+
rolesToRemove.forEach((toRemove) => {
|
|
71
|
+
const usedRoles = request.data.filter(item => item[propToMatch] === toRemove.id);
|
|
72
|
+
|
|
73
|
+
if (usedRoles.length) {
|
|
74
|
+
const uniqueUsers = [...new Set(usedRoles.map(item => item.userName))];
|
|
75
|
+
|
|
76
|
+
if (uniqueUsers.length) {
|
|
77
|
+
numberOfRolesWithBinds++;
|
|
78
|
+
uniqueUsers.forEach(user => uniqueUsersWithBinds.add(user));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (numberOfRolesWithBinds && uniqueUsersWithBinds.size) {
|
|
84
|
+
this.info = '';
|
|
85
|
+
this.warning = this.t('rbac.globalRoles.usersBound', { count: uniqueUsersWithBinds.size });
|
|
86
|
+
} else {
|
|
87
|
+
this.info = this.t('rbac.globalRoles.notBound', null, true);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
this.info = this.t('rbac.globalRoles.notBound', null, true);
|
|
91
|
+
}
|
|
92
|
+
} catch (e) {
|
|
93
|
+
this.info = this.t('rbac.globalRoles.unableToCheck');
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
};
|
package/scripts/publish-shell.sh
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
console.log(__dirname);
|
|
7
|
+
|
|
8
|
+
const dir = path.resolve('.');
|
|
9
|
+
|
|
10
|
+
console.log('Syncing shell dependencies');
|
|
11
|
+
|
|
12
|
+
const topFile = path.join(dir, 'package.json');
|
|
13
|
+
const shellFile = path.join(dir, 'shell', 'package.json');
|
|
14
|
+
|
|
15
|
+
console.log(topFile);
|
|
16
|
+
console.log(shellFile);
|
|
17
|
+
|
|
18
|
+
const mainPkg = JSON.parse(fs.readFileSync(topFile));
|
|
19
|
+
|
|
20
|
+
console.log(mainPkg.version);
|
|
21
|
+
|
|
22
|
+
const shellPkg = JSON.parse(fs.readFileSync(shellFile));
|
|
23
|
+
|
|
24
|
+
console.log(shellPkg.version);
|
|
25
|
+
|
|
26
|
+
Object.keys(shellPkg.dependencies).forEach(k => {
|
|
27
|
+
const version = mainPkg.dependencies[k] || mainPkg.devDependencies[k];
|
|
28
|
+
const current = shellPkg.dependencies[k];
|
|
29
|
+
|
|
30
|
+
if (version) {
|
|
31
|
+
console.log(`Syncing ${ k } -> ${ version } was ${ current }`);
|
|
32
|
+
shellPkg.dependencies[k] = version;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
fs.writeFileSync(shellFile, JSON.stringify(shellPkg, undefined, 2));
|
|
37
|
+
|
package/store/catalog.js
CHANGED
|
@@ -61,6 +61,11 @@ export const getters = {
|
|
|
61
61
|
return [...clustered, ...namespaced];
|
|
62
62
|
},
|
|
63
63
|
|
|
64
|
+
// Raw charts
|
|
65
|
+
rawCharts(state) {
|
|
66
|
+
return state.charts;
|
|
67
|
+
},
|
|
68
|
+
|
|
64
69
|
repo(state, getters) {
|
|
65
70
|
return ({ repoType, repoName }) => {
|
|
66
71
|
const ary = (repoType === 'cluster' ? state.clusterRepos : state.namespacedRepos);
|
|
@@ -250,19 +255,13 @@ export const getters = {
|
|
|
250
255
|
|
|
251
256
|
haveComponent() {
|
|
252
257
|
return (name) => {
|
|
253
|
-
|
|
254
|
-
require.resolve(`@shell/chart/${ name }`);
|
|
255
|
-
|
|
256
|
-
return true;
|
|
257
|
-
} catch (e) {
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
258
|
+
return getters['type-map/hasCustomChart'](name);
|
|
260
259
|
};
|
|
261
260
|
},
|
|
262
261
|
|
|
263
262
|
importComponent(state, getters) {
|
|
264
263
|
return (name) => {
|
|
265
|
-
return importChart(name);
|
|
264
|
+
return getters['type-map/importChart'](name);
|
|
266
265
|
};
|
|
267
266
|
},
|
|
268
267
|
|
|
@@ -340,6 +339,7 @@ export const actions = {
|
|
|
340
339
|
// Installing an app? This is fine (in cluster store)
|
|
341
340
|
// Fetching list of cluster templates? This is fine (in management store)
|
|
342
341
|
// Installing a cluster template? This isn't fine (in cluster store as per installing app, but if there is no cluster we need to default to management)
|
|
342
|
+
|
|
343
343
|
const inStore = rootGetters['currentCluster'] ? rootGetters['currentProduct'].inStore : 'management';
|
|
344
344
|
|
|
345
345
|
if ( rootGetters[`${ inStore }/schemaFor`](CATALOG.CLUSTER_REPO) ) {
|
|
@@ -517,6 +517,7 @@ function addChart(ctx, map, chart, repo) {
|
|
|
517
517
|
chartName: chart.name,
|
|
518
518
|
chartNameDisplay: chart.annotations?.[CATALOG_ANNOTATIONS.DISPLAY_NAME] || chart.name,
|
|
519
519
|
chartDescription: chart.description,
|
|
520
|
+
featured: chart.annotations?.[CATALOG_ANNOTATIONS.FEATURED],
|
|
520
521
|
repoKey: repo._key,
|
|
521
522
|
versions: [],
|
|
522
523
|
categories: filterCategories(chart.keywords),
|
package/store/i18n.js
CHANGED
|
@@ -139,6 +139,14 @@ export const getters = {
|
|
|
139
139
|
return state.default;
|
|
140
140
|
},
|
|
141
141
|
|
|
142
|
+
multiWithFallback: (state, getters) => (items, key = 'key') => {
|
|
143
|
+
return items.map((item) => {
|
|
144
|
+
item[key] = getters.withFallback(item[key], null, item[key]);
|
|
145
|
+
|
|
146
|
+
return item;
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
|
|
142
150
|
withFallback: (state, getters) => (key, args, fallback, fallbackIsKey = false) => {
|
|
143
151
|
// Support withFallback(key,fallback) when no args
|
|
144
152
|
if ( !fallback && typeof args === 'string' ) {
|
|
@@ -153,7 +161,8 @@ export const getters = {
|
|
|
153
161
|
} else {
|
|
154
162
|
return fallback;
|
|
155
163
|
}
|
|
156
|
-
}
|
|
164
|
+
},
|
|
165
|
+
|
|
157
166
|
};
|
|
158
167
|
|
|
159
168
|
export const mutations = {
|