Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loading native library failing with "Can't resolve" error #386

Closed
Tracked by #495
haridas opened this issue May 6, 2018 · 15 comments
Closed
Tracked by #495

loading native library failing with "Can't resolve" error #386

haridas opened this issue May 6, 2018 · 15 comments

Comments

@haridas
Copy link

haridas commented May 6, 2018

This is a Bug Report

I created an stackoverflow question for the same, i'm pasting the full content from there.

The stackoverflow link: https://stackoverflow.com/questions/50188865/load-native-npm-x509-package-using-webpack-in-serverless

I'm testing the aws iot features using serverless framework. So for one usecase I wasn't to analyse the x509 certificates iot devices register with aws from aws lambdas, I saw npm package named x509 to do it easily. It's a native package, so I'm building it in Ubuntu 64 bit box to make it compatible with aws environment.

Issues is the webpack is not identifying this npm package even after my package importing it.

The error it saying is,

$ sls package

Serverless: Bundling with Webpack... Time: 245ms Built at: 05/05/2018 11:04:55 AM
                       Asset      Size  Chunks             Chunk Names
               jitr/index.js  11.9 KiB       0  [emitted]  jitr/index jitr/build/Release/x509.node  44.2 KiB          [emitted] Entrypoint jitr/index = jitr/index.js [0] ./jitr/index.js 7.39 KiB {0} [built] [1] external "aws-sdk" 42 bytes {0} [built] [2] ./jitr/node_modules/x509/index.js 1.29 KiB {0} [built] [3] external "fs" 42 bytes {0} [built]

ERROR in ./jitr/node_modules/x509/index.js Module not found: Error: Can't resolve './build/Release/x509' in '/home/serverless/jitr/node_modules/x509'  @ ./jitr/node_modules/x509/index.js 1:11-42  @ ./jitr/index.js

  Error --------------------------------------------------

  Webpack compilation error, see above

     For debugging logs, run again after setting the "SLS_DEBUG=*" environment variable.

  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Forums:        forum.serverless.com
     Chat:          gitter.im/serverless/serverless

  Your Environment Information -----------------------------
     OS:                     linux
     Node Version:           8.10.0
     Serverless Version:     1.26.1

My package dir structure is,

- myproject
-- webpack.config.js
-- serverless.yml
-- jitr
--- index.js <- Here I'm importing x509
--- node_modules
---- x509
----- index.js <- First line: var x509 = require('./build/Release/x509')
----- build/Release/x509.node

My webpack config file

const path = require("path");
const slsw = require("serverless-webpack");
const nodeExternals = require("webpack-node-externals");
const CopyWebpackPlugin = require("copy-webpack-plugin");

module.exports = {
    entry: slsw.lib.entries,
    target: "node",
    output: {
        libraryTarget: "commonjs2",
        path: path.resolve(__dirname, ".webpack"),
        filename: "[name].js"
    },
    module: {
        rules: [
            {
                test: /\.json$/,
                loader: "json-loader"
            },
            {
                test: /\.node$/,
                loader: "node-loader"
            }
        ]
    },
    mode: "none",
    externals: [
        nodeExternals({
            whitelist: ["x509"]
        })
    ],
    plugins: [
        new CopyWebpackPlugin([
            {
                from: "jitr/node_modules/x509/build/Release/x509.node",
                to: "jitr/build/Release/x509.node",
                toType: "file"
            }
        ])
    ]
};

What might be going wrong here ?. I was looking for more debugging steps when doing webpack command, but here i'm using a serverless plugin named serverless-webpack to package and deploy the lambdas.

Aws lambda have access to raw openssl command, if nothing works I have to use that command from nodejs to do the things.

@HyperBrain
Copy link
Member

Hi @haridas . You use

externals: [
        nodeExternals({
            whitelist: ["x509"]
        })
    ],

which bundles (i.e. integrates) the module into your code. I believe that is the reason for the actual error during building. Can you try to use nodeExternals without whitelist (nodeExternals()) and see if it then builds? The copy webpack plugin settings should be ok though.

Additionally, I'm not really sure if building on Ubuntu will work on AWS Lambda. They run Amazon Linux in their environment. I did some experiments with OpenCV in the past and I had to build the binary on an EC2 with Amazon Linux (latest version) to make it work when deployed.

@haridas
Copy link
Author

haridas commented May 8, 2018

Actually I tried without the nodeExternals, was producing the same result, along with that it bundles all the node_modules eg; aws-sdk etc. So later I included this one.

We used to push the node libs with native modules from Ubuntu 16.04 64 bit docker, it was working as expected, so I doubt the issues is not related to this.

If I zip the entire node_modules with native library and upload in lambda things are works, but via webpack it's not working. I will do some more attempts with this and post the updates here.

Some observations are:

x509/index.js
x509/build/Release/x509.node

This is the library folder structure inside node_modules. The x509/index.js first line is var x509 = require("./build/Release/x509"). This is the line cause issue. If I edit this import and put var x509 = require("./build/Release/x509.node") strangely it's working fine. Not sure why the require is sensitive with the module extension.

I was trying to avoid editing the library's index.js by using the WebpackCopy Plugin, and moving the x509.node file to x509 when building it, but it didn't helps though.

@HyperBrain
Copy link
Member

@haridas The .node extension reminded me of something. Can you try to use and configure the node-loader in Webpack? I think that enables Webpack to recognize native bindings and their extensions.

@haridas
Copy link
Author

haridas commented May 8, 2018

@HyperBrain
node-loader is already configured with webpack config file, but doesn't seems doing any thing in this context. Please check above webpack.config.js file from my issue posting, let me know if you see something with the configuration file.

Let me see I can package a sample test environment, so that the issue can be re-produceable using those files.

