-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create the "Asset Inventory" Kibana Plugin (#202291)
## Summary Closes #201704. Create an empty "Asset Inventory" plugin with the minimal boilerplate required to set it up, install it and run it on Kibana with a blank slate. I generated the files using the `node scripts/generate_plugin <NAME>` script as per [this documentation page](https://docs.elastic.dev/kibana-dev-docs/getting-started/hello-world-app#2-option-2---use-the-automatic-plugin-generator). ### Screenshots <details><summary>Main page (Sample page)</summary> <img width="2498" alt="Screenshot 2024-11-29 at 14 20 57" src="https://github.com/user-attachments/assets/9d8a3751-519b-4661-bc90-cbb1e836b111"> </details> ### Implementation details - [x] Generated a new Kibana plugin with minimal boilerplate and zero dependencies - [x] Use [Cloud Security Posture](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_security_posture) plugin as configuration reference - [x] Render "Inventory" title on the main page - [x] Ensure the plugin is properly integrated into Kibana's build and can be loaded without errors - [x] Place the plugin under the `x-pack/plugins` directory - [x] Include Readme file - [x] Introduce placeholders for initialization of pipelines and transforms following [Cloud Security Posture](https://github.com/elastic/kibana/blob/main/x-pack/plugins/cloud_security_posture/server/plugin.ts) plugin initialize function. ### PR Checklist - [ ] No docs for now ~~[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials~~ - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Risks No risks at all since this is totally green-field and will be hidden by a feature toggle. --------- Co-authored-by: kibanamachine <[email protected]> Co-authored-by: Paulo Silva <[email protected]>
- Loading branch information
1 parent
a3496c9
commit e5b1773
Showing
21 changed files
with
462 additions
and
0 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Asset Inventory Kibana Plugin | ||
|
||
Centralized asset inventory experience within the Elastic Security solution. A central place for users to view and manage all their assets from different environments. | ||
|
||
--- | ||
|
||
## Development | ||
|
||
See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment. | ||
|
||
## Testing | ||
|
||
For general guidelines, read [Kibana Testing Guide](https://www.elastic.co/guide/en/kibana/current/development-tests.html) for more details |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
export const PLUGIN_ID = 'assetInventory'; | ||
export const PLUGIN_NAME = 'assetInventory'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"type": "plugin", | ||
"id": "@kbn/asset-inventory-plugin", | ||
"owner": ["@elastic/kibana-cloud-security-posture"], | ||
"group": "security", | ||
"visibility": "private", | ||
"description": "Centralized asset inventory experience within the Elastic Security solution. A central place for users to view and manage all their assets from different environments", | ||
"plugin": { | ||
"id": "assetInventory", | ||
"browser": true, | ||
"server": true, | ||
"configPath": ["xpack", "assetInventory"], | ||
"requiredPlugins": [], | ||
"requiredBundles": [], | ||
"optionalPlugins": [] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"author": "Elastic", | ||
"name": "@kbn/asset-inventory-plugin", | ||
"version": "1.0.0", | ||
"private": true, | ||
"license": "Elastic License 2.0" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import type { AppMountParameters, CoreStart } from '@kbn/core/public'; | ||
import type { AppPluginStartDependencies } from './types'; | ||
import { AssetInventoryApp } from './components/app'; | ||
|
||
export const renderApp = ( | ||
{ notifications, http }: CoreStart, | ||
{}: AppPluginStartDependencies, | ||
{ appBasePath, element }: AppMountParameters | ||
) => { | ||
ReactDOM.render( | ||
<AssetInventoryApp basename={appBasePath} notifications={notifications} http={http} />, | ||
element | ||
); | ||
|
||
return () => ReactDOM.unmountComponentAtNode(element); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import React from 'react'; | ||
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react'; | ||
import { BrowserRouter as Router } from '@kbn/shared-ux-router'; | ||
import { EuiPageTemplate, EuiTitle } from '@elastic/eui'; | ||
import type { CoreStart } from '@kbn/core/public'; | ||
|
||
interface AssetInventoryAppDeps { | ||
basename: string; | ||
notifications: CoreStart['notifications']; | ||
http: CoreStart['http']; | ||
} | ||
|
||
export const AssetInventoryApp = ({ basename }: AssetInventoryAppDeps) => { | ||
return ( | ||
<Router basename={basename}> | ||
<I18nProvider> | ||
<> | ||
<EuiPageTemplate restrictWidth="1000px"> | ||
<EuiPageTemplate.Header> | ||
<EuiTitle size="l"> | ||
<h1> | ||
<FormattedMessage id="assetInventory.helloWorldText" defaultMessage="Inventory" /> | ||
</h1> | ||
</EuiTitle> | ||
</EuiPageTemplate.Header> | ||
<EuiPageTemplate.Section /> | ||
</EuiPageTemplate> | ||
</> | ||
</I18nProvider> | ||
</Router> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import { AssetInventoryPlugin } from './plugin'; | ||
|
||
// This exports static code and TypeScript types, | ||
// as well as, Kibana Platform `plugin()` initializer. | ||
export function plugin() { | ||
return new AssetInventoryPlugin(); | ||
} | ||
export type { AssetInventoryPluginSetup, AssetInventoryPluginStart } from './types'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import type { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; | ||
import type { | ||
AssetInventoryPluginSetup, | ||
AssetInventoryPluginStart, | ||
AppPluginStartDependencies, | ||
} from './types'; | ||
|
||
export class AssetInventoryPlugin | ||
implements Plugin<AssetInventoryPluginSetup, AssetInventoryPluginStart> | ||
{ | ||
public setup(core: CoreSetup): AssetInventoryPluginSetup { | ||
return {}; | ||
} | ||
public start( | ||
coreStart: CoreStart, | ||
depsStart: AppPluginStartDependencies | ||
): AssetInventoryPluginStart { | ||
return { | ||
getAssetInventoryPage: async (params: AppMountParameters) => { | ||
// Load application bundle | ||
const { renderApp } = await import('./application'); | ||
// Render the application | ||
return renderApp(coreStart, depsStart as AppPluginStartDependencies, params); | ||
}, | ||
}; | ||
} | ||
|
||
public stop() {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-empty-interface | ||
export interface AssetInventoryPluginSetup {} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-empty-interface | ||
export interface AssetInventoryPluginStart {} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-empty-interface | ||
export interface AppPluginStartDependencies {} |
142 changes: 142 additions & 0 deletions
142
x-pack/plugins/asset_inventory/server/create_transforms/create_transforms.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import { transformError } from '@kbn/securitysolution-es-utils'; | ||
import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; | ||
import type { ElasticsearchClient, Logger } from '@kbn/core/server'; | ||
import { errors } from '@elastic/elasticsearch'; | ||
|
||
// TODO: Move transforms to integration package | ||
export const initializeTransforms = async ( | ||
esClient: ElasticsearchClient, | ||
logger: Logger | ||
): Promise<void> => { | ||
// Deletes old assets from previous versions as part of upgrade process | ||
await deletePreviousTransformsVersions(esClient, logger); | ||
// TODO initialize transforms here | ||
// await initializeTransform(esClient, <TRANSFORM_HERE>, logger); | ||
}; | ||
|
||
export const initializeTransform = async ( | ||
esClient: ElasticsearchClient, | ||
transform: TransformPutTransformRequest, | ||
logger: Logger | ||
) => { | ||
const success = await createTransformIfNotExists(esClient, transform, logger); | ||
|
||
if (success) { | ||
await startTransformIfNotStarted(esClient, transform.transform_id, logger); | ||
} | ||
}; | ||
|
||
/** | ||
* Checks if a transform exists, And if not creates it | ||
* | ||
* @param transform - the transform to create. If a transform with the same transform_id already exists, nothing is created or updated. | ||
* | ||
* @return true if the transform exits or created, false otherwise. | ||
*/ | ||
export const createTransformIfNotExists = async ( | ||
esClient: ElasticsearchClient, | ||
transform: TransformPutTransformRequest, | ||
logger: Logger | ||
) => { | ||
try { | ||
await esClient.transform.getTransform({ | ||
transform_id: transform.transform_id, | ||
}); | ||
return true; | ||
} catch (existErr) { | ||
const existError = transformError(existErr); | ||
if (existError.statusCode === 404) { | ||
try { | ||
await esClient.transform.putTransform(transform); | ||
return true; | ||
} catch (createErr) { | ||
const createError = transformError(createErr); | ||
logger.error( | ||
`Failed to create transform ${transform.transform_id}: ${createError.message}` | ||
); | ||
} | ||
} else { | ||
logger.error( | ||
`Failed to check if transform ${transform.transform_id} exists: ${existError.message}` | ||
); | ||
} | ||
} | ||
return false; | ||
}; | ||
|
||
export const startTransformIfNotStarted = async ( | ||
esClient: ElasticsearchClient, | ||
transformId: string, | ||
logger: Logger | ||
) => { | ||
try { | ||
const transformStats = await esClient.transform.getTransformStats({ | ||
transform_id: transformId, | ||
}); | ||
|
||
if (transformStats.count <= 0) { | ||
logger.error(`Failed starting transform ${transformId}: couldn't find transform`); | ||
return; | ||
} | ||
|
||
const fetchedTransformStats = transformStats.transforms[0]; | ||
|
||
// trying to restart the transform in case it comes to a full stop or failure | ||
if (fetchedTransformStats.state === 'stopped' || fetchedTransformStats.state === 'failed') { | ||
try { | ||
return await esClient.transform.startTransform({ transform_id: transformId }); | ||
} catch (startErr) { | ||
const startError = transformError(startErr); | ||
logger.error( | ||
`Failed to start transform ${transformId}. Transform State: Transform State: ${fetchedTransformStats.state}. Error: ${startError.message}` | ||
); | ||
} | ||
} | ||
|
||
if (fetchedTransformStats.state === 'stopping' || fetchedTransformStats.state === 'aborting') { | ||
logger.error( | ||
`Not starting transform ${transformId} since it's state is: ${fetchedTransformStats.state}` | ||
); | ||
} | ||
} catch (statsErr) { | ||
const statsError = transformError(statsErr); | ||
logger.error(`Failed to check if transform ${transformId} is started: ${statsError.message}`); | ||
} | ||
}; | ||
|
||
const deletePreviousTransformsVersions = async (esClient: ElasticsearchClient, logger: Logger) => { | ||
// TODO Concat all deprecated transforms versions | ||
const deprecatedTransforms: string[] = []; | ||
|
||
for (const transform of deprecatedTransforms) { | ||
const response = await deleteTransformSafe(esClient, logger, transform); | ||
if (response) return; | ||
} | ||
}; | ||
|
||
const deleteTransformSafe = async ( | ||
esClient: ElasticsearchClient, | ||
logger: Logger, | ||
name: string | ||
): Promise<boolean> => { | ||
try { | ||
await esClient.transform.deleteTransform({ transform_id: name, force: true }); | ||
logger.info(`Deleted transform successfully [Name: ${name}]`); | ||
return true; | ||
} catch (e) { | ||
if (e instanceof errors.ResponseError && e.statusCode === 404) { | ||
logger.trace(`Transform not exists [Name: ${name}]`); | ||
return false; | ||
} else { | ||
logger.error(`Failed to delete transform [Name: ${name}]`); | ||
logger.error(e); | ||
return false; | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
import type { PluginInitializerContext } from '@kbn/core/server'; | ||
|
||
// This exports static code and TypeScript types, | ||
// as well as, Kibana Platform `plugin()` initializer. | ||
|
||
export async function plugin(initializerContext: PluginInitializerContext) { | ||
const { AssetInventoryPlugin } = await import('./plugin'); | ||
return new AssetInventoryPlugin(initializerContext); | ||
} | ||
|
||
export type { AssetInventoryPluginSetup, AssetInventoryPluginStart } from './types'; |
Oops, something went wrong.