Skip to content

Commit

Permalink
[Ingest Manager] Allow prerelease in package version (#74452)
Browse files Browse the repository at this point in the history
* Allow prerelease in version

* Adding integration test for prerelease version of a package

* Tests for invalid package key

* Removing inter-test dependency
  • Loading branch information
jonathan-buttner committed Aug 10, 2020
1 parent 09a6c9d commit e26c754
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/dev/precommit_hook/casing_check_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export const IGNORE_FILE_GLOBS = [
'x-pack/plugins/apm/e2e/**/*',

'x-pack/plugins/maps/server/fonts/**/*',
// packages for the ingest manager's api integration tests could be valid semver which has dashes
'x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/**/*',
];

/**
Expand Down
5 changes: 3 additions & 2 deletions x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
getInstallationObject,
} from '../../services/epm/packages';
import { IngestManagerError, getHTTPResponseCode } from '../../errors';
import { splitPkgKey } from '../../services/epm/registry';

export const getCategoriesHandler: RequestHandler<
undefined,
Expand Down Expand Up @@ -131,7 +132,7 @@ export const getInfoHandler: RequestHandler<TypeOf<typeof GetInfoRequestSchema.p
const { pkgkey } = request.params;
const savedObjectsClient = context.core.savedObjects.client;
// TODO: change epm API to /packageName/version so we don't need to do this
const [pkgName, pkgVersion] = pkgkey.split('-');
const { pkgName, pkgVersion } = splitPkgKey(pkgkey);
const res = await getPackageInfo({ savedObjectsClient, pkgName, pkgVersion });
const body: GetInfoResponse = {
response: res,
Expand All @@ -155,7 +156,7 @@ export const installPackageHandler: RequestHandler<
const savedObjectsClient = context.core.savedObjects.client;
const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser;
const { pkgkey } = request.params;
const [pkgName, pkgVersion] = pkgkey.split('-');
const { pkgName, pkgVersion } = splitPkgKey(pkgkey);
try {
const res = await installPackage({
savedObjectsClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export async function installPackage({
force?: boolean;
}): Promise<AssetReference[]> {
// TODO: change epm API to /packageName/version so we don't need to do this
const [pkgName, pkgVersion] = pkgkey.split('-');
const { pkgName, pkgVersion } = Registry.splitPkgKey(pkgkey);
// TODO: calls to getInstallationObject, Registry.fetchInfo, and Registry.fetchFindLatestPackge
// and be replaced by getPackageInfo after adjusting for it to not group/use archive assets
const latestPackage = await Registry.fetchFindLatestPackage(pkgName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { getInstallation, savedObjectTypes } from './index';
import { deletePipeline } from '../elasticsearch/ingest_pipeline/';
import { installIndexPatterns } from '../kibana/index_pattern/install';
import { packageConfigService, appContextService } from '../..';
import { splitPkgKey } from '../registry';

export async function removeInstallation(options: {
savedObjectsClient: SavedObjectsClientContract;
Expand All @@ -21,7 +22,7 @@ export async function removeInstallation(options: {
}): Promise<AssetReference[]> {
const { savedObjectsClient, pkgkey, callCluster } = options;
// TODO: the epm api should change to /name/version so we don't need to do this
const [pkgName] = pkgkey.split('-');
const { pkgName } = splitPkgKey(pkgkey);
const installation = await getInstallation({ savedObjectsClient, pkgName });
if (!installation) throw Boom.badRequest(`${pkgName} is not installed`);
if (installation.removable === false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { AssetParts } from '../../../types';
import { pathParts } from './index';
import { pathParts, splitPkgKey } from './index';

const testPaths = [
{
Expand Down Expand Up @@ -48,3 +48,35 @@ test('testPathParts', () => {
expect(pathParts(value.path)).toStrictEqual(value.assetParts as AssetParts);
}
});

describe('splitPkgKey tests', () => {
it('throws an error if the delimiter is not found', () => {
expect(() => {
splitPkgKey('awesome_package');
}).toThrow();
});

it('throws an error if there is nothing before the delimiter', () => {
expect(() => {
splitPkgKey('-0.0.1-dev1');
}).toThrow();
});

it('throws an error if the version is not a semver', () => {
expect(() => {
splitPkgKey('awesome-laskdfj');
}).toThrow();
});

it('returns the name and version if the delimiter is found once', () => {
const { pkgName, pkgVersion } = splitPkgKey('awesome-0.1.0');
expect(pkgName).toBe('awesome');
expect(pkgVersion).toBe('0.1.0');
});

it('returns the name and version if the delimiter is found multiple times', () => {
const { pkgName, pkgVersion } = splitPkgKey('endpoint-0.13.0-alpha.1+abcd');
expect(pkgName).toBe('endpoint');
expect(pkgVersion).toBe('0.13.0-alpha.1+abcd');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import semver from 'semver';
import { Response } from 'node-fetch';
import { URL } from 'url';
import {
Expand Down Expand Up @@ -35,6 +36,26 @@ export interface CategoriesParams {
experimental?: boolean;
}

/**
* Extract the package name and package version from a string.
*
* @param pkgkey a string containing the package name delimited by the package version
*/
export function splitPkgKey(pkgkey: string): { pkgName: string; pkgVersion: string } {
// this will return an empty string if `indexOf` returns -1
const pkgName = pkgkey.substr(0, pkgkey.indexOf('-'));
if (pkgName === '') {
throw new Error('Package key parsing failed: package name was empty');
}

// this will return the entire string if `indexOf` return -1
const pkgVersion = pkgkey.substr(pkgkey.indexOf('-') + 1);
if (!semver.valid(pkgVersion)) {
throw new Error('Package key parsing failed: package version was not a valid semver');
}
return { pkgName, pkgVersion };
}

export const pkgToPkgKey = ({ name, version }: { name: string; version: string }) =>
`${name}-${version}`;

Expand Down
33 changes: 33 additions & 0 deletions x-pack/test/ingest_manager_api_integration/apis/epm/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
import { warnAndSkipTest } from '../../helpers';

export default function ({ getService }: FtrProviderContext) {
const log = getService('log');
const supertest = getService('supertest');
const dockerServers = getService('dockerServers');
const server = dockerServers.get('registry');

describe('EPM - get', () => {
it('returns a 500 for a package key without a proper name', async function () {
if (server.enabled) {
await supertest.get('/api/ingest_manager/epm/packages/-0.1.0').expect(500);
} else {
warnAndSkipTest(this, log);
}
});

it('returns a 500 for a package key without a proper semver version', async function () {
if (server.enabled) {
await supertest.get('/api/ingest_manager/epm/packages/endpoint-0.1.0.1.2.3').expect(500);
} else {
warnAndSkipTest(this, log);
}
});
});
}
2 changes: 2 additions & 0 deletions x-pack/test/ingest_manager_api_integration/apis/epm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
export default function loadTests({ loadTestFile }) {
describe('EPM Endpoints', () => {
loadTestFile(require.resolve('./list'));
loadTestFile(require.resolve('./get'));
loadTestFile(require.resolve('./file'));
//loadTestFile(require.resolve('./template'));
loadTestFile(require.resolve('./ilm'));
loadTestFile(require.resolve('./install_overrides'));
loadTestFile(require.resolve('./install_prerelease'));
loadTestFile(require.resolve('./install_remove_assets'));
loadTestFile(require.resolve('./install_update'));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ export default function ({ getService }: FtrProviderContext) {
const dockerServers = getService('dockerServers');
const log = getService('log');

const mappingsPackage = 'overrides-0.1.0';
const server = dockerServers.get('registry');

const deletePackage = async (pkgkey: string) => {
await supertest.delete(`/api/ingest_manager/epm/packages/${pkgkey}`).set('kbn-xsrf', 'xxxx');
};

const mappingsPackage = 'overrides-0.1.0';
const server = dockerServers.get('registry');

describe('installs packages that include settings and mappings overrides', async () => {
after(async () => {
if (server.enabled) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
import { warnAndSkipTest } from '../../helpers';

export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const dockerServers = getService('dockerServers');
const log = getService('log');

const testPackage = 'prerelease-0.1.0-dev.0+abc';
const server = dockerServers.get('registry');

const deletePackage = async (pkgkey: string) => {
await supertest.delete(`/api/ingest_manager/epm/packages/${pkgkey}`).set('kbn-xsrf', 'xxxx');
};

describe('installs package that has a prerelease version', async () => {
after(async () => {
if (server.enabled) {
// remove the package just in case it being installed will affect other tests
await deletePackage(testPackage);
}
});

it('should install the package correctly', async function () {
if (server.enabled) {
await supertest
.post(`/api/ingest_manager/epm/packages/${testPackage}`)
.set('kbn-xsrf', 'xxxx')
.expect(200);
} else {
warnAndSkipTest(this, log);
}
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
- name: data_stream.type
type: constant_keyword
description: >
Data stream type.
- name: data_stream.dataset
type: constant_keyword
description: >
Data stream dataset.
- name: data_stream.namespace
type: constant_keyword
description: >
Data stream namespace.
- name: '@timestamp'
type: date
description: >
Event timestamp.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
title: Test Dataset

type: logs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Test package

For testing a prerelease package
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
format_version: 1.0.0
name: prerelease
title: Prerelease package
description: This is a test package for testing that parsing a prerelease version works
version: 0.1.0-dev.0+abc
categories: ['security']
release: beta
type: integration
license: basic

requirement:
elasticsearch:
versions: '>7.7.0'
kibana:
versions: '>7.7.0'

icons:
- src: '/img/logo_prerelease_64_color.svg'
size: '16x16'
type: 'image/svg+xml'

0 comments on commit e26c754

Please sign in to comment.