@rancher/shell 0.1.3 → 0.1.21
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/favicon.png +0 -0
- 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 +1 -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 +219 -47
- 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 +64 -22
- package/components/CruResource.vue +11 -3
- package/components/Dialog.vue +102 -0
- package/components/ExplorerMembers.vue +2 -4
- package/components/ExplorerProjectsNamespaces.vue +25 -9
- package/components/IconMessage.vue +9 -1
- package/components/LazyImage.vue +21 -8
- package/components/LocaleSelector.vue +62 -29
- package/components/PromptRemove.vue +2 -2
- package/components/ResourceList/Masthead.vue +21 -1
- package/components/ResourceList/ResourceLoadingIndicator.vue +0 -8
- package/components/ResourceList/index.vue +9 -23
- package/components/ResourceTable.vue +7 -2
- package/components/SimpleBox.vue +6 -4
- package/components/SortableTable/index.vue +18 -25
- package/components/Tabbed/Tab.vue +5 -0
- package/components/Tabbed/index.vue +54 -9
- package/components/TypeDescription.vue +10 -1
- package/components/auth/Principal.vue +1 -0
- package/components/fleet/FleetBundles.vue +8 -3
- package/components/fleet/FleetClusters.vue +6 -0
- package/components/fleet/FleetRepos.vue +7 -1
- package/components/fleet/FleetSummary.vue +6 -0
- package/components/form/Command.vue +5 -0
- package/components/form/EnvVars.vue +5 -0
- package/components/form/KeyValue.vue +80 -58
- package/components/form/NameNsDescription.vue +13 -5
- package/components/form/NodeScheduling.vue +6 -1
- package/components/form/PodAffinity.vue +5 -0
- package/components/form/ResourceTabs/index.vue +5 -1
- package/components/form/ServiceNameSelect.vue +5 -0
- package/components/form/ValueFromResource.vue +7 -1
- package/components/formatter/ClusterLink.vue +3 -7
- package/components/nav/NamespaceFilter.vue +3 -3
- package/components/nav/TopLevelMenu.vue +12 -29
- package/config/home-links.js +155 -0
- package/config/labels-annotations.js +2 -1
- package/config/private-label.js +1 -1
- package/config/product/explorer.js +5 -4
- package/config/product/legacy.js +0 -47
- package/config/product/manager.js +0 -2
- 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 +23 -2
- package/config/types.js +5 -1
- package/config/uiplugins.js +117 -0
- package/config/version.js +17 -0
- package/content/docs/en-us/getting-started.md +1 -26
- package/core/plugin.ts +12 -0
- package/core/plugins.js +38 -2
- package/core/types.ts +6 -0
- package/creators/app/{.eslintignore → files/.eslintignore} +0 -0
- package/creators/app/{.eslintrc.js → files/.eslintrc.js} +0 -0
- package/creators/app/{.vscode → files/.vscode}/settings.json +0 -0
- package/creators/app/{babel.config.js → files/babel.config.js} +0 -0
- package/creators/app/{nuxt.config.js → files/nuxt.config.js} +0 -0
- package/creators/app/{tsconfig.json → files/tsconfig.json} +2 -1
- package/creators/app/init +16 -17
- package/creators/app/package.json +6 -0
- package/creators/pkg/{babel.config.js → files/babel.config.js} +0 -0
- package/creators/pkg/{index.ts → files/index.ts} +0 -0
- package/creators/pkg/{tsconfig.json → files/tsconfig.json} +13 -12
- package/creators/pkg/{vue.config.js → files/vue.config.js} +0 -0
- package/creators/pkg/init +1 -1
- package/creators/update/init +54 -0
- package/creators/update/package.json +20 -0
- package/creators/update/upgrade +56 -0
- package/creators/update/yarn-error.log +54 -0
- package/detail/provisioning.cattle.io.cluster.vue +3 -3
- package/detail/workload/index.vue +3 -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 +51 -13
- package/edit/persistentvolumeclaim.vue +31 -13
- package/edit/pod.vue +27 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +103 -24
- package/edit/service.vue +7 -5
- package/edit/workload/__tests__/Upgrading.test.ts +1 -0
- package/edit/workload/index.vue +32 -10
- package/edit/workload/mixins/workload.js +121 -126
- 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 +33 -65
- package/edit/workload/storage/persistentVolumeClaim/index.vue +5 -0
- package/edit/workload/storage/secret.vue +6 -1
- 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/catalog.cattle.io.app.vue +10 -9
- package/list/catalog.cattle.io.clusterrepo.vue +6 -61
- package/list/cis.cattle.io.clusterscan.vue +12 -12
- package/list/fleet.cattle.io.bundle.vue +33 -28
- package/list/fleet.cattle.io.cluster.vue +26 -22
- package/list/fleet.cattle.io.clustergroup.vue +6 -0
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +28 -24
- package/list/fleet.cattle.io.gitrepo.vue +25 -14
- package/list/helm.cattle.io.projecthelmchart.vue +52 -33
- package/list/logging.banzaicloud.io.clusterflow.vue +7 -12
- package/list/logging.banzaicloud.io.flow.vue +7 -14
- package/list/management.cattle.io.cluster.vue +26 -15
- package/list/management.cattle.io.feature.vue +13 -8
- package/list/management.cattle.io.setting.vue +3 -3
- package/list/management.cattle.io.user.vue +38 -19
- package/list/monitoring.coreos.com.alertmanagerconfig.vue +8 -15
- package/list/namespace.vue +14 -1
- package/list/node.vue +13 -16
- package/list/persistentvolume.vue +16 -9
- package/list/persistentvolumeclaim.vue +5 -8
- package/list/provisioning.cattle.io.cluster.vue +35 -9
- package/list/service.vue +24 -12
- package/list/ui.cattle.io.navlink.vue +6 -0
- package/list/workload.vue +2 -2
- package/machine-config/harvester.vue +5 -3
- package/middleware/authenticated.js +6 -0
- package/mixins/resource-fetch.js +12 -18
- package/mixins/resource-manager.js +126 -0
- package/models/catalog.cattle.io.uiplugin.js +38 -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/pod.js +15 -5
- package/models/provisioning.cattle.io.cluster.js +16 -6
- package/models/workload.js +5 -3
- package/models/workload.service.js +10 -0
- package/nuxt.config.js +70 -25
- package/package.json +108 -109
- package/pages/auth/login.vue +11 -1
- package/pages/auth/verify.vue +9 -0
- 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/DefaultLinksEditor.vue +108 -0
- package/pages/c/_cluster/settings/brand.vue +0 -40
- package/pages/c/_cluster/settings/links.vue +152 -0
- package/pages/c/_cluster/settings/performance.vue +90 -7
- package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +232 -0
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +293 -0
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +300 -0
- package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +125 -0
- package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +261 -0
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +122 -0
- package/pages/c/_cluster/uiplugins/index.vue +808 -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 +34 -137
- package/pkg/dynamic-importer.lib.js +4 -0
- package/plugins/dashboard-store/actions.js +19 -0
- package/plugins/dashboard-store/getters.js +20 -3
- package/plugins/dashboard-store/mutations.js +13 -7
- package/plugins/dashboard-store/resource-class.js +2 -2
- package/plugins/formatters.js +15 -0
- package/plugins/plugin.js +61 -6
- package/plugins/steve/getters.js +12 -0
- 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/plugins/version.js +21 -0
- 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/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
- package/rancher-components/components/BadgeState/BadgeState.spec.ts +12 -0
- package/rancher-components/components/BadgeState/BadgeState.vue +107 -0
- package/rancher-components/components/BadgeState/index.ts +1 -0
- package/rancher-components/components/Banner/Banner.test.ts +13 -0
- package/rancher-components/components/Banner/Banner.vue +163 -0
- package/rancher-components/components/Banner/index.ts +1 -0
- package/rancher-components/components/Card/Card.vue +150 -0
- package/rancher-components/components/Card/index.ts +1 -0
- package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +77 -0
- package/rancher-components/components/Form/Checkbox/Checkbox.vue +395 -0
- package/rancher-components/components/Form/Checkbox/index.ts +1 -0
- package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +29 -0
- package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +343 -0
- package/rancher-components/components/Form/LabeledInput/index.ts +1 -0
- package/rancher-components/components/Form/Radio/RadioButton.vue +270 -0
- package/rancher-components/components/Form/Radio/RadioGroup.vue +235 -0
- package/rancher-components/components/Form/Radio/index.ts +2 -0
- package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +168 -0
- package/rancher-components/components/Form/TextArea/index.ts +1 -0
- package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +107 -0
- package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +137 -0
- package/rancher-components/components/Form/ToggleSwitch/index.ts +1 -0
- package/rancher-components/components/Form/index.ts +5 -0
- package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +137 -0
- package/rancher-components/components/LabeledTooltip/index.ts +1 -0
- package/scripts/publish-shell.sh +40 -7
- package/scripts/record-deps.js +37 -0
- package/scripts/sync-shell-deps +37 -0
- package/scripts/test-plugins-build.sh +8 -5
- package/scripts/typegen.sh +84 -0
- package/store/auth.js +3 -0
- package/store/catalog.js +9 -8
- package/store/i18n.js +10 -1
- package/store/index.js +12 -3
- package/store/prefs.js +16 -0
- package/store/type-map.js +32 -5
- package/store/uiplugins.ts +15 -61
- package/types/shell/index.d.ts +3046 -0
- 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/favicon.js +8 -2
- package/utils/gc/gc-interval.ts +40 -0
- package/utils/gc/gc-root-store.js +76 -0
- package/utils/gc/gc-route-changed.ts +44 -0
- package/utils/gc/gc-types.ts +21 -0
- package/utils/gc/gc.ts +282 -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/config/footer.js +0 -19
- package/creators/pkg/nuxt.config.js +0 -6
- package/pages/plugins.vue +0 -387
- package/server/verdaccio-middleware.js +0 -56
|
@@ -38,18 +38,6 @@ describe('fx: get', () => {
|
|
|
38
38
|
expect(result).toBeUndefined();
|
|
39
39
|
expect(() => result).not.toThrow();
|
|
40
40
|
});
|
|
41
|
-
|
|
42
|
-
it.each([
|
|
43
|
-
'key2.nonsense',
|
|
44
|
-
'non.sense',
|
|
45
|
-
])('should catch error and return undefined', (path) => {
|
|
46
|
-
const obj = { key1: 'value', key2: { bat: 42, 'with.dots': 43 } };
|
|
47
|
-
|
|
48
|
-
const result = get(obj, path);
|
|
49
|
-
|
|
50
|
-
expect(result).toBeUndefined();
|
|
51
|
-
expect(() => result).not.toThrow();
|
|
52
|
-
});
|
|
53
41
|
});
|
|
54
42
|
|
|
55
43
|
describe('fx: getter', () => {
|
|
@@ -95,18 +83,6 @@ describe('fx: getter', () => {
|
|
|
95
83
|
expect(result).toBeUndefined();
|
|
96
84
|
expect(() => result).not.toThrow();
|
|
97
85
|
});
|
|
98
|
-
|
|
99
|
-
it.each([
|
|
100
|
-
'key2.nonsense',
|
|
101
|
-
'non.sense',
|
|
102
|
-
])('should catch error and return undefined', (path) => {
|
|
103
|
-
const obj = { key1: 'value', key2: { bat: 42, 'with.dots': 43 } };
|
|
104
|
-
|
|
105
|
-
const result = getter(path)(obj);
|
|
106
|
-
|
|
107
|
-
expect(result).toBeUndefined();
|
|
108
|
-
expect(() => result).not.toThrow();
|
|
109
|
-
});
|
|
110
86
|
});
|
|
111
87
|
});
|
|
112
88
|
|
|
@@ -171,7 +171,7 @@ describe('fx: parse', () => {
|
|
|
171
171
|
['!some.prefix/key-bar_baz '],
|
|
172
172
|
['! some.prefix/key-bar_baz '],
|
|
173
173
|
[' ! some.prefix/key-bar_baz '],
|
|
174
|
-
])('should parse expression %p to selector %p', (expression) => {
|
|
174
|
+
])('should parse expression %p to selector %p using prefixes', (expression) => {
|
|
175
175
|
const expected = {
|
|
176
176
|
key: 'some.prefix/key-bar_baz',
|
|
177
177
|
operator: 'DoesNotExist',
|
|
@@ -107,6 +107,10 @@ export function resolveList(key) {
|
|
|
107
107
|
return require.resolve(`@shell/list/${ key }`);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
export function resolveChart(key) {
|
|
111
|
+
return require.resolve(`@shell/chart/${ key }`);
|
|
112
|
+
}
|
|
113
|
+
|
|
110
114
|
export function resolveEdit(key) {
|
|
111
115
|
return require.resolve(`@shell/edit/${ key }`);
|
|
112
116
|
}
|
package/utils/favicon.js
CHANGED
|
@@ -10,11 +10,17 @@ export function haveSetFavIcon() {
|
|
|
10
10
|
export function setFavIcon(store) {
|
|
11
11
|
const app = store.app;
|
|
12
12
|
const res = store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.FAVICON);
|
|
13
|
-
|
|
13
|
+
const brandSetting = store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.BRAND);
|
|
14
14
|
const link = findIconLink(app.head.link);
|
|
15
15
|
|
|
16
16
|
if (link) {
|
|
17
|
-
|
|
17
|
+
let brandImage;
|
|
18
|
+
|
|
19
|
+
if (brandSetting?.value === 'suse') {
|
|
20
|
+
brandImage = require('~shell/assets/brand/suse/favicon.png');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
link.href = res?.value || brandImage || defaultFavIcon;
|
|
18
24
|
favIconSet = true;
|
|
19
25
|
}
|
|
20
26
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import gc from './gc';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Kick of regular GC
|
|
5
|
+
*/
|
|
6
|
+
class GarbageCollectInterval {
|
|
7
|
+
private gcInterval?: NodeJS.Timer;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Request we start garbage collection at regular intervals
|
|
11
|
+
*
|
|
12
|
+
* If GC is disabled or running return early
|
|
13
|
+
*/
|
|
14
|
+
gcStartIntervals(ctx: any) {
|
|
15
|
+
const { enabled, interval } = gc.gcEnabledInterval(ctx);
|
|
16
|
+
|
|
17
|
+
if (!gc.gcEnabledSetting(ctx) || !enabled) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (this.gcInterval) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
this.gcInterval = setInterval(() => {
|
|
26
|
+
ctx.dispatch('garbageCollect'); // gc.garbageCollect is per store, so dispatch via central point
|
|
27
|
+
}, interval * 1000);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
gcStopIntervals() {
|
|
31
|
+
if (this.gcInterval) {
|
|
32
|
+
clearInterval(this.gcInterval);
|
|
33
|
+
delete this.gcInterval;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const gci = new GarbageCollectInterval();
|
|
39
|
+
|
|
40
|
+
export default gci;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import gc from './gc';
|
|
2
|
+
import gcInterval from './gc-interval';
|
|
3
|
+
import gcRoute from './gc-route-changed';
|
|
4
|
+
|
|
5
|
+
export const gcGetters = {
|
|
6
|
+
/**
|
|
7
|
+
* Fetch all stores that support garbage collection
|
|
8
|
+
*/
|
|
9
|
+
gcStores(state) {
|
|
10
|
+
// It would be nice to grab all vuex module stores that we've registered, apparently this is only possible via the
|
|
11
|
+
// internal properties store._modules.root._children.
|
|
12
|
+
// So instead loop through all state entries to find the gc stores
|
|
13
|
+
return Object.entries(state).filter(([storeName, storeState]) => {
|
|
14
|
+
if (typeof (storeState) !== 'object') {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!gc.gcEnabledForStore(storeState)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return true;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const gcActions = {
|
|
28
|
+
gcPreferencesUpdated({ dispatch }, { previouslyEnabled, newPreferences }) {
|
|
29
|
+
// Always stop the interval
|
|
30
|
+
// - GC Disabled, so it needs to stop
|
|
31
|
+
// - GC Enabled, we need to pick up new settings
|
|
32
|
+
dispatch('gcStopIntervals', { root: true });
|
|
33
|
+
|
|
34
|
+
if (newPreferences.enabled) {
|
|
35
|
+
// Cater for functionality that we get when the app loads
|
|
36
|
+
dispatch('gcStartIntervals', { root: true });
|
|
37
|
+
} else if (previouslyEnabled) {
|
|
38
|
+
// If we're going from enabled --> disabled we should reset any gc state we have stored. This avoids...
|
|
39
|
+
// - Last accessed times are stored
|
|
40
|
+
// - GC disabled so we don't update last accessed times
|
|
41
|
+
// - GC enabled and we have stale accessed times in the store
|
|
42
|
+
dispatch('gcResetStores', { root: true });
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
gcRouteChanged(ctx, to) {
|
|
47
|
+
gcRoute.gcRouteChanged(ctx, to);
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
gcStartIntervals(ctx) {
|
|
51
|
+
gcInterval.gcStartIntervals(ctx);
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
gcStopIntervals(ctx) {
|
|
55
|
+
gcInterval.gcStopIntervals();
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
gcResetStores({ dispatch, getters }) {
|
|
59
|
+
getters.gcStores.forEach(([storeName, storeState]) => {
|
|
60
|
+
dispatch(`${ storeName }/gcResetStore`);
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Kick of a GC in all stores that support it
|
|
66
|
+
*/
|
|
67
|
+
garbageCollect({ rootState, dispatch, getters }, ignoreTypes) {
|
|
68
|
+
if (!gc.gcEnabledSetting({ rootState })) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getters.gcStores.forEach(([storeName, storeState]) => {
|
|
73
|
+
dispatch(`${ storeName }/garbageCollect`, ignoreTypes);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import gc from './gc';
|
|
2
|
+
|
|
3
|
+
function getResourceFromRoute(to: any) {
|
|
4
|
+
let resource = to.params?.resource;
|
|
5
|
+
|
|
6
|
+
if ( !resource ) {
|
|
7
|
+
const match = to.name?.match(/^c-cluster-([^-]+)/);
|
|
8
|
+
|
|
9
|
+
if ( match ) {
|
|
10
|
+
resource = match[2];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return resource;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Handle GC on route change (given settings this might be a no-op)
|
|
19
|
+
*/
|
|
20
|
+
class GarbageCollectRouteChanged {
|
|
21
|
+
/**
|
|
22
|
+
* A logged in route has changed
|
|
23
|
+
* 1) Track the time this occurred to ensure any resources fetched afterwards are not GCd
|
|
24
|
+
* 2) Kick off a GC
|
|
25
|
+
*/
|
|
26
|
+
gcRouteChanged(ctx: any, to: any) {
|
|
27
|
+
gc.gcUpdateRouteChanged();
|
|
28
|
+
// commit(`gcRouteChanged`);
|
|
29
|
+
|
|
30
|
+
if (!gc.gcEnabledSetting(ctx) || !gc.gcEnabledRoute(ctx) || to.name === 'auth-logout') {
|
|
31
|
+
// (auth-logout convenience, no point GC'ing if we've just lost all types)
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const resource = getResourceFromRoute(to);
|
|
36
|
+
const ignoreTYpes = !!resource ? { [resource]: true } : {};
|
|
37
|
+
|
|
38
|
+
ctx.dispatch('garbageCollect', ignoreTYpes); // gc.garbageCollect is per store, so dispatch via central point
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const gcrc = new GarbageCollectRouteChanged();
|
|
43
|
+
|
|
44
|
+
export default gcrc;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type GC_PREFERENCES = {
|
|
2
|
+
enabled: boolean,
|
|
3
|
+
enabledInterval: boolean,
|
|
4
|
+
interval: number,
|
|
5
|
+
enabledOnNavigate: boolean,
|
|
6
|
+
ageThreshold: number,
|
|
7
|
+
countThreshold: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const GC_DEFAULTS: GC_PREFERENCES = {
|
|
11
|
+
enabled: false,
|
|
12
|
+
|
|
13
|
+
// When GC Runs
|
|
14
|
+
enabledInterval: true,
|
|
15
|
+
interval: 1 * 60 * 5,
|
|
16
|
+
enabledOnNavigate: true,
|
|
17
|
+
|
|
18
|
+
// How GC handles resources when GC'ing
|
|
19
|
+
ageThreshold: 1 * 60 * 2,
|
|
20
|
+
countThreshold: 500,
|
|
21
|
+
};
|
package/utils/gc/gc.ts
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { SETTING } from '@shell/config/settings';
|
|
2
|
+
import { COUNT, MANAGEMENT } from '@shell/config/types';
|
|
3
|
+
import { GC_DEFAULTS, GC_PREFERENCES } from './gc-types';
|
|
4
|
+
|
|
5
|
+
class GarbageCollect {
|
|
6
|
+
private static ENABLE_DEBUG_LOGGING = false;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Don't run GC if it's been run within 5 seconds
|
|
10
|
+
*/
|
|
11
|
+
private static GC_RE_RUN_GAP = 1000 * 5;
|
|
12
|
+
|
|
13
|
+
private cachedGcPrefs: GC_PREFERENCES = GC_DEFAULTS;
|
|
14
|
+
|
|
15
|
+
private cachedGcPrefsStamp: string = '';
|
|
16
|
+
|
|
17
|
+
private debugLog = (...args: any) => {
|
|
18
|
+
if (GarbageCollect.ENABLE_DEBUG_LOGGING) {
|
|
19
|
+
console.debug('GC', ...args); // eslint-disable-line no-console
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Time the GC last ran
|
|
25
|
+
*/
|
|
26
|
+
private gcLastRun: number | null = 0;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* To avoid JSON.parse on the `ui-performance` setting keep a local cache
|
|
30
|
+
*/
|
|
31
|
+
private getUiPerfGarbageCollection = (rootState: any) => {
|
|
32
|
+
const uiPerfSetting = rootState.management.types[MANAGEMENT.SETTING].list.find((s: any) => s.id === SETTING.UI_PERFORMANCE);
|
|
33
|
+
|
|
34
|
+
if (!uiPerfSetting || !uiPerfSetting.value) {
|
|
35
|
+
// Could be in the process of logging out
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const stamp = `${ uiPerfSetting.metadata.generation }-${ uiPerfSetting.metadata.resourceVersion }`;
|
|
39
|
+
|
|
40
|
+
if (this.cachedGcPrefsStamp !== stamp) {
|
|
41
|
+
this.debugLog(`Updating ${ SETTING.UI_PERFORMANCE } cache`);
|
|
42
|
+
this.cachedGcPrefsStamp = stamp;
|
|
43
|
+
const uiPerfSettingParsed = JSON.parse(uiPerfSetting.value);
|
|
44
|
+
|
|
45
|
+
this.cachedGcPrefs = uiPerfSettingParsed.garbageCollection;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return this.cachedGcPrefs;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The last time a resource was accessed by either find or getters style functions
|
|
53
|
+
*/
|
|
54
|
+
private lastAccessedCache: {
|
|
55
|
+
[store: string]: {
|
|
56
|
+
[type: string]: number;
|
|
57
|
+
}
|
|
58
|
+
} = {}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Track when a logged in route changes.
|
|
62
|
+
*/
|
|
63
|
+
private lastRouteChange: number = 0;
|
|
64
|
+
|
|
65
|
+
// ------------- GC Enabled ---------------------
|
|
66
|
+
|
|
67
|
+
gcEnabledAll(pseudoCtx: any, type: string) {
|
|
68
|
+
return this.gcEnabledForStore(pseudoCtx.state) && this.gcEnabledSetting(pseudoCtx) && this.gcEnabledForType(pseudoCtx, type);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
gcEnabledSetting(pseudoCtx: any) {
|
|
72
|
+
const { rootState } = pseudoCtx;
|
|
73
|
+
|
|
74
|
+
// Don't use a getter... as we'll end up triggering ourselves again
|
|
75
|
+
const uiPerfGarbageCollection = this.getUiPerfGarbageCollection(rootState);
|
|
76
|
+
|
|
77
|
+
return uiPerfGarbageCollection?.enabled;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
gcEnabledForStore(state: any) {
|
|
81
|
+
return state?.config?.supportsGc;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Store can require certain types are not GC'd (for example `cluster` and `schema`s, `counts`, etc)
|
|
86
|
+
*/
|
|
87
|
+
gcEnabledForType(pseudoCtx: any, type: string) {
|
|
88
|
+
const { getters } = pseudoCtx;
|
|
89
|
+
|
|
90
|
+
if (!type || getters.gcIgnoreTypes[type]) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
gcEnabledInterval(pseudoCtx: any) {
|
|
98
|
+
const { rootState } = pseudoCtx;
|
|
99
|
+
|
|
100
|
+
// Don't use a getter... as we'll end up triggering ourselves again
|
|
101
|
+
const uiPerfGarbageCollection = this.getUiPerfGarbageCollection(rootState);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
enabled: uiPerfGarbageCollection?.enabledInterval,
|
|
105
|
+
interval: uiPerfGarbageCollection?.interval || 0
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
gcEnabledRoute(pseudoCtx: any) {
|
|
110
|
+
const { rootState } = pseudoCtx;
|
|
111
|
+
|
|
112
|
+
// Don't use a getter... as we'll end up triggering ourselves again
|
|
113
|
+
const uiPerfGarbageCollection = this.getUiPerfGarbageCollection(rootState);
|
|
114
|
+
|
|
115
|
+
return uiPerfGarbageCollection?.enabledOnNavigate;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ------------- GC (actual) ---------------------
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Remove stale resource types from the store and stop watching them for changes
|
|
122
|
+
*/
|
|
123
|
+
garbageCollect(ctx: any, ignoreTypes: {[type: string]: boolean} = {}) {
|
|
124
|
+
const now = new Date().getTime();
|
|
125
|
+
|
|
126
|
+
// Is gc currently running OR has run in the past GC_RE_RUN_GAP return early
|
|
127
|
+
if (this.gcLastRun === null || now - this.gcLastRun < GarbageCollect.GC_RE_RUN_GAP) {
|
|
128
|
+
this.debugLog('Skipping (running or recently run)', this.gcLastRun ? new Date(this.gcLastRun) : 'running');
|
|
129
|
+
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.gcLastRun = null;
|
|
134
|
+
const gcd: {[type: string]: number} = {};
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const { getters, rootState, dispatch } = ctx;
|
|
138
|
+
|
|
139
|
+
if (!rootState.clusterReady) {
|
|
140
|
+
this.debugLog('Skipping (cluster not ready)');
|
|
141
|
+
this.gcLastRun = new Date().getTime();
|
|
142
|
+
|
|
143
|
+
return ;
|
|
144
|
+
}
|
|
145
|
+
this.debugLog(`------ Started ------`);
|
|
146
|
+
|
|
147
|
+
const uiPerfGarbageCollection = this.getUiPerfGarbageCollection(rootState);
|
|
148
|
+
|
|
149
|
+
if (!uiPerfGarbageCollection) {
|
|
150
|
+
return ;
|
|
151
|
+
}
|
|
152
|
+
const maxAge = uiPerfGarbageCollection.ageThreshold * 1000;
|
|
153
|
+
const maxCount = uiPerfGarbageCollection.countThreshold;
|
|
154
|
+
|
|
155
|
+
this.debugLog(`Max Age: ${ maxAge }. Max Count: ${ maxCount }`);// , 'Cache', this.lastAccessedCache
|
|
156
|
+
|
|
157
|
+
const store = ctx.state.config.namespace;
|
|
158
|
+
|
|
159
|
+
// this.debugLog('Cache', this.lastAccessedCache);
|
|
160
|
+
|
|
161
|
+
Object.entries((this.lastAccessedCache[store] || {})).forEach(([type, lastAccessed]) => {
|
|
162
|
+
if (!lastAccessed) {
|
|
163
|
+
// There's no last accessed time... gc is probably disabled in another way
|
|
164
|
+
this.debugLog(`${ type }: Skipping (no accessed time)`);
|
|
165
|
+
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!this.gcEnabledForType(ctx, type)) {
|
|
170
|
+
// This specific store is telling us to ignore the type (for example `cluster` store will not GC schema's, counts, etc)
|
|
171
|
+
this.debugLog(`${ type }: Skipping (type ignored by store)`);
|
|
172
|
+
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (ignoreTypes[type]) {
|
|
177
|
+
// We're going to a place that needs the resource
|
|
178
|
+
this.debugLog(`${ type }: Skipping (navigating to type)`);
|
|
179
|
+
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (now - lastAccessed <= maxAge) {
|
|
184
|
+
// The resource was recently accessed
|
|
185
|
+
|
|
186
|
+
this.debugLog(`${ type }: Skipping (recently accessed)`);
|
|
187
|
+
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (this.lastRouteChange !== undefined && this.lastRouteChange < lastAccessed ) {
|
|
192
|
+
// The resource is being used in the current route/page
|
|
193
|
+
this.debugLog(`${ type }: Skipping (used in current route/page)`);
|
|
194
|
+
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const countFromResource = getters.all(COUNT)[0].counts[type]?.summary.count;
|
|
199
|
+
const currentCount = countFromResource ?? 0;
|
|
200
|
+
|
|
201
|
+
if (currentCount === undefined || currentCount < maxCount) {
|
|
202
|
+
// There's too few resources, we might as well keep them to avoid a network request when we need them again
|
|
203
|
+
this.debugLog(`${ type }: Skipping (too few of resource)`);
|
|
204
|
+
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this.debugLog(`${ type }: Removing from store`);
|
|
209
|
+
dispatch('forgetType', type);
|
|
210
|
+
gcd[type] = currentCount;
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
this.gcLastRun = new Date().getTime();
|
|
214
|
+
} catch (e) {
|
|
215
|
+
this.debugLog(`: Error`, e);
|
|
216
|
+
this.gcLastRun = new Date().getTime();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (Object.keys(gcd).length > 0) {
|
|
220
|
+
console.info('Garbage Collected Resources', gcd); // eslint-disable-line no-console
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
this.debugLog(`------ Finished ------`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ------------- GC Update local cache ---------------------
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Update the time that the resource type was accessed
|
|
230
|
+
* This needs to run after any type initialisation (aka registerType)
|
|
231
|
+
*/
|
|
232
|
+
gcUpdateLastAccessed(pseudoCtx: any, type: string) {
|
|
233
|
+
if (!this.gcEnabledAll(pseudoCtx, type)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const store = pseudoCtx.state.config.namespace;
|
|
237
|
+
|
|
238
|
+
if (!this.lastAccessedCache[store]) {
|
|
239
|
+
this.lastAccessedCache[store] = {};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
this.lastAccessedCache[store][type] = new Date().getTime();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Update the time the user last changed routes
|
|
247
|
+
*/
|
|
248
|
+
gcUpdateRouteChanged() {
|
|
249
|
+
this.lastRouteChange = new Date().getTime();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ------------- GC reset ---------------------
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Remove all cached access times for the given store
|
|
256
|
+
*/
|
|
257
|
+
gcResetStore(state: any) {
|
|
258
|
+
const store = state.config.namespace;
|
|
259
|
+
|
|
260
|
+
delete this.lastAccessedCache[store];
|
|
261
|
+
|
|
262
|
+
this.debugLog('Forgetting Store:', store);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Remove cached access time for the given resource type
|
|
267
|
+
*/
|
|
268
|
+
gcResetType(state: any, type: string) {
|
|
269
|
+
const store = state.config.namespace;
|
|
270
|
+
|
|
271
|
+
if (!this.lastAccessedCache[store]) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
delete this.lastAccessedCache[store][type];
|
|
275
|
+
|
|
276
|
+
this.debugLog('Forgetting Type:', store, type);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const gc = new GarbageCollect();
|
|
281
|
+
|
|
282
|
+
export default gc;
|
package/utils/grafana.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { haveV2Monitoring } from '@shell/utils/monitoring';
|
|
1
2
|
import { parse as parseUrl, addParam } from '@shell/utils/url';
|
|
2
|
-
import { MONITORING } from '@shell/config/types';
|
|
3
3
|
|
|
4
4
|
export function computeDashboardUrl(embedUrl, clusterId, params) {
|
|
5
5
|
const url = parseUrl(embedUrl);
|
|
@@ -21,7 +21,7 @@ export function computeDashboardUrl(embedUrl, clusterId, params) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export async function dashboardExists(store, clusterId, embedUrl, storeName = 'cluster') {
|
|
24
|
-
if (!
|
|
24
|
+
if ( !haveV2Monitoring(store.getters) ) {
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -81,7 +81,3 @@ export async function failedProposals(dispatch, clusterId) {
|
|
|
81
81
|
|
|
82
82
|
return response.data.result[0]?.values?.[0]?.[1] || 0;
|
|
83
83
|
}
|
|
84
|
-
|
|
85
|
-
function isMonitoringInstalled(getters, storeName = 'cluster') {
|
|
86
|
-
return !!getters[`${ storeName }/schemaFor`](MONITORING.SERVICEMONITOR);
|
|
87
|
-
}
|