mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
* [Fleet] Add `installed_kibana_space_id` to `epm-packages` saved objects (#120517) # Conflicts: # x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts # x-pack/plugins/fleet/server/routes/preconfiguration/index.ts * fix handler types Co-authored-by: Mark Hopkin <mark.hopkin@elastic.co> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
d8cb6f164b
commit
1ef87700b1
40 changed files with 430 additions and 60 deletions
|
@ -36,6 +36,7 @@ interface SavedObjectResponse<Attributes extends Record<string, any>> {
|
|||
interface GetOptions {
|
||||
type: string;
|
||||
id: string;
|
||||
space?: string;
|
||||
}
|
||||
|
||||
interface IndexOptions<Attributes> {
|
||||
|
@ -110,11 +111,13 @@ export class KbnClientSavedObjects {
|
|||
* Get an object
|
||||
*/
|
||||
public async get<Attributes extends Record<string, any>>(options: GetOptions) {
|
||||
this.log.debug('Gettings saved object: %j', options);
|
||||
this.log.debug('Getting saved object: %j', options);
|
||||
|
||||
const { data } = await this.requester.request<SavedObjectResponse<Attributes>>({
|
||||
description: 'get saved object',
|
||||
path: uriencode`/api/saved_objects/${options.type}/${options.id}`,
|
||||
path: options.space
|
||||
? uriencode`/s/${options.space}/api/saved_objects/${options.type}/${options.id}`
|
||||
: uriencode`/api/saved_objects/${options.type}/${options.id}`,
|
||||
method: 'GET',
|
||||
});
|
||||
return data;
|
||||
|
@ -174,7 +177,9 @@ export class KbnClientSavedObjects {
|
|||
|
||||
const { data } = await this.requester.request({
|
||||
description: 'delete saved object',
|
||||
path: uriencode`/api/saved_objects/${options.type}/${options.id}`,
|
||||
path: options.space
|
||||
? uriencode`/s/${options.space}/api/saved_objects/${options.type}/${options.id}`
|
||||
: uriencode`/api/saved_objects/${options.type}/${options.id}`,
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
|
|
|
@ -402,6 +402,7 @@ export interface Installation extends SavedObjectAttributes {
|
|||
install_version: string;
|
||||
install_started_at: string;
|
||||
install_source: InstallSource;
|
||||
installed_kibana_space_id?: string;
|
||||
keep_policies_up_to_date?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -645,6 +645,7 @@
|
|||
"updated_at": "2021-09-30T10:47:12.961Z",
|
||||
"version": "WzI1NjgsMV0=",
|
||||
"attributes": {
|
||||
"installed_kibana_space_id": "default",
|
||||
"installed_kibana": [
|
||||
{
|
||||
"id": "apache-Logs-Apache-Dashboard",
|
||||
|
|
|
@ -127,12 +127,10 @@ used for other types of outputs like separate monitoring clusters, Logstash, etc
|
|||
- `installed_es` - array of assets installed into Elasticsearch
|
||||
- `installed_es.id` - ID in Elasticsearch of an asset (eg. `logs-system.application-1.1.2`)
|
||||
- `installed_es.type` - type of Elasticsearch asset (eg. `ingest_pipeline`)
|
||||
- `installed_kibana_space_id` - the id of the space the assets were installed in (eg. `default`)
|
||||
- `installed_kibana` - array of assets that were installed into Kibana
|
||||
- `installed_kibana.id` - Saved Object ID (eg. `system-01c54730-fee6-11e9-8405-516218e3d268`)
|
||||
- `installed_kibana.type` - Saved Object type name (eg. `dashboard`)
|
||||
- One caveat with this array is that the IDs are currently space-specific so if a package's assets were installed in
|
||||
one space, they may not be visible in other spaces. We also do not keep track of which space these assets were
|
||||
installed into.
|
||||
- `package_assets` - array of original file contents of the package as it was installed
|
||||
- `package_assets.id` - Saved Object ID for a `epm-package-assets` type
|
||||
- `package_assets.type` - Saved Object type for the asset. As of now, only `epm-packages-assets` are supported.
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"server": true,
|
||||
"ui": true,
|
||||
"configPath": ["xpack", "fleet"],
|
||||
"requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share"],
|
||||
"requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share", "spaces"],
|
||||
"optionalPlugins": ["security", "features", "cloud", "usageCollection", "home", "globalSearch", "telemetry"],
|
||||
"extraPublicDirs": ["common"],
|
||||
"requiredBundles": ["kibanaReact", "esUiShared", "home", "infra", "kibanaUtils", "usageCollection"]
|
||||
|
|
|
@ -63,6 +63,7 @@ export const Installed = ({ width, ...props }: Args) => {
|
|||
install_version: props.version,
|
||||
es_index_patterns: {},
|
||||
installed_kibana: [],
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_es: [],
|
||||
install_status: 'installed',
|
||||
install_source: 'registry',
|
||||
|
|
|
@ -80,6 +80,7 @@ export const createFleetRequestHandlerContextMock = (): jest.Mocked<
|
|||
epm: {
|
||||
internalSoClient: savedObjectsClientMock.create(),
|
||||
},
|
||||
spaceId: 'default',
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ import type { PluginSetupContract as FeaturesPluginSetup } from '../../features/
|
|||
import type { FleetConfigType, FleetAuthz } from '../common';
|
||||
import { INTEGRATIONS_PLUGIN_ID } from '../common';
|
||||
import type { CloudSetup } from '../../cloud/server';
|
||||
import type { SpacesPluginStart } from '../../spaces/server';
|
||||
|
||||
import {
|
||||
PLUGIN_ID,
|
||||
|
@ -96,6 +97,7 @@ export interface FleetSetupDeps {
|
|||
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup;
|
||||
cloud?: CloudSetup;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
spaces: SpacesPluginStart;
|
||||
telemetry?: TelemetryPluginSetup;
|
||||
}
|
||||
|
||||
|
@ -297,6 +299,9 @@ export class FleetPlugin
|
|||
.getScopedClient(request, { excludedWrappers: ['security'] });
|
||||
},
|
||||
},
|
||||
get spaceId() {
|
||||
return deps.spaces.spacesService.getSpaceId(request);
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
@ -22,6 +22,7 @@ import type {
|
|||
CopyAgentPolicyRequestSchema,
|
||||
DeleteAgentPolicyRequestSchema,
|
||||
GetFullAgentPolicyRequestSchema,
|
||||
FleetRequestHandler,
|
||||
} from '../../types';
|
||||
import type { AgentPolicy, NewPackagePolicy } from '../../types';
|
||||
import { FLEET_SYSTEM_PACKAGE } from '../../../common';
|
||||
|
@ -100,7 +101,7 @@ export const getOneAgentPolicyHandler: RequestHandler<
|
|||
}
|
||||
};
|
||||
|
||||
export const createAgentPolicyHandler: RequestHandler<
|
||||
export const createAgentPolicyHandler: FleetRequestHandler<
|
||||
undefined,
|
||||
TypeOf<typeof CreateAgentPolicyRequestSchema.query>,
|
||||
TypeOf<typeof CreateAgentPolicyRequestSchema.body>
|
||||
|
@ -109,7 +110,7 @@ export const createAgentPolicyHandler: RequestHandler<
|
|||
const esClient = context.core.elasticsearch.client.asInternalUser;
|
||||
const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined;
|
||||
const withSysMonitoring = request.query.sys_monitoring ?? false;
|
||||
|
||||
const spaceId = context.fleet.spaceId;
|
||||
try {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let [agentPolicy, newSysPackagePolicy] = await Promise.all<
|
||||
|
@ -136,6 +137,7 @@ export const createAgentPolicyHandler: RequestHandler<
|
|||
newSysPackagePolicy.name = await incrementPackageName(soClient, FLEET_SYSTEM_PACKAGE);
|
||||
|
||||
await packagePolicyService.create(soClient, esClient, newSysPackagePolicy, {
|
||||
spaceId,
|
||||
user,
|
||||
bumpRevision: false,
|
||||
});
|
||||
|
|
|
@ -255,11 +255,13 @@ export const installPackageFromRegistryHandler: FleetRequestHandler<
|
|||
const esClient = context.core.elasticsearch.client.asInternalUser;
|
||||
const { pkgName, pkgVersion } = request.params;
|
||||
|
||||
const spaceId = context.fleet.spaceId;
|
||||
const res = await installPackage({
|
||||
installSource: 'registry',
|
||||
savedObjectsClient,
|
||||
pkgkey: pkgVersion ? `${pkgName}-${pkgVersion}` : pkgName,
|
||||
esClient,
|
||||
spaceId,
|
||||
force: request.body?.force,
|
||||
});
|
||||
if (!res.error) {
|
||||
|
@ -294,10 +296,12 @@ export const bulkInstallPackagesFromRegistryHandler: FleetRequestHandler<
|
|||
> = async (context, request, response) => {
|
||||
const savedObjectsClient = context.fleet.epm.internalSoClient;
|
||||
const esClient = context.core.elasticsearch.client.asInternalUser;
|
||||
const spaceId = context.fleet.spaceId;
|
||||
const bulkInstalledResponses = await bulkInstallPackages({
|
||||
savedObjectsClient,
|
||||
esClient,
|
||||
packagesToInstall: request.body.packages,
|
||||
spaceId,
|
||||
});
|
||||
const payload = bulkInstalledResponses.map(bulkInstallServiceResponseToHttpEntry);
|
||||
const body: BulkInstallPackagesResponse = {
|
||||
|
@ -322,12 +326,13 @@ export const installPackageByUploadHandler: FleetRequestHandler<
|
|||
const esClient = context.core.elasticsearch.client.asInternalUser;
|
||||
const contentType = request.headers['content-type'] as string; // from types it could also be string[] or undefined but this is checked later
|
||||
const archiveBuffer = Buffer.from(request.body);
|
||||
|
||||
const spaceId = context.fleet.spaceId;
|
||||
const res = await installPackage({
|
||||
installSource: 'upload',
|
||||
savedObjectsClient,
|
||||
esClient,
|
||||
archiveBuffer,
|
||||
spaceId,
|
||||
contentType,
|
||||
});
|
||||
if (!res.error) {
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
|
||||
import { httpServerMock, httpServiceMock } from 'src/core/server/mocks';
|
||||
import type { KibanaRequest } from 'kibana/server';
|
||||
import type { IRouter, RequestHandler, RouteConfig } from 'kibana/server';
|
||||
import type { RouteConfig } from 'kibana/server';
|
||||
|
||||
import type { FleetRouter } from '../../types/request_context';
|
||||
import { PACKAGE_POLICY_API_ROUTES } from '../../../common/constants';
|
||||
import { appContextService, packagePolicyService } from '../../services';
|
||||
import { createAppContextStartContractMock, xpackMocks } from '../../mocks';
|
||||
|
@ -21,8 +22,7 @@ import type {
|
|||
CreatePackagePolicyRequestSchema,
|
||||
UpdatePackagePolicyRequestSchema,
|
||||
} from '../../types/rest_spec';
|
||||
|
||||
import type { PackagePolicy } from '../../types';
|
||||
import type { PackagePolicy, FleetRequestHandler } from '../../types';
|
||||
|
||||
import { registerRoutes } from './index';
|
||||
|
||||
|
@ -93,8 +93,8 @@ jest.mock('../../services/epm/packages', () => {
|
|||
});
|
||||
|
||||
describe('When calling package policy', () => {
|
||||
let routerMock: jest.Mocked<IRouter>;
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
let routerMock: jest.Mocked<FleetRouter>;
|
||||
let routeHandler: FleetRequestHandler<any, any, any>;
|
||||
let routeConfig: RouteConfig<any, any, any, any>;
|
||||
let context: ReturnType<typeof xpackMocks.createRequestHandlerContext>;
|
||||
let response: ReturnType<typeof httpServerMock.createResponseFactory>;
|
||||
|
|
|
@ -19,6 +19,7 @@ import type {
|
|||
DeletePackagePoliciesRequestSchema,
|
||||
UpgradePackagePoliciesRequestSchema,
|
||||
DryRunPackagePoliciesRequestSchema,
|
||||
FleetRequestHandler,
|
||||
} from '../../types';
|
||||
import type {
|
||||
CreatePackagePolicyResponse,
|
||||
|
@ -80,7 +81,7 @@ export const getOnePackagePolicyHandler: RequestHandler<
|
|||
}
|
||||
};
|
||||
|
||||
export const createPackagePolicyHandler: RequestHandler<
|
||||
export const createPackagePolicyHandler: FleetRequestHandler<
|
||||
undefined,
|
||||
undefined,
|
||||
TypeOf<typeof CreatePackagePolicyRequestSchema.body>
|
||||
|
@ -89,6 +90,7 @@ export const createPackagePolicyHandler: RequestHandler<
|
|||
const esClient = context.core.elasticsearch.client.asInternalUser;
|
||||
const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined;
|
||||
const { force, ...newPolicy } = request.body;
|
||||
const spaceId = context.fleet.spaceId;
|
||||
try {
|
||||
const newPackagePolicy = await packagePolicyService.enrichPolicyWithDefaultsFromPackage(
|
||||
soClient,
|
||||
|
@ -106,6 +108,7 @@ export const createPackagePolicyHandler: RequestHandler<
|
|||
const packagePolicy = await packagePolicyService.create(soClient, esClient, newData, {
|
||||
user,
|
||||
force,
|
||||
spaceId,
|
||||
});
|
||||
const body: CreatePackagePolicyResponse = { item: packagePolicy };
|
||||
return response.ok({
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { IRouter } from 'src/core/server';
|
||||
|
||||
import { PLUGIN_ID, PACKAGE_POLICY_API_ROUTES } from '../../constants';
|
||||
import type { FleetRouter } from '../../types/request_context';
|
||||
import {
|
||||
GetPackagePoliciesRequestSchema,
|
||||
GetOnePackagePolicyRequestSchema,
|
||||
|
@ -28,7 +27,7 @@ import {
|
|||
dryRunUpgradePackagePolicyHandler,
|
||||
} from './handlers';
|
||||
|
||||
export const registerRoutes = (router: IRouter) => {
|
||||
export const registerRoutes = (router: FleetRouter) => {
|
||||
// List
|
||||
router.get(
|
||||
{
|
||||
|
|
|
@ -11,11 +11,12 @@ import type { TypeOf } from '@kbn/config-schema';
|
|||
import type { PreconfiguredAgentPolicy } from '../../../common';
|
||||
|
||||
import { PLUGIN_ID, PRECONFIGURATION_API_ROUTES } from '../../constants';
|
||||
import type { FleetRequestHandler } from '../../types';
|
||||
import { PutPreconfigurationSchema } from '../../types';
|
||||
import { defaultIngestErrorHandler } from '../../errors';
|
||||
import { ensurePreconfiguredPackagesAndPolicies, outputService } from '../../services';
|
||||
|
||||
export const updatePreconfigurationHandler: RequestHandler<
|
||||
export const updatePreconfigurationHandler: FleetRequestHandler<
|
||||
undefined,
|
||||
undefined,
|
||||
TypeOf<typeof PutPreconfigurationSchema.body>
|
||||
|
@ -23,7 +24,7 @@ export const updatePreconfigurationHandler: RequestHandler<
|
|||
const soClient = context.core.savedObjects.client;
|
||||
const esClient = context.core.elasticsearch.client.asInternalUser;
|
||||
const defaultOutput = await outputService.ensureDefaultOutput(soClient);
|
||||
|
||||
const spaceId = context.fleet.spaceId;
|
||||
const { agentPolicies, packages } = request.body;
|
||||
|
||||
try {
|
||||
|
@ -32,7 +33,8 @@ export const updatePreconfigurationHandler: RequestHandler<
|
|||
esClient,
|
||||
(agentPolicies as PreconfiguredAgentPolicy[]) ?? [],
|
||||
packages ?? [],
|
||||
defaultOutput
|
||||
defaultOutput,
|
||||
spaceId
|
||||
);
|
||||
return response.ok({ body });
|
||||
} catch (error) {
|
||||
|
@ -47,6 +49,6 @@ export const registerRoutes = (router: IRouter) => {
|
|||
validate: PutPreconfigurationSchema,
|
||||
options: { tags: [`access:${PLUGIN_ID}-all`] },
|
||||
},
|
||||
updatePreconfigurationHandler
|
||||
updatePreconfigurationHandler as RequestHandler
|
||||
);
|
||||
};
|
||||
|
|
|
@ -45,6 +45,7 @@ describe('FleetSetupHandler', () => {
|
|||
epm: {
|
||||
internalSoClient: savedObjectsClientMock.create(),
|
||||
},
|
||||
spaceId: 'default',
|
||||
},
|
||||
};
|
||||
response = httpServerMock.createResponseFactory();
|
||||
|
|
|
@ -239,6 +239,7 @@ const getSavedObjectTypes = (
|
|||
type: { type: 'keyword' },
|
||||
},
|
||||
},
|
||||
installed_kibana_space_id: { type: 'keyword' },
|
||||
package_assets: {
|
||||
type: 'nested',
|
||||
properties: {
|
||||
|
|
|
@ -9,6 +9,8 @@ import type { SavedObjectsClientContract, ElasticsearchClient } from 'src/core/s
|
|||
import { savedObjectsClientMock, elasticsearchServiceMock } from 'src/core/server/mocks';
|
||||
import { loggerMock } from '@kbn/logging/mocks';
|
||||
|
||||
import { DEFAULT_SPACE_ID } from '../../../../../spaces/common/constants';
|
||||
|
||||
import { appContextService } from '../../app_context';
|
||||
import { createAppContextStartContractMock } from '../../../mocks';
|
||||
|
||||
|
@ -78,6 +80,7 @@ describe('_installPackage', () => {
|
|||
},
|
||||
installType: 'install',
|
||||
installSource: 'registry',
|
||||
spaceId: DEFAULT_SPACE_ID,
|
||||
});
|
||||
|
||||
// if we have a .catch this will fail nicely (test pass)
|
||||
|
|
|
@ -56,6 +56,7 @@ export async function _installPackage({
|
|||
packageInfo,
|
||||
installType,
|
||||
installSource,
|
||||
spaceId,
|
||||
}: {
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
savedObjectsImporter: Pick<SavedObjectsImporter, 'import' | 'resolveImportErrors'>;
|
||||
|
@ -66,6 +67,7 @@ export async function _installPackage({
|
|||
packageInfo: InstallablePackage;
|
||||
installType: InstallType;
|
||||
installSource: InstallSource;
|
||||
spaceId: string;
|
||||
}): Promise<AssetReference[]> {
|
||||
const { name: pkgName, version: pkgVersion } = packageInfo;
|
||||
|
||||
|
@ -99,15 +101,12 @@ export async function _installPackage({
|
|||
savedObjectsClient,
|
||||
packageInfo,
|
||||
installSource,
|
||||
spaceId,
|
||||
});
|
||||
}
|
||||
|
||||
const kibanaAssets = await getKibanaAssets(paths);
|
||||
if (installedPkg)
|
||||
await deleteKibanaSavedObjectsAssets(
|
||||
savedObjectsClient,
|
||||
installedPkg.attributes.installed_kibana
|
||||
);
|
||||
if (installedPkg) await deleteKibanaSavedObjectsAssets({ savedObjectsClient, installedPkg });
|
||||
// save new kibana refs before installing the assets
|
||||
const installedKibanaAssetsRefs = await saveKibanaAssetsRefs(
|
||||
savedObjectsClient,
|
||||
|
|
|
@ -20,12 +20,14 @@ interface BulkInstallPackagesParams {
|
|||
packagesToInstall: Array<string | { name: string; version: string }>;
|
||||
esClient: ElasticsearchClient;
|
||||
force?: boolean;
|
||||
spaceId: string;
|
||||
}
|
||||
|
||||
export async function bulkInstallPackages({
|
||||
savedObjectsClient,
|
||||
packagesToInstall,
|
||||
esClient,
|
||||
spaceId,
|
||||
force,
|
||||
}: BulkInstallPackagesParams): Promise<BulkInstallResponse[]> {
|
||||
const logger = appContextService.getLogger();
|
||||
|
@ -70,6 +72,7 @@ export async function bulkInstallPackages({
|
|||
esClient,
|
||||
pkgkey: Registry.pkgToPkgKey(pkgKeyProps),
|
||||
installSource,
|
||||
spaceId,
|
||||
force,
|
||||
});
|
||||
if (installResult.error) {
|
||||
|
|
|
@ -18,6 +18,7 @@ const mockInstallation: SavedObject<Installation> = {
|
|||
type: 'epm-packages',
|
||||
attributes: {
|
||||
id: 'test-pkg',
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }],
|
||||
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
|
||||
package_assets: [],
|
||||
|
@ -37,6 +38,7 @@ const mockInstallationUpdateFail: SavedObject<Installation> = {
|
|||
type: 'epm-packages',
|
||||
attributes: {
|
||||
id: 'test-pkg',
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_kibana: [{ type: KibanaSavedObjectType.dashboard, id: 'dashboard-1' }],
|
||||
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
|
||||
package_assets: [],
|
||||
|
|
|
@ -9,6 +9,8 @@ import { savedObjectsClientMock } from 'src/core/server/mocks';
|
|||
|
||||
import type { ElasticsearchClient } from 'kibana/server';
|
||||
|
||||
import { DEFAULT_SPACE_ID } from '../../../../../spaces/common/constants';
|
||||
|
||||
import * as Registry from '../registry';
|
||||
|
||||
import { sendTelemetryEvents } from '../../upgrade_sender';
|
||||
|
@ -75,6 +77,7 @@ describe('install', () => {
|
|||
describe('registry', () => {
|
||||
it('should send telemetry on install failure, out of date', async () => {
|
||||
await installPackage({
|
||||
spaceId: DEFAULT_SPACE_ID,
|
||||
installSource: 'registry',
|
||||
pkgkey: 'apache-1.1.0',
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
|
@ -96,6 +99,7 @@ describe('install', () => {
|
|||
it('should send telemetry on install failure, license error', async () => {
|
||||
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(false);
|
||||
await installPackage({
|
||||
spaceId: DEFAULT_SPACE_ID,
|
||||
installSource: 'registry',
|
||||
pkgkey: 'apache-1.3.0',
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
|
@ -117,6 +121,7 @@ describe('install', () => {
|
|||
it('should send telemetry on install success', async () => {
|
||||
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
|
||||
await installPackage({
|
||||
spaceId: DEFAULT_SPACE_ID,
|
||||
installSource: 'registry',
|
||||
pkgkey: 'apache-1.3.0',
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
|
@ -140,6 +145,7 @@ describe('install', () => {
|
|||
.mockImplementationOnce(() => Promise.resolve({ attributes: { version: '1.2.0' } } as any));
|
||||
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
|
||||
await installPackage({
|
||||
spaceId: DEFAULT_SPACE_ID,
|
||||
installSource: 'registry',
|
||||
pkgkey: 'apache-1.3.0',
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
|
@ -163,6 +169,7 @@ describe('install', () => {
|
|||
.mockImplementation(() => Promise.reject(new Error('error')));
|
||||
jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true);
|
||||
await installPackage({
|
||||
spaceId: DEFAULT_SPACE_ID,
|
||||
installSource: 'registry',
|
||||
pkgkey: 'apache-1.3.0',
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
|
@ -188,6 +195,7 @@ describe('install', () => {
|
|||
.spyOn(obj, 'getInstallationObject')
|
||||
.mockImplementationOnce(() => Promise.resolve({ attributes: { version: '1.2.0' } } as any));
|
||||
await installPackage({
|
||||
spaceId: DEFAULT_SPACE_ID,
|
||||
installSource: 'upload',
|
||||
archiveBuffer: {} as Buffer,
|
||||
contentType: '',
|
||||
|
@ -210,6 +218,7 @@ describe('install', () => {
|
|||
|
||||
it('should send telemetry on install success', async () => {
|
||||
await installPackage({
|
||||
spaceId: DEFAULT_SPACE_ID,
|
||||
installSource: 'upload',
|
||||
archiveBuffer: {} as Buffer,
|
||||
contentType: '',
|
||||
|
@ -233,6 +242,7 @@ describe('install', () => {
|
|||
.spyOn(install, '_installPackage')
|
||||
.mockImplementation(() => Promise.reject(new Error('error')));
|
||||
await installPackage({
|
||||
spaceId: DEFAULT_SPACE_ID,
|
||||
installSource: 'upload',
|
||||
archiveBuffer: {} as Buffer,
|
||||
contentType: '',
|
||||
|
|
|
@ -11,7 +11,7 @@ import type Boom from '@hapi/boom';
|
|||
import type { ElasticsearchClient, SavedObject, SavedObjectsClientContract } from 'src/core/server';
|
||||
|
||||
import { generateESIndexPatterns } from '../elasticsearch/template/template';
|
||||
|
||||
import { DEFAULT_SPACE_ID } from '../../../../../spaces/common/constants';
|
||||
import type {
|
||||
BulkInstallPackageInfo,
|
||||
EpmPackageInstallStatus,
|
||||
|
@ -85,8 +85,9 @@ export async function ensureInstalledPackage(options: {
|
|||
pkgName: string;
|
||||
esClient: ElasticsearchClient;
|
||||
pkgVersion?: string;
|
||||
spaceId?: string;
|
||||
}): Promise<Installation> {
|
||||
const { savedObjectsClient, pkgName, esClient, pkgVersion } = options;
|
||||
const { savedObjectsClient, pkgName, esClient, pkgVersion, spaceId = DEFAULT_SPACE_ID } = options;
|
||||
|
||||
// If pkgVersion isn't specified, find the latest package version
|
||||
const pkgKeyProps = pkgVersion
|
||||
|
@ -106,6 +107,7 @@ export async function ensureInstalledPackage(options: {
|
|||
installSource: 'registry',
|
||||
savedObjectsClient,
|
||||
pkgkey,
|
||||
spaceId,
|
||||
esClient,
|
||||
force: true, // Always force outdated packages to be installed if a later version isn't installed
|
||||
});
|
||||
|
@ -142,6 +144,7 @@ export async function handleInstallPackageFailure({
|
|||
pkgVersion,
|
||||
installedPkg,
|
||||
esClient,
|
||||
spaceId,
|
||||
}: {
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
error: IngestManagerError | Boom.Boom | Error;
|
||||
|
@ -149,6 +152,7 @@ export async function handleInstallPackageFailure({
|
|||
pkgVersion: string;
|
||||
installedPkg: SavedObject<Installation> | undefined;
|
||||
esClient: ElasticsearchClient;
|
||||
spaceId: string;
|
||||
}) {
|
||||
if (error instanceof IngestManagerError) {
|
||||
return;
|
||||
|
@ -183,6 +187,7 @@ export async function handleInstallPackageFailure({
|
|||
savedObjectsClient,
|
||||
pkgkey: prevVersion,
|
||||
esClient,
|
||||
spaceId,
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
@ -202,6 +207,7 @@ interface InstallRegistryPackageParams {
|
|||
savedObjectsClient: SavedObjectsClientContract;
|
||||
pkgkey: string;
|
||||
esClient: ElasticsearchClient;
|
||||
spaceId: string;
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
|
@ -229,6 +235,7 @@ async function installPackageFromRegistry({
|
|||
savedObjectsClient,
|
||||
pkgkey,
|
||||
esClient,
|
||||
spaceId,
|
||||
force = false,
|
||||
}: InstallRegistryPackageParams): Promise<InstallResult> {
|
||||
const logger = appContextService.getLogger();
|
||||
|
@ -317,6 +324,7 @@ async function installPackageFromRegistry({
|
|||
paths,
|
||||
packageInfo,
|
||||
installType,
|
||||
spaceId,
|
||||
installSource: 'registry',
|
||||
})
|
||||
.then(async (assets) => {
|
||||
|
@ -339,6 +347,7 @@ async function installPackageFromRegistry({
|
|||
pkgName,
|
||||
pkgVersion,
|
||||
installedPkg,
|
||||
spaceId,
|
||||
esClient,
|
||||
});
|
||||
sendEvent({
|
||||
|
@ -364,6 +373,7 @@ interface InstallUploadedArchiveParams {
|
|||
esClient: ElasticsearchClient;
|
||||
archiveBuffer: Buffer;
|
||||
contentType: string;
|
||||
spaceId: string;
|
||||
}
|
||||
|
||||
async function installPackageByUpload({
|
||||
|
@ -371,6 +381,7 @@ async function installPackageByUpload({
|
|||
esClient,
|
||||
archiveBuffer,
|
||||
contentType,
|
||||
spaceId,
|
||||
}: InstallUploadedArchiveParams): Promise<InstallResult> {
|
||||
const logger = appContextService.getLogger();
|
||||
// if an error happens during getInstallType, report that we don't know
|
||||
|
@ -427,6 +438,7 @@ async function installPackageByUpload({
|
|||
packageInfo,
|
||||
installType,
|
||||
installSource,
|
||||
spaceId,
|
||||
})
|
||||
.then((assets) => {
|
||||
sendEvent({
|
||||
|
@ -451,9 +463,10 @@ async function installPackageByUpload({
|
|||
}
|
||||
}
|
||||
|
||||
export type InstallPackageParams =
|
||||
export type InstallPackageParams = { spaceId: string } & (
|
||||
| ({ installSource: Extract<InstallSource, 'registry'> } & InstallRegistryPackageParams)
|
||||
| ({ installSource: Extract<InstallSource, 'upload'> } & InstallUploadedArchiveParams);
|
||||
| ({ installSource: Extract<InstallSource, 'upload'> } & InstallUploadedArchiveParams)
|
||||
);
|
||||
|
||||
export async function installPackage(args: InstallPackageParams) {
|
||||
if (!('installSource' in args)) {
|
||||
|
@ -463,23 +476,25 @@ export async function installPackage(args: InstallPackageParams) {
|
|||
const { savedObjectsClient, esClient } = args;
|
||||
|
||||
if (args.installSource === 'registry') {
|
||||
const { pkgkey, force } = args;
|
||||
const { pkgkey, force, spaceId } = args;
|
||||
logger.debug(`kicking off install of ${pkgkey} from registry`);
|
||||
const response = installPackageFromRegistry({
|
||||
savedObjectsClient,
|
||||
pkgkey,
|
||||
esClient,
|
||||
spaceId,
|
||||
force,
|
||||
});
|
||||
return response;
|
||||
} else if (args.installSource === 'upload') {
|
||||
const { archiveBuffer, contentType } = args;
|
||||
const { archiveBuffer, contentType, spaceId } = args;
|
||||
logger.debug(`kicking off install of uploaded package`);
|
||||
const response = installPackageByUpload({
|
||||
savedObjectsClient,
|
||||
esClient,
|
||||
archiveBuffer,
|
||||
contentType,
|
||||
spaceId,
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
@ -515,6 +530,7 @@ export async function createInstallation(options: {
|
|||
savedObjectsClient: SavedObjectsClientContract;
|
||||
packageInfo: InstallablePackage;
|
||||
installSource: InstallSource;
|
||||
spaceId: string;
|
||||
}) {
|
||||
const { savedObjectsClient, packageInfo, installSource } = options;
|
||||
const { name: pkgName, version: pkgVersion } = packageInfo;
|
||||
|
@ -534,6 +550,7 @@ export async function createInstallation(options: {
|
|||
PACKAGES_SAVED_OBJECT_TYPE,
|
||||
{
|
||||
installed_kibana: [],
|
||||
installed_kibana_space_id: options.spaceId,
|
||||
installed_es: [],
|
||||
package_assets: [],
|
||||
es_index_patterns: toSaveESIndexPatterns,
|
||||
|
@ -627,6 +644,7 @@ export async function ensurePackagesCompletedInstall(
|
|||
savedObjectsClient,
|
||||
pkgkey,
|
||||
esClient,
|
||||
spaceId: pkg.attributes.installed_kibana_space_id || DEFAULT_SPACE_ID,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,9 +6,15 @@
|
|||
*/
|
||||
|
||||
import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server';
|
||||
|
||||
import Boom from '@hapi/boom';
|
||||
|
||||
import type { SavedObject } from 'src/core/server';
|
||||
|
||||
import { SavedObjectsClient } from '../../../../../../../src/core/server';
|
||||
|
||||
import { PACKAGE_POLICY_SAVED_OBJECT_TYPE, PACKAGES_SAVED_OBJECT_TYPE } from '../../../constants';
|
||||
import { DEFAULT_SPACE_ID } from '../../../../../spaces/common/constants';
|
||||
import { ElasticsearchAssetType } from '../../../types';
|
||||
import type {
|
||||
AssetReference,
|
||||
|
@ -25,6 +31,7 @@ import { packagePolicyService, appContextService } from '../..';
|
|||
import { deletePackageCache } from '../archive';
|
||||
import { deleteIlms } from '../elasticsearch/datastream_ilm/remove';
|
||||
import { removeArchiveEntries } from '../archive/storage';
|
||||
import { SavedObjectsUtils } from '../../../../../../../src/core/server';
|
||||
|
||||
import { getInstallation, kibanaSavedObjectTypes } from './index';
|
||||
|
||||
|
@ -80,10 +87,15 @@ export async function removeInstallation(options: {
|
|||
|
||||
async function deleteKibanaAssets(
|
||||
installedObjects: KibanaAssetReference[],
|
||||
savedObjectsClient: SavedObjectsClientContract
|
||||
spaceId: string = DEFAULT_SPACE_ID
|
||||
) {
|
||||
const savedObjectsClient = new SavedObjectsClient(
|
||||
appContextService.getSavedObjects().createInternalRepository()
|
||||
);
|
||||
const namespace = SavedObjectsUtils.namespaceStringToId(spaceId);
|
||||
const { resolved_objects: resolvedObjects } = await savedObjectsClient.bulkResolve(
|
||||
installedObjects
|
||||
installedObjects,
|
||||
{ namespace }
|
||||
);
|
||||
|
||||
const foundObjects = resolvedObjects.filter(
|
||||
|
@ -94,7 +106,7 @@ async function deleteKibanaAssets(
|
|||
// we filter these out before calling delete
|
||||
const assetsToDelete = foundObjects.map(({ saved_object: { id, type } }) => ({ id, type }));
|
||||
const promises = assetsToDelete.map(async ({ id, type }) => {
|
||||
return savedObjectsClient.delete(type, id);
|
||||
return savedObjectsClient.delete(type, id, { namespace });
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
|
@ -123,12 +135,15 @@ function deleteESAssets(
|
|||
}
|
||||
|
||||
async function deleteAssets(
|
||||
{ installed_es: installedEs, installed_kibana: installedKibana }: Installation,
|
||||
{
|
||||
installed_es: installedEs,
|
||||
installed_kibana: installedKibana,
|
||||
installed_kibana_space_id: spaceId = DEFAULT_SPACE_ID,
|
||||
}: Installation,
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
esClient: ElasticsearchClient
|
||||
) {
|
||||
const logger = appContextService.getLogger();
|
||||
|
||||
// must delete index templates first, or component templates which reference them cannot be deleted
|
||||
// must delete ingestPipelines first, or ml models referenced in them cannot be deleted.
|
||||
// separate the assets into Index Templates and other assets.
|
||||
|
@ -155,7 +170,7 @@ async function deleteAssets(
|
|||
// then the other asset types
|
||||
await Promise.all([
|
||||
...deleteESAssets(otherAssets, esClient),
|
||||
deleteKibanaAssets(installedKibana, savedObjectsClient),
|
||||
deleteKibanaAssets(installedKibana, spaceId),
|
||||
]);
|
||||
} catch (err) {
|
||||
// in the rollback case, partial installs are likely, so missing assets are not an error
|
||||
|
@ -187,19 +202,24 @@ async function deleteComponentTemplate(esClient: ElasticsearchClient, name: stri
|
|||
}
|
||||
}
|
||||
|
||||
export async function deleteKibanaSavedObjectsAssets(
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
installedRefs: KibanaAssetReference[]
|
||||
) {
|
||||
export async function deleteKibanaSavedObjectsAssets({
|
||||
savedObjectsClient,
|
||||
installedPkg,
|
||||
}: {
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
installedPkg: SavedObject<Installation>;
|
||||
}) {
|
||||
const { installed_kibana: installedRefs, installed_kibana_space_id: spaceId } =
|
||||
installedPkg.attributes;
|
||||
if (!installedRefs.length) return;
|
||||
|
||||
const logger = appContextService.getLogger();
|
||||
const assetsToDelete = installedRefs
|
||||
.filter(({ type }) => kibanaSavedObjectTypes.includes(type))
|
||||
.map(({ id, type }) => ({ id, type }));
|
||||
.map(({ id, type }) => ({ id, type } as KibanaAssetReference));
|
||||
|
||||
try {
|
||||
await deleteKibanaAssets(assetsToDelete, savedObjectsClient);
|
||||
await deleteKibanaAssets(assetsToDelete, spaceId);
|
||||
} catch (err) {
|
||||
// in the rollback case, partial installs are likely, so missing assets are not an error
|
||||
if (!savedObjectsClient.errors.isNotFoundError(err)) {
|
||||
|
|
|
@ -18,6 +18,8 @@ import type {
|
|||
import uuid from 'uuid';
|
||||
import { safeLoad } from 'js-yaml';
|
||||
|
||||
import { DEFAULT_SPACE_ID } from '../../../spaces/common/constants';
|
||||
|
||||
import type { AuthenticatedUser } from '../../../security/server';
|
||||
import {
|
||||
packageToPackagePolicy,
|
||||
|
@ -92,6 +94,7 @@ class PackagePolicyService {
|
|||
esClient: ElasticsearchClient,
|
||||
packagePolicy: NewPackagePolicy,
|
||||
options?: {
|
||||
spaceId?: string;
|
||||
id?: string;
|
||||
user?: AuthenticatedUser;
|
||||
bumpRevision?: boolean;
|
||||
|
@ -134,6 +137,7 @@ class PackagePolicyService {
|
|||
const [, packageInfo] = await Promise.all([
|
||||
ensureInstalledPackage({
|
||||
esClient,
|
||||
spaceId: options?.spaceId || DEFAULT_SPACE_ID,
|
||||
savedObjectsClient: soClient,
|
||||
pkgName: packagePolicy.package.name,
|
||||
pkgVersion: packagePolicy.package.version,
|
||||
|
|
|
@ -9,6 +9,7 @@ import uuid from 'uuid';
|
|||
import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks';
|
||||
|
||||
import { SavedObjectsErrorHelpers } from '../../../../../src/core/server';
|
||||
import { DEFAULT_SPACE_ID } from '../../../spaces/common/constants';
|
||||
|
||||
import type {
|
||||
InstallResult,
|
||||
|
@ -225,7 +226,8 @@ describe('policy preconfiguration', () => {
|
|||
esClient,
|
||||
[],
|
||||
[],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
|
||||
expect(policies.length).toBe(0);
|
||||
|
@ -242,7 +244,8 @@ describe('policy preconfiguration', () => {
|
|||
esClient,
|
||||
[],
|
||||
[{ name: 'test_package', version: '3.0.0' }],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
|
||||
expect(policies.length).toBe(0);
|
||||
|
@ -271,7 +274,8 @@ describe('policy preconfiguration', () => {
|
|||
},
|
||||
] as PreconfiguredAgentPolicy[],
|
||||
[{ name: 'test_package', version: '3.0.0' }],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
|
||||
expect(policies.length).toEqual(1);
|
||||
|
@ -322,7 +326,8 @@ describe('policy preconfiguration', () => {
|
|||
},
|
||||
] as PreconfiguredAgentPolicy[],
|
||||
[{ name: 'test_package', version: '3.0.0' }],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
|
||||
expect(mockedPackagePolicyService.create).not.toBeCalled();
|
||||
|
@ -371,7 +376,8 @@ describe('policy preconfiguration', () => {
|
|||
},
|
||||
] as PreconfiguredAgentPolicy[],
|
||||
[{ name: 'test_package', version: '3.0.0' }],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
|
||||
expect(mockedPackagePolicyService.create).toBeCalledTimes(1);
|
||||
|
@ -398,7 +404,8 @@ describe('policy preconfiguration', () => {
|
|||
{ name: 'test_package', version: '3.0.0' },
|
||||
{ name: 'test_package', version: '2.0.0' },
|
||||
],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
)
|
||||
).rejects.toThrow(
|
||||
'Duplicate packages specified in configuration: test_package-3.0.0, test_package-2.0.0'
|
||||
|
@ -429,7 +436,8 @@ describe('policy preconfiguration', () => {
|
|||
esClient,
|
||||
policies,
|
||||
[{ name: 'test_package', version: '3.0.0' }],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
)
|
||||
).rejects.toThrow(
|
||||
'[Test policy] could not be added. [test_package] could not be installed due to error: [Error: REGISTRY ERROR]'
|
||||
|
@ -460,7 +468,8 @@ describe('policy preconfiguration', () => {
|
|||
esClient,
|
||||
policies,
|
||||
[{ name: 'CANNOT_MATCH', version: 'x.y.z' }],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
)
|
||||
).rejects.toThrow(
|
||||
'[Test policy] could not be added. [test_package] is not installed, add [test_package] to [xpack.fleet.packages] or remove it from [Test package].'
|
||||
|
@ -484,7 +493,8 @@ describe('policy preconfiguration', () => {
|
|||
},
|
||||
] as PreconfiguredAgentPolicy[],
|
||||
[],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
|
||||
expect(policiesA.length).toEqual(1);
|
||||
|
@ -509,7 +519,8 @@ describe('policy preconfiguration', () => {
|
|||
},
|
||||
] as PreconfiguredAgentPolicy[],
|
||||
[],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
|
||||
expect(policiesB.length).toEqual(1);
|
||||
|
@ -548,7 +559,8 @@ describe('policy preconfiguration', () => {
|
|||
},
|
||||
] as PreconfiguredAgentPolicy[],
|
||||
[],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
expect(spyAgentPolicyServiceUpdate).toBeCalled();
|
||||
expect(spyAgentPolicyServiceUpdate).toBeCalledWith(
|
||||
|
@ -584,7 +596,8 @@ describe('policy preconfiguration', () => {
|
|||
esClient,
|
||||
[policy],
|
||||
[],
|
||||
mockDefaultOutput
|
||||
mockDefaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
expect(spyAgentPolicyServiceUpdate).not.toBeCalled();
|
||||
expect(policies.length).toEqual(1);
|
||||
|
|
|
@ -144,7 +144,8 @@ export async function ensurePreconfiguredPackagesAndPolicies(
|
|||
esClient: ElasticsearchClient,
|
||||
policies: PreconfiguredAgentPolicy[] = [],
|
||||
packages: PreconfiguredPackage[] = [],
|
||||
defaultOutput: Output
|
||||
defaultOutput: Output,
|
||||
spaceId: string
|
||||
): Promise<PreconfigurationResult> {
|
||||
const logger = appContextService.getLogger();
|
||||
|
||||
|
@ -179,6 +180,7 @@ export async function ensurePreconfiguredPackagesAndPolicies(
|
|||
pkg.version === PRECONFIGURATION_LATEST_KEYWORD ? pkg.name : pkg
|
||||
),
|
||||
force: true, // Always force outdated packages to be installed if a later version isn't installed
|
||||
spaceId,
|
||||
});
|
||||
|
||||
const fulfilledPackages = [];
|
||||
|
|
|
@ -12,6 +12,7 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/s
|
|||
import { AUTO_UPDATE_PACKAGES } from '../../common';
|
||||
import type { DefaultPackagesInstallationError, PreconfigurationError } from '../../common';
|
||||
import { SO_SEARCH_LIMIT, DEFAULT_PACKAGES } from '../constants';
|
||||
import { DEFAULT_SPACE_ID } from '../../../spaces/common/constants';
|
||||
|
||||
import { appContextService } from './app_context';
|
||||
import { agentPolicyService } from './agent_policy';
|
||||
|
@ -103,7 +104,8 @@ async function createSetupSideEffects(
|
|||
esClient,
|
||||
policies,
|
||||
packages,
|
||||
defaultOutput
|
||||
defaultOutput,
|
||||
DEFAULT_SPACE_ID
|
||||
);
|
||||
|
||||
logger.debug('Cleaning up Fleet outputs');
|
||||
|
@ -160,6 +162,7 @@ export async function ensureFleetGlobalEsAssets(
|
|||
savedObjectsClient: soClient,
|
||||
pkgkey: pkgToPkgKey({ name: installation.name, version: installation.version }),
|
||||
esClient,
|
||||
spaceId: DEFAULT_SPACE_ID,
|
||||
// Force install the package will update the index template and the datastream write indices
|
||||
force: true,
|
||||
}).catch((err) => {
|
||||
|
|
|
@ -33,6 +33,7 @@ export interface FleetRequestHandlerContext extends RequestHandlerContext {
|
|||
*/
|
||||
readonly internalSoClient: SavedObjectsClientContract;
|
||||
};
|
||||
spaceId: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ export const items: GetPackagesResponse['items'] = [
|
|||
id: 'ga_installed',
|
||||
attributes: {
|
||||
installed_kibana: [],
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_es: [],
|
||||
package_assets: [],
|
||||
es_index_patterns: {},
|
||||
|
@ -86,6 +87,7 @@ export const items: GetPackagesResponse['items'] = [
|
|||
id: 'ga_installed_update',
|
||||
attributes: {
|
||||
installed_kibana: [],
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_es: [],
|
||||
package_assets: [],
|
||||
es_index_patterns: {},
|
||||
|
@ -147,6 +149,7 @@ export const items: GetPackagesResponse['items'] = [
|
|||
id: 'beta_installed',
|
||||
attributes: {
|
||||
installed_kibana: [],
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_es: [],
|
||||
package_assets: [],
|
||||
es_index_patterns: {},
|
||||
|
@ -183,6 +186,7 @@ export const items: GetPackagesResponse['items'] = [
|
|||
id: 'beta_installed_update',
|
||||
attributes: {
|
||||
installed_kibana: [],
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_es: [],
|
||||
package_assets: [],
|
||||
es_index_patterns: {},
|
||||
|
@ -253,6 +257,7 @@ export const items: GetPackagesResponse['items'] = [
|
|||
id: 'exp_installed',
|
||||
attributes: {
|
||||
installed_kibana: [],
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_es: [],
|
||||
package_assets: [],
|
||||
es_index_patterns: {},
|
||||
|
@ -289,6 +294,7 @@ export const items: GetPackagesResponse['items'] = [
|
|||
id: 'exp_installed_update',
|
||||
attributes: {
|
||||
installed_kibana: [],
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_es: [],
|
||||
package_assets: [],
|
||||
es_index_patterns: {},
|
||||
|
|
|
@ -446,6 +446,7 @@ Object {
|
|||
"type": "search",
|
||||
},
|
||||
],
|
||||
"installed_kibana_space_id": "default",
|
||||
"name": "apache",
|
||||
"package_assets": Array [
|
||||
Object {
|
||||
|
|
|
@ -19,6 +19,7 @@ export default function loadTests({ loadTestFile }) {
|
|||
loadTestFile(require.resolve('./install_overrides'));
|
||||
loadTestFile(require.resolve('./install_prerelease'));
|
||||
loadTestFile(require.resolve('./install_remove_assets'));
|
||||
loadTestFile(require.resolve('./install_remove_kbn_assets_in_space'));
|
||||
loadTestFile(require.resolve('./install_remove_multiple'));
|
||||
loadTestFile(require.resolve('./install_update'));
|
||||
loadTestFile(require.resolve('./bulk_upgrade'));
|
||||
|
|
|
@ -496,6 +496,7 @@ const expectAssetsInstalled = ({
|
|||
package_assets: sortBy(res.attributes.package_assets, (o: AssetReference) => o.type),
|
||||
};
|
||||
expect(sortedRes).eql({
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_kibana: [
|
||||
{
|
||||
id: 'sample_dashboard',
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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 { Client } from '@elastic/elasticsearch';
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
|
||||
import { skipIfNoDockerRegistry } from '../../helpers';
|
||||
import { setupFleetAndAgents } from '../agents/services';
|
||||
|
||||
const testSpaceId = 'fleet_test_space';
|
||||
|
||||
export default function (providerContext: FtrProviderContext) {
|
||||
const { getService } = providerContext;
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const supertest = getService('supertest');
|
||||
const dockerServers = getService('dockerServers');
|
||||
const server = dockerServers.get('registry');
|
||||
const es: Client = getService('es');
|
||||
const pkgName = 'only_dashboard';
|
||||
const pkgVersion = '0.1.0';
|
||||
|
||||
const uninstallPackage = async (pkg: string, version: string) => {
|
||||
await supertest.delete(`/api/fleet/epm/packages/${pkg}/${version}`).set('kbn-xsrf', 'xxxx');
|
||||
};
|
||||
|
||||
const installPackageInSpace = async (pkg: string, version: string, spaceId: string) => {
|
||||
await supertest
|
||||
.post(`/s/${spaceId}/api/fleet/epm/packages/${pkg}/${version}`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({ force: true });
|
||||
};
|
||||
const createSpace = async (spaceId: string) => {
|
||||
await supertest.post(`/api/spaces/space`).set('kbn-xsrf', 'xxxx').send({
|
||||
name: spaceId,
|
||||
id: spaceId,
|
||||
initials: 's',
|
||||
color: '#D6BF57',
|
||||
disabledFeatures: [],
|
||||
imageUrl: '',
|
||||
});
|
||||
};
|
||||
|
||||
const deleteSpace = async (spaceId: string) => {
|
||||
await supertest.delete(`/api/spaces/space/${spaceId}`).set('kbn-xsrf', 'xxxx').send();
|
||||
};
|
||||
|
||||
describe('installs and uninstalls all assets (non default space)', async () => {
|
||||
skipIfNoDockerRegistry(providerContext);
|
||||
setupFleetAndAgents(providerContext);
|
||||
before(async () => {
|
||||
await createSpace(testSpaceId);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteSpace(testSpaceId);
|
||||
});
|
||||
describe('installs all assets when installing a package for the first time in non default space', async () => {
|
||||
before(async () => {
|
||||
if (!server.enabled) return;
|
||||
await installPackageInSpace(pkgName, pkgVersion, testSpaceId);
|
||||
});
|
||||
after(async () => {
|
||||
if (!server.enabled) return;
|
||||
await uninstallPackage(pkgName, pkgVersion);
|
||||
});
|
||||
|
||||
expectAssetsInstalled({
|
||||
pkgVersion,
|
||||
pkgName,
|
||||
es,
|
||||
kibanaServer,
|
||||
});
|
||||
});
|
||||
|
||||
describe('uninstalls all assets when uninstalling a package from a different space', async () => {
|
||||
before(async () => {
|
||||
if (!server.enabled) return;
|
||||
await installPackageInSpace(pkgName, pkgVersion, testSpaceId);
|
||||
await uninstallPackage(pkgName, pkgVersion);
|
||||
});
|
||||
|
||||
it('should have uninstalled the kibana assets', async function () {
|
||||
let resDashboard;
|
||||
try {
|
||||
resDashboard = await kibanaServer.savedObjects.get({
|
||||
type: 'dashboard',
|
||||
id: 'test_dashboard',
|
||||
space: testSpaceId,
|
||||
});
|
||||
} catch (err) {
|
||||
resDashboard = err;
|
||||
}
|
||||
expect(resDashboard.response.data.statusCode).equal(404);
|
||||
|
||||
let resVis;
|
||||
try {
|
||||
resVis = await kibanaServer.savedObjects.get({
|
||||
type: 'visualization',
|
||||
id: 'test_visualization',
|
||||
space: testSpaceId,
|
||||
});
|
||||
} catch (err) {
|
||||
resVis = err;
|
||||
}
|
||||
expect(resVis.response.data.statusCode).equal(404);
|
||||
let resSearch;
|
||||
try {
|
||||
resVis = await kibanaServer.savedObjects.get({
|
||||
type: 'search',
|
||||
id: 'test_search',
|
||||
space: testSpaceId,
|
||||
});
|
||||
} catch (err) {
|
||||
resSearch = err;
|
||||
}
|
||||
expect(resSearch.response.data.statusCode).equal(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const expectAssetsInstalled = ({
|
||||
pkgVersion,
|
||||
pkgName,
|
||||
es,
|
||||
kibanaServer,
|
||||
}: {
|
||||
pkgVersion: string;
|
||||
pkgName: string;
|
||||
es: Client;
|
||||
kibanaServer: any;
|
||||
}) => {
|
||||
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-*',
|
||||
});
|
||||
expect(resIndexPatternLogs.id).equal('logs-*');
|
||||
const resIndexPatternMetrics = await kibanaServer.savedObjects.get({
|
||||
type: 'index-pattern',
|
||||
id: 'metrics-*',
|
||||
});
|
||||
expect(resIndexPatternMetrics.id).equal('metrics-*');
|
||||
|
||||
// These are the assets from the package
|
||||
const resDashboard = await kibanaServer.savedObjects.get({
|
||||
type: 'dashboard',
|
||||
id: 'test_dashboard',
|
||||
space: testSpaceId,
|
||||
});
|
||||
expect(resDashboard.id).equal('test_dashboard');
|
||||
|
||||
const resVis = await kibanaServer.savedObjects.get({
|
||||
type: 'visualization',
|
||||
id: 'test_visualization',
|
||||
space: testSpaceId,
|
||||
});
|
||||
expect(resVis.id).equal('test_visualization');
|
||||
const resSearch = await kibanaServer.savedObjects.get({
|
||||
type: 'search',
|
||||
id: 'test_search',
|
||||
space: testSpaceId,
|
||||
});
|
||||
expect(resSearch.id).equal('test_search');
|
||||
});
|
||||
};
|
|
@ -328,6 +328,7 @@ export default function (providerContext: FtrProviderContext) {
|
|||
id: 'all_assets',
|
||||
});
|
||||
expect(res.attributes).eql({
|
||||
installed_kibana_space_id: 'default',
|
||||
installed_kibana: [
|
||||
{
|
||||
id: 'sample_dashboard',
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# Test package
|
||||
|
||||
For testing that a package installs its elasticsearch assets when installed for the first time (not updating) and removing the package
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#F04E98" d="M29,32.0001 L15.935,9.4321 C13.48,5.1941 7,6.9351 7,11.8321 L7,52.1681 C7,57.0651 13.48,58.8061 15.935,54.5671 L29,32.0001 Z"/>
|
||||
<path fill="#FA744E" d="M34.7773,32.0001 L33.3273,34.5051 L20.2613,57.0731 C19.8473,57.7871 19.3533,58.4271 18.8023,59.0001 L34.9273,59.0001 C38.7073,59.0001 42.2213,57.0601 44.2363,53.8611 L58.0003,32.0001 L34.7773,32.0001 Z"/>
|
||||
<path fill="#343741" d="M44.2363,10.1392 C42.2213,6.9402 38.7073,5.0002 34.9273,5.0002 L18.8023,5.0002 C19.3533,5.5732 19.8473,6.2122 20.2613,6.9272 L33.3273,29.4942 L34.7773,32.0002 L58.0003,32.0002 L44.2363,10.1392 Z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 751 B |
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"attributes": {
|
||||
"description": "Sample dashboard",
|
||||
"hits": 0,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": "{\"filter\":[],\"highlightAll\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}"
|
||||
},
|
||||
"optionsJSON": "{\"darkTheme\":false}",
|
||||
"panelsJSON": "[{\"embeddableConfig\":{},\"gridData\":{\"h\":12,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"panelRefName\":\"panel_0\",\"version\":\"7.3.0\"},{\"embeddableConfig\":{\"columns\":[\"kafka.log.class\",\"kafka.log.trace.class\",\"kafka.log.trace.full\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":12,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"panelRefName\":\"panel_1\",\"version\":\"7.3.0\"},{\"embeddableConfig\":{\"columns\":[\"log.level\",\"kafka.log.component\",\"message\"],\"sort\":[\"@timestamp\",\"desc\"]},\"gridData\":{\"h\":20,\"i\":\"3\",\"w\":48,\"x\":0,\"y\":20},\"panelIndex\":\"3\",\"panelRefName\":\"panel_2\",\"version\":\"7.3.0\"},{\"embeddableConfig\":{},\"gridData\":{\"h\":8,\"i\":\"4\",\"w\":48,\"x\":0,\"y\":12},\"panelIndex\":\"4\",\"panelRefName\":\"panel_3\",\"version\":\"7.3.0\"}]",
|
||||
"timeRestore": false,
|
||||
"title": "[Logs Sample] Overview ECS",
|
||||
"version": 1
|
||||
},
|
||||
"references": [
|
||||
{ "id": "test_visualization", "name": "panel_0", "type": "visualization" },
|
||||
{ "id": "test_search", "name": "panel_1", "type": "search" },
|
||||
{ "id": "test_search", "name": "panel_2", "type": "search" },
|
||||
{ "id": "test_visualization", "name": "panel_3", "type": "visualization" }
|
||||
],
|
||||
"id": "test_dashboard",
|
||||
"type": "dashboard"
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"attributes": {
|
||||
"columns": [
|
||||
"log.level",
|
||||
"kafka.log.component",
|
||||
"message"
|
||||
],
|
||||
"description": "",
|
||||
"hits": 0,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": "{\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\",\"key\":\"dataset.name\",\"negate\":false,\"params\":{\"query\":\"kafka.log\",\"type\":\"phrase\"},\"type\":\"phrase\",\"value\":\"log\"},\"query\":{\"match\":{\"dataset.name\":{\"query\":\"kafka.log\",\"type\":\"phrase\"}}}}],\"highlightAll\":true,\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"version\":true}"
|
||||
},
|
||||
"sort": [
|
||||
[
|
||||
"@timestamp",
|
||||
"desc"
|
||||
]
|
||||
],
|
||||
"title": "All logs [Logs Kafka] ECS",
|
||||
"version": 1
|
||||
},
|
||||
"id": "test_search",
|
||||
"type": "search"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"attributes": {
|
||||
"description": "sample visualization update",
|
||||
"title": "sample vis title",
|
||||
"uiStateJSON": "{}",
|
||||
"version": 1,
|
||||
"visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"extended_bounds\":{},\"field\":\"@timestamp\",\"interval\":\"auto\",\"min_doc_count\":1},\"schema\":\"segment\",\"type\":\"date_histogram\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"customLabel\":\"Log Level\",\"field\":\"log.level\",\"order\":\"desc\",\"orderBy\":\"1\",\"size\":5},\"schema\":\"group\",\"type\":\"terms\"}],\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"labels\":{\"show\":true,\"truncate\":100},\"position\":\"bottom\",\"scale\":{\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"@timestamp per day\"},\"type\":\"category\"}],\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"legendPosition\":\"right\",\"seriesParams\":[{\"data\":{\"id\":\"1\",\"label\":\"Count\"},\"drawLinesBetweenPoints\":true,\"mode\":\"stacked\",\"show\":\"true\",\"showCircles\":true,\"type\":\"histogram\",\"valueAxis\":\"ValueAxis-1\"}],\"times\":[],\"type\":\"histogram\",\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"labels\":{\"filter\":false,\"rotate\":0,\"show\":true,\"truncate\":100},\"name\":\"LeftAxis-1\",\"position\":\"left\",\"scale\":{\"mode\":\"normal\",\"type\":\"linear\"},\"show\":true,\"style\":{},\"title\":{\"text\":\"Count\"},\"type\":\"value\"}]},\"title\":\"Log levels over time [Logs Kafka] ECS\",\"type\":\"histogram\"}"
|
||||
},
|
||||
"id": "test_visualization",
|
||||
"type": "visualization"
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
format_version: 1.0.0
|
||||
name: only_dashboard
|
||||
title: Only installs one dashboard
|
||||
description: This package is used to test installing assets into a non-default space.
|
||||
version: 0.1.0
|
||||
categories: []
|
||||
release: beta
|
||||
type: integration
|
||||
license: basic
|
||||
|
||||
requirement:
|
||||
elasticsearch:
|
||||
versions: '>7.7.0'
|
||||
kibana:
|
||||
versions: '>7.7.0'
|
||||
|
||||
icons:
|
||||
- src: '/img/logo_overrides_64_color.svg'
|
||||
size: '16x16'
|
||||
type: 'image/svg+xml'
|
Loading…
Add table
Add a link
Reference in a new issue