Revert "[Fleet] Allow snake cased Kibana assets (#77515)" (#82706)

This reverts commit 1cd477a793.
This commit is contained in:
Corey Robertson 2020-11-05 07:19:36 -05:00 committed by GitHub
parent db8f98cc0c
commit bc05e79b85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 48 additions and 219 deletions

View file

@ -25,7 +25,7 @@ describe('Ingest Manager - packageToPackagePolicy', () => {
dashboard: [],
visualization: [],
search: [],
index_pattern: [],
'index-pattern': [],
map: [],
},
},

View file

@ -35,21 +35,7 @@ export type ServiceName = 'kibana' | 'elasticsearch';
export type AgentAssetType = typeof agentAssetTypes;
export type AssetType = KibanaAssetType | ElasticsearchAssetType | ValueOf<AgentAssetType>;
/*
Enum mapping of a saved object asset type to how it would appear in a package file path (snake cased)
*/
export enum KibanaAssetType {
dashboard = 'dashboard',
visualization = 'visualization',
search = 'search',
indexPattern = 'index_pattern',
map = 'map',
}
/*
Enum of saved object types that are allowed to be installed
*/
export enum KibanaSavedObjectType {
dashboard = 'dashboard',
visualization = 'visualization',
search = 'search',
@ -285,7 +271,7 @@ export type NotInstalled<T = {}> = T & {
export type AssetReference = KibanaAssetReference | EsAssetReference;
export type KibanaAssetReference = Pick<SavedObjectReference, 'id'> & {
type: KibanaSavedObjectType;
type: KibanaAssetType;
};
export type EsAssetReference = Pick<SavedObjectReference, 'id'> & {
type: ElasticsearchAssetType;

View file

@ -20,7 +20,7 @@ export const AssetTitleMap: Record<AssetType, string> = {
ilm_policy: 'ILM Policy',
ingest_pipeline: 'Ingest Pipeline',
transform: 'Transform',
index_pattern: 'Index Pattern',
'index-pattern': 'Index Pattern',
index_template: 'Index Template',
component_template: 'Component Template',
search: 'Saved Search',
@ -36,7 +36,7 @@ export const ServiceTitleMap: Record<ServiceName, string> = {
export const AssetIcons: Record<KibanaAssetType, IconType> = {
dashboard: 'dashboardApp',
index_pattern: 'indexPatternApp',
'index-pattern': 'indexPatternApp',
search: 'searchProfilerApp',
visualization: 'visualizeApp',
map: 'mapApp',

View file

@ -5,7 +5,7 @@
*/
import { RequestHandler, SavedObjectsClientContract } from 'src/core/server';
import { DataStream } from '../../types';
import { GetDataStreamsResponse, KibanaAssetType, KibanaSavedObjectType } from '../../../common';
import { GetDataStreamsResponse, KibanaAssetType } from '../../../common';
import { getPackageSavedObjects, getKibanaSavedObject } from '../../services/epm/packages/get';
import { defaultIngestErrorHandler } from '../../errors';
@ -124,7 +124,7 @@ export const getListHandler: RequestHandler = async (context, request, response)
// then pick the dashboards from the package saved object
const dashboards =
pkgSavedObject[0].attributes?.installed_kibana?.filter(
(o) => o.type === KibanaSavedObjectType.dashboard
(o) => o.type === KibanaAssetType.dashboard
) || [];
// and then pick the human-readable titles from the dashboard saved objects
const enhancedDashboards = await getEnhancedDashboards(

View file

@ -11,49 +11,17 @@ import {
} from 'src/core/server';
import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../../../common';
import * as Registry from '../../registry';
import {
AssetType,
KibanaAssetType,
AssetReference,
AssetParts,
KibanaSavedObjectType,
} from '../../../../types';
import { AssetType, KibanaAssetType, AssetReference } from '../../../../types';
import { savedObjectTypes } from '../../packages';
import { indexPatternTypes } from '../index_pattern/install';
type SavedObjectToBe = Required<Pick<SavedObjectsBulkCreateObject, keyof ArchiveAsset>> & {
type: KibanaSavedObjectType;
type: AssetType;
};
export type ArchiveAsset = Pick<
SavedObject,
'id' | 'attributes' | 'migrationVersion' | 'references'
> & {
type: KibanaSavedObjectType;
};
// KibanaSavedObjectTypes are used to ensure saved objects being created for a given
// KibanaAssetType have the correct type
const KibanaSavedObjectTypeMapping: Record<KibanaAssetType, KibanaSavedObjectType> = {
[KibanaAssetType.dashboard]: KibanaSavedObjectType.dashboard,
[KibanaAssetType.indexPattern]: KibanaSavedObjectType.indexPattern,
[KibanaAssetType.map]: KibanaSavedObjectType.map,
[KibanaAssetType.search]: KibanaSavedObjectType.search,
[KibanaAssetType.visualization]: KibanaSavedObjectType.visualization,
};
// Define how each asset type will be installed
const AssetInstallers: Record<
KibanaAssetType,
(args: {
savedObjectsClient: SavedObjectsClientContract;
kibanaAssets: ArchiveAsset[];
}) => Promise<Array<SavedObject<unknown>>>
> = {
[KibanaAssetType.dashboard]: installKibanaSavedObjects,
[KibanaAssetType.indexPattern]: installKibanaIndexPatterns,
[KibanaAssetType.map]: installKibanaSavedObjects,
[KibanaAssetType.search]: installKibanaSavedObjects,
[KibanaAssetType.visualization]: installKibanaSavedObjects,
type: AssetType;
};
export async function getKibanaAsset(key: string): Promise<ArchiveAsset> {
@ -79,22 +47,16 @@ export function createSavedObjectKibanaAsset(asset: ArchiveAsset): SavedObjectTo
export async function installKibanaAssets(options: {
savedObjectsClient: SavedObjectsClientContract;
pkgName: string;
kibanaAssets: Record<KibanaAssetType, ArchiveAsset[]>;
kibanaAssets: ArchiveAsset[];
}): Promise<SavedObject[]> {
const { savedObjectsClient, kibanaAssets } = options;
// install the assets
const kibanaAssetTypes = Object.values(KibanaAssetType);
const installedAssets = await Promise.all(
kibanaAssetTypes.map((assetType) => {
if (kibanaAssets[assetType]) {
return AssetInstallers[assetType]({
savedObjectsClient,
kibanaAssets: kibanaAssets[assetType],
});
}
return [];
})
kibanaAssetTypes.map((assetType) =>
installKibanaSavedObjects({ savedObjectsClient, assetType, kibanaAssets })
)
);
return installedAssets.flat();
}
@ -112,50 +74,25 @@ export const deleteKibanaInstalledRefs = async (
installed_kibana: installedAssetsToSave,
});
};
export async function getKibanaAssets(
paths: string[]
): Promise<Record<KibanaAssetType, ArchiveAsset[]>> {
const kibanaAssetTypes = Object.values(KibanaAssetType);
const isKibanaAssetType = (path: string) => {
const parts = Registry.pathParts(path);
return parts.service === 'kibana' && (kibanaAssetTypes as string[]).includes(parts.type);
};
const filteredPaths = paths
.filter(isKibanaAssetType)
.map<[string, AssetParts]>((path) => [path, Registry.pathParts(path)]);
const assetArrays: Array<Promise<ArchiveAsset[]>> = [];
for (const assetType of kibanaAssetTypes) {
const matching = filteredPaths.filter(([path, parts]) => parts.type === assetType);
assetArrays.push(Promise.all(matching.map(([path]) => path).map(getKibanaAsset)));
}
const resolvedAssets = await Promise.all(assetArrays);
const result = {} as Record<KibanaAssetType, ArchiveAsset[]>;
for (const [index, assetType] of kibanaAssetTypes.entries()) {
const expectedType = KibanaSavedObjectTypeMapping[assetType];
const properlyTypedAssets = resolvedAssets[index].filter(({ type }) => type === expectedType);
result[assetType] = properlyTypedAssets;
}
return result;
export async function getKibanaAssets(paths: string[]) {
const isKibanaAssetType = (path: string) => Registry.pathParts(path).type in KibanaAssetType;
const filteredPaths = paths.filter(isKibanaAssetType);
const kibanaAssets = await Promise.all(filteredPaths.map((path) => getKibanaAsset(path)));
return kibanaAssets;
}
async function installKibanaSavedObjects({
savedObjectsClient,
assetType,
kibanaAssets,
}: {
savedObjectsClient: SavedObjectsClientContract;
assetType: KibanaAssetType;
kibanaAssets: ArchiveAsset[];
}) {
const isSameType = (asset: ArchiveAsset) => assetType === asset.type;
const filteredKibanaAssets = kibanaAssets.filter((asset) => isSameType(asset));
const toBeSavedObjects = await Promise.all(
kibanaAssets.map((asset) => createSavedObjectKibanaAsset(asset))
filteredKibanaAssets.map((asset) => createSavedObjectKibanaAsset(asset))
);
if (toBeSavedObjects.length === 0) {
@ -168,23 +105,8 @@ async function installKibanaSavedObjects({
}
}
async function installKibanaIndexPatterns({
savedObjectsClient,
kibanaAssets,
}: {
savedObjectsClient: SavedObjectsClientContract;
kibanaAssets: ArchiveAsset[];
}) {
// Filter out any reserved index patterns
const reservedPatterns = indexPatternTypes.map((pattern) => `${pattern}-*`);
const nonReservedPatterns = kibanaAssets.filter((asset) => !reservedPatterns.includes(asset.id));
return installKibanaSavedObjects({ savedObjectsClient, kibanaAssets: nonReservedPatterns });
}
export function toAssetReference({ id, type }: SavedObject) {
const reference: AssetReference = { id, type: type as KibanaSavedObjectType };
const reference: AssetReference = { id, type: type as KibanaAssetType };
return reference;
}

View file

@ -72,7 +72,6 @@ export interface IndexPatternField {
readFromDocValues: boolean;
}
export const indexPatternTypes = Object.values(dataTypes);
// TODO: use a function overload and make pkgName and pkgVersion required for install/update
// and not for an update removal. or separate out the functions
export async function installIndexPatterns(
@ -117,6 +116,7 @@ export async function installIndexPatterns(
const packageVersionsInfo = await Promise.all(packageVersionsFetchInfoPromise);
// for each index pattern type, create an index pattern
const indexPatternTypes = Object.values(dataTypes);
indexPatternTypes.forEach(async (indexPatternType) => {
// if this is an update because a package is being uninstalled (no pkgkey argument passed) and no other packages are installed, remove the index pattern
if (!pkgName && installedPackages.length === 0) {

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ElasticsearchAssetType, Installation, KibanaSavedObjectType } from '../../../types';
import { ElasticsearchAssetType, Installation, KibanaAssetType } from '../../../types';
import { SavedObject, SavedObjectsClientContract } from 'src/core/server';
jest.mock('./install');
@ -41,7 +41,7 @@ const mockInstallation: SavedObject<Installation> = {
type: 'epm-packages',
attributes: {
id: 'test-pkg',
installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }],
installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }],
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
es_index_patterns: { pattern: 'pattern-name' },
name: 'test package',

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedObject } from 'src/core/server';
import { ElasticsearchAssetType, Installation, KibanaSavedObjectType } from '../../../types';
import { ElasticsearchAssetType, Installation, KibanaAssetType } from '../../../types';
import { getInstallType } from './install';
const mockInstallation: SavedObject<Installation> = {
@ -13,7 +13,7 @@ const mockInstallation: SavedObject<Installation> = {
type: 'epm-packages',
attributes: {
id: 'test-pkg',
installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }],
installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }],
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
es_index_patterns: { pattern: 'pattern-name' },
name: 'test packagek',
@ -30,7 +30,7 @@ const mockInstallationUpdateFail: SavedObject<Installation> = {
type: 'epm-packages',
attributes: {
id: 'test-pkg',
installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }],
installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }],
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
es_index_patterns: { pattern: 'pattern-name' },
name: 'test packagek',

View file

@ -18,7 +18,6 @@ import {
KibanaAssetReference,
EsAssetReference,
InstallType,
KibanaAssetType,
} from '../../../types';
import * as Registry from '../registry';
import {
@ -365,9 +364,9 @@ export async function createInstallation(options: {
export const saveKibanaAssetsRefs = async (
savedObjectsClient: SavedObjectsClientContract,
pkgName: string,
kibanaAssets: Record<KibanaAssetType, ArchiveAsset[]>
kibanaAssets: ArchiveAsset[]
) => {
const assetRefs = Object.values(kibanaAssets).flat().map(toAssetReference);
const assetRefs = kibanaAssets.map(toAssetReference);
await savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, {
installed_kibana: assetRefs,
});

View file

@ -12,9 +12,6 @@ import {
AssetType,
CallESAsCurrentUser,
ElasticsearchAssetType,
EsAssetReference,
KibanaAssetReference,
Installation,
} from '../../../types';
import { getInstallation, savedObjectTypes } from './index';
import { deletePipeline } from '../elasticsearch/ingest_pipeline/';
@ -49,7 +46,7 @@ export async function removeInstallation(options: {
// Delete the installed assets
const installedAssets = [...installation.installed_kibana, ...installation.installed_es];
await deleteAssets(installation, savedObjectsClient, callCluster);
await deleteAssets(installedAssets, savedObjectsClient, callCluster);
// Delete the manager saved object with references to the asset objects
// could also update with [] or some other state
@ -67,20 +64,17 @@ export async function removeInstallation(options: {
// successful delete's in SO client return {}. return something more useful
return installedAssets;
}
function deleteKibanaAssets(
installedObjects: KibanaAssetReference[],
savedObjectsClient: SavedObjectsClientContract
async function deleteAssets(
installedObjects: AssetReference[],
savedObjectsClient: SavedObjectsClientContract,
callCluster: CallESAsCurrentUser
) {
return installedObjects.map(async ({ id, type }) => {
return savedObjectsClient.delete(type, id);
});
}
function deleteESAssets(installedObjects: EsAssetReference[], callCluster: CallESAsCurrentUser) {
return installedObjects.map(async ({ id, type }) => {
const logger = appContextService.getLogger();
const deletePromises = installedObjects.map(async ({ id, type }) => {
const assetType = type as AssetType;
if (assetType === ElasticsearchAssetType.ingestPipeline) {
if (savedObjectTypes.includes(assetType)) {
return savedObjectsClient.delete(assetType, id);
} else if (assetType === ElasticsearchAssetType.ingestPipeline) {
return deletePipeline(callCluster, id);
} else if (assetType === ElasticsearchAssetType.indexTemplate) {
return deleteTemplate(callCluster, id);
@ -88,22 +82,8 @@ function deleteESAssets(installedObjects: EsAssetReference[], callCluster: CallE
return deleteTransforms(callCluster, [id]);
}
});
}
async function deleteAssets(
{ installed_es: installedEs, installed_kibana: installedKibana }: Installation,
savedObjectsClient: SavedObjectsClientContract,
callCluster: CallESAsCurrentUser
) {
const logger = appContextService.getLogger();
const deletePromises: Array<Promise<unknown>> = [
...deleteESAssets(installedEs, callCluster),
...deleteKibanaAssets(installedKibana, savedObjectsClient),
];
try {
await Promise.all(deletePromises);
await Promise.all([...deletePromises]);
} catch (err) {
logger.error(err);
}

View file

@ -242,12 +242,10 @@ export function getAsset(key: string) {
}
export function groupPathsByService(paths: string[]): AssetsGroupedByServiceByType {
const kibanaAssetTypes = Object.values<string>(KibanaAssetType);
// ASK: best way, if any, to avoid `any`?
const assets = paths.reduce((map: any, path) => {
const parts = pathParts(path.replace(/^\/package\//, ''));
if (parts.service === 'kibana' && kibanaAssetTypes.includes(parts.type)) {
if (parts.type in KibanaAssetType) {
if (!map[parts.service]) map[parts.service] = {};
if (!map[parts.service][parts.type]) map[parts.service][parts.type] = [];
map[parts.service][parts.type].push(parts);

View file

@ -56,7 +56,6 @@ export {
AssetType,
Installable,
KibanaAssetType,
KibanaSavedObjectType,
AssetParts,
AssetsGroupedByServiceByType,
CategoryId,

View file

@ -184,16 +184,6 @@ export default function (providerContext: FtrProviderContext) {
resSearch = err;
}
expect(resSearch.response.data.statusCode).equal(404);
let resIndexPattern;
try {
resIndexPattern = await kibanaServer.savedObjects.get({
type: 'index-pattern',
id: 'test-*',
});
} catch (err) {
resIndexPattern = err;
}
expect(resIndexPattern.response.data.statusCode).equal(404);
});
it('should have removed the fields from the index patterns', async () => {
// The reason there is an expect inside the try and inside the catch in this test case is to guard against two
@ -355,7 +345,6 @@ const expectAssetsInstalled = ({
expect(res.statusCode).equal(200);
});
it('should have installed the kibana assets', async function () {
// These are installed from Fleet along with every package
const resIndexPatternLogs = await kibanaServer.savedObjects.get({
type: 'index-pattern',
id: 'logs-*',
@ -366,8 +355,6 @@ const expectAssetsInstalled = ({
id: 'metrics-*',
});
expect(resIndexPatternMetrics.id).equal('metrics-*');
// These are the assets from the package
const resDashboard = await kibanaServer.savedObjects.get({
type: 'dashboard',
id: 'sample_dashboard',
@ -388,22 +375,6 @@ const expectAssetsInstalled = ({
id: 'sample_search',
});
expect(resSearch.id).equal('sample_search');
const resIndexPattern = await kibanaServer.savedObjects.get({
type: 'index-pattern',
id: 'test-*',
});
expect(resIndexPattern.id).equal('test-*');
let resInvalidTypeIndexPattern;
try {
resInvalidTypeIndexPattern = await kibanaServer.savedObjects.get({
type: 'invalid-type',
id: 'invalid',
});
} catch (err) {
resInvalidTypeIndexPattern = err;
}
expect(resInvalidTypeIndexPattern.response.data.statusCode).equal(404);
});
it('should create an index pattern with the package fields', async () => {
const resIndexPatternLogs = await kibanaServer.savedObjects.get({
@ -444,10 +415,6 @@ const expectAssetsInstalled = ({
id: 'sample_dashboard2',
type: 'dashboard',
},
{
id: 'test-*',
type: 'index-pattern',
},
{
id: 'sample_search',
type: 'search',

View file

@ -283,14 +283,14 @@ export default function (providerContext: FtrProviderContext) {
id: 'sample_dashboard',
type: 'dashboard',
},
{
id: 'sample_visualization',
type: 'visualization',
},
{
id: 'sample_search2',
type: 'search',
},
{
id: 'sample_visualization',
type: 'visualization',
},
],
installed_es: [
{

View file

@ -1,11 +0,0 @@
{
"attributes": {
"fieldFormatMap": "{}",
"fields": "[]",
"timeFieldName": "@timestamp",
"title": "invalid"
},
"id": "invalid",
"references": [],
"type": "invalid-type"
}

View file

@ -1,11 +0,0 @@
{
"attributes": {
"fieldFormatMap": "{}",
"fields": "[]",
"timeFieldName": "@timestamp",
"title": "test-*"
},
"id": "test-*",
"references": [],
"type": "index-pattern"
}