From 884dfdabc60e465e7c9adb8141661d0edeeae45e Mon Sep 17 00:00:00 2001 From: danhtran94 Date: Wed, 18 Dec 2024 21:39:54 +0700 Subject: [PATCH 1/3] feat: add bun environtment --- bun/.gitignore | 1 + bun/Dockerfile | 18 +++ bun/Makefile | 16 +++ bun/README.md | 57 ++++++++++ bun/builder/Dockerfile | 13 +++ bun/builder/Makefile | 16 +++ bun/builder/build.sh | 10 ++ bun/bun.lockb | Bin 0 -> 50213 bytes bun/envconfig.json | 61 ++++++++++ bun/package.json | 32 ++++++ bun/server.ts | 249 +++++++++++++++++++++++++++++++++++++++++ bun/tsconfig.json | 27 +++++ 12 files changed, 500 insertions(+) create mode 100644 bun/.gitignore create mode 100644 bun/Dockerfile create mode 100644 bun/Makefile create mode 100644 bun/README.md create mode 100644 bun/builder/Dockerfile create mode 100644 bun/builder/Makefile create mode 100755 bun/builder/build.sh create mode 100755 bun/bun.lockb create mode 100644 bun/envconfig.json create mode 100644 bun/package.json create mode 100644 bun/server.ts create mode 100644 bun/tsconfig.json diff --git a/bun/.gitignore b/bun/.gitignore new file mode 100644 index 00000000..c2658d7d --- /dev/null +++ b/bun/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/bun/Dockerfile b/bun/Dockerfile new file mode 100644 index 00000000..e289a6f4 --- /dev/null +++ b/bun/Dockerfile @@ -0,0 +1,18 @@ +ARG BUN_BASE_IMG + +FROM oven/bun:${BUN_BASE_IMG} + +ARG BUN_ENV +ENV BUN_ENV $BUN_ENV + +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app + +COPY package.json /usr/src/app/ +RUN bun install +COPY server.ts /usr/src/app/server.ts +COPY tsconfig.json /usr/src/app/tsconfig.json + +CMD [ "bun", "run", "server.ts" ] + +EXPOSE 8888 \ No newline at end of file diff --git a/bun/Makefile b/bun/Makefile new file mode 100644 index 00000000..d705915e --- /dev/null +++ b/bun/Makefile @@ -0,0 +1,16 @@ +-include ../rules.mk + +.PHONY: all +all: bun-builder bun-env-img bun-env-debian-img bun-env-1140-img + +bun-env-img-buildargs := --build-arg BUN_BASE_IMG=1.1.40-alpine + +bun-env-debian-img-buildargs := --build-arg BUN_BASE_IMG=1.1.40 + +bun-env-1140-img-buildargs := --build-arg BUN_BASE_IMG=1.1.40-alpine + +bun-env-img: Dockerfile + +bun-env-debian-img: Dockerfile + +bun-env-1140-img: Dockerfile \ No newline at end of file diff --git a/bun/README.md b/bun/README.md new file mode 100644 index 00000000..d7632794 --- /dev/null +++ b/bun/README.md @@ -0,0 +1,57 @@ +# Fission: NodeJS Environment + +This is the NodeJS environment for Fission. + +It's a Docker image containing a NodeJS runtime, along with a dynamic +loader. A few common dependencies are included in the package.json +file. + +Looking for ready-to-run examples? See the [NodeJS examples directory](../../examples/nodejs). + +## Customizing this image + +To add package dependencies, edit [package.json](./package.json) to add what you need, and rebuild this image (instructions below). + +You also may want to customize what's available to the function in it's request context. +You can do this by editing [server.js](./server.js) (see the comment in that file about customizing request context). + +## Rebuilding and pushing the image + +You'll need access to a Docker registry to push the image: you can sign up for Docker hub at hub.docker.com, or use registries from gcr.io, quay.io, etc. +Let's assume you're using a docker hub account called USER. +Build and push the image to the the registry: + +Building runtime image, + +```console +docker build -t USER/bun-env --build-arg BUN_BASE_IMG=1.1.40-alpine -f Dockerfile . +docker build -t ghcr.io/danhtran94/bun-env --build-arg BUN_BASE_IMG=1.1.40-alpine -f Dockerfile . +docker push USER/bun-env +docker push ghcr.io/danhtran94/bun-env +``` + +Building builder image, + +```console +cd builder && docker build -t USER/bun-builder --build-arg BUN_BASE_IMG=1.1.40-alpine -f Dockerfile . +cd builder && docker build -t ghcr.io/danhtran94/bun-builder --build-arg BUN_BASE_IMG=1.1.40-alpine -f Dockerfile . +docker push USER/bun-builder +docker push ghcr.io/danhtran94/bun-builder +``` + +## Using the image in fission + +You can add this customized image to fission with "fission env create": + +```console +fission env create --name node --image USER/bun-env +``` + +Or, if you already have an environment, you can update its image: + +```console +fission env update --name node --image USER/bun-env +``` + +After this, fission functions that have the env parameter set to the +same environment name as this command will use this environment. diff --git a/bun/builder/Dockerfile b/bun/builder/Dockerfile new file mode 100644 index 00000000..37c22e78 --- /dev/null +++ b/bun/builder/Dockerfile @@ -0,0 +1,13 @@ +ARG BUN_BASE_IMG +ARG BUILDER_IMAGE=fission/builder:latest + +FROM ${BUILDER_IMAGE} + +FROM oven/bun:${BUN_BASE_IMG} + +ARG BUN_ENV +ENV BUN_ENV $BUN_ENV + +COPY --from=0 /builder /builder +ADD build.sh /usr/local/bin/build +RUN chmod +x /usr/local/bin/build diff --git a/bun/builder/Makefile b/bun/builder/Makefile new file mode 100644 index 00000000..f7db4c3a --- /dev/null +++ b/bun/builder/Makefile @@ -0,0 +1,16 @@ +-include ../../rules.mk + +.PHONY: all +all: bun-builder-debian-img bun-builder-img bun-builder-1140-img + +bun-builder-debian-img-buildargs := --build-arg BUN_BASE_IMG=1.1.40 + +bun-builder-img-buildargs := --build-arg BUN_BASE_IMG=1.1.40-alpine + +bun-builder-1140-img-buildargs := --build-arg BUN_BASE_IMG=1.1.40-alpine + +bun-builder-debian-img: Dockerfile + +bun-builder-img: Dockerfile + +bun-builder-1140-img: Dockerfile diff --git a/bun/builder/build.sh b/bun/builder/build.sh new file mode 100755 index 00000000..ff79599f --- /dev/null +++ b/bun/builder/build.sh @@ -0,0 +1,10 @@ +#!/bin/sh +cd ${SRC_PKG} + +if [[ -n "$NPM_TOKEN" ]] && [[ -n "$NPM_REGISTRY" ]]; then + touch bunfig.toml + echo "[install.scopes]" >> bunfig.toml + echo "\"@private\" = { token = \"$NPM_TOKEN\", url = \"$NPM_REGISTRY\" }" >> bunfig.toml +fi + +bun install && cp -r ${SRC_PKG} ${DEPLOY_PKG} diff --git a/bun/bun.lockb b/bun/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..6ee12744b16e2cc62fea5ac5267c8206117b4850 GIT binary patch literal 50213 zcmeHw30zFw8~>!qlBE!pLWR7_v}-|&$P$$(q=l)bnwFW&OsRxOglum_C`(D!ED_mB zC}hdLW?!;<$^L(yJNN2XY9{>tpa1{!ao*44+;h+Qe$R8B^PJ_Ldz-g|u5pxDWbDru z81n?tI{r}tWe`rdFd!_5AI{?h2}KcH2`Ab_xgLwfvKjvD@$9behGYe4Zf^a?Xxf_> zbK{1mwtaVMmOD?+H^6zpAt(ePNgA;leitXu=@*Ig1r@bsu@c6LrFR4nDMNY$#CTuC z8xzG7OIT4+{6H)*5Y%V__Z=W^4DlERX*x1W5E~#2^D~ z9Y^pZc$`3g7B;IT+~ZiSB_*joynp}><^^)aAt(WKd9GMsiseJaks^KsixtfiiG(5+YX_JJ^{NNt z$a@gd*sfc&9?Fo3`P|TGgtMYVeBfuXa^a>L+@B3`8;B=DjP1MxIMj0w#F+L6QfvoT z94iP2<4cayyu26*PXHDT;EH+SJh7NNR&0U%`81vju`1wPAjW#ah1ePvD9KRz@_*iu>^XG>d!!)e^fB=MbP~< zf-WDy7w{waVhJxMQUvO;qPZfd1nPy(qUw!}|pNUeal%9_)YQ`vQ$azUQr^<@tgTo`^3IGx`NV?a*u%Pr^ktSzCYy%ddeL?HmZs zDH6uABDo?lFG>{7_}v4@$9DT_NXvQ9@nyJ=`bF?0A;Lg0>lmb)LwXOy*#A+2NPZ+} zmJVsOe}GUJ#)tGQIzJ9#Xkvm8V$? n0|-2F7}8(RPlN@Wh~}KjdRSa03E(krFX$ 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.0.0", + "minimist": "*", + "morgan": "*", + "mz": "~2.7.0", + "request": "^2.81.0", + "request-promise-native": "^1.0.3", + "underscore": ">=1.8.3", + "ws": "^7.5.5" + }, + "devDependencies": { + "@types/bun": "^1.1.14", + "@types/express": "^5.0.0", + "@types/minimist": "^1.2.5", + "@types/morgan": "^1.9.9", + "@types/request": "^2.48.12" + } +} diff --git a/bun/server.ts b/bun/server.ts new file mode 100644 index 00000000..b52e0b82 --- /dev/null +++ b/bun/server.ts @@ -0,0 +1,249 @@ +import fs from "fs"; +import path from "path"; +import process from "process"; +import type { Request, Response } from "express"; +import express from "express"; +import request from "request"; +import morgan from "morgan"; +import { WebSocketServer, WebSocket } from "ws"; +import minimist from "minimist"; +import http from "http"; + +const app = express(); +const argv = minimist(process.argv.slice(1)); // Command line opts + +if (!argv["port"]) { + argv["port"] = 8888; +} + +// Interval at which we poll for connections to be active +let timeout: number; +if (process.env["TIMEOUT"]) { + timeout = parseInt(process.env["TIMEOUT"], 10); +} else { + timeout = 60000; +} + +// To catch unhandled exceptions thrown by user code async callbacks, +// these exceptions cannot be caught by try-catch in user function invocation code below +process.on("uncaughtException", (err) => { + console.error(`Caught exception: ${err}`); +}); + +// User function. Starts out undefined. +let userFunction: Function | undefined; + +const loadFunction = async (modulepath: string, funcname?: string) => { + try { + let startTime = process.hrtime(); + const pkg = await import(modulepath); + let userFunction = funcname ? pkg[funcname] : pkg.default; + + let elapsed = process.hrtime(startTime); + console.log( + `user code loaded in ${elapsed[0]}sec ${elapsed[1] / 1000000}ms` + ); + + return userFunction; + } catch (e: any) { + console.error(`user code load error: ${e}`); + return e; + } +}; + +const withEnsureGeneric = ( + func: (req: Request, res: Response) => Promise +) => { + return (req: Request, res: Response) => { + if (userFunction) { + res.status(400).send("Not a generic container"); + return; + } + func(req, res); + }; +}; + +const isFunction = (func: any): func is Function => { + return func && func.constructor && func.call && func.apply; +}; + +const specializeV2 = async (req: Request, res: Response) => { + const entrypoint = req.body.functionName + ? req.body.functionName.split(".") + : []; + const modulepath = path.join(req.body.filepath, entrypoint[0] || ""); + const result = await loadFunction(modulepath, entrypoint[1]); + + if (isFunction(result)) { + userFunction = result; + res.status(202).send(); + } else { + res.status(500).send(JSON.stringify(result)); + } +}; + +const specialize = async (req: Request, res: Response) => { + const modulepath = argv["codepath"] || "/userfunc/user"; + + if (!fs.existsSync(`${path.dirname(modulepath)}/node_modules`)) { + fs.symlinkSync( + "/usr/src/app/node_modules", + `${path.dirname(modulepath)}/node_modules` + ); + } + const result = await loadFunction(modulepath); + + if (isFunction(result)) { + userFunction = result; + res.status(202).send(); + } else { + res.status(500).send(JSON.stringify(result)); + } +}; + +// Request logger +app.use(morgan("combined")); + +let bodyParserLimit = process.env["BODY_PARSER_LIMIT"] || "1mb"; + +app.use(express.urlencoded({ extended: false, limit: bodyParserLimit })); +app.use(express.json({ limit: bodyParserLimit })); +app.use(express.raw({ limit: bodyParserLimit })); +app.use(express.text({ type: "text/*", limit: bodyParserLimit })); + +app.post("/specialize", withEnsureGeneric(specialize)); +app.post("/v2/specialize", withEnsureGeneric(specializeV2)); + +// Generic route -- all http requests go to the user function. +app.all("*", (req: Request, res: Response) => { + if (!userFunction) { + res.status(500).send("Generic container: no requests supported"); + return; + } + + const context = { + request: req, + response: res, + }; + + const callback = ( + status: number, + body: any, + headers?: { [key: string]: string } + ) => { + if (!status) return; + if (headers) { + for (let name of Object.keys(headers)) { + res.set(name, headers[name]); + } + } + res.status(status).send(body); + }; + + if (userFunction.length <= 1) { + let result: Promise; + if (userFunction.length === 0) { + result = Promise.resolve(userFunction()); + } else { + result = Promise.resolve(userFunction(context)); + } + result + .then(({ status, body, headers }) => { + callback(status, body, headers); + }) + .catch((err) => { + console.log(`Function error: ${err}`); + callback(500, "Internal server error"); + }); + } else { + try { + userFunction(context, callback); + } catch (err) { + console.log(`Function error: ${err}`); + callback(500, "Internal server error"); + } + } +}); + +let server = http.createServer(); + +// Also mount the app here +server.on("request", app); + +const wsStartEvent = { + url: "http://127.0.0.1:8000/wsevent/start", +}; + +const wsInactiveEvent = { + url: "http://127.0.0.1:8000/wsevent/end", +}; + +// Create web socket server on top of a regular http server +let wss = new WebSocketServer({ + server: server, +}); + +const noop = () => {}; + +const heartbeat = function (this: WebSocket) { + (this as any).isAlive = true; +}; + +let warm = false; + +let interval = setInterval(() => { + if (warm) { + if (wss.clients.size > 0) { + wss.clients.forEach((ws) => { + if ((ws as any).isAlive === false) return ws.terminate(); + + (ws as any).isAlive = false; + ws.ping(noop); + }); + } else { + request(wsInactiveEvent, (err, res) => { + if (err || res.statusCode != 200) { + if (err) { + console.log(err); + } else { + console.log("Unexpected response"); + } + return; + } + }); + return; + } + } +}, timeout); + +wss.on("connection", (ws) => { + if (warm == false) { + warm = true; + request(wsStartEvent, (err, res) => { + if (err || res.statusCode != 200) { + if (err) { + console.log(err); + } else { + console.log("Unexpected response"); + } + return; + } + }); + } + + (ws as any).isAlive = true; + ws.on("pong", heartbeat); + + wss.on("close", () => { + clearInterval(interval); + }); + + try { + userFunction?.(ws, wss.clients); + } catch (err) { + console.log(`Function error: ${err}`); + ws.close(); + } +}); + +server.listen(argv["port"], () => {}); diff --git a/bun/tsconfig.json b/bun/tsconfig.json new file mode 100644 index 00000000..59a229d4 --- /dev/null +++ b/bun/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags + "noUnusedLocals": true, + "noUnusedParameters": true, + "noPropertyAccessFromIndexSignature": true + } +} \ No newline at end of file From 027dd5e1be9928315fcce9a1d91d51167f73de8e Mon Sep 17 00:00:00 2001 From: danhtran94 Date: Wed, 18 Dec 2024 21:45:50 +0700 Subject: [PATCH 2/3] chore: update readme --- bun/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bun/README.md b/bun/README.md index d7632794..44d6de4d 100644 --- a/bun/README.md +++ b/bun/README.md @@ -1,12 +1,12 @@ -# Fission: NodeJS Environment +# Fission: Bun Environment -This is the NodeJS environment for Fission. +This is the Bun environment for Fission. -It's a Docker image containing a NodeJS runtime, along with a dynamic +It's a Docker image containing a Bun runtime, along with a dynamic loader. A few common dependencies are included in the package.json file. -Looking for ready-to-run examples? See the [NodeJS examples directory](../../examples/nodejs). +Looking for ready-to-run examples? See the [Bun examples directory](../../examples/bun). ## Customizing this image @@ -44,13 +44,13 @@ docker push ghcr.io/danhtran94/bun-builder You can add this customized image to fission with "fission env create": ```console -fission env create --name node --image USER/bun-env +fission env create --name bun --image USER/bun-env ``` Or, if you already have an environment, you can update its image: ```console -fission env update --name node --image USER/bun-env +fission env update --name bun --image USER/bun-env ``` After this, fission functions that have the env parameter set to the From 2c988a646352053f3d295529dc78b43a4841800a Mon Sep 17 00:00:00 2001 From: danhtran94 Date: Wed, 18 Dec 2024 22:20:17 +0700 Subject: [PATCH 3/3] chore: update readme --- bun/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bun/README.md b/bun/README.md index 44d6de4d..953dd183 100644 --- a/bun/README.md +++ b/bun/README.md @@ -25,18 +25,14 @@ Building runtime image, ```console docker build -t USER/bun-env --build-arg BUN_BASE_IMG=1.1.40-alpine -f Dockerfile . -docker build -t ghcr.io/danhtran94/bun-env --build-arg BUN_BASE_IMG=1.1.40-alpine -f Dockerfile . docker push USER/bun-env -docker push ghcr.io/danhtran94/bun-env ``` Building builder image, ```console cd builder && docker build -t USER/bun-builder --build-arg BUN_BASE_IMG=1.1.40-alpine -f Dockerfile . -cd builder && docker build -t ghcr.io/danhtran94/bun-builder --build-arg BUN_BASE_IMG=1.1.40-alpine -f Dockerfile . docker push USER/bun-builder -docker push ghcr.io/danhtran94/bun-builder ``` ## Using the image in fission