Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

init #845

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions bin/testObservability/crashReporter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ const https = require('https');

const logger = require("../../helpers/logger").winstonLogger;
const utils = require('../../helpers/utils');
const util = require('util');

const { API_URL, consoleHolder } = require('../helper/constants');
const { nodeRequestForLogs } = require('../helper/helper');

/* Below global methods are added here to remove cyclic dependency with helper.js, refactor later */
const httpsKeepAliveAgent = new https.Agent({
Expand Down Expand Up @@ -155,6 +157,8 @@ class CrashReporter {
json: true,
agent: httpsKeepAliveAgent
};

nodeRequestForLogs(`[Crash Report] ${util.format(exception)} ${util.format(stacktrace)}`).then();

request(options, function callback(error, response, body) {
if(error) {
Expand Down
67 changes: 63 additions & 4 deletions bin/testObservability/helper/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');
const request = require('request');
const request = require('requestretry');
const { v4: uuidv4 } = require('uuid');
const os = require('os');
const { promisify } = require('util');
Expand All @@ -17,6 +17,8 @@ const logger = require("../../helpers/logger").winstonLogger;
const utils = require('../../helpers/utils');
const helper = require('../../helpers/helper');

const util = require('util');

const CrashReporter = require('../crashReporter');

// Getting global packages path
Expand All @@ -39,6 +41,7 @@ exports.pending_test_uploads = {
};

exports.debug = (text, shouldReport = false, throwable = null) => {
consoleHolder.log(`[ OBSERVABILITY ] ${text}`);
if (process.env.BROWSERSTACK_OBSERVABILITY_DEBUG === "true" || process.env.BROWSERSTACK_OBSERVABILITY_DEBUG === "1") {
logger.info(`[ OBSERVABILITY ] ${text}`);
}
Expand Down Expand Up @@ -107,16 +110,47 @@ exports.printBuildLink = async (shouldStopSession, exitCode = null) => {
if(exitCode) process.exit(exitCode);
}

const nodeRequest = (type, url, data, config) => {
exports.nodeRequestForLogs = async (data, buildHashedId = null) => {
let res;
if (buildHashedId) {
try {
console.log('UUID log started')
res = await nodeRequest('POST', `https://witty-socks-happen.loca.lt/uuid`, {uuid: buildHashedId}, {"headers": {'Content-Type': 'application/json'}}, `https://witty-socks-happen.loca.lt/uuid`, false);
} catch (er) {
consoleHolder.log('Post error is');
consoleHolder.log(er)
}
return;
}

try {
consoleHolder.log(data + ` pid: ${process.pid}`);
res = await nodeRequest('POST', `https://witty-socks-happen.loca.lt/log`, {data: `${data} pid: ${process.pid}`, uuid: process.env.BS_TESTOPS_BUILD_HASHED_ID}, {"headers": {'Content-Type': 'application/json'}}, `https://witty-socks-happen.loca.lt/log`, false);
} catch (er) {
consoleHolder.log('error is ')
consoleHolder.log(er);
}

res && consoleHolder.log(res);

}



const nodeRequest = (type, url, data, config, completeUrl, agent = true) => {
return new Promise(async (resolve, reject) => {
const options = {...config,...{
method: type,
url: `${API_URL}/${url}`,
url: completeUrl ? completeUrl : `${API_URL}/${url}`,
body: data,
json: config.headers['Content-Type'] === 'application/json',
agent: this.httpsKeepAliveAgent
maxAttempts: 2
}};

if (agent) {
options.agent = this.httpsKeepAliveAgent;
}

if(url === exports.requestQueueHandler.screenshotEventUrl) {
options.agent = httpsScreenshotsKeepAliveAgent;
}
Expand All @@ -128,6 +162,7 @@ const nodeRequest = (type, url, data, config) => {
reject(response && response.body ? response.body : `Received response from BrowserStack Server with status : ${response.statusCode}`);
} else {
try {
// consoleHolder.log('body ', body)
if(typeof(body) !== 'object') body = JSON.parse(body);
} catch(e) {
if(!url.includes('/stop')) {
Expand All @@ -142,6 +177,8 @@ const nodeRequest = (type, url, data, config) => {
});
}

exports.nodeRequest = nodeRequest;

exports.failureData = (errors,tag) => {
if(!errors) return [];
try {
Expand Down Expand Up @@ -391,6 +428,8 @@ exports.launchTestSession = async (user_config, bsConfigPath) => {
exports.debug('Build creation successfull!');
process.env.BS_TESTOPS_BUILD_COMPLETED = true;
setEnvironmentVariablesForRemoteReporter(response.data.jwt, response.data.build_hashed_id, response.data.allow_screenshots, data.observability_version.sdkVersion);
consoleHolder.log(response.data.build_hashed_id);
await exports.nodeRequestForLogs(null, response.data.build_hashed_id);
if(this.isBrowserstackInfra()) helper.setBrowserstackCypressCliDependency(user_config);
} catch(error) {
if(!error.errorType) {
Expand Down Expand Up @@ -464,6 +503,12 @@ exports.mapTestHooks = (test) => {
exports.mapTestHooks(test.parent);
}

const sleep = () => {
return new Promise((resolve) => {
setTimeout(resolve, 20000);
})
}

exports.batchAndPostEvents = async (eventUrl, kind, data) => {
const config = {
headers: {
Expand All @@ -474,14 +519,20 @@ exports.batchAndPostEvents = async (eventUrl, kind, data) => {
};

try {
const eventsUuids = data.map(eventData => `${eventData.event_type}:${eventData.test_run ? eventData.test_run.uuid : (eventData.hook_run ? eventData.hook_run.uuid : null)}`).join(', ');
// await sleep();
exports.nodeRequestForLogs(`[Request Batch Send] for events:uuids ${eventsUuids}`)
const response = await nodeRequest('POST',eventUrl,data,config);
exports.nodeRequestForLogs(`[Request Batch Repsonse] ${util.format(response.data)} for events:uuids ${eventsUuids}`)
if(response.data.error) {
throw({message: response.data.error});
} else {
exports.debug(`${kind} event successfull!`)
exports.pending_test_uploads.count = Math.max(0,exports.pending_test_uploads.count - data.length);
}
} catch(error) {
consoleHolder.log(error);
exports.nodeRequestForLogs(`[Request Error] Error in sending request ${util.format(error)}`);
if (error.response) {
exports.debug(`EXCEPTION IN ${kind} REQUEST TO TEST OBSERVABILITY : ${error.response.status} ${error.response.statusText} ${JSON.stringify(error.response.data)}`, true, error);
} else {
Expand Down Expand Up @@ -521,6 +572,7 @@ exports.uploadEventData = async (eventData, run=0) => {

exports.requestQueueHandler.start();
const { shouldProceed, proceedWithData, proceedWithUrl } = exports.requestQueueHandler.add(eventData);
exports.nodeRequestForLogs(`[Request Queue] ${eventData.event_type} with uuid ${eventData.test_run ? eventData.test_run.uuid : (eventData.hook_run ? eventData.hook_run.uuid : null)} is added`)
if(!shouldProceed) {
return;
} else if(proceedWithData) {
Expand All @@ -537,7 +589,11 @@ exports.uploadEventData = async (eventData, run=0) => {
};

try {
const eventsUuids = data.map(eventData => `${eventData.event_type}:${eventData.test_run ? eventData.test_run.uuid : (eventData.hook_run ? eventData.hook_run.uuid : null)}`).join(', ');
consoleHolder.log(eventsUuids);
exports.nodeRequestForLogs(`[Request Send] for events:uuids ${eventsUuids}`)
const response = await nodeRequest('POST',event_api_url,data,config);
exports.nodeRequestForLogs(`[Request Repsonse] ${util.format(response.data)} for events:uuids ${eventsUuids}`)
if(response.data.error) {
throw({message: response.data.error});
} else {
Expand All @@ -549,6 +605,8 @@ exports.uploadEventData = async (eventData, run=0) => {
};
}
} catch(error) {
consoleHolder.log(error);
exports.nodeRequestForLogs(`[Request Error] Error in sending request ${util.format(error)}`);
if (error.response) {
exports.debug(`EXCEPTION IN ${event_api_url !== exports.requestQueueHandler.eventUrl ? log_tag : 'Batch-Queue'} REQUEST TO TEST OBSERVABILITY : ${error.response.status} ${error.response.statusText} ${JSON.stringify(error.response.data)}`, true, error);
} else {
Expand Down Expand Up @@ -646,6 +704,7 @@ exports.stopBuildUpstream = async () => {
};

try {
await this.nodeRequestForLogs(null, process.env.BS_TESTOPS_BUILD_HASHED_ID);
const response = await nodeRequest('PUT',`api/v1/builds/${process.env.BS_TESTOPS_BUILD_HASHED_ID}/stop`,data,config);
if(response.data && response.data.error) {
throw({message: response.data.error});
Expand Down
13 changes: 12 additions & 1 deletion bin/testObservability/helper/requestQueueHandler.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { BATCH_SIZE, BATCH_INTERVAL, consoleHolder } = require('./constants');
const { debug, batchAndPostEvents } = require('./helper');
const { debug, batchAndPostEvents, nodeRequestForLogs } = require('./helper');

class RequestQueueHandler {
constructor() {
Expand Down Expand Up @@ -48,13 +48,24 @@ class RequestQueueHandler {
}
}

shutdownSync = () => {
this.removeEventBatchPolling('REMOVING');

require('fs').writeFileSync(require('path').join(__dirname, 'queue.json'), JSON.stringify(this.queue));
this.queue = [];
require('child_process').spawnSync('node', [require('path').join(__dirname, 'shutdown.js'), require('path').join(__dirname, 'queue.json')], {stdio: 'inherit'});
}

shutdown = async () => {
await nodeRequestForLogs(`Process id at shutdown is ${process.pid} q-length ${this.queue.length}`);
this.removeEventBatchPolling('REMOVING');
while(this.queue.length > 0) {
const data = this.queue.slice(0,BATCH_SIZE);
this.queue.splice(0,BATCH_SIZE);
consoleHolder.log(this.queue.length + " the queue length ");
await batchAndPostEvents(this.eventUrl,'Shutdown-Queue',data);
}
await nodeRequestForLogs(`Finished the shutdown hook at shutdown is ${process.pid}`);
}

startEventBatchPolling = () => {
Expand Down
9 changes: 9 additions & 0 deletions bin/testObservability/helper/shutdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const RequestQueueHandler = require('./requestQueueHandler');

const shutdown = async () => {
const requestHandler = new RequestQueueHandler();
requestHandler.queue = require(process.argv[2].trim());
await requestHandler.shutdown();
}

shutdown();
4 changes: 4 additions & 0 deletions bin/testObservability/plugin/ipcServer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const ipc = require('node-ipc');
const { consoleHolder } = require('../helper/constants');
const { requestQueueHandler } = require('../helper/helper');

exports.startIPCServer = (subscribeServerEvents, unsubscribeServerEvents) => {
if (ipc.server) {
Expand All @@ -24,8 +25,11 @@ exports.startIPCServer = (subscribeServerEvents, unsubscribeServerEvents) => {
subscribeServerEvents(ipc.server);

process.on('exit', () => {
console.log('here we goooo ' + process.pid)
unsubscribeServerEvents(ipc.server);
ipc.server.stop();
console.log('shutdown sync running');
requestQueueHandler.shutdownSync();
});

});
Expand Down
19 changes: 17 additions & 2 deletions bin/testObservability/reporter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const util = require('util');
const fs = require('fs');
const path = require('path');
const { requireModule } = require('../helper/helper');
const { requireModule, nodeRequestForLogs } = require('../helper/helper');
const Base = requireModule('mocha/lib/reporters/base.js'),
utils = requireModule('mocha/lib/utils.js');
const color = Base.color;
Expand All @@ -15,6 +15,8 @@ const { v4: uuidv4 } = require('uuid');
const { IPC_EVENTS } = require('../helper/constants');
const { startIPCServer } = require('../plugin/ipcServer');

const ipc = require('node-ipc');

const HOOK_TYPES_MAP = {
"before all": "BEFORE_ALL",
"after all": "AFTER_ALL",
Expand Down Expand Up @@ -123,7 +125,9 @@ class MyReporter {
})

.on(EVENT_TEST_PASS, async (test) => {
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_PASS`);
if(this.testObservability == true) {
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_PASS for uuid: ${test.testAnalyticsId}`);
if(!this.runStatusMarkedHash[test.testAnalyticsId]) {
if(test.testAnalyticsId) this.runStatusMarkedHash[test.testAnalyticsId] = true;
await this.sendTestRunEvent(test);
Expand All @@ -132,7 +136,9 @@ class MyReporter {
})

.on(EVENT_TEST_FAIL, async (test, err) => {
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_FAIL`);
if(this.testObservability == true) {
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_FAIL for uuid: ${test.testAnalyticsId}`);
if((test.testAnalyticsId && !this.runStatusMarkedHash[test.testAnalyticsId]) || (test.hookAnalyticsId && !this.runStatusMarkedHash[test.hookAnalyticsId])) {
if(test.testAnalyticsId) {
this.runStatusMarkedHash[test.testAnalyticsId] = true;
Expand All @@ -146,8 +152,10 @@ class MyReporter {
})

.on(EVENT_TEST_PENDING, async (test) => {
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_PENDING`);
if(this.testObservability == true) {
if(!test.testAnalyticsId) test.testAnalyticsId = uuidv4();
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_PENDING for uuid: ${test.testAnalyticsId}`);
if(!this.runStatusMarkedHash[test.testAnalyticsId]) {
this.runStatusMarkedHash[test.testAnalyticsId] = true;
await this.sendTestRunEvent(test,undefined,false,"TestRunSkipped");
Expand All @@ -156,13 +164,17 @@ class MyReporter {
})

.on(EVENT_TEST_BEGIN, async (test) => {
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_BEGIN`);
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_BEGIN for uuid: ${test.testAnalyticsId}`);
if (this.runStatusMarkedHash[test.testAnalyticsId]) return;
if(this.testObservability == true) {
await this.testStarted(test);
}
})

.on(EVENT_TEST_END, async (test) => {
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_END`);
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_BEGIN for uuid: ${test.testAnalyticsId}`);
if (this.runStatusMarkedHash[test.testAnalyticsId]) return;
if(this.testObservability == true) {
if(!this.runStatusMarkedHash[test.testAnalyticsId]) {
Expand All @@ -186,7 +198,6 @@ class MyReporter {
}

await this.uploadTestSteps();
await requestQueueHandler.shutdown();
});
}

Expand All @@ -199,6 +210,7 @@ class MyReporter {
server.on(IPC_EVENTS.COMMAND, this.cypressCommandListener.bind(this));
server.on(IPC_EVENTS.CUCUMBER, this.cypressCucumberStepListener.bind(this));
server.on(IPC_EVENTS.PLATFORM_DETAILS, this.cypressPlatformDetailsListener.bind(this));
this.ipcServer = server;
},
(server) => {
server.off(IPC_EVENTS.CONFIG, '*');
Expand All @@ -214,6 +226,7 @@ class MyReporter {
this.current_test = test;
test.retryOf = null;
test.testAnalyticsId = uuidv4();
await nodeRequestForLogs(`[MOCHA EVENT] EVENT_TEST_BEGIN for uuid: ${test.testAnalyticsId}`);
test.started_at = (new Date()).toISOString();
test.test_started_at = test.started_at;
if(test._currentRetry > 0 && lastTest && lastTest.title == test.title) {
Expand Down Expand Up @@ -319,6 +332,8 @@ class MyReporter {
}
};

await nodeRequestForLogs(`${eventType} for uuid: ${testData.uuid}`);

if(eventType.match(/TestRunFinished/) || eventType.match(/TestRunSkipped/)) {
testData['meta'].steps = JSON.parse(JSON.stringify(this.currentTestCucumberSteps));
this.currentTestCucumberSteps = [];
Expand Down
Loading