mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Add FTRs to the cloud_links
plugin (#150220)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Resolves https://github.com/elastic/kibana/issues/149206
This commit is contained in:
parent
7ee34928ad
commit
8eb89aaf19
7 changed files with 301 additions and 0 deletions
|
@ -243,6 +243,7 @@ enabled:
|
|||
- x-pack/test/functional/config_security_basic.ts
|
||||
- x-pack/test/functional/config.ccs.ts
|
||||
- x-pack/test/functional/config.firefox.js
|
||||
- x-pack/test/functional_cloud/config.ts
|
||||
- x-pack/test/kubernetes_security/basic/config.ts
|
||||
- x-pack/test/licensing_plugin/config.public.ts
|
||||
- x-pack/test/licensing_plugin/config.ts
|
||||
|
|
63
x-pack/test/functional_cloud/config.ts
Normal file
63
x-pack/test/functional_cloud/config.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 { resolve } from 'path';
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js'));
|
||||
|
||||
const kibanaPort = functionalConfig.get('servers.kibana.port');
|
||||
const idpPath = resolve(
|
||||
__dirname,
|
||||
'../security_api_integration/fixtures/saml/saml_provider/metadata.xml'
|
||||
);
|
||||
const samlIdPPlugin = resolve(
|
||||
__dirname,
|
||||
'../security_api_integration/fixtures/saml/saml_provider'
|
||||
);
|
||||
|
||||
return {
|
||||
...functionalConfig.getAll(),
|
||||
rootTags: ['skipCloud'],
|
||||
testFiles: [require.resolve('./tests')],
|
||||
security: { disableTestUser: true },
|
||||
junit: {
|
||||
reportName: 'Cloud Integrations Functional Tests',
|
||||
},
|
||||
esTestCluster: {
|
||||
...functionalConfig.get('esTestCluster'),
|
||||
serverArgs: [
|
||||
...functionalConfig.get('esTestCluster.serverArgs'),
|
||||
'xpack.security.authc.token.enabled=true',
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.order=0',
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.idp.metadata.path=${idpPath}`,
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.idp.entity_id=http://www.elastic.co/saml1',
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.entity_id=http://localhost:${kibanaPort}`,
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.logout=http://localhost:${kibanaPort}/logout`,
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.acs=http://localhost:${kibanaPort}/api/security/saml/callback`,
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.attributes.principal=urn:oid:0.0.7',
|
||||
],
|
||||
},
|
||||
kbnTestServer: {
|
||||
...functionalConfig.get('kbnTestServer'),
|
||||
serverArgs: [
|
||||
...functionalConfig.get('kbnTestServer.serverArgs'),
|
||||
`--plugin-path=${samlIdPPlugin}`,
|
||||
'--xpack.cloud.id=ftr_fake_cloud_id',
|
||||
'--xpack.cloud.base_url=https://cloud.elastic.co',
|
||||
'--xpack.cloud.deployment_url=/deployments/deploymentId',
|
||||
'--xpack.cloud.organization_url=/organization/organizationId',
|
||||
'--xpack.cloud.profile_url=/user/userId',
|
||||
'--xpack.security.authc.selector.enabled=false',
|
||||
`--xpack.security.authc.providers=${JSON.stringify({
|
||||
basic: { 'cloud-basic': { order: 1 } },
|
||||
saml: { 'cloud-saml-kibana': { order: 0, realm: 'cloud-saml-kibana' } },
|
||||
})}`,
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
13
x-pack/test/functional_cloud/ftr_provider_context.ts
Normal file
13
x-pack/test/functional_cloud/ftr_provider_context.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 { GenericFtrProviderContext } from '@kbn/test';
|
||||
import { pageObjects } from '../functional/page_objects';
|
||||
import { services } from './services';
|
||||
|
||||
export type FtrProviderContext = GenericFtrProviderContext<typeof services, typeof pageObjects>;
|
||||
export { pageObjects };
|
10
x-pack/test/functional_cloud/services.ts
Normal file
10
x-pack/test/functional_cloud/services.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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 { services as functionalServices } from '../functional/services';
|
||||
|
||||
export const services = functionalServices;
|
138
x-pack/test/functional_cloud/test_utils.ts
Normal file
138
x-pack/test/functional_cloud/test_utils.ts
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* 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 Fs from 'fs/promises';
|
||||
import Path from 'path';
|
||||
import { isEqualWith } from 'lodash';
|
||||
import type { Ecs, KibanaExecutionContext } from '@kbn/core/server';
|
||||
import type { RetryService } from '@kbn/ftr-common-functional-services';
|
||||
import { concatMap, defer, filter, firstValueFrom, ReplaySubject, scan, timeout } from 'rxjs';
|
||||
|
||||
export const logFilePath = Path.resolve(__dirname, './kibana.log');
|
||||
export const ANY = Symbol('any');
|
||||
|
||||
let logstream$: ReplaySubject<Ecs> | undefined;
|
||||
|
||||
export function getExecutionContextFromLogRecord(record: Ecs | undefined): KibanaExecutionContext {
|
||||
if (record?.log?.logger !== 'execution_context' || !record?.message) {
|
||||
throw new Error(`The record is not an entry of execution context`);
|
||||
}
|
||||
return JSON.parse(record.message);
|
||||
}
|
||||
|
||||
export function isExecutionContextLog(
|
||||
record: Ecs | undefined,
|
||||
executionContext: KibanaExecutionContext
|
||||
) {
|
||||
try {
|
||||
const object = getExecutionContextFromLogRecord(record);
|
||||
return isEqualWith(object, executionContext, function customizer(obj1: any, obj2: any) {
|
||||
if (obj2 === ANY) return true;
|
||||
});
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// to avoid splitting log record containing \n symbol
|
||||
const endOfLine = /(?<=})\s*\n/;
|
||||
export async function assertLogContains({
|
||||
description,
|
||||
predicate,
|
||||
retry,
|
||||
}: {
|
||||
description: string;
|
||||
predicate: (record: Ecs) => boolean;
|
||||
retry: RetryService;
|
||||
}): Promise<void> {
|
||||
// logs are written to disk asynchronously. I sacrificed performance to reduce flakiness.
|
||||
await retry.waitFor(description, async () => {
|
||||
if (!logstream$) {
|
||||
logstream$ = getLogstream$();
|
||||
}
|
||||
try {
|
||||
await firstValueFrom(logstream$.pipe(filter(predicate), timeout(5_000)));
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an observable that continuously tails the log file.
|
||||
*/
|
||||
function getLogstream$(): ReplaySubject<Ecs> {
|
||||
const stream$ = new ReplaySubject<Ecs>();
|
||||
|
||||
defer(async function* () {
|
||||
const fd = await Fs.open(logFilePath, 'rs');
|
||||
while (!stream$.isStopped) {
|
||||
const { bytesRead, buffer } = await fd.read();
|
||||
if (bytesRead) {
|
||||
yield buffer.toString('utf8', 0, bytesRead);
|
||||
}
|
||||
}
|
||||
await fd.close();
|
||||
})
|
||||
.pipe(
|
||||
scan<string, { buffer: string; records: Ecs[] }>(
|
||||
({ buffer }, chunk) => {
|
||||
const logString = buffer.concat(chunk);
|
||||
const lines = logString.split(endOfLine);
|
||||
const lastLine = lines.pop();
|
||||
const records = lines.map((s) => JSON.parse(s));
|
||||
|
||||
let leftover = '';
|
||||
if (lastLine) {
|
||||
try {
|
||||
const validRecord = JSON.parse(lastLine);
|
||||
records.push(validRecord);
|
||||
} catch (err) {
|
||||
leftover = lastLine;
|
||||
}
|
||||
}
|
||||
|
||||
return { buffer: leftover, records };
|
||||
},
|
||||
{
|
||||
records: [], // The ECS entries in the logs
|
||||
buffer: '', // Accumulated leftovers from the previous operation
|
||||
}
|
||||
),
|
||||
concatMap(({ records }) => records)
|
||||
)
|
||||
.subscribe(stream$);
|
||||
|
||||
// let the content start flowing
|
||||
stream$.subscribe();
|
||||
|
||||
return stream$;
|
||||
}
|
||||
|
||||
export function closeLogstream() {
|
||||
logstream$?.complete();
|
||||
logstream$ = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the log file to avoid tests looking at the logs from previous executions.
|
||||
*/
|
||||
export async function clearLogFile() {
|
||||
closeLogstream();
|
||||
await Fs.writeFile(logFilePath, '', 'utf8');
|
||||
await forceSyncLogFile();
|
||||
logstream$ = getLogstream$();
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the completion of all the pending I/O operations in the OS related to the log file.
|
||||
*/
|
||||
export async function forceSyncLogFile() {
|
||||
const fileDescriptor = await Fs.open(logFilePath);
|
||||
await fileDescriptor.datasync();
|
||||
await fileDescriptor.close();
|
||||
}
|
62
x-pack/test/functional_cloud/tests/cloud_links.ts
Normal file
62
x-pack/test/functional_cloud/tests/cloud_links.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const find = getService('find');
|
||||
const PageObjects = getPageObjects(['common', 'header']);
|
||||
|
||||
describe('Cloud Links integration', function () {
|
||||
before(async () => {
|
||||
// Create role mapping so user gets superuser access
|
||||
await getService('esSupertest')
|
||||
.post('/_security/role_mapping/cloud-saml-kibana')
|
||||
.send({
|
||||
roles: ['superuser'],
|
||||
enabled: true,
|
||||
rules: { field: { 'realm.name': 'cloud-saml-kibana' } },
|
||||
})
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await PageObjects.common.navigateToUrl('home');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
});
|
||||
|
||||
it('The button "Setup guides" is loaded', async () => {
|
||||
expect(await find.byCssSelector('[data-test-subj="guideButtonRedirect"]')).to.not.be(null);
|
||||
const cloudLink = await find.byLinkText('Setup guides');
|
||||
expect(cloudLink).to.not.be(null);
|
||||
});
|
||||
|
||||
it('"Manage this deployment" is appended to the nav list', async () => {
|
||||
await PageObjects.common.clickAndValidate('toggleNavButton', 'collapsibleNavCustomNavLink');
|
||||
const cloudLink = await find.byLinkText('Manage this deployment');
|
||||
expect(cloudLink).to.not.be(null);
|
||||
});
|
||||
|
||||
describe('Fills up the user menu items', () => {
|
||||
it('Shows the button Edit profile', async () => {
|
||||
await PageObjects.common.clickAndValidate('userMenuButton', 'userMenuLink__Edit profile');
|
||||
const cloudLink = await find.byLinkText('Edit profile');
|
||||
expect(cloudLink).to.not.be(null);
|
||||
});
|
||||
|
||||
it('Shows the button Account & Billing', async () => {
|
||||
await PageObjects.common.clickAndValidate(
|
||||
'userMenuButton',
|
||||
'userMenuLink__Account & Billing'
|
||||
);
|
||||
const cloudLink = await find.byLinkText('Account & Billing');
|
||||
expect(cloudLink).to.not.be(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
14
x-pack/test/functional_cloud/tests/index.ts
Normal file
14
x-pack/test/functional_cloud/tests/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Cloud Integrations', function () {
|
||||
loadTestFile(require.resolve('./cloud_links'));
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue