Skip to content

Commit

Permalink
WIP Test DNS monitor
Browse files Browse the repository at this point in the history
  • Loading branch information
powerivq committed Feb 14, 2020
1 parent 882bc62 commit 2575952
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 12 deletions.
13 changes: 8 additions & 5 deletions build-system/pr-check/checks.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const {
stopTimer,
stopTimedJob,
timedExecOrDie: timedExecOrDieBase,
timedExecOrDieWithDnsMonitor: timedExecOrDieWithDnsMonitorBase,
} = require('./utils');
const {determineBuildTargets} = require('./build-targets');
const {isTravisPullRequestBuild} = require('../common/travis');
Expand All @@ -37,6 +38,8 @@ const {runYarnChecks} = require('./yarn-checks');
const FILENAME = 'checks.js';
const timedExecOrDie = (cmd, unusedFileName) =>
timedExecOrDieBase(cmd, FILENAME);
const timedExecOrDieWithDnsMonitor = (cmd, unusedFileName) =>
timedExecOrDieWithDnsMonitorBase(cmd, FILENAME);

async function main() {
const startTime = startTimer(FILENAME, FILENAME);
Expand All @@ -61,12 +64,12 @@ async function main() {
printChangeSummary(FILENAME);
const buildTargets = determineBuildTargets(FILENAME);
await reportAllExpectedTests(buildTargets);
timedExecOrDie('gulp update-packages');
await timedExecOrDieWithDnsMonitor('gulp update-packages');

timedExecOrDie('gulp check-exact-versions');
timedExecOrDie('gulp lint');
timedExecOrDie('gulp prettify');
timedExecOrDie('gulp presubmit');
await timedExecOrDieWithDnsMonitor('gulp check-exact-versions');
await timedExecOrDieWithDnsMonitor('gulp lint');
await timedExecOrDieWithDnsMonitor('gulp prettify');
await timedExecOrDieWithDnsMonitor('gulp presubmit');

if (buildTargets.has('AVA')) {
timedExecOrDie('gulp ava');
Expand Down
25 changes: 19 additions & 6 deletions build-system/pr-check/local-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const {
startTimer,
stopTimer,
timedExecOrDie: timedExecOrDieBase,
timedExecOrDieWithDnsMonitor: timedExecOrDieWithDnsMonitorBase,
} = require('./utils');
const {determineBuildTargets} = require('./build-targets');
const {isTravisPullRequestBuild} = require('../common/travis');
Expand All @@ -36,15 +37,21 @@ const FILENAME = 'local-tests.js';
const FILELOGPREFIX = colors.bold(colors.yellow(`${FILENAME}:`));
const timedExecOrDie = (cmd, unusedFileName) =>
timedExecOrDieBase(cmd, FILENAME);
const timedExecOrDieWithDnsMonitor = (cmd, unusedFileName) =>
timedExecOrDieWithDnsMonitorBase(cmd, FILENAME);

function main() {
async function main() {
const startTime = startTimer(FILENAME, FILENAME);

if (!isTravisPullRequestBuild()) {
downloadBuildOutput(FILENAME);
timedExecOrDie('gulp update-packages');
timedExecOrDie('gulp integration --nobuild --headless --coverage');
timedExecOrDie('gulp unit --nobuild --headless --coverage');
await timedExecOrDieWithDnsMonitor(
'gulp integration --nobuild --headless --coverage'
);
await timedExecOrDieWithDnsMonitor(
'gulp unit --nobuild --headless --coverage'
);
timedExecOrDie('gulp codecov-upload');
} else {
printChangeSummary(FILENAME);
Expand All @@ -69,19 +76,25 @@ function main() {
timedExecOrDie('gulp update-packages');

if (buildTargets.has('RUNTIME') || buildTargets.has('UNIT_TEST')) {
timedExecOrDie('gulp unit --nobuild --headless --local_changes');
await timedExecOrDieWithDnsMonitor(
'gulp unit --nobuild --headless --local_changes'
);
}

if (
buildTargets.has('RUNTIME') ||
buildTargets.has('FLAG_CONFIG') ||
buildTargets.has('INTEGRATION_TEST')
) {
timedExecOrDie('gulp integration --nobuild --headless --coverage');
await timedExecOrDieWithDnsMonitor(
'gulp integration --nobuild --headless --coverage'
);
}

if (buildTargets.has('RUNTIME') || buildTargets.has('UNIT_TEST')) {
timedExecOrDie('gulp unit --nobuild --headless --coverage');
await timedExecOrDieWithDnsMonitor(
'gulp unit --nobuild --headless --coverage'
);
}

if (buildTargets.has('RUNTIME')) {
Expand Down
60 changes: 59 additions & 1 deletion build-system/pr-check/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@

const colors = require('ansi-colors');
const requestPromise = require('request-promise');
const {
execOrDie,
execScriptAsync,
execWithError,
exec,
} = require('../common/exec');
const {
gitBranchCreationPoint,
gitBranchName,
Expand All @@ -31,7 +37,6 @@ const {
travisBuildNumber,
travisPullRequestSha,
} = require('../common/travis');
const {execOrDie, execWithError, exec} = require('../common/exec');
const {replaceUrls, signalDistUpload} = require('../tasks/pr-deploy-bot-utils');

const BUILD_OUTPUT_FILE = isTravisBuild()
Expand Down Expand Up @@ -131,6 +136,29 @@ async function startSauceConnect(functionName) {
execOrDie(startScCmd);
}

/**
* Start DNS monitor
* @return {!Promise}
*/
function startDnsMonitor_() {
// if (!isTravisBuild()) {
// return Promise.resolve(null);
// }

let resolver;
const deferred = new Promise(resolverIn => {
resolver = resolverIn;
});

const dnsProcess = execScriptAsync('gulp dns-monitor --nonblocking');

// Wait for the heartbeat from stderr before starting the test
dnsProcess.stderr.on('data', () => {
resolver(dnsProcess);
});
return deferred;
}

/**
* Stops connection to Sauce Labs
* @param {string} functionName
Expand Down Expand Up @@ -233,6 +261,35 @@ function timedExecOrDie(cmd, fileName = 'utils.js') {
stopTimer(cmd, fileName, startTime);
}

/**
* Executes the command and check unpermitted DNS requests in the interim. In
* case if failure, it terminates.
* @param {string} cmd
* @param {string} fileName
*/
async function timedExecOrDieWithDnsMonitor(cmd, fileName = 'utils.js') {
return startDnsMonitor_().then(dnsMonitor => {
timedExecOrDie(cmd, fileName);
if (dnsMonitor) {
dnsMonitor.stdout.pipe(process.stdout);
dnsMonitor.stderr.pipe(process.stderr);
dnsMonitor.on('exit', code => {
if (code) {
process.exit(1);
}
resolver();
});
dnsMonitor.kill('SIGTERM');
let resolver;
return new Promise(resolverIn => {
resolver = resolverIn;
});
} else {
return Promise.resolve();
}
});
}

/**
* Download output helper
* @param {string} functionName
Expand Down Expand Up @@ -387,6 +444,7 @@ module.exports = {
stopTimedJob,
timedExec,
timedExecOrDie,
timedExecOrDieWithDnsMonitor,
timedExecWithError,
uploadBuildOutput,
uploadDistOutput,
Expand Down
122 changes: 122 additions & 0 deletions build-system/tasks/dns-monitor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

const argv = require('minimist')(process.argv.slice(2));
const colors = require('ansi-colors');
const log = require('fancy-log');
const readline = require('readline');
const {exec, execScriptAsync} = require('../common/exec');

const PERMITTED_DOMAIN_REGEXES = [
/\.internal$/, // Travis use
/^(amp-test-status-bot)\.appspot\.com$/, // Infra team use
/\.test$/, // .test TLD is safe to use for tests
];

const TCPDUMP_DNS_REGEX = /^(\d{2}:\d{2}:\d{2})\.\d{6} IP6? [^ ]+ > [^ ]+: \d+\+ (A|AAAA)\? ([^ ]+)\. \(\d+\)$/;

/**
* Monitor DNS requests
* @return {!Promise}
*/
function dnsMonitor() {
let resolver;
const deferred = new Promise(resolverIn => {
resolver = resolverIn;
});

const monProcess = execScriptAsync('sudo tcpdump -l port 53');
console.log(monProcess.pid);
monProcess.unref();
console.log(monProcess.pid);
const domainAccesses = new Map();

const onFinish = () => {
if (domainAccesses.size) {
log(
'Tests that send requests to external domains might slow',
'down the tests and cause flakiness.'
);
log(
'If possible, please change them to a *.test domain since',
'they never resolve.'
);
log(
'If these requests are indeed necessary to the test, go to',
colors.cyan('build-system/tasks/dns-monitor.js'),
'to permit it.'
);
if (!argv.nonblocking) {
process.exitCode = 1;
}
} else {
log(colors.green('No impermissible external requests found'));
}
for (const [domain, ts] of domainAccesses.entries()) {
log(ts, 'Request(s) to', colors.red(domain), 'recorded.');
}

monProcess.kill();
execScriptAsync('sudo kill ' + monProcess.pid);
setTimeout(() => {
execScriptAsync('sudo ps -ef').stdout.pipe(process.stdout);
execScriptAsync('sudo kill -9 ' + monProcess.pid);
}, 500);
resolver();
};
process.on('SIGTERM', onFinish);
process.on('SIGINT', onFinish);

console.error('DNS Ready'); // To signal that the monitor is up.
readline
.createInterface({
input: monProcess.stdout,
})
.on('line', line => {
handleLog_(line, domainAccesses);
});
return deferred;
}

function handleLog_(line, domainAccesses) {
const match = TCPDUMP_DNS_REGEX.exec(line);
if (
match &&
!domainAccesses.has(match[3]) &&
!matchOneOfRegexes(match[3], PERMITTED_DOMAIN_REGEXES)
) {
domainAccesses.set(match[3], match[1]);
}
}

function matchOneOfRegexes(testStr, regexes) {
for (let i = 0; i < regexes.length; i++) {
if (regexes[i].exec(testStr)) {
return true;
}
}
return false;
}

module.exports = {
dnsMonitor,
};

dnsMonitor.description =
'Check if there are unpermitted DNS requests (requires sudo)';
dnsMonitor.flags = {
'nonblocking': 'Do not block the test even if the test fails',
};
1 change: 1 addition & 0 deletions build-system/tasks/presubmit-checks.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ const forbiddenTerms = {
'build-system/tasks/check-owners.js',
'build-system/tasks/check-types.js',
'build-system/tasks/dist.js',
'build-system/tasks/dns-monitor.js',
'build-system/tasks/generate-runner.js',
'build-system/tasks/helpers.js',
'build-system/tasks/prettify.js',
Expand Down
2 changes: 2 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const {csvifySize} = require('./build-system/tasks/csvify-size');
const {depCheck} = require('./build-system/tasks/dep-check');
const {devDashboardTests} = require('./build-system/tasks/dev-dashboard-tests');
const {dist} = require('./build-system/tasks/dist');
const {dnsMonitor} = require('./build-system/tasks/dns-monitor');
const {e2e} = require('./build-system/tasks/e2e');
const {firebase} = require('./build-system/tasks/firebase');
const {getZindex} = require('./build-system/tasks/get-zindex');
Expand Down Expand Up @@ -89,6 +90,7 @@ gulp.task('default', defaultTask);
gulp.task('dep-check', depCheck);
gulp.task('dev-dashboard-tests', devDashboardTests);
gulp.task('dist', dist);
gulp.task('dns-monitor', dnsMonitor);
gulp.task('e2e', e2e);
gulp.task('firebase', firebase);
gulp.task('generate-vendor-jsons', generateVendorJsons);
Expand Down

0 comments on commit 2575952

Please sign in to comment.