diff --git a/src/api.ts b/src/api.ts index b347367..3476c91 100644 --- a/src/api.ts +++ b/src/api.ts @@ -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, @@ -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; +} diff --git a/src/app.ts b/src/app.ts index 3308d96..63c943e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -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; + public proc?: ChildProcess; + public deployment?: Deployment; + + public kill(): void { + this.proc?.kill(); + } +} + +export const Applications: Record = {}; diff --git a/src/constants.ts b/src/constants.ts deleted file mode 100644 index a8e9f48..0000000 --- a/src/constants.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { DeployStatus, MetaCallJSON } from '@metacall/protocol/deployment'; - -export interface Deployment { - id: string; - path: string; - jsons: MetaCallJSON[]; - runners?: string[]; - type?: string; - blob?: string; -} - -export const deploymentMap: Record> = {}; - -export type tpackages = Record; - -// TODO: Isn't this available inside protocol package? We MUST reuse it -export interface IApp { - status: DeployStatus; - prefix: string; - suffix: string; - version: string; - packages: tpackages; - ports: number[]; -} - -// TODO: Isn't this available inside protocol package? We MUST reuse it -export class App implements IApp { - public status: DeployStatus; - public prefix: string; - public suffix: string; - public version: string; - public packages: tpackages; - public ports: number[]; - - constructor( - status: DeployStatus, - prefix: string, - suffix: string, - version: string, - packages: tpackages, - ports: number[] - ) { - this.status = status; - this.prefix = prefix; - this.suffix = suffix; - this.version = version; - this.packages = packages; - this.ports = ports; - } -} - -export type IAllApps = Record; - -export const allApplications: IAllApps = {}; diff --git a/src/controller/call.ts b/src/controller/call.ts index 20426c7..9856a21 100644 --- a/src/controller/call.ts +++ b/src/controller/call.ts @@ -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); + }); }; diff --git a/src/controller/delete.ts b/src/controller/delete.ts index 123b722..9fac7c7 100644 --- a/src/controller/delete.ts +++ b/src/controller/delete.ts @@ -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 @@ -37,45 +21,29 @@ export const deployDelete = catchAsync( _next: NextFunction ): Promise => { // 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!'); } ); diff --git a/src/controller/deploy.ts b/src/controller/deploy.ts index f514c20..ee601e8 100644 --- a/src/controller/deploy.ts +++ b/src/controller/deploy.ts @@ -1,10 +1,9 @@ import { NextFunction, Request, Response } from 'express'; import { hostname } from 'os'; -import { deploymentMap } from '../constants'; - import AppError from '../utils/appError'; +import { Applications } from '../app'; import { deployProcess } from '../utils/deploy'; import { installDependencies } from '../utils/install'; import { catchAsync } from './catch'; @@ -29,24 +28,25 @@ export const deploy = catchAsync( // TODO: Implement repository // req.body.resourceType == 'Repository' - const deployment = await deploymentMap[req.body.suffix]; + const application = Applications[req.body.suffix]; - if (deployment === undefined) { - return next( - new AppError( - `Invalid deployment id: ${req.body.suffix}`, - 400 - ) + // Check if the application exists and it is stored + if (!application?.resource) { + throw new AppError( + `Invalid deployment id: ${req.body.suffix}`, + 400 ); } - await installDependencies(deployment); + const resource = await application.resource; + + await installDependencies(resource); - await deployProcess(deployment); + await deployProcess(resource); return res.status(200).json({ prefix: hostname(), - suffix: deployment.id, + suffix: resource?.id, version: 'v1' }); } catch (err) { diff --git a/src/controller/inspect.ts b/src/controller/inspect.ts new file mode 100644 index 0000000..7f6daac --- /dev/null +++ b/src/controller/inspect.ts @@ -0,0 +1,16 @@ +import { Deployment } from '@metacall/protocol'; +import { Request, Response } from 'express'; +import { Applications } from '../app'; + +export const inspect = (_req: Request, res: Response): Response => { + const deployments: Deployment[] = []; + + for (const application of Object.values(Applications)) { + // Check if the application is deployed + if (application.deployment) { + deployments.push(application.deployment); + } + } + + return res.send(deployments); +}; diff --git a/src/controller/static.ts b/src/controller/static.ts index 17ed5a0..363a99a 100644 --- a/src/controller/static.ts +++ b/src/controller/static.ts @@ -1,37 +1,41 @@ import { NextFunction, Request, Response } from 'express'; import path from 'path'; +import { Applications } from '../app'; import AppError from '../utils/appError'; import { appsDirectory } from '../utils/config'; import { exists } from '../utils/filesystem'; -import { Processes } from '../worker/master'; import { catchAsync } from './catch'; export const serveStatic = catchAsync( async (req: Request, res: Response, next: NextFunction): Promise => { - if (!req.params) next(new AppError('Invalid API endpoint', 404)); - - const { app, file } = req.params; + if (!req.params) { + throw new AppError('Invalid API endpoint', 404); + } - const appLocation = path.join(appsDirectory, `${app}/${file}`); + const { suffix, file } = req.params; + const application = Applications[suffix]; - if (!(app in Processes)) { + // Check if the application exists and it is running + if (!application?.proc) { next( new AppError( - `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' hasn't been deployed yet. Please deploy it before you can call its functions.`, 404 ) ); } - if (!(await exists(appLocation))) + const filePath = path.join(appsDirectory, suffix, file); + + if (!(await exists(filePath))) next( new AppError( - "The file you're looking for might not be available or the application may not be deployed.", + 'The file you are looking for might not be available or the application may not be deployed.', 404 ) ); - return res.status(200).sendFile(appLocation); + return res.status(200).sendFile(filePath); } ); diff --git a/src/controller/upload.ts b/src/controller/upload.ts index e33956d..2329df2 100644 --- a/src/controller/upload.ts +++ b/src/controller/upload.ts @@ -1,14 +1,13 @@ import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; +import os from 'os'; +import path from 'path'; import busboy from 'busboy'; import { NextFunction, Request, Response } from 'express'; import { Extract, ParseOptions } from 'unzipper'; -import { Deployment, deploymentMap } from '../constants'; - import { MetaCallJSON } from '@metacall/protocol/deployment'; +import { Application, Applications, Resource } from '../app'; import AppError from '../utils/appError'; import { appsDirectory } from '../utils/config'; import { ensureFolderExists } from '../utils/filesystem'; @@ -50,16 +49,22 @@ export const uploadPackage = ( next: NextFunction ): void => { const bb = busboy({ headers: req.headers }); - const deployment: Deployment = { + const resource: Resource = { id: '', type: '', path: '', jsons: [] }; + let handled = false; // TODO: Promisify the whole controller + const errorHandler = (error: AppError) => { - req.unpipe(bb); - next(error); + if (handled == false) { + req.unpipe(bb); + next(error); + } + + handled = true; }; const eventHandler = (type: keyof busboy.BusboyEvents, listener: T) => { @@ -91,12 +96,12 @@ export const uploadPackage = ( ); } - const appLocation = path.join(appsDirectory, deployment.id); - deployment.path = appLocation; + const appLocation = path.join(appsDirectory, resource.id); + resource.path = appLocation; // Create temporary directory for the blob fs.mkdtemp( - path.join(os.tmpdir(), `metacall-faas-${deployment.id}-`), + path.join(os.tmpdir(), `metacall-faas-${resource.id}-`), (err, folder) => { if (err !== null) { return errorHandler( @@ -107,20 +112,20 @@ export const uploadPackage = ( ); } - deployment.blob = path.join(folder, filename); + resource.blob = path.join(folder, filename); // Create the app folder ensureFolderExists(appLocation) .then(() => { // Create the write stream for storing the blob file.pipe( - fs.createWriteStream(deployment.blob as string) + fs.createWriteStream(resource.blob as string) ); }) .catch((error: Error) => { errorHandler( new AppError( - `Failed to create folder for the deployment at: ${appLocation} - ${error.toString()}`, + `Failed to create folder for the resource at: ${appLocation} - ${error.toString()}`, 404 ) ); @@ -130,24 +135,24 @@ export const uploadPackage = ( } ); - eventHandler('field', (name: keyof Deployment, val: string) => { + eventHandler('field', (name: keyof Resource, val: string) => { if (name === 'runners') { - deployment['runners'] = JSON.parse(val) as string[]; + resource.runners = JSON.parse(val) as string[]; } else if (name === 'jsons') { - deployment['jsons'] = JSON.parse(val) as MetaCallJSON[]; + resource.jsons = JSON.parse(val) as MetaCallJSON[]; } else { - deployment[name] = val; + resource[name] = val; } }); eventHandler('finish', () => { - if (deployment.blob === undefined) { + if (resource.blob === undefined) { throw Error('Invalid file upload, blob path is not defined'); } const deleteBlob = () => { - if (deployment.blob !== undefined) { - fs.unlink(deployment.blob, error => { + if (resource.blob !== undefined) { + fs.unlink(resource.blob, error => { if (error !== null) { errorHandler( new AppError( @@ -160,39 +165,70 @@ export const uploadPackage = ( } }; - const options: ParseOptions = { path: deployment.path }; + const deleteFolder = () => { + if (resource.path !== undefined) { + fs.unlink(resource.path, error => { + if (error !== null) { + errorHandler( + new AppError( + `Failed to delete the path at: ${error.toString()}`, + 500 + ) + ); + } + }); + } + }; + + if (Applications[resource.id]) { + deleteBlob(); + return errorHandler( + new AppError( + `There is an application with name '${resource.id}' already deployed, delete it first.`, + 400 + ) + ); + } - let deployResolve: ( - value: Deployment | PromiseLike - ) => void; - let deployReject: (reason?: unknown) => void; + let resourceResolve: (value: Resource | PromiseLike) => void; + let resourceReject: (reason?: unknown) => void; - deploymentMap[deployment.id] = new Promise((resolve, reject) => { - deployResolve = resolve; - deployReject = reject; - }); + Applications[resource.id] = new Application(); + Applications[resource.id].resource = new Promise( + (resolve, reject) => { + resourceResolve = resolve; + resourceReject = reject; + } + ); + + const options: ParseOptions = { path: resource.path }; - fs.createReadStream(deployment.blob) + fs.createReadStream(resource.blob) .pipe(Extract(options)) .on('close', () => { deleteBlob(); - deployResolve(deployment); + resourceResolve(resource); }) .on('error', error => { deleteBlob(); + deleteFolder(); const appError = new AppError( - `Failed to unzip the deployment at: ${error.toString()}`, + `Failed to unzip the resource at: ${error.toString()}`, 500 ); errorHandler(appError); - deployReject(appError); + resourceReject(appError); }); }); eventHandler('close', () => { - res.status(201).json({ - id: deployment.id - }); + if (handled == false) { + res.status(201).json({ + id: resource.id + }); + } + + handled = true; }); req.pipe(bb); diff --git a/src/controller/validate.ts b/src/controller/validate.ts index a97ad56..f97ae66 100644 --- a/src/controller/validate.ts +++ b/src/controller/validate.ts @@ -1,6 +1,5 @@ import { Request, Response } from 'express'; -// TODO: Implement wait to autoDeploy to finish export const validate = (_req: Request, res: Response): Response => res.status(200).json({ status: 'success', diff --git a/src/index.ts b/src/index.ts index f5b9efc..63b1420 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import colors from 'colors'; import dotenv from 'dotenv'; -import app from './app'; +import { initializeAPI } from './api'; import { autoDeployApps } from './utils/autoDeploy'; import { appsDirectory } from './utils/config'; import { ensureFolderExists } from './utils/filesystem'; @@ -16,12 +16,13 @@ void (async (): Promise => { await autoDeployApps(appsDirectory); + const app = initializeAPI(); const port = process.env.PORT || 9000; app.listen(port, () => { console.log(`Server is running on the port ${port}`); }); } catch (e) { - console.error('Error while re-deploying applications: ', e); + console.error('Error while initializing: ', e); } })(); diff --git a/src/utils/autoDeploy.ts b/src/utils/autoDeploy.ts index 8fe54d3..b926be4 100644 --- a/src/utils/autoDeploy.ts +++ b/src/utils/autoDeploy.ts @@ -1,7 +1,7 @@ import { Dirent } from 'fs'; import * as fs from 'fs/promises'; import * as path from 'path'; -import { Deployment } from '../constants'; +import { Application, Applications, Resource } from '../app'; import { deployProcess } from './deploy'; export const autoDeployApps = async (appsDir: string): Promise => { @@ -15,15 +15,21 @@ export const autoDeployApps = async (appsDir: string): Promise => { [] ); - const deployments: Deployment[] = directories.map(dir => ({ + const resources: Resource[] = directories.map(dir => ({ id: dir.name, path: path.join(appsDir, dir.name), jsons: [] })); - await Promise.all(deployments.map(deployProcess)); + await Promise.all( + resources.map(resource => { + Applications[resource.id] = new Application(); + Applications[resource.id].resource = Promise.resolve(resource); + return deployProcess(resource); + }) + ); - if (deployments.length > 0) { + if (resources.length > 0) { console.log( 'Previously deployed applications deployed successfully'.green ); diff --git a/src/utils/deploy.ts b/src/utils/deploy.ts index 42775ea..76e6906 100644 --- a/src/utils/deploy.ts +++ b/src/utils/deploy.ts @@ -1,14 +1,11 @@ +import { Deployment } from '@metacall/protocol'; import { spawn } from 'child_process'; import path from 'path'; -import { App, Deployment, allApplications } from '../constants'; -import { - Processes, - WorkerMessageType, - WorkerMessageUnknown -} from '../worker/master'; +import { Applications, Resource } from '../app'; +import { WorkerMessageType, WorkerMessageUnknown } from '../worker/protocol'; import { logProcessOutput } from './logger'; -export const deployProcess = async (deployment: Deployment): Promise => { +export const deployProcess = async (resource: Resource): Promise => { // Spawn a new process const desiredPath = path.join( path.resolve(__dirname, '..'), @@ -23,11 +20,11 @@ export const deployProcess = async (deployment: Deployment): Promise => { // Send load message with the deploy information proc.send({ type: WorkerMessageType.Load, - data: deployment + data: resource }); // Pipe the stdout and stderr to the logger - logProcessOutput(proc, deployment.id); + logProcessOutput(proc, resource.id); // Wait for load result let deployResolve: (value: void) => void; @@ -41,9 +38,11 @@ export const deployProcess = async (deployment: Deployment): Promise => { proc.on('message', (payload: WorkerMessageUnknown) => { // Get the deploy data and store the process and app into our tables if (payload.type === WorkerMessageType.MetaData) { - const app = payload.data as App; - Processes[app.suffix] = proc; - allApplications[app.suffix] = app; + const application = Applications[resource.id]; + const deployment = payload.data as Deployment; + + application.proc = proc; + application.deployment = deployment; deployResolve(); } }); @@ -53,7 +52,7 @@ export const deployProcess = async (deployment: Deployment): Promise => { // probably segmentation fault (exit code 139 in Linux) deployReject( new Error( - `Deployment '${deployment.id}' process exited with code: ${ + `Deployment '${resource.id}' process exited with code: ${ code || 'unknown' }` ) diff --git a/src/utils/install.ts b/src/utils/install.ts index 60555c8..4d74827 100644 --- a/src/utils/install.ts +++ b/src/utils/install.ts @@ -1,4 +1,4 @@ -import { Deployment } from '../constants'; +import { Resource } from '../app'; import { exec } from './exec'; const createInstallDependenciesScript = ( @@ -8,23 +8,21 @@ const createInstallDependenciesScript = ( const installDependenciesScript: Record = { python: `cd ${path} && metacall pip3 install -r requirements.txt`, nodejs: `cd ${path} && metacall npm i`, - csharp: `cd ${path} && metacall dotnet restore && metacall dotnet release;`, - ruby: `cd ${path} && metacall bundle install` + ruby: `cd ${path} && metacall bundle install`, + csharp: `cd ${path} && metacall dotnet restore && metacall dotnet release` }; return installDependenciesScript[runner]; }; export const installDependencies = async ( - deployment: Deployment + resource: Resource ): Promise => { - if (!deployment.runners) return; + if (!resource.runners) return; - for (const runner of deployment.runners) { + for (const runner of resource.runners) { if (runner == undefined) continue; else { - await exec( - createInstallDependenciesScript(runner, deployment.path) - ); + await exec(createInstallDependenciesScript(runner, resource.path)); } } }; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 84dc36e..601fd6b 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -32,7 +32,7 @@ const logFileFullPath = path.resolve(path.join(logFilePath, logFileName)); // TODO: Implement this properly? // const maxWorkerWidth = (maxIndexWidth = 3): number => { -// const workerLengths = Object.keys(allApplications).map( +// const workerLengths = Object.keys(Applications).map( // worker => worker.length // ); // return Math.max(...workerLengths) + maxIndexWidth; diff --git a/src/worker/index.ts b/src/worker/index.ts index 2ed5535..f8a6341 100644 --- a/src/worker/index.ts +++ b/src/worker/index.ts @@ -1,6 +1,6 @@ /* eslint-disable */ -import { MetaCallJSON } from '@metacall/protocol'; +import { Deployment, MetaCallJSON } from '@metacall/protocol'; import { findFilesPath, findMetaCallJsons } from '@metacall/protocol/package'; import { promises as fs } from 'fs'; import { @@ -9,12 +9,12 @@ import { } from 'metacall'; import { hostname } from 'os'; import { join } from 'path'; -import { App, Deployment } from '../constants'; +import { Resource } from '../app'; import { WorkerMessage, WorkerMessageType, WorkerMessageUnknown -} from '../worker/master'; +} from './protocol'; const functions: Record any> = {}; @@ -28,12 +28,22 @@ const createMetacallJsonFiles = async ( } }; -const loadDeployment = (deployment: Deployment, jsonPaths: string[]): App => { - const app = new App('create', hostname(), deployment.id, 'v1', {}, []); +const loadDeployment = ( + resource: Resource, + jsonPaths: string[] +): Deployment => { + const deployment: Deployment = { + status: 'create', + prefix: hostname(), + suffix: resource.id, + version: 'v1', + packages: {} as Deployment['packages'], + ports: [] + }; for (const path of jsonPaths) { // Load the json into metacall - const fullPath = join(deployment.path, path); + const fullPath = join(resource.path, path); const exports = metacall_load_from_configuration_export(fullPath); // Get the inspect information @@ -44,7 +54,7 @@ const loadDeployment = (deployment: Deployment, jsonPaths: string[]): App => { throw new Error(`language_id not found in ${path}`); } - app.packages = inspect[json.language_id][path]; + deployment.packages = inspect[json.language_id][path]; // Store the functions Object.keys(exports).forEach(func => { @@ -52,34 +62,34 @@ const loadDeployment = (deployment: Deployment, jsonPaths: string[]): App => { }); } - return app; + return deployment; }; -const handleDeployment = async (deployment: Deployment): Promise => { +const handleDeployment = async (resource: Resource): Promise => { // Check if the deploy comes with extra JSONs and store them - if (deployment.jsons.length > 0) { + if (resource.jsons.length > 0) { const jsonPaths = await createMetacallJsonFiles( - deployment.path, - deployment.jsons + resource.path, + resource.jsons ); } // List all files except by the ignored ones - const filesPaths = await findFilesPath(deployment.path); + const filesPaths = await findFilesPath(resource.path); // Get the JSONs from the list of files const jsonPaths = findMetaCallJsons(filesPaths); // Deploy the JSONs - return loadDeployment(deployment, jsonPaths); + return loadDeployment(resource, jsonPaths); }; process.on('message', (payload: WorkerMessageUnknown) => { switch (payload.type) { // Handle deploy load case WorkerMessageType.Load: { - const deployment = (payload as WorkerMessage).data; - handleDeployment(deployment) + const resource = (payload as WorkerMessage).data; + handleDeployment(resource) .then(app => { if (process.send) { process.send({ diff --git a/src/worker/master.ts b/src/worker/protocol.ts similarity index 75% rename from src/worker/master.ts rename to src/worker/protocol.ts index c091124..2884ff4 100644 --- a/src/worker/master.ts +++ b/src/worker/protocol.ts @@ -1,5 +1,3 @@ -import { ChildProcess } from 'child_process'; - export enum WorkerMessageType { Install = 'InstallDependencies', Load = 'LoadFunctions', @@ -14,5 +12,3 @@ export interface WorkerMessage { } export type WorkerMessageUnknown = WorkerMessage; - -export const Processes: { [key: string]: ChildProcess } = {};