mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[ILM] Add index templates to "add policy" modal in policies table (#77077)
* [ILM] Add index templates to "add policy" modal in policies table * [ILM] Change select to a combobox in 'add policy' modal in policies table * [ILM] Add not found message to "add policy to template" modal * [ILM] Fix api integration test * [ILM] Fix type check error * [ILM] Add PR review suggestions * [ILM] Fix type check error Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
77bd7ea9cf
commit
337fe73d09
6 changed files with 344 additions and 214 deletions
|
@ -4,13 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { get, find } from 'lodash';
|
||||
import React, { Fragment, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import {
|
||||
EuiCallOut,
|
||||
EuiSelect,
|
||||
EuiComboBox,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiOverlayMask,
|
||||
|
@ -18,82 +17,42 @@ import {
|
|||
EuiFieldText,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiSwitch,
|
||||
EuiButton,
|
||||
EuiComboBoxOptionOption,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { PolicyFromES } from '../../../../../common/types';
|
||||
import { LearnMoreLink } from '../../edit_policy/components';
|
||||
import { addLifecyclePolicyToTemplate, loadIndexTemplates } from '../../../services/api';
|
||||
import { addLifecyclePolicyToTemplate, useLoadIndexTemplates } from '../../../services/api';
|
||||
import { toasts } from '../../../services/notification';
|
||||
import { showApiError } from '../../../services/api_errors';
|
||||
import { LearnMoreLink } from '../../edit_policy/components';
|
||||
|
||||
interface Props {
|
||||
policy: PolicyFromES;
|
||||
onCancel: () => void;
|
||||
}
|
||||
interface State {
|
||||
templates: Array<{ name: string }>;
|
||||
templateName?: string;
|
||||
aliasName?: string;
|
||||
templateError?: string;
|
||||
}
|
||||
export class AddPolicyToTemplateConfirmModal extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
templates: [],
|
||||
};
|
||||
}
|
||||
async componentDidMount() {
|
||||
const templates = await loadIndexTemplates();
|
||||
this.setState({ templates });
|
||||
}
|
||||
addPolicyToTemplate = async () => {
|
||||
const { policy, onCancel } = this.props;
|
||||
const { templateName, aliasName } = this.state;
|
||||
const policyName = policy.name;
|
||||
if (!templateName) {
|
||||
this.setState({
|
||||
templateError: i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.noTemplateSelectedErrorMessage',
|
||||
{ defaultMessage: 'You must select an index template.' }
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await addLifecyclePolicyToTemplate({
|
||||
policyName,
|
||||
templateName,
|
||||
aliasName,
|
||||
});
|
||||
const message = i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.successMessage',
|
||||
{
|
||||
defaultMessage: 'Added policy {policyName} to index template {templateName}',
|
||||
values: { policyName, templateName },
|
||||
}
|
||||
);
|
||||
toasts.addSuccess(message);
|
||||
onCancel();
|
||||
} catch (e) {
|
||||
const title = i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.errorMessage',
|
||||
{
|
||||
defaultMessage: 'Error adding policy "{policyName}" to index template {templateName}',
|
||||
values: { policyName, templateName },
|
||||
}
|
||||
);
|
||||
showApiError(e, title);
|
||||
}
|
||||
};
|
||||
renderTemplateHasPolicyWarning() {
|
||||
const selectedTemplate = this.getSelectedTemplate();
|
||||
const existingPolicyName = get(selectedTemplate, 'settings.index.lifecycle.name');
|
||||
|
||||
export const AddPolicyToTemplateConfirmModal: React.FunctionComponent<Props> = ({
|
||||
policy,
|
||||
onCancel,
|
||||
}) => {
|
||||
const [isLegacy, setIsLegacy] = useState<boolean>(false);
|
||||
const [templateName, setTemplateName] = useState<string>('');
|
||||
const [aliasName, setAliasName] = useState<string>('');
|
||||
const [templateError, setTemplateError] = useState<string>('');
|
||||
|
||||
const { error, isLoading, data: templates, resendRequest } = useLoadIndexTemplates(isLegacy);
|
||||
|
||||
const renderTemplateHasPolicyWarning = () => {
|
||||
const selectedTemplate = templates!.find((template) => template.name === templateName);
|
||||
const existingPolicyName = selectedTemplate?.settings?.index?.lifecycle?.name;
|
||||
if (!existingPolicyName) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiCallOut
|
||||
style={{ maxWidth: 400 }}
|
||||
title={
|
||||
|
@ -116,57 +75,40 @@ export class AddPolicyToTemplateConfirmModal extends Component<Props, State> {
|
|||
<EuiSpacer size="s" />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
getSelectedTemplate() {
|
||||
const { templates, templateName } = this.state;
|
||||
return find(templates, (template) => template.name === templateName);
|
||||
}
|
||||
renderForm() {
|
||||
const { templates, templateName, templateError } = this.state;
|
||||
const options = templates.map(({ name }) => {
|
||||
return {
|
||||
value: name,
|
||||
text: name,
|
||||
};
|
||||
});
|
||||
options.unshift({
|
||||
value: '',
|
||||
text: i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.chooseTemplateMessage',
|
||||
{
|
||||
defaultMessage: 'Select an index template',
|
||||
}
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const renderUnableToLoadTemplatesCallout = () => {
|
||||
const { statusCode = '', message = '' } = error!;
|
||||
return (
|
||||
<EuiForm>
|
||||
{this.renderTemplateHasPolicyWarning()}
|
||||
<EuiFormRow
|
||||
isInvalid={!!templateError}
|
||||
error={templateError}
|
||||
label={
|
||||
<Fragment>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiCallOut
|
||||
style={{ maxWidth: 400 }}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.chooseTemplateLabel"
|
||||
defaultMessage="Index template"
|
||||
id="xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.errorLoadingTemplatesTitle"
|
||||
defaultMessage="Unable to load index templates"
|
||||
/>
|
||||
}
|
||||
color="danger"
|
||||
>
|
||||
<EuiSelect
|
||||
options={options}
|
||||
value={templateName}
|
||||
onChange={(e) => {
|
||||
this.setState({ templateError: undefined, templateName: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{this.renderAliasFormElement()}
|
||||
</EuiForm>
|
||||
<p>
|
||||
{message} ({statusCode})
|
||||
</p>
|
||||
<EuiButton isLoading={isLoading} color="danger" onClick={resendRequest}>
|
||||
<FormattedMessage
|
||||
id="xpack.indexLifecycleMgmt.indexManagementTable.addLifecyclePolicyToTemplateConfirmModal.errorLoadingTemplatesButton"
|
||||
defaultMessage="Try again"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size="s" />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
renderAliasFormElement = () => {
|
||||
const { aliasName } = this.state;
|
||||
const { policy } = this.props;
|
||||
const showAliasTextInput = policy && get(policy, 'policy.phases.hot.actions.rollover');
|
||||
};
|
||||
|
||||
const renderAliasFormElement = () => {
|
||||
const showAliasTextInput = policy.policy.phases.hot?.actions.rollover;
|
||||
if (!showAliasTextInput) {
|
||||
return null;
|
||||
}
|
||||
|
@ -182,62 +124,178 @@ export class AddPolicyToTemplateConfirmModal extends Component<Props, State> {
|
|||
<EuiFieldText
|
||||
value={aliasName}
|
||||
onChange={(e) => {
|
||||
this.setState({ aliasName: e.target.value });
|
||||
setAliasName(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
};
|
||||
render() {
|
||||
const { policy, onCancel } = this.props;
|
||||
const title = i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.title',
|
||||
{
|
||||
defaultMessage: 'Add policy "{name}" to index template',
|
||||
values: { name: policy.name },
|
||||
}
|
||||
);
|
||||
|
||||
const renderForm = () => {
|
||||
let options: EuiComboBoxOptionOption[] = [];
|
||||
if (templates) {
|
||||
options = templates.map(({ name }) => {
|
||||
return {
|
||||
label: name,
|
||||
};
|
||||
});
|
||||
}
|
||||
const onComboChange = (comboOptions: EuiComboBoxOptionOption[]) => {
|
||||
setTemplateError('');
|
||||
setTemplateName(comboOptions.length > 0 ? comboOptions[0].label : '');
|
||||
};
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiConfirmModal
|
||||
title={title}
|
||||
onCancel={onCancel}
|
||||
onConfirm={this.addPolicyToTemplate}
|
||||
cancelButtonText={i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.cancelButton',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
)}
|
||||
confirmButtonText={i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.confirmButton',
|
||||
{
|
||||
defaultMessage: 'Add policy',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiText>
|
||||
<p>
|
||||
<EuiForm>
|
||||
<EuiFormRow>
|
||||
<EuiSwitch
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.explanationText"
|
||||
defaultMessage="This will apply the lifecycle policy to
|
||||
all indices which match the index template."
|
||||
/>{' '}
|
||||
<LearnMoreLink
|
||||
docPath="indices-templates.html"
|
||||
text={
|
||||
<FormattedMessage
|
||||
id="xpack.indexLifecycleMgmt.editPolicy.learnAboutIndexTemplatesLink"
|
||||
defaultMessage="Learn about index templates"
|
||||
/>
|
||||
}
|
||||
id="xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.showLegacyTemplates"
|
||||
defaultMessage="Show legacy index templates"
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
{this.renderForm()}
|
||||
</EuiConfirmModal>
|
||||
</EuiOverlayMask>
|
||||
}
|
||||
checked={isLegacy}
|
||||
onChange={(e) => {
|
||||
setTemplateName('');
|
||||
setIsLegacy(e.target.checked);
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{error ? (
|
||||
renderUnableToLoadTemplatesCallout()
|
||||
) : (
|
||||
<>
|
||||
{renderTemplateHasPolicyWarning()}
|
||||
<EuiFormRow
|
||||
isInvalid={!!templateError}
|
||||
error={templateError}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.chooseTemplateLabel"
|
||||
defaultMessage="Index template"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiComboBox
|
||||
isLoading={isLoading}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.chooseTemplateMessage',
|
||||
{
|
||||
defaultMessage: 'Select an index template',
|
||||
}
|
||||
)}
|
||||
options={options}
|
||||
selectedOptions={
|
||||
templateName
|
||||
? [
|
||||
{
|
||||
label: templateName,
|
||||
},
|
||||
]
|
||||
: []
|
||||
}
|
||||
onChange={onComboChange}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
isClearable={true}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</>
|
||||
)}
|
||||
{renderAliasFormElement()}
|
||||
</EuiForm>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addPolicyToTemplate = async () => {
|
||||
const policyName = policy.name;
|
||||
if (!templateName) {
|
||||
setTemplateError(
|
||||
i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.noTemplateSelectedErrorMessage',
|
||||
{ defaultMessage: 'You must select an index template.' }
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await addLifecyclePolicyToTemplate(
|
||||
{
|
||||
policyName,
|
||||
templateName,
|
||||
aliasName: aliasName === '' ? undefined : aliasName,
|
||||
},
|
||||
isLegacy
|
||||
);
|
||||
const message = i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.successMessage',
|
||||
{
|
||||
defaultMessage: 'Added policy {policyName} to index template {templateName}',
|
||||
values: { policyName, templateName },
|
||||
}
|
||||
);
|
||||
toasts.addSuccess(message);
|
||||
onCancel();
|
||||
} catch (e) {
|
||||
const title = i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.errorMessage',
|
||||
{
|
||||
defaultMessage: 'Error adding policy "{policyName}" to index template {templateName}',
|
||||
values: { policyName, templateName },
|
||||
}
|
||||
);
|
||||
showApiError(e, title);
|
||||
}
|
||||
};
|
||||
|
||||
const title = i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.title',
|
||||
{
|
||||
defaultMessage: 'Add policy "{name}" to index template',
|
||||
values: { name: policy.name },
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiConfirmModal
|
||||
title={title}
|
||||
onCancel={onCancel}
|
||||
onConfirm={addPolicyToTemplate}
|
||||
cancelButtonText={i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.cancelButton',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
)}
|
||||
confirmButtonText={i18n.translate(
|
||||
'xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.confirmButton',
|
||||
{
|
||||
defaultMessage: 'Add policy',
|
||||
}
|
||||
)}
|
||||
confirmButtonDisabled={isLoading || !!error || !templates}
|
||||
>
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.indexLifecycleMgmt.policyTable.addLifecyclePolicyToTemplateConfirmModal.explanationText"
|
||||
defaultMessage="This will apply the lifecycle policy to
|
||||
all indices which match the index template."
|
||||
/>{' '}
|
||||
<LearnMoreLink
|
||||
docPath="indices-templates.html"
|
||||
text={
|
||||
<FormattedMessage
|
||||
id="xpack.indexLifecycleMgmt.editPolicy.learnAboutIndexTemplatesLink"
|
||||
defaultMessage="Learn about index templates"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
{renderForm()}
|
||||
</EuiConfirmModal>
|
||||
</EuiOverlayMask>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,10 +17,7 @@ import {
|
|||
} from '../constants';
|
||||
import { trackUiMetric } from './ui_metric';
|
||||
import { sendGet, sendPost, sendDelete, useRequest } from './http';
|
||||
|
||||
interface GenericObject {
|
||||
[key: string]: any;
|
||||
}
|
||||
import { IndexSettings } from '../../../../index_management/common/types';
|
||||
|
||||
export const useLoadNodes = () => {
|
||||
return useRequest<ListNodesRouteResponse>({
|
||||
|
@ -37,9 +34,14 @@ export const useLoadNodeDetails = (selectedNodeAttrs: string) => {
|
|||
});
|
||||
};
|
||||
|
||||
export async function loadIndexTemplates() {
|
||||
return await sendGet(`templates`);
|
||||
}
|
||||
export const useLoadIndexTemplates = (legacy: boolean = false) => {
|
||||
return useRequest<Array<{ name: string; settings: IndexSettings }>>({
|
||||
path: 'templates',
|
||||
query: { legacy },
|
||||
method: 'get',
|
||||
initialData: [],
|
||||
});
|
||||
};
|
||||
|
||||
export async function loadPolicies(withIndices: boolean) {
|
||||
return await sendGet('policies', { withIndices });
|
||||
|
@ -89,8 +91,15 @@ export const addLifecyclePolicyToIndex = async (body: {
|
|||
return response;
|
||||
};
|
||||
|
||||
export const addLifecyclePolicyToTemplate = async (body: GenericObject) => {
|
||||
const response = await sendPost(`template`, body);
|
||||
export const addLifecyclePolicyToTemplate = async (
|
||||
body: {
|
||||
policyName: string;
|
||||
templateName: string;
|
||||
aliasName?: string;
|
||||
},
|
||||
legacy: boolean = false
|
||||
) => {
|
||||
const response = await sendPost(`template`, body, { legacy });
|
||||
// Only track successful actions.
|
||||
trackUiMetric(METRIC_TYPE.COUNT, UIM_POLICY_ATTACH_INDEX_TEMPLATE);
|
||||
return response;
|
||||
|
|
|
@ -30,8 +30,8 @@ function getFullPath(path: string): string {
|
|||
return apiPrefix;
|
||||
}
|
||||
|
||||
export function sendPost(path: string, payload: GenericObject) {
|
||||
return _httpClient.post(getFullPath(path), { body: JSON.stringify(payload) });
|
||||
export function sendPost(path: string, payload: GenericObject, query?: GenericObject) {
|
||||
return _httpClient.post(getFullPath(path), { body: JSON.stringify(payload), query });
|
||||
}
|
||||
|
||||
export function sendGet(path: string, query?: GenericObject): any {
|
||||
|
|
|
@ -5,45 +5,78 @@
|
|||
*/
|
||||
|
||||
import { merge } from 'lodash';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { LegacyAPICaller } from 'src/core/server';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { TemplateFromEs, TemplateSerialized } from '../../../../../index_management/common/types';
|
||||
import { LegacyTemplateSerialized } from '../../../../../index_management/server';
|
||||
import { RouteDependencies } from '../../../types';
|
||||
import { addBasePath } from '../../../services';
|
||||
|
||||
async function getIndexTemplate(
|
||||
async function getLegacyIndexTemplate(
|
||||
callAsCurrentUser: LegacyAPICaller,
|
||||
templateName: string
|
||||
): Promise<LegacyTemplateSerialized> {
|
||||
): Promise<LegacyTemplateSerialized | undefined> {
|
||||
const response = await callAsCurrentUser('indices.getTemplate', { name: templateName });
|
||||
return response[templateName];
|
||||
}
|
||||
|
||||
async function getIndexTemplate(
|
||||
callAsCurrentUser: LegacyAPICaller,
|
||||
templateName: string
|
||||
): Promise<TemplateSerialized | undefined> {
|
||||
const params = {
|
||||
method: 'GET',
|
||||
path: `/_index_template/${encodeURIComponent(templateName)}`,
|
||||
// we allow 404 incase the user shutdown security in-between the check and now
|
||||
ignore: [404],
|
||||
};
|
||||
|
||||
const { index_templates: templates } = await callAsCurrentUser<{
|
||||
index_templates: TemplateFromEs[];
|
||||
}>('transport.request', params);
|
||||
return templates?.find((template) => template.name === templateName)?.index_template;
|
||||
}
|
||||
|
||||
async function updateIndexTemplate(
|
||||
callAsCurrentUser: LegacyAPICaller,
|
||||
isLegacy: boolean,
|
||||
templateName: string,
|
||||
policyName: string,
|
||||
aliasName?: string
|
||||
): Promise<any> {
|
||||
// Fetch existing template
|
||||
const template = await getIndexTemplate(callAsCurrentUser, templateName);
|
||||
merge(template, {
|
||||
settings: {
|
||||
index: {
|
||||
lifecycle: {
|
||||
name: policyName,
|
||||
rollover_alias: aliasName,
|
||||
},
|
||||
const settings = {
|
||||
index: {
|
||||
lifecycle: {
|
||||
name: policyName,
|
||||
rollover_alias: aliasName,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const indexTemplate = isLegacy
|
||||
? await getLegacyIndexTemplate(callAsCurrentUser, templateName)
|
||||
: await getIndexTemplate(callAsCurrentUser, templateName);
|
||||
if (!indexTemplate) {
|
||||
return false;
|
||||
}
|
||||
if (isLegacy) {
|
||||
merge(indexTemplate, { settings });
|
||||
} else {
|
||||
merge(indexTemplate, {
|
||||
template: {
|
||||
settings,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const pathPrefix = isLegacy ? '/_template/' : '/_index_template/';
|
||||
const params = {
|
||||
method: 'PUT',
|
||||
path: `/_template/${encodeURIComponent(templateName)}`,
|
||||
path: `${pathPrefix}${encodeURIComponent(templateName)}`,
|
||||
ignore: [404],
|
||||
body: template,
|
||||
body: indexTemplate,
|
||||
};
|
||||
|
||||
return await callAsCurrentUser('transport.request', params);
|
||||
|
@ -55,20 +88,35 @@ const bodySchema = schema.object({
|
|||
aliasName: schema.maybe(schema.string()),
|
||||
});
|
||||
|
||||
const querySchema = schema.object({
|
||||
legacy: schema.maybe(schema.oneOf([schema.literal('true'), schema.literal('false')])),
|
||||
});
|
||||
|
||||
export function registerAddPolicyRoute({ router, license, lib }: RouteDependencies) {
|
||||
router.post(
|
||||
{ path: addBasePath('/template'), validate: { body: bodySchema } },
|
||||
{ path: addBasePath('/template'), validate: { body: bodySchema, query: querySchema } },
|
||||
license.guardApiRoute(async (context, request, response) => {
|
||||
const body = request.body as typeof bodySchema.type;
|
||||
const { templateName, policyName, aliasName } = body;
|
||||
|
||||
const isLegacy = (request.query as TypeOf<typeof querySchema>).legacy === 'true';
|
||||
try {
|
||||
await updateIndexTemplate(
|
||||
const updatedTemplate = await updateIndexTemplate(
|
||||
context.core.elasticsearch.legacy.client.callAsCurrentUser,
|
||||
isLegacy,
|
||||
templateName,
|
||||
policyName,
|
||||
aliasName
|
||||
);
|
||||
if (!updatedTemplate) {
|
||||
return response.notFound({
|
||||
body: i18n.translate('xpack.indexLifecycleMgmt.templateNotFoundMessage', {
|
||||
defaultMessage: `Template {name} not found.`,
|
||||
values: {
|
||||
name: templateName,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
return response.ok();
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
|
|
|
@ -4,20 +4,16 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { LegacyAPICaller } from 'src/core/server';
|
||||
import { LegacyTemplateSerialized } from '../../../../../index_management/server';
|
||||
|
||||
import { LegacyAPICaller } from 'kibana/server';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import {
|
||||
IndexSettings,
|
||||
LegacyTemplateSerialized,
|
||||
TemplateFromEs,
|
||||
} from '../../../../../index_management/common/types';
|
||||
import { RouteDependencies } from '../../../types';
|
||||
import { addBasePath } from '../../../services';
|
||||
|
||||
/**
|
||||
* We don't want to output system template (whose name starts with a ".") which don't
|
||||
* have a time base index pattern (with a wildcard in it) as those templates are already
|
||||
* assigned to a single index.
|
||||
*
|
||||
* @param {String} templateName The index template
|
||||
* @param {Array} indexPatterns Index patterns
|
||||
*/
|
||||
function isReservedSystemTemplate(templateName: string, indexPatterns: string[]): boolean {
|
||||
return (
|
||||
templateName.startsWith('kibana_index_template') ||
|
||||
|
@ -28,9 +24,9 @@ function isReservedSystemTemplate(templateName: string, indexPatterns: string[])
|
|||
);
|
||||
}
|
||||
|
||||
function filterAndFormatTemplates(templates: {
|
||||
function filterLegacyTemplates(templates: {
|
||||
[templateName: string]: LegacyTemplateSerialized;
|
||||
}): Array<{}> {
|
||||
}): Array<{ name: string; settings?: IndexSettings }> {
|
||||
const formattedTemplates = [];
|
||||
const templateNames = Object.keys(templates);
|
||||
for (const templateName of templateNames) {
|
||||
|
@ -40,11 +36,6 @@ function filterAndFormatTemplates(templates: {
|
|||
continue;
|
||||
}
|
||||
const formattedTemplate = {
|
||||
index_lifecycle_name:
|
||||
settings!.index && settings!.index.lifecycle ? settings!.index.lifecycle.name : undefined,
|
||||
index_patterns,
|
||||
allocation_rules:
|
||||
settings!.index && settings!.index.routing ? settings!.index.routing : undefined,
|
||||
settings,
|
||||
name: templateName,
|
||||
};
|
||||
|
@ -53,12 +44,30 @@ function filterAndFormatTemplates(templates: {
|
|||
return formattedTemplates;
|
||||
}
|
||||
|
||||
function filterTemplates(
|
||||
templates:
|
||||
| { index_templates: TemplateFromEs[] }
|
||||
| { [templateName: string]: LegacyTemplateSerialized },
|
||||
isLegacy: boolean
|
||||
): Array<{ name: string; settings?: IndexSettings }> {
|
||||
if (isLegacy) {
|
||||
return filterLegacyTemplates(templates as { [templateName: string]: LegacyTemplateSerialized });
|
||||
}
|
||||
const { index_templates: indexTemplates } = templates as { index_templates: TemplateFromEs[] };
|
||||
return indexTemplates.map((template: TemplateFromEs) => {
|
||||
return { name: template.name, settings: template.index_template.template?.settings };
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchTemplates(
|
||||
callAsCurrentUser: LegacyAPICaller
|
||||
): Promise<{ [templateName: string]: LegacyTemplateSerialized }> {
|
||||
callAsCurrentUser: LegacyAPICaller,
|
||||
isLegacy: boolean
|
||||
): Promise<
|
||||
{ index_templates: TemplateFromEs[] } | { [templateName: string]: LegacyTemplateSerialized }
|
||||
> {
|
||||
const params = {
|
||||
method: 'GET',
|
||||
path: '/_template',
|
||||
path: isLegacy ? '/_template' : '/_index_template',
|
||||
// we allow 404 incase the user shutdown security in-between the check and now
|
||||
ignore: [404],
|
||||
};
|
||||
|
@ -66,15 +75,21 @@ async function fetchTemplates(
|
|||
return await callAsCurrentUser('transport.request', params);
|
||||
}
|
||||
|
||||
const querySchema = schema.object({
|
||||
legacy: schema.maybe(schema.oneOf([schema.literal('true'), schema.literal('false')])),
|
||||
});
|
||||
|
||||
export function registerFetchRoute({ router, license, lib }: RouteDependencies) {
|
||||
router.get(
|
||||
{ path: addBasePath('/templates'), validate: false },
|
||||
{ path: addBasePath('/templates'), validate: { query: querySchema } },
|
||||
license.guardApiRoute(async (context, request, response) => {
|
||||
const isLegacy = (request.query as TypeOf<typeof querySchema>).legacy === 'true';
|
||||
try {
|
||||
const templates = await fetchTemplates(
|
||||
context.core.elasticsearch.legacy.client.callAsCurrentUser
|
||||
context.core.elasticsearch.legacy.client.callAsCurrentUser,
|
||||
isLegacy
|
||||
);
|
||||
const okResponse = { body: filterAndFormatTemplates(templates) };
|
||||
const okResponse = { body: filterTemplates(templates, isLegacy) };
|
||||
return response.ok(okResponse);
|
||||
} catch (e) {
|
||||
if (lib.isEsError(e)) {
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
import { API_BASE_PATH } from './constants';
|
||||
|
||||
export const registerHelpers = ({ supertest }) => {
|
||||
const loadTemplates = () => supertest.get(`${API_BASE_PATH}/templates`);
|
||||
const loadTemplates = () => supertest.get(`${API_BASE_PATH}/templates?legacy=true`);
|
||||
|
||||
const addPolicyToTemplate = (templateName, policyName, aliasName) =>
|
||||
supertest.post(`${API_BASE_PATH}/template`).set('kbn-xsrf', 'xxx').send({
|
||||
supertest.post(`${API_BASE_PATH}/template?legacy=true`).set('kbn-xsrf', 'xxx').send({
|
||||
templateName,
|
||||
policyName,
|
||||
aliasName,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue