mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Defend Workflows] Use Vagrant for real agent Cypress e2e (#160050)
## Summary Run Real Endpoint Cypress E2E on CI using Vagrant --------- Co-authored-by: Tomasz Ciecierski <ciecierskitomek@gmail.com> Co-authored-by: Ashokaditya <am.struktr@gmail.com>
This commit is contained in:
parent
bda0195982
commit
ba539d7a39
59 changed files with 799 additions and 623 deletions
|
@ -28,7 +28,6 @@ disabled:
|
|||
- x-pack/test/defend_workflows_cypress/cli_config.ts
|
||||
- x-pack/test/defend_workflows_cypress/config.ts
|
||||
- x-pack/test/defend_workflows_cypress/endpoint_config.ts
|
||||
- x-pack/test/defend_workflows_cypress/visual_config.ts
|
||||
- x-pack/plugins/observability_onboarding/e2e/ftr_config_open.ts
|
||||
- x-pack/plugins/observability_onboarding/e2e/ftr_config_runner.ts
|
||||
- x-pack/plugins/observability_onboarding/e2e/ftr_config.ts
|
||||
|
|
|
@ -14,3 +14,19 @@ steps:
|
|||
limit: 1
|
||||
artifact_paths:
|
||||
- "target/kibana-security-solution/**/*"
|
||||
|
||||
- command: .buildkite/scripts/steps/functional/defend_workflows_vagrant.sh
|
||||
label: 'Defend Workflows Endpoint Cypress Tests'
|
||||
agents:
|
||||
queue: n2-16-virt
|
||||
depends_on: build
|
||||
timeout_in_minutes: 120
|
||||
parallelism: 6
|
||||
retry:
|
||||
automatic:
|
||||
- exit_status: '-1'
|
||||
limit: 3
|
||||
- exit_status: '*'
|
||||
limit: 1
|
||||
artifact_paths:
|
||||
- "target/kibana-security-solution/**/*"
|
||||
|
|
4
.buildkite/scripts/steps/functional/common_cypress.sh
Normal file
4
.buildkite/scripts/steps/functional/common_cypress.sh
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
Xvfb -screen 0 1680x946x24 :99 &
|
||||
export DISPLAY=:99
|
|
@ -3,14 +3,11 @@
|
|||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/functional/common.sh
|
||||
source .buildkite/scripts/steps/functional/common_cypress.sh
|
||||
|
||||
export JOB=kibana-defend-workflows-cypress
|
||||
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
|
||||
|
||||
Xvfb -screen 0 1680x946x24 :99 &
|
||||
|
||||
export DISPLAY=:99
|
||||
|
||||
echo "--- Defend Workflows Cypress tests"
|
||||
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:dw:run
|
||||
|
|
14
.buildkite/scripts/steps/functional/defend_workflows_vagrant.sh
Executable file
14
.buildkite/scripts/steps/functional/defend_workflows_vagrant.sh
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/functional/common.sh
|
||||
source .buildkite/scripts/steps/functional/common_cypress.sh
|
||||
|
||||
export JOB=kibana-defend-workflows-endpoint-cypress
|
||||
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
|
||||
|
||||
|
||||
echo "--- Defend Workflows Endpoint Cypress tests"
|
||||
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:dw:endpoint:run
|
|
@ -3,14 +3,11 @@
|
|||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/common/util.sh
|
||||
source .buildkite/scripts/steps/functional/common_cypress.sh
|
||||
|
||||
.buildkite/scripts/bootstrap.sh
|
||||
node scripts/build_kibana_platform_plugins.js
|
||||
|
||||
Xvfb :99 -screen 0 1600x1200x24 &
|
||||
|
||||
export DISPLAY=:99
|
||||
|
||||
export JOB=kibana-osquery-cypress
|
||||
|
||||
echo "--- Osquery Cypress tests"
|
||||
|
|
|
@ -3,14 +3,11 @@
|
|||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/functional/common.sh
|
||||
source .buildkite/scripts/steps/functional/common_cypress.sh
|
||||
|
||||
export JOB=kibana-security-solution-chrome
|
||||
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
|
||||
|
||||
Xvfb -screen 0 1680x946x24 :99 &
|
||||
|
||||
export DISPLAY=:99
|
||||
|
||||
echo "--- Response Ops Cypress Tests on Security Solution"
|
||||
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:run:respops
|
||||
|
|
|
@ -3,14 +3,11 @@
|
|||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/functional/common.sh
|
||||
source .buildkite/scripts/steps/functional/common_cypress.sh
|
||||
|
||||
export JOB=kibana-security-solution-chrome
|
||||
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
|
||||
|
||||
Xvfb -screen 0 1680x946x24 :99 &
|
||||
|
||||
export DISPLAY=:99
|
||||
|
||||
echo "--- Response Ops Cases Cypress Tests on Security Solution"
|
||||
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:run:cases
|
||||
|
|
|
@ -3,14 +3,11 @@
|
|||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/functional/common.sh
|
||||
source .buildkite/scripts/steps/functional/common_cypress.sh
|
||||
|
||||
export JOB=kibana-security-solution-chrome
|
||||
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
|
||||
|
||||
Xvfb :99 -screen 0 1600x1200x24 &
|
||||
|
||||
export DISPLAY=:99
|
||||
|
||||
echo "--- Security Solution tests (Chrome)"
|
||||
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:run
|
||||
|
|
|
@ -3,15 +3,11 @@
|
|||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/functional/common.sh
|
||||
source .buildkite/scripts/steps/functional/common_cypress.sh
|
||||
|
||||
export JOB=kibana-security-solution-chrome
|
||||
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
|
||||
|
||||
|
||||
Xvfb :99 -screen 0 1600x1200x24 &
|
||||
|
||||
export DISPLAY=:99
|
||||
|
||||
echo "--- Explore Cypress Tests on Security Solution"
|
||||
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:explore:run
|
||||
|
|
|
@ -3,15 +3,11 @@
|
|||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/functional/common.sh
|
||||
source .buildkite/scripts/steps/functional/common_cypress.sh
|
||||
|
||||
export JOB=kibana-security-solution-chrome
|
||||
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
|
||||
|
||||
|
||||
Xvfb :99 -screen 0 1600x1200x24 &
|
||||
|
||||
export DISPLAY=:99
|
||||
|
||||
echo "--- Investigations Cypress Tests on Security Solution"
|
||||
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:investigations:run
|
||||
|
|
|
@ -3,14 +3,11 @@
|
|||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/functional/common.sh
|
||||
source .buildkite/scripts/steps/functional/common_cypress.sh
|
||||
|
||||
export JOB=kibana-threat-intelligence-chrome
|
||||
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
|
||||
|
||||
Xvfb :99 -screen 0 1600x1200x24 &
|
||||
|
||||
export DISPLAY=:99
|
||||
|
||||
echo "--- Threat Intelligence tests (Chrome)"
|
||||
|
||||
yarn --cwd x-pack/plugins/threat_intelligence cypress:run
|
||||
|
|
|
@ -44,6 +44,7 @@ export const IGNORE_FILE_GLOBS = [
|
|||
'packages/kbn-test/jest-preset.js',
|
||||
'packages/kbn-test/*/jest-preset.js',
|
||||
'test/package/Vagrantfile',
|
||||
'x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/Vagrantfile',
|
||||
'**/test/**/fixtures/**/*',
|
||||
|
||||
// Required to match the name in the docs.elastic.dev repo.
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source test/scripts/jenkins_test_setup_xpack.sh
|
||||
|
||||
echo " -> Running defend workflows cypress tests"
|
||||
cd "$XPACK_DIR"
|
||||
|
||||
node scripts/functional_tests \
|
||||
--debug --bail \
|
||||
--kibana-install-dir "$KIBANA_INSTALL_DIR" \
|
||||
--config test/defend_workflows_cypress/cli_config.ts
|
||||
|
||||
echo ""
|
||||
echo ""
|
|
@ -21,8 +21,8 @@
|
|||
"cypress:run:upgrade": "yarn cypress:run:reporter --browser chrome --config specPattern=./cypress/upgrade_e2e/**/*.cy.ts",
|
||||
"cypress:dw:open": "node ./scripts/start_cypress_parallel open --config-file ./public/management/cypress.config.ts ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/cli_config",
|
||||
"cypress:dw:run": "node ./scripts/start_cypress_parallel run --config-file ./public/management/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/cli_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json; status=$?; yarn junit:merge && exit $status",
|
||||
"cypress:dw:endpoint:open": "yarn cypress open --config-file ./public/management/cypress_endpoint.config.ts",
|
||||
"cypress:dw:endpoint:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/defend_workflows_cypress/endpoint_config.ts",
|
||||
"cypress:dw:endpoint:run": "node ./scripts/start_cypress_parallel run --config-file ./public/management/cypress_endpoint.config.ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/endpoint_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json --concurrency 1; status=$?; yarn junit:merge && exit $status",
|
||||
"cypress:dw:endpoint:open": "node ./scripts/start_cypress_parallel open --config-file ./public/management/cypress_endpoint.config.ts ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/endpoint_config",
|
||||
"cypress:investigations:run": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/investigations/**/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status",
|
||||
"cypress:explore:run": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/explore/**/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status",
|
||||
"junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results && mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/",
|
||||
|
|
|
@ -1,14 +1,43 @@
|
|||
# Cypress Tests
|
||||
|
||||
The `management/cypress` directory contains functional UI tests that execute using [Cypress](https://www.cypress.io/).
|
||||
The `plugins/security_solution/public/management/cypress` directory contains functional UI tests that execute
|
||||
using [Cypress](https://www.cypress.io/).
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
Good to have before you run the tests:
|
||||
|
||||
- Docker CLI, as Docker desktop doesn't install it by default. Install using `brew`:
|
||||
|
||||
```shell
|
||||
brew install docker
|
||||
```
|
||||
|
||||
- [Multipass](https://multipass.run/) for running the tests against real endpoint. Install using `brew`:
|
||||
|
||||
```shell
|
||||
brew install multipass
|
||||
```
|
||||
|
||||
If you also want to run the tests against real endpoints as on the CI pipeline, then you need to have the following:
|
||||
|
||||
- [Vagrant](https://developer.hashicorp.com/vagrant/docs/installation)
|
||||
- [Vagrant provider for VMware](https://developer.hashicorp.com/vagrant/docs/providers/vmware/installation)
|
||||
- [Vagrant VMware Utility](https://developer.hashicorp.com/vagrant/docs/providers/vmware/vagrant-vmware-utility)
|
||||
- [VMware Fusion](https://www.vmware.com/products/fusion/fusion-evaluation.html)
|
||||
|
||||
See [running interactive tests on real endpoint with vagrant](#cypress-interactive-with-real-endpoints-using-vagrant)
|
||||
for more information.
|
||||
|
||||
## Running the tests
|
||||
|
||||
There are currently three ways to run the tests, comprised of two execution modes and two target environments, which will be detailed below.
|
||||
There are currently three ways to run the tests, comprised of two execution modes and two target environments, which
|
||||
will be detailed below.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
The test suites are setup with defaults for Kibana and Elasticsearch. The following environment variables can be set to changes those defaults and target a run against different instances of the stack:
|
||||
The test suites are set up with defaults for Kibana and Elasticsearch. The following environment variables can be set to
|
||||
changes those defaults and target a run against different instances of the stack:
|
||||
|
||||
```
|
||||
CYPRESS_KIBANA_URL
|
||||
|
@ -27,68 +56,143 @@ Some notes:
|
|||
Example:
|
||||
|
||||
```shell
|
||||
cd /x-pack/plugins/security_solution
|
||||
yarn --cwd x-pack/plugins/security_solution
|
||||
CYPRESS_BASE_URL=http://localhost:5601 \
|
||||
CYPRESS_KIBANA_URL=http://localhost:5601 \
|
||||
CYPRESS_ELASTICSEARCH_USERNAME=elastic \
|
||||
CYPRESS_ELASTICSEARCH_PASSWORD=changeme \
|
||||
CYPRESS_ELASTICSEARCH_URL=http://localhost:9200 yarn cypress:dw:open
|
||||
CYPRESS_ELASTICSEARCH_URL=http://localhost:9200 cypress:dw:open
|
||||
```
|
||||
|
||||
|
||||
### Execution modes
|
||||
|
||||
#### Interactive mode
|
||||
|
||||
When you run Cypress in interactive mode, an interactive runner is displayed that allows you to see commands as they execute while also viewing the application under test. For more information, please see [cypress documentation](https://docs.cypress.io/guides/core-concepts/test-runner.html#Overview).
|
||||
When you run Cypress in interactive mode, an interactive runner is displayed that allows you to see commands as they
|
||||
execute while also viewing the application under test. For more information, please
|
||||
see [cypress documentation](https://docs.cypress.io/guides/core-concepts/test-runner.html#Overview).
|
||||
|
||||
#### Headless mode
|
||||
|
||||
A headless browser is a browser simulation program that does not have a user interface. These programs operate like any other browser but do not display any UI. This is why meanwhile you are executing the tests on this mode you are not going to see the application under test. Just the output of the test is displayed on the terminal once the execution is finished.
|
||||
A headless browser is a browser simulation program that does not have a user interface. These programs operate like any
|
||||
other browser but do not display any UI. So when you are executing the tests on this mode you are not
|
||||
going to see the application under test. Just the output of the test is displayed on the terminal once the execution is
|
||||
finished.
|
||||
|
||||
### Target environments
|
||||
|
||||
#### FTR (CI)
|
||||
|
||||
This is the configuration used by CI. It uses the FTR to spawn both a Kibana instance (http://localhost:5620) and an Elasticsearch instance (http://localhost:9220) with a preloaded minimum set of data (see preceding "Test data" section), and then executes cypress against this stack. You can find this configuration in `x-pack/test/defend_workflows_cypress`
|
||||
This is the configuration used by CI. It uses the FTR to spawn both a Kibana instance (http://localhost:5620) and an
|
||||
Elasticsearch instance (http://localhost:9220) with a preloaded minimum set of data (see preceding "Test data" section),
|
||||
and then executes cypress against this stack. You can find this configuration in `x-pack/test/defend_workflows_cypress`
|
||||
|
||||
### Test Execution: Examples
|
||||
|
||||
#### FTR + Headless (Chrome)
|
||||
#### Cypress + Headless (Chrome)
|
||||
|
||||
Since this is how tests are run on CI, this will likely be the configuration you want to reproduce failures locally, etc.
|
||||
Since this is how tests are run on CI, this will likely be the configuration you want to use in order to reproduce
|
||||
failures locally, etc.
|
||||
|
||||
```shell
|
||||
# bootstrap kibana from the project root
|
||||
yarn kbn bootstrap
|
||||
|
||||
# build the plugins/assets that cypress will execute against
|
||||
node scripts/build_kibana_platform_plugins
|
||||
# bootstrap kibana from the project root and build the plugins/assets that cypress will execute against
|
||||
yarn kbn bootstrap && node scripts/build_kibana_platform_plugins
|
||||
|
||||
# launch the cypress test runner
|
||||
cd x-pack/plugins/security_solution
|
||||
yarn cypress:dw:run-as-ci
|
||||
```
|
||||
#### FTR + Interactive
|
||||
|
||||
This is the preferred mode for developing new tests.
|
||||
# or
|
||||
|
||||
# launch without changing directory from kibana/
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:dw:run-as-ci
|
||||
```
|
||||
|
||||
#### Cypress
|
||||
|
||||
This is the preferred mode for developing new tests against mocked data
|
||||
|
||||
```shell
|
||||
# bootstrap kibana from the project root
|
||||
yarn kbn bootstrap
|
||||
|
||||
# build the plugins/assets that cypress will execute against
|
||||
node scripts/build_kibana_platform_plugins
|
||||
# bootstrap kibana from the project root and build the plugins/assets that cypress will execute against
|
||||
yarn kbn bootstrap && node scripts/build_kibana_platform_plugins
|
||||
|
||||
# launch the cypress test runner
|
||||
cd x-pack/plugins/security_solution
|
||||
yarn cypress:dw:open-as-ci
|
||||
yarn cypress:dw:open
|
||||
|
||||
# or
|
||||
|
||||
# launch without changing directory from kibana/
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:dw:open
|
||||
```
|
||||
|
||||
For developing/debugging tests against real endpoint please use:
|
||||
|
||||
Endpoint tests require [Multipass](https://multipass.run/) to be installed on your machine.
|
||||
|
||||
```shell
|
||||
# bootstrap kibana from the project root and build the plugins/assets that cypress will execute against
|
||||
yarn kbn bootstrap && node scripts/build_kibana_platform_plugins
|
||||
|
||||
# launch the cypress test runner with real endpoint
|
||||
cd x-pack/plugins/security_solution
|
||||
yarn cypress:dw:endpoint:open
|
||||
|
||||
# or
|
||||
|
||||
# launch without changing directory from kibana/
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:dw:endpoint:open
|
||||
```
|
||||
|
||||
#### Cypress (interactive) with real Endpoints using Vagrant
|
||||
|
||||
```shell
|
||||
# bootstrap kibana from the project root and build the plugins/assets that cypress will execute against
|
||||
yarn kbn bootstrap && node scripts/build_kibana_platform_plugins
|
||||
|
||||
# launch the cypress test runner with real endpoint
|
||||
cd x-pack/plugins/security_solution
|
||||
export CI=true
|
||||
yarn cypress:dw:endpoint:open
|
||||
````
|
||||
|
||||
Note that you can select the browser you want to use on the top right side of the interactive runner.
|
||||
|
||||
#### Cypress against REAL Endpoint + Headless (Chrome)
|
||||
|
||||
This requires some additional setup as mentioned in the [pre-requisites](#pre-requisites) section.
|
||||
|
||||
Endpoint tests require [Multipass](https://multipass.run/) to be installed on your machine.
|
||||
|
||||
```shell
|
||||
# bootstrap kibana from the project root and build the plugins/assets that cypress will execute against
|
||||
yarn kbn bootstrap && node scripts/build_kibana_platform_plugins
|
||||
|
||||
# launch the cypress test runner with real endpoint
|
||||
cd x-pack/plugins/security_solution
|
||||
yarn cypress:dw:endpoint:run
|
||||
|
||||
# or
|
||||
|
||||
# launch without changing directory from kibana/
|
||||
yarn --cwd x-pack/plugins/security_solution cypress:dw:endpoint:run
|
||||
```
|
||||
|
||||
## Folder Structure
|
||||
|
||||
### e2e/
|
||||
|
||||
Contains all the tests. Within it are two sub-folders:
|
||||
|
||||
#### cypress/endpoint
|
||||
|
||||
Contains all the tests that are executed against real endpoints.
|
||||
|
||||
#### cypress/mocked_data
|
||||
|
||||
Contains all the tests that are executed against mocked endpoint and run on CI. If you want to add tests that run on CI
|
||||
then this is where you should add those.
|
||||
|
||||
### integration/
|
||||
|
||||
Cypress convention. Contains the specs that are going to be executed.
|
||||
|
@ -99,7 +203,8 @@ Cypress convention. Fixtures are used as external pieces of static data when we
|
|||
|
||||
### support/
|
||||
|
||||
Cypress convention. As a convenience, by default Cypress will automatically include the plugins file cypress/plugins/index.js before every single spec file it runs.
|
||||
Cypress convention. As a convenience, by default Cypress will automatically include the plugins file
|
||||
cypress/plugins/index.js before every single spec file it runs.
|
||||
Directory also holds Cypress Plugins that are then initialized via `setupNodeEvents()` in the Cypress configuration.
|
||||
|
||||
### screens/
|
||||
|
@ -112,7 +217,7 @@ Each file inside the screens folder represents a screen in our application.
|
|||
|
||||
_Tasks_ are functions that may be reused across tests.
|
||||
|
||||
Each file inside the tasks folder represents a screen of our application.
|
||||
Each file inside the tasks folder represents a screen of our application.
|
||||
|
||||
## Test data
|
||||
|
||||
|
@ -122,9 +227,10 @@ The data the tests need:
|
|||
|
||||
## Development Best Practices
|
||||
|
||||
### Clean up the state
|
||||
### Clean up the state
|
||||
|
||||
Remember to clean up the state of the test after its execution. Be mindful of failure scenarios, as well: if your test fails, will it leave the environment in a recoverable state?
|
||||
Remember to clean up the state of the test after its execution. Be mindful of failure scenarios, as well: if your test
|
||||
fails, will it leave the environment in a recoverable state?
|
||||
|
||||
### Minimize the use of es_archive
|
||||
|
||||
|
@ -143,4 +249,5 @@ Remember that by minimizing the number of times the web page is loaded, we minim
|
|||
|
||||
## Linting
|
||||
|
||||
Optional linting rules for Cypress and linting setup can be found [here](https://github.com/cypress-io/eslint-plugin-cypress#usage)
|
||||
Optional linting rules for Cypress and linting setup can be
|
||||
found [here](https://github.com/cypress-io/eslint-plugin-cypress#usage)
|
||||
|
|
|
@ -6,32 +6,57 @@
|
|||
*/
|
||||
|
||||
import { recurse } from 'cypress-recurse';
|
||||
import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
|
||||
import { HOST_METADATA_LIST_ROUTE } from '../../../../../common/endpoint/constants';
|
||||
import type { MetadataListResponse } from '../../../../../common/endpoint/types';
|
||||
import type { MetadataListResponse, PolicyData } from '../../../../../common/endpoint/types';
|
||||
import { APP_ENDPOINTS_PATH } from '../../../../../common/constants';
|
||||
import { getArtifactsListTestsData } from '../../fixtures/artifacts_page';
|
||||
import { removeAllArtifacts } from '../../tasks/artifacts';
|
||||
import { loadEndpointDataForEventFiltersIfNeeded } from '../../tasks/load_endpoint_data';
|
||||
import { login } from '../../tasks/login';
|
||||
import { performUserActions } from '../../tasks/perform_user_actions';
|
||||
import { request } from '../../tasks/common';
|
||||
import { yieldEndpointPolicyRevision } from '../../tasks/fleet';
|
||||
import { request, loadPage } from '../../tasks/common';
|
||||
import {
|
||||
createAgentPolicyTask,
|
||||
getEndpointIntegrationVersion,
|
||||
yieldEndpointPolicyRevision,
|
||||
} from '../../tasks/fleet';
|
||||
import type { CreateAndEnrollEndpointHostResponse } from '../../../../../scripts/endpoint/common/endpoint_host_services';
|
||||
import { createEndpointHost } from '../../tasks/create_endpoint_host';
|
||||
import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data';
|
||||
import { enableAllPolicyProtections } from '../../tasks/endpoint_policy';
|
||||
|
||||
const yieldAppliedEndpointRevision = (): Cypress.Chainable<number> =>
|
||||
request<MetadataListResponse>({
|
||||
method: 'GET',
|
||||
url: HOST_METADATA_LIST_ROUTE,
|
||||
}).then(({ body }) => {
|
||||
expect(body.data.length).is.lte(1); // during update it can be temporary zero
|
||||
expect(body.data.length).is.lte(2); // during update it can be temporary zero
|
||||
return Number(body.data?.[0]?.metadata.Endpoint.policy.applied.endpoint_policy_version) ?? -1;
|
||||
});
|
||||
|
||||
const parseRevNumber = (revString: string) => Number(revString.match(/\d+/)?.[0]);
|
||||
|
||||
describe('Artifact pages', () => {
|
||||
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
|
||||
let policy: PolicyData;
|
||||
let createdHost: CreateAndEnrollEndpointHostResponse;
|
||||
|
||||
before(() => {
|
||||
getEndpointIntegrationVersion().then((version) =>
|
||||
createAgentPolicyTask(version).then((data) => {
|
||||
indexedPolicy = data;
|
||||
policy = indexedPolicy.integrationPolicies[0];
|
||||
|
||||
return enableAllPolicyProtections(policy.id).then(() => {
|
||||
// Create and enroll a new Endpoint host
|
||||
return createEndpointHost(policy.policy_id).then((host) => {
|
||||
createdHost = host as CreateAndEnrollEndpointHostResponse;
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
login();
|
||||
loadEndpointDataForEventFiltersIfNeeded();
|
||||
removeAllArtifacts();
|
||||
|
||||
// wait for ManifestManager to pick up artifact changes that happened either here
|
||||
|
@ -49,11 +74,23 @@ describe('Artifact pages', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
cy.visit(APP_ENDPOINTS_PATH);
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
removeAllArtifacts();
|
||||
|
||||
if (createdHost) {
|
||||
cy.task('destroyEndpointHost', createdHost);
|
||||
}
|
||||
|
||||
if (indexedPolicy) {
|
||||
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
|
||||
}
|
||||
|
||||
if (createdHost) {
|
||||
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
|
||||
}
|
||||
});
|
||||
|
||||
for (const testData of getArtifactsListTestsData()) {
|
||||
|
@ -64,7 +101,7 @@ describe('Artifact pages', () => {
|
|||
.invoke('text')
|
||||
.then(parseRevNumber)
|
||||
.then((initialRevisionNumber) => {
|
||||
cy.visit(`/app/security/administration/${testData.urlPath}`);
|
||||
loadPage(`/app/security/administration/${testData.urlPath}`);
|
||||
|
||||
cy.getByTestSubj(`${testData.pagePrefix}-emptyState-addButton`).click();
|
||||
performUserActions(testData.create.formActions);
|
||||
|
@ -75,7 +112,7 @@ describe('Artifact pages', () => {
|
|||
cy.getByTestSubj(checkResult.selector).should('have.text', checkResult.value);
|
||||
}
|
||||
|
||||
cy.visit(APP_ENDPOINTS_PATH);
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
|
||||
// depends on the 10s auto refresh
|
||||
cy.getByTestSubj('policyListRevNo')
|
||||
|
|
|
@ -5,25 +5,57 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Agent } from '@kbn/fleet-plugin/common';
|
||||
import type { PolicyData } from '../../../../../common/endpoint/types';
|
||||
import { APP_ENDPOINTS_PATH } from '../../../../../common/constants';
|
||||
import { closeAllToasts } from '../../tasks/toasts';
|
||||
import { toggleRuleOffAndOn, visitRuleAlerts } from '../../tasks/isolate';
|
||||
import { cleanupRule, loadRule } from '../../tasks/api_fixtures';
|
||||
import { ENDPOINT_VM_NAME } from '../../tasks/common';
|
||||
import { login } from '../../tasks/login';
|
||||
import { loadPage } from '../../tasks/common';
|
||||
import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
|
||||
import {
|
||||
createAgentPolicyTask,
|
||||
getAgentByHostName,
|
||||
getEndpointIntegrationVersion,
|
||||
reassignAgentPolicy,
|
||||
} from '../../tasks/fleet';
|
||||
import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../tasks/fleet';
|
||||
import { changeAlertsFilter } from '../../tasks/alerts';
|
||||
import type { CreateAndEnrollEndpointHostResponse } from '../../../../../scripts/endpoint/common/endpoint_host_services';
|
||||
import { createEndpointHost } from '../../tasks/create_endpoint_host';
|
||||
import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data';
|
||||
import { enableAllPolicyProtections } from '../../tasks/endpoint_policy';
|
||||
|
||||
describe('Automated Response Actions', () => {
|
||||
const endpointHostname = Cypress.env(ENDPOINT_VM_NAME);
|
||||
const hostname = Cypress.env('hostname');
|
||||
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
|
||||
let policy: PolicyData;
|
||||
let createdHost: CreateAndEnrollEndpointHostResponse;
|
||||
|
||||
before(() => {
|
||||
getEndpointIntegrationVersion().then((version) =>
|
||||
createAgentPolicyTask(version, 'automated_response_actions').then((data) => {
|
||||
indexedPolicy = data;
|
||||
policy = indexedPolicy.integrationPolicies[0];
|
||||
|
||||
return enableAllPolicyProtections(policy.id).then(() => {
|
||||
// Create and enroll a new Endpoint host
|
||||
return createEndpointHost(policy.policy_id).then((host) => {
|
||||
createdHost = host as CreateAndEnrollEndpointHostResponse;
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (createdHost) {
|
||||
cy.task('destroyEndpointHost', createdHost);
|
||||
}
|
||||
|
||||
if (indexedPolicy) {
|
||||
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
|
||||
}
|
||||
|
||||
if (createdHost) {
|
||||
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
|
||||
}
|
||||
});
|
||||
|
||||
const hostname = new URL(Cypress.env('FLEET_SERVER_URL')).port;
|
||||
const fleetHostname = `dev-fleet-server.${hostname}`;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -31,47 +63,28 @@ describe('Automated Response Actions', () => {
|
|||
});
|
||||
|
||||
describe('From alerts', () => {
|
||||
let response: IndexedFleetEndpointPolicyResponse;
|
||||
let initialAgentData: Agent;
|
||||
let ruleId: string;
|
||||
let ruleName: string;
|
||||
|
||||
before(() => {
|
||||
getAgentByHostName(endpointHostname).then((agentData) => {
|
||||
initialAgentData = agentData;
|
||||
});
|
||||
|
||||
getEndpointIntegrationVersion().then((version) => {
|
||||
createAgentPolicyTask(version).then((data) => {
|
||||
response = data;
|
||||
});
|
||||
});
|
||||
loadRule(true).then((data) => {
|
||||
loadRule().then((data) => {
|
||||
ruleId = data.id;
|
||||
ruleName = data.name;
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (initialAgentData?.policy_id) {
|
||||
reassignAgentPolicy(initialAgentData.id, initialAgentData.policy_id);
|
||||
}
|
||||
if (response) {
|
||||
cy.task('deleteIndexedFleetEndpointPolicies', response);
|
||||
}
|
||||
if (ruleId) {
|
||||
cleanupRule(ruleId);
|
||||
}
|
||||
});
|
||||
|
||||
it('should have generated endpoint and rule', () => {
|
||||
cy.visit(APP_ENDPOINTS_PATH);
|
||||
cy.contains(endpointHostname).should('exist');
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
cy.contains(createdHost.hostname).should('exist');
|
||||
|
||||
toggleRuleOffAndOn(ruleName);
|
||||
});
|
||||
|
||||
it('should display endpoint automated response action in event details flyout ', () => {
|
||||
visitRuleAlerts(ruleName);
|
||||
closeAllToasts();
|
||||
|
||||
|
@ -80,9 +93,9 @@ describe('Automated Response Actions', () => {
|
|||
cy.getByTestSubj('responseActionsViewTab').click();
|
||||
cy.getByTestSubj('response-actions-notification').should('not.have.text', '0');
|
||||
|
||||
cy.getByTestSubj(`response-results-${endpointHostname}-details-tray`)
|
||||
cy.getByTestSubj(`response-results-${createdHost.hostname}-details-tray`)
|
||||
.should('contain', 'isolate completed successfully')
|
||||
.and('contain', endpointHostname);
|
||||
.and('contain', createdHost.hostname);
|
||||
|
||||
cy.getByTestSubj(`response-results-${fleetHostname}-details-tray`)
|
||||
.should('contain', 'The host does not have Elastic Defend integration installed')
|
||||
|
|
|
@ -42,7 +42,7 @@ describe('Endpoint generated alerts', () => {
|
|||
|
||||
after(() => {
|
||||
if (createdHost) {
|
||||
cy.task('destroyEndpointHost', createdHost).then(() => {});
|
||||
cy.task('destroyEndpointHost', createdHost);
|
||||
}
|
||||
|
||||
if (indexedPolicy) {
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
*/
|
||||
|
||||
import type { Agent } from '@kbn/fleet-plugin/common';
|
||||
import type { PolicyData } from '../../../../../common/endpoint/types';
|
||||
import { APP_ENDPOINTS_PATH } from '../../../../../common/constants';
|
||||
import { ENDPOINT_VM_NAME } from '../../tasks/common';
|
||||
import {
|
||||
createAgentPolicyTask,
|
||||
getAgentByHostName,
|
||||
|
@ -16,6 +16,7 @@ import {
|
|||
} from '../../tasks/fleet';
|
||||
import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
|
||||
import { login } from '../../tasks/login';
|
||||
import { loadPage } from '../../tasks/common';
|
||||
import {
|
||||
AGENT_HOSTNAME_CELL,
|
||||
TABLE_ROW_ACTIONS,
|
||||
|
@ -26,18 +27,56 @@ import {
|
|||
FLEET_REASSIGN_POLICY_MODAL,
|
||||
FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON,
|
||||
} from '../../screens/fleet';
|
||||
import type { CreateAndEnrollEndpointHostResponse } from '../../../../../scripts/endpoint/common/endpoint_host_services';
|
||||
import { createEndpointHost } from '../../tasks/create_endpoint_host';
|
||||
import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data';
|
||||
import { enableAllPolicyProtections } from '../../tasks/endpoint_policy';
|
||||
|
||||
describe('Endpoints page', () => {
|
||||
const endpointHostname = Cypress.env(ENDPOINT_VM_NAME);
|
||||
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
|
||||
let policy: PolicyData;
|
||||
let createdHost: CreateAndEnrollEndpointHostResponse;
|
||||
|
||||
before(() => {
|
||||
getEndpointIntegrationVersion().then((version) => {
|
||||
createAgentPolicyTask(version).then((data) => {
|
||||
indexedPolicy = data;
|
||||
policy = indexedPolicy.integrationPolicies[0];
|
||||
|
||||
return enableAllPolicyProtections(policy.id).then(() => {
|
||||
// Create and enroll a new Endpoint host
|
||||
return createEndpointHost(policy.policy_id).then((host) => {
|
||||
createdHost = host as CreateAndEnrollEndpointHostResponse;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (createdHost) {
|
||||
cy.task('destroyEndpointHost', createdHost);
|
||||
}
|
||||
|
||||
if (indexedPolicy) {
|
||||
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
|
||||
}
|
||||
|
||||
if (createdHost) {
|
||||
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
});
|
||||
|
||||
it('Shows endpoint on the list', () => {
|
||||
cy.visit(APP_ENDPOINTS_PATH);
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
cy.contains('Hosts running Elastic Defend').should('exist');
|
||||
cy.getByTestSubj(AGENT_HOSTNAME_CELL).should('have.text', endpointHostname);
|
||||
cy.getByTestSubj(AGENT_HOSTNAME_CELL)
|
||||
.contains(createdHost.hostname)
|
||||
.should('have.text', createdHost.hostname);
|
||||
});
|
||||
|
||||
describe('Endpoint reassignment', () => {
|
||||
|
@ -45,7 +84,7 @@ describe('Endpoints page', () => {
|
|||
let initialAgentData: Agent;
|
||||
|
||||
before(() => {
|
||||
getAgentByHostName(endpointHostname).then((agentData) => {
|
||||
getAgentByHostName(createdHost.hostname).then((agentData) => {
|
||||
initialAgentData = agentData;
|
||||
});
|
||||
getEndpointIntegrationVersion().then((version) => {
|
||||
|
@ -69,24 +108,24 @@ describe('Endpoints page', () => {
|
|||
});
|
||||
|
||||
it('User can reassign a single endpoint to a different Agent Configuration', () => {
|
||||
cy.visit(APP_ENDPOINTS_PATH);
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
const hostname = cy
|
||||
.getByTestSubj(AGENT_HOSTNAME_CELL)
|
||||
.filter(`:contains("${endpointHostname}")`);
|
||||
.filter(`:contains("${createdHost.hostname}")`);
|
||||
const tableRow = hostname.parents('tr');
|
||||
tableRow.getByTestSubj(TABLE_ROW_ACTIONS).click();
|
||||
tableRow.findByTestSubj(TABLE_ROW_ACTIONS).click();
|
||||
cy.getByTestSubj(TABLE_ROW_ACTIONS_MENU).contains('Reassign agent policy').click();
|
||||
cy.getByTestSubj(FLEET_REASSIGN_POLICY_MODAL)
|
||||
.find('select')
|
||||
.select(response.agentPolicies[0].name);
|
||||
cy.getByTestSubj(FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON).click();
|
||||
cy.getByTestSubj(AGENT_HOSTNAME_CELL)
|
||||
.filter(`:contains("${endpointHostname}")`)
|
||||
.filter(`:contains("${createdHost.hostname}")`)
|
||||
.should('exist');
|
||||
cy.getByTestSubj(AGENT_HOSTNAME_CELL)
|
||||
.filter(`:contains("${endpointHostname}")`)
|
||||
.filter(`:contains("${createdHost.hostname}")`)
|
||||
.parents('tr')
|
||||
.getByTestSubj(AGENT_POLICY_CELL)
|
||||
.findByTestSubj(AGENT_POLICY_CELL)
|
||||
.should('have.text', response.agentPolicies[0].name);
|
||||
});
|
||||
});
|
||||
|
@ -94,7 +133,7 @@ describe('Endpoints page', () => {
|
|||
it('should update endpoint policy on Endpoint', () => {
|
||||
const parseRevNumber = (revString: string) => Number(revString.match(/\d+/)?.[0]);
|
||||
|
||||
cy.visit(APP_ENDPOINTS_PATH);
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
|
||||
cy.getByTestSubj('policyListRevNo')
|
||||
.first()
|
||||
|
|
|
@ -5,14 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Agent } from '@kbn/fleet-plugin/common';
|
||||
import type { PolicyData } from '../../../../../common/endpoint/types';
|
||||
import { APP_CASES_PATH, APP_ENDPOINTS_PATH } from '../../../../../common/constants';
|
||||
import { closeAllToasts } from '../../tasks/toasts';
|
||||
import {
|
||||
checkEndpointListForOnlyIsolatedHosts,
|
||||
checkEndpointListForOnlyUnIsolatedHosts,
|
||||
checkFlyoutEndpointIsolation,
|
||||
filterOutEndpoints,
|
||||
filterOutIsolatedHosts,
|
||||
isolateHostWithComment,
|
||||
openAlertDetails,
|
||||
|
@ -23,53 +22,63 @@ import {
|
|||
waitForReleaseOption,
|
||||
} from '../../tasks/isolate';
|
||||
import { cleanupCase, cleanupRule, loadCase, loadRule } from '../../tasks/api_fixtures';
|
||||
import { ENDPOINT_VM_NAME } from '../../tasks/common';
|
||||
import { login } from '../../tasks/login';
|
||||
import { loadPage } from '../../tasks/common';
|
||||
import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
|
||||
import {
|
||||
createAgentPolicyTask,
|
||||
getAgentByHostName,
|
||||
getEndpointIntegrationVersion,
|
||||
reassignAgentPolicy,
|
||||
} from '../../tasks/fleet';
|
||||
import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../tasks/fleet';
|
||||
import type { CreateAndEnrollEndpointHostResponse } from '../../../../../scripts/endpoint/common/endpoint_host_services';
|
||||
import { createEndpointHost } from '../../tasks/create_endpoint_host';
|
||||
import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data';
|
||||
import { enableAllPolicyProtections } from '../../tasks/endpoint_policy';
|
||||
|
||||
describe('Isolate command', () => {
|
||||
const endpointHostname = Cypress.env(ENDPOINT_VM_NAME);
|
||||
const isolateComment = `Isolating ${endpointHostname}`;
|
||||
const releaseComment = `Releasing ${endpointHostname}`;
|
||||
let isolateComment: string;
|
||||
let releaseComment: string;
|
||||
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
|
||||
let policy: PolicyData;
|
||||
let createdHost: CreateAndEnrollEndpointHostResponse;
|
||||
|
||||
before(() => {
|
||||
getEndpointIntegrationVersion().then((version) => {
|
||||
createAgentPolicyTask(version).then((data) => {
|
||||
indexedPolicy = data;
|
||||
policy = indexedPolicy.integrationPolicies[0];
|
||||
|
||||
return enableAllPolicyProtections(policy.id).then(() => {
|
||||
// Create and enroll a new Endpoint host
|
||||
return createEndpointHost(policy.policy_id).then((host) => {
|
||||
createdHost = host as CreateAndEnrollEndpointHostResponse;
|
||||
isolateComment = `Isolating ${host.hostname}`;
|
||||
releaseComment = `Releasing ${host.hostname}`;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (createdHost) {
|
||||
cy.task('destroyEndpointHost', createdHost);
|
||||
}
|
||||
|
||||
if (indexedPolicy) {
|
||||
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
|
||||
}
|
||||
|
||||
if (createdHost) {
|
||||
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
});
|
||||
|
||||
describe('From manage', () => {
|
||||
let response: IndexedFleetEndpointPolicyResponse;
|
||||
let initialAgentData: Agent;
|
||||
|
||||
before(() => {
|
||||
getAgentByHostName(endpointHostname).then((agentData) => {
|
||||
initialAgentData = agentData;
|
||||
});
|
||||
|
||||
getEndpointIntegrationVersion().then((version) =>
|
||||
createAgentPolicyTask(version).then((data) => {
|
||||
response = data;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (initialAgentData?.policy_id) {
|
||||
reassignAgentPolicy(initialAgentData.id, initialAgentData.policy_id);
|
||||
}
|
||||
if (response) {
|
||||
cy.task('deleteIndexedFleetEndpointPolicies', response);
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow filtering endpoint by Isolated status', () => {
|
||||
cy.visit(APP_ENDPOINTS_PATH);
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
closeAllToasts();
|
||||
cy.getByTestSubj('globalLoadingIndicator-hidden').should('exist');
|
||||
checkEndpointListForOnlyUnIsolatedHosts();
|
||||
|
||||
filterOutIsolatedHosts();
|
||||
|
@ -79,11 +88,11 @@ describe('Isolate command', () => {
|
|||
cy.getByTestSubj('endpointTableRowActions').click();
|
||||
cy.getByTestSubj('isolateLink').click();
|
||||
|
||||
cy.contains(`Isolate host ${endpointHostname} from network.`);
|
||||
cy.contains(`Isolate host ${createdHost.hostname} from network.`);
|
||||
cy.getByTestSubj('endpointHostIsolationForm');
|
||||
cy.getByTestSubj('host_isolation_comment').type(isolateComment);
|
||||
cy.getByTestSubj('hostIsolateConfirmButton').click();
|
||||
cy.contains(`Isolation on host ${endpointHostname} successfully submitted`);
|
||||
cy.contains(`Isolation on host ${createdHost.hostname} successfully submitted`);
|
||||
cy.getByTestSubj('euiFlyoutCloseButton').click();
|
||||
cy.getByTestSubj('rowHostStatus-actionStatuses').should('contain.text', 'Isolated');
|
||||
filterOutIsolatedHosts();
|
||||
|
@ -92,7 +101,7 @@ describe('Isolate command', () => {
|
|||
|
||||
cy.getByTestSubj('endpointTableRowActions').click();
|
||||
cy.getByTestSubj('unIsolateLink').click();
|
||||
releaseHostWithComment(releaseComment, endpointHostname);
|
||||
releaseHostWithComment(releaseComment, createdHost.hostname);
|
||||
cy.contains('Confirm').click();
|
||||
cy.getByTestSubj('euiFlyoutCloseButton').click();
|
||||
cy.getByTestSubj('adminSearchBar').click().type('{selectall}{backspace}');
|
||||
|
@ -102,68 +111,49 @@ describe('Isolate command', () => {
|
|||
});
|
||||
|
||||
describe('From alerts', () => {
|
||||
let response: IndexedFleetEndpointPolicyResponse;
|
||||
let initialAgentData: Agent;
|
||||
let ruleId: string;
|
||||
let ruleName: string;
|
||||
|
||||
before(() => {
|
||||
getAgentByHostName(endpointHostname).then((agentData) => {
|
||||
initialAgentData = agentData;
|
||||
});
|
||||
|
||||
getEndpointIntegrationVersion().then((version) =>
|
||||
createAgentPolicyTask(version).then((data) => {
|
||||
response = data;
|
||||
})
|
||||
);
|
||||
loadRule(false).then((data) => {
|
||||
loadRule(
|
||||
{ query: `agent.name: ${createdHost.hostname} and agent.type: endpoint` },
|
||||
false
|
||||
).then((data) => {
|
||||
ruleId = data.id;
|
||||
ruleName = data.name;
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (initialAgentData?.policy_id) {
|
||||
reassignAgentPolicy(initialAgentData.id, initialAgentData.policy_id);
|
||||
}
|
||||
if (response) {
|
||||
cy.task('deleteIndexedFleetEndpointPolicies', response);
|
||||
}
|
||||
if (ruleId) {
|
||||
cleanupRule(ruleId);
|
||||
}
|
||||
});
|
||||
|
||||
it('should have generated endpoint and rule', () => {
|
||||
cy.visit(APP_ENDPOINTS_PATH);
|
||||
cy.contains(endpointHostname).should('exist');
|
||||
it('should isolate and release host', () => {
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
cy.contains(createdHost.hostname).should('exist');
|
||||
|
||||
toggleRuleOffAndOn(ruleName);
|
||||
});
|
||||
|
||||
it('should isolate and release host', () => {
|
||||
visitRuleAlerts(ruleName);
|
||||
|
||||
filterOutEndpoints(endpointHostname);
|
||||
|
||||
closeAllToasts();
|
||||
openAlertDetails();
|
||||
|
||||
isolateHostWithComment(isolateComment, endpointHostname);
|
||||
isolateHostWithComment(isolateComment, createdHost.hostname);
|
||||
|
||||
cy.getByTestSubj('hostIsolateConfirmButton').click();
|
||||
cy.contains(`Isolation on host ${endpointHostname} successfully submitted`);
|
||||
cy.contains(`Isolation on host ${createdHost.hostname} successfully submitted`);
|
||||
|
||||
cy.getByTestSubj('euiFlyoutCloseButton').click();
|
||||
openAlertDetails();
|
||||
|
||||
checkFlyoutEndpointIsolation();
|
||||
|
||||
releaseHostWithComment(releaseComment, endpointHostname);
|
||||
releaseHostWithComment(releaseComment, createdHost.hostname);
|
||||
cy.contains('Confirm').click();
|
||||
|
||||
cy.contains(`Release on host ${endpointHostname} successfully submitted`);
|
||||
cy.contains(`Release on host ${createdHost.hostname} successfully submitted`);
|
||||
cy.getByTestSubj('euiFlyoutCloseButton').click();
|
||||
openAlertDetails();
|
||||
cy.getByTestSubj('event-field-agent.status').within(() => {
|
||||
|
@ -173,8 +163,6 @@ describe('Isolate command', () => {
|
|||
});
|
||||
|
||||
describe('From cases', () => {
|
||||
let response: IndexedFleetEndpointPolicyResponse;
|
||||
let initialAgentData: Agent;
|
||||
let ruleId: string;
|
||||
let ruleName: string;
|
||||
let caseId: string;
|
||||
|
@ -182,16 +170,10 @@ describe('Isolate command', () => {
|
|||
const caseOwner = 'securitySolution';
|
||||
|
||||
before(() => {
|
||||
getAgentByHostName(endpointHostname).then((agentData) => {
|
||||
initialAgentData = agentData;
|
||||
});
|
||||
getEndpointIntegrationVersion().then((version) =>
|
||||
createAgentPolicyTask(version).then((data) => {
|
||||
response = data;
|
||||
})
|
||||
);
|
||||
|
||||
loadRule(false).then((data) => {
|
||||
loadRule(
|
||||
{ query: `agent.name: ${createdHost.hostname} and agent.type: endpoint` },
|
||||
false
|
||||
).then((data) => {
|
||||
ruleId = data.id;
|
||||
ruleName = data.name;
|
||||
});
|
||||
|
@ -205,12 +187,6 @@ describe('Isolate command', () => {
|
|||
});
|
||||
|
||||
after(() => {
|
||||
if (initialAgentData?.policy_id) {
|
||||
reassignAgentPolicy(initialAgentData.id, initialAgentData.policy_id);
|
||||
}
|
||||
if (response) {
|
||||
cy.task('deleteIndexedFleetEndpointPolicies', response);
|
||||
}
|
||||
if (ruleId) {
|
||||
cleanupRule(ruleId);
|
||||
}
|
||||
|
@ -219,16 +195,13 @@ describe('Isolate command', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('should have generated endpoint and rule', () => {
|
||||
cy.visit(APP_ENDPOINTS_PATH);
|
||||
cy.contains(endpointHostname).should('exist');
|
||||
it('should isolate and release host', () => {
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
cy.contains(createdHost.hostname).should('exist');
|
||||
|
||||
toggleRuleOffAndOn(ruleName);
|
||||
});
|
||||
|
||||
it('should isolate and release host', () => {
|
||||
visitRuleAlerts(ruleName);
|
||||
filterOutEndpoints(endpointHostname);
|
||||
closeAllToasts();
|
||||
|
||||
openAlertDetails();
|
||||
|
@ -238,13 +211,13 @@ describe('Isolate command', () => {
|
|||
cy.contains(`An alert was added to \"Test ${caseOwner} case`);
|
||||
|
||||
cy.intercept('GET', `/api/cases/${caseId}/user_actions/_find*`).as('case');
|
||||
cy.visit(`${APP_CASES_PATH}/${caseId}`);
|
||||
loadPage(`${APP_CASES_PATH}/${caseId}`);
|
||||
cy.wait('@case', { timeout: 30000 }).then(({ response: res }) => {
|
||||
const caseAlertId = res?.body.userActions[1].id;
|
||||
|
||||
closeAllToasts();
|
||||
openCaseAlertDetails(caseAlertId);
|
||||
isolateHostWithComment(isolateComment, endpointHostname);
|
||||
isolateHostWithComment(isolateComment, createdHost.hostname);
|
||||
cy.getByTestSubj('hostIsolateConfirmButton').click();
|
||||
|
||||
cy.getByTestSubj('euiFlyoutCloseButton').click();
|
||||
|
@ -257,11 +230,11 @@ describe('Isolate command', () => {
|
|||
|
||||
waitForReleaseOption(caseAlertId);
|
||||
|
||||
releaseHostWithComment(releaseComment, endpointHostname);
|
||||
releaseHostWithComment(releaseComment, createdHost.hostname);
|
||||
|
||||
cy.contains('Confirm').click();
|
||||
|
||||
cy.contains(`Release on host ${endpointHostname} successfully submitted`);
|
||||
cy.contains(`Release on host ${createdHost.hostname} successfully submitted`);
|
||||
cy.getByTestSubj('euiFlyoutCloseButton').click();
|
||||
|
||||
cy.getByTestSubj('user-actions-list').within(() => {
|
||||
|
|
|
@ -23,14 +23,11 @@ import {
|
|||
} from '../../tasks/isolate';
|
||||
|
||||
import { login } from '../../tasks/login';
|
||||
import { ENDPOINT_VM_NAME } from '../../tasks/common';
|
||||
import { enableAllPolicyProtections } from '../../tasks/endpoint_policy';
|
||||
import { createEndpointHost } from '../../tasks/create_endpoint_host';
|
||||
import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data';
|
||||
|
||||
describe('Response console', () => {
|
||||
const endpointHostname = Cypress.env(ENDPOINT_VM_NAME);
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
});
|
||||
|
@ -58,7 +55,7 @@ describe('Response console', () => {
|
|||
|
||||
after(() => {
|
||||
if (createdHost) {
|
||||
cy.task('destroyEndpointHost', createdHost).then(() => {});
|
||||
cy.task('destroyEndpointHost', createdHost);
|
||||
}
|
||||
|
||||
if (indexedPolicy) {
|
||||
|
@ -72,25 +69,25 @@ describe('Response console', () => {
|
|||
|
||||
it('should isolate host from response console', () => {
|
||||
const command = 'isolate';
|
||||
waitForEndpointListPageToBeLoaded(endpointHostname);
|
||||
waitForEndpointListPageToBeLoaded(createdHost.hostname);
|
||||
checkEndpointListForOnlyUnIsolatedHosts();
|
||||
openResponseConsoleFromEndpointList();
|
||||
performCommandInputChecks(command);
|
||||
submitCommand();
|
||||
waitForCommandToBeExecuted(command);
|
||||
waitForEndpointListPageToBeLoaded(endpointHostname);
|
||||
waitForEndpointListPageToBeLoaded(createdHost.hostname);
|
||||
checkEndpointListForOnlyIsolatedHosts();
|
||||
});
|
||||
|
||||
it('should release host from response console', () => {
|
||||
const command = 'release';
|
||||
waitForEndpointListPageToBeLoaded(endpointHostname);
|
||||
waitForEndpointListPageToBeLoaded(createdHost.hostname);
|
||||
checkEndpointListForOnlyIsolatedHosts();
|
||||
openResponseConsoleFromEndpointList();
|
||||
performCommandInputChecks(command);
|
||||
submitCommand();
|
||||
waitForCommandToBeExecuted(command);
|
||||
waitForEndpointListPageToBeLoaded(endpointHostname);
|
||||
waitForEndpointListPageToBeLoaded(createdHost.hostname);
|
||||
checkEndpointListForOnlyUnIsolatedHosts();
|
||||
});
|
||||
});
|
||||
|
@ -121,7 +118,7 @@ describe('Response console', () => {
|
|||
|
||||
after(() => {
|
||||
if (createdHost) {
|
||||
cy.task('destroyEndpointHost', createdHost).then(() => {});
|
||||
cy.task('destroyEndpointHost', createdHost);
|
||||
}
|
||||
|
||||
if (indexedPolicy) {
|
||||
|
@ -134,7 +131,7 @@ describe('Response console', () => {
|
|||
});
|
||||
|
||||
it('"processes" - should obtain a list of processes', () => {
|
||||
waitForEndpointListPageToBeLoaded(endpointHostname);
|
||||
waitForEndpointListPageToBeLoaded(createdHost.hostname);
|
||||
openResponseConsoleFromEndpointList();
|
||||
performCommandInputChecks('processes');
|
||||
submitCommand();
|
||||
|
@ -159,7 +156,7 @@ describe('Response console', () => {
|
|||
});
|
||||
|
||||
it('"kill-process --pid" - should kill a process', () => {
|
||||
waitForEndpointListPageToBeLoaded(endpointHostname);
|
||||
waitForEndpointListPageToBeLoaded(createdHost.hostname);
|
||||
openResponseConsoleFromEndpointList();
|
||||
inputConsoleCommand(`kill-process --pid ${cronPID}`);
|
||||
submitCommand();
|
||||
|
@ -183,7 +180,7 @@ describe('Response console', () => {
|
|||
});
|
||||
|
||||
it('"suspend-process --pid" - should suspend a process', () => {
|
||||
waitForEndpointListPageToBeLoaded(endpointHostname);
|
||||
waitForEndpointListPageToBeLoaded(createdHost.hostname);
|
||||
openResponseConsoleFromEndpointList();
|
||||
inputConsoleCommand(`suspend-process --pid ${newCronPID}`);
|
||||
submitCommand();
|
||||
|
@ -191,11 +188,11 @@ describe('Response console', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('File operations: get-file and execute', () => {
|
||||
const homeFilePath = `/home/ubuntu`;
|
||||
describe('File operations: get-file and execute', () => {
|
||||
const homeFilePath = process.env.CI || true ? '/home/vagrant' : `/home/ubuntu`;
|
||||
|
||||
const fileContent = 'This is a test file for the get-file command.';
|
||||
const filePath = `/home/ubuntu/test_file.txt`;
|
||||
const filePath = `${homeFilePath}/test_file.txt`;
|
||||
|
||||
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
|
||||
let policy: PolicyData;
|
||||
|
@ -219,7 +216,7 @@ describe('Response console', () => {
|
|||
|
||||
after(() => {
|
||||
if (createdHost) {
|
||||
cy.task('destroyEndpointHost', createdHost).then(() => {});
|
||||
cy.task('destroyEndpointHost', createdHost);
|
||||
}
|
||||
|
||||
if (indexedPolicy) {
|
||||
|
@ -232,7 +229,12 @@ describe('Response console', () => {
|
|||
});
|
||||
|
||||
it('"get-file --path" - should retrieve a file', () => {
|
||||
waitForEndpointListPageToBeLoaded(endpointHostname);
|
||||
waitForEndpointListPageToBeLoaded(createdHost.hostname);
|
||||
cy.task('createFileOnEndpoint', {
|
||||
hostname: createdHost.hostname,
|
||||
path: filePath,
|
||||
content: fileContent,
|
||||
});
|
||||
openResponseConsoleFromEndpointList();
|
||||
inputConsoleCommand(`get-file --path ${filePath}`);
|
||||
submitCommand();
|
||||
|
@ -247,14 +249,14 @@ describe('Response console', () => {
|
|||
cy.readFile(`${downloadsFolder}/upload.zip`);
|
||||
|
||||
cy.task('uploadFileToEndpoint', {
|
||||
hostname: endpointHostname,
|
||||
hostname: createdHost.hostname,
|
||||
srcPath: `${downloadsFolder}/upload.zip`,
|
||||
destPath: '/home/ubuntu/upload.zip',
|
||||
destPath: `${homeFilePath}/upload.zip`,
|
||||
});
|
||||
|
||||
cy.task('readZippedFileContentOnEndpoint', {
|
||||
hostname: endpointHostname,
|
||||
path: '/home/ubuntu/upload.zip',
|
||||
hostname: createdHost.hostname,
|
||||
path: `${homeFilePath}/upload.zip`,
|
||||
password: 'elastic',
|
||||
}).then((unzippedFileContent) => {
|
||||
expect(unzippedFileContent).to.equal(fileContent);
|
||||
|
@ -262,8 +264,8 @@ describe('Response console', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('"execute --command" - should execute a command', async () => {
|
||||
waitForEndpointListPageToBeLoaded(endpointHostname);
|
||||
it('"execute --command" - should execute a command', () => {
|
||||
waitForEndpointListPageToBeLoaded(createdHost.hostname);
|
||||
openResponseConsoleFromEndpointList();
|
||||
inputConsoleCommand(`execute --command "ls -al ${homeFilePath}"`);
|
||||
submitCommand();
|
||||
|
@ -294,7 +296,7 @@ describe('Response console', () => {
|
|||
|
||||
after(() => {
|
||||
if (createdHost) {
|
||||
cy.task('destroyEndpointHost', createdHost).then(() => {});
|
||||
cy.task('destroyEndpointHost', createdHost);
|
||||
}
|
||||
|
||||
if (indexedPolicy) {
|
||||
|
@ -307,19 +309,19 @@ describe('Response console', () => {
|
|||
});
|
||||
|
||||
it('should fail if data tampered', () => {
|
||||
waitForEndpointListPageToBeLoaded(endpointHostname);
|
||||
waitForEndpointListPageToBeLoaded(createdHost.hostname);
|
||||
checkEndpointListForOnlyUnIsolatedHosts();
|
||||
openResponseConsoleFromEndpointList();
|
||||
performCommandInputChecks('isolate');
|
||||
|
||||
// stop host so that we ensure tamper happens before endpoint processes the action
|
||||
cy.task('stopEndpointHost');
|
||||
cy.task('stopEndpointHost', createdHost.hostname);
|
||||
// get action doc before we submit command so we know when the new action doc is indexed
|
||||
cy.task('getLatestActionDoc').then((previousActionDoc) => {
|
||||
submitCommand();
|
||||
cy.task('tamperActionDoc', previousActionDoc);
|
||||
});
|
||||
cy.task('startEndpointHost');
|
||||
cy.task('startEndpointHost', createdHost.hostname);
|
||||
|
||||
const actionValidationErrorMsg =
|
||||
'Fleet action response error: Failed to validate action signature; check Endpoint logs for details';
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
loginWithRole,
|
||||
ROLE,
|
||||
} from '../../tasks/login';
|
||||
import { loadPage } from '../../tasks/common';
|
||||
|
||||
import { getArtifactsListTestsData } from '../../fixtures/artifacts_page';
|
||||
import { removeAllArtifacts } from '../../tasks/artifacts';
|
||||
|
@ -20,18 +21,18 @@ import { loadEndpointDataForEventFiltersIfNeeded } from '../../tasks/load_endpoi
|
|||
|
||||
const loginWithWriteAccess = (url: string) => {
|
||||
loginWithRole(ROLE.endpoint_security_policy_manager);
|
||||
cy.visit(url);
|
||||
loadPage(url);
|
||||
};
|
||||
|
||||
const loginWithReadAccess = (privilegePrefix: string, url: string) => {
|
||||
const roleWithArtifactReadPrivilege = getRoleWithArtifactReadPrivilege(privilegePrefix);
|
||||
loginWithCustomRole('roleWithArtifactReadPrivilege', roleWithArtifactReadPrivilege);
|
||||
cy.visit(url);
|
||||
loadPage(url);
|
||||
};
|
||||
|
||||
const loginWithoutAccess = (url: string) => {
|
||||
loginWithRole(ROLE.t1_analyst);
|
||||
cy.visit(url);
|
||||
loadPage(url);
|
||||
};
|
||||
|
||||
describe('Artifacts pages', () => {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { closeAllToasts } from '../../tasks/toasts';
|
||||
import { login } from '../../tasks/login';
|
||||
import { loadPage } from '../../tasks/common';
|
||||
|
||||
describe('When defining a kibana role for Endpoint security access', () => {
|
||||
const getAllSubFeatureRows = (): Cypress.Chainable<JQuery<HTMLElement>> => {
|
||||
|
@ -18,7 +19,7 @@ describe('When defining a kibana role for Endpoint security access', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
cy.visit('/app/management/security/roles/edit');
|
||||
loadPage('/app/management/security/roles/edit');
|
||||
closeAllToasts();
|
||||
cy.getByTestSubj('addSpacePrivilegeButton').click();
|
||||
cy.getByTestSubj('featureCategoryButton_securitySolution').closest('button').click();
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { APP_ENDPOINTS_PATH } from '../../../../../common/constants';
|
||||
import type { ReturnTypeFromChainable } from '../../types';
|
||||
import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
|
||||
import { login } from '../../tasks/login';
|
||||
import { loadPage } from '../../tasks/common';
|
||||
|
||||
describe('Endpoints page', () => {
|
||||
let endpointData: ReturnTypeFromChainable<typeof indexEndpointHosts>;
|
||||
|
@ -31,7 +33,7 @@ describe('Endpoints page', () => {
|
|||
});
|
||||
|
||||
it('Loads the endpoints page', () => {
|
||||
cy.visit('/app/security/administration/endpoints');
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
cy.contains('Hosts running Elastic Defend').should('exist');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,6 +24,7 @@ import type { ReturnTypeFromChainable } from '../../types';
|
|||
import { addAlertsToCase } from '../../tasks/add_alerts_to_case';
|
||||
import { APP_ALERTS_PATH, APP_CASES_PATH, APP_PATH } from '../../../../../common/constants';
|
||||
import { login } from '../../tasks/login';
|
||||
import { loadPage } from '../../tasks/common';
|
||||
import { indexNewCase } from '../../tasks/index_new_case';
|
||||
import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
|
||||
import { indexEndpointRuleAlerts } from '../../tasks/index_endpoint_rule_alerts';
|
||||
|
@ -78,7 +79,7 @@ describe('Isolate command', () => {
|
|||
});
|
||||
|
||||
it('should allow filtering endpoint by Isolated status', () => {
|
||||
cy.visit(APP_PATH + getEndpointListPath({ name: 'endpointList' }));
|
||||
loadPage(APP_PATH + getEndpointListPath({ name: 'endpointList' }));
|
||||
closeAllToasts();
|
||||
filterOutIsolatedHosts();
|
||||
isolatedEndpointHostnames.forEach(checkEndpointIsIsolated);
|
||||
|
@ -130,7 +131,7 @@ describe('Isolate command', () => {
|
|||
let isolateRequestResponse: ActionDetails;
|
||||
let releaseRequestResponse: ActionDetails;
|
||||
|
||||
cy.visit(APP_ALERTS_PATH);
|
||||
loadPage(APP_ALERTS_PATH);
|
||||
closeAllToasts();
|
||||
|
||||
cy.getByTestSubj('alertsTable').within(() => {
|
||||
|
@ -256,7 +257,7 @@ describe('Isolate command', () => {
|
|||
const releaseComment = `Releasing ${hostname}`;
|
||||
const caseAlertId = caseAlertActions.comments[alertId];
|
||||
|
||||
cy.visit(caseUrlPath);
|
||||
loadPage(caseUrlPath);
|
||||
closeAllToasts();
|
||||
openCaseAlertDetails(caseAlertId);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import type { ReturnTypeFromChainable } from '../../types';
|
||||
import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
|
||||
import { login } from '../../tasks/login';
|
||||
import { loadPage } from '../../tasks/common';
|
||||
|
||||
describe('Response actions history page', () => {
|
||||
let endpointData: ReturnTypeFromChainable<typeof indexEndpointHosts>;
|
||||
|
@ -33,7 +34,7 @@ describe('Response actions history page', () => {
|
|||
|
||||
// Flakey, example build failure: https://buildkite.com/elastic/kibana-pull-request/builds/132245
|
||||
it.skip('retains expanded action details on page reload', () => {
|
||||
cy.visit(`/app/security/administration/response_actions_history`);
|
||||
loadPage(`/app/security/administration/response_actions_history`);
|
||||
cy.getByTestSubj('response-actions-list-expand-button').eq(3).click(); // 4th row on 1st page
|
||||
cy.getByTestSubj('response-actions-list-details-tray').should('exist');
|
||||
cy.url().should('include', 'withOutputs');
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
setResponderActionLogDateRange,
|
||||
} from '../../screens/responder';
|
||||
import { login } from '../../tasks/login';
|
||||
import { loadPage } from '../../tasks/common';
|
||||
import { indexNewCase } from '../../tasks/index_new_case';
|
||||
import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
|
||||
import { indexEndpointRuleAlerts } from '../../tasks/index_endpoint_rule_alerts';
|
||||
|
@ -102,14 +103,14 @@ describe('When accessing Endpoint Response Console', () => {
|
|||
});
|
||||
|
||||
it('should display responder option in take action menu', () => {
|
||||
cy.visit(caseUrlPath);
|
||||
loadPage(caseUrlPath);
|
||||
closeAllToasts();
|
||||
openCaseAlertDetails();
|
||||
cy.getByTestSubj('endpointResponseActions-action-item').should('be.enabled');
|
||||
});
|
||||
|
||||
it('should display Responder response action interface', () => {
|
||||
cy.visit(caseUrlPath);
|
||||
loadPage(caseUrlPath);
|
||||
closeAllToasts();
|
||||
openCaseAlertDetails();
|
||||
cy.getByTestSubj('endpointResponseActions-action-item').click();
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
import { APP_ALERTS_PATH } from '../../../../common/constants';
|
||||
import { loadPage } from '../tasks/common';
|
||||
|
||||
export const navigateToAlertsList = (urlQueryParams: string = '') => {
|
||||
cy.visit(`${APP_ALERTS_PATH}${urlQueryParams ? `?${urlQueryParams}` : ''}`);
|
||||
loadPage(`${APP_ALERTS_PATH}${urlQueryParams ? `?${urlQueryParams}` : ''}`);
|
||||
};
|
||||
|
||||
export const clickAlertListRefreshButton = (): Cypress.Chainable => {
|
||||
|
|
|
@ -6,22 +6,17 @@
|
|||
*/
|
||||
|
||||
import { APP_PATH } from '../../../../common/constants';
|
||||
import { getEndpointDetailsPath, getEndpointListPath } from '../../common/routing';
|
||||
import { getEndpointDetailsPath } from '../../common/routing';
|
||||
import { loadPage } from '../tasks/common';
|
||||
|
||||
export const AGENT_HOSTNAME_CELL = 'hostnameCellLink';
|
||||
export const AGENT_POLICY_CELL = 'policyNameCellLink';
|
||||
export const TABLE_ROW_ACTIONS = 'endpointTableRowActions';
|
||||
export const TABLE_ROW_ACTIONS_MENU = 'tableRowActionsMenuPanel';
|
||||
|
||||
export const navigateToEndpointPolicyResponse = (
|
||||
endpointAgentId: string
|
||||
): Cypress.Chainable<Cypress.AUTWindow> => {
|
||||
return cy.visit(
|
||||
export const navigateToEndpointPolicyResponse = (endpointAgentId: string): void => {
|
||||
loadPage(
|
||||
APP_PATH +
|
||||
getEndpointDetailsPath({ name: 'endpointPolicyResponse', selected_endpoint: endpointAgentId })
|
||||
);
|
||||
};
|
||||
|
||||
export const navigateToEndpointList = (): Cypress.Chainable<Cypress.AUTWindow> => {
|
||||
return cy.visit(APP_PATH + getEndpointListPath({ name: 'endpointList' }));
|
||||
};
|
||||
|
|
|
@ -6,18 +6,15 @@
|
|||
*/
|
||||
|
||||
import { FLEET_BASE_PATH } from '@kbn/fleet-plugin/public/constants';
|
||||
import { loadPage } from '../tasks/common';
|
||||
|
||||
export const FLEET_REASSIGN_POLICY_MODAL = 'agentReassignPolicyModal';
|
||||
export const FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON = 'confirmModalConfirmButton';
|
||||
|
||||
export const navigateToFleetAgentDetails = (
|
||||
agentId: string
|
||||
): Cypress.Chainable<Cypress.AUTWindow> => {
|
||||
export const navigateToFleetAgentDetails = (agentId: string): void => {
|
||||
// FYI: attempted to use fleet's `pagePathGetters()`, but got compile
|
||||
// errors due to it pulling too many modules
|
||||
const response = cy.visit(`${FLEET_BASE_PATH}/agents/${agentId}`);
|
||||
loadPage(`${FLEET_BASE_PATH}/agents/${agentId}`);
|
||||
|
||||
cy.getByTestSubj('agentPolicyNameLink').should('be.visible');
|
||||
|
||||
return response;
|
||||
};
|
||||
|
|
|
@ -15,11 +15,11 @@ import type {
|
|||
import { packagePolicyRouteService } from '@kbn/fleet-plugin/common';
|
||||
import { APP_POLICIES_PATH } from '../../../../common/constants';
|
||||
import type { PolicyConfig } from '../../../../common/endpoint/types';
|
||||
import { request } from '../tasks/common';
|
||||
import { request, loadPage } from '../tasks/common';
|
||||
import { expectAndCloseSuccessToast } from '../tasks/toasts';
|
||||
|
||||
export const visitPolicyDetailsPage = () => {
|
||||
cy.visit(APP_POLICIES_PATH);
|
||||
loadPage(APP_POLICIES_PATH);
|
||||
|
||||
cy.getByTestSubj('policyNameCellLink').eq(0).click({ force: true });
|
||||
cy.getByTestSubj('policyDetailsPage').should('exist');
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
import type { CasePostRequest } from '@kbn/cases-plugin/common/api';
|
||||
import execa from 'execa';
|
||||
import { startRuntimeServices } from '../../../../scripts/endpoint/endpoint_agent_runner/runtime';
|
||||
import { runFleetServerIfNeeded } from '../../../../scripts/endpoint/endpoint_agent_runner/fleet_server';
|
||||
import {
|
||||
sendEndpointActionResponse,
|
||||
sendFleetActionResponse,
|
||||
|
@ -50,8 +52,8 @@ import {
|
|||
startEndpointHost,
|
||||
createAndEnrollEndpointHost,
|
||||
destroyEndpointHost,
|
||||
getEndpointHosts,
|
||||
stopEndpointHost,
|
||||
VAGRANT_CWD,
|
||||
} from '../../../../scripts/endpoint/common/endpoint_host_services';
|
||||
|
||||
/**
|
||||
|
@ -204,6 +206,8 @@ export const dataLoadersForRealEndpoints = (
|
|||
on: Cypress.PluginEvents,
|
||||
config: Cypress.PluginConfigOptions
|
||||
): void => {
|
||||
let fleetServerContainerId: string | undefined;
|
||||
|
||||
const stackServicesPromise = createRuntimeServices({
|
||||
kibanaUrl: config.env.KIBANA_URL,
|
||||
elasticsearchUrl: config.env.ELASTICSEARCH_URL,
|
||||
|
@ -213,6 +217,25 @@ export const dataLoadersForRealEndpoints = (
|
|||
asSuperuser: true,
|
||||
});
|
||||
|
||||
on('before:run', async () => {
|
||||
await startRuntimeServices({
|
||||
kibanaUrl: config.env.KIBANA_URL,
|
||||
elasticUrl: config.env.ELASTICSEARCH_URL,
|
||||
fleetServerUrl: config.env.FLEET_SERVER_URL,
|
||||
username: config.env.ELASTICSEARCH_USERNAME,
|
||||
password: config.env.ELASTICSEARCH_PASSWORD,
|
||||
asSuperuser: true,
|
||||
});
|
||||
const data = await runFleetServerIfNeeded();
|
||||
fleetServerContainerId = data?.fleetServerContainerId;
|
||||
});
|
||||
|
||||
on('after:run', () => {
|
||||
if (fleetServerContainerId) {
|
||||
execa.sync('docker', ['kill', fleetServerContainerId]);
|
||||
}
|
||||
});
|
||||
|
||||
on('task', {
|
||||
createEndpointHost: async (
|
||||
options: Omit<CreateAndEnrollEndpointHostOptions, 'log' | 'kbnClient'>
|
||||
|
@ -224,7 +247,7 @@ export const dataLoadersForRealEndpoints = (
|
|||
log,
|
||||
kbnClient,
|
||||
}).then((newHost) => {
|
||||
return waitForEndpointToStreamData(kbnClient, newHost.agentId, 120000).then(() => {
|
||||
return waitForEndpointToStreamData(kbnClient, newHost.agentId, 360000).then(() => {
|
||||
return newHost;
|
||||
});
|
||||
});
|
||||
|
@ -246,7 +269,15 @@ export const dataLoadersForRealEndpoints = (
|
|||
path: string;
|
||||
content: string;
|
||||
}): Promise<null> => {
|
||||
await execa(`multipass`, ['exec', hostname, '--', 'sh', '-c', `echo ${content} > ${path}`]);
|
||||
if (process.env.CI) {
|
||||
await execa('vagrant', ['ssh', '--', `echo ${content} > ${path}`], {
|
||||
env: {
|
||||
VAGRANT_CWD,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await execa(`multipass`, ['exec', hostname, '--', 'sh', '-c', `echo ${content} > ${path}`]);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
|
@ -259,7 +290,16 @@ export const dataLoadersForRealEndpoints = (
|
|||
srcPath: string;
|
||||
destPath: string;
|
||||
}): Promise<null> => {
|
||||
await execa(`multipass`, ['transfer', srcPath, `${hostname}:${destPath}`]);
|
||||
if (process.env.CI) {
|
||||
await execa('vagrant', ['upload', srcPath, destPath], {
|
||||
env: {
|
||||
VAGRANT_CWD,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await execa(`multipass`, ['transfer', srcPath, `${hostname}:${destPath}`]);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
|
@ -290,26 +330,37 @@ export const dataLoadersForRealEndpoints = (
|
|||
path: string;
|
||||
password?: string;
|
||||
}): Promise<string> => {
|
||||
const result = await execa(`multipass`, [
|
||||
'exec',
|
||||
hostname,
|
||||
'--',
|
||||
'sh',
|
||||
'-c',
|
||||
`unzip -p ${password ? `-P ${password} ` : ''}${path}`,
|
||||
]);
|
||||
let result;
|
||||
|
||||
if (process.env.CI) {
|
||||
result = await execa(
|
||||
`vagrant`,
|
||||
['ssh', '--', `unzip -p ${password ? `-P ${password} ` : ''}${path}`],
|
||||
{
|
||||
env: {
|
||||
VAGRANT_CWD,
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
result = await execa(`multipass`, [
|
||||
'exec',
|
||||
hostname,
|
||||
'--',
|
||||
'sh',
|
||||
'-c',
|
||||
`unzip -p ${password ? `-P ${password} ` : ''}${path}`,
|
||||
]);
|
||||
}
|
||||
|
||||
return result.stdout;
|
||||
},
|
||||
|
||||
stopEndpointHost: async () => {
|
||||
const hosts = await getEndpointHosts();
|
||||
const hostName = hosts[0].name;
|
||||
stopEndpointHost: async (hostName) => {
|
||||
return stopEndpointHost(hostName);
|
||||
},
|
||||
|
||||
startEndpointHost: async () => {
|
||||
const hosts = await getEndpointHosts();
|
||||
const hostName = hosts[0].name;
|
||||
startEndpointHost: async (hostName) => {
|
||||
return startEndpointHost(hostName);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ export const cleanupRule = (id: string) => {
|
|||
request({ method: 'DELETE', url: `/api/detection_engine/rules?id=${id}` });
|
||||
};
|
||||
|
||||
export const loadRule = (includeResponseActions = true) =>
|
||||
export const loadRule = (body = {}, includeResponseActions = true) =>
|
||||
request<RuleResponse>({
|
||||
method: 'POST',
|
||||
url: `/api/detection_engine/rules`,
|
||||
|
@ -55,6 +55,7 @@ export const loadRule = (includeResponseActions = true) =>
|
|||
actions: [],
|
||||
enabled: true,
|
||||
throttle: 'no_actions',
|
||||
...body,
|
||||
...(includeResponseActions
|
||||
? {
|
||||
response_actions: [
|
||||
|
|
|
@ -5,20 +5,29 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export const ENDPOINT_VM_NAME = 'ENDPOINT_VM_NAME';
|
||||
|
||||
export const API_AUTH = Object.freeze({
|
||||
user: Cypress.env('ELASTICSEARCH_USERNAME'),
|
||||
pass: Cypress.env('ELASTICSEARCH_PASSWORD'),
|
||||
});
|
||||
|
||||
export const API_HEADERS = Object.freeze({ 'kbn-xsrf': 'cypress' });
|
||||
export const COMMON_API_HEADERS = { 'kbn-xsrf': 'cypress' };
|
||||
|
||||
export const request = <T = unknown>(
|
||||
options: Partial<Cypress.RequestOptions>
|
||||
): Cypress.Chainable<Cypress.Response<T>> =>
|
||||
export const waitForPageToBeLoaded = () => {
|
||||
cy.getByTestSubj('globalLoadingIndicator-hidden').should('exist');
|
||||
cy.getByTestSubj('globalLoadingIndicator').should('not.exist');
|
||||
};
|
||||
|
||||
export const loadPage = (url: string, options: Partial<Cypress.VisitOptions> = {}) => {
|
||||
cy.visit(url, options);
|
||||
waitForPageToBeLoaded();
|
||||
};
|
||||
|
||||
export const request = <T = unknown>({
|
||||
headers,
|
||||
...options
|
||||
}: Partial<Cypress.RequestOptions>): Cypress.Chainable<Cypress.Response<T>> =>
|
||||
cy.request<T>({
|
||||
auth: API_AUTH,
|
||||
headers: API_HEADERS,
|
||||
headers: Object.freeze({ ...COMMON_API_HEADERS, ...headers }),
|
||||
...options,
|
||||
});
|
||||
|
|
|
@ -17,6 +17,6 @@ export const createEndpointHost = (
|
|||
{
|
||||
agentPolicyId,
|
||||
},
|
||||
{ timeout: timeout ?? 180000 }
|
||||
{ timeout: timeout ?? 600000 }
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { ActionDetails } from '../../../../common/endpoint/types';
|
||||
import { loadPage } from './common';
|
||||
|
||||
const API_ENDPOINT_ACTION_PATH = '/api/endpoint/action/*';
|
||||
export const interceptActionRequests = (
|
||||
|
@ -70,9 +71,10 @@ export const waitForReleaseOption = (alertId: string): void => {
|
|||
};
|
||||
|
||||
export const visitRuleAlerts = (ruleName: string) => {
|
||||
cy.visit('/app/security/rules');
|
||||
loadPage('/app/security/rules');
|
||||
cy.contains(ruleName).click();
|
||||
};
|
||||
|
||||
export const checkFlyoutEndpointIsolation = (): void => {
|
||||
cy.getByTestSubj('event-field-agent.status').then(($status) => {
|
||||
if ($status.find('[title="Isolated"]').length > 0) {
|
||||
|
@ -90,7 +92,7 @@ export const checkFlyoutEndpointIsolation = (): void => {
|
|||
};
|
||||
|
||||
export const toggleRuleOffAndOn = (ruleName: string): void => {
|
||||
cy.visit('/app/security/rules');
|
||||
loadPage('/app/security/rules');
|
||||
cy.wait(2000);
|
||||
cy.contains(ruleName)
|
||||
.parents('tr')
|
||||
|
@ -105,7 +107,8 @@ export const toggleRuleOffAndOn = (ruleName: string): void => {
|
|||
|
||||
export const filterOutEndpoints = (endpointHostname: string): void => {
|
||||
cy.getByTestSubj('filters-global-container').within(() => {
|
||||
cy.getByTestSubj('queryInput').click().type(`host.hostname : "${endpointHostname}"`);
|
||||
cy.getByTestSubj('queryInput').click();
|
||||
cy.getByTestSubj('queryInput').type(`host.name: ${endpointHostname}`);
|
||||
cy.getByTestSubj('querySubmitButton').click();
|
||||
});
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ import Url from 'url';
|
|||
import type { Role } from '@kbn/security-plugin/common';
|
||||
import { getWithResponseActionsRole } from '../../../../scripts/endpoint/common/roles_users/with_response_actions_role';
|
||||
import { getNoResponseActionsRole } from '../../../../scripts/endpoint/common/roles_users/without_response_actions_role';
|
||||
import { request } from './common';
|
||||
import { request, loadPage } from './common';
|
||||
import { getT1Analyst } from '../../../../scripts/endpoint/common/roles_users/t1_analyst';
|
||||
import { getT2Analyst } from '../../../../scripts/endpoint/common/roles_users/t2_analyst';
|
||||
import { getHunter } from '../../../../scripts/endpoint/common/roles_users/hunter';
|
||||
|
@ -342,7 +342,7 @@ export const getEnvAuth = (): User => {
|
|||
*/
|
||||
export const loginAndWaitForPage = (url: string) => {
|
||||
login();
|
||||
cy.visit(url);
|
||||
loadPage(url);
|
||||
};
|
||||
|
||||
export const getRoleWithArtifactReadPrivilege = (privilegePrefix: string) => {
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
*/
|
||||
|
||||
import { TOGGLE_NAVIGATION_BTN } from '../screens/navigation';
|
||||
import { loadPage } from './common';
|
||||
|
||||
export const INTEGRATIONS = 'app/integrations#/';
|
||||
export const FLEET = 'app/fleet/';
|
||||
export const FLEET_AGENT_POLICIES = 'app/fleet/policies';
|
||||
export const navigateTo = (page: string, opts?: Partial<Cypress.VisitOptions>) => {
|
||||
cy.visit(page, opts);
|
||||
loadPage(page, opts);
|
||||
cy.contains('Loading Elastic').should('exist');
|
||||
cy.contains('Loading Elastic').should('not.exist');
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { request } from './common';
|
||||
import { request, loadPage } from './common';
|
||||
import { resolvePathVariables } from '../../../common/utils/resolve_path_variables';
|
||||
import { ACTION_DETAILS_ROUTE } from '../../../../common/endpoint/constants';
|
||||
import type { ActionDetails, ActionDetailsApiResponse } from '../../../../common/endpoint/types';
|
||||
|
@ -32,7 +32,7 @@ export const focusAndOpenCommandDropdown = (number = 0) => {
|
|||
});
|
||||
};
|
||||
export const fillUpNewRule = (name = 'Test', description = 'Test') => {
|
||||
cy.visit('app/security/rules/management');
|
||||
loadPage('app/security/rules/management');
|
||||
cy.getByTestSubj('create-new-rule').click();
|
||||
cy.getByTestSubj('stepDefineRule').within(() => {
|
||||
cy.getByTestSubj('queryInput').first().type('_id:*{enter}');
|
||||
|
@ -48,7 +48,7 @@ export const fillUpNewRule = (name = 'Test', description = 'Test') => {
|
|||
cy.getByTestSubj('schedule-continue').click();
|
||||
};
|
||||
export const visitRuleActions = (ruleId: string) => {
|
||||
cy.visit(`app/security/rules/id/${ruleId}/edit`);
|
||||
loadPage(`app/security/rules/id/${ruleId}/edit`);
|
||||
cy.getByTestSubj('edit-rule-actions-tab').should('exist');
|
||||
cy.getByTestSubj('globalLoadingIndicator').should('not.exist');
|
||||
cy.getByTestSubj('stepPanelProgress').should('not.exist');
|
||||
|
@ -74,7 +74,7 @@ export const tryAddingDisabledResponseAction = (itemNumber = 0) => {
|
|||
*/
|
||||
export const waitForActionToComplete = (
|
||||
actionId: string,
|
||||
timeout = 60000
|
||||
timeout = 120000
|
||||
): Cypress.Chainable<ActionDetails> => {
|
||||
let action: ActionDetails | undefined;
|
||||
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
import type { ConsoleResponseActionCommands } from '../../../../common/endpoint/service/response_actions/constants';
|
||||
import { closeAllToasts } from './toasts';
|
||||
import { APP_ENDPOINTS_PATH } from '../../../../common/constants';
|
||||
import { loadPage } from './common';
|
||||
import Chainable = Cypress.Chainable;
|
||||
|
||||
export const waitForEndpointListPageToBeLoaded = (endpointHostname: string): void => {
|
||||
cy.visit(APP_ENDPOINTS_PATH);
|
||||
loadPage(APP_ENDPOINTS_PATH);
|
||||
closeAllToasts();
|
||||
cy.contains(endpointHostname).should('exist');
|
||||
};
|
||||
|
|
|
@ -37,6 +37,8 @@ export default defineCypressConfig({
|
|||
},
|
||||
|
||||
e2e: {
|
||||
experimentalMemoryManagement: true,
|
||||
experimentalInteractiveRunEvents: true,
|
||||
baseUrl: 'http://localhost:5620',
|
||||
supportFile: 'public/management/cypress/support/e2e.ts',
|
||||
specPattern: 'public/management/cypress/e2e/endpoint/*.cy.{js,jsx,ts,tsx}',
|
||||
|
|
|
@ -20,6 +20,8 @@ import {
|
|||
waitForHostToEnroll,
|
||||
} from './fleet_services';
|
||||
|
||||
export const VAGRANT_CWD = `${__dirname}/../endpoint_agent_runner/`;
|
||||
|
||||
export interface CreateAndEnrollEndpointHostOptions
|
||||
extends Pick<CreateMultipassVmOptions, 'disk' | 'cpus' | 'memory'> {
|
||||
kbnClient: KbnClient;
|
||||
|
@ -60,38 +62,48 @@ export const createAndEnrollEndpointHost = async ({
|
|||
deleted: [],
|
||||
});
|
||||
|
||||
const [vm, agentDownload, fleetServerUrl, enrollmentToken] = await Promise.all([
|
||||
createMultipassVm({
|
||||
vmName: hostname ?? `test-host-${Math.random().toString().substring(2, 6)}`,
|
||||
disk,
|
||||
cpus,
|
||||
memory,
|
||||
}),
|
||||
const vmName = hostname ?? `test-host-${Math.random().toString().substring(2, 6)}`;
|
||||
|
||||
getAgentDownloadUrl(version, useClosestVersionMatch, log).then<{
|
||||
url: string;
|
||||
cache?: DownloadedAgentInfo;
|
||||
}>((url) => {
|
||||
if (useCache) {
|
||||
cacheCleanupPromise = cleanupDownloads();
|
||||
const agentDownload = await getAgentDownloadUrl(version, useClosestVersionMatch, log).then<{
|
||||
url: string;
|
||||
cache?: DownloadedAgentInfo;
|
||||
}>((url) => {
|
||||
if (useCache) {
|
||||
cacheCleanupPromise = cleanupDownloads();
|
||||
|
||||
return downloadAndStoreAgent(url).then((cache) => {
|
||||
return {
|
||||
url,
|
||||
cache,
|
||||
};
|
||||
});
|
||||
}
|
||||
return downloadAndStoreAgent(url).then((cache) => {
|
||||
return {
|
||||
url,
|
||||
cache,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return { url };
|
||||
}),
|
||||
return { url };
|
||||
});
|
||||
|
||||
const [vm, fleetServerUrl, enrollmentToken] = await Promise.all([
|
||||
process.env.CI
|
||||
? createVagrantVm({
|
||||
vmName,
|
||||
log,
|
||||
cachedAgentDownload: agentDownload.cache as DownloadedAgentInfo,
|
||||
})
|
||||
: createMultipassVm({
|
||||
vmName,
|
||||
disk,
|
||||
cpus,
|
||||
memory,
|
||||
}),
|
||||
|
||||
fetchFleetServerUrl(kbnClient),
|
||||
|
||||
fetchAgentPolicyEnrollmentKey(kbnClient, agentPolicyId),
|
||||
]);
|
||||
|
||||
log.verbose(await execa('multipass', ['info', vm.vmName]));
|
||||
if (!process.env.CI) {
|
||||
log.verbose(await execa('multipass', ['info', vm.vmName]));
|
||||
}
|
||||
|
||||
// Some validations before we proceed
|
||||
assert(agentDownload.url, 'Missing agent download URL');
|
||||
|
@ -143,6 +155,54 @@ export const destroyEndpointHost = async (
|
|||
]);
|
||||
};
|
||||
|
||||
interface CreateVmResponse {
|
||||
vmName: string;
|
||||
}
|
||||
|
||||
interface CreateVagrantVmOptions {
|
||||
vmName: string;
|
||||
cachedAgentDownload: DownloadedAgentInfo;
|
||||
log: ToolingLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new VM using `vagrant`
|
||||
*/
|
||||
const createVagrantVm = async ({
|
||||
vmName,
|
||||
cachedAgentDownload,
|
||||
log,
|
||||
}: CreateVagrantVmOptions): Promise<CreateVmResponse> => {
|
||||
try {
|
||||
await execa.command(`vagrant destroy -f`, {
|
||||
env: {
|
||||
VAGRANT_CWD,
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
await execa.command(`vagrant up`, {
|
||||
env: {
|
||||
VAGRANT_DISABLE_VBOXSYMLINKCREATE: '1',
|
||||
VAGRANT_CWD,
|
||||
VMNAME: vmName,
|
||||
CACHED_AGENT_SOURCE: cachedAgentDownload.fullFilePath,
|
||||
CACHED_AGENT_FILENAME: cachedAgentDownload.filename,
|
||||
},
|
||||
stdio: ['inherit', 'inherit', 'inherit'],
|
||||
});
|
||||
} catch (e) {
|
||||
log.error(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
return {
|
||||
vmName,
|
||||
};
|
||||
};
|
||||
|
||||
interface CreateMultipassVmOptions {
|
||||
vmName: string;
|
||||
/** Number of CPUs */
|
||||
|
@ -153,10 +213,6 @@ interface CreateMultipassVmOptions {
|
|||
memory?: string;
|
||||
}
|
||||
|
||||
interface CreateMultipassVmResponse {
|
||||
vmName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new VM using `multipass`
|
||||
*/
|
||||
|
@ -165,7 +221,7 @@ const createMultipassVm = async ({
|
|||
disk = '8G',
|
||||
cpus = 1,
|
||||
memory = '1G',
|
||||
}: CreateMultipassVmOptions): Promise<CreateMultipassVmResponse> => {
|
||||
}: CreateMultipassVmOptions): Promise<CreateVmResponse> => {
|
||||
await execa.command(
|
||||
`multipass launch --name ${vmName} --disk ${disk} --cpus ${cpus} --memory ${memory}`
|
||||
);
|
||||
|
@ -176,7 +232,15 @@ const createMultipassVm = async ({
|
|||
};
|
||||
|
||||
const deleteMultipassVm = async (vmName: string): Promise<void> => {
|
||||
await execa.command(`multipass delete -p ${vmName}`);
|
||||
if (process.env.CI) {
|
||||
await execa.command(`vagrant destroy -f`, {
|
||||
env: {
|
||||
VAGRANT_CWD,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await execa.command(`multipass delete -p ${vmName}`);
|
||||
}
|
||||
};
|
||||
|
||||
interface EnrollHostWithFleetOptions {
|
||||
|
@ -206,35 +270,30 @@ const enrollHostWithFleet = async ({
|
|||
`Installing agent on host using cached download from [${cachedAgentDownload.fullFilePath}]`
|
||||
);
|
||||
|
||||
// mount local folder on VM
|
||||
await execa.command(
|
||||
`multipass mount ${cachedAgentDownload.directory} ${vmName}:~/_agent_downloads`
|
||||
);
|
||||
await execa.command(
|
||||
`multipass exec ${vmName} -- tar -zxf _agent_downloads/${cachedAgentDownload.filename}`
|
||||
);
|
||||
await execa.command(`multipass unmount ${vmName}:~/_agent_downloads`);
|
||||
if (!process.env.CI) {
|
||||
// mount local folder on VM
|
||||
await execa.command(
|
||||
`multipass mount ${cachedAgentDownload.directory} ${vmName}:~/_agent_downloads`
|
||||
);
|
||||
await execa.command(
|
||||
`multipass exec ${vmName} -- tar -zxf _agent_downloads/${cachedAgentDownload.filename}`
|
||||
);
|
||||
await execa.command(`multipass unmount ${vmName}:~/_agent_downloads`);
|
||||
}
|
||||
} else {
|
||||
log.verbose(`downloading and installing agent from URL [${agentDownloadUrl}]`);
|
||||
|
||||
// download into VM
|
||||
await execa.command(
|
||||
`multipass exec ${vmName} -- curl -L ${agentDownloadUrl} -o ${agentDownloadedFile}`
|
||||
);
|
||||
await execa.command(`multipass exec ${vmName} -- tar -zxf ${agentDownloadedFile}`);
|
||||
await execa.command(`multipass exec ${vmName} -- rm -f ${agentDownloadedFile}`);
|
||||
if (!process.env.CI) {
|
||||
// download into VM
|
||||
await execa.command(
|
||||
`multipass exec ${vmName} -- curl -L ${agentDownloadUrl} -o ${agentDownloadedFile}`
|
||||
);
|
||||
await execa.command(`multipass exec ${vmName} -- tar -zxf ${agentDownloadedFile}`);
|
||||
await execa.command(`multipass exec ${vmName} -- rm -f ${agentDownloadedFile}`);
|
||||
}
|
||||
}
|
||||
|
||||
const agentInstallArguments = [
|
||||
'exec',
|
||||
|
||||
vmName,
|
||||
|
||||
'--working-directory',
|
||||
`/home/ubuntu/${vmDirName}`,
|
||||
|
||||
'--',
|
||||
|
||||
'sudo',
|
||||
|
||||
'./elastic-agent',
|
||||
|
@ -253,10 +312,28 @@ const enrollHostWithFleet = async ({
|
|||
];
|
||||
|
||||
log.info(`Enrolling elastic agent with Fleet`);
|
||||
log.verbose(`Command: multipass ${agentInstallArguments.join(' ')}`);
|
||||
if (process.env.CI) {
|
||||
log.verbose(`Command: vagrant ${agentInstallArguments.join(' ')}`);
|
||||
|
||||
await execa(`multipass`, agentInstallArguments);
|
||||
await execa(`vagrant`, ['ssh', '--', `cd ${vmDirName} && ${agentInstallArguments.join(' ')}`], {
|
||||
env: {
|
||||
VAGRANT_CWD,
|
||||
},
|
||||
stdio: ['inherit', 'inherit', 'inherit'],
|
||||
});
|
||||
} else {
|
||||
log.verbose(`Command: multipass ${agentInstallArguments.join(' ')}`);
|
||||
|
||||
await execa(`multipass`, [
|
||||
'exec',
|
||||
vmName,
|
||||
'--working-directory',
|
||||
`/home/ubuntu/${vmDirName}`,
|
||||
|
||||
'--',
|
||||
...agentInstallArguments,
|
||||
]);
|
||||
}
|
||||
log.info(`Waiting for Agent to check-in with Fleet`);
|
||||
const agent = await waitForHostToEnroll(kbnClient, vmName, 120000);
|
||||
|
||||
|
@ -273,9 +350,24 @@ export async function getEndpointHosts(): Promise<
|
|||
}
|
||||
|
||||
export function stopEndpointHost(hostName: string) {
|
||||
if (process.env.CI) {
|
||||
return execa('vagrant', ['suspend'], {
|
||||
env: {
|
||||
VAGRANT_CWD,
|
||||
VMNAME: hostName,
|
||||
},
|
||||
});
|
||||
}
|
||||
return execa('multipass', ['stop', hostName]);
|
||||
}
|
||||
|
||||
export function startEndpointHost(hostName: string) {
|
||||
if (process.env.CI) {
|
||||
return execa('vagrant', ['up'], {
|
||||
env: {
|
||||
VAGRANT_CWD,
|
||||
},
|
||||
});
|
||||
}
|
||||
return execa('multipass', ['start', hostName]);
|
||||
}
|
||||
|
|
29
x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/Vagrantfile
vendored
Normal file
29
x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/Vagrantfile
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
hostname = ENV["VMNAME"] || 'ubuntu'
|
||||
cachedAgentSource = ENV["CACHED_AGENT_SOURCE"] || ''
|
||||
cachedAgentFilename = ENV["CACHED_AGENT_FILENAME"] || ''
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.hostname = hostname
|
||||
config.vm.box = 'ubuntu/jammy64'
|
||||
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.memory = 4096
|
||||
vb.cpus = 2
|
||||
end
|
||||
|
||||
config.vm.provider :vmware_desktop do |v, override|
|
||||
override.vm.box = "starboard/ubuntu-arm64-20.04.5"
|
||||
override.vm.box_version = "20221120.20.40.0"
|
||||
override.vm.box_download_insecure = true
|
||||
override.vm.network "private_network", type: "dhcp"
|
||||
|
||||
v.ssh_info_public = true
|
||||
v.gui = true
|
||||
v.linked_clone = false
|
||||
v.vmx["ethernet0.virtualdev"] = "vmxnet3"
|
||||
end
|
||||
|
||||
config.vm.provision "file", source: cachedAgentSource, destination: "~/#{cachedAgentFilename}"
|
||||
config.vm.provision "shell", inline: "tar -zxf #{cachedAgentFilename} && rm -f #{cachedAgentFilename}"
|
||||
config.vm.provision "shell", inline: "sudo apt-get install unzip"
|
||||
end
|
|
@ -60,13 +60,23 @@ export const enrollEndpointHost = async (): Promise<string | undefined> => {
|
|||
disk: '8G',
|
||||
});
|
||||
|
||||
log.info(`VM created using Multipass.
|
||||
VM Name: ${vmName}
|
||||
Elastic Agent Version: ${version}
|
||||
if (process.env.CI) {
|
||||
log.info(`VM created using Vagrant.
|
||||
VM Name: ${vmName}
|
||||
Elastic Agent Version: ${version}
|
||||
|
||||
Shell access: ${chalk.bold(`multipass shell ${vmName}`)}
|
||||
Delete VM: ${chalk.bold(`multipass delete -p ${vmName}${await getVmCountNotice()}`)}
|
||||
`);
|
||||
Shell access: ${chalk.bold(`vagrant ssh ${vmName}`)}
|
||||
Delete VM: ${chalk.bold(`vagrant destroy ${vmName} -f`)}
|
||||
`);
|
||||
} else {
|
||||
log.info(`VM created using Multipass.
|
||||
VM Name: ${vmName}
|
||||
Elastic Agent Version: ${version}
|
||||
|
||||
Shell access: ${chalk.bold(`multipass shell ${vmName}`)}
|
||||
Delete VM: ${chalk.bold(`multipass delete -p ${vmName}${await getVmCountNotice()}`)}
|
||||
`);
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(dump(error));
|
||||
log.indent(-4);
|
||||
|
|
|
@ -50,22 +50,11 @@ export const runFleetServerIfNeeded = async (): Promise<
|
|||
const {
|
||||
log,
|
||||
kibana: { isLocalhost: isKibanaOnLocalhost },
|
||||
kbnClient,
|
||||
} = getRuntimeServices();
|
||||
|
||||
log.info(`Setting up fleet server (if necessary)`);
|
||||
log.indent(4);
|
||||
|
||||
const currentFleetServerUrl = await fetchFleetServerUrl(kbnClient);
|
||||
|
||||
if (currentFleetServerUrl) {
|
||||
log.info(
|
||||
`Fleet server is already enrolled with Fleet - URL:\n${currentFleetServerUrl}\nNothing to do.`
|
||||
);
|
||||
log.indent(-4);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
fleetServerAgentPolicyId = await getOrCreateFleetServerAgentPolicyId();
|
||||
const serviceToken = await generateFleetServiceToken();
|
||||
|
|
|
@ -37,6 +37,7 @@ export const startRuntimeServices = async ({
|
|||
username,
|
||||
password,
|
||||
log,
|
||||
asSuperuser: otherOptions?.asSuperuser,
|
||||
});
|
||||
|
||||
runtimeServices = {
|
||||
|
|
|
@ -16,4 +16,5 @@ export interface StartRuntimeServicesOptions {
|
|||
version?: string;
|
||||
policy?: string;
|
||||
log?: ToolingLog;
|
||||
asSuperuser?: boolean;
|
||||
}
|
||||
|
|
|
@ -99,6 +99,10 @@ export const cli = () => {
|
|||
};
|
||||
|
||||
const getKibanaPort = <T>(): T | number => {
|
||||
if (isOpen) {
|
||||
return 5620;
|
||||
}
|
||||
|
||||
const kibanaPort = parseInt(`56${Math.floor(Math.random() * 89) + 10}`, 10);
|
||||
if (kibanaPorts.includes(kibanaPort)) {
|
||||
return getKibanaPort();
|
||||
|
@ -108,6 +112,10 @@ export const cli = () => {
|
|||
};
|
||||
|
||||
const getFleetServerPort = <T>(): T | number => {
|
||||
if (isOpen) {
|
||||
return 8220;
|
||||
}
|
||||
|
||||
const fleetServerPort = parseInt(`82${Math.floor(Math.random() * 89) + 10}`, 10);
|
||||
if (fleetServerPorts.includes(fleetServerPort)) {
|
||||
return getFleetServerPort();
|
||||
|
@ -378,7 +386,7 @@ export const cli = () => {
|
|||
}
|
||||
|
||||
await procs.stop('kibana');
|
||||
shutdownEs();
|
||||
await shutdownEs();
|
||||
cleanupServerPorts({ esPort, kibanaPort, fleetServerPort });
|
||||
|
||||
return result;
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* 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 execa from 'execa';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import { enrollEndpointHost } from '@kbn/security-solution-plugin/scripts/endpoint/endpoint_agent_runner/elastic_endpoint';
|
||||
import { Manager } from './resource_manager';
|
||||
|
||||
export class AgentManager extends Manager {
|
||||
private log: ToolingLog;
|
||||
private vmName?: string;
|
||||
constructor(log: ToolingLog) {
|
||||
super();
|
||||
this.log = log;
|
||||
this.vmName = undefined;
|
||||
}
|
||||
|
||||
public async setup() {
|
||||
this.vmName = await enrollEndpointHost();
|
||||
|
||||
return this.vmName;
|
||||
}
|
||||
|
||||
public cleanup() {
|
||||
super.cleanup();
|
||||
this.log.info('Cleaning up the agent process');
|
||||
if (this.vmName) {
|
||||
execa.commandSync(`multipass delete -p ${this.vmName}`);
|
||||
|
||||
this.log.info('Agent process closed');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,7 +49,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
'servers.elasticsearch.port'
|
||||
)}`,
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'endpointRbacEnabled',
|
||||
'endpointResponseActionsEnabled',
|
||||
])}`,
|
||||
],
|
||||
|
|
|
@ -9,7 +9,7 @@ import { getLocalhostRealIp } from '@kbn/security-solution-plugin/scripts/endpoi
|
|||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
import { ExperimentalFeatures } from '@kbn/security-solution-plugin/common/experimental_features';
|
||||
import { DefendWorkflowsCypressEndpointTestRunner } from './runner';
|
||||
import { DefendWorkflowsCypressCliTestRunner } from './runner';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const defendWorkflowsCypressConfig = await readConfigFile(require.resolve('./config.ts'));
|
||||
|
@ -43,6 +43,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
`--xpack.securitySolution.enableExperimental=${JSON.stringify(enabledFeatureFlags)}`,
|
||||
],
|
||||
},
|
||||
testRunner: DefendWorkflowsCypressEndpointTestRunner,
|
||||
testRunner: DefendWorkflowsCypressCliTestRunner,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* 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 execa from 'execa';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import { runFleetServerIfNeeded } from '@kbn/security-solution-plugin/scripts/endpoint/endpoint_agent_runner/fleet_server';
|
||||
import { Manager } from './resource_manager';
|
||||
|
||||
export class FleetManager extends Manager {
|
||||
private fleetContainerId?: string;
|
||||
private log: ToolingLog;
|
||||
|
||||
constructor(log: ToolingLog) {
|
||||
super();
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
public async setup(): Promise<void> {
|
||||
const fleetServerConfig = await runFleetServerIfNeeded();
|
||||
|
||||
if (!fleetServerConfig) {
|
||||
throw new Error('Fleet server config not found');
|
||||
}
|
||||
|
||||
this.fleetContainerId = fleetServerConfig.fleetServerContainerId;
|
||||
}
|
||||
|
||||
public cleanup() {
|
||||
super.cleanup();
|
||||
|
||||
this.log.info('Removing old fleet config');
|
||||
if (this.fleetContainerId) {
|
||||
this.log.info('Closing fleet process');
|
||||
|
||||
execa.sync('docker', ['kill', this.fleetContainerId]);
|
||||
this.log.info('Fleet server process closed');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const CLEANUP_EVENTS = ['SIGINT', 'exit', 'uncaughtException', 'unhandledRejection'];
|
||||
export class Manager {
|
||||
constructor() {
|
||||
const cleanup = () => this.cleanup();
|
||||
CLEANUP_EVENTS.forEach((ev) => process.on(ev, cleanup));
|
||||
}
|
||||
cleanup() {}
|
||||
}
|
|
@ -6,72 +6,9 @@
|
|||
*/
|
||||
|
||||
import Url from 'url';
|
||||
import { startRuntimeServices } from '@kbn/security-solution-plugin/scripts/endpoint/endpoint_agent_runner/runtime';
|
||||
import { FtrProviderContext } from './ftr_provider_context';
|
||||
import { AgentManager } from './agent';
|
||||
import { FleetManager } from './fleet_server';
|
||||
import { getLatestAvailableAgentVersion } from './utils';
|
||||
|
||||
type RunnerEnv = Record<string, string | undefined>;
|
||||
|
||||
async function withFleetAgent(
|
||||
{ getService }: FtrProviderContext,
|
||||
runner: (runnerEnv: RunnerEnv) => RunnerEnv
|
||||
) {
|
||||
const log = getService('log');
|
||||
const config = getService('config');
|
||||
const kbnClient = getService('kibanaServer');
|
||||
|
||||
const elasticUrl = Url.format(config.get('servers.elasticsearch'));
|
||||
const kibanaUrl = Url.format(config.get('servers.kibana'));
|
||||
const fleetServerUrl = config.get('servers.fleetserver')
|
||||
? Url.format(config.get('servers.fleetserver'))
|
||||
: undefined;
|
||||
const username = config.get('servers.elasticsearch.username');
|
||||
const password = config.get('servers.elasticsearch.password');
|
||||
|
||||
await startRuntimeServices({
|
||||
log,
|
||||
elasticUrl,
|
||||
kibanaUrl,
|
||||
fleetServerUrl,
|
||||
username,
|
||||
password,
|
||||
version: await getLatestAvailableAgentVersion(kbnClient),
|
||||
});
|
||||
|
||||
const fleetManager = new FleetManager(log);
|
||||
const agentManager = new AgentManager(log);
|
||||
|
||||
await fleetManager.setup();
|
||||
const agentVmName = await agentManager.setup();
|
||||
try {
|
||||
await runner({ agentVmName });
|
||||
} finally {
|
||||
agentManager.cleanup();
|
||||
fleetManager.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
export async function DefendWorkflowsCypressCliTestRunner(context: FtrProviderContext) {
|
||||
return startDefendWorkflowsCypress(context, 'dw:run');
|
||||
}
|
||||
|
||||
export async function DefendWorkflowsCypressVisualTestRunner(context: FtrProviderContext) {
|
||||
return startDefendWorkflowsCypress(context, 'dw:open');
|
||||
}
|
||||
|
||||
export async function DefendWorkflowsCypressEndpointTestRunner(context: FtrProviderContext) {
|
||||
return withFleetAgent(context, (runnerEnv) =>
|
||||
startDefendWorkflowsCypress(context, 'dw:endpoint:open', runnerEnv)
|
||||
);
|
||||
}
|
||||
|
||||
function startDefendWorkflowsCypress(
|
||||
context: FtrProviderContext,
|
||||
cypressCommand: 'dw:endpoint:open' | 'dw:open' | 'dw:run',
|
||||
runnerEnv?: RunnerEnv
|
||||
) {
|
||||
export function DefendWorkflowsCypressCliTestRunner(context: FtrProviderContext) {
|
||||
const config = context.getService('config');
|
||||
|
||||
return {
|
||||
|
@ -87,6 +24,5 @@ function startDefendWorkflowsCypress(
|
|||
hostname: config.get('servers.kibana.hostname'),
|
||||
port: config.get('servers.kibana.port'),
|
||||
}),
|
||||
ENDPOINT_VM_NAME: runnerEnv?.agentVmName,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* 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 axios from 'axios';
|
||||
import semver from 'semver';
|
||||
import { map } from 'lodash';
|
||||
import { KbnClient } from '@kbn/test';
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export const getLatestAvailableAgentVersion = async (kbnClient: KbnClient): Promise<string> => {
|
||||
const kbnStatus = await kbnClient.status.get();
|
||||
const agentVersions = await axios
|
||||
.get('https://artifacts-api.elastic.co/v1/versions')
|
||||
.then((response) => map(response.data.versions, (version) => version.split('-SNAPSHOT')[0]));
|
||||
|
||||
let version =
|
||||
semver.maxSatisfying(agentVersions, `<=${kbnStatus.version.number}`) ??
|
||||
kbnStatus.version.number;
|
||||
|
||||
// Add `-SNAPSHOT` if version indicates it was from a snapshot or the build hash starts
|
||||
// with `xxxxxxxxx` (value that seems to be present when running kibana from source)
|
||||
if (
|
||||
kbnStatus.version.build_snapshot ||
|
||||
kbnStatus.version.build_hash.startsWith('XXXXXXXXXXXXXXX')
|
||||
) {
|
||||
version += '-SNAPSHOT';
|
||||
}
|
||||
|
||||
return version;
|
||||
};
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* 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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
import { DefendWorkflowsCypressVisualTestRunner } from './runner';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const defendWorkflowsCypressConfig = await readConfigFile(require.resolve('./config.ts'));
|
||||
return {
|
||||
...defendWorkflowsCypressConfig.getAll(),
|
||||
|
||||
testRunner: DefendWorkflowsCypressVisualTestRunner,
|
||||
};
|
||||
}
|
|
@ -12,7 +12,7 @@ import { FtrProviderContext } from './ftr_provider_context';
|
|||
|
||||
import { AgentManager } from './agent';
|
||||
import { FleetManager } from './fleet_server';
|
||||
import { getLatestAvailableAgentVersion } from '../defend_workflows_cypress/utils';
|
||||
import { getLatestAvailableAgentVersion } from './utils';
|
||||
|
||||
async function setupFleetAgent({ getService }: FtrProviderContext) {
|
||||
const log = getService('log');
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import semver from 'semver';
|
||||
import { map } from 'lodash';
|
||||
import { PackagePolicy, CreatePackagePolicyResponse } from '@kbn/fleet-plugin/common';
|
||||
import { KbnClient } from '@kbn/test';
|
||||
|
||||
|
@ -48,3 +51,31 @@ export const addIntegrationToAgentPolicy = async (
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export const getLatestAvailableAgentVersion = async (kbnClient: KbnClient): Promise<string> => {
|
||||
const kbnStatus = await kbnClient.status.get();
|
||||
const agentVersions = await axios
|
||||
.get('https://artifacts-api.elastic.co/v1/versions')
|
||||
.then((response) => map(response.data.versions, (version) => version.split('-SNAPSHOT')[0]));
|
||||
|
||||
let version =
|
||||
semver.maxSatisfying(agentVersions, `<=${kbnStatus.version.number}`) ??
|
||||
kbnStatus.version.number;
|
||||
|
||||
// Add `-SNAPSHOT` if version indicates it was from a snapshot or the build hash starts
|
||||
// with `xxxxxxxxx` (value that seems to be present when running kibana from source)
|
||||
if (
|
||||
kbnStatus.version.build_snapshot ||
|
||||
kbnStatus.version.build_hash.startsWith('XXXXXXXXXXXXXXX')
|
||||
) {
|
||||
version += '-SNAPSHOT';
|
||||
}
|
||||
|
||||
return version;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue