Skip to content

Commit

Permalink
[7.x] Add functional test for Kibana embedded in iframe (elastic#68544)…
Browse files Browse the repository at this point in the history
… (elastic#69487)

* Add functional test for Kibana embedded in iframe (elastic#68544)

* convert kbn test config into TS

* add test  for Kibana embedded in iframe

* run embedded tests in functional suite

* ignore tls errors in functional tests by default

* switch test to https

* remove env vars mutation

* allow to pass ssl config to Kibana

* pass ssl config to axios

* adopt KbnClient interfaces

* adopt KibanaServer

* use KbnRequester in security service

* set sameSiteCookies:None in test

* acceptInsecureCerts in chrome

* remove leftovers

* fix type error

* remove unnecessary field

* address comments

* refactor plugin

* refactor test

* make acceptInsecureCerts configurable

* run firefox tests on ci

* up TS version

* fix firefox.sh script

* fix path

Co-authored-by: Elastic Machine <[email protected]>
# Conflicts:
#	test/functional/services/remote/remote.ts
#	test/functional/services/remote/webdriver.ts
#	x-pack/scripts/functional_tests.js

* create browser config. lost during conflict resolution
  • Loading branch information
mshustov authored Jun 18, 2020
1 parent 543319f commit c521487
Show file tree
Hide file tree
Showing 30 changed files with 394 additions and 113 deletions.
8 changes: 4 additions & 4 deletions packages/kbn-dev-utils/src/kbn_client/kbn_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
*/

import { ToolingLog } from '../tooling_log';
import { KbnClientRequester, ReqOptions } from './kbn_client_requester';
import { KibanaConfig, 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 class KbnClient {
private readonly requester = new KbnClientRequester(this.log, this.kibanaUrls);
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);
Expand All @@ -43,10 +43,10 @@ export class KbnClient {
*/
constructor(
private readonly log: ToolingLog,
private readonly kibanaUrls: string[],
private readonly kibanaConfig: KibanaConfig,
private readonly uiSettingDefaults?: UiSettingValues
) {
if (!kibanaUrls.length) {
if (!kibanaConfig.url) {
throw new Error('missing Kibana urls');
}
}
Expand Down
38 changes: 28 additions & 10 deletions packages/kbn-dev-utils/src/kbn_client/kbn_client_requester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/

import Url from 'url';

import Axios from 'axios';
import Https from 'https';
import Axios, { AxiosResponse } from 'axios';

import { isAxiosRequestError, isAxiosResponseError } from '../axios';
import { ToolingLog } from '../tooling_log';
Expand Down Expand Up @@ -70,20 +69,38 @@ const delay = (ms: number) =>
setTimeout(resolve, ms);
});

export interface KibanaConfig {
url: string;
ssl?: {
enabled: boolean;
key: string;
certificate: string;
certificateAuthorities: string;
};
}

export class KbnClientRequester {
constructor(private readonly log: ToolingLog, private readonly kibanaUrls: string[]) {}
private readonly httpsAgent: Https.Agent | null;
constructor(private readonly log: ToolingLog, private readonly kibanaConfig: KibanaConfig) {
this.httpsAgent =
kibanaConfig.ssl && kibanaConfig.ssl.enabled
? new Https.Agent({
cert: kibanaConfig.ssl.certificate,
key: kibanaConfig.ssl.key,
ca: kibanaConfig.ssl.certificateAuthorities,
})
: null;
}

private pickUrl() {
const url = this.kibanaUrls.shift()!;
this.kibanaUrls.push(url);
return url;
return this.kibanaConfig.url;
}

public resolveUrl(relativeUrl: string = '/') {
return Url.resolve(this.pickUrl(), relativeUrl);
}

async request<T>(options: ReqOptions): Promise<T> {
async request<T>(options: ReqOptions): Promise<AxiosResponse<T>> {
const url = Url.resolve(this.pickUrl(), options.path);
const description = options.description || `${options.method} ${url}`;
let attempt = 0;
Expand All @@ -93,17 +110,18 @@ export class KbnClientRequester {
attempt += 1;

try {
const response = await Axios.request<T>({
const response = await Axios.request({
method: options.method,
url,
data: options.body,
params: options.query,
headers: {
'kbn-xsrf': 'kbn-client',
},
httpsAgent: this.httpsAgent,
});

return response.data;
return response;
} catch (error) {
const conflictOnGet = isConcliftOnGetError(error);
const requestedRetries = options.retries !== undefined;
Expand Down
16 changes: 11 additions & 5 deletions packages/kbn-dev-utils/src/kbn_client/kbn_client_saved_objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,13 @@ export class KbnClientSavedObjects {
public async migrate() {
this.log.debug('Migrating saved objects');

return await this.requester.request<MigrateResponse>({
const { data } = await this.requester.request<MigrateResponse>({
description: 'migrate saved objects',
path: uriencode`/internal/saved_objects/_migrate`,
method: 'POST',
body: {},
});
return data;
}

/**
Expand All @@ -85,11 +86,12 @@ export class KbnClientSavedObjects {
public async get<Attributes extends Record<string, any>>(options: GetOptions) {
this.log.debug('Gettings saved object: %j', options);

return await this.requester.request<SavedObjectResponse<Attributes>>({
const { data } = await this.requester.request<SavedObjectResponse<Attributes>>({
description: 'get saved object',
path: uriencode`/api/saved_objects/${options.type}/${options.id}`,
method: 'GET',
});
return data;
}

/**
Expand All @@ -98,7 +100,7 @@ export class KbnClientSavedObjects {
public async create<Attributes extends Record<string, any>>(options: IndexOptions<Attributes>) {
this.log.debug('Creating saved object: %j', options);

return await this.requester.request<SavedObjectResponse<Attributes>>({
const { data } = await this.requester.request<SavedObjectResponse<Attributes>>({
description: 'update saved object',
path: options.id
? uriencode`/api/saved_objects/${options.type}/${options.id}`
Expand All @@ -113,6 +115,7 @@ export class KbnClientSavedObjects {
references: options.references,
},
});
return data;
}

/**
Expand All @@ -121,7 +124,7 @@ export class KbnClientSavedObjects {
public async update<Attributes extends Record<string, any>>(options: UpdateOptions<Attributes>) {
this.log.debug('Updating saved object: %j', options);

return await this.requester.request<SavedObjectResponse<Attributes>>({
const { data } = await this.requester.request<SavedObjectResponse<Attributes>>({
description: 'update saved object',
path: uriencode`/api/saved_objects/${options.type}/${options.id}`,
query: {
Expand All @@ -134,6 +137,7 @@ export class KbnClientSavedObjects {
references: options.references,
},
});
return data;
}

/**
Expand All @@ -142,10 +146,12 @@ export class KbnClientSavedObjects {
public async delete(options: GetOptions) {
this.log.debug('Deleting saved object %s/%s', options);

return await this.requester.request({
const { data } = await this.requester.request({
description: 'delete saved object',
path: uriencode`/api/saved_objects/${options.type}/${options.id}`,
method: 'DELETE',
});

return data;
}
}
3 changes: 2 additions & 1 deletion packages/kbn-dev-utils/src/kbn_client/kbn_client_status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ export class KbnClientStatus {
* Get the full server status
*/
async get() {
return await this.requester.request<ApiResponseStatus>({
const { data } = await this.requester.request<ApiResponseStatus>({
method: 'GET',
path: 'api/status',
});
return data;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ export class KbnClientUiSettings {
* Unset a uiSetting
*/
async unset(setting: string) {
return await this.requester.request<any>({
const { data } = await this.requester.request<any>({
path: uriencode`/api/kibana/settings/${setting}`,
method: 'DELETE',
});
return data;
}

/**
Expand Down Expand Up @@ -105,11 +106,11 @@ export class KbnClientUiSettings {
}

private async getAll() {
const resp = await this.requester.request<UiSettingsApiResponse>({
const { data } = await this.requester.request<UiSettingsApiResponse>({
path: '/api/kibana/settings',
method: 'GET',
});

return resp.settings;
return data.settings;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ 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(),
})
.default();

Expand Down Expand Up @@ -122,6 +130,7 @@ export const schema = Joi.object()
type: Joi.string().valid('chrome', 'firefox', 'ie', 'msedge').default('chrome'),

logPollingMs: Joi.number().default(100),
acceptInsecureCerts: Joi.boolean().default(false),
})
.default(),

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,34 @@
* specific language governing permissions and limitations
* under the License.
*/

import { kibanaTestUser } from './users';
import url from 'url';
import { kibanaTestUser } from './users';

interface UrlParts {
protocol?: string;
hostname?: string;
port?: number;
auth?: string;
username?: string;
password?: string;
}

export const kbnTestConfig = new (class KbnTestConfig {
getPort() {
return this.getUrlParts().port;
}

getUrlParts() {
getUrlParts(): UrlParts {
// allow setting one complete TEST_KIBANA_URL for ES like https://elastic:[email protected]:9200
if (process.env.TEST_KIBANA_URL) {
const testKibanaUrl = url.parse(process.env.TEST_KIBANA_URL);
return {
protocol: testKibanaUrl.protocol.slice(0, -1),
protocol: testKibanaUrl.protocol?.slice(0, -1),
hostname: testKibanaUrl.hostname,
port: parseInt(testKibanaUrl.port, 10),
port: testKibanaUrl.port ? parseInt(testKibanaUrl.port, 10) : undefined,
auth: testKibanaUrl.auth,
username: testKibanaUrl.auth.split(':')[0],
password: testKibanaUrl.auth.split(':')[1],
username: testKibanaUrl.auth?.split(':')[0],
password: testKibanaUrl.auth?.split(':')[1],
};
}

Expand All @@ -44,7 +52,7 @@ export const kbnTestConfig = new (class KbnTestConfig {
return {
protocol: process.env.TEST_KIBANA_PROTOCOL || 'http',
hostname: process.env.TEST_KIBANA_HOSTNAME || 'localhost',
port: parseInt(process.env.TEST_KIBANA_PORT, 10) || 5620,
port: process.env.TEST_KIBANA_PORT ? parseInt(process.env.TEST_KIBANA_PORT, 10) : 5620,
auth: `${username}:${password}`,
username,
password,
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion src/es_archiver/es_archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class EsArchiver {
this.client = client;
this.dataDir = dataDir;
this.log = log;
this.kbnClient = new KbnClient(log, [kibanaUrl]);
this.kbnClient = new KbnClient(log, { url: kibanaUrl });
}

/**
Expand Down
4 changes: 2 additions & 2 deletions test/common/services/kibana_server/kibana_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ 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], defaults);
const kbn = new KbnClient(log, { url, ssl }, defaults);

if (defaults) {
lifecycle.beforeTests.add(async () => {
Expand Down
28 changes: 12 additions & 16 deletions test/common/services/security/role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,20 @@
* under the License.
*/

import axios, { AxiosInstance } from 'axios';
import util from 'util';
import { ToolingLog } from '@kbn/dev-utils';
import { KbnClient, ToolingLog } from '@kbn/dev-utils';

export class Role {
private log: ToolingLog;
private axios: AxiosInstance;

constructor(url: string, log: ToolingLog) {
this.log = log;
this.axios = axios.create({
headers: { 'kbn-xsrf': 'x-pack/ftr/services/security/role' },
baseURL: url,
maxRedirects: 0,
validateStatus: () => true, // we do our own validation below and throw better error messages
});
}
constructor(private log: ToolingLog, private kibanaServer: KbnClient) {}

public async create(name: string, role: any) {
this.log.debug(`creating role ${name}`);
const { data, status, statusText } = await this.axios.put(`/api/security/role/${name}`, role);
const { data, status, statusText } = await this.kibanaServer.request({
path: `/api/security/role/${name}`,
method: 'PUT',
body: role,
retries: 0,
});
if (status !== 204) {
throw new Error(
`Expected status code of 204, received ${status} ${statusText}: ${util.inspect(data)}`
Expand All @@ -47,7 +40,10 @@ export class Role {

public async delete(name: string) {
this.log.debug(`deleting role ${name}`);
const { data, status, statusText } = await this.axios.delete(`/api/security/role/${name}`);
const { data, status, statusText } = await this.kibanaServer.request({
path: `/api/security/role/${name}`,
method: 'DELETE',
});
if (status !== 204 && status !== 404) {
throw new Error(
`Expected status code of 204 or 404, received ${status} ${statusText}: ${util.inspect(
Expand Down
Loading

0 comments on commit c521487

Please sign in to comment.