Skip to content

Commit 8c55139

Browse files
Merge branch 'Develop'
2 parents 1941a04 + 0ce4a4d commit 8c55139

33 files changed

+467
-2003
lines changed

API/Endpoints.js

Lines changed: 42 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,33 @@
11
import fs from 'fs';
22
import path from "path";
33
import crypto from "crypto";
4-
5-
import {
6-
getMinerPath,
7-
getMinerFile,
8-
} from "../App/ConfigUnpacker.js";
4+
import { getForeignMiner } from "./RequestHandlers.js";
5+
import { initSingleVenv, getVenvStatusDeleteIfDone } from "../App/PyVenvHelper.js";
6+
import { getMinerPath, getMinerFile } from "../App/ConfigUnpacker.js";
97
import {
108
stopProcess,
119
getStatusDeleteIfDone,
1210
getProcessStatusList,
1311
processStart,
1412
} from "../App/Wrapper.js";
15-
import {
16-
getForeignMiner,
17-
} from "./RequestHandlers.js";
18-
import {
19-
initSingleVenv,
20-
getVenvStatusDeleteIfDone,
21-
} from "../App/PyVenvHelper.js";
2213

2314
const port = 5000; // The host port express will listen on.
2415

2516
export function initEndpoints(app, configList) {
26-
app.get("/", function (req, res) {
17+
18+
app.get("/", function (req, res) {
2719
res.send("Default page");
2820
});
29-
app.get(`/ping`, function (req, res) {
21+
22+
app.get(`/ping`, function (req, res) { // return pong
3023
res.send("pong");
3124
});
32-
app.get(`/configurations`, function (req, res) {
25+
26+
app.get(`/configurations`, function (req, res) { // return all configurations
3327
res.send(configList);
3428
});
35-
// Endpoint to return algorithm file that needs to be shadowed.
36-
app.get(`/shadow/:minerId`, function (req, res) {
29+
30+
app.get(`/shadow/:minerId`, function (req, res) { // return algorthm file of a miner
3731
const minerId = req.params.minerId;
3832
console.log(`Getting a request on /shadow/${minerId}`);
3933
let requestedConfig = configList.find(miner => miner.MinerId == minerId);
@@ -52,37 +46,29 @@ export function initEndpoints(app, configList) {
5246
return;
5347
}
5448

55-
// else, all is good, return file
56-
res.setHeader('Content-disposition', 'attachment; filename=shadow-miner');
57-
58-
// TODO: Consider if these are necessary? Leave them out for now, since we're testing with a .py script.
59-
// res.setHeader('Content-type', 'application/x-msdownload'); //for exe file
60-
// res.setHeader('Content-type', 'application/x-rar-compressed'); //for rar file
49+
// else, no issues, return file
50+
res.setHeader('Content-disposition', 'attachment; filename=shadow-miner');
6151
var file = fs.createReadStream(pathToFile);
62-
file.pipe(res); //send file
52+
file.pipe(res);
6353
});
64-
// Endpoint to return requirements file that needs to be shadowed.
65-
app.get(`/shadow/requirements/:minerId`, function (req, res) {
54+
55+
app.get(`/shadow/requirements/:minerId`, function (req, res) { // Return requirements file of a miner
6656
console.log(`Getting a request on /shadow/requirements/${req.params.minerId}`);
6757
let requestedConfig = configList.find(miner => miner.MinerId == req.params.minerId);
6858
if(!requestedConfig.Shadow) res.status(400).send(`Invalid request, cannot shadow Miner with id \"${requestedConfig.MinerId}\" and label: \"${requestedConfig.MinerLabel}\".`);
6959
else {
7060
res.setHeader('Content-disposition', 'attachment; filename=shadow-miner');
71-
72-
// TODO: Consider if these are necessary? Leave them out for now, since we're testing with a .py script.
73-
// res.setHeader('Content-type', 'application/x-msdownload'); //for exe file
74-
// res.setHeader('Content-type', 'application/x-rar-compressed'); //for rar file
75-
const pathToFile = path.join(getMinerPath(requestedConfig), "requirements.txt"); // TODO: Name of the file shouldn't just be hardcoded in here.
61+
const pathToFile = path.join(getMinerPath(requestedConfig), "requirements.txt");
7662
if(!fs.existsSync(pathToFile)) res.status(404).send(`Unable to find requirements file for requested miner.`);
7763
else {
7864
var file = fs.createReadStream(pathToFile);
79-
file.pipe(res); //send file
65+
file.pipe(res);
8066
}
8167
}
8268
});
83-
// Initiate shadow process - request foreign miner on "shadow/:minerId" to get the foreign miner .exe/script
84-
app.post(`/shadow`, async function (req, res) {
85-
console.log(`Getting a request on /shadow`);
69+
70+
app.post(`/shadow`, async function (req, res) { // Initiate cloning process: request foreign miner on "shadow/:minerId" to get the foreign miner .exe/script
71+
console.log(`\nGetting a request on /shadow`);
8672

8773
const venvInitId = crypto.randomUUID();
8874
let body = await req.body;
@@ -98,55 +84,54 @@ export function initEndpoints(app, configList) {
9884
});
9985
});
10086

101-
app.get(`/shadow/status/:venvInitId`, async function (req, res) {
87+
88+
app.get(`/shadow/status/:venvInitId`, async function (req, res) { // Return status of cloning action
10289
let venvInitId = req.params.venvInitId;
10390
console.log(`Getting a request on /shadow/status for id ${venvInitId}`);
10491
let venvStatus = await getVenvStatusDeleteIfDone(venvInitId);
10592
if(venvStatus) res.status(200).send(venvStatus);
10693
else res.status(400).send(`No process exists with ID: ${venvInitId}`);
10794
});
10895

96+
app.post(`/miner`, async function (req, res) { // Start miner - return process id.
97+
console.log("Received POST request on /miner");
98+
function sendProcessId(processId, error) {
99+
if(error) {
100+
console.error("Error: " + error);
101+
res.status(400).send(error);
102+
}
103+
else {
104+
res.send(processId.toString());
105+
}
106+
}
107+
const body = await req.body;
108+
const ownUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
109+
processStart(sendProcessId, body, ownUrl, configList);
110+
});
109111

110-
111-
app.get(`/status`, async function (req, res) {
112+
app.get(`/status`, async function (req, res) { // Return status of all active miners
112113
console.log(`Getting a request on /status for status list`);
113114
res.status(200).send(getProcessStatusList());
114115
});
115116

116-
app.get(`/status/:processId`, async function (req, res) {
117+
app.get(`/status/:processId`, async function (req, res) { // Return status of specific miner
117118
let processId = req.params.processId;
118119
// console.log(`Getting a request on /status for id ${processId}`);
119120
let statusDict = await getStatusDeleteIfDone(processId);
120121
if(statusDict) res.status(200).send(statusDict);
121122
else res.status(400).send(`No process exists with ID: ${processId}`);
122123
});
123124

124-
app.delete(`/stop/:processId`, async function (req, res) {
125+
app.delete(`/stop/:processId`, async function (req, res) { // Stop miner - return confirmation.
125126
let processId = req.params.processId
126127
console.log(`Getting a request on /stop for id ${processId}`);
127128
let result = await stopProcess(processId);
128129
if(result) res.status(200).send(`Killed process with ID: ${processId}`);
129130
else res.status(400).send(`No active process with ID: ${processId}`);
130131
});
131132

132-
app.post(`/miner`, async function (req, res) {
133-
console.log("Received POST request on /miner");
134-
function sendProcessId(processId, error) {
135-
if(error) {
136-
console.error("Error: " + error);
137-
res.status(400).send(error);
138-
}
139-
else {
140-
// console.log(`Sending processId ${processId}`);
141-
res.send(processId.toString());
142-
}
143-
}
144-
const body = await req.body;
145-
const ownUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
146-
processStart(sendProcessId, body, ownUrl, configList);
147-
});
148133

149-
app.listen(port, '0.0.0.0', () => {
134+
app.listen(port, '0.0.0.0', () => { // Start listening on port {port}
150135
console.log(`Example app listening on port ${port}`);
151136
});
152137
}

API/RequestHandlers.js

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from "fs";
22
import FormData from "form-data";
33
import path from "path";
4-
import crypto from "crypto";
4+
import { appendUrl } from "../App/Utils.js";
55
import {
66
UpdateMetadata,
77
PostMetadata,
@@ -24,12 +24,6 @@ import {
2424
getMinerResourceOutputExtension,
2525
getMinerFile,
2626
} from "../App/ConfigUnpacker.js";
27-
import {
28-
appendUrl,
29-
removeFile,
30-
removeFolder,
31-
} from "../App/Utils.js";
32-
import { AxiosError } from "axios";
3327

3428
export const getForeignMiner = async (body, venvInitId) => {
3529
const shadowConfig = body.Config;
@@ -51,15 +45,14 @@ export const getForeignMiner = async (body, venvInitId) => {
5145
console.log("error when getting external miner: " + shadowMinerGetResult.data);
5246
throw "Error retrieving miner from external source: " + shadowMinerGetResult.data;
5347
}
54-
if(shadowExtension != "py"){ // Don't need to install dependencies if it's not a python script
48+
if(shadowExtension != "py"){ // Requirements are only installed for python scripts
5549
console.log("not a python miner, returning");
5650
return shadowConfig;
5751
}
5852
console.log("is a python miner!");
5953
const requirementsPath = path.join(shadowFolderPath, "requirements.txt");
6054
const requirementsGetResult = await GetFile(requirementsUrl, requirementsPath)
6155
if(requirementsGetResult.status !== 200){
62-
// removeFolder(shadowFolderPath); // TODO: Should delete the folder if it reaches here and fails, but only deletes the contents. Probably some asynchronous stuff.
6356
throw "Error retrieving requirements from external source: " + requirementsGetResult.data;
6457
}
6558
return shadowConfig;
@@ -68,23 +61,15 @@ export const getForeignMiner = async (body, venvInitId) => {
6861
export const getResourceFromRepo = async (url, filePath) => {
6962
const result = await GetFile(url, filePath);
7063
return result;
71-
// return {
72-
// response: result ? "File saved" : "Exception when writing downloaded file to Tmp folder.",
73-
// status: !!result,
74-
// }
7564
}
7665

7766
export const updateMetadata = async (body, resourceId, isDynamic) => {
67+
if(!resourceId) return "Never received any messages, resulting in the miner to never send a resource, which means it has no resourceId"
7868
console.log(`Updating metadata on url: ${appendUrl([getBodyOutputHostInit(body), resourceId]).toString()} to set Dynamic to: ${isDynamic}`);
7969
const data = new FormData();
8070
data.append("Dynamic", isDynamic.toString()); // If it's a stream miner, it should be marked as dynamic
81-
8271
const res = await UpdateMetadata(getBodyOutputHostInit(body), resourceId, data);
8372
return res;
84-
// return {
85-
// response: res.data,
86-
// status: res.status == 200,
87-
// };
8873
};
8974

9075
export const sendMetadata = async (body, minerToRun, ownUrl, parents) => {
@@ -95,9 +80,6 @@ export const sendMetadata = async (body, minerToRun, ownUrl, parents) => {
9580
SourceId: getMinerId(minerToRun),
9681
SourceLabel: getMinerLabel(minerToRun),
9782
});
98-
99-
console.log("Trying to send metadata");
100-
10183
const data = new FormData();
10284
data.append("Host", getBodyOutputHost(body)); // mqtt.eclipseprojects.io
10385
data.append("StreamTopic", getBodyOutputTopic(body)); // FilteredAlphabetStream
@@ -121,7 +103,7 @@ export const updateResourceOnRepo = async (body, minerResult, resourceId) => {
121103
const fileStream = fs.createReadStream(minerResult);
122104
const data = new FormData();
123105
data.append("field-name", fileStream, { knownLength: fileSizeInBytes });
124-
106+
125107
const res = await UpdateFile(getBodyOutputHost(body), resourceId, data);
126108
return {
127109
response: res.data,

API/Requests.js

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import axios from "axios";
22
import fs from "fs";
33
import { appendUrl } from "../App/Utils.js";
4-
// TODO: Delete this dict and its uses before hand-in
5-
var numUpdates = {};
64

75
export const GetMetadata = async (path, resourceId) => {
86
const url = appendUrl([path, resourceId]).toString();
@@ -45,26 +43,8 @@ export const PostMetadata = async (path, data) => {
4543
});;
4644
return {data: res.data, status: res.status};
4745
}
48-
// TODO: Delete? Don't think it was used
49-
// export const GetResource = async (path, resourceId) => {
50-
// const url = appendUrl([path, resourceId]).toString();
51-
// const res = await axios.get(url)
52-
// .then((response) => {
53-
// return {data: response.data, status: response.status};
54-
// })
55-
// .catch(error => {
56-
// console.error("CATCH: fetch error: ");
57-
// console.error(error);
58-
// return {data: error.message, status: error.status};
59-
// });
60-
// return {data: res.data, status: res.status};
61-
// }
62-
export const UpdateFile = async (path, resourceId, data) => {
63-
// TODO: Delete uses of "numUpdates" below before hand-in. Just to track how many updates it's got.
64-
// if(numUpdates[resourceId] == null) numUpdates[resourceId] = 1;
65-
// else numUpdates[resourceId] += 1;
66-
// console.log(`Num updates for ${resourceId} = ${numUpdates[resourceId]}`);
6746

47+
export const UpdateFile = async (path, resourceId, data) => {
6848
const url = appendUrl([path, resourceId]).toString();
6949
const res = await axios.put(url, data)
7050
.then((response) => {

App/ConfigUnpacker.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@ const configName = "../config.json";
1010
const configPath = path.join(__dirname, configName);
1111

1212
export function writeConfig(configList) {
13-
// var configList = getConfig();
14-
// if (configList.filter(e => e.MinerId === config.MinerId).length > 0) {
15-
// console.log("Config with that ID already exist. Leaving it as is. Overwriting shouldn't be done through here.");
16-
// return;
17-
// }
1813
var configListString = JSON.stringify(configList, null, 2);
1914
fs.writeFileSync(configPath, configListString, 'utf8', function(err) {
2015
if(err) throw err;

App/ProcessHelper.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import spawn from "child_process";
12
var processDict = {}; // Dict of all processes
23
var processStatusDict = {}; // Dict of all process status objects
34

@@ -77,9 +78,20 @@ export function setProcess(processId, process) {
7778
processDict[processId] = process;
7879
console.log(`Process added to dict. All current process IDs: ${Object.keys(processDict)}`);
7980
}
81+
// export function killProcess(processId) {
82+
// if(getProcess(processId)) {
83+
// getProcess(processId).kill();
84+
// deleteFromProcessDict(processId);
85+
// }
86+
// }
8087
export function killProcess(processId) {
88+
8189
if(getProcess(processId)) {
82-
getProcess(processId).kill();
90+
spawn.exec(`taskkill /PID ${processId} /F /T`, (stdout) => {
91+
if(stdout) {
92+
console.log(stdout);
93+
}
94+
});
8395
deleteFromProcessDict(processId);
8496
}
8597
}

0 commit comments

Comments
 (0)