[Ingest Manager] Ingest setup upgrade (#78081)

* Adding bulk upgrade api

* Addressing comments

* Removing todo

* Changing body field

* Adding helper for getting the bulk install route

* Adding request spec

* Pulling in Johns changes

* Removing test for same package upgraded multiple times

* Adding upgrade to setup route

* Adding setup integration test

* Clean up error handling

* Beginning to add tests

* Failing jest mock tests

* Break up tests & modules for easier testing.

Deal with issue described in https://github.com/facebook/jest/issues/1075#issuecomment-221771095

epm/packages/install has functions a, b, c which are independent but a can also call b and c

function a() {
  b();
  c();
}

The linked FB issue describes the cause and rationale (Jest works on "module" boundary) but TL;DR: it's easier if you split up your files

Some related links I found during this journey

 * https://medium.com/@qjli/how-to-mock-specific-module-function-in-jest-715e39a391f4
  * https://stackoverflow.com/questions/52650367/jestjs-how-to-test-function-being-called-inside-another-function
   * 50855968 (50855968)

* Add test confirming update error result will throw

* Keep orig error. Add status code in http handler

* Leave error as-is

* Removing accidental code changes. File rename.

* Missed a function when moving to a new file

* Add missing type imports

* Lift .map lambda into named outer function

* Adding additional test

* Fixing type error

Co-authored-by: John Schulz <john.schulz@elastic.co>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Jonathan Buttner 2020-09-28 16:12:34 -04:00 committed by GitHub
parent 26f2bd204d
commit e75b36a68d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 426 additions and 191 deletions

View file

@ -71,7 +71,7 @@ export interface InstallPackageResponse {
response: AssetReference[];
}
export interface IBulkInstallPackageError {
export interface IBulkInstallPackageHTTPError {
name: string;
statusCode: number;
error: string | Error;
@ -86,7 +86,7 @@ export interface BulkInstallPackageInfo {
}
export interface BulkInstallPackagesResponse {
response: Array<BulkInstallPackageInfo | IBulkInstallPackageError>;
response: Array<BulkInstallPackageInfo | IBulkInstallPackageHTTPError>;
}
export interface BulkInstallPackagesRequest {

View file

@ -13,7 +13,9 @@ import {
GetCategoriesResponse,
GetPackagesResponse,
GetLimitedPackagesResponse,
BulkInstallPackageInfo,
BulkInstallPackagesResponse,
IBulkInstallPackageHTTPError,
} from '../../../common';
import {
GetCategoriesRequestSchema,
@ -26,21 +28,21 @@ import {
BulkUpgradePackagesFromRegistryRequestSchema,
} from '../../types';
import {
BulkInstallResponse,
bulkInstallPackages,
getCategories,
getPackages,
getFile,
getPackageInfo,
handleInstallPackageFailure,
installPackage,
isBulkInstallError,
removeInstallation,
getLimitedPackages,
getInstallationObject,
} from '../../services/epm/packages';
import { defaultIngestErrorHandler } from '../../errors';
import { defaultIngestErrorHandler, ingestErrorToResponseOptions } from '../../errors';
import { splitPkgKey } from '../../services/epm/registry';
import {
handleInstallPackageFailure,
bulkInstallPackages,
} from '../../services/epm/packages/install';
export const getCategoriesHandler: RequestHandler<
undefined,
@ -171,6 +173,21 @@ export const installPackageFromRegistryHandler: RequestHandler<
}
};
const bulkInstallServiceResponseToHttpEntry = (
result: BulkInstallResponse
): BulkInstallPackageInfo | IBulkInstallPackageHTTPError => {
if (isBulkInstallError(result)) {
const { statusCode, body } = ingestErrorToResponseOptions(result.error);
return {
name: result.name,
statusCode,
error: body.message,
};
} else {
return result;
}
};
export const bulkInstallPackagesFromRegistryHandler: RequestHandler<
undefined,
undefined,
@ -178,13 +195,14 @@ export const bulkInstallPackagesFromRegistryHandler: RequestHandler<
> = async (context, request, response) => {
const savedObjectsClient = context.core.savedObjects.client;
const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser;
const res = await bulkInstallPackages({
const bulkInstalledResponses = await bulkInstallPackages({
savedObjectsClient,
callCluster,
packagesToUpgrade: request.body.packages,
});
const payload = bulkInstalledResponses.map(bulkInstallServiceResponseToHttpEntry);
const body: BulkInstallPackagesResponse = {
response: res,
response: payload,
};
return response.ok({ body });
};

View file

@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedObjectsClientContract } from 'src/core/server';
import { CallESAsCurrentUser } from '../../../types';
import * as Registry from '../registry';
import { getInstallationObject } from './index';
import { BulkInstallResponse, IBulkInstallPackageError, upgradePackage } from './install';
interface BulkInstallPackagesParams {
savedObjectsClient: SavedObjectsClientContract;
packagesToUpgrade: string[];
callCluster: CallESAsCurrentUser;
}
export async function bulkInstallPackages({
savedObjectsClient,
packagesToUpgrade,
callCluster,
}: BulkInstallPackagesParams): Promise<BulkInstallResponse[]> {
const installedAndLatestPromises = packagesToUpgrade.map((pkgToUpgrade) =>
Promise.all([
getInstallationObject({ savedObjectsClient, pkgName: pkgToUpgrade }),
Registry.fetchFindLatestPackage(pkgToUpgrade),
])
);
const installedAndLatestResults = await Promise.allSettled(installedAndLatestPromises);
const installResponsePromises = installedAndLatestResults.map(async (result, index) => {
const pkgToUpgrade = packagesToUpgrade[index];
if (result.status === 'fulfilled') {
const [installedPkg, latestPkg] = result.value;
return upgradePackage({
savedObjectsClient,
callCluster,
installedPkg,
latestPkg,
pkgToUpgrade,
});
} else {
return { name: pkgToUpgrade, error: result.reason };
}
});
const installResults = await Promise.allSettled(installResponsePromises);
const installResponses = installResults.map((result, index) => {
const pkgToUpgrade = packagesToUpgrade[index];
if (result.status === 'fulfilled') {
return result.value;
} else {
return { name: pkgToUpgrade, error: result.reason };
}
});
return installResponses;
}
export function isBulkInstallError(test: any): test is IBulkInstallPackageError {
return 'error' in test && test.error instanceof Error;
}

View file

@ -0,0 +1,144 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ElasticsearchAssetType, Installation, KibanaAssetType } from '../../../types';
import { SavedObject, SavedObjectsClientContract } from 'src/core/server';
jest.mock('./install');
jest.mock('./bulk_install_packages');
jest.mock('./get');
import { bulkInstallPackages, isBulkInstallError } from './bulk_install_packages';
const { ensureInstalledDefaultPackages } = jest.requireActual('./install');
const { isBulkInstallError: actualIsBulkInstallError } = jest.requireActual(
'./bulk_install_packages'
);
import { getInstallation } from './get';
import { savedObjectsClientMock } from 'src/core/server/mocks';
import { appContextService } from '../../app_context';
import { createAppContextStartContractMock } from '../../../mocks';
// if we add this assertion, TS will type check the return value
// and the editor will also know about .mockImplementation, .mock.calls, etc
const mockedBulkInstallPackages = bulkInstallPackages as jest.MockedFunction<
typeof bulkInstallPackages
>;
const mockedIsBulkInstallError = isBulkInstallError as jest.MockedFunction<
typeof isBulkInstallError
>;
const mockedGetInstallation = getInstallation as jest.MockedFunction<typeof getInstallation>;
// I was unable to get the actual implementation set in the `jest.mock()` call at the top to work
// so this will set the `isBulkInstallError` function back to the actual implementation
mockedIsBulkInstallError.mockImplementation(actualIsBulkInstallError);
const mockInstallation: SavedObject<Installation> = {
id: 'test-pkg',
references: [],
type: 'epm-packages',
attributes: {
id: 'test-pkg',
installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }],
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
es_index_patterns: { pattern: 'pattern-name' },
name: 'test package',
version: '1.0.0',
install_status: 'installed',
install_version: '1.0.0',
install_started_at: new Date().toISOString(),
},
};
describe('ensureInstalledDefaultPackages', () => {
let soClient: jest.Mocked<SavedObjectsClientContract>;
beforeEach(async () => {
soClient = savedObjectsClientMock.create();
appContextService.start(createAppContextStartContractMock());
});
afterEach(async () => {
appContextService.stop();
});
it('should return an array of Installation objects when successful', async () => {
mockedGetInstallation.mockImplementation(async () => {
return mockInstallation.attributes;
});
mockedBulkInstallPackages.mockImplementationOnce(async function () {
return [
{
name: mockInstallation.attributes.name,
assets: [],
newVersion: '',
oldVersion: '',
statusCode: 200,
},
];
});
const resp = await ensureInstalledDefaultPackages(soClient, jest.fn());
expect(resp).toEqual([mockInstallation.attributes]);
});
it('should throw the first Error it finds', async () => {
class SomeCustomError extends Error {}
mockedGetInstallation.mockImplementation(async () => {
return mockInstallation.attributes;
});
mockedBulkInstallPackages.mockImplementationOnce(async function () {
return [
{
name: 'success one',
assets: [],
newVersion: '',
oldVersion: '',
statusCode: 200,
},
{
name: 'success two',
assets: [],
newVersion: '',
oldVersion: '',
statusCode: 200,
},
{
name: 'failure one',
error: new SomeCustomError('abc 123'),
},
{
name: 'success three',
assets: [],
newVersion: '',
oldVersion: '',
statusCode: 200,
},
{
name: 'failure two',
error: new Error('zzz'),
},
];
});
const installPromise = ensureInstalledDefaultPackages(soClient, jest.fn());
expect.assertions(2);
expect(installPromise).rejects.toThrow(SomeCustomError);
expect(installPromise).rejects.toThrow('abc 123');
});
it('should throw an error when get installation returns undefined', async () => {
mockedGetInstallation.mockImplementation(async () => {
return undefined;
});
mockedBulkInstallPackages.mockImplementationOnce(async function () {
return [
{
name: 'undefined package',
assets: [],
newVersion: '',
oldVersion: '',
statusCode: 200,
},
];
});
const installPromise = ensureInstalledDefaultPackages(soClient, jest.fn());
expect.assertions(1);
expect(installPromise).rejects.toThrow();
});
});

View file

@ -0,0 +1,101 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedObject } from 'src/core/server';
import { ElasticsearchAssetType, Installation, KibanaAssetType } from '../../../types';
import { getInstallType } from './install';
const mockInstallation: SavedObject<Installation> = {
id: 'test-pkg',
references: [],
type: 'epm-packages',
attributes: {
id: 'test-pkg',
installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }],
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
es_index_patterns: { pattern: 'pattern-name' },
name: 'test packagek',
version: '1.0.0',
install_status: 'installed',
install_version: '1.0.0',
install_started_at: new Date().toISOString(),
},
};
const mockInstallationUpdateFail: SavedObject<Installation> = {
id: 'test-pkg',
references: [],
type: 'epm-packages',
attributes: {
id: 'test-pkg',
installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }],
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
es_index_patterns: { pattern: 'pattern-name' },
name: 'test packagek',
version: '1.0.0',
install_status: 'installing',
install_version: '1.0.1',
install_started_at: new Date().toISOString(),
},
};
describe('getInstallType', () => {
it('should return correct type when installing and no other version is currently installed', () => {
const installTypeInstall = getInstallType({ pkgVersion: '1.0.0', installedPkg: undefined });
expect(installTypeInstall).toBe('install');
// @ts-expect-error can only be 'install' if no installedPkg given
expect(installTypeInstall === 'update').toBe(false);
// @ts-expect-error can only be 'install' if no installedPkg given
expect(installTypeInstall === 'reinstall').toBe(false);
// @ts-expect-error can only be 'install' if no installedPkg given
expect(installTypeInstall === 'reupdate').toBe(false);
// @ts-expect-error can only be 'install' if no installedPkg given
expect(installTypeInstall === 'rollback').toBe(false);
});
it('should return correct type when installing the same version', () => {
const installTypeReinstall = getInstallType({
pkgVersion: '1.0.0',
installedPkg: mockInstallation,
});
expect(installTypeReinstall).toBe('reinstall');
// @ts-expect-error cannot be 'install' if given installedPkg
expect(installTypeReinstall === 'install').toBe(false);
});
it('should return correct type when moving from one version to another', () => {
const installTypeUpdate = getInstallType({
pkgVersion: '1.0.1',
installedPkg: mockInstallation,
});
expect(installTypeUpdate).toBe('update');
// @ts-expect-error cannot be 'install' if given installedPkg
expect(installTypeUpdate === 'install').toBe(false);
});
it('should return correct type when update fails and trys again', () => {
const installTypeReupdate = getInstallType({
pkgVersion: '1.0.1',
installedPkg: mockInstallationUpdateFail,
});
expect(installTypeReupdate).toBe('reupdate');
// @ts-expect-error cannot be 'install' if given installedPkg
expect(installTypeReupdate === 'install').toBe(false);
});
it('should return correct type when attempting to rollback from a failed update', () => {
const installTypeRollback = getInstallType({
pkgVersion: '1.0.0',
installedPkg: mockInstallationUpdateFail,
});
expect(installTypeRollback).toBe('rollback');
// @ts-expect-error cannot be 'install' if given installedPkg
expect(installTypeRollback === 'install').toBe(false);
});
});

View file

@ -12,6 +12,8 @@ import {
InstallationStatus,
KibanaAssetType,
} from '../../../types';
export { bulkInstallPackages, isBulkInstallError } from './bulk_install_packages';
export {
getCategories,
getFile,
@ -23,7 +25,13 @@ export {
SearchParams,
} from './get';
export { installPackage, ensureInstalledPackage } from './install';
export {
BulkInstallResponse,
handleInstallPackageFailure,
installPackage,
IBulkInstallPackageError,
ensureInstalledPackage,
} from './install';
export { removeInstallation } from './remove';
type RequiredPackage = 'system' | 'endpoint';

View file

@ -1,103 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ElasticsearchAssetType, Installation, KibanaAssetType } from '../../../types';
import { SavedObject } from 'src/core/server';
import { getInstallType } from './install';
const mockInstallation: SavedObject<Installation> = {
id: 'test-pkg',
references: [],
type: 'epm-packages',
attributes: {
id: 'test-pkg',
installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }],
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
es_index_patterns: { pattern: 'pattern-name' },
name: 'test packagek',
version: '1.0.0',
install_status: 'installed',
install_version: '1.0.0',
install_started_at: new Date().toISOString(),
},
};
const mockInstallationUpdateFail: SavedObject<Installation> = {
id: 'test-pkg',
references: [],
type: 'epm-packages',
attributes: {
id: 'test-pkg',
installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }],
installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }],
es_index_patterns: { pattern: 'pattern-name' },
name: 'test packagek',
version: '1.0.0',
install_status: 'installing',
install_version: '1.0.1',
install_started_at: new Date().toISOString(),
},
};
describe('install', () => {
describe('getInstallType', () => {
it('should return correct type when installing and no other version is currently installed', () => {
const installTypeInstall = getInstallType({ pkgVersion: '1.0.0', installedPkg: undefined });
expect(installTypeInstall).toBe('install');
// @ts-expect-error can only be 'install' if no installedPkg given
expect(installTypeInstall === 'update').toBe(false);
// @ts-expect-error can only be 'install' if no installedPkg given
expect(installTypeInstall === 'reinstall').toBe(false);
// @ts-expect-error can only be 'install' if no installedPkg given
expect(installTypeInstall === 'reupdate').toBe(false);
// @ts-expect-error can only be 'install' if no installedPkg given
expect(installTypeInstall === 'rollback').toBe(false);
});
it('should return correct type when installing the same version', () => {
const installTypeReinstall = getInstallType({
pkgVersion: '1.0.0',
installedPkg: mockInstallation,
});
expect(installTypeReinstall).toBe('reinstall');
// @ts-expect-error cannot be 'install' if given installedPkg
expect(installTypeReinstall === 'install').toBe(false);
});
it('should return correct type when moving from one version to another', () => {
const installTypeUpdate = getInstallType({
pkgVersion: '1.0.1',
installedPkg: mockInstallation,
});
expect(installTypeUpdate).toBe('update');
// @ts-expect-error cannot be 'install' if given installedPkg
expect(installTypeUpdate === 'install').toBe(false);
});
it('should return correct type when update fails and trys again', () => {
const installTypeReupdate = getInstallType({
pkgVersion: '1.0.1',
installedPkg: mockInstallationUpdateFail,
});
expect(installTypeReupdate).toBe('reupdate');
// @ts-expect-error cannot be 'install' if given installedPkg
expect(installTypeReupdate === 'install').toBe(false);
});
it('should return correct type when attempting to rollback from a failed update', () => {
const installTypeRollback = getInstallType({
pkgVersion: '1.0.0',
installedPkg: mockInstallationUpdateFail,
});
expect(installTypeRollback).toBe('rollback');
// @ts-expect-error cannot be 'install' if given installedPkg
expect(installTypeRollback === 'install').toBe(false);
});
});
});

View file

@ -8,7 +8,7 @@ import { SavedObject, SavedObjectsClientContract } from 'src/core/server';
import semver from 'semver';
import Boom from 'boom';
import { UnwrapPromise } from '@kbn/utility-types';
import { BulkInstallPackageInfo, IBulkInstallPackageError } from '../../../../common';
import { BulkInstallPackageInfo } from '../../../../common';
import { PACKAGES_SAVED_OBJECT_TYPE, MAX_TIME_COMPLETE_INSTALL } from '../../../constants';
import {
AssetReference,
@ -23,7 +23,13 @@ import {
} from '../../../types';
import { installIndexPatterns } from '../kibana/index_pattern/install';
import * as Registry from '../registry';
import { getInstallation, getInstallationObject, isRequiredPackage } from './index';
import {
getInstallation,
getInstallationObject,
isRequiredPackage,
bulkInstallPackages,
isBulkInstallError,
} from './index';
import { installTemplates } from '../elasticsearch/template/install';
import { generateESIndexPatterns } from '../elasticsearch/template/template';
import { installPipelines, deletePreviousPipelines } from '../elasticsearch/ingest_pipeline/';
@ -36,11 +42,7 @@ import {
} from '../kibana/assets/install';
import { updateCurrentWriteIndices } from '../elasticsearch/template/template';
import { deleteKibanaSavedObjectsAssets, removeInstallation } from './remove';
import {
IngestManagerError,
PackageOutdatedError,
ingestErrorToResponseOptions,
} from '../../../errors';
import { IngestManagerError, PackageOutdatedError } from '../../../errors';
import { getPackageSavedObjects } from './get';
import { installTransformForDataset } from '../elasticsearch/transform/install';
import { appContextService } from '../../app_context';
@ -68,17 +70,27 @@ export async function ensureInstalledDefaultPackages(
callCluster: CallESAsCurrentUser
): Promise<Installation[]> {
const installations = [];
for (const pkgName in DefaultPackages) {
if (!DefaultPackages.hasOwnProperty(pkgName)) continue;
const installation = ensureInstalledPackage({
savedObjectsClient,
pkgName,
callCluster,
});
installations.push(installation);
const bulkResponse = await bulkInstallPackages({
savedObjectsClient,
packagesToUpgrade: Object.values(DefaultPackages),
callCluster,
});
for (const resp of bulkResponse) {
if (isBulkInstallError(resp)) {
throw resp.error;
} else {
installations.push(getInstallation({ savedObjectsClient, pkgName: resp.name }));
}
}
return Promise.all(installations);
const retrievedInstallations = await Promise.all(installations);
return retrievedInstallations.map((installation, index) => {
if (!installation) {
throw new Error(`could not get installation ${bulkResponse[index].name}`);
}
return installation;
});
}
export async function ensureInstalledPackage(options: {
@ -154,21 +166,11 @@ export async function handleInstallPackageFailure({
}
}
type BulkInstallResponse = BulkInstallPackageInfo | IBulkInstallPackageError;
function bulkInstallErrorToOptions({
pkgToUpgrade,
error,
}: {
pkgToUpgrade: string;
export interface IBulkInstallPackageError {
name: string;
error: Error;
}): IBulkInstallPackageError {
const { statusCode, body } = ingestErrorToResponseOptions(error);
return {
name: pkgToUpgrade,
statusCode,
error: body.message,
};
}
export type BulkInstallResponse = BulkInstallPackageInfo | IBulkInstallPackageError;
interface UpgradePackageParams {
savedObjectsClient: SavedObjectsClientContract;
@ -177,7 +179,7 @@ interface UpgradePackageParams {
latestPkg: UnwrapPromise<ReturnType<typeof Registry.fetchFindLatestPackage>>;
pkgToUpgrade: string;
}
async function upgradePackage({
export async function upgradePackage({
savedObjectsClient,
callCluster,
installedPkg,
@ -207,7 +209,7 @@ async function upgradePackage({
installedPkg,
callCluster,
});
return bulkInstallErrorToOptions({ pkgToUpgrade, error: installFailed });
return { name: pkgToUpgrade, error: installFailed };
}
} else {
// package was already at the latest version
@ -223,51 +225,6 @@ async function upgradePackage({
}
}
interface BulkInstallPackagesParams {
savedObjectsClient: SavedObjectsClientContract;
packagesToUpgrade: string[];
callCluster: CallESAsCurrentUser;
}
export async function bulkInstallPackages({
savedObjectsClient,
packagesToUpgrade,
callCluster,
}: BulkInstallPackagesParams): Promise<BulkInstallResponse[]> {
const installedAndLatestPromises = packagesToUpgrade.map((pkgToUpgrade) =>
Promise.all([
getInstallationObject({ savedObjectsClient, pkgName: pkgToUpgrade }),
Registry.fetchFindLatestPackage(pkgToUpgrade),
])
);
const installedAndLatestResults = await Promise.allSettled(installedAndLatestPromises);
const installResponsePromises = installedAndLatestResults.map(async (result, index) => {
const pkgToUpgrade = packagesToUpgrade[index];
if (result.status === 'fulfilled') {
const [installedPkg, latestPkg] = result.value;
return upgradePackage({
savedObjectsClient,
callCluster,
installedPkg,
latestPkg,
pkgToUpgrade,
});
} else {
return bulkInstallErrorToOptions({ pkgToUpgrade, error: result.reason });
}
});
const installResults = await Promise.allSettled(installResponsePromises);
const installResponses = installResults.map((result, index) => {
const pkgToUpgrade = packagesToUpgrade[index];
if (result.status === 'fulfilled') {
return result.value;
} else {
return bulkInstallErrorToOptions({ pkgToUpgrade, error: result.reason });
}
});
return installResponses;
}
interface InstallPackageParams {
savedObjectsClient: SavedObjectsClientContract;
pkgkey: string;

View file

@ -10,7 +10,7 @@ import { skipIfNoDockerRegistry } from '../../helpers';
import {
BulkInstallPackageInfo,
BulkInstallPackagesResponse,
IBulkInstallPackageError,
IBulkInstallPackageHTTPError,
} from '../../../../plugins/ingest_manager/common';
export default function (providerContext: FtrProviderContext) {
@ -68,7 +68,7 @@ export default function (providerContext: FtrProviderContext) {
expect(entry.oldVersion).equal('0.1.0');
expect(entry.newVersion).equal('0.3.0');
const err = body.response[1] as IBulkInstallPackageError;
const err = body.response[1] as IBulkInstallPackageHTTPError;
expect(err.statusCode).equal(404);
expect(body.response[1].name).equal('blahblah');
});

View file

@ -7,6 +7,7 @@
export default function loadTests({ loadTestFile }) {
describe('EPM Endpoints', () => {
loadTestFile(require.resolve('./list'));
loadTestFile(require.resolve('./setup'));
loadTestFile(require.resolve('./get'));
loadTestFile(require.resolve('./file'));
//loadTestFile(require.resolve('./template'));

View file

@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
import { skipIfNoDockerRegistry } from '../../helpers';
import { GetInfoResponse, Installed } from '../../../../plugins/ingest_manager/common';
export default function (providerContext: FtrProviderContext) {
const { getService } = providerContext;
const supertest = getService('supertest');
const log = getService('log');
describe('setup api', async () => {
skipIfNoDockerRegistry(providerContext);
describe('setup performs upgrades', async () => {
const oldEndpointVersion = '0.13.0';
beforeEach(async () => {
await supertest
.post(`/api/ingest_manager/epm/packages/endpoint-${oldEndpointVersion}`)
.set('kbn-xsrf', 'xxxx')
.send({ force: true })
.expect(200);
});
it('upgrades the endpoint package from 0.13.0 to the latest version available', async function () {
let { body }: { body: GetInfoResponse } = await supertest
.get(`/api/ingest_manager/epm/packages/endpoint-${oldEndpointVersion}`)
.expect(200);
const latestEndpointVersion = body.response.latestVersion;
log.info(`Endpoint package latest version: ${latestEndpointVersion}`);
// make sure we're actually doing an upgrade
expect(latestEndpointVersion).not.eql(oldEndpointVersion);
await supertest.post(`/api/ingest_manager/setup`).set('kbn-xsrf', 'xxxx').expect(200);
({ body } = await supertest
.get(`/api/ingest_manager/epm/packages/endpoint-${latestEndpointVersion}`)
.expect(200));
expect(body.response).to.have.property('savedObject');
expect((body.response as Installed).savedObject.attributes.install_version).to.eql(
latestEndpointVersion
);
});
});
});
}