mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Index Management] Update editable index settings for Serverless (#168884)
## Summary
Fixes https://github.com/elastic/kibana/issues/165895
This PR limits which index settings are displayed on the index details
page, "Settings" tab in the edit mode. On serverless only a handful of
index settings will be editable by the user. The UI only prevents
displaying some index settings, but it's still possible for the user to
type in a setting that can't be edited. That is the case on dedicated as
well.
### How to test
1. Start Serverless ES and Kibana
2. Navigate to Index Management and create a test index
3. Click on the index name and on the details page click the tab
"Settings"
4. Toggle the "Edit mode" switch and verify that only editable settings
are displayed.
#### Screenshot
<img width="527" alt="Screenshot 2023-10-16 at 20 25 49"
src="e6678cca
-3494-4c63-ae66-ace9c823d12d">
This commit is contained in:
parent
c58238d989
commit
11b1bc77a6
12 changed files with 108 additions and 36 deletions
|
@ -42,6 +42,8 @@ xpack.index_management.enableIndexActions: false
|
|||
xpack.index_management.enableLegacyTemplates: false
|
||||
# Disable index stats information from Index Management UI
|
||||
xpack.index_management.enableIndexStats: false
|
||||
# Only limited index settings can be edited
|
||||
xpack.index_management.editableIndexSettings: limited
|
||||
|
||||
# Keep deeplinks visible so that they are shown in the sidenav
|
||||
dev_tools.deeplinks.navLinkStatus: visible
|
||||
|
|
|
@ -266,6 +266,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
|
|||
'xpack.index_management.enableIndexActions (any)',
|
||||
'xpack.index_management.enableLegacyTemplates (any)',
|
||||
'xpack.index_management.enableIndexStats (any)',
|
||||
'xpack.index_management.editableIndexSettings (any)',
|
||||
'xpack.infra.sources.default.fields.message (array)',
|
||||
/**
|
||||
* Feature flags bellow are conditional based on traditional/serverless offering
|
||||
|
|
|
@ -82,7 +82,7 @@ const appDependencies = {
|
|||
enableLegacyTemplates: true,
|
||||
enableIndexActions: true,
|
||||
enableIndexStats: true,
|
||||
enableIndexDetailsPage: false,
|
||||
editableIndexSettings: 'all',
|
||||
},
|
||||
} as any;
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@ import {
|
|||
IndexManagementBreadcrumb,
|
||||
} from '../../../public/application/services/breadcrumbs';
|
||||
import {
|
||||
testIndexEditableSettings,
|
||||
testIndexEditableSettingsAll,
|
||||
testIndexEditableSettingsLimited,
|
||||
testIndexMappings,
|
||||
testIndexMock,
|
||||
testIndexName,
|
||||
|
@ -429,13 +430,30 @@ describe('<IndexDetailsPage />', () => {
|
|||
await testBed.actions.settings.clickEditModeSwitch();
|
||||
});
|
||||
|
||||
it('displays the editable settings (flattened and filtered)', () => {
|
||||
it('displays all editable settings (flattened and filtered)', () => {
|
||||
const editorContent = testBed.actions.settings.getCodeEditorContent();
|
||||
expect(editorContent).toEqual(JSON.stringify(testIndexEditableSettings, null, 2));
|
||||
expect(editorContent).toEqual(JSON.stringify(testIndexEditableSettingsAll, null, 2));
|
||||
});
|
||||
|
||||
it('displays limited editable settings (flattened and filtered)', async () => {
|
||||
await act(async () => {
|
||||
testBed = await setup({
|
||||
httpSetup,
|
||||
dependencies: {
|
||||
config: { editableIndexSettings: 'limited' },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
testBed.component.update();
|
||||
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Settings);
|
||||
await testBed.actions.settings.clickEditModeSwitch();
|
||||
const editorContent = testBed.actions.settings.getCodeEditorContent();
|
||||
expect(editorContent).toEqual(JSON.stringify(testIndexEditableSettingsLimited, null, 2));
|
||||
});
|
||||
|
||||
it('updates the settings', async () => {
|
||||
const updatedSettings = { ...testIndexEditableSettings, 'index.priority': '2' };
|
||||
const updatedSettings = { ...testIndexEditableSettingsAll, 'index.priority': '2' };
|
||||
await testBed.actions.settings.updateCodeEditorContent(JSON.stringify(updatedSettings));
|
||||
await testBed.actions.settings.saveSettings();
|
||||
expect(httpSetup.put).toHaveBeenLastCalledWith(
|
||||
|
@ -452,7 +470,7 @@ describe('<IndexDetailsPage />', () => {
|
|||
it('reloads the settings after an update', async () => {
|
||||
const numberOfRequests = 2;
|
||||
expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests);
|
||||
const updatedSettings = { ...testIndexEditableSettings, 'index.priority': '2' };
|
||||
const updatedSettings = { ...testIndexEditableSettingsAll, 'index.priority': '2' };
|
||||
await testBed.actions.settings.updateCodeEditorContent(JSON.stringify(updatedSettings));
|
||||
await testBed.actions.settings.saveSettings();
|
||||
expect(httpSetup.get).toHaveBeenCalledTimes(numberOfRequests + 1);
|
||||
|
@ -463,11 +481,11 @@ describe('<IndexDetailsPage />', () => {
|
|||
});
|
||||
|
||||
it('resets the changes in the editor', async () => {
|
||||
const updatedSettings = { ...testIndexEditableSettings, 'index.priority': '2' };
|
||||
const updatedSettings = { ...testIndexEditableSettingsAll, 'index.priority': '2' };
|
||||
await testBed.actions.settings.updateCodeEditorContent(JSON.stringify(updatedSettings));
|
||||
await testBed.actions.settings.resetChanges();
|
||||
const editorContent = testBed.actions.settings.getCodeEditorContent();
|
||||
expect(editorContent).toEqual(JSON.stringify(testIndexEditableSettings, null, 2));
|
||||
expect(editorContent).toEqual(JSON.stringify(testIndexEditableSettingsAll, null, 2));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -664,7 +682,7 @@ describe('<IndexDetailsPage />', () => {
|
|||
it('updates settings with the encoded index name', async () => {
|
||||
await testBed.actions.clickIndexDetailsTab(IndexDetailsSection.Settings);
|
||||
await testBed.actions.settings.clickEditModeSwitch();
|
||||
const updatedSettings = { ...testIndexEditableSettings, 'index.priority': '2' };
|
||||
const updatedSettings = { ...testIndexEditableSettingsAll, 'index.priority': '2' };
|
||||
await testBed.actions.settings.updateCodeEditorContent(JSON.stringify(updatedSettings));
|
||||
await testBed.actions.settings.saveSettings();
|
||||
expect(httpSetup.put).toHaveBeenLastCalledWith(
|
||||
|
|
|
@ -67,11 +67,14 @@ export const testIndexSettings = {
|
|||
},
|
||||
},
|
||||
};
|
||||
export const testIndexEditableSettings = {
|
||||
export const testIndexEditableSettingsAll = {
|
||||
'index.priority': '1',
|
||||
'index.query.default_field': ['*'],
|
||||
'index.routing.allocation.include._tier_preference': 'data_content',
|
||||
};
|
||||
export const testIndexEditableSettingsLimited = {
|
||||
'index.query.default_field': ['*'],
|
||||
};
|
||||
|
||||
// Mocking partial index stats response
|
||||
export const testIndexStats = {
|
||||
|
|
|
@ -53,6 +53,7 @@ export interface AppDependencies {
|
|||
enableIndexActions: boolean;
|
||||
enableLegacyTemplates: boolean;
|
||||
enableIndexStats: boolean;
|
||||
editableIndexSettings: 'all' | 'limited';
|
||||
};
|
||||
history: ScopedHistory;
|
||||
setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs'];
|
||||
|
|
|
@ -22,7 +22,25 @@ export const readOnlySettings = [
|
|||
'index.routing_partition_size',
|
||||
'index.store.type',
|
||||
];
|
||||
export const settingsToDisplay = [
|
||||
|
||||
export const limitedEditableSettings = [
|
||||
'index.blocks.write',
|
||||
'index.blocks.read',
|
||||
'index.blocks.read_only',
|
||||
'index.default_pipeline',
|
||||
'index.lifecycle.origination_date',
|
||||
'index.final_pipeline',
|
||||
'index.query.default_field',
|
||||
'index.refresh_interval',
|
||||
'index.mapping.ignore_malformed',
|
||||
'index.mapping.total_fields.limit',
|
||||
'index.merge.policy.deletes_pct_allowed',
|
||||
'index.merge.policy.max_merge_at_once',
|
||||
'index.merge.policy.expunge_deletes_allowed',
|
||||
'index.merge.policy.floor_segment',
|
||||
];
|
||||
|
||||
export const defaultsToDisplay = [
|
||||
'index.number_of_replicas',
|
||||
'index.blocks.read_only_allow_delete',
|
||||
'index.codec',
|
||||
|
|
|
@ -54,9 +54,7 @@ export async function mountManagementSection({
|
|||
extensionsService,
|
||||
isFleetEnabled,
|
||||
kibanaVersion,
|
||||
enableIndexActions = true,
|
||||
enableLegacyTemplates = true,
|
||||
enableIndexStats = true,
|
||||
config,
|
||||
cloud,
|
||||
}: {
|
||||
coreSetup: CoreSetup<StartDependencies>;
|
||||
|
@ -65,9 +63,7 @@ export async function mountManagementSection({
|
|||
extensionsService: ExtensionsService;
|
||||
isFleetEnabled: boolean;
|
||||
kibanaVersion: SemVer;
|
||||
enableIndexActions?: boolean;
|
||||
enableLegacyTemplates?: boolean;
|
||||
enableIndexStats?: boolean;
|
||||
config: AppDependencies['config'];
|
||||
cloud?: CloudSetup;
|
||||
}) {
|
||||
const { element, setBreadcrumbs, history, theme$ } = params;
|
||||
|
@ -114,11 +110,7 @@ export async function mountManagementSection({
|
|||
uiMetricService,
|
||||
extensionsService,
|
||||
},
|
||||
config: {
|
||||
enableIndexActions,
|
||||
enableLegacyTemplates,
|
||||
enableIndexStats,
|
||||
},
|
||||
config,
|
||||
history,
|
||||
setBreadcrumbs,
|
||||
uiSettings,
|
||||
|
|
|
@ -30,22 +30,39 @@ import { Error } from '../../../../../shared_imports';
|
|||
import { documentationService, updateIndexSettings } from '../../../../services';
|
||||
import { notificationService } from '../../../../services/notification';
|
||||
import { flattenObject } from '../../../../lib/flatten_object';
|
||||
import { readOnlySettings, settingsToDisplay } from '../../../../lib/edit_settings';
|
||||
import {
|
||||
readOnlySettings,
|
||||
defaultsToDisplay,
|
||||
limitedEditableSettings,
|
||||
} from '../../../../lib/edit_settings';
|
||||
import { AppDependencies, useAppContext } from '../../../../app_context';
|
||||
|
||||
const getEditableSettings = (
|
||||
data: Props['data'],
|
||||
isIndexOpen: boolean
|
||||
): { originalSettings: Record<string, any>; settingsString: string } => {
|
||||
const getEditableSettings = ({
|
||||
data,
|
||||
isIndexOpen,
|
||||
editableIndexSettings,
|
||||
}: {
|
||||
data: Props['data'];
|
||||
isIndexOpen: boolean;
|
||||
editableIndexSettings: AppDependencies['config']['editableIndexSettings'];
|
||||
}): { originalSettings: Record<string, any>; settingsString: string } => {
|
||||
const { defaults, settings } = data;
|
||||
// settings user has actually set
|
||||
const flattenedSettings = flattenObject(settings);
|
||||
// settings with their defaults
|
||||
const flattenedDefaults = flattenObject(defaults);
|
||||
const filteredDefaults = _.pick(flattenedDefaults, settingsToDisplay);
|
||||
const newSettings = { ...filteredDefaults, ...flattenedSettings };
|
||||
// store these to be used as autocomplete values later
|
||||
readOnlySettings.forEach((e) => delete newSettings[e]);
|
||||
// can't change codec on open index
|
||||
const filteredDefaults = _.pick(flattenedDefaults, defaultsToDisplay);
|
||||
|
||||
let newSettings = { ...filteredDefaults, ...flattenedSettings };
|
||||
if (editableIndexSettings === 'limited') {
|
||||
// only pick limited settings
|
||||
newSettings = _.pick(newSettings, limitedEditableSettings);
|
||||
} else {
|
||||
// remove read only settings
|
||||
readOnlySettings.forEach((e) => delete newSettings[e]);
|
||||
}
|
||||
|
||||
// can't change codec on an open index
|
||||
if (isIndexOpen) {
|
||||
delete newSettings['index.codec'];
|
||||
}
|
||||
|
@ -67,12 +84,19 @@ export const DetailsPageSettingsContent: FunctionComponent<Props> = ({
|
|||
reloadIndexSettings,
|
||||
}) => {
|
||||
const [isEditMode, setIsEditMode] = useState(false);
|
||||
const {
|
||||
config: { editableIndexSettings },
|
||||
} = useAppContext();
|
||||
const onEditModeChange = (event: EuiSwitchEvent) => {
|
||||
setUpdateError(null);
|
||||
setIsEditMode(event.target.checked);
|
||||
};
|
||||
|
||||
const { originalSettings, settingsString } = getEditableSettings(data, isIndexOpen);
|
||||
const { originalSettings, settingsString } = getEditableSettings({
|
||||
data,
|
||||
isIndexOpen,
|
||||
editableIndexSettings,
|
||||
});
|
||||
const [editableSettings, setEditableSettings] = useState(settingsString);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [updateError, setUpdateError] = useState<Error | null>(null);
|
||||
|
|
|
@ -41,11 +41,18 @@ export class IndexMgmtUIPlugin {
|
|||
enableIndexActions,
|
||||
enableLegacyTemplates,
|
||||
enableIndexStats,
|
||||
editableIndexSettings,
|
||||
} = this.ctx.config.get<ClientConfigType>();
|
||||
|
||||
if (isIndexManagementUiEnabled) {
|
||||
const { fleet, usageCollection, management, cloud } = plugins;
|
||||
const kibanaVersion = new SemVer(this.ctx.env.packageInfo.version);
|
||||
const config = {
|
||||
enableIndexActions: enableIndexActions ?? true,
|
||||
enableLegacyTemplates: enableLegacyTemplates ?? true,
|
||||
enableIndexStats: enableIndexStats ?? true,
|
||||
editableIndexSettings: editableIndexSettings ?? 'all',
|
||||
};
|
||||
management.sections.section.data.registerApp({
|
||||
id: PLUGIN.id,
|
||||
title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }),
|
||||
|
@ -59,9 +66,7 @@ export class IndexMgmtUIPlugin {
|
|||
extensionsService: this.extensionsService,
|
||||
isFleetEnabled: Boolean(fleet),
|
||||
kibanaVersion,
|
||||
enableIndexActions,
|
||||
enableLegacyTemplates,
|
||||
enableIndexStats,
|
||||
config,
|
||||
cloud,
|
||||
});
|
||||
},
|
||||
|
|
|
@ -35,4 +35,5 @@ export interface ClientConfigType {
|
|||
enableIndexActions?: boolean;
|
||||
enableLegacyTemplates?: boolean;
|
||||
enableIndexStats?: boolean;
|
||||
editableIndexSettings?: 'all' | 'limited';
|
||||
}
|
||||
|
|
|
@ -41,6 +41,12 @@ const schemaLatest = schema.object(
|
|||
// We take this approach in order to have a central place (serverless.yml) for serverless config across Kibana
|
||||
serverless: schema.boolean({ defaultValue: true }),
|
||||
}),
|
||||
editableIndexSettings: offeringBasedSchema({
|
||||
// on serverless only a limited set of index settings can be edited
|
||||
serverless: schema.oneOf([schema.literal('all'), schema.literal('limited')], {
|
||||
defaultValue: 'all',
|
||||
}),
|
||||
}),
|
||||
},
|
||||
{ defaultValue: undefined }
|
||||
);
|
||||
|
@ -51,6 +57,7 @@ const configLatest: PluginConfigDescriptor<IndexManagementConfig> = {
|
|||
enableIndexActions: true,
|
||||
enableLegacyTemplates: true,
|
||||
enableIndexStats: true,
|
||||
editableIndexSettings: true,
|
||||
},
|
||||
schema: schemaLatest,
|
||||
deprecations: ({ unused }) => [unused('dev.enableIndexDetailsPage', { level: 'warning' })],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue