mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Security Solution][Serverless] Logging - Fix explore test issue (#195941)
## Summary This PR addresses two points: - First it introduces the project information logging (not any sensitive data like pwd etc) for better troubleshooting. This will allow teams to be able to get the project ID and the organisation ID in order to search for project logs etc in the overview consoles. - Explore test issue: A specific spec file was crashing causing Buildkite timeout which was taking 5 hours to be reached. Something seems to be going wrong with the order of the tests within the specific spec suite, **specifically in CI**. Potentially the configuration of the machines where the test run. After a lot of investigation the order is changed and the `Copy value` test was moved to the top of the spec file. This allows the proper execution of all the tests. Pending further investigation. Me and @MadameSheema tested this locally and it always passes without any issues. Also I tried to increase the resources of the agent assigned in Buildkite to run the tests but this still does not seem to be resolving the issue. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
ac5b14b125
commit
90b4ba5561
6 changed files with 76 additions and 40 deletions
|
@ -93,7 +93,11 @@ export function proxyHealthcheck(proxyUrl: string): Promise<boolean> {
|
|||
}
|
||||
|
||||
// Wait until elasticsearch status goes green
|
||||
export function waitForEsStatusGreen(esUrl: string, auth: string, runnerId: string): Promise<void> {
|
||||
export function waitForEsStatusGreen(
|
||||
esUrl: string,
|
||||
auth: string,
|
||||
projectId: string
|
||||
): Promise<void> {
|
||||
const fetchHealthStatusAttempt = async (attemptNum: number) => {
|
||||
log.info(`Retry number ${attemptNum} to check if Elasticsearch is green.`);
|
||||
|
||||
|
@ -105,13 +109,13 @@ export function waitForEsStatusGreen(esUrl: string, auth: string, runnerId: stri
|
|||
})
|
||||
.catch(catchAxiosErrorFormatAndThrow);
|
||||
|
||||
log.info(`${runnerId}: Elasticsearch is ready with status ${response.data.status}.`);
|
||||
log.info(`${projectId}: Elasticsearch is ready with status ${response.data.status}.`);
|
||||
};
|
||||
const retryOptions = {
|
||||
onFailedAttempt: (error: Error | AxiosError) => {
|
||||
if (error instanceof AxiosError && error.code === 'ENOTFOUND') {
|
||||
log.info(
|
||||
`${runnerId}: The Elasticsearch URL is not yet reachable. A retry will be triggered soon...`
|
||||
`${projectId}: The Elasticsearch URL is not yet reachable. A retry will be triggered soon...`
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -127,7 +131,7 @@ export function waitForEsStatusGreen(esUrl: string, auth: string, runnerId: stri
|
|||
export function waitForKibanaAvailable(
|
||||
kbUrl: string,
|
||||
auth: string,
|
||||
runnerId: string
|
||||
projectId: string
|
||||
): Promise<void> {
|
||||
const fetchKibanaStatusAttempt = async (attemptNum: number) => {
|
||||
log.info(`Retry number ${attemptNum} to check if kibana is available.`);
|
||||
|
@ -139,19 +143,19 @@ export function waitForKibanaAvailable(
|
|||
})
|
||||
.catch(catchAxiosErrorFormatAndThrow);
|
||||
if (response.data.status.overall.level !== 'available') {
|
||||
throw new Error(`${runnerId}: Kibana is not available. A retry will be triggered soon...`);
|
||||
throw new Error(`${projectId}: Kibana is not available. A retry will be triggered soon...`);
|
||||
} else {
|
||||
log.info(`${runnerId}: Kibana status overall is ${response.data.status.overall.level}.`);
|
||||
log.info(`${projectId}: Kibana status overall is ${response.data.status.overall.level}.`);
|
||||
}
|
||||
};
|
||||
const retryOptions = {
|
||||
onFailedAttempt: (error: Error | AxiosError) => {
|
||||
if (error instanceof AxiosError && error.code === 'ENOTFOUND') {
|
||||
log.info(
|
||||
`${runnerId}: The Kibana URL is not yet reachable. A retry will be triggered soon...`
|
||||
`${projectId}: The Kibana URL is not yet reachable. A retry will be triggered soon...`
|
||||
);
|
||||
} else {
|
||||
log.info(`${runnerId}: ${error.message}`);
|
||||
log.info(`${projectId}: ${error.message}`);
|
||||
}
|
||||
},
|
||||
retries: 50,
|
||||
|
@ -162,7 +166,7 @@ export function waitForKibanaAvailable(
|
|||
}
|
||||
|
||||
// Wait for Elasticsearch to be accessible
|
||||
export function waitForEsAccess(esUrl: string, auth: string, runnerId: string): Promise<void> {
|
||||
export function waitForEsAccess(esUrl: string, auth: string, projectId: string): Promise<void> {
|
||||
const fetchEsAccessAttempt = async (attemptNum: number) => {
|
||||
log.info(`Retry number ${attemptNum} to check if can be accessed.`);
|
||||
|
||||
|
@ -178,7 +182,7 @@ export function waitForEsAccess(esUrl: string, auth: string, runnerId: string):
|
|||
onFailedAttempt: (error: Error | AxiosError) => {
|
||||
if (error instanceof AxiosError && error.code === 'ENOTFOUND') {
|
||||
log.info(
|
||||
`${runnerId}: The elasticsearch url is not yet reachable. A retry will be triggered soon...`
|
||||
`${projectId}: The elasticsearch url is not yet reachable. A retry will be triggered soon...`
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -447,7 +451,7 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
|
|||
: (parseTestFileConfig(filePath).productTypes as ProductType[]);
|
||||
|
||||
log.info(`Running spec file: ${filePath}`);
|
||||
log.info(`${id}: Creating project ${PROJECT_NAME}...`);
|
||||
log.info(`Creating project ${PROJECT_NAME}...`);
|
||||
// Creating project for the test to run
|
||||
const project = await cloudHandler.createSecurityProject(
|
||||
PROJECT_NAME,
|
||||
|
@ -461,6 +465,21 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
|
|||
return process.exit(1);
|
||||
}
|
||||
|
||||
log.info(`
|
||||
-----------------------------------------------
|
||||
Project created with details:
|
||||
-----------------------------------------------
|
||||
ID: ${project.id}
|
||||
Name: ${project.name}
|
||||
Region: ${project.region}
|
||||
Elasticsearch URL: ${project.es_url}
|
||||
Kibana URL: ${project.kb_url}
|
||||
Product: ${project.product}
|
||||
Organization ID: ${project.proxy_org_id}
|
||||
Organization Name: ${project.proxy_org_name}
|
||||
-----------------------------------------------
|
||||
`);
|
||||
|
||||
context.addCleanupTask(() => {
|
||||
let command: string;
|
||||
if (cloudHandler instanceof CloudHandler) {
|
||||
|
@ -470,7 +489,7 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
|
|||
});
|
||||
|
||||
// Reset credentials for elastic user
|
||||
const credentials = await cloudHandler.resetCredentials(project.id, id);
|
||||
const credentials = await cloudHandler.resetCredentials(project.id);
|
||||
|
||||
if (!credentials) {
|
||||
log.error('Credentials could not be reset.');
|
||||
|
@ -485,13 +504,13 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
|
|||
const auth = btoa(`${credentials.username}:${credentials.password}`);
|
||||
|
||||
// Wait for elasticsearch status to go green.
|
||||
await waitForEsStatusGreen(project.es_url, auth, id);
|
||||
await waitForEsStatusGreen(project.es_url, auth, project.id);
|
||||
|
||||
// Wait until Kibana is available
|
||||
await waitForKibanaAvailable(project.kb_url, auth, id);
|
||||
await waitForKibanaAvailable(project.kb_url, auth, project.id);
|
||||
|
||||
// Wait for Elasticsearch to be accessible
|
||||
await waitForEsAccess(project.es_url, auth, id);
|
||||
await waitForEsAccess(project.es_url, auth, project.id);
|
||||
|
||||
// Wait until application is ready
|
||||
await waitForKibanaLogin(project.kb_url, credentials);
|
||||
|
@ -499,7 +518,6 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
|
|||
// Check if proxy service is used to define which org executes the tests.
|
||||
const proxyOrg =
|
||||
cloudHandler instanceof ProxyHandler ? project.proxy_org_name : undefined;
|
||||
log.info(`Proxy Organization used id : ${proxyOrg}`);
|
||||
|
||||
// Normalized the set of available env vars in cypress
|
||||
const cyCustomEnv = {
|
||||
|
@ -576,13 +594,14 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
|
|||
failedSpecFilePaths.push(filePath);
|
||||
}
|
||||
// Delete serverless project
|
||||
log.info(`${id} : Deleting project ${PROJECT_NAME}...`);
|
||||
log.info(`Deleting project ${PROJECT_NAME} and ID ${project.id} ...`);
|
||||
await cloudHandler.deleteSecurityProject(project.id, PROJECT_NAME);
|
||||
} catch (error) {
|
||||
// False positive
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
result = error;
|
||||
failedSpecFilePaths.push(filePath);
|
||||
log.error(`Cypress runner failed: ${error}`);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -105,8 +105,8 @@ export class CloudHandler extends ProjectHandler {
|
|||
}
|
||||
|
||||
// Method to reset the credentials for the created project.
|
||||
resetCredentials(projectId: string, runnerId: string): Promise<Credentials | undefined> {
|
||||
this.log.info(`${runnerId} : Reseting credentials`);
|
||||
resetCredentials(projectId: string): Promise<Credentials | undefined> {
|
||||
this.log.info(`${projectId} : Reseting credentials`);
|
||||
|
||||
const fetchResetCredentialsStatusAttempt = async (attemptNum: number) => {
|
||||
const response = await axios.post(
|
||||
|
@ -118,7 +118,7 @@ export class CloudHandler extends ProjectHandler {
|
|||
},
|
||||
}
|
||||
);
|
||||
this.log.info('Credentials have ben reset');
|
||||
this.log.info('Credentials have been reset');
|
||||
return {
|
||||
password: response.data.password,
|
||||
username: response.data.username,
|
||||
|
|
|
@ -75,7 +75,7 @@ export class ProjectHandler {
|
|||
throw new Error(this.DEFAULT_ERROR_MSG);
|
||||
}
|
||||
|
||||
resetCredentials(projectId: string, runnerId: string): Promise<Credentials | undefined> {
|
||||
resetCredentials(projectId: string): Promise<Credentials | undefined> {
|
||||
throw new Error(this.DEFAULT_ERROR_MSG);
|
||||
}
|
||||
|
||||
|
|
|
@ -104,8 +104,8 @@ export class ProxyHandler extends ProjectHandler {
|
|||
}
|
||||
|
||||
// Method to reset the credentials for the created project.
|
||||
resetCredentials(projectId: string, runnerId: string): Promise<Credentials | undefined> {
|
||||
this.log.info(`${runnerId} : Reseting credentials`);
|
||||
resetCredentials(projectId: string): Promise<Credentials | undefined> {
|
||||
this.log.info(`${projectId} : Reseting credentials`);
|
||||
|
||||
const fetchResetCredentialsStatusAttempt = async (attemptNum: number) => {
|
||||
const response = await axios.post(
|
||||
|
@ -117,7 +117,7 @@ export class ProxyHandler extends ProjectHandler {
|
|||
},
|
||||
}
|
||||
);
|
||||
this.log.info('Credentials have ben reset');
|
||||
this.log.info('Credentials have been reset');
|
||||
return {
|
||||
password: response.data.password,
|
||||
username: response.data.username,
|
||||
|
|
|
@ -117,19 +117,36 @@ export const cli = () => {
|
|||
const productTypes = await parseProductTypes(log);
|
||||
|
||||
// Creating project for the test to run
|
||||
log.info(`${id}: Creating project ${PROJECT_NAME}...`);
|
||||
const project = await cloudHandler.createSecurityProject(PROJECT_NAME, productTypes);
|
||||
// Check if proxy service is used to define which org executes the tests.
|
||||
const proxyOrg = cloudHandler instanceof ProxyHandler ? project?.proxy_org_name : undefined;
|
||||
log.info(`Proxy Organization used id : ${proxyOrg}`);
|
||||
|
||||
if (!project) {
|
||||
log.error('Failed to create project.');
|
||||
return process.exit(1);
|
||||
}
|
||||
|
||||
log.info(`
|
||||
-----------------------------------------------
|
||||
Project created with details:
|
||||
-----------------------------------------------
|
||||
ID: ${project.id}
|
||||
Name: ${project.name}
|
||||
Region: ${project.region}
|
||||
Elasticsearch URL: ${project.es_url}
|
||||
Kibana URL: ${project.kb_url}
|
||||
Product: ${project.product}
|
||||
Organization ID: ${project.proxy_org_id}
|
||||
Organization Name: ${project.proxy_org_name}
|
||||
-----------------------------------------------
|
||||
`);
|
||||
|
||||
// Check if proxy service is used to define which org executes the tests.
|
||||
const proxyOrg = cloudHandler instanceof ProxyHandler ? project?.proxy_org_name : undefined;
|
||||
|
||||
let statusCode: number = 0;
|
||||
try {
|
||||
// Reset credentials for elastic user
|
||||
const credentials = await cloudHandler.resetCredentials(project.id, id);
|
||||
const credentials = await cloudHandler.resetCredentials(project.id);
|
||||
|
||||
if (!credentials) {
|
||||
log.error('Credentials could not be reset.');
|
||||
|
@ -144,13 +161,13 @@ export const cli = () => {
|
|||
const auth = btoa(`${credentials.username}:${credentials.password}`);
|
||||
|
||||
// Wait for elasticsearch status to go green.
|
||||
await waitForEsStatusGreen(project.es_url, auth, id);
|
||||
await waitForEsStatusGreen(project.es_url, auth, project.id);
|
||||
|
||||
// Wait until Kibana is available
|
||||
await waitForKibanaAvailable(project.kb_url, auth, id);
|
||||
await waitForKibanaAvailable(project.kb_url, auth, project.id);
|
||||
|
||||
// Wait for Elasticsearch to be accessible
|
||||
await waitForEsAccess(project.es_url, auth, id);
|
||||
await waitForEsAccess(project.es_url, auth, project.id);
|
||||
|
||||
const FORMATTED_ES_URL = project.es_url.replace('https://', '');
|
||||
const FORMATTED_KB_URL = project.kb_url.replace('https://', '');
|
||||
|
@ -175,7 +192,7 @@ export const cli = () => {
|
|||
statusCode = 1;
|
||||
} finally {
|
||||
// Delete serverless project
|
||||
log.info(`${id} : Deleting project ${PROJECT_NAME}...`);
|
||||
log.info(`Deleting project ${PROJECT_NAME} and ID ${project.id} ...`);
|
||||
await cloudHandler.deleteSecurityProject(project.id, PROJECT_NAME);
|
||||
}
|
||||
process.exit(statusCode);
|
||||
|
|
|
@ -48,6 +48,14 @@ describe('Hover actions', { tags: ['@ess', '@serverless'] }, () => {
|
|||
mouseoverOnToOverflowItem();
|
||||
});
|
||||
|
||||
it('Copy value', () => {
|
||||
cy.document().then((doc) => cy.spy(doc, 'execCommand').as('execCommand'));
|
||||
|
||||
clickOnCopyValue();
|
||||
|
||||
cy.get('@execCommand').should('have.been.calledOnceWith', 'copy');
|
||||
});
|
||||
|
||||
it('Adds global filter - filter in', () => {
|
||||
clickOnFilterIn();
|
||||
|
||||
|
@ -75,12 +83,4 @@ describe('Hover actions', { tags: ['@ess', '@serverless'] }, () => {
|
|||
clickOnShowTopN();
|
||||
cy.get(TOP_N_CONTAINER).should('exist').should('contain.text', 'Top destination.domain');
|
||||
});
|
||||
|
||||
it('Copy value', () => {
|
||||
cy.document().then((doc) => cy.spy(doc, 'execCommand').as('execCommand'));
|
||||
|
||||
clickOnCopyValue();
|
||||
|
||||
cy.get('@execCommand').should('have.been.calledOnceWith', 'copy');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue