mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[EDR Workflows][MKI][Osquery] Osquery Cypress tests in MKI (#190675)
This PR introduces osquery Cypress tests to the MKI environment. These tests will be executed within the **Kibana / Serverless / Security Solution Quality Gate / Defend Workflows** scope. You can find an example of the build here: [Buildkite Example](https://buildkite.com/elastic/kibana-serverless-security-solution-quality-gate-defend-workflows/builds/1049). The main challenge with running osquery tests in MKI is that we cannot continuously log in users as we do in other environments. This is due to an issue where users are logged out when switching apps. To address this, I've added a simple check in the login method to prevent the login function from being called unnecessarily in MKI. This is a temporary solution until we can properly log in users via API calls using authentication tokens. All the `chalk` & `log.indent` changes were made to make logs more beautiful 😸  --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
69a842eab7
commit
7027b0b4b3
24 changed files with 317 additions and 139 deletions
|
@ -2,7 +2,7 @@ steps:
|
|||
- group: "Cypress MKI - Defend Workflows"
|
||||
key: cypress_test_defend_workflows
|
||||
steps:
|
||||
- label: "Running cypress:dw:qa:serverless:run"
|
||||
- label: "Cypress - DW - Running cypress:dw:qa:serverless:run"
|
||||
command: .buildkite/scripts/pipelines/security_solution_quality_gate/edr_workflows/mki_security_solution_defend_workflows.sh cypress:dw:qa:serverless:run
|
||||
key: test_defend_workflows
|
||||
agents:
|
||||
|
@ -14,7 +14,7 @@ steps:
|
|||
localSsdInterface: nvme
|
||||
machineType: n2-standard-4
|
||||
timeout_in_minutes: 300
|
||||
parallelism: 6
|
||||
parallelism: 5
|
||||
retry:
|
||||
automatic:
|
||||
- exit_status: "*"
|
||||
|
@ -91,7 +91,7 @@ steps:
|
|||
# - exit_status: "1"
|
||||
# limit: 1
|
||||
|
||||
- label: "Running edr_workflows:policy_response:qa:serverless"
|
||||
- label: "API - DW - Running edr_workflows:policy_response:qa:serverless"
|
||||
command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh edr_workflows:policy_response:qa:serverless
|
||||
key: edr_workflows:policy_response:qa:serverless
|
||||
agents:
|
||||
|
@ -108,7 +108,7 @@ steps:
|
|||
- exit_status: "1"
|
||||
limit: 1
|
||||
|
||||
- label: "Running edr_workflows:resolver:qa:serverless"
|
||||
- label: "API - DW - Running edr_workflows:resolver:qa:serverless"
|
||||
command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh edr_workflows:resolver:qa:serverless
|
||||
key: edr_workflows:resolver:qa:serverless
|
||||
agents:
|
||||
|
@ -125,7 +125,7 @@ steps:
|
|||
- exit_status: "1"
|
||||
limit: 1
|
||||
|
||||
- label: "Running edr_workflows:response_actions:qa:serverless"
|
||||
- label: "API - DW - Running edr_workflows:response_actions:qa:serverless"
|
||||
command: .buildkite/scripts/pipelines/security_solution_quality_gate/api_integration/api-integration-tests.sh edr_workflows:response_actions:qa:serverless
|
||||
key: edr_workflows:response_actions:qa:serverless
|
||||
agents:
|
||||
|
@ -141,3 +141,24 @@ steps:
|
|||
automatic:
|
||||
- exit_status: "1"
|
||||
limit: 1
|
||||
|
||||
- group: "Osquery MKI - Defend Workflows"
|
||||
key: cypress_test_osquery_defend_workflows
|
||||
steps:
|
||||
- label: "Osquery - Cypress - DW - Running cypress:qa:serverless:run"
|
||||
command: .buildkite/scripts/pipelines/security_solution_quality_gate/edr_workflows/mki_security_solution_defend_workflows_osquery.sh cypress:qa:serverless:run
|
||||
key: test_osquery_defend_workflows
|
||||
agents:
|
||||
image: family/kibana-ubuntu-2004
|
||||
imageProject: elastic-images-prod
|
||||
provider: gcp
|
||||
enableNestedVirtualization: true
|
||||
localSsds: 1
|
||||
localSsdInterface: nvme
|
||||
machineType: n2-standard-4
|
||||
timeout_in_minutes: 300
|
||||
parallelism: 3
|
||||
retry:
|
||||
automatic:
|
||||
- exit_status: "*"
|
||||
limit: 1
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "No target script from the package.json file, is supplied"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
source .buildkite/scripts/common/util.sh
|
||||
.buildkite/scripts/bootstrap.sh
|
||||
|
||||
export JOB=kibana-defend-workflows-osquery-serverless-cypress
|
||||
|
||||
buildkite-agent meta-data set "${BUILDKITE_JOB_ID}_is_test_execution_step" "true"
|
||||
|
||||
source .buildkite/scripts/pipelines/security_solution_quality_gate/prepare_vault_entries.sh
|
||||
|
||||
cd x-pack/plugins/osquery
|
||||
set +e
|
||||
|
||||
export BK_ANALYTICS_API_KEY=$(vault_get security-solution-quality-gate serverless-cypress-defend-workflows)
|
||||
|
||||
echo "--- Running the tests for target $1"
|
||||
BK_ANALYTICS_API_KEY=$BK_ANALYTICS_API_KEY yarn $1; status=$?; yarn junit:merge || :; exit $status
|
|
@ -7,60 +7,15 @@
|
|||
|
||||
import { defineCypressConfig } from '@kbn/cypress-config';
|
||||
|
||||
import path from 'path';
|
||||
import { safeLoad as loadYaml } from 'js-yaml';
|
||||
import { readFileSync } from 'fs';
|
||||
import { getCypressBaseConfig } from './cypress_base.config';
|
||||
|
||||
import type { YamlRoleDefinitions } from '@kbn/test-suites-serverless/shared/lib';
|
||||
import { setupUserDataLoader } from '@kbn/test-suites-serverless/functional/test_suites/security/cypress/support/setup_data_loader_tasks';
|
||||
import { getFailedSpecVideos } from './support/filter_videos';
|
||||
const ROLES_YAML_FILE_PATH = path.join(
|
||||
`${__dirname}/support`,
|
||||
'project_controller_osquery_roles.yml'
|
||||
);
|
||||
const roleDefinitions = loadYaml(readFileSync(ROLES_YAML_FILE_PATH, 'utf8')) as YamlRoleDefinitions;
|
||||
|
||||
export default defineCypressConfig({
|
||||
reporter: '../../../node_modules/cypress-multi-reporters',
|
||||
reporterOptions: {
|
||||
configFile: './cypress/reporter_config.json',
|
||||
},
|
||||
|
||||
defaultCommandTimeout: 60000,
|
||||
execTimeout: 120000,
|
||||
pageLoadTimeout: 12000,
|
||||
|
||||
retries: {
|
||||
runMode: 1,
|
||||
openMode: 0,
|
||||
},
|
||||
|
||||
screenshotsFolder: '../../../target/kibana-osquery/cypress/screenshots',
|
||||
trashAssetsBeforeRuns: false,
|
||||
video: true,
|
||||
videosFolder: '../../../target/kibana-osquery/cypress/videos',
|
||||
videoCompression: 15,
|
||||
viewportHeight: 900,
|
||||
viewportWidth: 1440,
|
||||
experimentalStudio: true,
|
||||
|
||||
env: {
|
||||
grepFilterSpecs: true,
|
||||
grepTags: '@ess',
|
||||
grepOmitFiltered: true,
|
||||
},
|
||||
|
||||
e2e: {
|
||||
specPattern: './cypress/e2e/**/*.cy.ts',
|
||||
baseUrl: 'http://localhost:5601',
|
||||
experimentalRunAllSpecs: true,
|
||||
experimentalMemoryManagement: true,
|
||||
numTestsKeptInMemory: 3,
|
||||
setupNodeEvents(on, config) {
|
||||
setupUserDataLoader(on, config, { roleDefinitions, additionalRoleName: 'viewer' });
|
||||
on('after:spec', getFailedSpecVideos);
|
||||
|
||||
return config;
|
||||
export default defineCypressConfig(
|
||||
getCypressBaseConfig({
|
||||
env: {
|
||||
grepTags: '@ess',
|
||||
},
|
||||
},
|
||||
});
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:5601',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
|
70
x-pack/plugins/osquery/cypress/cypress_base.config.ts
Normal file
70
x-pack/plugins/osquery/cypress/cypress_base.config.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 { merge } from 'lodash';
|
||||
import path from 'path';
|
||||
import { safeLoad as loadYaml } from 'js-yaml';
|
||||
import { readFileSync } from 'fs';
|
||||
import type { YamlRoleDefinitions } from '@kbn/test-suites-serverless/shared/lib';
|
||||
import { setupUserDataLoader } from '@kbn/test-suites-serverless/functional/test_suites/security/cypress/support/setup_data_loader_tasks';
|
||||
import { samlAuthentication } from '@kbn/security-solution-plugin/public/management/cypress/support/saml_authentication';
|
||||
import { getFailedSpecVideos } from './support/filter_videos';
|
||||
|
||||
const ROLES_YAML_FILE_PATH = path.join(
|
||||
`${__dirname}/support`,
|
||||
'project_controller_osquery_roles.yml'
|
||||
);
|
||||
const roleDefinitions = loadYaml(readFileSync(ROLES_YAML_FILE_PATH, 'utf8')) as YamlRoleDefinitions;
|
||||
|
||||
export const getCypressBaseConfig = (
|
||||
overrides: Cypress.ConfigOptions = {}
|
||||
): Cypress.ConfigOptions =>
|
||||
merge(
|
||||
{
|
||||
reporter: '../../../node_modules/cypress-multi-reporters',
|
||||
reporterOptions: {
|
||||
configFile: './cypress/reporter_config.json',
|
||||
},
|
||||
|
||||
defaultCommandTimeout: 60000,
|
||||
execTimeout: 120000,
|
||||
pageLoadTimeout: 12000,
|
||||
screenshotsFolder: '../../../target/kibana-osquery/cypress/screenshots',
|
||||
trashAssetsBeforeRuns: false,
|
||||
video: true,
|
||||
videosFolder: '../../../target/kibana-osquery/cypress/videos',
|
||||
|
||||
retries: {
|
||||
runMode: 1,
|
||||
openMode: 0,
|
||||
},
|
||||
videoCompression: 15,
|
||||
viewportHeight: 900,
|
||||
viewportWidth: 1440,
|
||||
experimentalStudio: true,
|
||||
|
||||
env: {
|
||||
grepFilterSpecs: true,
|
||||
grepOmitFiltered: true,
|
||||
},
|
||||
|
||||
e2e: {
|
||||
specPattern: './cypress/e2e/**/*.cy.ts',
|
||||
experimentalRunAllSpecs: true,
|
||||
experimentalMemoryManagement: true,
|
||||
numTestsKeptInMemory: 3,
|
||||
setupNodeEvents(on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) {
|
||||
setupUserDataLoader(on, config, { roleDefinitions, additionalRoleName: 'viewer' });
|
||||
samlAuthentication(on, config);
|
||||
on('after:spec', getFailedSpecVideos);
|
||||
|
||||
return config;
|
||||
},
|
||||
},
|
||||
},
|
||||
overrides
|
||||
);
|
|
@ -30,11 +30,9 @@ describe('Alert Event Details - Cases', { tags: ['@ess', '@serverless'] }, () =>
|
|||
let packId: string;
|
||||
let packName: string;
|
||||
const packData = packFixture();
|
||||
before(() => {
|
||||
initializeDataViews();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
initializeDataViews();
|
||||
loadPack(packData).then((data) => {
|
||||
packId = data.saved_object_id;
|
||||
packName = data.name;
|
||||
|
@ -89,13 +87,13 @@ describe('Alert Event Details - Cases', { tags: ['@ess', '@serverless'] }, () =>
|
|||
describe('Case', () => {
|
||||
let caseId: string;
|
||||
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
loadCase('securitySolution').then((data) => {
|
||||
caseId = data.id;
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
afterEach(() => {
|
||||
cleanupCase(caseId);
|
||||
});
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import { RESULTS_TABLE, RESULTS_TABLE_BUTTON } from '../../screens/live_query';
|
|||
describe(
|
||||
'Alert Event Details',
|
||||
{
|
||||
tags: ['@ess', '@serverless'],
|
||||
tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
|
||||
},
|
||||
() => {
|
||||
let ruleId: string;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ServerlessRoleName } from '../../support/roles';
|
||||
import { initializeDataViews } from '../../tasks/login';
|
||||
import {
|
||||
addLiveQueryToCase,
|
||||
|
@ -13,7 +14,6 @@ import {
|
|||
} from '../../tasks/live_query';
|
||||
import { navigateTo } from '../../tasks/navigation';
|
||||
import { loadLiveQuery, loadCase, cleanupCase } from '../../tasks/api_fixtures';
|
||||
import { ServerlessRoleName } from '../../support/roles';
|
||||
|
||||
describe('Add to Cases', () => {
|
||||
let liveQueryId: string;
|
||||
|
@ -38,7 +38,7 @@ describe('Add to Cases', () => {
|
|||
caseId = caseInfo.id;
|
||||
caseTitle = caseInfo.title;
|
||||
});
|
||||
cy.login(ServerlessRoleName.SOC_MANAGER);
|
||||
cy.login(ServerlessRoleName.SOC_MANAGER, false);
|
||||
navigateTo('/app/osquery');
|
||||
});
|
||||
|
||||
|
@ -70,7 +70,7 @@ describe('Add to Cases', () => {
|
|||
caseId = caseInfo.id;
|
||||
caseTitle = caseInfo.title;
|
||||
});
|
||||
cy.login(ServerlessRoleName.SOC_MANAGER);
|
||||
cy.login(ServerlessRoleName.SOC_MANAGER, false);
|
||||
navigateTo('/app/osquery');
|
||||
});
|
||||
|
||||
|
|
|
@ -17,15 +17,10 @@ import {
|
|||
typeInECSFieldInput,
|
||||
typeInOsqueryFieldInput,
|
||||
} from '../../tasks/live_query';
|
||||
import { ServerlessRoleName } from '../../support/roles';
|
||||
|
||||
describe('EcsMapping', { tags: ['@ess', '@serverless'] }, () => {
|
||||
before(() => {
|
||||
initializeDataViews();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login(ServerlessRoleName.SOC_MANAGER);
|
||||
initializeDataViews();
|
||||
});
|
||||
|
||||
it('should properly show static values in form and results', () => {
|
||||
|
|
|
@ -10,7 +10,7 @@ import { checkOsqueryResponseActionsPermissions } from '../../tasks/response_act
|
|||
describe(
|
||||
'App Features for Security Complete PLI',
|
||||
{
|
||||
tags: ['@serverless'],
|
||||
tags: ['@serverless', '@skipInServerlessMKI'],
|
||||
env: {
|
||||
ftrConfig: {
|
||||
productTypes: [{ product_line: 'security', product_tier: 'complete' }],
|
||||
|
|
|
@ -12,7 +12,12 @@ describe(
|
|||
{
|
||||
tags: ['@serverless'],
|
||||
env: {
|
||||
ftrConfig: { productTypes: [{ product_line: 'security', product_tier: 'essentials' }] },
|
||||
ftrConfig: {
|
||||
productTypes: [
|
||||
{ product_line: 'security', product_tier: 'essentials' },
|
||||
{ product_line: 'endpoint', product_tier: 'essentials' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
() => checkOsqueryResponseActionsPermissions(false)
|
||||
|
|
|
@ -6,43 +6,19 @@
|
|||
*/
|
||||
|
||||
import { defineCypressConfig } from '@kbn/cypress-config';
|
||||
import { setupUserDataLoader } from '@kbn/test-suites-serverless/functional/test_suites/security/cypress/support/setup_data_loader_tasks';
|
||||
import { getFailedSpecVideos } from './support/filter_videos';
|
||||
import { getCypressBaseConfig } from './cypress_base.config';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default defineCypressConfig({
|
||||
reporter: '../../../node_modules/cypress-multi-reporters',
|
||||
reporterOptions: {
|
||||
configFile: './cypress/reporter_config.json',
|
||||
},
|
||||
export default defineCypressConfig(
|
||||
getCypressBaseConfig({
|
||||
execTimeout: 60000,
|
||||
pageLoadTimeout: 60000,
|
||||
responseTimeout: 60000,
|
||||
viewportHeight: 946,
|
||||
viewportWidth: 1680,
|
||||
|
||||
defaultCommandTimeout: 60000,
|
||||
execTimeout: 60000,
|
||||
pageLoadTimeout: 60000,
|
||||
responseTimeout: 60000,
|
||||
screenshotsFolder: '../../../target/kibana-osquery/cypress/screenshots',
|
||||
trashAssetsBeforeRuns: false,
|
||||
video: true,
|
||||
videosFolder: '../../../target/kibana-osquery/cypress/videos',
|
||||
videoCompression: 15,
|
||||
viewportHeight: 946,
|
||||
viewportWidth: 1680,
|
||||
|
||||
env: {
|
||||
grepFilterSpecs: true,
|
||||
grepTags: '@serverless --@brokenInServerless',
|
||||
grepOmitFiltered: true,
|
||||
},
|
||||
e2e: {
|
||||
specPattern: './cypress/e2e/**/*.cy.ts',
|
||||
experimentalRunAllSpecs: true,
|
||||
experimentalMemoryManagement: true,
|
||||
numTestsKeptInMemory: 3,
|
||||
setupNodeEvents: (on, config) => {
|
||||
setupUserDataLoader(on, config, { additionalRoleName: 'viewer' });
|
||||
on('after:spec', getFailedSpecVideos);
|
||||
|
||||
return config;
|
||||
env: {
|
||||
grepTags: '@serverless --@brokenInServerless --@skipInServerless',
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 { defineCypressConfig } from '@kbn/cypress-config';
|
||||
import { getCypressBaseConfig } from './cypress_base.config';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default defineCypressConfig(
|
||||
getCypressBaseConfig({
|
||||
execTimeout: 60000,
|
||||
pageLoadTimeout: 60000,
|
||||
responseTimeout: 60000,
|
||||
viewportHeight: 946,
|
||||
viewportWidth: 1680,
|
||||
env: {
|
||||
grepTags: '@serverless --@skipInServerless --@brokenInServerless --@skipInServerlessMKI',
|
||||
},
|
||||
|
||||
e2e: {
|
||||
experimentalCspAllowList: ['default-src', 'script-src', 'script-src-elem'],
|
||||
},
|
||||
})
|
||||
);
|
|
@ -37,7 +37,7 @@ import { login } from '@kbn/security-solution-plugin/public/management/cypress/t
|
|||
import type { ServerlessRoleName } from './roles';
|
||||
|
||||
import { waitUntil } from '../tasks/wait_until';
|
||||
import { isServerless } from '../tasks/serverless';
|
||||
import { isCloudServerless, isServerless } from '../tasks/serverless';
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
|
@ -57,7 +57,7 @@ declare global {
|
|||
|
||||
clickOutside(): Chainable<JQuery<HTMLBodyElement>>;
|
||||
|
||||
login(role: ServerlessRoleName): void;
|
||||
login(role: ServerlessRoleName, useCookiesForMKI?: boolean): void;
|
||||
|
||||
waitUntil(fn: () => Cypress.Chainable): Cypress.Chainable | undefined;
|
||||
}
|
||||
|
@ -78,8 +78,15 @@ Cypress.Commands.add(
|
|||
() => cy.get('body').click(0, 0) // 0,0 here are the x and y coordinates
|
||||
);
|
||||
|
||||
Cypress.Commands.add('login', (role) => {
|
||||
if (isServerless) {
|
||||
Cypress.Commands.add('login', (role, useCookiesForMKI = true) => {
|
||||
// MKI does not support multiple logins throughout the test suite using cookies.
|
||||
// Until a better alternative is found, we will prevent multiple logins in the MKI environment in test suites.
|
||||
if (isCloudServerless && !useCookiesForMKI) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isServerless && !isCloudServerless) {
|
||||
// Do not use login.with in MKI env, default to login which will route to proper login method
|
||||
return login.with(role, 'changeme');
|
||||
}
|
||||
|
||||
|
|
|
@ -175,7 +175,7 @@ export const loadLiveQuery = (
|
|||
}).then((response) => response.body.data);
|
||||
|
||||
export const loadRule = (includeResponseActions = false) => {
|
||||
cy.login(ServerlessRoleName.SOC_MANAGER);
|
||||
cy.login(ServerlessRoleName.SOC_MANAGER, false);
|
||||
|
||||
return request<RuleResponse>({
|
||||
method: 'POST',
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
import { waitForAlertsToPopulate } from '@kbn/test-suites-xpack/security_solution_cypress/cypress/tasks/create_new_rule';
|
||||
import { disableNewFeaturesTours } from './navigation';
|
||||
import { getAdvancedButton } from '../screens/integrations';
|
||||
import { ServerlessRoleName } from '../support/roles';
|
||||
import { LIVE_QUERY_EDITOR, OSQUERY_FLYOUT_BODY_EDITOR } from '../screens/live_query';
|
||||
import { ServerlessRoleName } from '../support/roles';
|
||||
|
||||
export const DEFAULT_QUERY = 'select * from processes;';
|
||||
export const BIG_QUERY = 'select * from processes, users limit 110;';
|
||||
|
@ -118,7 +118,7 @@ export const toggleRuleOffAndOn = (ruleName: string) => {
|
|||
};
|
||||
|
||||
export const loadRuleAlerts = (ruleName: string) => {
|
||||
cy.login(ServerlessRoleName.SOC_MANAGER);
|
||||
cy.login(ServerlessRoleName.SOC_MANAGER, false);
|
||||
cy.visit('/app/security/rules', {
|
||||
onBeforeLoad: (win) => disableNewFeaturesTours(win),
|
||||
});
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
*/
|
||||
|
||||
export const isServerless = Cypress.env().IS_SERVERLESS;
|
||||
export const isCloudServerless = Cypress.env().CLOUD_SERVERLESS;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"**/*",
|
||||
"./cypress.config.ts",
|
||||
"./serverless_cypress.config.ts",
|
||||
"./serverless_cypress_qa.config.ts",
|
||||
"../../../test_serverless/shared/lib",
|
||||
],
|
||||
"exclude": [
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
"cypress:serverless": "NODE_OPTIONS=--openssl-legacy-provider node ../security_solution/scripts/start_cypress_parallel --config-file ../osquery/cypress/serverless_cypress.config.ts --ftr-config-file ../../../x-pack/test/osquery_cypress/serverless_cli_config",
|
||||
"cypress:serverless:open": "yarn cypress:serverless open",
|
||||
"cypress:serverless:run": "yarn cypress:serverless run",
|
||||
"cypress:qa:serverless": "NODE_OPTIONS=--openssl-legacy-provider node ../security_solution/scripts/start_cypress_parallel_serverless --config-file ../osquery/cypress/serverless_cypress_qa.config.ts --onBeforeHook ../../test/osquery_cypress/runner_qa.ts",
|
||||
"cypress:qa:serverless:run": "yarn cypress:qa:serverless run",
|
||||
"nyc": "../../../node_modules/.bin/nyc report --reporter=text-summary",
|
||||
"junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-osquery/cypress/results/mochawesome*.json > ../../../target/kibana-osquery/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-osquery/cypress/results/output.json --reportDir ../../../target/kibana-osquery/cypress/results && yarn junit:transform && mkdir -p ../../../target/junit && cp ../../../target/kibana-osquery/cypress/results/*.xml ../../../target/junit/",
|
||||
"junit:transform": "node ../security_solution/scripts/junit_transformer --pathPattern '../../../target/kibana-osquery/cypress/results/*.xml' --rootDirectory ../../../ --reportName 'Osquery Cypress' --writeInPlace",
|
||||
|
|
|
@ -28,7 +28,7 @@ import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants';
|
|||
import { catchAxiosErrorFormatAndThrow } from '../../common/endpoint/format_axios_error';
|
||||
import { createToolingLogger } from '../../common/endpoint/data_loaders/utils';
|
||||
import { renderSummaryTable } from './print_run';
|
||||
import { parseTestFileConfig, retrieveIntegrations } from './utils';
|
||||
import { getOnBeforeHook, parseTestFileConfig, retrieveIntegrations } from './utils';
|
||||
import { prefixedOutputLogger } from '../endpoint/common/utils';
|
||||
|
||||
import type { ProductType, Credentials, ProjectHandler } from './project_handler/project_handler';
|
||||
|
@ -327,6 +327,12 @@ export const cli = () => {
|
|||
alias: 'c',
|
||||
type: 'string',
|
||||
default: '',
|
||||
})
|
||||
.option('onBeforeHook', {
|
||||
// Execute a hook before running the tests with cypress.open/run
|
||||
alias: 'b',
|
||||
type: 'string',
|
||||
default: '',
|
||||
});
|
||||
|
||||
log.info(`
|
||||
|
@ -527,6 +533,15 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
|
|||
}
|
||||
process.env.TEST_CLOUD_HOST_NAME = new URL(BASE_ENV_URL).hostname;
|
||||
|
||||
// If provided, execute the onBeforeHook directly before running the tests once everything is set up
|
||||
if (argv.onBeforeHook) {
|
||||
const onBeforeFilePath = require.resolve(`../../${argv.onBeforeHook}`) as string;
|
||||
const module: unknown = await import(onBeforeFilePath);
|
||||
const onBeforeHook = getOnBeforeHook(module, onBeforeFilePath);
|
||||
|
||||
await onBeforeHook(cyCustomEnv);
|
||||
}
|
||||
|
||||
if (isOpen) {
|
||||
await cypress.open({
|
||||
configFile: cypressConfigFilePath,
|
||||
|
|
|
@ -11,6 +11,7 @@ import * as parser from '@babel/parser';
|
|||
import generate from '@babel/generator';
|
||||
import type { ExpressionStatement, ObjectExpression, ObjectProperty } from '@babel/types';
|
||||
import { schema, type TypeOf } from '@kbn/config-schema';
|
||||
import chalk from 'chalk';
|
||||
|
||||
/**
|
||||
* Retrieve test files using a glob pattern.
|
||||
|
@ -135,3 +136,23 @@ const TestFileFtrConfigSchema = schema.object(
|
|||
);
|
||||
|
||||
export type SecuritySolutionDescribeBlockFtrConfig = TypeOf<typeof TestFileFtrConfigSchema>;
|
||||
|
||||
export const getOnBeforeHook = (module: unknown, beforeSpecFilePath: string): Function => {
|
||||
if (typeof module !== 'object' || module === null) {
|
||||
throw new Error(
|
||||
`${chalk.bold(
|
||||
beforeSpecFilePath
|
||||
)} expected to explicitly export function member named "onBeforeHook"`
|
||||
);
|
||||
}
|
||||
|
||||
if (!('onBeforeHook' in module) || typeof module.onBeforeHook !== 'function') {
|
||||
throw new Error(
|
||||
`${chalk.bold('onBeforeHook')} exported from ${chalk.bold(
|
||||
beforeSpecFilePath
|
||||
)} is not a function`
|
||||
);
|
||||
}
|
||||
|
||||
return module.onBeforeHook;
|
||||
};
|
||||
|
|
|
@ -13,16 +13,17 @@ import {
|
|||
waitForHostToEnroll,
|
||||
} from '@kbn/security-solution-plugin/scripts/endpoint/common/fleet_services';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { getLatestVersion } from './artifact_manager';
|
||||
import { Manager } from './resource_manager';
|
||||
import { generateRandomString } from './utils';
|
||||
|
||||
export class AgentManager extends Manager {
|
||||
private log: ToolingLog;
|
||||
private policyEnrollmentKey: string;
|
||||
private fleetServerPort: string;
|
||||
private readonly log: ToolingLog;
|
||||
private readonly policyEnrollmentKey: string;
|
||||
private readonly fleetServerPort: string;
|
||||
private readonly kbnClient: KbnClient;
|
||||
private agentContainerId?: string;
|
||||
private kbnClient: KbnClient;
|
||||
|
||||
constructor(
|
||||
policyEnrollmentKey: string,
|
||||
|
@ -38,14 +39,15 @@ export class AgentManager extends Manager {
|
|||
}
|
||||
|
||||
public async setup() {
|
||||
this.log.info('Running agent preconfig');
|
||||
this.log.info(chalk.bold('Setting up Agent'));
|
||||
|
||||
const artifact = `docker.elastic.co/beats/elastic-agent:${await getLatestVersion()}`;
|
||||
this.log.info(artifact);
|
||||
this.log.indent(4, () => this.log.info(`Image: ${artifact}`));
|
||||
const containerName = generateRandomString(12);
|
||||
const fleetServerUrl =
|
||||
(await fetchFleetServerUrl(this.kbnClient)) ??
|
||||
`https://host.docker.internal:${this.fleetServerPort}`;
|
||||
this.log.indent(4, () => this.log.info(`Fleet Server: ${fleetServerUrl}`));
|
||||
|
||||
const dockerArgs = [
|
||||
'run',
|
||||
|
@ -72,24 +74,31 @@ export class AgentManager extends Manager {
|
|||
|
||||
const startedContainer = await execa('docker', dockerArgs);
|
||||
|
||||
this.log.info(`agent docker container started:\n${JSON.stringify(startedContainer, null, 2)}`);
|
||||
this.log.debug(`Agent docker container started:\n${JSON.stringify(startedContainer, null, 2)}`);
|
||||
|
||||
this.agentContainerId = startedContainer.stdout;
|
||||
this.log.indent(4, () =>
|
||||
this.log.info(`Agent container started with id ${this.agentContainerId}`)
|
||||
);
|
||||
await waitForHostToEnroll(this.kbnClient, this.log, containerName, 240000);
|
||||
}
|
||||
|
||||
public cleanup() {
|
||||
super.cleanup();
|
||||
this.log.info('Cleaning up the agent process');
|
||||
this.log.info(chalk.bold('Cleaning up the agent process'));
|
||||
if (this.agentContainerId) {
|
||||
this.log.info('Closing fleet process');
|
||||
this.log.indent(4, () =>
|
||||
this.log.info(`Stopping and removing agent [${this.agentContainerId}] container`)
|
||||
);
|
||||
|
||||
try {
|
||||
execa.sync('docker', ['kill', this.agentContainerId]);
|
||||
} catch (err) {
|
||||
this.log.error('Error closing fleet agent process');
|
||||
}
|
||||
this.log.info('Fleet agent process closed');
|
||||
this.log.indent(4, () =>
|
||||
this.log.info(`Stopped and removed agent [${this.agentContainerId}] container`)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
x-pack/test/osquery_cypress/runner_qa.ts
Normal file
28
x-pack/test/osquery_cypress/runner_qa.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 { prefixedOutputLogger } from '@kbn/security-solution-plugin/scripts/endpoint/common/utils';
|
||||
import { createToolingLogger } from '@kbn/security-solution-plugin/common/endpoint/data_loaders/utils';
|
||||
import { setupStackServicesUsingCypressConfig } from '@kbn/security-solution-plugin/public/management/cypress/support/common';
|
||||
import { maybeCreateDockerNetwork, verifyDockerInstalled } from '@kbn/es';
|
||||
import { AgentManager } from './agent';
|
||||
import { createAgentPolicy } from './utils';
|
||||
|
||||
export async function onBeforeHook(config: Record<string, string | number | boolean | undefined>) {
|
||||
const log = prefixedOutputLogger('cy.parallel(svl).onBeforeHook', createToolingLogger());
|
||||
const stackServicesPromise = setupStackServicesUsingCypressConfig({ env: config });
|
||||
const { kbnClient } = await stackServicesPromise;
|
||||
|
||||
await verifyDockerInstalled(log);
|
||||
await maybeCreateDockerNetwork(log);
|
||||
const policyEnrollmentKey = await createAgentPolicy(kbnClient, log, 'Default policy');
|
||||
const policyEnrollmentKeyTwo = await createAgentPolicy(kbnClient, log, 'Osquery policy');
|
||||
|
||||
// For MKI, we need to fetch the fleet server URL. Therefore, we pass '0000' as the fleet server port, which will be ignored.
|
||||
await new AgentManager(policyEnrollmentKey, '0000', log, kbnClient).setup();
|
||||
await new AgentManager(policyEnrollmentKeyTwo, '0000', log, kbnClient).setup();
|
||||
}
|
|
@ -15,6 +15,7 @@ import {
|
|||
CreateAgentPolicyResponse,
|
||||
} from '@kbn/fleet-plugin/common/types';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export const DEFAULT_HEADERS = Object.freeze({
|
||||
'x-elastic-internal-product': 'security-solution',
|
||||
|
@ -38,10 +39,10 @@ export const getInstalledIntegration = async (kbnClient: KbnClient, integrationN
|
|||
export const createAgentPolicy = async (
|
||||
kbnClient: KbnClient,
|
||||
log: ToolingLog,
|
||||
agentPolicyName = 'Osquery policy'
|
||||
agentPolicyName = 'Osquery policy',
|
||||
integrationName: string = 'osquery_manager'
|
||||
) => {
|
||||
log.info(`Creating "${agentPolicyName}" agent policy`);
|
||||
|
||||
log.info(chalk.bold(`Creating "${agentPolicyName}" agent policy`));
|
||||
const {
|
||||
data: {
|
||||
item: { id: agentPolicyId },
|
||||
|
@ -60,12 +61,26 @@ export const createAgentPolicy = async (
|
|||
inactivity_timeout: 1209600,
|
||||
},
|
||||
});
|
||||
log.indent(4, () => log.info(`Created "${agentPolicyName}" agent policy`));
|
||||
|
||||
log.info(`Adding integration to ${agentPolicyId}`);
|
||||
log.info(
|
||||
chalk.bold(
|
||||
`Adding "${integrationName}" integration to agent policy "${agentPolicyName}" with id ${agentPolicyId}`
|
||||
)
|
||||
);
|
||||
|
||||
await addIntegrationToAgentPolicy(kbnClient, agentPolicyId, agentPolicyName);
|
||||
await addIntegrationToAgentPolicy(kbnClient, agentPolicyId, agentPolicyName, integrationName);
|
||||
log.indent(4, () =>
|
||||
log.info(
|
||||
`Added "${integrationName}" integration to agent policy "${agentPolicyName}" with id ${agentPolicyId}`
|
||||
)
|
||||
);
|
||||
|
||||
log.info('Getting agent enrollment key');
|
||||
log.info(
|
||||
chalk.bold(
|
||||
`Getting agent enrollment key for agent policy "${agentPolicyName}" with id ${agentPolicyId}`
|
||||
)
|
||||
);
|
||||
const { data: apiKeys } = await kbnClient.request<GetEnrollmentAPIKeysResponse>({
|
||||
method: 'GET',
|
||||
headers: {
|
||||
|
@ -73,7 +88,11 @@ export const createAgentPolicy = async (
|
|||
},
|
||||
path: '/api/fleet/enrollment_api_keys',
|
||||
});
|
||||
|
||||
log.indent(4, () =>
|
||||
log.info(
|
||||
`Got agent enrollment key for agent policy "${agentPolicyName}" with id ${agentPolicyId}`
|
||||
)
|
||||
);
|
||||
return apiKeys.items[0].api_key;
|
||||
};
|
||||
|
||||
|
@ -119,7 +138,7 @@ const isValidArtifactVersion = (version: string) => !!version.match(/^\d+\.\d+\.
|
|||
/**
|
||||
* Returns the Agent version that is available for install (will check `artifacts-api.elastic.co/v1/versions`)
|
||||
* that is equal to or less than `maxVersion`.
|
||||
* @param maxVersion
|
||||
* @param kbnClient
|
||||
*/
|
||||
|
||||
export const getLatestAvailableAgentVersion = async (kbnClient: KbnClient): Promise<string> => {
|
||||
|
|
|
@ -177,6 +177,7 @@
|
|||
"@kbn/entities-schema",
|
||||
"@kbn/actions-simulators-plugin",
|
||||
"@kbn/cases-api-integration-test-plugin",
|
||||
"@kbn/security-solution-plugin/public/management/cypress",
|
||||
"@kbn/management-settings-ids",
|
||||
"@kbn/mock-idp-utils"
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue