Skip to content

Commit

Permalink
[8.8] [Defend Workflows][E2E]Endpoint e2e response console multipass (#…
Browse files Browse the repository at this point in the history
…155519) (#155975)

# Backport

This will backport the following commits from `main` to `8.8`:
- [[Defend Workflows][E2E]Endpoint e2e response console multipass
(#155519)](#155519)

<!--- Backport version: 8.9.7 -->

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

<!--BACKPORT [{"author":{"name":"Konrad
Szwarc","email":"konrad.szwarc@elastic.co"},"sourceCommit":{"committedDate":"2023-04-27T08:27:21Z","message":"[Defend
Workflows][E2E]Endpoint e2e response console multipass (#155519)\n\nThis
PR adds e2e test run on real endpoint for coverage of
isolate,\r\nprocesses, kill-process and suspend-process commands from
respond\r\nconsole.\r\n\r\nDepends on
https://github.com/elastic/kibana/pull/155360","sha":"d80fdd6bceec438cae572ba13eae3ee3a9d3c5c3","branchLabelMapping":{"^v8.9.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Defend
Workflows","v8.8.0","v8.9.0"],"number":155519,"url":"https://github.com/elastic/kibana/pull/155519","mergeCommit":{"message":"[Defend
Workflows][E2E]Endpoint e2e response console multipass (#155519)\n\nThis
PR adds e2e test run on real endpoint for coverage of
isolate,\r\nprocesses, kill-process and suspend-process commands from
respond\r\nconsole.\r\n\r\nDepends on
https://github.com/elastic/kibana/pull/155360","sha":"d80fdd6bceec438cae572ba13eae3ee3a9d3c5c3"}},"sourceBranch":"main","suggestedTargetBranches":["8.8"],"targetPullRequestStates":[{"branch":"8.8","label":"v8.8.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.9.0","labelRegex":"^v8.9.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/155519","number":155519,"mergeCommit":{"message":"[Defend
Workflows][E2E]Endpoint e2e response console multipass (#155519)\n\nThis
PR adds e2e test run on real endpoint for coverage of
isolate,\r\nprocesses, kill-process and suspend-process commands from
respond\r\nconsole.\r\n\r\nDepends on
https://github.com/elastic/kibana/pull/155360","sha":"d80fdd6bceec438cae572ba13eae3ee3a9d3c5c3"}}]}]
BACKPORT-->

Co-authored-by: Konrad Szwarc <konrad.szwarc@elastic.co>
Co-authored-by: Patryk Kopyciński <contact@patrykkopycinski.com>
  • Loading branch information
3 people authored May 2, 2023
1 parent a675560 commit 742d261
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import type { Agent } from '@kbn/fleet-plugin/common';
import { APP_CASES_PATH, APP_ENDPOINTS_PATH } from '../../../../../common/constants';
import { closeAllToasts } from '../../tasks/close_all_toasts';
import {
checkEndpointListForIsolatedHosts,
checkEndpointListForOnlyIsolatedHosts,
checkEndpointListForOnlyUnIsolatedHosts,
checkFlyoutEndpointIsolation,
createAgentPolicyTask,
filterOutEndpoints,
Expand Down Expand Up @@ -50,11 +51,11 @@ describe('Isolate command', () => {
initialAgentData = agentData;
});

getEndpointIntegrationVersion().then((version) => {
createAgentPolicyTask(version, (data) => {
getEndpointIntegrationVersion().then((version) =>
createAgentPolicyTask(version).then((data) => {
response = data;
});
});
})
);
});

after(() => {
Expand All @@ -66,11 +67,10 @@ describe('Isolate command', () => {
}
});

// flaky
it.skip('should allow filtering endpoint by Isolated status', () => {
it('should allow filtering endpoint by Isolated status', () => {
cy.visit(APP_ENDPOINTS_PATH);
closeAllToasts();
checkEndpointListForIsolatedHosts(false);
checkEndpointListForOnlyUnIsolatedHosts();

filterOutIsolatedHosts();
cy.contains('No items found');
Expand All @@ -88,7 +88,7 @@ describe('Isolate command', () => {
cy.getByTestSubj('rowHostStatus-actionStatuses').should('contain.text', 'Isolated');
filterOutIsolatedHosts();

checkEndpointListForIsolatedHosts();
checkEndpointListForOnlyIsolatedHosts();

cy.getByTestSubj('endpointTableRowActions').click();
cy.getByTestSubj('unIsolateLink').click();
Expand All @@ -97,7 +97,7 @@ describe('Isolate command', () => {
cy.getByTestSubj('euiFlyoutCloseButton').click();
cy.getByTestSubj('adminSearchBar').click().type('{selectall}{backspace}');
cy.getByTestSubj('querySubmitButton').click();
checkEndpointListForIsolatedHosts(false);
checkEndpointListForOnlyUnIsolatedHosts();
});
});

Expand All @@ -112,11 +112,11 @@ describe('Isolate command', () => {
initialAgentData = agentData;
});

getEndpointIntegrationVersion().then((version) => {
createAgentPolicyTask(version, (data) => {
getEndpointIntegrationVersion().then((version) =>
createAgentPolicyTask(version).then((data) => {
response = data;
});
});
})
);
loadRule(false).then((data) => {
ruleId = data.id;
ruleName = data.name;
Expand Down Expand Up @@ -185,11 +185,11 @@ describe('Isolate command', () => {
getAgentByHostName(endpointHostname).then((agentData) => {
initialAgentData = agentData;
});
getEndpointIntegrationVersion().then((version) => {
createAgentPolicyTask(version, (data) => {
getEndpointIntegrationVersion().then((version) =>
createAgentPolicyTask(version).then((data) => {
response = data;
});
});
})
);

loadRule(false).then((data) => {
ruleId = data.id;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* 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 type { Agent } from '@kbn/fleet-plugin/common';
import {
inputConsoleCommand,
openResponseConsoleFromEndpointList,
performCommandInputChecks,
submitCommand,
waitForCommandToBeExecuted,
waitForEndpointListPageToBeLoaded,
} from '../../tasks/response_console';
import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
import {
getAgentByHostName,
getEndpointIntegrationVersion,
reassignAgentPolicy,
} from '../../tasks/fleet';
import {
checkEndpointListForOnlyIsolatedHosts,
checkEndpointListForOnlyUnIsolatedHosts,
createAgentPolicyTask,
} from '../../tasks/isolate';
import { login } from '../../tasks/login';
import { ENDPOINT_VM_NAME } from '../../tasks/common';

describe('Response console', () => {
const endpointHostname = Cypress.env(ENDPOINT_VM_NAME);

beforeEach(() => {
login();
});

describe('User journey for Isolate command: isolate and release an endpoint', () => {
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 isolate host from response console', () => {
waitForEndpointListPageToBeLoaded(endpointHostname);
checkEndpointListForOnlyUnIsolatedHosts();
openResponseConsoleFromEndpointList();
performCommandInputChecks('isolate');
submitCommand();
waitForCommandToBeExecuted();
waitForEndpointListPageToBeLoaded(endpointHostname);
checkEndpointListForOnlyIsolatedHosts();
});

it('should release host from response console', () => {
waitForEndpointListPageToBeLoaded(endpointHostname);
checkEndpointListForOnlyIsolatedHosts();
openResponseConsoleFromEndpointList();
performCommandInputChecks('release');
submitCommand();
waitForCommandToBeExecuted();
waitForEndpointListPageToBeLoaded(endpointHostname);
checkEndpointListForOnlyUnIsolatedHosts();
});
});

describe('User journey for Processes commands: list, kill and suspend process.', () => {
let response: IndexedFleetEndpointPolicyResponse;
let initialAgentData: Agent;
let cronPID: string;
let newCronPID: string;

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('"processes" - should obtain a list of processes', () => {
waitForEndpointListPageToBeLoaded(endpointHostname);
openResponseConsoleFromEndpointList();
performCommandInputChecks('processes');
submitCommand();
cy.contains('Action pending.').should('exist');
cy.getByTestSubj('getProcessesSuccessCallout', { timeout: 120000 }).within(() => {
['USER', 'PID', 'ENTITY ID', 'COMMAND'].forEach((header) => {
cy.contains(header);
});

cy.get('tbody > tr').should('have.length.greaterThan', 0);
cy.get('tbody > tr > td').should('contain', '/usr/sbin/cron');
cy.get('tbody > tr > td')
.contains('/usr/sbin/cron')
.parents('td')
.siblings('td')
.eq(1)
.find('span')
.then((span) => {
cronPID = span.text();
});
});
});

it('"kill-process --pid" - should kill a process', () => {
waitForEndpointListPageToBeLoaded(endpointHostname);
openResponseConsoleFromEndpointList();
inputConsoleCommand(`kill-process --pid ${cronPID}`);
submitCommand();
waitForCommandToBeExecuted();

performCommandInputChecks('processes');
submitCommand();

cy.getByTestSubj('getProcessesSuccessCallout', { timeout: 120000 }).within(() => {
cy.get('tbody > tr > td')
.contains('/usr/sbin/cron')
.parents('td')
.siblings('td')
.eq(1)
.find('span')
.then((span) => {
newCronPID = span.text();
});
});
expect(newCronPID).to.not.equal(cronPID);
});

it('"suspend-process --pid" - should suspend a process', () => {
waitForEndpointListPageToBeLoaded(endpointHostname);
openResponseConsoleFromEndpointList();
inputConsoleCommand(`suspend-process --pid ${newCronPID}`);
submitCommand();
waitForCommandToBeExecuted();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { getEndpointListPath } from '../../../common/routing';
import {
checkEndpointListForIsolatedHosts,
checkEndpointListForOnlyIsolatedHosts,
checkFlyoutEndpointIsolation,
filterOutIsolatedHosts,
interceptActionRequests,
Expand Down Expand Up @@ -73,7 +73,7 @@ describe('Isolate command', () => {
closeAllToasts();
filterOutIsolatedHosts();
cy.contains('Showing 2 endpoints');
checkEndpointListForIsolatedHosts();
checkEndpointListForOnlyIsolatedHosts();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,24 +112,23 @@ export const filterOutEndpoints = (endpointHostname: string): void => {
};

export const createAgentPolicyTask = (
version: string,
cb: (response: IndexedFleetEndpointPolicyResponse) => void
) => {
version: string
): Cypress.Chainable<IndexedFleetEndpointPolicyResponse> => {
const policyName = `Reassign ${Math.random().toString(36).substring(2, 7)}`;

cy.task<IndexedFleetEndpointPolicyResponse>('indexFleetEndpointPolicy', {
return cy.task<IndexedFleetEndpointPolicyResponse>('indexFleetEndpointPolicy', {
policyName,
endpointPackageVersion: version,
agentPolicyName: policyName,
}).then(cb);
});
};

export const filterOutIsolatedHosts = (): void => {
cy.getByTestSubj('adminSearchBar').click().type('united.endpoint.Endpoint.state.isolation: true');
cy.getByTestSubj('querySubmitButton').click();
};

export const checkEndpointListForIsolatedHosts = (expectIsolated = true): void => {
const checkEndpointListForIsolatedHosts = (expectIsolated: boolean): void => {
const chainer = expectIsolated ? 'contain.text' : 'not.contain.text';
cy.getByTestSubj('endpointListTable').within(() => {
cy.get('tbody tr').each(($tr) => {
Expand All @@ -139,3 +138,8 @@ export const checkEndpointListForIsolatedHosts = (expectIsolated = true): void =
});
});
};

export const checkEndpointListForOnlyUnIsolatedHosts = (): void =>
checkEndpointListForIsolatedHosts(false);
export const checkEndpointListForOnlyIsolatedHosts = (): void =>
checkEndpointListForIsolatedHosts(true);
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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 { closeAllToasts } from './close_all_toasts';
import { APP_ENDPOINTS_PATH } from '../../../../common/constants';

export const waitForEndpointListPageToBeLoaded = (endpointHostname: string): void => {
cy.visit(APP_ENDPOINTS_PATH);
closeAllToasts();
cy.contains(endpointHostname).should('exist');
};
export const openResponseConsoleFromEndpointList = (): void => {
cy.getByTestSubj('endpointTableRowActions').first().click();
cy.contains('Respond').click();
};

export const inputConsoleCommand = (command: string): void => {
cy.getByTestSubj('endpointResponseActionsConsole-inputCapture').click().type(command);
};

export const clearConsoleCommandInput = (): void => {
cy.getByTestSubj('endpointResponseActionsConsole-inputCapture')
.click()
.type(`{selectall}{backspace}`);
};

export const selectCommandFromHelpMenu = (command: string): void => {
cy.getByTestSubj('endpointResponseActionsConsole-header-helpButton').click();
cy.getByTestSubj(
`endpointResponseActionsConsole-commandList-Responseactions-${command}-addToInput`
).click();
};

export const checkInputForCommandPresence = (command: string): void => {
cy.getByTestSubj('endpointResponseActionsConsole-cmdInput-leftOfCursor')
.invoke('text')
.should('eq', `${command} `); // command in the cli input is followed by a space
};

export const submitCommand = (): void => {
cy.getByTestSubj('endpointResponseActionsConsole-inputTextSubmitButton').click();
};

export const waitForCommandToBeExecuted = (): void => {
cy.contains('Action pending.').should('exist');
cy.contains('Action completed.', { timeout: 120000 }).should('exist');
};

export const performCommandInputChecks = (command: string) => {
inputConsoleCommand(command);
clearConsoleCommandInput();
selectCommandFromHelpMenu(command);
checkInputForCommandPresence(command);
};

0 comments on commit 742d261

Please sign in to comment.