From ae152913a3d4cf5e281576a3d509419de7bfb35f Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 3 Nov 2020 15:50:01 -0700 Subject: [PATCH] [ci] run server integration tests (#81698) Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- packages/kbn-dev-utils/src/index.ts | 2 +- .../kbn-dev-utils/src/kbn_client/index.ts | 2 +- .../src/kbn_client/kbn_client.ts | 54 ++++++++++++------- .../src/kbn_client/kbn_client_requester.ts | 24 ++++----- packages/kbn-es-archiver/src/es_archiver.ts | 2 +- .../lib/config/schema.ts | 9 +--- .../services/kibana_server/kibana_server.ts | 8 ++- test/scripts/server_integration.sh | 5 ++ test/server_integration/http/ssl/config.js | 4 +- .../http/ssl_redirect/config.js | 20 +++---- .../http/ssl_redirect/index.js | 2 +- .../http/ssl_with_p12/config.js | 4 +- .../http/ssl_with_p12_intermediate/config.js | 4 +- test/server_integration/services/supertest.js | 8 +-- vars/tasks.groovy | 13 ++++- .../kbn_client_with_api_key_support.ts | 39 +++++++------- .../endpoint/resolver_generator_script.ts | 9 +++- .../scripts/endpoint/trusted_apps/index.ts | 5 +- x-pack/test/functional_embedded/config.ts | 7 +-- 19 files changed, 132 insertions(+), 89 deletions(-) create mode 100755 test/scripts/server_integration.sh diff --git a/packages/kbn-dev-utils/src/index.ts b/packages/kbn-dev-utils/src/index.ts index 6a845825f0fd4..38b12a96f17ef 100644 --- a/packages/kbn-dev-utils/src/index.ts +++ b/packages/kbn-dev-utils/src/index.ts @@ -34,7 +34,7 @@ export { KBN_P12_PATH, KBN_P12_PASSWORD, } from './certs'; -export { KbnClient } from './kbn_client'; +export * from './kbn_client'; export * from './run'; export * from './axios'; export * from './stdio'; diff --git a/packages/kbn-dev-utils/src/kbn_client/index.ts b/packages/kbn-dev-utils/src/kbn_client/index.ts index 72214b6c61746..47ef47143a6d8 100644 --- a/packages/kbn-dev-utils/src/kbn_client/index.ts +++ b/packages/kbn-dev-utils/src/kbn_client/index.ts @@ -17,5 +17,5 @@ * under the License. */ -export { KbnClient } from './kbn_client'; +export * from './kbn_client'; export { uriencode } from './kbn_client_requester'; diff --git a/packages/kbn-dev-utils/src/kbn_client/kbn_client.ts b/packages/kbn-dev-utils/src/kbn_client/kbn_client.ts index 7184727fc53de..6539f5b50b56b 100644 --- a/packages/kbn-dev-utils/src/kbn_client/kbn_client.ts +++ b/packages/kbn-dev-utils/src/kbn_client/kbn_client.ts @@ -18,37 +18,55 @@ */ import { ToolingLog } from '../tooling_log'; -import { KibanaConfig, KbnClientRequester, ReqOptions } from './kbn_client_requester'; +import { KbnClientRequester, ReqOptions } from './kbn_client_requester'; import { KbnClientStatus } from './kbn_client_status'; import { KbnClientPlugins } from './kbn_client_plugins'; import { KbnClientVersion } from './kbn_client_version'; import { KbnClientSavedObjects } from './kbn_client_saved_objects'; import { KbnClientUiSettings, UiSettingValues } from './kbn_client_ui_settings'; +export interface KbnClientOptions { + url: string; + certificateAuthorities?: Buffer[]; + log: ToolingLog; + uiSettingDefaults?: UiSettingValues; +} + export class KbnClient { - private readonly requester = new KbnClientRequester(this.log, this.kibanaConfig); - readonly status = new KbnClientStatus(this.requester); - readonly plugins = new KbnClientPlugins(this.status); - readonly version = new KbnClientVersion(this.status); - readonly savedObjects = new KbnClientSavedObjects(this.log, this.requester); - readonly uiSettings = new KbnClientUiSettings(this.log, this.requester, this.uiSettingDefaults); + readonly status: KbnClientStatus; + readonly plugins: KbnClientPlugins; + readonly version: KbnClientVersion; + readonly savedObjects: KbnClientSavedObjects; + readonly uiSettings: KbnClientUiSettings; + + private readonly requester: KbnClientRequester; + private readonly log: ToolingLog; + private readonly uiSettingDefaults?: UiSettingValues; /** * Basic Kibana server client that implements common behaviors for talking * to the Kibana server from dev tooling. - * - * @param log ToolingLog - * @param kibanaUrls Array of kibana server urls to send requests to - * @param uiSettingDefaults Map of uiSetting values that will be merged with all uiSetting resets */ - constructor( - private readonly log: ToolingLog, - private readonly kibanaConfig: KibanaConfig, - private readonly uiSettingDefaults?: UiSettingValues - ) { - if (!kibanaConfig.url) { - throw new Error('missing Kibana urls'); + constructor(options: KbnClientOptions) { + if (!options.url) { + throw new Error('missing Kibana url'); } + if (!options.log) { + throw new Error('missing ToolingLog'); + } + + this.log = options.log; + this.uiSettingDefaults = options.uiSettingDefaults; + + this.requester = new KbnClientRequester(this.log, { + url: options.url, + certificateAuthorities: options.certificateAuthorities, + }); + this.status = new KbnClientStatus(this.requester); + this.plugins = new KbnClientPlugins(this.status); + this.version = new KbnClientVersion(this.status); + this.savedObjects = new KbnClientSavedObjects(this.log, this.requester); + this.uiSettings = new KbnClientUiSettings(this.log, this.requester, this.uiSettingDefaults); } /** diff --git a/packages/kbn-dev-utils/src/kbn_client/kbn_client_requester.ts b/packages/kbn-dev-utils/src/kbn_client/kbn_client_requester.ts index 2aba2be56f277..dad0854e51b44 100644 --- a/packages/kbn-dev-utils/src/kbn_client/kbn_client_requester.ts +++ b/packages/kbn-dev-utils/src/kbn_client/kbn_client_requester.ts @@ -69,31 +69,27 @@ const delay = (ms: number) => setTimeout(resolve, ms); }); -export interface KibanaConfig { +interface Options { url: string; - ssl?: { - enabled: boolean; - key: string; - certificate: string; - certificateAuthorities: string; - }; + certificateAuthorities?: Buffer[]; } export class KbnClientRequester { + private readonly url: string; private readonly httpsAgent: Https.Agent | null; - constructor(private readonly log: ToolingLog, private readonly kibanaConfig: KibanaConfig) { + + constructor(private readonly log: ToolingLog, options: Options) { + this.url = options.url; this.httpsAgent = - kibanaConfig.ssl && kibanaConfig.ssl.enabled + Url.parse(options.url).protocol === 'https:' ? new Https.Agent({ - cert: kibanaConfig.ssl.certificate, - key: kibanaConfig.ssl.key, - ca: kibanaConfig.ssl.certificateAuthorities, + ca: options.certificateAuthorities, }) : null; } private pickUrl() { - return this.kibanaConfig.url; + return this.url; } public resolveUrl(relativeUrl: string = '/') { @@ -132,7 +128,7 @@ export class KbnClientRequester { errorMessage = `Conflict on GET (path=${options.path}, attempt=${attempt}/${maxAttempts})`; this.log.error(errorMessage); } else if (requestedRetries || failedToGetResponse) { - errorMessage = `[${description}] request failed (attempt=${attempt}/${maxAttempts})`; + errorMessage = `[${description}] request failed (attempt=${attempt}/${maxAttempts}): ${error.message}`; this.log.error(errorMessage); } else { throw error; diff --git a/packages/kbn-es-archiver/src/es_archiver.ts b/packages/kbn-es-archiver/src/es_archiver.ts index d61e7d2a422e8..c6f890b963e3d 100644 --- a/packages/kbn-es-archiver/src/es_archiver.ts +++ b/packages/kbn-es-archiver/src/es_archiver.ts @@ -49,7 +49,7 @@ export class EsArchiver { this.client = client; this.dataDir = dataDir; this.log = log; - this.kbnClient = new KbnClient(log, { url: kibanaUrl }); + this.kbnClient = new KbnClient({ log, url: kibanaUrl }); } /** diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 701171876ad2c..6ed114d62e244 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -38,14 +38,7 @@ const urlPartsSchema = () => password: Joi.string(), pathname: Joi.string().regex(/^\//, 'start with a /'), hash: Joi.string().regex(/^\//, 'start with a /'), - ssl: Joi.object() - .keys({ - enabled: Joi.boolean().default(false), - certificate: Joi.string().optional(), - certificateAuthorities: Joi.string().optional(), - key: Joi.string().optional(), - }) - .default(), + certificateAuthorities: Joi.array().items(Joi.binary()).optional(), }) .default(); diff --git a/test/common/services/kibana_server/kibana_server.ts b/test/common/services/kibana_server/kibana_server.ts index 4a251cca044d3..d808815f7a2c0 100644 --- a/test/common/services/kibana_server/kibana_server.ts +++ b/test/common/services/kibana_server/kibana_server.ts @@ -27,9 +27,13 @@ export function KibanaServerProvider({ getService }: FtrProviderContext) { const config = getService('config'); const lifecycle = getService('lifecycle'); const url = Url.format(config.get('servers.kibana')); - const ssl = config.get('servers.kibana').ssl; const defaults = config.get('uiSettings.defaults'); - const kbn = new KbnClient(log, { url, ssl }, defaults); + const kbn = new KbnClient({ + log, + url, + certificateAuthorities: config.get('servers.kibana.certificateAuthorities'), + uiSettingDefaults: defaults, + }); if (defaults) { lifecycle.beforeTests.add(async () => { diff --git a/test/scripts/server_integration.sh b/test/scripts/server_integration.sh new file mode 100755 index 0000000000000..82bc733e51b26 --- /dev/null +++ b/test/scripts/server_integration.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +source test/scripts/jenkins_test_setup_oss.sh + +yarn run grunt run:serverIntegrationTests diff --git a/test/server_integration/http/ssl/config.js b/test/server_integration/http/ssl/config.js index a7cbd0cce2570..db2451c6ac21c 100644 --- a/test/server_integration/http/ssl/config.js +++ b/test/server_integration/http/ssl/config.js @@ -23,13 +23,14 @@ import { createKibanaSupertestProvider } from '../../services'; export default async function ({ readConfigFile }) { const httpConfig = await readConfigFile(require.resolve('../../config')); + const certificateAuthorities = [readFileSync(CA_CERT_PATH)]; return { testFiles: [require.resolve('./')], services: { ...httpConfig.get('services'), supertest: createKibanaSupertestProvider({ - certificateAuthorities: [readFileSync(CA_CERT_PATH)], + certificateAuthorities, }), }, servers: { @@ -37,6 +38,7 @@ export default async function ({ readConfigFile }) { kibana: { ...httpConfig.get('servers.kibana'), protocol: 'https', + certificateAuthorities, }, }, junit: { diff --git a/test/server_integration/http/ssl_redirect/config.js b/test/server_integration/http/ssl_redirect/config.js index ab3bd2a19c005..392b0434b6284 100644 --- a/test/server_integration/http/ssl_redirect/config.js +++ b/test/server_integration/http/ssl_redirect/config.js @@ -17,6 +17,7 @@ * under the License. */ +import Url from 'url'; import { readFileSync } from 'fs'; import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; @@ -24,22 +25,22 @@ import { createKibanaSupertestProvider } from '../../services'; export default async function ({ readConfigFile }) { const httpConfig = await readConfigFile(require.resolve('../../config')); + const certificateAuthorities = [readFileSync(CA_CERT_PATH)]; - const redirectPort = httpConfig.get('servers.kibana.port') + 1; - const supertestOptions = { - ...httpConfig.get('servers.kibana'), - port: redirectPort, - // test with non ssl protocol - protocol: 'http', - }; + const redirectPort = httpConfig.get('servers.kibana.port') + 1234; return { testFiles: [require.resolve('./')], services: { ...httpConfig.get('services'), supertest: createKibanaSupertestProvider({ - certificateAuthorities: [readFileSync(CA_CERT_PATH)], - options: supertestOptions, + certificateAuthorities, + kibanaUrl: Url.format({ + ...httpConfig.get('servers.kibana'), + port: redirectPort, + // test with non ssl protocol + protocol: 'http', + }), }), }, servers: { @@ -48,6 +49,7 @@ export default async function ({ readConfigFile }) { ...httpConfig.get('servers.kibana'), // start the server with https protocol: 'https', + certificateAuthorities, }, }, junit: { diff --git a/test/server_integration/http/ssl_redirect/index.js b/test/server_integration/http/ssl_redirect/index.js index 3a7b0e310fb23..3893e02696926 100644 --- a/test/server_integration/http/ssl_redirect/index.js +++ b/test/server_integration/http/ssl_redirect/index.js @@ -28,7 +28,7 @@ export default function ({ getService }) { await supertest.get('/').expect('location', url).expect(302); - await supertest.get('/').redirects(1).expect('location', '/app/kibana').expect(302); + await supertest.get('/').redirects(1).expect('location', '/app/home').expect(302); }); }); } diff --git a/test/server_integration/http/ssl_with_p12/config.js b/test/server_integration/http/ssl_with_p12/config.js index 88c03302fb754..906c38538e501 100644 --- a/test/server_integration/http/ssl_with_p12/config.js +++ b/test/server_integration/http/ssl_with_p12/config.js @@ -23,13 +23,14 @@ import { createKibanaSupertestProvider } from '../../services'; export default async function ({ readConfigFile }) { const httpConfig = await readConfigFile(require.resolve('../../config')); + const certificateAuthorities = [readFileSync(CA_CERT_PATH)]; return { testFiles: [require.resolve('./')], services: { ...httpConfig.get('services'), supertest: createKibanaSupertestProvider({ - certificateAuthorities: [readFileSync(CA_CERT_PATH)], + certificateAuthorities, }), }, servers: { @@ -37,6 +38,7 @@ export default async function ({ readConfigFile }) { kibana: { ...httpConfig.get('servers.kibana'), protocol: 'https', + certificateAuthorities, }, }, junit: { diff --git a/test/server_integration/http/ssl_with_p12_intermediate/config.js b/test/server_integration/http/ssl_with_p12_intermediate/config.js index 24f8eefd1077e..5b031bdf8a0fe 100644 --- a/test/server_integration/http/ssl_with_p12_intermediate/config.js +++ b/test/server_integration/http/ssl_with_p12_intermediate/config.js @@ -23,13 +23,14 @@ import { createKibanaSupertestProvider } from '../../services'; export default async function ({ readConfigFile }) { const httpConfig = await readConfigFile(require.resolve('../../config')); + const certificateAuthorities = [readFileSync(CA1_CERT_PATH), readFileSync(CA2_CERT_PATH)]; return { testFiles: [require.resolve('./')], services: { ...httpConfig.get('services'), supertest: createKibanaSupertestProvider({ - certificateAuthorities: [readFileSync(CA1_CERT_PATH), readFileSync(CA2_CERT_PATH)], + certificateAuthorities, }), }, servers: { @@ -37,6 +38,7 @@ export default async function ({ readConfigFile }) { kibana: { ...httpConfig.get('servers.kibana'), protocol: 'https', + certificateAuthorities, }, }, junit: { diff --git a/test/server_integration/services/supertest.js b/test/server_integration/services/supertest.js index 9d38b83d4bcd1..f540a0e0755e6 100644 --- a/test/server_integration/services/supertest.js +++ b/test/server_integration/services/supertest.js @@ -21,14 +21,14 @@ import { format as formatUrl } from 'url'; import supertestAsPromised from 'supertest-as-promised'; -export function createKibanaSupertestProvider({ certificateAuthorities, options } = {}) { +export function createKibanaSupertestProvider({ certificateAuthorities, kibanaUrl } = {}) { return function ({ getService }) { const config = getService('config'); - const kibanaServerUrl = options ? formatUrl(options) : formatUrl(config.get('servers.kibana')); + kibanaUrl = kibanaUrl ?? formatUrl(config.get('servers.kibana')); return certificateAuthorities - ? supertestAsPromised.agent(kibanaServerUrl, { ca: certificateAuthorities }) - : supertestAsPromised(kibanaServerUrl); + ? supertestAsPromised.agent(kibanaUrl, { ca: certificateAuthorities }) + : supertestAsPromised(kibanaUrl); }; } diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 42b384ffa7bb1..de1dba13037f4 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -41,7 +41,14 @@ def test() { } def functionalOss(Map params = [:]) { - def config = params ?: [ciGroups: true, firefox: true, accessibility: true, pluginFunctional: true, visualRegression: false] + def config = params ?: [ + serverIntegration: true, + ciGroups: true, + firefox: true, + accessibility: true, + pluginFunctional: true, + visualRegression: false, + ] task { kibanaPipeline.buildOss(6) @@ -66,6 +73,10 @@ def functionalOss(Map params = [:]) { if (config.visualRegression) { task(kibanaPipeline.functionalTestProcess('oss-visualRegression', './test/scripts/jenkins_visual_regression.sh')) } + + if (config.serverIntegration) { + task(kibanaPipeline.scriptTaskDocker('serverIntegration', './test/scripts/server_integration.sh')) + } } } diff --git a/x-pack/plugins/security_solution/scripts/endpoint/kbn_client_with_api_key_support.ts b/x-pack/plugins/security_solution/scripts/endpoint/kbn_client_with_api_key_support.ts index 526c0eb4a9055..0016c3f26da39 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/kbn_client_with_api_key_support.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/kbn_client_with_api_key_support.ts @@ -4,29 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KbnClient, ToolingLog } from '@kbn/dev-utils'; -import { KibanaConfig } from '@kbn/dev-utils/target/kbn_client/kbn_client_requester'; -import fetch, { RequestInit as FetchRequestInit } from 'node-fetch'; +import { URL } from 'url'; + +import { KbnClient, KbnClientOptions } from '@kbn/dev-utils'; +import fetch, { RequestInit } from 'node-fetch'; export class KbnClientWithApiKeySupport extends KbnClient { - private kibanaUrlNoAuth: string; - constructor(log: ToolingLog, kibanaConfig: KibanaConfig) { - super(log, kibanaConfig); - const kibanaUrl = this.resolveUrl(kibanaConfig.url); - const matches = kibanaUrl.match(/(https?:\/\/)(.*\:.*\@)(.*)/); + private kibanaUrlNoAuth: URL; + + constructor(options: KbnClientOptions) { + super(options); + // strip auth from url - this.kibanaUrlNoAuth = - matches && matches.length >= 3 - ? matches[1] + matches[3].replace('/', '') - : kibanaUrl.replace('/', ''); + const url = new URL(this.resolveUrl('/')); + url.username = ''; + url.password = ''; + + this.kibanaUrlNoAuth = url; } + /** - * The fleet api to enroll and agent requires an api key when you mke the request, however KbnClient currently does not support sending an api key with the request. This function allows you to send an api key with a request. + * The fleet api to enroll and agent requires an api key when you make + * the request, however KbnClient currently does not support sending + * an api key with the request. This function allows you to send an + * api key with a request. */ - requestWithApiKey(path: string, init?: RequestInit | undefined): Promise { - return (fetch( - `${this.kibanaUrlNoAuth}${path}`, - init as FetchRequestInit - ) as unknown) as Promise; + requestWithApiKey(path: string, init?: RequestInit) { + return fetch(new URL(path, this.kibanaUrlNoAuth), init); } } diff --git a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts index 9fa2257afd411..b1088a6a956bf 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts @@ -203,7 +203,14 @@ async function main() { default: false, }, }).argv; - const kbnClient = new KbnClientWithApiKeySupport(new ToolingLog(), { url: argv.kibana }); + + const kbnClient = new KbnClientWithApiKeySupport({ + log: new ToolingLog({ + level: 'info', + writeTo: process.stdout, + }), + url: argv.kibana, + }); try { await doIngestSetup(kbnClient); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts index 03f0bf94a4264..14e18724f9552 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts @@ -34,7 +34,10 @@ ${separator}`); export const run: (options?: RunOptions) => Promise = async ({ count = 10, }: RunOptions = {}) => { - const kbnClient = new KbnClient(logger, { url: 'http://elastic:changeme@localhost:5601' }); + const kbnClient = new KbnClient({ + log: logger, + url: 'http://elastic:changeme@localhost:5601', + }); // touch the Trusted Apps List so it can be created await kbnClient.request({ diff --git a/x-pack/test/functional_embedded/config.ts b/x-pack/test/functional_embedded/config.ts index 95b290ece7db2..386d2570784a6 100644 --- a/x-pack/test/functional_embedded/config.ts +++ b/x-pack/test/functional_embedded/config.ts @@ -23,12 +23,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { kibana: { ...kibanaFunctionalConfig.get('servers.kibana'), protocol: 'https', - ssl: { - enabled: true, - key: Fs.readFileSync(KBN_KEY_PATH).toString('utf8'), - certificate: Fs.readFileSync(KBN_CERT_PATH).toString('utf8'), - certificateAuthorities: Fs.readFileSync(CA_CERT_PATH).toString('utf8'), - }, + certificateAuthorities: [Fs.readFileSync(CA_CERT_PATH)], }, };