[8.x] [Fleet] Add cypress test against space awareness (#195372) (#195873)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Fleet] Add cypress test against space awareness
(#195372)](https://github.com/elastic/kibana/pull/195372)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Nicolas
Chaulet","email":"nicolas.chaulet@elastic.co"},"sourceCommit":{"committedDate":"2024-10-11T07:19:21Z","message":"[Fleet]
Add cypress test against space awareness
(#195372)","sha":"5b697499978170937d8c0280b0cf184ee84b57ab","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Fleet","v9.0.0","backport:prev-minor","v8.16.0"],"number":195372,"url":"https://github.com/elastic/kibana/pull/195372","mergeCommit":{"message":"[Fleet]
Add cypress test against space awareness
(#195372)","sha":"5b697499978170937d8c0280b0cf184ee84b57ab"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/195372","number":195372,"mergeCommit":{"message":"[Fleet]
Add cypress test against space awareness
(#195372)","sha":"5b697499978170937d8c0280b0cf184ee84b57ab"}},{"branch":"8.x","label":"v8.16.0","labelRegex":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Nicolas Chaulet 2024-10-15 10:02:16 -04:00 committed by GitHub
parent c04b25b9ee
commit 89f85ab596
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 303 additions and 14 deletions

View file

@ -34,7 +34,9 @@ disabled:
# Cypress configs, for now these are still run manually
- x-pack/test/fleet_cypress/cli_config.ts
- x-pack/test/fleet_cypress/cli_config.space_awareness.ts
- x-pack/test/fleet_cypress/config.ts
- x-pack/test/fleet_cypress/config.space_awareness.ts
- x-pack/test/fleet_cypress/visual_config.ts
defaultQueue: 'n2-4-spot'

View file

@ -12,4 +12,4 @@ echo "--- Fleet Cypress tests (Chrome)"
cd x-pack/plugins/fleet
set +e
yarn cypress:run:reporter; status=$?; yarn junit:merge || :; exit $status
yarn cypress:run:reporter; status=$?; yarn cypress_space_awareness:run:reporter; space_status=$?; yarn junit:merge || :; [ "$status" -ne 0 ] && exit $status || [ "$space_status" -ne 0 ] && exit $space_status || exit 0

View file

@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { defineCypressConfig } from '@kbn/cypress-config';
// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
defaultCommandTimeout: 60000,
requestTimeout: 60000,
responseTimeout: 60000,
execTimeout: 120000,
pageLoadTimeout: 120000,
retries: {
runMode: 2,
},
env: {
grepFilterSpecs: false,
},
screenshotsFolder: '../../../target/kibana-fleet/cypress/screenshots',
trashAssetsBeforeRuns: false,
video: false,
videosFolder: '../../../target/kibana-fleet/cypress/videos',
viewportHeight: 900,
viewportWidth: 1440,
screenshotOnRunFailure: true,
e2e: {
baseUrl: 'http://localhost:5601',
experimentalRunAllSpecs: true,
experimentalMemoryManagement: true,
numTestsKeptInMemory: 3,
specPattern: './cypress/e2e/space_awareness/**/*.cy.ts',
supportFile: './cypress/support/e2e.ts',
setupNodeEvents(on, config) {
// eslint-disable-next-line @typescript-eslint/no-var-requires, @kbn/imports/no_boundary_crossing
return require('./cypress/plugins')(on, config);
},
},
});

View file

@ -39,6 +39,7 @@ export default defineCypressConfig({
specPattern: './cypress/e2e/**/*.cy.ts',
supportFile: './cypress/support/e2e.ts',
excludeSpecPattern: './cypress/e2e/space_awareness/**/*.cy.ts',
setupNodeEvents(on, config) {
// eslint-disable-next-line @typescript-eslint/no-var-requires, @kbn/imports/no_boundary_crossing

View file

@ -0,0 +1,75 @@
/*
* 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 {
ADD_AGENT_POLICY_BTN,
AGENT_POLICIES_TABLE,
AGENT_POLICY_CREATE_AGENT_POLICY_NAME_FIELD,
AGENT_POLICY_DETAILS_PAGE,
AGENT_POLICY_FLYOUT_CREATE_BUTTON,
AGENT_POLICY_SYSTEM_MONITORING_CHECKBOX,
} from '../../screens/fleet';
import { login } from '../../tasks/login';
import { createSpaces, enableSpaceAwareness } from '../../tasks/spaces';
import { cleanupAgentPolicies } from '../../tasks/cleanup';
describe('Space aware policies creation', { testIsolation: false }, () => {
before(() => {
enableSpaceAwareness();
createSpaces();
cleanupAgentPolicies();
cleanupAgentPolicies('test');
login();
});
beforeEach(() => {
cy.intercept('GET', /\/api\/fleet\/agent_policies/).as('getAgentPolicies');
cy.intercept('GET', /\/internal\/fleet\/agent_policies_spaces/).as('getAgentPoliciesSpaces');
});
const POLICY_NAME = `Policy 1 space test`;
const NO_AGENT_POLICIES = 'No agent policies';
it('should allow to create an agent policy in the test space', () => {
cy.visit('/s/test/app/fleet/policies');
cy.getBySel(ADD_AGENT_POLICY_BTN).click();
cy.getBySel(AGENT_POLICY_CREATE_AGENT_POLICY_NAME_FIELD).type(POLICY_NAME);
cy.getBySel(AGENT_POLICY_SYSTEM_MONITORING_CHECKBOX).uncheck();
cy.getBySel(AGENT_POLICY_FLYOUT_CREATE_BUTTON).click();
cy.getBySel(AGENT_POLICIES_TABLE).contains(POLICY_NAME);
});
it('the created policy should not be visible in the default space', () => {
cy.visit('/app/fleet/policies');
cy.wait('@getAgentPolicies');
cy.getBySel(AGENT_POLICIES_TABLE).contains(NO_AGENT_POLICIES);
});
it('should allow to update that policy to belong to both test and default space', () => {
cy.visit('/s/test/app/fleet/policies');
cy.getBySel(AGENT_POLICIES_TABLE).contains(POLICY_NAME).click();
cy.getBySel(AGENT_POLICY_DETAILS_PAGE.SETTINGS_TAB).click();
cy.wait('@getAgentPoliciesSpaces');
cy.getBySel(AGENT_POLICY_DETAILS_PAGE.SPACE_SELECTOR_COMBOBOX).click().type('default{enter}');
cy.getBySel(AGENT_POLICY_DETAILS_PAGE.SAVE_BUTTON).click();
});
it('the policy should be visible in the test space', () => {
cy.visit('/s/test/app/fleet/policies');
cy.wait('@getAgentPolicies');
cy.getBySel(AGENT_POLICIES_TABLE).contains(POLICY_NAME);
});
it('the policy should be visible in the default space', () => {
cy.visit('/app/fleet/policies');
cy.wait('@getAgentPolicies');
cy.getBySel(AGENT_POLICIES_TABLE).contains(POLICY_NAME);
});
});

View file

@ -46,6 +46,8 @@ export const AGENT_POLICY_CREATE_AGENT_POLICY_NAME_FIELD = 'createAgentPolicyNam
export const AGENT_POLICIES_FLYOUT_ADVANCED_DEFAULT_NAMESPACE_HEADER = 'defaultNamespaceHeader';
export const AGENT_POLICY_FLYOUT_CREATE_BUTTON = 'createAgentPolicyFlyoutBtn';
export const AGENT_POLICIES_TABLE = 'agentPoliciesTable';
export const ENROLLMENT_TOKENS = {
CREATE_TOKEN_BUTTON: 'createEnrollmentTokenButton',
CREATE_TOKEN_MODAL_NAME_FIELD: 'createEnrollmentTokenNameField',
@ -241,4 +243,7 @@ export const API_KEYS = {
export const AGENT_POLICY_DETAILS_PAGE = {
ADD_AGENT_LINK: 'addAgentLink',
SETTINGS_TAB: 'agentPolicySettingsTab',
SPACE_SELECTOR_COMBOBOX: 'spaceSelectorComboBox',
SAVE_BUTTON: 'agentPolicyDetailsSaveButton',
};

View file

@ -7,18 +7,20 @@
import { request } from './common';
export function cleanupAgentPolicies() {
request({ url: '/api/fleet/agent_policies' }).then((response: any) => {
response.body.items
.filter((policy: any) => policy.agents === 0)
.forEach((policy: any) => {
request({
method: 'POST',
url: '/api/fleet/agent_policies/delete',
body: { agentPolicyId: policy.id },
export function cleanupAgentPolicies(spaceId?: string) {
request({ url: `${spaceId ? `/s/${spaceId}` : ''}/api/fleet/agent_policies` }).then(
(response: any) => {
response.body.items
.filter((policy: any) => policy.agents === 0)
.forEach((policy: any) => {
request({
method: 'POST',
url: `${spaceId ? `/s/${spaceId}` : ''}/api/fleet/agent_policies/delete`,
body: { agentPolicyId: policy.id },
});
});
});
});
}
);
}
export function unenrollAgent() {

View file

@ -28,6 +28,12 @@ export const COMMON_API_HEADERS = Object.freeze({
'Elastic-Api-Version': API_VERSIONS.public.v1,
});
export const COMMON_INTERNAL_API_HEADERS = Object.freeze({
'kbn-xsrf': 'cypress',
'x-elastic-internal-origin': 'fleet',
'Elastic-Api-Version': API_VERSIONS.internal.v1,
});
// Replaces request - adds baseline authentication + global headers
export const request = <T = unknown>({
headers,
@ -40,6 +46,17 @@ export const request = <T = unknown>({
});
};
export const internalRequest = <T = unknown>({
headers,
...options
}: Partial<Cypress.RequestOptions>): Cypress.Chainable<Cypress.Response<T>> => {
return cy.request<T>({
auth: API_AUTH,
headers: { ...COMMON_INTERNAL_API_HEADERS, ...headers },
...options,
});
};
/**
* For all the new features tours we show in the app, this method disables them
* by setting their configs in the local storage. It prevents the tours from appearing

View file

@ -0,0 +1,38 @@
/*
* 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 { request, internalRequest } from './common';
export function enableSpaceAwareness() {
return internalRequest({
url: '/internal/fleet/enable_space_awareness',
failOnStatusCode: false,
method: 'POST',
});
}
export function createSpaces() {
return request({
url: '/api/spaces/space',
failOnStatusCode: false,
method: 'POST',
body: {
id: 'test',
name: 'Test',
description: 'Test space',
color: '#aabbcc',
initials: 'TE',
disabledFeatures: [],
imageUrl:
'data:image/png;base64,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',
},
}).then((response: any) => {
if (response.status !== 200 && response.status !== 409) {
throw new Error(`Failed to create space test`);
}
});
}

View file

@ -2,7 +2,8 @@
"extends": "../../../../tsconfig.base.json",
"include": [
"**/*",
"../cypress.config.ts"
"../cypress.config.ts",
"../cypress.config.space_awareness.ts"
],
"exclude": [
"target/**/*"

View file

@ -5,6 +5,10 @@
"private": true,
"license": "Elastic License 2.0",
"scripts": {
"cypress_space_awareness": "NODE_OPTIONS=--openssl-legacy-provider node ../security_solution/scripts/start_cypress_parallel --config-file ../fleet/cypress.config.space_awareness.ts --ftr-config-file ../../../x-pack/test/fleet_cypress/cli_config.space_awareness",
"cypress_space_awareness:open": "yarn cypress_space_awareness open",
"cypress_space_awareness:run": "yarn cypress_space_awareness run",
"cypress_space_awareness:run:reporter": "yarn cypress_space_awareness run --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=../fleet/cypress/reporter_config.json",
"cypress": "NODE_OPTIONS=--openssl-legacy-provider node ../security_solution/scripts/start_cypress_parallel --config-file ../fleet/cypress.config.ts --ftr-config-file ../../../x-pack/test/fleet_cypress/cli_config",
"cypress:open": "yarn cypress open",
"cypress:run": "yarn cypress run",

View file

@ -244,6 +244,7 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>(
Object.keys(validation).length > 0 ||
hasAdvancedSettingsErrors
}
data-test-subj="agentPolicyDetailsSaveButton"
iconType="save"
color="primary"
fill

View file

@ -90,6 +90,7 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => {
name: i18n.translate('xpack.fleet.policyDetails.subTabs.settingsTabText', {
defaultMessage: 'Settings',
}),
'data-test-subj': 'agentPolicySettingsTab',
href: getHref('policy_details', { policyId, tabId: 'settings' }),
isSelected: tabId === 'settings',
},

View file

@ -356,6 +356,7 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => {
<EuiSpacer size="m" />
<EuiBasicTable<AgentPolicy>
loading={isLoading}
data-test-subj="agentPoliciesTable"
noItemsMessage={
isLoading ? (
<FormattedMessage

View file

@ -5,6 +5,7 @@
},
"exclude": [
"cypress.config.ts",
"cypress.config.space_awareness.ts",
"target/**/*",
],
"include": [
@ -17,6 +18,7 @@
"scripts/**/*",
"package.json",
"cypress.config.ts",
"cypress.config.space_awareness.ts",
"../../../typings/**/*"
],
"kbn_references": [

View file

@ -87,8 +87,10 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
const specConfig = cypressConfigFile.e2e.specPattern;
const specArg = argv.spec;
const specPattern = specArg ?? specConfig;
const excludeSpecPattern = cypressConfigFile.e2e.excludeSpecPattern;
log.info('Config spec pattern:', specConfig);
log.info('Exclude spec pattern:', excludeSpecPattern);
log.info('Arguments spec pattern:', specArg);
log.info('Resulting spec pattern:', specPattern);
@ -121,7 +123,14 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
const concreteFilePaths = isGrepReturnedFilePaths
? grepSpecPattern // use the returned concrete file paths
: globby.sync(specPattern); // convert the glob pattern to concrete file paths
: globby.sync(
specPattern,
excludeSpecPattern
? {
ignore: excludeSpecPattern,
}
: undefined
); // convert the glob pattern to concrete file paths
let files = retrieveIntegrations(concreteFilePaths);

View file

@ -0,0 +1,19 @@
/*
* 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 { FleetCypressCliTestRunner } from './runner';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const cypressConfig = await readConfigFile(require.resolve('./config.space_awareness.ts'));
return {
...cypressConfig.getAll(),
testRunner: FleetCypressCliTestRunner,
};
}

View file

@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrConfigProviderContext, getKibanaCliLoggers } from '@kbn/test';
import { CA_CERT_PATH } from '@kbn/dev-utils';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const kibanaCommonTestsConfig = await readConfigFile(
require.resolve('@kbn/test-suites-src/common/config')
);
const xpackFunctionalTestsConfig = await readConfigFile(
require.resolve('../functional/config.base.js')
);
return {
...kibanaCommonTestsConfig.getAll(),
esTestCluster: {
...xpackFunctionalTestsConfig.get('esTestCluster'),
serverArgs: [
...xpackFunctionalTestsConfig.get('esTestCluster.serverArgs'),
// define custom es server here
// API Keys is enabled at the top level
'xpack.security.enabled=true',
'http.host=0.0.0.0',
],
},
kbnTestServer: {
...xpackFunctionalTestsConfig.get('kbnTestServer'),
serverArgs: [
...xpackFunctionalTestsConfig.get('kbnTestServer.serverArgs'),
'--csp.warnLegacyBrowsers=false',
'--csp.strict=false',
// define custom kibana server args here
`--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`,
// add feature flags here
`--xpack.fleet.enableExperimental=${JSON.stringify([
'agentTamperProtectionEnabled',
'subfeaturePrivileges',
'useSpaceAwareness',
])}`,
`--logging.loggers=${JSON.stringify([
...getKibanaCliLoggers(xpackFunctionalTestsConfig.get('kbnTestServer.serverArgs')),
// Enable debug fleet logs by default
{
name: 'plugins.fleet',
level: 'debug',
appenders: ['default'],
},
])}`,
],
},
};
}