Skip to content

Commit

Permalink
[Security Solution][Endpoint] Fix and unskip test (#171719)
Browse files Browse the repository at this point in the history
## Summary

Fix and unskip isolation cypress test from response console

- correctly does substring match when agent isolation state is
`Isolated`
- splits the tests so to avoid flaky states where the first test fails
and on a re-run creates another agent and that fails the test as there
are two agents on the list instead of one.

fixes /issues/170470

**Flaky test runner**
-
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4044
x 50 ( all pass )
-
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4049
x 150 ( all pass )
-
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4050
x 150 ( 1 fail )

_with split tests_
-
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4052
x 150 ( all pass )

_with action API for isolating host_
-
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4054
x 150 ( 1 failed, cancelled rest )

_with action retries_
-
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4055
x 150 ( all pass )

### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
ashokaditya and kibanamachine authored Nov 24, 2023
1 parent 0c0ac93 commit bb34e1b
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,67 +19,51 @@ import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../../t
import {
checkEndpointListForOnlyIsolatedHosts,
checkEndpointListForOnlyUnIsolatedHosts,
isolateHostFromEndpointList,
} from '../../../tasks/isolate';

import { login } from '../../../tasks/login';
import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy';
import { createEndpointHost } from '../../../tasks/create_endpoint_host';
import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data';

// Failing: See https://github.com/elastic/kibana/issues/170470
describe.skip('Response console', { tags: ['@ess', '@serverless'] }, () => {
beforeEach(() => {
login();
});

describe('Host Isolation:', () => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
let createdHost: CreateAndEnrollEndpointHostResponse;
describe('Response console', { tags: ['@ess', '@serverless'] }, () => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
let createdHost: CreateAndEnrollEndpointHostResponse;

before(() => {
getEndpointIntegrationVersion().then((version) =>
createAgentPolicyTask(version).then((data) => {
indexedPolicy = data;
policy = indexedPolicy.integrationPolicies[0];
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;
});
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);
}
after(() => {
if (createdHost) {
cy.task('destroyEndpointHost', createdHost);
}

if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}
if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}

if (createdHost) {
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
}
});
if (createdHost) {
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
}
});

it('should release an isolated host from response console', () => {
const command = 'release';
waitForEndpointListPageToBeLoaded(createdHost.hostname);
// isolate the host first
isolateHostFromEndpointList();
checkEndpointListForOnlyIsolatedHosts();
openResponseConsoleFromEndpointList();
performCommandInputChecks(command);
submitCommand();
waitForCommandToBeExecuted(command);
waitForEndpointListPageToBeLoaded(createdHost.hostname);
checkEndpointListForOnlyUnIsolatedHosts();
describe('Host Isolation:', () => {
beforeEach(() => {
login();
});

it('should isolate a host from response console', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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 { PolicyData } from '../../../../../../common/endpoint/types';
import type { CreateAndEnrollEndpointHostResponse } from '../../../../../../scripts/endpoint/common/endpoint_host_services';
import {
openResponseConsoleFromEndpointList,
performCommandInputChecks,
submitCommand,
waitForCommandToBeExecuted,
waitForEndpointListPageToBeLoaded,
} from '../../../tasks/response_console';
import type { IndexedFleetEndpointPolicyResponse } from '../../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
import { createAgentPolicyTask, getEndpointIntegrationVersion } from '../../../tasks/fleet';
import {
checkEndpointListForOnlyIsolatedHosts,
checkEndpointListForOnlyUnIsolatedHosts,
isolateHostActionViaAPI,
} from '../../../tasks/isolate';

import { login } from '../../../tasks/login';
import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy';
import { createEndpointHost } from '../../../tasks/create_endpoint_host';
import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data';

describe('Response console', { tags: ['@ess', '@serverless'] }, () => {
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] });
}
});

describe('Host Isolation:', () => {
beforeEach(() => {
login();
});

it('should release an isolated host via response console', () => {
const command = 'release';
waitForEndpointListPageToBeLoaded(createdHost.hostname);
// isolate the host first
isolateHostActionViaAPI(createdHost.agentId);
// verify and find the isolated host
checkEndpointListForOnlyIsolatedHosts();
openResponseConsoleFromEndpointList();
performCommandInputChecks(command);
submitCommand();
waitForCommandToBeExecuted(command);
waitForEndpointListPageToBeLoaded(createdHost.hostname);
checkEndpointListForOnlyUnIsolatedHosts();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

/* eslint-disable cypress/no-unnecessary-waiting */

import { API_VERSIONS } from '@kbn/fleet-plugin/common';
import { openAlertDetailsView } from '../screens/alerts';
import type { ActionDetails } from '../../../../common/endpoint/types';
import { loadPage } from './common';
import { waitForActionToSucceed } from './response_actions';

const API_ENDPOINT_ACTION_PATH = '/api/endpoint/action/*';
export const interceptActionRequests = (
Expand Down Expand Up @@ -41,29 +43,6 @@ export const isolateHostWithComment = (comment: string, hostname: string): void
cy.getByTestSubj('host_isolation_comment').type(comment);
};

export const isolateHostFromEndpointList = (index: number = 0): void => {
// open the action menu and click isolate action
cy.getByTestSubj('endpointTableRowActions').eq(index).click();
cy.getByTestSubj('isolateLink').click();
// isolation form, click confirm button
cy.getByTestSubj('hostIsolateConfirmButton').click();
// return to endpoint details
cy.getByTestSubj('hostIsolateSuccessCompleteButton').click();
// close details flyout
cy.getByTestSubj('euiFlyoutCloseButton').click();

// ensure the host is isolated, wait for 3 minutes for the host to be isolated
cy.wait(18000);

cy.getByTestSubj('endpointListTable').within(() => {
cy.get('tbody tr')
.eq(index)
.within(() => {
cy.get('td').eq(1).should('contain.text', 'Isolated');
});
});
};

export const releaseHostWithComment = (comment: string, hostname: string): void => {
cy.contains(`${hostname} is currently isolated.`);
cy.getByTestSubj('endpointHostIsolationForm');
Expand Down Expand Up @@ -139,28 +118,46 @@ export const filterOutIsolatedHosts = (): void => {
cy.getByTestSubj('querySubmitButton').click();
};

const checkEndpointListForIsolatedHosts = (expectIsolated: boolean): void => {
const chainer = expectIsolated ? 'contain.text' : 'not.contain.text';
const checkEndpointListForIsolationStatus = (expectIsolated: boolean): void => {
const chainer = expectIsolated ? 'contain' : 'not.contain';
cy.getByTestSubj('endpointListTable').within(() => {
cy.get('tbody tr').each(($tr) => {
cy.wrap($tr).within(() => {
cy.get('tbody tr')
.eq(0)
.within(() => {
cy.get('td').eq(1).should(chainer, 'Isolated');
});
});
});
};

export const checkEndpointListForOnlyUnIsolatedHosts = (): void =>
checkEndpointListForIsolatedHosts(false);
checkEndpointListForIsolationStatus(false);
export const checkEndpointListForOnlyIsolatedHosts = (): void =>
checkEndpointListForIsolatedHosts(true);
checkEndpointListForIsolationStatus(true);

export const isolateHostActionViaAPI = (agentId: string): void => {
cy.request({
headers: {
'kbn-xsrf': 'cypress-creds',
'elastic-api-version': API_VERSIONS.public.v1,
},
method: 'POST',
url: 'api/endpoint/action/isolate',
body: {
endpoint_ids: [agentId],
},
})
// verify action was successful
.then((response) => waitForActionToSucceed(response.body.data.id))
.then((actionResponse) => {
expect(actionResponse.status).to.equal('successful');
});
};

export const checkEndpointIsolationStatus = (
endpointHostname: string,
expectIsolated: boolean
): void => {
const chainer = expectIsolated ? 'contain.text' : 'not.contain.text';

const chainer = expectIsolated ? 'contain' : 'not.contain';
cy.contains(endpointHostname).parents('td').siblings('td').eq(0).should(chainer, 'Isolated');
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,38 @@ export const waitForActionToComplete = (
});
};

export const waitForActionToSucceed = (
actionId: string,
timeout = 180000
): Cypress.Chainable<ActionDetails> => {
let action: ActionDetails | undefined;

return cy
.waitUntil(
() => {
return request<ActionDetailsApiResponse>({
method: 'GET',
url: resolvePathVariables(ACTION_DETAILS_ROUTE, { action_id: actionId || 'undefined' }),
}).then((response) => {
if (response.body.data.isCompleted && response.body.data.status === 'successful') {
action = response.body.data;
return true;
}

return false;
});
},
{ timeout, interval: 2000 }
)
.then(() => {
if (!action) {
throw new Error('Failed to retrieve successful action');
}

return action;
});
};

/**
* Ensure user has the given `accessLevel` to the type of response action
* @param accessLevel
Expand Down

0 comments on commit bb34e1b

Please sign in to comment.