Skip to content

Commit

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

This PR adds e2e test run on real endpoint for coverage of isolate,
processes, kill-process and suspend-process commands from respond
console.

Depends on elastic#155360
  • Loading branch information
szwarckonrad authored Apr 27, 2023
1 parent c571a38 commit d80fdd6
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 @@ -72,7 +72,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 d80fdd6

Please sign in to comment.