testaro 6.0.0 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/watch.js +81 -122
package/package.json
CHANGED
package/watch.js
CHANGED
|
@@ -1,54 +1,53 @@
|
|
|
1
1
|
/*
|
|
2
2
|
watch.js
|
|
3
3
|
Watches for a script and runs it.
|
|
4
|
+
Arguments:
|
|
5
|
+
0. Watch type: 'dir' or 'net'.
|
|
6
|
+
1. How long to watch: 'once' or 'forever'.
|
|
7
|
+
2. How often to check in seconds.
|
|
8
|
+
Usage example: node watch dir once 15
|
|
4
9
|
*/
|
|
5
10
|
|
|
6
11
|
// ########## IMPORTS
|
|
7
12
|
|
|
8
13
|
// Module to keep secrets local.
|
|
9
|
-
// require('dotenv').config({override: true});
|
|
10
14
|
require('dotenv').config();
|
|
11
15
|
// Module to read and write files.
|
|
12
16
|
const fs = require('fs/promises');
|
|
13
17
|
// Module to perform tests.
|
|
14
18
|
const {doJob} = require('./run');
|
|
15
|
-
// Module to convert a script and a batch to a batch-based array of scripts.
|
|
16
|
-
const {batchify} = require('./batchify');
|
|
17
19
|
|
|
18
20
|
// ########## CONSTANTS
|
|
19
21
|
|
|
20
|
-
const watchType = process.
|
|
22
|
+
const watchType = process.argv[2];
|
|
23
|
+
const watchForever = process.argv[3] === 'forever';
|
|
24
|
+
const interval = Number.parseInt(process.argv[4]);
|
|
21
25
|
let client;
|
|
22
26
|
if (watchType === 'net') {
|
|
23
27
|
client = require(process.env.PROTOCOL || 'https');
|
|
24
28
|
}
|
|
25
29
|
const jobURL = process.env.JOB_URL;
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
30
|
+
const worker = process.env.WORKER;
|
|
31
|
+
const watchDir = process.env.WATCHDIR;
|
|
32
|
+
const doneDir = process.env.DONEDIR;
|
|
29
33
|
const reportURL = process.env.REPORT_URL;
|
|
30
34
|
const reportDir = process.env.REPORTDIR;
|
|
31
|
-
const interval = process.env.INTERVAL;
|
|
32
|
-
// Values of process.env properties are coerced to strings.
|
|
33
|
-
const watchForever = process.env.WATCH_FOREVER == 'true';
|
|
34
35
|
|
|
35
36
|
// ########## FUNCTIONS
|
|
36
37
|
|
|
37
38
|
// Checks for a directory job.
|
|
38
39
|
const checkDirJob = async () => {
|
|
39
|
-
const jobDirFileNames = await fs.readdir(
|
|
40
|
+
const jobDirFileNames = await fs.readdir(watchDir);
|
|
40
41
|
const jobFileNames = jobDirFileNames.filter(fileName => fileName.endsWith('.json'));
|
|
41
42
|
if (jobFileNames.length) {
|
|
42
|
-
const
|
|
43
|
-
const jobJSON = await fs.readFile(`${jobDir}/${jobFileNames[0]}`, 'utf8');
|
|
43
|
+
const scriptJSON = await fs.readFile(`${watchDir}/${jobFileNames[0]}`, 'utf8');
|
|
44
44
|
try {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
return job;
|
|
45
|
+
const script = JSON.parse(scriptJSON, null, 2);
|
|
46
|
+
return script;
|
|
48
47
|
}
|
|
49
48
|
catch(error) {
|
|
50
49
|
return {
|
|
51
|
-
error: 'ERROR:
|
|
50
|
+
error: 'ERROR: Script was not JSON',
|
|
52
51
|
message: error.message
|
|
53
52
|
};
|
|
54
53
|
}
|
|
@@ -59,8 +58,8 @@ const checkDirJob = async () => {
|
|
|
59
58
|
};
|
|
60
59
|
// Checks for a network job.
|
|
61
60
|
const checkNetJob = async () => {
|
|
62
|
-
const
|
|
63
|
-
const wholeURL = `${process.env.PROTOCOL}://${jobURL}?
|
|
61
|
+
const script = await new Promise(resolve => {
|
|
62
|
+
const wholeURL = `${process.env.PROTOCOL}://${jobURL}?worker=${worker}`;
|
|
64
63
|
const request = client.request(wholeURL, response => {
|
|
65
64
|
const chunks = [];
|
|
66
65
|
response.on('data', chunk => {
|
|
@@ -68,18 +67,10 @@ const checkNetJob = async () => {
|
|
|
68
67
|
});
|
|
69
68
|
response.on('end', () => {
|
|
70
69
|
try {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
// Return it.
|
|
76
|
-
resolve(job);
|
|
77
|
-
}
|
|
78
|
-
// Otherwise, i.e. if there was no qualifying job:
|
|
79
|
-
else {
|
|
80
|
-
// Return this.
|
|
81
|
-
resolve({});
|
|
82
|
-
}
|
|
70
|
+
const scriptJSON = chunks.join('');
|
|
71
|
+
const script = JSON.parse(scriptJSON);
|
|
72
|
+
// Return it.
|
|
73
|
+
resolve(script);
|
|
83
74
|
}
|
|
84
75
|
catch(error) {
|
|
85
76
|
resolve({
|
|
@@ -92,20 +83,21 @@ const checkNetJob = async () => {
|
|
|
92
83
|
});
|
|
93
84
|
request.end();
|
|
94
85
|
});
|
|
95
|
-
return
|
|
86
|
+
return script;
|
|
96
87
|
};
|
|
97
88
|
// Writes a directory report.
|
|
98
89
|
const writeDirReport = async report => {
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
101
|
-
const reportJSON = JSON.stringify(report, null, 2);
|
|
90
|
+
const scriptID = report && report.script && report.script.id;
|
|
91
|
+
if (scriptID) {
|
|
102
92
|
try {
|
|
103
|
-
|
|
104
|
-
|
|
93
|
+
const reportJSON = JSON.stringify(report, null, 2);
|
|
94
|
+
const reportName = `${report.timeStamp}-${scriptID}.json`;
|
|
95
|
+
await fs.writeFile(`${reportDir}/${reportName}`, reportJSON);
|
|
96
|
+
console.log(`Report ${reportName} saved`);
|
|
105
97
|
return true;
|
|
106
98
|
}
|
|
107
99
|
catch(error) {
|
|
108
|
-
console.log(`ERROR: Failed to write report ${
|
|
100
|
+
console.log(`ERROR: Failed to write report (${error.message})`);
|
|
109
101
|
return false;
|
|
110
102
|
}
|
|
111
103
|
}
|
|
@@ -113,7 +105,7 @@ const writeDirReport = async report => {
|
|
|
113
105
|
// Submits a network report.
|
|
114
106
|
const writeNetReport = async report => {
|
|
115
107
|
const ack = await new Promise(resolve => {
|
|
116
|
-
const wholeURL = `${process.env.PROTOCOL}://${reportURL}?
|
|
108
|
+
const wholeURL = `${process.env.PROTOCOL}://${reportURL}?worker=${worker}`;
|
|
117
109
|
const request = client.request(wholeURL, {method: 'POST'}, response => {
|
|
118
110
|
const chunks = [];
|
|
119
111
|
response.on('data', chunk => {
|
|
@@ -134,15 +126,15 @@ const writeNetReport = async report => {
|
|
|
134
126
|
});
|
|
135
127
|
request.write(JSON.stringify(report, null, 2));
|
|
136
128
|
request.end();
|
|
137
|
-
console.log(`Report
|
|
129
|
+
console.log(`Report ${report.timeStamp}-${report.script.id} submitted`);
|
|
138
130
|
});
|
|
139
131
|
return ack;
|
|
140
132
|
};
|
|
141
133
|
// Archives a job.
|
|
142
|
-
const
|
|
143
|
-
const jobJSON = JSON.stringify(
|
|
144
|
-
await fs.writeFile(`${
|
|
145
|
-
await fs.rm(`${
|
|
134
|
+
const archiveJob = async script => {
|
|
135
|
+
const jobJSON = JSON.stringify(script, null, 2);
|
|
136
|
+
await fs.writeFile(`${doneDir}/${script.timeStamp}-${script.id}.json`, jobJSON);
|
|
137
|
+
await fs.rm(`${watchDir}/${script.id}.json`);
|
|
146
138
|
};
|
|
147
139
|
// Waits.
|
|
148
140
|
const wait = ms => {
|
|
@@ -152,117 +144,84 @@ const wait = ms => {
|
|
|
152
144
|
}, ms);
|
|
153
145
|
});
|
|
154
146
|
};
|
|
155
|
-
// Runs
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
// Runs a job and returns a report file for the script or each host.
|
|
181
|
-
const runJob = async job => {
|
|
182
|
-
const {jobID, script, batch} = job;
|
|
183
|
-
if (jobID) {
|
|
184
|
-
if (script) {
|
|
185
|
-
try {
|
|
186
|
-
// Identify the start time and a time stamp.
|
|
187
|
-
const timeStamp = Math.floor((Date.now() - Date.UTC(2022, 1)) / 2000).toString(36);
|
|
188
|
-
job.timeStamp = timeStamp;
|
|
189
|
-
// If there is a batch:
|
|
190
|
-
if (batch) {
|
|
191
|
-
// Convert the script to a set of host scripts.
|
|
192
|
-
const specs = batchify(script, batch, timeStamp);
|
|
193
|
-
// For each host script:
|
|
194
|
-
let success = true;
|
|
195
|
-
while (specs.length && success) {
|
|
196
|
-
// Run it and write or submit a report with a host-suffixed time-stamp ID.
|
|
197
|
-
const spec = specs.shift();
|
|
198
|
-
const {id} = spec;
|
|
199
|
-
const hostScript = spec.script;
|
|
200
|
-
success = await runHost(jobID, timeStamp, id, hostScript);
|
|
201
|
-
}
|
|
202
|
-
return success;
|
|
147
|
+
// Runs a script, time-stamps it, and returns a report.
|
|
148
|
+
const runJob = async script => {
|
|
149
|
+
const {id} = script;
|
|
150
|
+
if (id) {
|
|
151
|
+
try {
|
|
152
|
+
// Identify the start time and a time stamp.
|
|
153
|
+
const timeStamp = Math.floor((Date.now() - Date.UTC(2022, 1)) / 2000).toString(36);
|
|
154
|
+
script.timeStamp = timeStamp;
|
|
155
|
+
// Initialize a report.
|
|
156
|
+
const report = {
|
|
157
|
+
log: [],
|
|
158
|
+
script,
|
|
159
|
+
acts: []
|
|
160
|
+
};
|
|
161
|
+
await doJob(report);
|
|
162
|
+
if (watchType === 'dir') {
|
|
163
|
+
return await writeDirReport(report);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const ack = await writeNetReport(report);
|
|
167
|
+
if (ack.error) {
|
|
168
|
+
console.log(JSON.stringify(ack, null, 2));
|
|
169
|
+
return false;
|
|
203
170
|
}
|
|
204
|
-
// Otherwise, i.e. if there is no batch:
|
|
205
171
|
else {
|
|
206
|
-
|
|
207
|
-
return await runHost(jobID, timeStamp, timeStamp, script);
|
|
172
|
+
return true;
|
|
208
173
|
}
|
|
209
174
|
}
|
|
210
|
-
catch(error) {
|
|
211
|
-
console.log(`ERROR: ${error.message}\n${error.stack}`);
|
|
212
|
-
return {
|
|
213
|
-
error: `ERROR: ${error.message}\n${error.stack}`
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
175
|
}
|
|
217
|
-
|
|
218
|
-
console.log(
|
|
176
|
+
catch(error) {
|
|
177
|
+
console.log(`ERROR: ${error.message}\n${error.stack}`);
|
|
219
178
|
return {
|
|
220
|
-
error:
|
|
179
|
+
error: `ERROR: ${error.message}\n${error.stack}`
|
|
221
180
|
};
|
|
222
181
|
}
|
|
223
182
|
}
|
|
224
183
|
else {
|
|
225
|
-
console.log('ERROR:
|
|
184
|
+
console.log('ERROR: script has no id');
|
|
226
185
|
return {
|
|
227
|
-
error: 'ERROR:
|
|
186
|
+
error: 'ERROR: script has no id'
|
|
228
187
|
};
|
|
229
188
|
}
|
|
230
189
|
};
|
|
231
|
-
//
|
|
190
|
+
// Checks for a job, performs it, and submits a report, once or repeatedly.
|
|
232
191
|
const cycle = async forever => {
|
|
233
|
-
const intervalMS = Number.parseInt(interval);
|
|
192
|
+
const intervalMS = 1000 * Number.parseInt(interval);
|
|
234
193
|
let statusOK = true;
|
|
235
194
|
let empty = false;
|
|
236
195
|
console.log(`Watching started with intervals of ${interval} seconds when idle`);
|
|
237
196
|
while (statusOK) {
|
|
238
197
|
if (empty) {
|
|
239
|
-
await wait(
|
|
198
|
+
await wait(intervalMS);
|
|
240
199
|
}
|
|
241
200
|
// Check for a job.
|
|
242
|
-
let
|
|
201
|
+
let script;
|
|
243
202
|
if (watchType === 'dir') {
|
|
244
|
-
|
|
203
|
+
script = await checkDirJob();
|
|
245
204
|
}
|
|
246
205
|
else if (watchType === 'net') {
|
|
247
|
-
|
|
206
|
+
script = await checkNetJob();
|
|
248
207
|
}
|
|
249
208
|
else {
|
|
250
|
-
|
|
209
|
+
script = {};
|
|
251
210
|
console.log('ERROR: invalid WATCH_TYPE environment variable');
|
|
252
211
|
statusOK = false;
|
|
253
212
|
}
|
|
254
213
|
// If there was one:
|
|
255
|
-
if (
|
|
256
|
-
// Run it.
|
|
257
|
-
console.log(`Running
|
|
258
|
-
statusOK = await runJob(
|
|
259
|
-
console.log(`Job ${
|
|
214
|
+
if (script.id) {
|
|
215
|
+
// Run it, add a timestamp to it, and save a report.
|
|
216
|
+
console.log(`Running script ${script.id}`);
|
|
217
|
+
statusOK = await runJob(script);
|
|
218
|
+
console.log(`Job ${script.id} finished with time stamp ${script.timeStamp}`);
|
|
260
219
|
if (statusOK) {
|
|
261
|
-
// If the
|
|
220
|
+
// If the script was in a directory:
|
|
262
221
|
if (watchType === 'dir') {
|
|
263
|
-
// Archive
|
|
264
|
-
await
|
|
265
|
-
console.log(`
|
|
222
|
+
// Archive the script.
|
|
223
|
+
await archiveJob(script);
|
|
224
|
+
console.log(`Script ${script.id}.json archived as ${script.timeStamp}-${script.id}.json`);
|
|
266
225
|
}
|
|
267
226
|
// If watching was specified for only 1 job, stop.
|
|
268
227
|
statusOK = forever;
|