testaro 6.0.0 → 6.0.2
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 +94 -130
package/package.json
CHANGED
package/watch.js
CHANGED
|
@@ -1,66 +1,70 @@
|
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
try {
|
|
41
|
+
const jobDirFileNames = await fs.readdir(watchDir);
|
|
42
|
+
const jobFileNames = jobDirFileNames.filter(fileName => fileName.endsWith('.json'));
|
|
43
|
+
if (jobFileNames.length) {
|
|
44
|
+
const scriptJSON = await fs.readFile(`${watchDir}/${jobFileNames[0]}`, 'utf8');
|
|
45
|
+
try {
|
|
46
|
+
const script = JSON.parse(scriptJSON, null, 2);
|
|
47
|
+
return script;
|
|
48
|
+
}
|
|
49
|
+
catch(error) {
|
|
50
|
+
return {
|
|
51
|
+
error: 'ERROR: Script was not JSON',
|
|
52
|
+
message: error.message
|
|
53
|
+
};
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
error: 'ERROR: Job was not JSON',
|
|
52
|
-
message: error.message
|
|
53
|
-
};
|
|
56
|
+
else {
|
|
57
|
+
return {};
|
|
54
58
|
}
|
|
55
59
|
}
|
|
56
|
-
|
|
60
|
+
catch {
|
|
57
61
|
return {};
|
|
58
62
|
}
|
|
59
63
|
};
|
|
60
64
|
// Checks for a network job.
|
|
61
65
|
const checkNetJob = async () => {
|
|
62
|
-
const
|
|
63
|
-
const wholeURL = `${process.env.PROTOCOL}://${jobURL}?
|
|
66
|
+
const script = await new Promise(resolve => {
|
|
67
|
+
const wholeURL = `${process.env.PROTOCOL}://${jobURL}?worker=${worker}`;
|
|
64
68
|
const request = client.request(wholeURL, response => {
|
|
65
69
|
const chunks = [];
|
|
66
70
|
response.on('data', chunk => {
|
|
@@ -68,18 +72,10 @@ const checkNetJob = async () => {
|
|
|
68
72
|
});
|
|
69
73
|
response.on('end', () => {
|
|
70
74
|
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
|
-
}
|
|
75
|
+
const scriptJSON = chunks.join('');
|
|
76
|
+
const script = JSON.parse(scriptJSON);
|
|
77
|
+
// Return it.
|
|
78
|
+
resolve(script);
|
|
83
79
|
}
|
|
84
80
|
catch(error) {
|
|
85
81
|
resolve({
|
|
@@ -92,20 +88,21 @@ const checkNetJob = async () => {
|
|
|
92
88
|
});
|
|
93
89
|
request.end();
|
|
94
90
|
});
|
|
95
|
-
return
|
|
91
|
+
return script;
|
|
96
92
|
};
|
|
97
93
|
// Writes a directory report.
|
|
98
94
|
const writeDirReport = async report => {
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
101
|
-
const reportJSON = JSON.stringify(report, null, 2);
|
|
95
|
+
const scriptID = report && report.script && report.script.id;
|
|
96
|
+
if (scriptID) {
|
|
102
97
|
try {
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
const reportJSON = JSON.stringify(report, null, 2);
|
|
99
|
+
const reportName = `${report.timeStamp}-${scriptID}.json`;
|
|
100
|
+
await fs.writeFile(`${reportDir}/${reportName}`, reportJSON);
|
|
101
|
+
console.log(`Report ${reportName} saved`);
|
|
105
102
|
return true;
|
|
106
103
|
}
|
|
107
104
|
catch(error) {
|
|
108
|
-
console.log(`ERROR: Failed to write report ${
|
|
105
|
+
console.log(`ERROR: Failed to write report (${error.message})`);
|
|
109
106
|
return false;
|
|
110
107
|
}
|
|
111
108
|
}
|
|
@@ -113,7 +110,7 @@ const writeDirReport = async report => {
|
|
|
113
110
|
// Submits a network report.
|
|
114
111
|
const writeNetReport = async report => {
|
|
115
112
|
const ack = await new Promise(resolve => {
|
|
116
|
-
const wholeURL = `${process.env.PROTOCOL}://${reportURL}?
|
|
113
|
+
const wholeURL = `${process.env.PROTOCOL}://${reportURL}?worker=${worker}`;
|
|
117
114
|
const request = client.request(wholeURL, {method: 'POST'}, response => {
|
|
118
115
|
const chunks = [];
|
|
119
116
|
response.on('data', chunk => {
|
|
@@ -134,15 +131,15 @@ const writeNetReport = async report => {
|
|
|
134
131
|
});
|
|
135
132
|
request.write(JSON.stringify(report, null, 2));
|
|
136
133
|
request.end();
|
|
137
|
-
console.log(`Report
|
|
134
|
+
console.log(`Report ${report.timeStamp}-${report.script.id} submitted`);
|
|
138
135
|
});
|
|
139
136
|
return ack;
|
|
140
137
|
};
|
|
141
138
|
// Archives a job.
|
|
142
|
-
const
|
|
143
|
-
const jobJSON = JSON.stringify(
|
|
144
|
-
await fs.writeFile(`${
|
|
145
|
-
await fs.rm(`${
|
|
139
|
+
const archiveJob = async script => {
|
|
140
|
+
const jobJSON = JSON.stringify(script, null, 2);
|
|
141
|
+
await fs.writeFile(`${doneDir}/${script.timeStamp}-${script.id}.json`, jobJSON);
|
|
142
|
+
await fs.rm(`${watchDir}/${script.id}.json`);
|
|
146
143
|
};
|
|
147
144
|
// Waits.
|
|
148
145
|
const wait = ms => {
|
|
@@ -152,117 +149,84 @@ const wait = ms => {
|
|
|
152
149
|
}, ms);
|
|
153
150
|
});
|
|
154
151
|
};
|
|
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;
|
|
152
|
+
// Runs a script, time-stamps it, and returns a report.
|
|
153
|
+
const runJob = async script => {
|
|
154
|
+
const {id} = script;
|
|
155
|
+
if (id) {
|
|
156
|
+
try {
|
|
157
|
+
// Identify the start time and a time stamp.
|
|
158
|
+
const timeStamp = Math.floor((Date.now() - Date.UTC(2022, 1)) / 2000).toString(36);
|
|
159
|
+
script.timeStamp = timeStamp;
|
|
160
|
+
// Initialize a report.
|
|
161
|
+
const report = {
|
|
162
|
+
log: [],
|
|
163
|
+
script,
|
|
164
|
+
acts: []
|
|
165
|
+
};
|
|
166
|
+
await doJob(report);
|
|
167
|
+
if (watchType === 'dir') {
|
|
168
|
+
return await writeDirReport(report);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
const ack = await writeNetReport(report);
|
|
172
|
+
if (ack.error) {
|
|
173
|
+
console.log(JSON.stringify(ack, null, 2));
|
|
174
|
+
return false;
|
|
203
175
|
}
|
|
204
|
-
// Otherwise, i.e. if there is no batch:
|
|
205
176
|
else {
|
|
206
|
-
|
|
207
|
-
return await runHost(jobID, timeStamp, timeStamp, script);
|
|
177
|
+
return true;
|
|
208
178
|
}
|
|
209
179
|
}
|
|
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
180
|
}
|
|
217
|
-
|
|
218
|
-
console.log(
|
|
181
|
+
catch(error) {
|
|
182
|
+
console.log(`ERROR: ${error.message}\n${error.stack}`);
|
|
219
183
|
return {
|
|
220
|
-
error:
|
|
184
|
+
error: `ERROR: ${error.message}\n${error.stack}`
|
|
221
185
|
};
|
|
222
186
|
}
|
|
223
187
|
}
|
|
224
188
|
else {
|
|
225
|
-
console.log('ERROR:
|
|
189
|
+
console.log('ERROR: script has no id');
|
|
226
190
|
return {
|
|
227
|
-
error: 'ERROR:
|
|
191
|
+
error: 'ERROR: script has no id'
|
|
228
192
|
};
|
|
229
193
|
}
|
|
230
194
|
};
|
|
231
|
-
//
|
|
195
|
+
// Checks for a job, performs it, and submits a report, once or repeatedly.
|
|
232
196
|
const cycle = async forever => {
|
|
233
|
-
const intervalMS = Number.parseInt(interval);
|
|
197
|
+
const intervalMS = 1000 * Number.parseInt(interval);
|
|
234
198
|
let statusOK = true;
|
|
235
199
|
let empty = false;
|
|
236
200
|
console.log(`Watching started with intervals of ${interval} seconds when idle`);
|
|
237
201
|
while (statusOK) {
|
|
238
202
|
if (empty) {
|
|
239
|
-
await wait(
|
|
203
|
+
await wait(intervalMS);
|
|
240
204
|
}
|
|
241
205
|
// Check for a job.
|
|
242
|
-
let
|
|
206
|
+
let script;
|
|
243
207
|
if (watchType === 'dir') {
|
|
244
|
-
|
|
208
|
+
script = await checkDirJob();
|
|
245
209
|
}
|
|
246
210
|
else if (watchType === 'net') {
|
|
247
|
-
|
|
211
|
+
script = await checkNetJob();
|
|
248
212
|
}
|
|
249
213
|
else {
|
|
250
|
-
|
|
214
|
+
script = {};
|
|
251
215
|
console.log('ERROR: invalid WATCH_TYPE environment variable');
|
|
252
216
|
statusOK = false;
|
|
253
217
|
}
|
|
254
218
|
// If there was one:
|
|
255
|
-
if (
|
|
256
|
-
// Run it.
|
|
257
|
-
console.log(`Running
|
|
258
|
-
statusOK = await runJob(
|
|
259
|
-
console.log(`Job ${
|
|
219
|
+
if (script.id) {
|
|
220
|
+
// Run it, add a timestamp to it, and save a report.
|
|
221
|
+
console.log(`Running script ${script.id}`);
|
|
222
|
+
statusOK = await runJob(script);
|
|
223
|
+
console.log(`Job ${script.id} finished with time stamp ${script.timeStamp}`);
|
|
260
224
|
if (statusOK) {
|
|
261
|
-
// If the
|
|
225
|
+
// If the script was in a directory:
|
|
262
226
|
if (watchType === 'dir') {
|
|
263
|
-
// Archive
|
|
264
|
-
await
|
|
265
|
-
console.log(`
|
|
227
|
+
// Archive the script.
|
|
228
|
+
await archiveJob(script);
|
|
229
|
+
console.log(`Script ${script.id}.json archived as ${script.timeStamp}-${script.id}.json`);
|
|
266
230
|
}
|
|
267
231
|
// If watching was specified for only 1 job, stop.
|
|
268
232
|
statusOK = forever;
|