Skip to content

Commit

Permalink
feat(runtime): detach custom dependency, support node_module caching (#…
Browse files Browse the repository at this point in the history
…1658)

* feat: add custom dep path

* chore: remove log4js from runtime, allow adding dup npm deps

* chore: rm unused imports

* fix(runtime): support custom dep path in import()

* chore: merge upstream main

* fix(runtime): add --experimental-vm-modules for runtime

* feat(runtime): support node_modules cache in init scripts

* fix(runtime): fix download error in init script

* feat(server): support node_modules cache in server

---------

Co-authored-by: maslow <wangfugen@126.com>
  • Loading branch information
skyoct and maslow committed Nov 27, 2023
1 parent eb35c59 commit 2fbc5f1
Show file tree
Hide file tree
Showing 22 changed files with 9,013 additions and 1,696 deletions.
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"Chakra",
"chatgpt",
"ciphertext",
"cloudbin",
"clsx",
"coll",
"compat",
Expand Down Expand Up @@ -105,6 +106,7 @@
"statefulset",
"storageclass",
"Streamable",
"stylelint",
"tailwindcss",
"tanstack",
"telepresence",
Expand All @@ -125,8 +127,7 @@
"withs",
"xmlparser",
"zcube",
"zustand",
"stylelint"
"zustand"
],
"i18n-ally.localesPaths": "web/public/locales",
"i18n-ally.enabledParsers": [
Expand Down
8 changes: 6 additions & 2 deletions runtimes/nodejs/Dockerfile.init
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
FROM lafyun/runtime-node:latest
FROM node:20

CMD [ "sh", "/app/init.sh" ]
WORKDIR /app

COPY ./init.sh /app/init.sh

CMD [ "sh", "/app/init.sh" ]
99 changes: 96 additions & 3 deletions runtimes/nodejs/init.sh
Original file line number Diff line number Diff line change
@@ -1,15 +1,108 @@
#!/bin/sh

set -e
# node ./dist/init.js

# skip init if $DEPENDENCIES is empty
if [ -z "$DEPENDENCIES" ]; then
echo "No dependencies to install."
cp -r /app/* /tmp/app
exit 0
fi

# if $NODE_MODULES_URL is not empty
if [ -n "$NODE_MODULES_PULL_URL" ]; then
echo "Downloading node_modules from $NODE_MODULES_PULL_URL"

# get start time
start_time=$(date +%s)

# temporarily disable set -e
set +e

# download node_modules.tar and untar to `node_modules`
curl -sSfL $NODE_MODULES_PULL_URL -o node_modules.tar

end_time=$(date +%s)

# if error
if [ $? -ne 0 ]; then
echo "Failed to download node_modules cache."
else
elapsed_time=$(expr $end_time - $start_time)
echo "Downloaded node_modules.tar in $elapsed_time seconds."
fi

# untar node_modules.tar
tar -xf node_modules.tar -C .

# check tar exit code
if [ $? -ne 0 ]; then
echo "Failed to extract node_modules cache."
else
end_time_2=$(date +%s)
elapsed_time_2=$(expr $end_time_2 - $end_time)
echo "Extracted node_modules cache in $elapsed_time_2 seconds."
fi

# re-enable set -e
set -e
else
echo "No node_modules cache found, continuing installation."
fi

CACHED_DEPENDENCIES=""
# if node_modules/.dependencies exists
if [ -f "node_modules/.dependencies" ]; then
CACHED_DEPENDENCIES=`cat node_modules/.dependencies`
fi

echo "Cached dependencies: $CACHED_DEPENDENCIES"
echo "Dependencies to install: $DEPENDENCIES"

# if $CACHED_DEPENDENCIES is equal to $DEPENDENCIES
if [ "$CACHED_DEPENDENCIES" = "$DEPENDENCIES" ]; then
echo "No dependencies changed since last cache build."
exit 0
else
echo "Dependencies changed since last cache build."
fi


# npm install $DEPENDENCIES
start_time=$(date +%s)
echo "npm install $DEPENDENCIES $NPM_INSTALL_FLAGS"
npm install $DEPENDENCIES $NPM_INSTALL_FLAGS
cp -r /app/* /tmp/app
end_time=$(date +%s)
elapsed_time=$(expr $end_time - $start_time)
echo "Installed dependencies in $elapsed_time seconds."

# if $NODE_MODULES_PUSH_URL is not empty
if [ -n "$NODE_MODULES_PUSH_URL" ]; then
# temporarily disable set -e
set +e

start_time=$(date +%s)
echo $DEPENDENCIES > node_modules/.dependencies
echo "Uploading node_modules to $NODE_MODULES_PUSH_URL"

# tar `node_modules` to node_modules.tar
tar -cf node_modules.tar ./node_modules

end_time_1=$(date +%s)
elapsed_time=$(expr $end_time_1 - $start_time)
echo "Compressed node_modules in $elapsed_time seconds."

# upload node_modules.tar to $NODE_MODULES_PUSH_URL
curl -sSfL -X PUT -T node_modules.tar $NODE_MODULES_PUSH_URL


if [ $? -ne 0 ]; then
echo "Failed to upload node_modules cache."
else
end_time_2=$(date +%s)
elapsed_time_2=$(expr $end_time_2 - $end_time)
echo "Uploaded node_modules.tar in $elapsed_time_2 seconds."
fi

# re-enable set -e
set -e
fi
1 change: 0 additions & 1 deletion runtimes/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
"express-xml-bodyparser": "^0.3.0",
"jsonwebtoken": "^9.0.0",
"lodash": "^4.17.21",
"log4js": "^6.7.1",
"minio": "^7.0.32",
"mongodb": "^4.12.1",
"mongodb-uri": "^0.9.7",
Expand Down
4 changes: 4 additions & 0 deletions runtimes/nodejs/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,8 @@ export default class Config {
static get DISABLE_MODULE_CACHE(): boolean {
return process.env.DISABLE_MODULE_CACHE === 'true'
}

static get CUSTOM_DEPENDENCY_BASE_PATH(): string {
return process.env.CUSTOM_DEPENDENCY_BASE_PATH || '/tmp/custom_dependency'
}
}
32 changes: 27 additions & 5 deletions runtimes/nodejs/src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,32 @@
import { LoggerInterface, MongoAccessor } from 'database-proxy'
import Config from './config'
import * as mongodb_uri from 'mongodb-uri'
import * as log4js from 'log4js'
import { logger } from './support/logger'


// Define a noop logger for mongo accessor
class AccessorLogger implements LoggerInterface {
level: number = 0
trace(..._params: any[]): void {
}

debug(..._params: any[]): void {
}

info(..._params: any[]): void {
}

warn(..._params: any[]): void {
}

error(..._params: any[]): void {
}

fatal(..._params: any[]): void {
}

}

/**
* Database Management
*/
Expand Down Expand Up @@ -39,16 +62,15 @@ export class DatabaseAgent {
const { database } = mongodb_uri.parse(Config.DB_URI)
const accessor = new MongoAccessor(database, Config.DB_URI)

const accessorLogger: any = log4js.getLogger('accessor')
accessorLogger.level = 'warning'
accessor.setLogger(accessorLogger as LoggerInterface)
const accessorLogger = new AccessorLogger()
accessor.setLogger(accessorLogger)
accessor
.init()
.then(async () => {
logger.info('db connected')
})
.catch((error) => {
accessorLogger.error(error)
logger.error(error)
setTimeout(() => process.exit(101), 0)
})

Expand Down
20 changes: 17 additions & 3 deletions runtimes/nodejs/src/handler/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import { logger } from '../support/logger'
import { IRequest } from '../support/types'
import { parseToken } from '../support/token'
import { FunctionCache } from '../support/engine'
import Config from '../config'
import * as fs from 'fs'

const nodeModulesRoot = path.resolve(__dirname, '../../node_modules')

/**
* Gets declaration files of a dependency package
*/
export async function handlePackageTypings(req: IRequest, res: Response) {
const requestId = req['requestId']

// verify the debug token
const token = req.get('x-laf-develop-token')
Expand Down Expand Up @@ -77,9 +78,22 @@ export async function handlePackageTypings(req: IRequest, res: Response) {
})
}

// get custom dependency types
const customDependencyPath = `${Config.CUSTOM_DEPENDENCY_BASE_PATH}/node_modules/`
if (fs.existsSync(`${customDependencyPath}/${packageName}`)) {
getThreePartyPackageTypings(req, res, customDependencyPath, packageName)
} else {
getThreePartyPackageTypings(req, res, nodeModulesRoot, packageName)
}
}



async function getThreePartyPackageTypings(req: IRequest, res: Response, basePath: string, packageName: string) {
const requestId = req['requestId']
try {
// Gets other three-party package types
const pkd = new PackageDeclaration(packageName, nodeModulesRoot)
const pkd = new PackageDeclaration(packageName, basePath)
await pkd.load()
return res.send({
code: 0,
Expand All @@ -92,4 +106,4 @@ export async function handlePackageTypings(req: IRequest, res: Response) {
error: error.toString(),
})
}
}
}
27 changes: 23 additions & 4 deletions runtimes/nodejs/src/support/engine/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@ import { FunctionCache, FunctionModuleGlobalContext } from '.'
import Config from '../../config'
import { Console } from '.'
import * as vm from 'vm'
import { createRequire } from 'node:module'

const CUSTOM_DEPENDENCY_NODE_MODULES_PATH = `${Config.CUSTOM_DEPENDENCY_BASE_PATH}/node_modules/`

export class FunctionModule {
protected static cache: Map<string, any> = new Map()

private static customRequire = createRequire(
CUSTOM_DEPENDENCY_NODE_MODULES_PATH,
)

static get(functionName: string): any {
const moduleName = `@/${functionName}`
return this.require(moduleName, [])
Expand Down Expand Up @@ -40,7 +47,13 @@ export class FunctionModule {
}
return mod
}
return require(name)

// load custom dependency from custom dependency path first
try {
return FunctionModule.customRequire(name)
} catch (e) {
return require(name)
}
}

/**
Expand All @@ -62,9 +75,11 @@ export class FunctionModule {
filename: `FunctionModule.${functionName}`,
displayErrors: true,
contextCodeGeneration: {
strings: false,
strings: true,
wasm: true,
},
} as any

const script = this.createScript(wrapped, {})
return script.runInNewContext(sandbox, options)
}
Expand Down Expand Up @@ -94,13 +109,17 @@ export class FunctionModule {
): vm.Script {
const _options = {
...options,

importModuleDynamically: async (
specifier: string,
_: vm.Script,
_importAssertions: any,
) => {
return await import(specifier)
try {
const resolvedPath = FunctionModule.customRequire.resolve(specifier)
return await import(resolvedPath)
} catch (e) {
return await import(specifier)
}
},
} as any

Expand Down
4 changes: 2 additions & 2 deletions runtimes/nodejs/start.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/sh

# source .env
echo "****** start service: node $FLAGS --experimental-fetch ./dist/index.js *******"
exec node $FLAGS ./dist/index.js
echo "****** start service: node $FLAGS --experimental-vm-modules --experimental-fetch ./dist/index.js *******"
exec node $FLAGS --experimental-vm-modules --experimental-fetch ./dist/index.js
14 changes: 6 additions & 8 deletions server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
- app storage management
- app log management
- app instance management
- app billing management
- app domain management
- app certificate management
- app metrics management

# Development

Expand All @@ -21,8 +25,6 @@
- [Telepresence](https://www.telepresence.io) for local development
- [MongoDb](https://docs.mongodb.com) basic use
- [MinIO](https://min.io) object storage
- [APISIX](https://apisix.apache.org) gateway
- [nestjs-i18n](https://nestjs-i18n.com/) i18n

## Prerequisites

Expand All @@ -44,15 +46,11 @@ telepresence connect -n laf-system
telepresence intercept laf-server -p 3000:3000 -e $(pwd)/.env

npm install
npm run watch
npm run dev
```

> Clean up
```bash
telepresence leave laf-server
```

## Troubleshooting

- `telepresence helm install` failed for `arm64 / Apple Chip` cluster, please upgrade your telepresence to `v2.11.1` or later.
```
Loading

0 comments on commit 2fbc5f1

Please sign in to comment.