mirror of https://github.com/rancher/dashboard.git
507 lines
17 KiB
Vue
507 lines
17 KiB
Vue
<script>
|
|
import { Checkbox } from '@components/Form/Checkbox';
|
|
import Loading from '@shell/components/Loading';
|
|
import AsyncButton from '@shell/components/AsyncButton';
|
|
import { Banner } from '@components/Banner';
|
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
import { MANAGEMENT } from '@shell/config/types';
|
|
import Vue from 'vue';
|
|
import { DEFAULT_PERF_SETTING, SETTING } from '@shell/config/settings';
|
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
import UnitInput from '@shell/components/form/UnitInput';
|
|
import { STEVE_CACHE } from '@shell/store/features';
|
|
import { NAME as SETTING_PRODUCT } from '@shell/config/product/settings';
|
|
|
|
const incompatible = {
|
|
incrementalLoading: ['forceNsFilterV2', 'serverPagination'],
|
|
manualRefresh: ['forceNsFilterV2', 'serverPagination'],
|
|
forceNsFilterV2: ['incrementalLoading', 'manualRefresh'],
|
|
serverPagination: ['incrementalLoading', 'manualRefresh'],
|
|
};
|
|
|
|
const l10n = {
|
|
incrementalLoading: 'incrementalLoad',
|
|
manualRefresh: 'manualRefresh',
|
|
forceNsFilterV2: 'nsFiltering',
|
|
serverPagination: 'serverPagination'
|
|
};
|
|
|
|
export default {
|
|
components: {
|
|
Checkbox,
|
|
Loading,
|
|
AsyncButton,
|
|
Banner,
|
|
LabeledInput,
|
|
UnitInput
|
|
},
|
|
|
|
async fetch() {
|
|
try {
|
|
this.uiPerfSetting = await this.$store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.UI_PERFORMANCE });
|
|
} catch {
|
|
this.uiPerfSetting = await this.$store.dispatch('management/create', { type: MANAGEMENT.SETTING }, { root: true });
|
|
// Setting does not exist - create a new one
|
|
this.uiPerfSetting.value = JSON.stringify(DEFAULT_PERF_SETTING);
|
|
this.uiPerfSetting.metadata = { name: SETTING.UI_PERFORMANCE };
|
|
}
|
|
|
|
try {
|
|
this.authUserTTL = await this.$store.dispatch(`management/find`, { type: MANAGEMENT.SETTING, id: SETTING.AUTH_USER_SESSION_TTL_MINUTES });
|
|
} catch {}
|
|
|
|
const sValue = this.uiPerfSetting?.value || JSON.stringify(DEFAULT_PERF_SETTING);
|
|
|
|
this.value = {
|
|
...DEFAULT_PERF_SETTING,
|
|
...JSON.parse(sValue),
|
|
};
|
|
|
|
this.gcStartedEnabled = this.value.garbageCollection.enabled;
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
uiPerfSetting: null,
|
|
authUserTTL: null,
|
|
bannerVal: {},
|
|
value: {},
|
|
errors: [],
|
|
gcStartedEnabled: null,
|
|
isInactivityThresholdValid: false,
|
|
ffUrl: this.$router.resolve({
|
|
name: 'c-cluster-product-resource',
|
|
params: {
|
|
product: SETTING_PRODUCT,
|
|
resource: MANAGEMENT.FEATURE
|
|
}
|
|
}).href
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
mode() {
|
|
const schema = this.$store.getters[`management/schemaFor`](MANAGEMENT.SETTING);
|
|
|
|
return schema?.resourceMethods?.includes('PUT') ? _EDIT : _VIEW;
|
|
},
|
|
|
|
canSave() {
|
|
return this.value.inactivity.enabled ? this.isInactivityThresholdValid : true;
|
|
},
|
|
|
|
steveCacheEnabled() {
|
|
return this.$store.getters['features/get'](STEVE_CACHE);
|
|
},
|
|
|
|
steveCacheApplicableResources() {
|
|
const storeResources = [];
|
|
|
|
Object.entries(this.value.serverPagination.stores).forEach(([store, settings]) => {
|
|
const resources = [];
|
|
|
|
if (settings.resources.enableAll) {
|
|
resources.push(this.t('performance.serverPagination.resources.all'));
|
|
} else {
|
|
settings.resources.enableSome.enabled.forEach((resource) => {
|
|
resources.push(!!resource.length ? resource : `${ resource.resource } (${ resource.context })`);
|
|
});
|
|
if (settings.resources.enableSome.generic) {
|
|
resources.push(this.t('performance.serverPagination.resources.generic', {}, true));
|
|
}
|
|
}
|
|
|
|
storeResources.push(`Resources in store '${ store }': ${ resources.join(', ') }`);
|
|
});
|
|
|
|
return storeResources.join('<br><br>');
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
validateInactivityThreshold(value) {
|
|
if (!this.authUserTTL?.value) {
|
|
this.isInactivityThresholdValid = true;
|
|
|
|
return;
|
|
}
|
|
|
|
if (parseInt(value) > parseInt(this.authUserTTL?.value)) {
|
|
this.isInactivityThresholdValid = false;
|
|
|
|
return this.t('performance.inactivity.authUserTTL', { current: this.authUserTTL.value });
|
|
}
|
|
this.isInactivityThresholdValid = true;
|
|
},
|
|
|
|
async save(btnCB) {
|
|
this.uiPerfSetting.value = JSON.stringify(this.value);
|
|
this.errors = [];
|
|
|
|
try {
|
|
await this.uiPerfSetting.save();
|
|
|
|
this.$store.dispatch('gcPreferencesUpdated', {
|
|
previouslyEnabled: this.gcStartedEnabled,
|
|
newPreferences: this.value.garbageCollection
|
|
}, { root: true });
|
|
|
|
this.gcStartedEnabled = this.value.garbageCollection.enabled;
|
|
btnCB(true);
|
|
} catch (err) {
|
|
this.errors.push(err);
|
|
btnCB(false);
|
|
}
|
|
},
|
|
|
|
compatibleWarning(property, enabled) {
|
|
if (!enabled) {
|
|
// Disabling a preference won't automatically turn on an incompatible one, so just set and exit
|
|
this.value[property].enabled = false;
|
|
|
|
return;
|
|
}
|
|
|
|
// We're enabling a preference. Are there any incompatible preferences?
|
|
if ((incompatible[property] || []).every((p) => !this.value[p].enabled)) {
|
|
// No, just set and exit
|
|
this.value[property].enabled = true;
|
|
|
|
return;
|
|
}
|
|
|
|
// Incompatible preferences found, so confirm with user before applying
|
|
this.$store.dispatch('cluster/promptModal', {
|
|
component: 'GenericPrompt',
|
|
componentProps: {
|
|
applyMode: 'enable',
|
|
confirm: (confirmed) => {
|
|
this.value[property].enabled = confirmed;
|
|
},
|
|
applyAction: (buttonDone) => {
|
|
(incompatible[property] || []).forEach((incompatible) => {
|
|
this.value[incompatible].enabled = false;
|
|
});
|
|
buttonDone(true);
|
|
},
|
|
title: this.t('promptRemove.title', {}, true),
|
|
body: this.t(`performance.${ l10n[property] }.incompatibleDescription`, {}, true),
|
|
},
|
|
});
|
|
},
|
|
|
|
setPaginationDefaults() {
|
|
this.value = {
|
|
...this.value,
|
|
serverPagination: { ...DEFAULT_PERF_SETTING.serverPagination }
|
|
};
|
|
}
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Loading v-if="$fetchState.pending" />
|
|
<div v-else>
|
|
<h1 class="mb-20">
|
|
{{ t('performance.label') }}
|
|
</h1>
|
|
<div>
|
|
<div class="ui-perf-setting">
|
|
<!-- Server Side Pagination -->
|
|
<div class="mt-40">
|
|
<h2>{{ t('performance.serverPagination.label') }}</h2>
|
|
<p>{{ t('performance.serverPagination.description') }}</p>
|
|
<Banner
|
|
color="error"
|
|
label-key="performance.experimental"
|
|
/>
|
|
<Banner
|
|
v-if="!steveCacheEnabled"
|
|
v-clean-html="t(`performance.serverPagination.featureFlag`, { ffUrl }, true)"
|
|
color="warning"
|
|
/>
|
|
<Checkbox
|
|
v-model:value="value.serverPagination.enabled"
|
|
:mode="mode"
|
|
:label="t('performance.serverPagination.checkboxLabel')"
|
|
class="mt-10 mb-20"
|
|
:primary="true"
|
|
:disabled="(!steveCacheEnabled && !value.serverPagination.enabled)"
|
|
@update:value="compatibleWarning('serverPagination', $event)"
|
|
/>
|
|
<p :class="{ 'text-muted': !value.serverPagination.enabled }">
|
|
{{ t('performance.serverPagination.applicable') }}
|
|
</p>
|
|
<p
|
|
v-clean-html="steveCacheApplicableResources"
|
|
:class="{ 'text-muted': !value.serverPagination.enabled }"
|
|
/>
|
|
<button
|
|
class="btn btn-sm role-primary"
|
|
style="width: fit-content;"
|
|
@click.prevent="setPaginationDefaults()"
|
|
>
|
|
{{ t('performance.serverPagination.populateDefaults') }}
|
|
</button>
|
|
</div>
|
|
<!-- Inactivity -->
|
|
<div class="mt-20">
|
|
<h2>{{ t('performance.inactivity.title') }}</h2>
|
|
<p>{{ t('performance.inactivity.description') }}</p>
|
|
<Checkbox
|
|
v-model:value="value.inactivity.enabled"
|
|
:mode="mode"
|
|
:label="t('performance.inactivity.checkboxLabel')"
|
|
class="mt-10 mb-20"
|
|
:primary="true"
|
|
/>
|
|
<div class="ml-20">
|
|
<LabeledInput
|
|
v-model:value="value.inactivity.threshold"
|
|
data-testid="inactivity-threshold"
|
|
:mode="mode"
|
|
:label="t('performance.inactivity.inputLabel')"
|
|
:disabled="!value.inactivity.enabled"
|
|
class="input mb-10"
|
|
type="number"
|
|
min="0"
|
|
:rules="[validateInactivityThreshold]"
|
|
/>
|
|
<span
|
|
v-clean-html="t('performance.inactivity.information', {}, true)"
|
|
:class="{ 'text-muted': !value.incrementalLoading.enabled }"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<!-- Websocket Notifications -->
|
|
<div class="mt-40">
|
|
<h2>{{ t('performance.websocketNotification.label') }}</h2>
|
|
<p>{{ t('performance.websocketNotification.description') }}</p>
|
|
<Checkbox
|
|
v-model:value="value.disableWebsocketNotification"
|
|
:mode="mode"
|
|
:label="t('performance.websocketNotification.checkboxLabel')"
|
|
class="mt-10 mb-20"
|
|
:primary="true"
|
|
/>
|
|
</div>
|
|
<!-- Incremental Loading -->
|
|
<div class="mt-40">
|
|
<h2>{{ t('performance.incrementalLoad.label') }}</h2>
|
|
<p>{{ t('performance.incrementalLoad.description') }}</p>
|
|
<Checkbox
|
|
:value="value.incrementalLoading.enabled"
|
|
:mode="mode"
|
|
:label="t('performance.incrementalLoad.checkboxLabel')"
|
|
class="mt-10 mb-20"
|
|
:primary="true"
|
|
@update:value="compatibleWarning('incrementalLoading', $event)"
|
|
/>
|
|
<div class="ml-20">
|
|
<p :class="{ 'text-muted': !value.incrementalLoading.enabled }">
|
|
{{ t('performance.incrementalLoad.setting') }}
|
|
</p>
|
|
<LabeledInput
|
|
v-model:value="value.incrementalLoading.threshold"
|
|
:mode="mode"
|
|
:label="t('performance.incrementalLoad.inputLabel')"
|
|
:disabled="!value.incrementalLoading.enabled"
|
|
class="input"
|
|
type="number"
|
|
min="0"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<!-- Enable manual refresh list views -->
|
|
<div class="mt-40">
|
|
<h2 v-t="'performance.manualRefresh.label'" />
|
|
<p>{{ t('performance.manualRefresh.description') }}</p>
|
|
<Banner
|
|
color="error"
|
|
label-key="performance.experimental"
|
|
/>
|
|
<Checkbox
|
|
:value="value.manualRefresh.enabled"
|
|
:mode="mode"
|
|
:label="t('performance.manualRefresh.checkboxLabel')"
|
|
class="mt-10 mb-20"
|
|
:primary="true"
|
|
@update:value="compatibleWarning('manualRefresh', $event)"
|
|
/>
|
|
<div class="ml-20">
|
|
<p :class="{ 'text-muted': !value.manualRefresh.enabled }">
|
|
{{ t('performance.manualRefresh.setting') }}
|
|
</p>
|
|
<LabeledInput
|
|
v-model:value.number="value.manualRefresh.threshold"
|
|
:mode="mode"
|
|
:label="t('performance.manualRefresh.inputLabel')"
|
|
:disabled="!value.manualRefresh.enabled"
|
|
class="input"
|
|
type="number"
|
|
min="0"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<!-- Enable GC of resources from store -->
|
|
<div class="mt-40">
|
|
<h2 v-t="'performance.gc.label'" />
|
|
<p>{{ t('performance.gc.description') }}</p>
|
|
<Banner
|
|
color="error"
|
|
label-key="performance.experimental"
|
|
/>
|
|
<Checkbox
|
|
v-model:value="value.garbageCollection.enabled"
|
|
:mode="mode"
|
|
:label="t('performance.gc.checkboxLabel')"
|
|
class="mt-10 mb-20"
|
|
:primary="true"
|
|
/>
|
|
<div class="ml-20">
|
|
<h3>{{ t('performance.gc.whenRun.description') }}</h3>
|
|
<div class="ml-20 mb-10">
|
|
<Checkbox
|
|
v-model:value="value.garbageCollection.enabledInterval"
|
|
:mode="mode"
|
|
:class="{ 'text-muted': !value.garbageCollection.enabled }"
|
|
:label="t('performance.gc.whenRun.intervalCheckBox.label')"
|
|
class="mt-10 mb-10"
|
|
:disabled="!value.garbageCollection.enabled"
|
|
:primary="true"
|
|
/>
|
|
<div class="ml-20">
|
|
<UnitInput
|
|
v-model:value="value.garbageCollection.interval"
|
|
:mode="mode"
|
|
:suffix="t('suffix.seconds', { count: value.garbageCollection.interval })"
|
|
:label="t('performance.gc.whenRun.interval.inputLabel')"
|
|
:disabled="!value.garbageCollection.enabled || !value.garbageCollection.enabledInterval"
|
|
min="30"
|
|
class="input"
|
|
/>
|
|
</div>
|
|
<Checkbox
|
|
v-model:value="value.garbageCollection.enabledOnNavigate"
|
|
:mode="mode"
|
|
:class="{ 'text-muted': !value.garbageCollection.enabled }"
|
|
:label="t('performance.gc.whenRun.route.description')"
|
|
class="mt-20 mb-10"
|
|
:disabled="!value.garbageCollection.enabled"
|
|
:primary="true"
|
|
/>
|
|
</div>
|
|
<h3>{{ t('performance.gc.howRun.description') }}</h3>
|
|
<div class="ml-20">
|
|
<p :class="{ 'text-muted': !value.garbageCollection.enabled }">
|
|
{{ t('performance.gc.howRun.age.description', {}, true) }}
|
|
</p>
|
|
<UnitInput
|
|
v-model:value="value.garbageCollection.ageThreshold"
|
|
:mode="mode"
|
|
:suffix="t('suffix.seconds', { count: value.garbageCollection.ageThreshold })"
|
|
:label="t('performance.gc.howRun.age.inputLabel')"
|
|
:disabled="!value.garbageCollection.enabled"
|
|
min="30"
|
|
class="input"
|
|
/>
|
|
<p
|
|
class="mt-20"
|
|
:class="{ 'text-muted': !value.garbageCollection.enabled }"
|
|
>
|
|
{{ t('performance.gc.howRun.count.description') }}
|
|
</p>
|
|
<LabeledInput
|
|
v-model:value.number="value.garbageCollection.countThreshold"
|
|
:mode="mode"
|
|
:label="t('performance.gc.howRun.count.inputLabel')"
|
|
:disabled="!value.garbageCollection.enabled"
|
|
class="input"
|
|
type="number"
|
|
min="0"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Force NS filter -->
|
|
<div class="mt-40">
|
|
<h2>{{ t('performance.nsFiltering.label') }}</h2>
|
|
<p>{{ t('performance.nsFiltering.description') }}</p>
|
|
<Banner
|
|
color="error"
|
|
label-key="performance.experimental"
|
|
/>
|
|
<Checkbox
|
|
:value="value.forceNsFilterV2.enabled"
|
|
:mode="mode"
|
|
:label="t('performance.nsFiltering.checkboxLabel')"
|
|
class="mt-10 mb-20"
|
|
:primary="true"
|
|
@update:value="compatibleWarning('forceNsFilterV2', $event)"
|
|
/>
|
|
</div>
|
|
<!-- Advanced Websocket Worker -->
|
|
<div class="mt-40">
|
|
<h2>{{ t('performance.advancedWorker.label') }}</h2>
|
|
<p>{{ t('performance.advancedWorker.description') }}</p>
|
|
<Banner
|
|
color="error"
|
|
label-key="performance.experimental"
|
|
/>
|
|
<Checkbox
|
|
v-model:value="value.advancedWorker.enabled"
|
|
:mode="mode"
|
|
:label="t('performance.advancedWorker.checkboxLabel')"
|
|
class="mt-10 mb-20"
|
|
:primary="true"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<template
|
|
v-for="(err, i) in errors"
|
|
:key="i"
|
|
>
|
|
<Banner
|
|
color="error"
|
|
:label="err"
|
|
/>
|
|
</template>
|
|
<div v-if="mode === 'edit'">
|
|
<AsyncButton
|
|
data-testid="performance__save-btn"
|
|
class="pull-right mt-20"
|
|
mode="apply"
|
|
:disabled="!canSave"
|
|
@click="save"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang='scss'>
|
|
.overlay {
|
|
width: 100%;
|
|
height: 100%;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
background-color: var(--overlay-bg);
|
|
z-index: 1;
|
|
}
|
|
.ui-perf-setting {
|
|
P {
|
|
line-height: 1.25;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.underline {
|
|
text-decoration: underline;
|
|
}
|
|
}
|
|
.input {
|
|
max-width: 25%;
|
|
}
|
|
</style>
|