Skip to content

Commit

Permalink
One more refactor.
Browse files Browse the repository at this point in the history
  • Loading branch information
viferga committed Jun 4, 2024
1 parent 7541399 commit f4faf86
Show file tree
Hide file tree
Showing 17 changed files with 306 additions and 304 deletions.
64 changes: 51 additions & 13 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { callFunction } from './controller/call';
import { deployDelete } from './controller/delete';
import { deploy } from './controller/deploy';
import { globalError } from './controller/error';
import { inspect } from './controller/inspect';
import { logs } from './controller/logs';
import {
fetchBranchList,
Expand All @@ -12,16 +13,53 @@ import { serveStatic } from './controller/static';
import { uploadPackage } from './controller/upload';
import { validate } from './controller/validate';

export default {
callFunction,
deployDelete,
deploy,
globalError,
logs,
fetchFilesFromRepo,
fetchBranchList,
fetchFileList,
serveStatic,
uploadPackage,
validate
};
import { hostname } from 'os';

import express, { Express, NextFunction, Request, Response } from 'express';

import AppError from './utils/appError';

export function initializeAPI(): Express {
const app = express();
const host = hostname();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get('/readiness', (_req: Request, res: Response) =>
res.sendStatus(200)
);
app.get('/validate', validate);
app.get('/api/account/deploy-enabled', validate);

app.get(`/${host}/:suffix/:version/call/:func`, callFunction);
app.post(`/${host}/:suffix/:version/call/:func`, callFunction);
app.get(
`/${host}/:suffix/:version/static/.metacall/faas/apps/:suffix/:file`,
serveStatic
);

app.post('/api/package/create', uploadPackage);
app.post('/api/repository/add', fetchFilesFromRepo);

app.post('/api/repository/branchlist', fetchBranchList);
app.post('/api/repository/filelist', fetchFileList);
app.post('/api/deploy/logs', logs);

app.post('/api/deploy/create', deploy);

app.get('/api/inspect', inspect);

app.post('/api/deploy/delete', deployDelete);

// For all the additional unimplemented routes
app.all('*', (req: Request, res: Response, next: NextFunction) => {
next(
new AppError(`Can't find ${req.originalUrl} on this server!`, 404)
);
});

app.use(globalError);

return app;
}
71 changes: 23 additions & 48 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,23 @@
import { hostname } from 'os';

import express, { NextFunction, Request, Response } from 'express';

import api from './api';
import { allApplications } from './constants';
import AppError from './utils/appError';

const app = express();
const host = hostname();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get('/readiness', (_req: Request, res: Response) => res.sendStatus(200));
app.get('/validate', api.validate);
app.get('/api/account/deploy-enabled', api.validate);

app.get(`/${host}/:appName/:version/call/:name`, api.callFunction);
app.post(`/${host}/:appName/:version/call/:name`, api.callFunction);
app.get(
`/${host}/:appName/:version/static/.metacall/faas/apps/:app/:file`,
api.serveStatic
);

app.post('/api/package/create', api.uploadPackage);
app.post('/api/repository/add', api.fetchFilesFromRepo);

app.post('/api/repository/branchlist', api.fetchBranchList);
app.post('/api/repository/filelist', api.fetchFileList);
app.post('/api/deploy/logs', api.logs);

app.post('/api/deploy/create', api.deploy);

app.get('/api/inspect', (_req, res) => {
res.send(Object.values(allApplications));
});

app.post('/api/deploy/delete', api.deployDelete);

// For all the additional unimplemented routes
app.all('*', (req: Request, res: Response, next: NextFunction) => {
next(new AppError(`Can't find ${req.originalUrl} on this server!`, 404));
});

app.use(api.globalError);

export default app;
import { Deployment, MetaCallJSON } from '@metacall/protocol/deployment';
import { ChildProcess } from 'child_process';

export interface Resource {
id: string;
path: string;
jsons: MetaCallJSON[];
runners?: string[];
type?: string;
blob?: string;
}

export class Application {
public resource?: Promise<Resource>;
public proc?: ChildProcess;
public deployment?: Deployment;

public kill(): void {
this.proc?.kill();
}
}

export const Applications: Record<string, Application> = {};
54 changes: 0 additions & 54 deletions src/constants.ts

This file was deleted.

80 changes: 45 additions & 35 deletions src/controller/call.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,72 @@
import { NextFunction, Request, Response } from 'express';
import { Applications } from '../app';
import AppError from '../utils/appError';
import {
Processes,
WorkerMessageType,
WorkerMessageUnknown
} from '../worker/master';
import { WorkerMessageType, WorkerMessageUnknown } from '../worker/protocol';

export const callFunction = (
req: Request,
res: Response,
next: NextFunction
): Response | void => {
if (!(req.params && req.params.name))
next(
if (!req.params?.suffix) {
return next(
new AppError('A the deployment name (suffix) is required.', 404)
);
}

if (!req.params?.func) {
return next(
new AppError(
'A function name is required in the path; i.e: /call/sum.',
404
)
);
}

const { appName: app, name } = req.params;
const { suffix, func } = req.params;
const args = Object.values(req.body);
const application = Applications[suffix];

if (!(app in Processes)) {
// Check if the application exists and it is running
if (!application?.proc) {
return res
.status(404)
.send(
`Oops! It looks like the application (${app}) hasn't been deployed yet. Please deploy it before you can call its functions.`
`Oops! It looks like the application '${suffix}' has not been deployed yet. Please deploy it before you can call its functions.`
);
}

let responseSent = false; // Flag to track if response has been sent
let errorCame = false;

Processes[app].send({
type: WorkerMessageType.Invoke,
data: {
name,
args
}
});
new Promise((resolve, reject) => {
application.proc?.send({
type: WorkerMessageType.Invoke,
data: {
name: func,
args
}
});

Processes[app].on('message', (message: WorkerMessageUnknown) => {
if (!responseSent) {
// Check if response has already been sent
application.proc?.on('message', (message: WorkerMessageUnknown) => {
if (message.type === WorkerMessageType.InvokeResult) {
responseSent = true; // Set flag to true to indicate response has been sent
return res.send(JSON.stringify(message.data));
} else {
errorCame = true;
resolve(JSON.stringify(message.data));
}
}
});
});

// Default response in case the 'message' event is not triggered
if (!responseSent && errorCame) {
responseSent = true; // Set flag to true to indicate response has been sent
errorCame = false;
return res.send('Function calling error');
}
application.proc?.on('exit', code => {
// The application may have been ended unexpectedly,
// probably segmentation fault (exit code 139 in Linux)
reject(
JSON.stringify({
error: `Deployment '${suffix}' process exited with code: ${
code || 'unknown'
}`
})
);
});
})
.then(data => {
res.send(data);
})
.catch(error => {
res.status(500).send(error);
});
};
60 changes: 14 additions & 46 deletions src/controller/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,10 @@ import { NextFunction, Request, Response } from 'express';
import { rm } from 'fs/promises';
import { join } from 'path';

import { allApplications } from '../constants';
import { Applications } from '../app';
import { appsDirectory } from '../utils/config';
import { ensureFolderExists } from '../utils/filesystem';
import { Processes } from '../worker/master';
import { catchAsync } from './catch';

const deleteStatusMessage = (
app: string
): {
success: string;
error: string;
folderShouldntExist: string;
appShouldntExist: string;
} => ({
success: 'Deploy Delete Succeed',
error: `Oops! It looks like the application (${app}) hasn't been deployed yet. Please deploy it before you delete it.`,
folderShouldntExist: `The folder shouldnt exist after deleting it.`,
appShouldntExist: `The application shouldnt exist after deleting it`
});

// TODO: Isn't this available inside protocol package? We MUST reuse it
type DeleteBody = {
suffix: string; // name of deployment
Expand All @@ -37,45 +21,29 @@ export const deployDelete = catchAsync(
_next: NextFunction
): Promise<Response> => {
// Extract the suffix (application name) of the application from the request body
const { suffix: app } = req.body;

// Initialize isError flag
let isError = false;

// Check if the application exists in Processes and allApplications objects
if (!(app in Processes && app in allApplications)) {
isError = true;
return res.send(deleteStatusMessage(app)['error']);
const { suffix } = req.body;
const application = Applications[suffix];

// Check if the application exists and it is running
if (!application?.proc) {
return res.send(
`Oops! It looks like the application '${suffix}' hasn't been deployed yet. Please deploy it before you delete it.`
);
}

// Retrieve the child process associated with the application and kill it
Processes[app].kill();
application.kill();

// Remove the application from Processes and allApplications objects
delete Processes[app];
delete allApplications[app];

if (app in Processes && app in allApplications) {
isError = true;
return res.send(deleteStatusMessage(app)['appShouldntExist']);
}
// Remove the application Applications object
delete Applications[suffix];

// Determine the location of the application
const appLocation = join(appsDirectory, app);
const appLocation = join(appsDirectory, suffix);

// Delete the directory of the application
await rm(appLocation, { recursive: true, force: true });

if (!(await ensureFolderExists(appLocation))) {
isError = true;
return res.send(deleteStatusMessage(app)['folderShouldntExist']);
}

// Send response based on whether there was an error during deletion
return res.send(
isError
? deleteStatusMessage(app)['error']
: deleteStatusMessage(app)['success']
);
return res.send('Deploy delete succeed!');
}
);
Loading

0 comments on commit f4faf86

Please sign in to comment.