@haridas
Copy link
Author

haridas commented May 9, 2018

@HyperBrain

sls_native_lib.zip

Here is sample serverless project to re-create the problem. Please download and try it.

Do following steps to reproduce the issue.

cd sls_native_lib
npm install
cd jitr
npm install
cd  ..

# Now packaging the sls project will give the error. 
sls package

Please try with this and let me know if you have found any issue with the configurations with the webpack or serverless.

@HyperBrain
Copy link
Member

@haridas Thanks for the sample 👍 . Hopefully that will help how to solve the problem. I think I can get some time to analyze it tomorrow.

@HyperBrain
Copy link
Member

Just did a first. The serverless.yml is not correctly configured. I'll try to get it corrected soon.

@HyperBrain
Copy link
Member

UPDATE:

Serverless: Bundling with Webpack...
Time: 778ms
Built at: 05/10/2018 12:49:01 AM
            Asset      Size  Chunks             Chunk Names
    jitr/index.js  1.83 KiB       0  [emitted]  jitr/index
jitr/index.js.map     7 KiB       0  [emitted]  jitr/index
Entrypoint jitr/index = jitr/index.js jitr/index.js.map
[0] external "fs" 42 bytes {0} [built]
[1] (webpack)/buildin/module.js 519 bytes {0} [built]
[2] ./jitr/node_modules/x509/build/Release/x509.node 209 bytes {0} [built]
[3] ./jitr/node_modules/x509/index.js 1.17 KiB {0} [built]
[4] ./jitr/index.js 141 bytes {0} [built]
Serverless: No external modules needed
Serverless: Packaging service...

The main reason was that the file name resolver was not configured in webpack.conf.js. It has to be configured to use .node as additional file extension to be able to resolve unqualified requires.
However there are some small other things that should be changed... I'll re-post the zip file with the working/changed configuration as soon as I did some final tests.

@haridas
Copy link
Author

haridas commented May 11, 2018

Yes adding the resolve to webpack helps to package the libs. But the native binary is left behind, that need to be handled with PluginCopy or any implicit webpack thing does the same ?

My updated webpack.config.js file

const path = require("path");
const slsw = require("serverless-webpack");
const nodeExternals = require("webpack-node-externals");
const CopyWebpackPlugin = require("copy-webpack-plugin");

module.exports = {
    entry: slsw.lib.entries,
    target: "node",
    output: {
        libraryTarget: "commonjs2",
        path: path.resolve(__dirname, ".webpack"),
        filename: "[name].js"
    },
    module: {
        rules: [
            {
                test: /\.node$/,
                loader: "node-loader"
            }
        ]
    },
    resolve: {
        extensions: [".js", ".json", ".node"]
    },
    mode: "none",
    externals: [
        nodeExternals({
            whitelist: ["x509"]
        })
    ],
    plugins: [
        //new CopyWebpackPlugin([
        //    {
        //        from: "jitr/node_modules/x509/build/Release/x509.node",
        //        to: "jitr/build/Release/x509.node",
        //        toType: "file"
        //    }
        //])
    ],
    devtool: "source-map"
};

@HyperBrain
Copy link
Member

HyperBrain commented May 11, 2018

Yes, you have to uncomment the copy plugin to get the binary library packaged. You have to check if the node file is sufficient or if you need to copy the whole Release folder to get all compiled binaries.

webpack-386-native-modules.zip

I also did some changes to the serverless.yml where I removed unnecessary stuff and enabled includeModules, so that in case the project uses more externals besides x509 it will automatically package them and the correct order of the 2 plugins.

The webpack config also includes the correct "mode" setting to enable debugging when run locally, but using production mode otherwise.

You can ignore the package.json changes. I only added serverless and sls-offline locally so that I could test better with specific versions.

@haridas
Copy link
Author

haridas commented May 15, 2018

Thanks alot @HyperBrain, the new zip is working as expected and it's copying the .node file via webpack plugin to target folder.

Currently I'm facing issue like, the webpack bundled index.js keeps the absolute path to the .node module, hence it fails to import in lambda.

/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(module) {
try {global.process.dlopen(module,
"/project/serverless/jitr/node_modules/x509/build/Release/x509.node"); 
} catch(e) {throw new Error('Cannot open ' + 
"/project/serverless/jitr/node_modules/x509/build/Release/x509.node" + ': ' + e);}
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(5)(module)))

Actually here the global.process.dlopen should pick the relate path. Any thoughts on this would be much helpful. I was checking the webpack's resolve.alias options, but no much luck yet.
Thank you.

@HyperBrain
Copy link
Member

I think the path injection itself is done by the node-loader, i.e. during compile it sets the path. I'll check later if the loader can support an option to disable this behavior or if maybe Webpack's DefinePlugin can be misused to overwrite the path somehow.

@haridas
Copy link
Author

haridas commented May 28, 2018

After trying multiple methods, it seems not initiative to go forward with the native library with lambda, it's more complex to manage and not fit for generic use-cases. Now I moved to node-forge package to handle all my certificate handling. It's non-native and friendly with the current webpack and serverless environment.

In my case I have around 100 lambda functions, all are packaged with serverless ensuring no code duplication as much as possible in each lambda bundles. In which only handful of lambdas requires certificate handling support.

@HyperBrain
Copy link
Member

HyperBrain commented May 28, 2018

Hi @haridas , good to hear that you found a feasible solution. Using node-forge sounds good, because it internally relies on OpenSSL, which is available in the lambda environments (independent of which version) natively.
From this experience the best solution might be, to completely separate lambdas that use native libraries into a single project (as long as they must be done with native libraries).

@hassankhan
Copy link
Contributor

Closing in favour of adding documentation as part of #495.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants