Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] [Serverless] Integrates Cypress in visual mode with QA environment #171107

Merged
merged 23 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9b80e45
adding the script
MadameSheema Nov 13, 2023
9d12316
trying to delete the project when cypress is not executed anymore
MadameSheema Nov 13, 2023
0bdc4e7
clean up task working solution
MadameSheema Nov 13, 2023
9925a08
removes severlessQA tag from test
MadameSheema Nov 13, 2023
dabe482
updates tags section in readme
MadameSheema Nov 13, 2023
d45456a
adding the new scripts to readme
MadameSheema Nov 14, 2023
eb0de0f
removing log.info added for testing purposes
MadameSheema Nov 14, 2023
3ed0458
extending readme
MadameSheema Nov 14, 2023
a7366d3
adding more details to readme
MadameSheema Nov 14, 2023
ef368d1
Merge branch 'main' into open-qa-serverless
MadameSheema Nov 14, 2023
eb188b5
adding node option to avoid es_archiver to fail when using cypress in…
MadameSheema Nov 14, 2023
8ec88f7
parametrizes the script to force a specific tier or product line
MadameSheema Nov 14, 2023
57d1183
completing readme
MadameSheema Nov 14, 2023
7e2f4e0
little refactor
MadameSheema Nov 14, 2023
5a6e17d
last adjusments
MadameSheema Nov 15, 2023
2facb09
fixes
MadameSheema Nov 15, 2023
b48532d
refactor
MadameSheema Nov 15, 2023
57923b2
last adjustments
MadameSheema Nov 15, 2023
073d7ec
updates readme
MadameSheema Nov 15, 2023
b48924c
Update x-pack/plugins/security_solution/scripts/run_cypress/parallel_…
MadameSheema Nov 15, 2023
6fbfbae
addressing comments
MadameSheema Nov 15, 2023
7d0b6aa
Merge branch 'main' into open-qa-serverless
MadameSheema Nov 15, 2023
7156604
refactor
MadameSheema Nov 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import pRetry from 'p-retry';

import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants';
import { exec } from 'child_process';
import { renderSummaryTable } from './print_run';
import type { SecuritySolutionDescribeBlockFtrConfig } from './utils';
import { parseTestFileConfig, retrieveIntegrations } from './utils';

interface ProductType {
Expand Down Expand Up @@ -53,6 +53,12 @@ interface Credentials {
password: string;
}

const DEFAULT_CONFIGURATION: Readonly<ProductType[]> = [
{ product_line: 'security', product_tier: 'complete' },
{ product_line: 'cloud', product_tier: 'complete' },
{ product_line: 'endpoint', product_tier: 'complete' },
] as const;

const DEFAULT_REGION = 'aws-eu-west-1';
const PROJECT_NAME_PREFIX = 'kibana-cypress-security-solution-ephemeral';
const BASE_ENV_URL = 'https://global.qa.cld.elstc.co';
Expand Down Expand Up @@ -82,19 +88,14 @@ const getApiKeyFromElasticCloudJsonFile = (): string | undefined => {
async function createSecurityProject(
projectName: string,
apiKey: string,
ftrConfig: SecuritySolutionDescribeBlockFtrConfig
productTypes: ProductType[]
): Promise<Project | undefined> {
const body: CreateProjectRequestBody = {
name: projectName,
region_id: DEFAULT_REGION,
product_types: productTypes,
};

const productTypes: ProductType[] = [];
ftrConfig?.productTypes?.forEach((t) => {
productTypes.push(t as ProductType);
});
if (productTypes.length > 0) body.product_types = productTypes;

try {
const response = await axios.post(`${BASE_ENV_URL}/api/v1/serverless/projects/security`, body, {
headers: {
Expand Down Expand Up @@ -325,9 +326,32 @@ function waitForKibanaLogin(kbUrl: string, credentials: Credentials): Promise<vo
return pRetry(fetchLoginStatusAttempt, retryOptions);
}

const getProductTypes = (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getProductTypes sounds too generic and doesn't reflect its purpose. Something like getVisualModeProductTypes should work better.

tier: string,
endpointAddon: boolean,
cloudAddon: boolean
): ProductType[] => {
let productTypes: ProductType[] = [...DEFAULT_CONFIGURATION];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Instead of filtering out product types I'd recommend to add product types if a corresponding flag is set. It will make the implementation clearer.


if (tier) {
productTypes = productTypes.map((product) => ({
...product,
product_tier: tier,
}));
}
if (!cloudAddon) {
productTypes = productTypes.filter((product) => product.product_line !== 'cloud');
}
if (!endpointAddon) {
productTypes = productTypes.filter((product) => product.product_line !== 'endpoint');
}

return productTypes;
};

export const cli = () => {
run(
async () => {
async (context) => {
log = new ToolingLog({
level: 'info',
writeTo: process.stdout,
Expand Down Expand Up @@ -371,7 +395,22 @@ export const cli = () => {
}
return acc;
}, {} as Record<string, string | number>)
);
)
.option('tier', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: tier, endpointAddon and cloudAddon are only specific for running Cypress against QA env in visual mode but script consumer can specify these props in the other modes as well though it has no effect. Since yargs supports commands clearer solution would to split functionality in commands with unique params.

alias: 't',
type: 'string',
default: 'complete',
})
.option('endpointAddon', {
alias: 'ea',
type: 'boolean',
default: true,
})
.option('cloudAddon', {
alias: 'ca',
type: 'boolean',
default: true,
});

log.info(`
----------------------------------------------
Expand All @@ -388,6 +427,10 @@ ${JSON.stringify(argv, null, 2)}
const cypressConfigFilePath = require.resolve(`../../${argv.configFile}`) as string;
const cypressConfigFile = await import(cypressConfigFilePath);

const tier: string = argv.tier;
const endpointAddon: boolean = argv.endpointAddon;
const cloudAddon: boolean = argv.cloudAddon;

log.info(`
----------------------------------------------
Cypress config for file: ${cypressConfigFilePath}:
Expand Down Expand Up @@ -456,7 +499,10 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
await withProcRunner(log, async (procs) => {
const id = crypto.randomBytes(8).toString('hex');
const PROJECT_NAME = `${PROJECT_NAME_PREFIX}-${id}`;
const specFileFTRConfig = parseTestFileConfig(filePath);

const productTypes = isOpen
? getProductTypes(tier, endpointAddon, cloudAddon)
: (parseTestFileConfig(filePath).productTypes as ProductType[]);

if (!API_KEY) {
log.info('API KEY to create project could not be retrieved.');
Expand All @@ -466,14 +512,19 @@ ${JSON.stringify(cypressConfigFile, null, 2)}

log.info(`${id}: Creating project ${PROJECT_NAME}...`);
// Creating project for the test to run
const project = await createSecurityProject(PROJECT_NAME, API_KEY, specFileFTRConfig);
const project = await createSecurityProject(PROJECT_NAME, API_KEY, productTypes);

if (!project) {
log.info('Failed to create project.');
// eslint-disable-next-line no-process-exit
return process.exit(1);
}

context.addCleanupTask(() => {
const command = `curl -X DELETE ${BASE_ENV_URL}/api/v1/serverless/projects/security/${project.id} -H "Authorization: ApiKey ${API_KEY}"`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious. Why did you decide to run curl command instead of axios?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maximpn this addCleanupTask() didn't accept async, that was the reason behind if there is any other solution/way, more than happy to implement it :)

exec(command);
});

// Reset credentials for elastic user
const credentials = await resetCredentials(project.id, id, API_KEY);

Expand Down Expand Up @@ -553,15 +604,13 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
env: cyCustomEnv,
},
});
// Delete serverless project
log.info(`${id} : Deleting project ${PROJECT_NAME}...`);
await deleteSecurityProject(project.id, PROJECT_NAME, API_KEY);
} catch (error) {
result = error;
}
}

// Delete serverless project
log.info(`${id} : Deleting project ${PROJECT_NAME}...`);
await deleteSecurityProject(project.id, PROJECT_NAME, API_KEY);

return result;
});
return result;
Expand Down
104 changes: 99 additions & 5 deletions x-pack/test/security_solution_cypress/cypress/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,10 @@ Please, before opening a PR with the new test, please make sure that the test fa

Note that we use tags in order to select which tests we want to execute:

- `@serverless` includes a test in the Serverless test suite for PRs (the so-called first quality gate). You need to explicitly add this tag to any test you want to run in CI for open PRs. These tests will run against a local, "simulated" serverless environment.
- `@serverlessQA` includes a test in the Serverless test suite for QA (the so-called second quality gate). You need to explicitly add this tag to any test you want to run in the CD pipeline against real serverless projects deployed in the Serverless QA environment.
- **NOTE:** We are adding this tag temporarily until we check the behavior of our tests in the second quality gate.
- `@serverless` includes a test in the Serverless test suite for PRs (the so-called first quality gate) and QA environemnt (the so-called second quality gate). You need to explicitly add this tag to any test you want to run in CI for serverless.
- `@ess` includes a test in the normal, non-Serverless test suite. You need to explicitly add this tag to any test you want to run against a non-Serverless environment.
- `@brokenInServerless` excludes a test from the Serverless test suite (even if it's tagged as `@serverless`). Indicates that a test should run in Serverless, but currently is broken.
- `@brokenInServerlessQA` excludes a test form the Serverless QA enviornment (second quality gate). Indicates that a test should run on it, but currently is broken.
- `@skipInServerless` excludes a test from the Serverless test suite (even if it's tagged as `@serverless`). Could indicate many things, e.g. "the test is flaky in Serverless", "the test is Flaky in any type of environemnt", "the test has been temporarily excluded, see the comment above why".

Please, before opening a PR with a new test, make sure that the test fails. If you never see your test fail you don’t know if your test is actually testing the right thing, or testing anything at all.
Expand All @@ -72,6 +71,10 @@ Run the tests with the following yarn scripts from `x-pack/test/security_solutio
| cypress:explore:run:ess | Runs all tests tagged as ESS in the `e2e/explore` directory in headless mode |
| cypress:investigations:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e/investigations` directory in headless mode |
| cypress:explore:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e/explore` directory in headless mode |
| cypress:open:qa:serverless | Opens the Cypress UI with all tests in the `e2e` directory tagged as SERVERLESS. This also creates an MKI project in console.qa enviornment. The kibana instance will reload when you make code changes. This is the recommended way to debug tests in QA. Follow the readme in order to learn about the known limitations. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kibana -> Kibana

| cypress:run:qa:serverless | Runs all tests tagged as SERVERLESS placed in the `e2e` directory excluding `investigations` and `explore` directories in headless mode using the QA environment and real MKI projects.|
| cypress:run:qa:serverless:explore | Runs all tests tagged as SERVERLESS in the `e2e/explore` directory in headless mode using the QA environment and real MKI prorjects. |
| cypress:run:qa:serverless:investigations | Runs all tests tagged as SERVERLESS in the `e2e/investigations` directory in headless mode using the QA environment and reak MKI projects. |
| junit:merge | Merges individual test reports into a single report and moves the report to the `junit` directory |

Please note that all the headless mode commands do not open the Cypress UI and are typically used in CI/CD environments. The scripts that open the Cypress UI are useful for development and debugging.
Expand Down Expand Up @@ -190,7 +193,7 @@ Task [cypress/support/es_archiver.ts](https://github.com/elastic/kibana/blob/mai
Note that we use tags in order to select which tests we want to execute, if you want a test to be executed on serverless you need to add @serverless tag to it.


### Running the serverless tests locally
### Running serverless tests locally pointing to FTR serverless (First Quality Gate)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Running serverless tests locally pointing to FTR serverless (First Quality Gate)
### Running serverless tests locally against FTR Serverless (First Quality Gate)


Run the tests with the following yarn scripts from `x-pack/test/security_solution_cypress`:

Expand All @@ -203,7 +206,7 @@ Run the tests with the following yarn scripts from `x-pack/test/security_solutio

Please note that all the headless mode commands do not open the Cypress UI and are typically used in CI/CD environments. The scripts that open the Cypress UI are useful for development and debugging.

### PLIs
#### PLIs
When running serverless Cypress tests, the following PLIs are set by default:

```
Expand Down Expand Up @@ -234,6 +237,97 @@ Per the way we set the environment during the execution process on CI, the above

For test developing or test debugging purposes, you need to modify the configuration but without committing and pushing the changes in `x-pack/test/security_solution_cypress/serverless_config.ts`.


### Running serverless tests locally pointing to a MKI project created in QA environment (Second Quality Gate)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Running serverless tests locally pointing to a MKI project created in QA environment (Second Quality Gate)
### Running serverless tests locally against a MKI project created in QA environment (Second Quality Gate)


Run the tests with the following yarn scripts from `x-pack/test/security_solution_cypress`:

| Script Name | Description |
| ----------- | ----------- |
| cypress:open:qa:serverless | Opens the Cypress UI with all tests in the `e2e` directory tagged as SERVERLESS. This also creates an MKI project in console.qa enviornment. The kibana instance will reload when you make code changes. This is the recommended way to debug tests in QA. Follow the readme in order to learn about the known limitations. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kibana -> Kibana

| cypress:run:qa:serverless | Runs all tests tagged as SERVERLESS placed in the `e2e` directory excluding `investigations` and `explore` directories in headless mode using the QA environment and real MKI projects.|
| cypress:run:qa:serverless:explore | Runs all tests tagged as SERVERLESS in the `e2e/explore` directory in headless mode using the QA environment and real MKI prorjects. |
| cypress:run:qa:serverless:investigations | Runs all tests tagged as SERVERLESS in the `e2e/investigations` directory in headless mode using the QA environment and reak MKI projects. |

Please note that all the headless mode commands do not open the Cypress UI and are typically used in CI/CD environments. The scripts that open the Cypress UI are useful for development and debugging.


#### Setup required

Setup a valid Elastic Cloud API key for QA environment:

1. Navigate to QA environment.
2. Click on the `User menu button` located on the top right of the header.
3. Click on `Organization`.
4. Click on the `API keys` tab.
5. Click on `Create API key` button.
6. Add a name, set an expiration date, assign an organization owner role.
7. Click on `Create API key`
8. Save the value of the key

Store the saved key on `~/.elastic/cloud.json` using the following format:

```json
{
"api_key": {
"qa": "<API_KEY>"
}
}
```

#### Known limitations
- Currently RBAC cannot be tested.

#### PLIs

When running serverless Cypress tests on QA environment, the following PLIs are set by default:
```
{ product_line: 'security', product_tier: 'complete' },
{ product_line: 'endpoint', product_tier: 'complete' },
{ product_line: 'cloud', product_tier: 'complete' },
```

With the above configuration we'll be able to cover most of the scenarios, but there are some cases were we might want to use a different configuration. In that case, we just need to pass to the header of the test, which is the configuration we want for it.

```typescript
describe(
'Entity Analytics Dashboard in Serverless',
{
tags: '@serverless',
env: {
ftrConfig: {
productTypes: [
{ product_line: 'security', product_tier: 'essentials' },
{ product_line: 'endpoint', product_tier: 'essentials' },
],
},
},
},
```

For test developing or test debugging purposes on QA, you have avaialable the following options:

```
yarn cypress:open:qa:serverless --tier <essentials|complete>
```

The above command will open the Cypress UI with all tests in the `e2e` directory tagged as SERVERLESS. This also creates an MKI project in console.qa enviornment with the passed tier essentials or complete. If no flag is passed, the project will be cretaed as complete.

```
yarn cypress:open:qa:serverless --no-endpoint-addon
```

The above command will open the Cypress UI with all tests in the `e2e` directory tagged as SERVERLESS. This also creates an MKI project in console.qa enviornment without the endpoint add-on.

```
yarn cypress:open:qa:serverless --no-cloud-addon
```

The above command will open the Cypress UI with all tests in the `e2e` directory tagged as SERVERLESS. This also creates an MKI project in console.qa enviornment without the cloud add-on.

Note that all the above flags can be combined.


## Development Best Practices

Below you will a set of best practices that should be followed when writing Cypress tests.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export default defineCypressConfig({
specPattern: './cypress/e2e/**/*.cy.ts',
setupNodeEvents(on, config) {
esArchiver(on, config);
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
return config;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { OVERVIEW_URL } from '../../../urls/navigation';
import { createTimeline, favoriteTimeline } from '../../../tasks/api_calls/timelines';
import { getTimeline } from '../../../objects/timeline';

describe('Overview Page', { tags: ['@ess', '@serverless', '@serverlessQA'] }, () => {
describe('Overview Page', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
cy.task('esArchiverLoad', { archiveName: 'overview' });
});
Expand Down
1 change: 1 addition & 0 deletions x-pack/test/security_solution_cypress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"cypress:changed-specs-only:serverless": "yarn cypress:serverless --changed-specs-only --env burn=5",
"cypress:burn:serverless": "yarn cypress:serverless --env burn=2",
"cypress:qa:serverless": "TZ=UTC NODE_OPTIONS=--openssl-legacy-provider node ../../plugins/security_solution/scripts/start_cypress_parallel_serverless --config-file ../../test/security_solution_cypress/cypress/cypress_ci_serverless_qa.config.ts",
"cypress:open:qa:serverless": "yarn cypress:qa:serverless open",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about command naming. We have only one separate QA environment we run tests against. It turns out qa is a subset of serverless in some sense. I can imagine we have ESS QA environment in the future and this naming will have more sense.

Have you considered the other options like cypress:open:serverless-qa?

Naming in this file isn't consistent but we can improve it by sticking to one format something like cypress:mode:env[:suites name].

"cypress:run:qa:serverless": "yarn cypress:qa:serverless --spec './cypress/e2e/!(investigations|explore)/**/*.cy.ts'",
"cypress:run:qa:serverless:investigations": "yarn cypress:qa:serverless --spec './cypress/e2e/investigations/**/*.cy.ts'",
"cypress:run:qa:serverless:explore": "yarn cypress:qa:serverless --spec './cypress/e2e/explore/**/*.cy.ts'"
Expand Down
Loading