-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Fleet] add ilm policy per data stream #85492
Changes from all commits
6e31537
128db0f
21b498e
204d903
e1b3f5c
f6433aa
0b528da
6f74373
109bb7f
dfa2410
d058bb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { SavedObjectsClientContract } from 'kibana/server'; | ||
import { | ||
ElasticsearchAssetType, | ||
EsAssetReference, | ||
InstallablePackage, | ||
RegistryDataStream, | ||
} from '../../../../../common/types/models'; | ||
import { CallESAsCurrentUser } from '../../../../types'; | ||
import { getInstallation } from '../../packages'; | ||
import { deleteIlmRefs, deleteIlms } from './remove'; | ||
import { saveInstalledEsRefs } from '../../packages/install'; | ||
import { getAsset } from '../transform/common'; | ||
|
||
interface IlmInstallation { | ||
installationName: string; | ||
content: string; | ||
} | ||
|
||
interface IlmPathDataset { | ||
path: string; | ||
dataStream: RegistryDataStream; | ||
} | ||
|
||
export const installIlmForDataStream = async ( | ||
registryPackage: InstallablePackage, | ||
paths: string[], | ||
callCluster: CallESAsCurrentUser, | ||
savedObjectsClient: SavedObjectsClientContract | ||
) => { | ||
const installation = await getInstallation({ savedObjectsClient, pkgName: registryPackage.name }); | ||
let previousInstalledIlmEsAssets: EsAssetReference[] = []; | ||
if (installation) { | ||
previousInstalledIlmEsAssets = installation.installed_es.filter( | ||
({ type, id }) => type === ElasticsearchAssetType.dataStreamIlmPolicy | ||
); | ||
} | ||
|
||
// delete all previous ilm | ||
await deleteIlms( | ||
callCluster, | ||
previousInstalledIlmEsAssets.map((asset) => asset.id) | ||
); | ||
// install the latest dataset | ||
const dataStreams = registryPackage.data_streams; | ||
if (!dataStreams?.length) return []; | ||
const dataStreamIlmPaths = paths.filter((path) => isDataStreamIlm(path)); | ||
let installedIlms: EsAssetReference[] = []; | ||
if (dataStreamIlmPaths.length > 0) { | ||
const ilmPathDatasets = dataStreams.reduce<IlmPathDataset[]>((acc, dataStream) => { | ||
dataStreamIlmPaths.forEach((path) => { | ||
if (isDatasetIlm(path, dataStream.path)) { | ||
acc.push({ path, dataStream }); | ||
} | ||
}); | ||
return acc; | ||
}, []); | ||
|
||
const ilmRefs = ilmPathDatasets.reduce<EsAssetReference[]>((acc, ilmPathDataset) => { | ||
if (ilmPathDataset) { | ||
acc.push({ | ||
id: getIlmNameForInstallation(ilmPathDataset), | ||
type: ElasticsearchAssetType.dataStreamIlmPolicy, | ||
}); | ||
} | ||
return acc; | ||
}, []); | ||
|
||
await saveInstalledEsRefs(savedObjectsClient, registryPackage.name, ilmRefs); | ||
|
||
const ilmInstallations: IlmInstallation[] = ilmPathDatasets.map( | ||
(ilmPathDataset: IlmPathDataset) => { | ||
return { | ||
installationName: getIlmNameForInstallation(ilmPathDataset), | ||
content: getAsset(ilmPathDataset.path).toString('utf-8'), | ||
}; | ||
} | ||
); | ||
|
||
const installationPromises = ilmInstallations.map(async (ilmInstallation) => { | ||
return handleIlmInstall({ callCluster, ilmInstallation }); | ||
}); | ||
|
||
installedIlms = await Promise.all(installationPromises).then((results) => results.flat()); | ||
} | ||
|
||
if (previousInstalledIlmEsAssets.length > 0) { | ||
const currentInstallation = await getInstallation({ | ||
savedObjectsClient, | ||
pkgName: registryPackage.name, | ||
}); | ||
|
||
// remove the saved object reference | ||
await deleteIlmRefs( | ||
savedObjectsClient, | ||
currentInstallation?.installed_es || [], | ||
registryPackage.name, | ||
previousInstalledIlmEsAssets.map((asset) => asset.id), | ||
installedIlms.map((installed) => installed.id) | ||
); | ||
} | ||
return installedIlms; | ||
}; | ||
|
||
async function handleIlmInstall({ | ||
callCluster, | ||
ilmInstallation, | ||
}: { | ||
callCluster: CallESAsCurrentUser; | ||
ilmInstallation: IlmInstallation; | ||
}): Promise<EsAssetReference> { | ||
await callCluster('transport.request', { | ||
method: 'PUT', | ||
path: `/_ilm/policy/${ilmInstallation.installationName}`, | ||
body: ilmInstallation.content, | ||
}); | ||
|
||
return { id: ilmInstallation.installationName, type: ElasticsearchAssetType.dataStreamIlmPolicy }; | ||
} | ||
|
||
const isDataStreamIlm = (path: string) => { | ||
return new RegExp('(?<package>.*)/data_stream/(?<dataset>.*)/elasticsearch/ilm/*.*').test(path); | ||
}; | ||
|
||
const isDatasetIlm = (path: string, datasetName: string) => { | ||
return new RegExp(`(?<package>.*)/data_stream\\/${datasetName}/elasticsearch/ilm/*.*`).test(path); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same questions about groups and escaping as above |
||
}; | ||
|
||
const getIlmNameForInstallation = (ilmPathDataset: IlmPathDataset) => { | ||
const filename = ilmPathDataset?.path.split('/')?.pop()?.split('.')[0]; | ||
return `${ilmPathDataset.dataStream.type}-${ilmPathDataset.dataStream.package}.${ilmPathDataset.dataStream.path}-${filename}`; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { SavedObjectsClientContract } from 'kibana/server'; | ||
import { CallESAsCurrentUser, ElasticsearchAssetType, EsAssetReference } from '../../../../types'; | ||
import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../../common/constants'; | ||
|
||
export const deleteIlms = async (callCluster: CallESAsCurrentUser, ilmPolicyIds: string[]) => { | ||
await Promise.all( | ||
ilmPolicyIds.map(async (ilmPolicyId) => { | ||
await callCluster('transport.request', { | ||
method: 'DELETE', | ||
path: `_ilm/policy/${ilmPolicyId}`, | ||
ignore: [404, 400], | ||
}); | ||
}) | ||
); | ||
}; | ||
|
||
export const deleteIlmRefs = async ( | ||
savedObjectsClient: SavedObjectsClientContract, | ||
installedEsAssets: EsAssetReference[], | ||
pkgName: string, | ||
installedEsIdToRemove: string[], | ||
currentInstalledEsIlmIds: string[] | ||
) => { | ||
const seen = new Set<string>(); | ||
const filteredAssets = installedEsAssets.filter(({ type, id }) => { | ||
if (type !== ElasticsearchAssetType.dataStreamIlmPolicy) return true; | ||
const add = | ||
(currentInstalledEsIlmIds.includes(id) || !installedEsIdToRemove.includes(id)) && | ||
!seen.has(id); | ||
seen.add(id); | ||
return add; | ||
}); | ||
return savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { | ||
installed_es: filteredAssets, | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ import { updateCurrentWriteIndices } from '../elasticsearch/template/template'; | |
import { deleteKibanaSavedObjectsAssets } from './remove'; | ||
import { installTransform } from '../elasticsearch/transform/install'; | ||
import { createInstallation, saveKibanaAssetsRefs, updateVersion } from './install'; | ||
import { installIlmForDataStream } from '../elasticsearch/datastream_ilm/install'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this file missing from the commit? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, updated |
||
import { saveArchiveEntries } from '../archive/storage'; | ||
import { ConcurrentInstallOperationError } from '../../../errors'; | ||
|
||
|
@@ -134,6 +135,13 @@ export async function _installPackage({ | |
// per data stream and we should then save them | ||
await installILMPolicy(paths, callCluster); | ||
|
||
const installedDataStreamIlm = await installIlmForDataStream( | ||
packageInfo, | ||
paths, | ||
callCluster, | ||
savedObjectsClient | ||
); | ||
|
||
// installs versionized pipelines without removing currently installed ones | ||
const installedPipelines = await installPipelines( | ||
packageInfo, | ||
|
@@ -212,6 +220,7 @@ export async function _installPackage({ | |
return [ | ||
...installedKibanaAssetsRefs, | ||
...installedPipelines, | ||
...installedDataStreamIlm, | ||
...installedTemplateRefs, | ||
...installedTransforms, | ||
]; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ import { deleteTransforms } from '../elasticsearch/transform/remove'; | |
import { packagePolicyService, appContextService } from '../..'; | ||
import { splitPkgKey } from '../registry'; | ||
import { deletePackageCache } from '../archive'; | ||
import { deleteIlms } from '../elasticsearch/datastream_ilm/remove'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here, is this file missing from the commit? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, updated |
||
import { removeArchiveEntries } from '../archive/storage'; | ||
|
||
export async function removeInstallation(options: { | ||
|
@@ -93,6 +94,8 @@ function deleteESAssets(installedObjects: EsAssetReference[], callCluster: CallE | |
return deleteTemplate(callCluster, id); | ||
} else if (assetType === ElasticsearchAssetType.transform) { | ||
return deleteTransforms(callCluster, [id]); | ||
} else if (assetType === ElasticsearchAssetType.dataStreamIlmPolicy) { | ||
return deleteIlms(callCluster, [id]); | ||
} | ||
}); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"policy": { | ||
"phases": { | ||
"hot": { | ||
"min_age": "0ms", | ||
"actions": { | ||
"rollover": { | ||
"max_size": "50gb", | ||
"max_age": "30d" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Do we need the
?<package>
or the?<dataset>
? I believe those are so we can extract out those specific fields.Do we need to escape all the
/
?https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
I was playing around with it here:
https://regex101.com/r/GLnjJN/1
https://regex101.com/r/GLnjJN/2
I think we might need one of these:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need? No, probably not.
(?<package>.*)
vs.*
is adding a named capture group.It doesn't look like we access or use the groups, so that's unnecessary. Adding the label, though, somewhat acts as in-regex-documentation of what we're looking at. I do like that.
We do not need to escape the slashes here because it is a
new Regexp
constructor using a string. Not using slash-literals.